]> git.gir.st - solVItaire.git/blob - sol.c
first working version of canfield
[solVItaire.git] / sol.c
1 #define _DEFAULT_SOURCE
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <time.h>
5 #include <termios.h>
6 #include <unistd.h>
7
8 #include "sol.h"
9 #include "schemes.h"
10
11 #ifdef KLONDIKE
12 #define NUM_PILES 7
13 #define MAX_HIDDEN 6 /*how many cards are turned over at most in a tableu pile*/
14 #define MAX_STOCK 24 /*how many cards can be in the stock at most (=@start)*/
15 #define NUM_DECKS 1
16 #elif defined SPIDER
17 #define MAX_HIDDEN 5
18 #define NUM_PILES 10
19 #define MAX_STOCK 50 /*how many cards can be dealt onto the piles*/
20 #define NUM_DECKS 2
21 #endif
22
23 #define get_suit(card) \
24 ((card-1) % NUM_SUITS)
25 #define get_rank(card) \
26 ((card-1) / NUM_SUITS)
27 #define get_color(card) \
28 ((get_suit(card) ^ get_suit(card)>>1) & 1)
29
30 struct playfield {
31 //TODO: stock and waste are incompatible with undo{}
32 card_t s[MAX_STOCK]; /* stock */
33 int z; /* stock size */
34 int w; /* waste; index into stock (const -1 in spider) */
35 #ifdef KLONDIKE
36 card_t f[NUM_SUITS][MAX_HIDDEN+NUM_RANKS]; /* foundation (oversized to ease find_top()) */
37 card_t t[NUM_PILES][MAX_HIDDEN+NUM_RANKS]; /* tableu piles */
38 #elif defined SPIDER
39 card_t f[NUM_DECKS*NUM_COLORS][MAX_HIDDEN+NUM_RANKS]; //each completed set gets put on its own pile, so undo is possible
40 card_t t[NUM_PILES][MAX_HIDDEN+NUM_RANKS];
41 #endif
42 struct undo {
43 int from; /* pile cards were taken from */
44 int to; /* pile cards were moved to */
45 int n; /* number of cards moved */
46 struct undo* prev;
47 struct undo* next;
48 } u;
49 } f;
50 struct opts {
51 const struct scheme* s;
52 } op;
53
54 #ifdef KLONDIKE
55 //declare action as array 10 of array 10 of pointer to function (int,int) returning int
56 // this stores a function pointer for every takeable action that is then called by execute()
57 //lines = from, cols = to
58 int (*action[10][10])(int,int) = {
59 /* 0 1 2 3 4 5 6 7 8 9 */
60 /* 0 */ { nop, f2t, f2t, f2t, f2t, f2t, f2t, f2t, nop, nop },
61 /* 1 */ { t2f, nop, t2t, t2t, t2t, t2t, t2t, t2t, nop, nop },
62 /* 2 */ { t2f, t2t, nop, t2t, t2t, t2t, t2t, t2t, nop, nop },
63 /* 3 */ { t2f, t2t, t2t, nop, t2t, t2t, t2t, t2t, nop, nop },
64 /* 4 */ { t2f, t2t, t2t, t2t, nop, t2t, t2t, t2t, nop, nop },
65 /* 5 */ { t2f, t2t, t2t, t2t, t2t, nop, t2t, t2t, nop, nop },
66 /* 6 */ { t2f, t2t, t2t, t2t, t2t, t2t, nop, t2t, nop, nop },
67 /* 7 */ { t2f, t2t, t2t, t2t, t2t, t2t, t2t, nop, nop, nop },
68 /* 8 */ { nop, nop, nop, nop, nop, nop, nop, nop, nop, s2w },
69 /* 9 */ { w2f, w2t, w2t, w2t, w2t, w2t, w2t, w2t, w2s, nop },
70 };
71 #elif defined SPIDER
72 int (*action[11][10])(int,int) = {
73 //piles are zero-indexed in spider; stk=stock
74 /* 0 1 2 3 4 5 6 7 8 9 */
75 /* 0 */ { nop, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
76 /* 1 */ { t2t, nop, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
77 /* 2 */ { t2t, t2t, nop, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
78 /* 3 */ { t2t, t2t, t2t, nop, t2t, t2t, t2t, t2t, t2t, t2t },
79 /* 4 */ { t2t, t2t, t2t, t2t, nop, t2t, t2t, t2t, t2t, t2t },
80 /* 5 */ { t2t, t2t, t2t, t2t, t2t, nop, t2t, t2t, t2t, t2t },
81 /* 6 */ { t2t, t2t, t2t, t2t, t2t, t2t, nop, t2t, t2t, t2t },
82 /* 7 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, nop, t2t, t2t },
83 /* 8 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, nop, t2t },
84 /* 9 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, nop },
85 /*stk*/ { s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t },
86 };
87 #endif
88
89 int main(int argc, char** argv) {
90 //op.s = &unicode_small_mono;
91 //op.s = &unicode_large_mono;
92 op.s = &unicode_large_color;
93 raw_mode(1); //TODO: alt.screen, etc.
94 sol();
95 raw_mode(0);
96 }
97
98 void sol(void) {
99 f = (const struct playfield){0};
100 deal();
101
102 int from, to;
103 print_table();
104 while (!get_cmd(&from, &to)) {
105 if (action[from][to](from,to)) {
106 printf ("\033[?5h");
107 fflush (stdout);
108 usleep (100000);
109 printf ("\033[?5l");
110 }
111 print_table();
112 }
113 }
114
115 int find_top(card_t* pile) {
116 int i;
117 for(i=MAX_HIDDEN+NUM_RANKS-1; i>=0 && !pile[i]; i--);
118 return i;
119 }
120 void turn_over(card_t* pile) {
121 int top = find_top(pile);
122 if (pile[top] < 0) pile[top] *= -1;
123 }
124 #ifdef KLONDIKE
125 card_t stack_take(void) { /*NOTE: assert(f.w >= 0) */
126 card_t card = f.s[f.w];
127 /* move stack one over, so there are no gaps in it: */
128 for (int i = f.w; i < f.z-1; i++)
129 f.s[i] = f.s[i+1];
130 f.z--;
131 f.w--; /* make previous card visible again */
132 return card;
133 }
134 int t2f(int from, int to) { /* tableu to foundation */
135 from--; //remove off-by-one
136 int top_from = find_top(f.t[from]);
137 to = get_suit(f.t[from][top_from]);
138 int top_to = find_top(f.f[to]);
139 if ((top_to < 0 && get_rank(f.t[from][top_from]) == RANK_A)
140 || (get_rank(f.f[to][top_to]) == get_rank(f.t[from][top_from])-1)) {
141 f.f[to][top_to+1] = f.t[from][top_from];
142 f.t[from][top_from] = NO_CARD;
143 turn_over(f.t[from]);
144 return 0;
145 } else return 1;
146 }
147 int w2f(int from, int to) { /* waste to foundation */
148 if (f.w < 0) return 1;
149 to = get_suit(f.s[f.w]);
150 int top_to = find_top(f.f[to]);
151 if ((top_to < 0 && get_rank(f.s[f.w]) == RANK_A)
152 || (get_rank(f.f[to][top_to]) == get_rank(f.s[f.w])-1)) {
153 f.f[to][top_to+1] = stack_take();
154 return 0;
155 } else return 1;
156
157 }
158 int s2w(int from, int to) { /* stock to waste */
159 if (f.z == 0) return 1;
160 f.w++;
161 if (f.w == f.z) f.w = -1;
162 return 0;
163 }
164 int w2s(int from, int to) { /* waste to stock (undoes stock to waste) */
165 if (f.z == 0) return 1;
166 f.w--;
167 if (f.w < -1) f.w = f.z-1;
168 return 0;
169 }
170 int f2t(int from, int to) { /* foundation to tableu */
171 //TODO: there are two possible cards one can take. choosing one isn't implemented yet!
172 to--; //remove off-by-one
173 int top_to = find_top(f.t[to]);
174 from = get_color(f.t[to][top_to]);
175 int top_from = find_top(f.f[from]);
176 if ((get_color(f.t[to][top_to]) != get_color(f.f[from][top_from]))
177 && (get_rank(f.t[to][top_to]) == get_rank(f.f[from][top_from])+1)) {
178 f.t[to][top_to+1] = stack_take();
179 return 0;
180 } else return 1;
181 }
182 int w2t(int from, int to) { //waste to tableu
183 to--; //remove off-by-one
184 int top_to = find_top(f.t[to]);
185 if (((get_color(f.t[to][top_to]) != get_color(f.s[f.w]))
186 && (get_rank(f.t[to][top_to]) == get_rank(f.s[f.w])+1))
187 || (top_to < 0 && get_rank(f.s[f.w]) == RANK_K)) {
188 f.t[to][top_to+1] = stack_take();
189 return 0;
190 } else return 1;
191 }
192 int t2t(int from, int to) {
193 from--; to--; //remove off-by-one
194 int top_to = find_top(f.t[to]);
195 int top_from = find_top(f.t[from]);
196 for (int i = top_from; i >=0; i--) {
197 //TODO: check that we aren't moving facedown cards!
198 if (((get_color(f.t[to][top_to]) != get_color(f.t[from][i]))
199 && (get_rank(f.t[to][top_to]) == get_rank(f.t[from][i])+1))
200 || (top_to < 0 && get_rank(f.t[from][i]) == RANK_K)) {
201 /* move cards [i..top_from] to their destination */
202 for (;i <= top_from; i++) {
203 top_to++;
204 f.t[to][top_to] = f.t[from][i];
205 f.t[from][i] = NO_CARD;
206 }
207 turn_over(f.t[from]);
208 return 0;
209 }
210 }
211 return 1; /* no such move possible */
212 }
213 #elif defined SPIDER
214 int t2t(int from, int to) { //TODO: implementation XXX
215 from--; to--; //remove off-by-one
216 f.t[from][find_top(f.t[from])] = NO_CARD;
217 return 1;
218 }
219 int s2t(int from, int to) {
220 if (f.z <= 0) return 1; /* stack still has cards? */
221 for (int pile = 0; pile < NUM_PILES; pile++)
222 if (f.t[pile][0] == NO_CARD) return 1; /*no piles may be empty*/
223 for (int pile = 0; pile < NUM_PILES; pile++) {
224 f.t[pile][find_top(f.t[pile])+1] = f.s[--f.z];
225 }
226 return 0;
227 }
228 #endif
229 int nop(int from, int to) { return 1; }
230
231 int get_cmd (int* from, int* to) {
232 //returns 0 on success or an error code indicating game quit, new game,...
233 //TODO: only works for KLONDIKE
234 //TODO: check validity
235 //TODO: segfault on invalid input!
236 char f, t;
237 f = getchar();
238 #ifdef SPIDER
239 if (f=='\n') {
240 *from = 10;
241 *to = 0;
242 return CMD_MOVE;
243 }
244 #endif
245 switch (f) {
246 case 'q': return CMD_QUIT;
247 case 'r': return CMD_NEW;
248 default: if (f < '0' || f > '9') return CMD_INVAL;
249 }
250 t =
251 #ifdef KLONDIKE
252 (f=='8')?'9':
253 #endif
254 getchar();
255 *from = f-'0';
256 *to = t-'0';
257 return CMD_MOVE;
258 }
259
260 void deal(void) {
261 card_t deck[DECK_SIZE*NUM_DECKS];
262 int avail = DECK_SIZE*NUM_DECKS;
263 for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) deck[i] = (i%DECK_SIZE)+1;
264 srandom (time(NULL));
265 for (int i = DECK_SIZE*NUM_DECKS-1; i > 0; i--) { //fisher-yates
266 int j = random() % (i+1);
267 if (j-i) deck[i]^=deck[j],deck[j]^=deck[i],deck[i]^=deck[j];
268 }
269 // deal cards
270 //foundation starts empty:
271 for (int i = 0; i < NUM_SUITS; i++)
272 for (int j = NUM_RANKS; j; j--) f.f[i][j] = NO_CARD;
273
274 for (int i = 0; i < NUM_PILES; i++) {
275 #ifdef KLONDIKE
276 //tableu: [0] = 0,1; [1] = 1,1; [2] = 2,1; ... [6] = 6,1
277 int closed = i;
278 #elif defined SPIDER
279 //left 4 piles have 5 hidden + 1 shown
280 //right6 piles have 4 hidden + 1 shown
281 int closed = i<4?5:4;
282 #endif
283
284 for (int j = 0; j < closed; j++) f.t[i][j] = -deck[--avail]; //face-down cards are negative
285 f.t[i][closed] = deck[--avail]; //the face-up card
286 for (int j = closed+1; j < MAX_HIDDEN+NUM_RANKS; j++) f.t[i][j] = NO_CARD;
287 }
288 //rest of the cards to the stock (should be 50 for spider):
289 for (f.z = 0; avail; f.z++) f.s[f.z] = deck[--avail];
290 f.w = -1; /* @start: nothing on waste (no waste in spider -> const) */
291 }
292
293 void print_table(void) { //{{{
294 printf("\033[2J\033[H");//TEMP XXX -- use raw term mode
295 #ifdef KLONDIKE
296 //print stock, waste and foundation:
297 for (int line = 0; line < op.s->height; line++) {
298 printf ("%s", ( /* stock */
299 (f.w < f.z-1)?op.s->facedown
300 :op.s->placeholder)[line]);
301 printf ("%s", ( /* waste */
302 ((short)f.w >= 0)?op.s->card[f.s[f.w]] //TODO: sometimes segfaults because f.w == (int)((short)-1)
303 :op.s->placeholder)[line]);
304 printf ("%s", op.s->card[NO_CARD][line]); /* spacer */
305 /* foundation: */
306 for (int pile = 0; pile < NUM_SUITS; pile++) {
307 for (int i = NUM_RANKS-1; i >=0; i--) {
308 if (f.f[pile][i]) {
309 printf ("%s", op.s->card[f.f[pile][i]][line]);
310 goto next;
311 }
312 }
313 printf ("%s", op.s->placeholder[line]);
314 next:;
315 }
316 printf("\n");
317 }
318 printf("\n");
319 #endif
320 //print tableu piles:
321 int row[NUM_PILES] = {0};
322 int line[NUM_PILES]= {0};
323 int line_had_card; // :|
324 int label[NUM_PILES] = {0};//XXX
325 do {
326 line_had_card = 0;
327 for (int pile = 0; pile < NUM_PILES; pile++) {
328 card_t card = f.t[pile][row[pile]];
329 card_t next = f.t[pile][row[pile]+1];
330 printf ("%s", (
331 (card<0)?op.s->facedown
332 :op.s->card[card]
333 )[line[pile]]);
334
335 if (++line[pile] >= (next?op.s->overlap:op.s->height)) {
336 //TODO: allow extreme overlap on sequences
337 line[pile]=0;
338 row[pile]++;
339 }
340 if(!card && !label[pile]) label[pile] = 1, printf ("\b\b%d ", (pile+1) % 10);//XXX: print tableu labels
341 line_had_card |= !!card;
342 }
343 printf ("\n");
344 } while (line_had_card);
345 }//}}}
346
347 void append_undo (int n, int f, int t) {
348 //check if we have to free redo buffer (.next)
349 //malloc
350 //update pointers
351 *NULL;
352 }
353
354 void raw_mode(int enable) { //{{{
355 static struct termios saved_term_mode;
356 struct termios raw_term_mode;
357
358 if (enable) {
359 tcgetattr(STDIN_FILENO, &saved_term_mode);
360 raw_term_mode = saved_term_mode;
361 raw_term_mode.c_lflag &= ~(ICANON | ECHO);
362 raw_term_mode.c_cc[VMIN] = 1 ;
363 raw_term_mode.c_cc[VTIME] = 0;
364 tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term_mode);
365 } else {
366 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term_mode);
367 }
368 } //}}}
369
370 //vim: foldmethod=marker
Imprint / Impressum