]> git.gir.st - solVItaire.git/blob - sol.c
'.' implementation (ugly and not portable)
[solVItaire.git] / sol.c
1 #define _DEFAULT_SOURCE /* for getopt, sigaction, usleep */
2 #include <poll.h>
3 #include <signal.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <sys/ioctl.h>
7 #include <time.h>
8 #include <termios.h>
9 #include <unistd.h>
10
11 #include "sol.h"
12 #include "schemes.h"
13
14 struct playfield f;
15 struct opts op;
16
17 // action table {{{
18 /* stores a function pointer for every takeable action; called by game loop */
19 int (*action[NUM_PLACES][10])(int,int,int) = {
20 #ifdef KLONDIKE
21 /* 1 2 3 4 5 6 7 stk wst fnd*/
22 /* 1 */ { t2f, t2t, t2t, t2t, t2t, t2t, t2t, nop, nop, t2f },
23 /* 2 */ { t2t, t2f, t2t, t2t, t2t, t2t, t2t, nop, nop, t2f },
24 /* 3 */ { t2t, t2t, t2f, t2t, t2t, t2t, t2t, nop, nop, t2f },
25 /* 4 */ { t2t, t2t, t2t, t2f, t2t, t2t, t2t, nop, nop, t2f },
26 /* 5 */ { t2t, t2t, t2t, t2t, t2f, t2t, t2t, nop, nop, t2f },
27 /* 6 */ { t2t, t2t, t2t, t2t, t2t, t2f, t2t, nop, nop, t2f },
28 /* 7 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2f, nop, nop, t2f },
29 /*stk*/ { nop, nop, nop, nop, nop, nop, nop, nop, s2w, nop },
30 /*wst*/ { w2t, w2t, w2t, w2t, w2t, w2t, w2t, w2s, w2f, w2f },
31 /*fnd*/ { f2t, f2t, f2t, f2t, f2t, f2t, f2t, nop, nop, nop },
32 #elif defined SPIDER
33 /* 1 2 3 4 5 6 7 8 9 10*/
34 /* 1 */ { t2f, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
35 /* 2 */ { t2t, t2f, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
36 /* 3 */ { t2t, t2t, t2f, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
37 /* 4 */ { t2t, t2t, t2t, t2f, t2t, t2t, t2t, t2t, t2t, t2t },
38 /* 5 */ { t2t, t2t, t2t, t2t, t2f, t2t, t2t, t2t, t2t, t2t },
39 /* 6 */ { t2t, t2t, t2t, t2t, t2t, t2f, t2t, t2t, t2t, t2t },
40 /* 7 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2f, t2t, t2t, t2t },
41 /* 8 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2f, t2t, t2t },
42 /* 9 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2f, t2t },
43 /*10 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2f },
44 /*stk*/ { s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t },
45 #elif defined FREECELL
46 /* 1 2 3 4 5 6 7 8 cll fnd*/
47 /* 1 */ { t2f, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2c, t2f },
48 /* 2 */ { t2t, t2f, t2t, t2t, t2t, t2t, t2t, t2t, t2c, t2f },
49 /* 3 */ { t2t, t2t, t2f, t2t, t2t, t2t, t2t, t2t, t2c, t2f },
50 /* 4 */ { t2t, t2t, t2t, t2f, t2t, t2t, t2t, t2t, t2c, t2f },
51 /* 5 */ { t2t, t2t, t2t, t2t, t2f, t2t, t2t, t2t, t2c, t2f },
52 /* 6 */ { t2t, t2t, t2t, t2t, t2t, t2f, t2t, t2t, t2c, t2f },
53 /* 7 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2f, t2t, t2c, t2f },
54 /* 8 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2f, t2c, t2f },
55 /*cll*/ { c2t, c2t, c2t, c2t, c2t, c2t, c2t, c2t, c2f, c2f },
56 /*fnd*/ { f2t, f2t, f2t, f2t, f2t, f2t, f2t, f2t, f2c, nop },
57 #endif
58 };
59 // }}}
60
61 // argv parsing, game loops, cleanup {{{
62 int main(int argc, char** argv) {
63 /* opinionated defaults: */
64 op.s = &unicode_large_color;
65 op.v = 1; /* enable fake visbell by default */
66 #ifdef SPIDER
67 op.m = MEDIUM;
68 #endif
69
70 int optget;
71 opterr = 0; /* don't print message on unrecognized option */
72 while ((optget = getopt (argc, argv, "+:hs:vbcmMV")) != -1) {
73 switch (optget) {
74 #ifdef SPIDER
75 case 's': /* number of suits */
76 switch (optarg[0]) {
77 case '1': op.m = EASY; break;
78 case '2': op.m = MEDIUM; break;
79 case '4': op.m = NORMAL; break;
80 default: goto error;
81 } break;
82 #endif
83 case 'b': op.s = &unicode_large_mono; break;
84 case 'c': op.s = &unicode_large_color; break;
85 case 'm': op.s = &unicode_small_mono; break; /* "mini, monochrome" */
86 case 'M': op.s = &unicode_small_color; break; /* "mini, colorful" */
87 case 'V': op.v = 0; break; /* WARN: experimental; might change */
88 case 'h': default: goto error;
89 error:
90 fprintf (stderr, SHORTHELP LONGHELP KEYHELP, argv[0]);
91 return optget != 'h';
92 }
93 }
94
95 signal_setup();
96 atexit (*quit);
97
98 signal_handler(SIGWINCH); /* initialize window size */
99
100 newgame:
101 screen_setup(1);
102
103 switch(sol()) {
104 case GAME_NEW: goto newgame;
105 case GAME_WON:
106 print_table(NO_HI, NO_HI);
107 win_anim();
108 if (getch(NULL)=='q') return 0;
109 goto newgame;
110 case GAME_QUIT: return 0;
111 }
112 }
113
114 #define is_tableu(where) (where <= TAB_MAX) /* "card games helper functions" */
115
116 int sol(void) {
117 long seed = time(NULL);
118 restart:
119 free_undo(f.u);
120 deal(seed);
121
122 int from, to, opt;
123 int ret;
124 for(;;) {
125 switch (get_cmd(&from, &to, &opt)) {
126 case CMD_MOVE:
127 ret = action[from][to](from,to,opt);
128 #ifdef FREECELL
129 if (ret == ERR && is_tableu(from) && to == from)
130 /* t2f failed? try t2c! */
131 ret = t2c(from, STOCK, 0);
132 #endif
133 #ifdef INVERSE_MOVE
134 if (ret == ERR && is_tableu(from) && is_tableu(to))
135 /* try again with from/to swapped: */
136 ret = action[to][from](to,from,opt);
137 #endif
138 switch (ret) {
139 case OK: break;
140 case ERR: visbell(); break;
141 case WON: return GAME_WON;
142 }
143 break;
144 case CMD_JOIN:
145 switch (join(to)) {
146 case OK: break;
147 case ERR: visbell(); break;
148 case WON: return GAME_WON;
149 }
150 break;
151 case CMD_HINT: break;//TODO: show a possible (and sensible) move. if possible, involve active cursor
152 case CMD_FIND:
153 f.h[0] = getchar(); /* NOTE: not using getch(), so f,esc clears hls */
154 f.h[1] = '\0';
155 break;
156 case CMD_SEARCH:
157 raw_mode(0);
158 printf("\r/"); fflush(stdout);
159 fgets(f.h, 3, stdin);
160 if (f.h[0] != '\n' && f.h[1] != '\n') while(getchar()!='\n'); // note: when we read 1 byte, it is followed by CR, NUL. if we read two bytes (or more), it is only followed by NUL, since there is no space for the CR. TODO: cleanup
161 raw_mode(1);
162 f.h[2] = '\0';
163 break;
164 case CMD_UNDO: undo_pop(f.u); break;
165 case CMD_INVAL: visbell(); break;
166 case CMD_NEW: return GAME_NEW;
167 case CMD_AGAIN: goto restart;
168 case CMD_QUIT: return GAME_QUIT;
169 case CMD_HELP:
170 printf (KEYHELP "\nPress any key to continue.");
171 getch(NULL);
172 break;
173 }
174 }
175 }
176
177 void quit(void) {
178 screen_setup(0);
179 free_undo(f.u);
180 }
181 //}}}
182
183 // card games helper functions {{{
184 #define get_suit(card) \
185 ((card-1) % NUM_SUITS)
186 #define get_rank(card) \
187 ((card-1) / NUM_SUITS)
188 #define get_color(card) \
189 ((get_suit(card) ^ get_suit(card)>>1) & 1)
190
191 int find_top(card_t* pile) {
192 int i;
193 for(i=PILE_SIZE-1; i>=0 && !pile[i]; i--);
194 return i;
195 }
196 int first_movable(card_t* pile) {
197 /* NOTE: in FREECELL this does not take max_move into account! */
198 int i = 0;
199 for (;pile[i] && !is_movable(pile, i); i++);
200 return i;
201 }
202 int turn_over(card_t* pile) {
203 int top = find_top(pile);
204 if (pile[top] < 0) {
205 pile[top] *= -1;
206 return 1;
207 } else return 0;
208 }
209 int check_won(void) {
210 for (int pile = 0; pile < NUM_DECKS*NUM_SUITS; pile++)
211 if (f.f[pile][NUM_RANKS-1] == NO_CARD) return 0;
212
213 return 1;
214 }
215 int rank_next (card_t a, card_t b) {
216 return get_rank(a) == get_rank(b)-1;
217 }
218 int color_ok (card_t a, card_t b) {
219 #if defined KLONDIKE || defined FREECELL
220 /* color opposite? */
221 return (get_color(a) != get_color(b));
222 #elif defined SPIDER
223 /* same suit? */
224 return (get_suit(a) == get_suit(b));
225 #endif
226 }
227 int is_consecutive (card_t* pile, int pos) {
228 if (pos+1 >= PILE_SIZE) return 1; /* card is last */
229 if (pile[pos+1] == NO_CARD) return 1; /* card is first */
230
231 /* ranks consecutive? */
232 if (!rank_next(pile[pos+1], pile[pos])) return 0;
233 /* color/suit OK? */
234 if (!color_ok(pile[pos+1], pile[pos])) return 0;
235
236 return 1;
237 }
238
239 int is_movable(card_t* pile, int n) {
240 #ifdef KLONDIKE
241 return(pile[n] > NO_CARD); /*non-movable cards don't exist in klondike*/
242 #elif defined SPIDER || defined FREECELL
243 int top = find_top(pile);
244 for (int i = top; i >= 0; i--) {
245 if (pile[i] <= NO_CARD) return 0; /*no card or card face down?*/
246 if (!is_consecutive(pile, i)) return 0;
247 if (i == n) return 1; /* card reached, must be movable */
248 }
249 return 0;
250 #endif
251 }
252
253 int hls(card_t card, char* hi) {
254 /* checks if a card matches a highlight search. a hilight search might be a rank, a suit, a color or both. */
255 // TODO: now we use rankletters in keyboard input and here. that's ugly.
256 int ok = 0; /* prevent an invalid highlight from matching everything */
257 for (; *hi; hi++) {
258 switch(*hi) {
259 /* letter ranks: */
260 case 'a': case 'A': if (get_rank(card)!=RANK_A) return 0; ok++; break;
261 case '0':
262 case 'x': case 'X': if (get_rank(card)!=RANK_X) return 0; ok++; break;
263 case 'j': case 'J': if (get_rank(card)!=RANK_J) return 0; ok++; break;
264 case 'q': case 'Q': if (get_rank(card)!=RANK_Q) return 0; ok++; break;
265 case 'k': case 'K': if (get_rank(card)!=RANK_K) return 0; ok++; break;
266
267 /* suits: */
268 case 'c': case 'C': if (get_suit(card)!=CLUBS) return 0; ok++; break;
269 case 'd': case 'D': if (get_suit(card)!=DIAMONDS)return 0;ok++; break;
270 case 'h': case 'H': if (get_suit(card)!=HEARTS) return 0; ok++; break;
271 case 's': case 'S': if (get_suit(card)!=SPADES) return 0; ok++; break;
272
273 /* colours: */
274 case 'r': case 'R': if (get_color(card)!=RED) return 0; ok++; break;
275 case 'b': case 'B': if (get_color(card)!=BLK) return 0; ok++; break;
276
277 /* number ranks: */
278 default:
279 if (*hi < '1' || *hi > '9') continue;
280 if (get_rank(card) != *hi - '1') return 0;
281 ok++;
282 }
283 }
284
285 return ok;
286 }
287 //}}}
288
289 // takeable actions {{{
290 #ifdef KLONDIKE
291 card_t stack_take(void) { /*NOTE: assert(f.w >= 0) */
292 card_t card = f.s[f.w];
293 /* move stack one over, so there are no gaps in it: */
294 for (int i = f.w; i < f.z-1; i++)
295 f.s[i] = f.s[i+1];
296 f.z--;
297 f.w--; /* make previous card visible again */
298 return card;
299 }
300 int t2f(int from, int to, int opt) { /* tableu to foundation */
301 (void) to; (void) opt; /* don't need */
302 int top_from = find_top(f.t[from]);
303 to = get_suit(f.t[from][top_from]);
304 int top_to = find_top(f.f[to]);
305 if ((top_to < 0 && get_rank(f.t[from][top_from]) == RANK_A)
306 || (top_to >= 0 && rank_next(f.f[to][top_to],f.t[from][top_from]))) {
307 f.f[to][top_to+1] = f.t[from][top_from];
308 f.t[from][top_from] = NO_CARD;
309 undo_push(from, FOUNDATION, to,
310 turn_over(f.t[from]));
311 if (check_won()) return WON;
312 return OK;
313 } else return ERR;
314 }
315 int w2f(int from, int to, int opt) { /* waste to foundation */
316 (void) from; (void) to; (void) opt; /* don't need */
317 if (f.w < 0) return ERR;
318 to = get_suit(f.s[f.w]);
319 int top_to = find_top(f.f[to]);
320 if ((top_to < 0 && get_rank(f.s[f.w]) == RANK_A)
321 || (top_to >= 0 && rank_next(f.f[to][top_to], f.s[f.w]))) {
322 undo_push(WASTE, FOUNDATION, f.w | to<<16, 0);//ugly encoding :|
323 f.f[to][top_to+1] = stack_take();
324 if (check_won()) return WON;
325 return OK;
326 } else return ERR;
327
328 }
329 int s2w(int from, int to, int opt) { /* stock to waste */
330 (void) from; (void) to; (void) opt; /* don't need */
331 if (f.z == 0) return ERR;
332 f.w++;
333 if (f.w == f.z) f.w = -1;
334 return OK;
335 }
336 int w2s(int from, int to, int opt) { /* waste to stock (undo stock to waste) */
337 (void) from; (void) to; (void) opt; /* don't need */
338 if (f.z == 0) return ERR;
339 f.w--;
340 if (f.w < -1) f.w = f.z-1;
341 return OK;
342 }
343 int f2t(int from, int to, int opt) { /* foundation to tableu */
344 (void) from; /* don't need */
345 int top_to = find_top(f.t[to]);
346 from = opt;
347 int top_from = find_top(f.f[from]);
348
349 if (color_ok(f.t[to][top_to], f.f[from][top_from])
350 && (rank_next(f.f[from][top_from], f.t[to][top_to]))) {
351 f.t[to][top_to+1] = f.f[from][top_from];
352 f.f[from][top_from] = NO_CARD;
353 undo_push(FOUNDATION, to, from, 0);
354 return OK;
355 } else return ERR;
356 }
357 int w2t(int from, int to, int opt) { /* waste to tableu */
358 (void) from; (void) opt; /* don't need */
359 if (f.w < 0) return ERR;
360 int top_to = find_top(f.t[to]);
361 if ((color_ok(f.t[to][top_to], f.s[f.w])
362 && (rank_next(f.s[f.w], f.t[to][top_to])))
363 || (top_to < 0 && get_rank(f.s[f.w]) == RANK_K)) {
364 undo_push(WASTE, to, f.w, 0);
365 f.t[to][top_to+1] = stack_take();
366 return OK;
367 } else return ERR;
368 }
369 int t2t(int from, int to, int opt) { /* tableu to tableu */
370 (void) opt; /* don't need */
371 int top_to = find_top(f.t[to]);
372 int top_from = find_top(f.t[from]);
373 for (int i = top_from; i >=0; i--) {
374 if ((color_ok(f.t[to][top_to], f.t[from][i])
375 && (rank_next(f.t[from][i], f.t[to][top_to]))
376 && f.t[from][i] > NO_CARD) /* card face up? */
377 || (top_to < 0 && get_rank(f.t[from][i]) == RANK_K)) {
378 /* move cards [i..top_from] to their destination */
379 int count = 0;
380 for (;i <= top_from; i++) {
381 top_to++;
382 f.t[to][top_to] = f.t[from][i];
383 f.t[from][i] = NO_CARD;
384 count++;
385 }
386 undo_push(from, to, count,
387 turn_over(f.t[from]));
388 return OK;
389 }
390 }
391 return ERR; /* no such move possible */
392 }
393 #elif defined SPIDER
394 int remove_if_complete (int pileno) { //cleanup!
395 card_t* pile = f.t[pileno];
396 /* test if K...A complete; move to foundation if so */
397 int top_from = find_top(pile);
398 if (get_rank(pile[top_from]) != RANK_A) return 0;
399 for (int i = top_from; i>=0; i--) {
400 if (!is_consecutive (pile, i)) return 0;
401 if (i+RANK_K == top_from /* if ace to king: remove it */
402 && get_rank(pile[top_from-RANK_K]) == RANK_K) {
403 for(int i=top_from, j=0; i>top_from-NUM_RANKS; i--,j++){
404 f.f[f.w][j] = pile[i];
405 pile[i] = NO_CARD;
406 }
407 undo_push(pileno, FOUNDATION, f.w,
408 turn_over(pile));
409 f.w++;
410 return 1;
411 }
412 }
413
414 return 0;
415 }
416 int t2t(int from, int to, int opt) { //in dire need of cleanup
417 int top_from = find_top(f.t[from]);
418 int top_to = find_top(f.t[to]);
419 int empty_to = (top_to < 0)? opt: -1; /* empty pile? */
420
421 for (int i = top_from; i >= 0; i--) {
422 if (!is_consecutive(f.t[from], i)) break;
423
424 /* is consecutive OR to empty pile and rank ok? */
425 if (rank_next(f.t[from][i], f.t[to][top_to])
426 || (empty_to >= RANK_A && get_rank(f.t[from][i]) == empty_to)) {
427 int count = 0;
428 for (;i <= top_from; i++) {
429 top_to++;
430 f.t[to][top_to] = f.t[from][i];
431 f.t[from][i] = NO_CARD;
432 count++;
433 }
434 undo_push(from, to, count,
435 turn_over(f.t[from]));
436 remove_if_complete(to);
437 if (check_won()) return WON;
438 return OK;
439 }
440 }
441
442 return ERR; /* no such move possible */
443 }
444 int s2t(int from, int to, int opt) {
445 (void) from; (void) to; (void) opt; /* don't need */
446 if (f.z <= 0) return ERR; /* stack out of cards */
447 for (int pile = 0; pile < NUM_PILES; pile++)
448 if (f.t[pile][0]==NO_CARD) return ERR; /*no piles may be empty*/
449
450 undo_push(STOCK, TABLEU, 1, 0); /* NOTE: before remove_if_complete()! */
451 for (int pile = 0; pile < NUM_PILES; pile++) {
452 f.t[pile][find_top(f.t[pile])+1] = f.s[--f.z];
453 remove_if_complete(pile);
454 if (check_won()) return WON;
455 }
456 return OK;
457 }
458 int t2f(int from, int to, int opt) {
459 (void) to; (void) opt; /* don't need */
460 /* manually retrigger remove_if_complete() (e.g. after undo_pop) */
461 return remove_if_complete(from)?OK:ERR;
462 }
463 #elif defined FREECELL
464 int max_move(int from, int to) {
465 /* returns the maximum number of cards that can be moved */
466 /* see also: https://boardgames.stackexchange.com/a/45157/26498 */
467 int free_tabs = 0, free_cells = 0;
468 for (int i = 0; i < NUM_PILES; i++) free_tabs += f.t[i][0] == NO_CARD;
469 for (int i = 0; i < NUM_CELLS; i++) free_cells += f.s[i] == NO_CARD;
470
471 /* don't count the tableau we are moving to: */
472 if (to >= 0 && f.t[to][0] == NO_CARD) free_tabs--;
473
474 /* theoretic maximum is limited by the number of cards on the pile */
475 int max_theory = (1<<free_tabs) * (free_cells + 1);
476 int max_effective = 1 + find_top(f.t[from]) - first_movable(f.t[from]);
477 return max_effective < max_theory? max_effective : max_theory;
478 }
479 //TODO FREECELL: auto move to tableu after each move (not all cards possible, only when it is the smallest rank still on the board)
480 int t2t(int from, int to, int opt) {
481 int top_to = find_top(f.t[to]);
482 int top_from = find_top(f.t[from]);
483 int cards = max_move(from, to);
484 if (top_to < 0) { /* moving to empty pile? */
485 if (opt > cards)
486 return ERR; /* cannot execute move */
487 cards = opt; /* user wants to move n cards*/
488 }
489
490 for (int i = top_from; i >=0; i--) {
491 if (cards-->0/*enough space and not more attempted than wanted*/
492 && ((top_to >= 0 /* if destn. not empty: check rank/color */
493 && (color_ok(f.t[to][top_to], f.t[from][i])
494 && (rank_next(f.t[from][i], f.t[to][top_to]))))
495 || (top_to < 0 && !cards))) {/*if dest empty and right # cards*/
496 /* move cards [i..top_from] to their destination */
497 int count = 0;
498 for (;i <= top_from; i++) {
499 top_to++;
500 f.t[to][top_to] = f.t[from][i];
501 f.t[from][i] = NO_CARD;
502 count++;
503 }
504 undo_push(from, to, count, 0);
505 return OK;
506 }
507 }
508 return ERR; /* no such move possible */
509 }
510 int t2f(int from, int to, int opt) { /* 1:1 copy from KLONDIKE */
511 (void) to; (void) opt; /* don't need */
512 int top_from = find_top(f.t[from]);
513 to = get_suit(f.t[from][top_from]);
514 int top_to = find_top(f.f[to]);
515 if ((top_to < 0 && get_rank(f.t[from][top_from]) == RANK_A)
516 || (top_to >= 0 && rank_next(f.f[to][top_to],f.t[from][top_from]))) {
517 f.f[to][top_to+1] = f.t[from][top_from];
518 f.t[from][top_from] = NO_CARD;
519 undo_push(from, FOUNDATION, to, 0);
520 if (check_won()) return WON;
521 return OK;
522 } else return ERR;
523 }
524 int f2t(int from, int to, int opt) {
525 (void) from; /* don't need */
526 int top_to = find_top(f.t[to]);
527 from = opt;
528 int top_from = find_top(f.f[from]);
529
530 if (top_to < 0 /* empty tableu? */
531 ||(color_ok(f.t[to][top_to], f.f[from][top_from])
532 && (rank_next(f.f[from][top_from], f.t[to][top_to])))) {
533 f.t[to][top_to+1] = f.f[from][top_from];
534 f.f[from][top_from] = NO_CARD;
535 undo_push(FOUNDATION, to, from, 0);
536 return OK;
537 } else return ERR;
538 }
539 int t2c(int from, int to, int opt) {
540 (void) to; (void) opt; /* don't need */
541 /* is a cell free? */
542 if (f.w == (1<<NUM_CELLS)-1)
543 return ERR;
544 for (to = 0; to < NUM_CELLS; to++)
545 if (!(f.w>>to&1)) break;
546 /* move 1 card */
547 int top_from = find_top(f.t[from]);
548 f.s[to] = f.t[from][top_from];
549 f.t[from][top_from] = NO_CARD;
550 f.w |= 1<<to; /* mark cell as occupied */
551 undo_push(from, STOCK, to, 0);
552
553 return OK;
554 }
555 int c2t(int from, int to, int opt) {
556 (void) from; /* don't need */
557 int top_to = find_top(f.t[to]);
558 from = opt;
559
560 if (top_to < 0 /* empty tableu? */
561 ||(color_ok(f.t[to][top_to], f.s[from])
562 && (rank_next(f.s[from], f.t[to][top_to])))) {
563 f.t[to][top_to+1] = f.s[from];
564 f.s[from] = NO_CARD;
565 f.w &= ~(1<<from); /* mark cell as free */
566 undo_push(STOCK, to, from, 0);
567 return OK;
568 } else return ERR;
569 return ERR;
570 }
571 int c2f(int from, int to, int opt) {
572 (void) from; (void) to; /* don't need */
573 from = opt;
574 if (f.s[from] == NO_CARD) return ERR;
575 to = get_suit(f.s[from]);
576 int top_to = find_top(f.f[to]);
577 if ((top_to < 0 && get_rank(f.s[from]) == RANK_A)
578 || (top_to >= 0 && rank_next(f.f[to][top_to],f.s[from]))) {
579 f.f[to][top_to+1] = f.s[from];
580 f.s[from] = NO_CARD;
581 f.w &= ~(1<<from); /* mark cell as free */
582 undo_push(STOCK, FOUNDATION, from | to<<16, 0);
583 if (check_won()) return WON;
584 return OK;
585 } else return ERR;
586 }
587 int f2c(int from, int to, int opt) {
588 (void) from; (void) to; /* don't need */
589 /* is a cell free? */
590 if (f.w == (1<<NUM_CELLS)-1)
591 return ERR;
592 for (to = 0; to < NUM_CELLS; to++)
593 if (!(f.w>>to&1)) break;
594 /* move 1 card */
595 from = opt;
596 int top_from = find_top(f.f[from]);
597 f.s[to] = f.f[from][top_from];
598 f.f[from][top_from] = NO_CARD;
599 f.w |= 1<<to; /* mark cell as occupied */
600 undo_push(FOUNDATION, STOCK, from | to<<16, 0);
601
602 return OK;
603 }
604 #define w2f c2f /* for join()'s "to foundation" */
605 #endif
606
607 //TODO: generalize prediction engine for CMD_HINT
608 #ifdef KLONDIKE
609 #define would_complete(pile) 0
610 #elif defined SPIDER
611 #define would_complete(pile) \
612 (get_rank(f.t[pile][r[pile].top]) == RANK_A \
613 && get_rank(f.t[to][bottom_to]) == RANK_K)
614 #elif defined FREECELL
615 #define would_complete(pile) 0
616 #endif
617 #define would_turn(pile) \
618 (f.t[pile][r[pile].pos-1] < 0)
619 #define would_empty(pile) \
620 (r[pile].pos == 0)
621
622 int join(int to) {
623 int top_to = find_top(f.t[to]);
624 #ifdef SPIDER
625 int bottom_to = first_movable(f.t[to]);
626 #endif
627
628 #if defined KLONDIKE || defined FREECELL
629 if (to == WASTE || to == STOCK) return ERR; /*why would you do that!?*/
630
631 if (to == FOUNDATION) {
632 int status = ERR;
633 for (int i = 0; i < NUM_PILES+NUM_CELLS; i++)
634 switch (is_tableu(i)?t2f(i, FOUNDATION, 0)
635 :w2f(STOCK,FOUNDATION,i-NUM_PILES)){
636 case WON: return WON;
637 case OK: status = OK;
638 case ERR: /* nop */;
639 }
640 return status;
641 }
642 #endif
643
644 #ifdef KLONDIKE
645 if (top_to < 0) { /* move a king to empty pile: */
646 for (int i = 0; i <= TAB_MAX; i++) {
647 if (f.t[i][0] < 0) /* i.e. would turn? */
648 if (t2t(i, to, 0) == OK) return OK;
649 }
650 return w2t(WASTE, to, 0);
651 }
652 #elif defined FREECELL
653 if (top_to < 0) { /* move longest cascade to empty tableu: */ //TODO FREECELL:
654 int longest = -1;
655 int length = -1;
656 for (int i = 0; i <= TAB_MAX; i++) {
657 int m = max_move(i, to);
658 /*longest cascade that won't uncover another free pile*/
659 //TODO: don't rip apart cascades
660 if (m >= length && m <= find_top(f.t[i]))
661 length = m, longest = i;
662 }
663 if (longest < 0) return ERR;
664 return t2t(longest, to, length);
665 }
666 #endif
667
668 struct rating {
669 int ok:1; /* card to move in pile? */
670 int above; /* number of movable cards above */
671 int below; /* number of cards below ours */
672 int pos; /* where the card to move is in the pile */
673 int top; /* find_top() */
674 } r[NUM_PILES] = {{0}};
675 int complete = 0;/* SPIDER: true if any pile would complete a stack */
676 int turn = 0; /* SPIDER: true if any pile would turn_over */
677 int empty = 0; /* true if any pile would become empty */
678
679 /* 1. rate each pile: */
680 #ifdef SPIDER
681 if (top_to < 0) {
682 for (int pile = 0; pile < NUM_PILES; pile++) {
683 if (pile == to) continue;
684 int top = find_top(f.t[pile]);
685 int bottom = first_movable(f.t[pile]);
686 r[pile].pos = bottom; /* need for would_empty */
687
688 if (top < 0) continue; /* no cards to move */
689 if (would_empty(pile)) continue; /* doesn't help */
690
691 r[pile].ok++;
692 r[pile].above = 0; /* always take as many as possible */
693 r[pile].below = top - bottom;
694 r[pile].top = top;
695 complete |= would_complete(pile); /* never happens */
696 turn |= would_turn(pile);
697 empty |= would_empty(pile);
698 }
699 } else
700 #endif
701 for (int pile = 0; pile < NUM_PILES; pile++) {
702 r[pile].top = r[pile].pos = find_top(f.t[pile]);
703 /* backtrack until we find a compatible-to-'to'-pile card: */
704 #ifdef FREECELL
705 int maxmove = max_move(pile, -1);
706 #endif
707 while (r[pile].pos >= 0 && is_movable(f.t[pile], r[pile].pos)) {
708 int rankdiff = get_rank(f.t[pile][r[pile].pos])
709 - get_rank(f.t[to][top_to]);
710 if (rankdiff >= 0) break; /* past our card */
711 #ifdef FREECELL
712 if (!maxmove--) break; /* can't move this many cards */
713 #endif
714 if (rankdiff == -1 && /* rank matches */
715 color_ok(f.t[pile][r[pile].pos], f.t[to][top_to])
716 ) {
717 r[pile].ok++;
718 complete |= would_complete(pile);
719 turn |= would_turn(pile);
720 empty |= would_empty(pile);
721 for (int i = r[pile].pos; i >= 0; i--)
722 if (is_movable(f.t[pile], i-1))
723 r[pile].above++;
724 else break;
725 break;
726 }
727 r[pile].pos--;
728 r[pile].below++;
729 }
730 }
731
732 /* 2. find optimal pile: (optimized for spider) */
733 //todo: in spider, prefer longest piles if above==0 (faster completions)
734 int from = -1;
735 for (int pile = 0, above = 99, below = 99; pile < NUM_PILES; pile++) {
736 if (!r[pile].ok) continue;
737 /* don't bother if another pile would be better: prefer ... */
738 /* ... to complete a stack: */
739 if (!would_complete(pile) && complete) continue;
740 /* ... emptying piles: */
741 if (!would_empty(pile) && empty && !complete) continue;
742 /* ... to turn_over: */
743 if (!would_turn(pile) && turn && !complete && !empty) continue;
744 /* ... not to rip apart too many cards: */
745 if (r[pile].above > above) continue;
746 /* if tied, prefer ... */
747 if (r[pile].above == above
748 /* ... larger pile if destination is empty */
749 && (top_to < 0? r[pile].below < below
750 /* ... shorter pile otherwise */
751 : r[pile].below > below))
752 continue;
753
754 from = pile;
755 above = r[pile].above;
756 below = r[pile].below;
757 }
758
759 /* 3. move cards over and return: */
760 #ifdef KLONDIKE
761 /* prefer waste if it wouldn't turn_over: */
762 /* NOTE: does not attempt to take from froundation */
763 if (!empty && !turn && w2t(WASTE, to, 0) == OK)
764 return OK;
765 if (from < 0) /* nothing found */
766 return ERR;
767 return t2t(from, to, 0);
768 #elif defined SPIDER
769 if (from < 0) /* nothing found */
770 return ERR;
771 int bottom = first_movable(f.t[from]);
772 return t2t(from, to, get_rank(f.t[from][bottom]));
773 #elif defined FREECELL
774 //TODO: if would rip apart, try freecells first (instead after)
775 if (from < 0) /* no tableu move found */ {
776 /* try all free cells before giving up: */
777 for (int i = 0; i < NUM_CELLS; i++)
778 if (c2t(STOCK, to, i) == OK) return OK;
779 return ERR;
780 }
781 return t2t(from, to, 0);
782 #endif
783 }
784 #undef would_empty
785 #undef would_turn
786 #undef would_complete
787 int nop(int from, int to, int opt) { (void)from;(void)to;(void)opt;return ERR; }
788 // }}}
789
790 // keyboard input handling {{{
791 // cursor functions{{{
792 #ifdef KLONDIKE
793 void cursor_left (struct cursor* cursor) {
794 op.h = 1;
795 if (is_tableu(cursor->pile)) {
796 if (cursor->pile > 0) cursor->pile--;
797 cursor->opt = 0;
798 } else { /* stock/waste/foundation*/
799 switch (cursor->pile) {
800 case WASTE: cursor->pile = STOCK; cursor->opt = 0; break;
801 case FOUNDATION:
802 if (cursor->opt <= 0)
803 cursor->pile = WASTE;
804 else
805 cursor->opt--;
806 }
807 }
808 }
809 void cursor_down (struct cursor* cursor) {
810 op.h = 1;
811 if (!is_tableu(cursor->pile)) {
812 switch (cursor->pile) {
813 case STOCK: cursor->pile = TAB_1; break;
814 case WASTE: cursor->pile = TAB_2; break;
815 case FOUNDATION:
816 cursor->pile = TAB_4 + cursor->opt;
817 }
818 cursor->opt = 0;
819 }
820 }
821 void cursor_up (struct cursor* cursor) {
822 op.h = 1;
823 if (is_tableu(cursor->pile)) {
824 switch (cursor->pile) { //ugly :|
825 case TAB_1: cursor->pile = STOCK; break;
826 case TAB_2: cursor->pile = WASTE; break;
827 case TAB_3: cursor->pile = WASTE; break;
828 case TAB_4: case TAB_5: case TAB_6: case TAB_7:
829 cursor->opt=cursor->pile-TAB_4;
830 cursor->pile = FOUNDATION;
831 break;
832 }
833 }
834 }
835 void cursor_right (struct cursor* cursor) {
836 op.h = 1;
837 if (is_tableu(cursor->pile)) {
838 if (cursor->pile < TAB_MAX) cursor->pile++;
839 cursor->opt = 0;
840 } else {
841 switch (cursor->pile) {
842 case STOCK: cursor->pile = WASTE; break;
843 case WASTE: cursor->pile = FOUNDATION;cursor->opt = 0; break;
844 case FOUNDATION:
845 if (cursor->opt < NUM_SUITS-1)
846 cursor->opt++;
847 }
848 }
849 }
850 #elif defined SPIDER
851 /*NOTE: one can't highlight the stock due to me being too lazy to implement it*/
852 void cursor_left (struct cursor* cursor) {
853 op.h = 1;
854 if (cursor->pile > 0) cursor->pile--;
855 cursor->opt = 0;
856 }
857 void cursor_down (struct cursor* cursor) {
858 op.h = 1;
859 int first = first_movable(f.t[cursor->pile]);
860 int top = find_top(f.t[cursor->pile]);
861 if (first + cursor->opt < top)
862 cursor->opt++;
863 }
864 void cursor_up (struct cursor* cursor) {
865 op.h = 1;
866 if (cursor->opt > 0) cursor->opt--;
867 }
868 void cursor_right (struct cursor* cursor) {
869 op.h = 1;
870 if (cursor->pile < TAB_MAX) cursor->pile++;
871 cursor->opt = 0;
872 }
873 #elif defined FREECELL
874 void cursor_left (struct cursor* cursor) {
875 op.h = 1;
876 if (is_tableu(cursor->pile)) {
877 if (cursor->pile > 0) cursor->pile--;
878 cursor->opt = 0;
879 } else { /* cells/foundation*/
880 switch (cursor->pile) {
881 case STOCK:
882 if (cursor->opt > 0)
883 cursor->opt--;
884 break;
885 case FOUNDATION:
886 if (cursor->opt <= 0) {
887 cursor->pile = STOCK;
888 cursor->opt = 3;
889 } else {
890 cursor->opt--;
891 }
892 }
893 }
894 }
895 void cursor_down (struct cursor* cursor) {
896 op.h = 1;
897 if (is_tableu(cursor->pile)) {
898 if (cursor->opt < max_move(cursor->pile, -1)-1)
899 cursor->opt++;
900 } else {
901 cursor->pile = cursor->opt+NUM_CELLS*(cursor->pile==FOUNDATION);
902 cursor->opt = 0;
903 }
904 }
905 void cursor_up (struct cursor* cursor) {
906 op.h = 1;
907 if (is_tableu(cursor->pile)) {
908 if (cursor->opt > 0) {
909 cursor->opt--;
910 } else {
911 switch (cursor->pile) {
912 case TAB_1: case TAB_2: case TAB_3: case TAB_4:
913 cursor->opt = cursor->pile; /*assumes TAB_1==0*/
914 cursor->pile = STOCK;
915 break;
916 case TAB_5: case TAB_6: case TAB_7: case TAB_8:
917 cursor->opt = cursor->pile - NUM_CELLS;
918 cursor->pile = FOUNDATION;
919 }
920 }
921 }
922 }
923 void cursor_right (struct cursor* cursor) {
924 op.h = 1;
925 if (is_tableu(cursor->pile)) {
926 if (cursor->pile < TAB_MAX) cursor->pile++;
927 cursor->opt = 0;
928 } else {
929 switch (cursor->pile) {
930 case STOCK:
931 if (cursor->opt < NUM_SUITS-1) {
932 cursor->opt++;
933 } else {
934 cursor->pile = FOUNDATION;
935 cursor->opt = 0;
936 } break;
937 case FOUNDATION:
938 if (cursor->opt < NUM_SUITS-1)
939 cursor->opt++;
940 }
941 }
942 }
943 #endif
944 void cursor_to (struct cursor* cursor, int pile) {
945 op.h = 1;
946 cursor->pile = pile;
947 cursor->opt = 0;
948 }
949 int set_mouse(int pile, int* main, int* opt) {
950 //TODO: this should set cursor.opt, so card selector choice dialog does not trigger!
951 op.h = 0;
952 if (pile < 0) return 1;
953 *main = pile;
954 #ifdef KLONDIKE
955 if (pile >= FOUNDATION)//TODO: check upper bound!
956 *main = FOUNDATION,
957 *opt = pile - FOUNDATION;
958 #elif defined SPIDER
959 (void)opt;
960 #elif defined FREECELL
961 if (pile > TAB_MAX) {
962 *main = pile-STOCK < NUM_CELLS? STOCK : FOUNDATION;
963 *opt = (pile-STOCK) % 4;
964 }
965 #endif
966 return 0;
967 }
968 //}}}
969 int get_cmd (int* from, int* to, int* opt) {
970 int _f, t;
971 unsigned char mouse[6] = {0}; /* must clear [3]! */
972 struct cursor inactive = {-1,-1};
973 static struct cursor active = {0,0};
974 static char last_successful_action[2] = {0,0}; //TODO: dot implementation should be in main game loop (CMD_AGAIN)
975 if (is_tableu(active.pile))
976 active.opt = 0;
977
978 /***/
979 from_l: print_table(&active, &inactive);
980 _f = getch(mouse);
981
982 switch (_f) {
983 /* direct addressing: */
984 case '1': *from = TAB_1; break;
985 case '2': *from = TAB_2; break;
986 case '3': *from = TAB_3; break;
987 case '4': *from = TAB_4; break;
988 case '5': *from = TAB_5; break;
989 case '6': *from = TAB_6; break;
990 case '7': *from = TAB_7; break;
991 #ifdef SPIDER
992 case '8': *from = TAB_8; break;
993 case '9': *from = TAB_9; break;
994 case '0': *from = TAB_10;break;
995 #elif defined FREECELL
996 case '8': *from = TAB_8; break;
997 case '9': *from = STOCK; break;
998 case '0': *from = FOUNDATION; break;
999 #elif defined KLONDIKE
1000 case '9': *from = WASTE; break;
1001 case '0': *from = FOUNDATION; break;
1002 case '8': /* fallthrough */
1003 #endif
1004 #ifndef FREECELL
1005 case '\n': *from = STOCK; break;
1006 #endif
1007 /* cursor keys addressing: */
1008 case KEY_LEFT:
1009 case 'h': cursor_left (&active); goto from_l;
1010 case KEY_DOWN:
1011 case 'j': cursor_down (&active); goto from_l;
1012 case KEY_UP:
1013 case 'k': cursor_up (&active); goto from_l;
1014 case KEY_RIGHT:
1015 case 'l': cursor_right(&active); goto from_l;
1016 case KEY_HOME:
1017 case 'H': cursor_to(&active,TAB_1); goto from_l; /* leftmost tableu */
1018 case KEY_END:
1019 case 'L': cursor_to(&active,TAB_MAX);goto from_l; /* rigthmost tableu */
1020 case KEY_INS:
1021 case 'M': cursor_to(&active,TAB_MAX/2); goto from_l; /* center tableu */
1022 case ' ': /* continue with second cursor */
1023 *from = active.pile;
1024 #ifdef KLONDIKE
1025 *opt = active.opt; /* when FOUNDATION */
1026 #endif
1027 inactive = active;
1028 break;
1029 /* mouse addressing: */
1030 case MOUSE_MIDDLE: return CMD_NONE;
1031 case MOUSE_RIGHT:
1032 if (set_mouse(term2pile(mouse), to, opt))
1033 return CMD_INVAL;
1034 return CMD_JOIN;
1035 case MOUSE_LEFT:
1036 if (set_mouse(term2pile(mouse), from, opt))
1037 return CMD_INVAL;
1038 if (!is_tableu(*from))
1039 inactive.opt = *opt; /* prevents card selector dialog */
1040 break;
1041 /* misc keys: */
1042 case '.':
1043 ungetc(last_successful_action[1], stdin);
1044 ungetc(last_successful_action[0], stdin); //XXX: 2nd ungetc() not portable!
1045 goto from_l;
1046 case ':':
1047 {char buf[256];
1048 fprintf (stderr, ":");
1049 raw_mode(0); /* turn on echo */
1050 fgets (buf, 256, stdin);
1051 raw_mode(1);
1052 switch(buf[0]) {
1053 case 'q': return CMD_QUIT;
1054 case 'n': return CMD_NEW;
1055 case 'r': return CMD_AGAIN;
1056 case 'h': return CMD_HELP;
1057 default: return CMD_INVAL;
1058 }}
1059 case 'J':
1060 *to = active.pile;
1061 return CMD_JOIN;
1062 case 'K': /* fallthrough */
1063 case '?': return CMD_HINT;
1064 case 'f': return CMD_FIND;
1065 case '/': return CMD_SEARCH;
1066 case 'u': return CMD_UNDO;
1067 case 002: return CMD_NONE; /* sent by SIGWINCH */
1068 case EOF: return CMD_NONE; /* sent by SIGCONT */
1069 default: return CMD_INVAL;
1070 }
1071 inactive.pile = *from; /* for direct addressing highlighting */
1072 if (is_tableu(*from) && f.t[*from][0] == NO_CARD) return CMD_INVAL;
1073 //TODO: freecell: if from==stock && stock[x] == empty: return inval
1074
1075 #ifndef FREECELL
1076 if (*from == STOCK) {
1077 *to = WASTE;
1078 return CMD_MOVE;
1079 }
1080 #endif
1081
1082 /***/
1083 to_l: print_table(&active, &inactive);
1084 t = getch(mouse);
1085
1086 switch (t) {
1087 case KEY_LEFT:
1088 case 'h': cursor_left (&active); goto to_l;
1089 case KEY_DOWN:
1090 case 'j': cursor_down (&active); goto to_l;
1091 case KEY_UP:
1092 case 'k': cursor_up (&active); goto to_l;
1093 case KEY_RIGHT:
1094 case 'l': cursor_right(&active); goto to_l;
1095 case KEY_HOME:
1096 case 'H': cursor_to(&active,TAB_1); goto to_l;
1097 case KEY_END:
1098 case 'L': cursor_to(&active,TAB_MAX); goto to_l;
1099 case KEY_INS:
1100 case 'M': cursor_to(&active,TAB_MAX/2); goto to_l;
1101 case 'J': /* fallthrough; just join selected pile */
1102 case ' ':
1103 *to = active.pile;
1104 break; /* continues with the foundation/empty tableu check */
1105 case MOUSE_MIDDLE:
1106 case MOUSE_RIGHT: return CMD_NONE;
1107 case MOUSE_LEFT:
1108 if (set_mouse(term2pile(mouse), to, opt))
1109 return CMD_INVAL;
1110 break;
1111 case 'K': /* fallthrough */
1112 case '?': return CMD_HINT;
1113 case 'f': return CMD_FIND; // XXX: will cancel from-card
1114 case '/': return CMD_SEARCH; //ditto.
1115 case 'u': return CMD_NONE; /* cancel selection */
1116 case EOF: return CMD_NONE; /* sent by SIGCONT */
1117 default:
1118 if (t < '0' || t > '9') return CMD_INVAL;
1119 if (t == '0')
1120 #ifdef KLONDIKE
1121 *to = FOUNDATION;
1122 #elif defined SPIDER
1123 *to = TAB_10;
1124 #elif defined FREECELL
1125 *to = FOUNDATION;
1126 else if (t == '9')
1127 *to = STOCK;
1128 #endif
1129 else
1130 *to = t-'1';
1131 }
1132 last_successful_action[0] = _f;
1133 last_successful_action[1] = t;
1134
1135 /***/
1136 /* direct addressing post-processing stage:
1137 because foundations/freecells share the same key (and you can't select
1138 partial piles) there are sometimes ambiguous situations where it isn't
1139 clear from which pile (or how many cards) to take. the code below will
1140 only ask the user if there are at least two possible moves and
1141 automatically choose otherwise. */
1142 #ifdef FREECELL
1143 /* if it was selected with a cursor, it's obvious: */
1144 if (inactive.opt >= 0) {
1145 if (is_tableu(*from)) {
1146 /* NOTE: max_move same as in cursor_down() */
1147 *opt = max_move(*from, -1) - inactive.opt;
1148 } else {
1149 *opt = inactive.opt;
1150 }
1151 /* moving from tableu to empty tableu? */
1152 } else if(is_tableu(*from) && is_tableu(*to) && f.t[*to][0] == NO_CARD){
1153 int top = find_top(f.t[*from]);
1154 int max = max_move(*from, *to);
1155 int rank;
1156 if (top < 0) return CMD_INVAL;
1157 if (max == 1) { /* only 1 movable? */
1158 return *opt = 1, CMD_MOVE;
1159 } else { /* only ask the user if it's unclear: */
1160 int bottom = top - (max-1);
1161 printf ("\rup to ([a23456789xjqk] or space/return): ");
1162 rank = getch(NULL);
1163 switch (rank) {
1164 case ' ': rank = get_rank(f.t[*from][top]); break;
1165 case'\n': rank = get_rank(f.t[*from][bottom]); break;
1166 case 'a': case 'A': rank = RANK_A; break;
1167 case '0': /* fallthrough */
1168 case 'x': case 'X': rank = RANK_X; break;
1169 case 'j': case 'J': rank = RANK_J; break;
1170 case 'q': case 'Q': rank = RANK_Q; break;
1171 case 'k': case 'K': rank = RANK_K; break;
1172 default: rank -= '1';
1173 }
1174 if (rank < RANK_A || rank > RANK_K) return CMD_INVAL;
1175
1176 for (int i = 0; max--; i++)
1177 if (get_rank(f.t[*from][top-i]) == rank)
1178 return *opt = 1+i, CMD_MOVE;
1179
1180 return CMD_INVAL;
1181 }
1182 /* `opt` is the number of cards to move */
1183 /* moving between stock/foundation? */
1184 } else if (*from == FOUNDATION && *to == FOUNDATION) {
1185 return CMD_INVAL; /* nonsensical */
1186 } else if (*from == FOUNDATION && *to == STOCK) {
1187 if (f.w == (1<<NUM_CELLS)-1) return CMD_INVAL; /*no free cells*/
1188 int ok_foundation; /* find compatible (non-empty) foundations:*/
1189 int used_fs=0; for (int i = 0; i < NUM_SUITS; i++)
1190 if (!!f.f[i][0]) ok_foundation = i, used_fs++;
1191
1192 if (used_fs == 0) return CMD_INVAL; /* nowhere to take from */
1193 if (used_fs == 1) { /* take from the only one */
1194 return *opt = ok_foundation, CMD_MOVE;
1195 } else { /* ask user */
1196 printf ("take from (1-4): "); fflush (stdout);
1197 *opt = getch(NULL) - '1';
1198 if (*opt < 0 || *opt > 3) return CMD_INVAL;
1199 }
1200 /* `opt` is the foundation index (0..3) */
1201 } else if (*from == STOCK) { /* cell -> foundation/tableu */
1202 if (!f.w) return CMD_INVAL; /* no cell to take from */
1203 int ok_cell; /* find compatible (non-empty) cells: */
1204 int tab = is_tableu(*to);
1205 int used_cs=0; for (int i = 0; i < NUM_CELLS; i++) {
1206 card_t* pile = (tab?f.t[*to]:f.f[get_suit(f.s[i])]);
1207 int top_to = find_top(pile);
1208 if (tab? /* to tableu? */
1209 ((top_to<0 && f.s[i] > NO_CARD)
1210 ||(top_to>=0 && rank_next(f.s[i], pile[top_to])
1211 && color_ok(f.s[i], pile[top_to])))
1212 : /* to foundation? */
1213 ((top_to<0 && get_rank(f.s[i]) == RANK_A)
1214 ||(top_to>=0 && rank_next(pile[top_to],f.s[i])))
1215 )
1216 ok_cell = i, used_cs++;
1217 }
1218
1219 if (used_cs == 0) return CMD_INVAL; /* nowhere to take from */
1220 if (used_cs == 1) { /* take from the only one */
1221 return *opt = ok_cell, CMD_MOVE;
1222 } else { /* ask user */
1223 printf ("take from (1-4): "); fflush (stdout);
1224 *opt = getch(NULL) - '1';
1225 if (*opt < 0 || *opt > 3) return CMD_INVAL;
1226 }
1227 /* `opt` is the cell index (0..3) */
1228 } else
1229 #endif
1230 //TODO: mouse-friendly "up to?" dialog
1231 #if defined KLONDIKE || defined FREECELL
1232 if (*from == FOUNDATION) {
1233 if (inactive.opt >= 0) {
1234 *opt = inactive.opt;
1235 return CMD_MOVE;
1236 }
1237 int top = find_top(f.t[*to]);
1238 if (top < 0) return CMD_INVAL;
1239 int color = get_color(f.t[*to][top]);
1240 int choice_1 = 1-color; /* selects piles of */
1241 int choice_2 = 2+color; /* the opposite color */
1242 int top_c1 = find_top(f.f[choice_1]);
1243 int top_c2 = find_top(f.f[choice_2]);
1244
1245 switch ((rank_next(f.f[choice_1][top_c1], f.t[*to][top])
1246 && top_c1 >= 0 ) << 0
1247 |(rank_next(f.f[choice_2][top_c2], f.t[*to][top])
1248 && top_c2 >= 0 ) << 1) {
1249 case ( 1<<0): *opt = choice_1; break; /* choice_1 only */
1250 case (1<<1 ): *opt = choice_2; break; /* choice_2 only */
1251 case (1<<1 | 1<<0): /* both, ask user which to pick from */
1252 printf ("take from (1-4): "); fflush (stdout);
1253 *opt = getch(NULL) - '1';
1254 if (*opt < 0 || *opt > 3) return CMD_INVAL;
1255 break;
1256 default: return CMD_INVAL; /* none matched */
1257 }
1258 /* `opt` is the foundation index (0..3) */
1259 }
1260 #elif defined SPIDER
1261 /* moving to empty tableu? */
1262 if (is_tableu(*to) && f.t[*to][0] == NO_CARD) {
1263 int bottom = first_movable(f.t[*from]);
1264 if (inactive.opt >= 0) { /*if from was cursor addressed: */
1265 *opt = get_rank(f.t[*from][bottom + inactive.opt]);
1266 return CMD_MOVE;
1267 }
1268 int top = find_top(f.t[*from]);
1269 if (top < 0) return CMD_INVAL;
1270 if (top >= 0 && !is_movable(f.t[*from], top-1)) {
1271 *opt = get_rank(f.t[*from][top]);
1272 } else { /* only ask the user if it's unclear: */
1273 printf ("\rup to ([a23456789xjqk] or space/return): ");
1274 *opt = getch(NULL);
1275 switch (*opt) {
1276 case ' ': *opt = get_rank(f.t[*from][top]); break;
1277 case'\n': *opt = get_rank(f.t[*from][bottom]); break;
1278 case 'a': case 'A': *opt = RANK_A; break;
1279 case '0': /* fallthrough */
1280 case 'x': case 'X': *opt = RANK_X; break;
1281 case 'j': case 'J': *opt = RANK_J; break;
1282 case 'q': case 'Q': *opt = RANK_Q; break;
1283 case 'k': case 'K': *opt = RANK_K; break;
1284 default: *opt -= '1';
1285 }
1286 if (*opt < RANK_A || *opt > RANK_K) return CMD_INVAL;
1287 }
1288 /* `opt` is the rank of the highest card to move */
1289 }
1290 #endif
1291 return CMD_MOVE;
1292 }
1293
1294 int getctrlseq(unsigned char* buf) {
1295 int c;
1296 enum esc_states {
1297 START,
1298 ESC_SENT,
1299 CSI_SENT,
1300 MOUSE_EVENT,
1301 } state = START;
1302 int offset = 0x20; /* never sends control chars as data */
1303 while ((c = getchar()) != EOF) {
1304 switch (state) {
1305 case START:
1306 switch (c) {
1307 case '\033': state=ESC_SENT; break;
1308 default: return c;
1309 }
1310 break;
1311 case ESC_SENT:
1312 switch (c) {
1313 case '[': state=CSI_SENT; break;
1314 default: return KEY_INVAL;
1315 }
1316 break;
1317 case CSI_SENT:
1318 switch (c) {
1319 case 'A': return KEY_UP;
1320 case 'B': return KEY_DOWN;
1321 case 'C': return KEY_RIGHT;
1322 case 'D': return KEY_LEFT;
1323 /*NOTE: home/end send ^[[x~ . no support for modifiers*/
1324 case 'H': return KEY_HOME;
1325 case 'F': return KEY_END;
1326 case '2': getchar(); return KEY_INS;
1327 case '5': getchar(); return KEY_PGUP;
1328 case '6': getchar(); return KEY_PGDN;
1329 case 'M': state=MOUSE_EVENT; break;
1330 default: return KEY_INVAL;
1331 }
1332 break;
1333 case MOUSE_EVENT:
1334 if (buf == NULL) return KEY_INVAL;
1335 buf[0] = c - offset;
1336 buf[1] = getchar() - offset;
1337 buf[2] = getchar() - offset;
1338 return MOUSE_ANY;
1339 default:
1340 return KEY_INVAL;
1341 }
1342 }
1343 return 2;
1344 }
1345 int term2pile(unsigned char *mouse) {
1346 int line = (mouse[2]-1);
1347 int column = (mouse[1]-1) / op.s->width;
1348
1349 if (line < op.s->height) { /* first line */
1350 #ifdef KLONDIKE
1351 switch (column) {
1352 case 0: return STOCK;
1353 case 1: return WASTE;
1354 case 2: return -1; /* spacer */
1355 case 3: return FOUNDATION+0;
1356 case 4: return FOUNDATION+1;
1357 case 5: return FOUNDATION+2;
1358 case 6: return FOUNDATION+3;
1359 }
1360 #elif defined SPIDER
1361 if (column < 3) return STOCK;
1362 return -1;
1363 #elif defined FREECELL
1364 if (column < NUM_SUITS + NUM_CELLS) return STOCK+column;
1365 return -1;
1366 #endif
1367 } else if (line > op.s->height) { /* tableu */
1368 if (column <= TAB_MAX) return column;
1369 }
1370 return -1;
1371 }
1372 int wait_mouse_up(unsigned char* mouse) {
1373 //TODO: mouse drag: start gets inactive, hovering gets active cursors
1374 struct cursor cur = {-1,-1};
1375 int level = 1;
1376 /* note: if dragged [3]==1 and second position is in mouse[0,4,5] */
1377
1378 /* display a cursor while mouse button is pushed: */
1379 int pile = term2pile(mouse);
1380 cur.pile = pile;
1381 #ifdef KLONDIKE
1382 if (pile >= FOUNDATION) {
1383 cur.pile = FOUNDATION;
1384 cur.opt = pile-FOUNDATION;
1385 }
1386 #elif defined FREECELL
1387 if (pile > TAB_MAX) {
1388 cur.pile = pile-STOCK < NUM_CELLS? STOCK : FOUNDATION;
1389 cur.opt = (pile-STOCK) % 4;
1390 }
1391 #endif
1392 /* need to temporarily show the cursor, then revert to last state: */
1393 int old_show_cursor_hi = op.h; //TODO: ARGH! that's awful!
1394 op.h = 1;
1395 print_table(&cur, NO_HI); //TODO: should not overwrite inactive cursor!
1396 op.h = old_show_cursor_hi;
1397
1398 while (level > 0) {
1399 if (getctrlseq (mouse+3) == MOUSE_ANY) {
1400 /* ignore mouse wheel events: */
1401 if (mouse[3] & 0x40) continue;
1402
1403 else if((mouse[3]&3) == 3) level--; /* release event */
1404 else level++; /* another button pressed */
1405 }
1406 }
1407
1408 int success = mouse[1] == mouse[4] && mouse[2] == mouse[5];
1409 if (success) {
1410 mouse[3] = 0;
1411 }
1412 return success;
1413 }
1414
1415 int getch(unsigned char* buf) {
1416 //TODO: if buf==NULL disable mouse input
1417 /* returns a character, EOF, or constant for an escape/control sequence - NOT
1418 compatible with the ncurses implementation of same name */
1419 int action;
1420 if (buf && buf[3]) {
1421 /* mouse was dragged; return 'ungetted' previous destination */
1422 action = MOUSE_DRAG;
1423 /* keep original [0], as [3] only contains release event */
1424 buf[1] = buf[4];
1425 buf[2] = buf[5];
1426 buf[3] = 0;
1427 } else {
1428 action = getctrlseq(buf);
1429 }
1430
1431 switch (action) {
1432 case MOUSE_ANY:
1433 if (buf[0] > 3) break; /* ignore wheel events */
1434 wait_mouse_up(buf);
1435 /* fallthrough */
1436 case MOUSE_DRAG:
1437 switch (buf[0]) {
1438 case 0: return MOUSE_LEFT;
1439 case 1: return MOUSE_MIDDLE;
1440 case 2: return MOUSE_RIGHT;
1441 }
1442 }
1443
1444 return action;
1445 }
1446 // }}}
1447
1448 // shuffling and dealing {{{
1449 void deal(long seed) {
1450 //TODO: clear hls/f.h
1451 f = (const struct playfield){0}; /* clear playfield */
1452 card_t deck[DECK_SIZE*NUM_DECKS];
1453 int avail = DECK_SIZE*NUM_DECKS;
1454 for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) deck[i] = (i%DECK_SIZE)+1;
1455 #ifdef SPIDER
1456 if (op.m != NORMAL) for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) {
1457 if (op.m == MEDIUM) deck[i] = 1+((deck[i]-1) | 2);
1458 if (op.m == EASY) deck[i] = 1+((deck[i]-1) | 2 | 1);
1459 /* the 1+ -1 dance gets rid of the offset created by NO_CARD */
1460 }
1461 #endif
1462 srand (seed);
1463 for (int i = DECK_SIZE*NUM_DECKS-1; i > 0; i--) { /* fisher-yates */
1464 int j = rand() % (i+1);
1465 if (j-i) deck[i]^=deck[j],deck[j]^=deck[i],deck[i]^=deck[j];
1466 }
1467
1468 /* deal cards: */
1469 for (int i = 0; i < NUM_PILES; i++) {
1470 #ifdef KLONDIKE
1471 #define SIGN -
1472 int count = i; /* pile n has n closed cards, then 1 open */
1473 #elif defined SPIDER
1474 #define SIGN -
1475 int count = i<4?5:4; /* pile 1-4 have 5, 5-10 have 4 closed */
1476 #elif defined FREECELL
1477 #define SIGN +
1478 int count = i<4?6:5;/*like spider, but cards are dealt face-up*/
1479 #endif
1480 /* "SIGN": face down cards are negated */
1481 for (int j = 0; j < count; j++) f.t[i][j] = SIGN deck[--avail];
1482 f.t[i][count] = deck[--avail]; /* the face-up card */
1483 #undef SIGN
1484 }
1485 /* rest of the cards to the stock: */
1486 /* NOTE: assert(avail==50) for spider, assert(avail==0) for freecell */
1487 for (f.z = 0; avail; f.z++) f.s[f.z] = deck[--avail];
1488 #ifdef KLONDIKE
1489 f.w = -1; /* @start: nothing on waste */
1490 #elif defined SPIDER
1491 f.w = 0; /* number of used foundations */
1492 #elif defined FREECELL
1493 f.w = 0; /* bitmask of used free cells */
1494 #endif
1495
1496 f.u = &undo_sentinel;
1497 }
1498 //}}}
1499
1500 // screen drawing routines {{{
1501 void print_hi(int invert, int grey_bg, int bold, int blink, char* str) {
1502 if (!op.h) invert = 0; /* don't show invert if we used the mouse last */
1503 if (bold && op.s == &unicode_large_color){ //awful hack for bold + faint
1504 int offset = str[3]==017?16:str[4]==017?17:0;
1505 printf ("%s%s%s%s""%.*s%s%s""%s%s%s%s",
1506 "\033[1m", invert?"\033[7m":"", grey_bg?"\033[100m":"", blink?"\033[5m":"",
1507 offset, str, bold?"\033[1m":"", str+offset,
1508 blink?"\033[25m":"", grey_bg?"\033[49m":"", invert?"\033[27m":"","\033[22m");
1509 return;
1510 }
1511 printf ("%s%s%s%s%s%s%s%s%s",
1512 bold?"\033[1m":"", invert?"\033[7m":"", grey_bg?"\033[100m":"", blink?"\033[5m":"",
1513 str,
1514 blink?"\033[25m":"", grey_bg?"\033[49m":"", invert?"\033[27m":"",bold?"\033[22m":"");
1515 }
1516 void print_table(const struct cursor* active, const struct cursor* inactive) {
1517 int do_blink = 0; //XXX: remove
1518 printf("\033[2J\033[H"); /* clear screen, reset cursor */
1519 #ifdef KLONDIKE
1520 /* print stock, waste and foundation: */
1521 for (int line = 0; line < op.s->height; line++) {
1522 /* stock: */
1523 print_hi (active->pile == STOCK, inactive->pile == STOCK, 1, do_blink, (
1524 (f.w < f.z-1)?op.s->facedown
1525 :op.s->placeholder)[line]);
1526 /* waste: */
1527 print_hi (active->pile == WASTE, inactive->pile == WASTE, 1, do_blink, (
1528 /* NOTE: cast, because f.w sometimes is (short)-1 !? */
1529 ((short)f.w >= 0)?op.s->card[f.s[f.w]]
1530 :op.s->placeholder)[line]);
1531 printf ("%s", op.s->card[NO_CARD][line]); /* spacer */
1532 /* foundation: */
1533 for (int pile = 0; pile < NUM_SUITS; pile++) {
1534 int card = find_top(f.f[pile]);
1535 print_hi (active->pile==FOUNDATION && active->opt==pile,
1536 inactive->pile==FOUNDATION && (
1537 /* cursor addr. || direct addr. */
1538 inactive->opt==pile || inactive->opt < 0
1539 ), !!f.f[pile][0], do_blink,
1540 (card < 0)?op.s->foundation[line]
1541 :op.s->card[f.f[pile][card]][line]);
1542 }
1543 printf("\n");
1544 }
1545 printf("\n");
1546 #elif defined SPIDER
1547 int fdone; for (fdone = NUM_DECKS*NUM_SUITS; fdone; fdone--)
1548 if (f.f[fdone-1][RANK_K]) break; /*number of completed stacks*/
1549 int spacer_from = f.z?(f.z/10-1) * op.s->halfwidth[0] + op.s->width:0;
1550 int spacer_to = NUM_PILES*op.s->width -
1551 ((fdone?(fdone-1) * op.s->halfwidth[1]:0)+op.s->width);
1552 for (int line = 0; line < op.s->height; line++) {
1553 /* available stock: */
1554 for (int i = f.z/10; i; i--) {
1555 if (i==1) printf ("%s", op.s->facedown[line]);
1556 else printf ("%s", op.s->halfstack[line]);
1557 }
1558 /* spacer: */
1559 for (int i = spacer_from; i < spacer_to; i++) printf (" ");
1560 /* foundation (overlapping): */
1561 for (int i = NUM_DECKS*NUM_SUITS-1, half = 0; i >= 0; i--) {
1562 int overlap = half? op.s->halfcard[line]: 0;
1563 if (f.f[i][RANK_K]) printf ("%.*s", op.s->halfwidth[2],
1564 op.s->card[f.f[i][RANK_K]][line]+overlap),
1565 half++;
1566 }
1567 printf("\n");
1568 }
1569 printf("\n");
1570 #elif defined FREECELL
1571 /* print open cells, foundation: */
1572 for (int line = 0; line < op.s->height; line++) {
1573 for (int pile = 0; pile < NUM_CELLS; pile++)
1574 print_hi (active->pile==STOCK && active->opt==pile,
1575 inactive->pile==STOCK && (
1576 /* cursor addr. || direct addr. */
1577 inactive->opt==pile || inactive->opt < 0
1578 ), !!f.s[pile], do_blink,
1579 ((f.s[pile])?op.s->card[f.s[pile]]
1580 :op.s->placeholder)[line]);
1581 for (int pile = 0; pile < NUM_SUITS; pile++) {
1582 int card = find_top(f.f[pile]);
1583 print_hi (active->pile==FOUNDATION && active->opt==pile,
1584 inactive->pile==FOUNDATION && (
1585 /* cursor addr. || direct addr. */
1586 inactive->opt==pile || inactive->opt < 0
1587 ), !!f.f[pile][0], do_blink,
1588 (card < 0)?op.s->foundation[line]
1589 :op.s->card[f.f[pile][card]][line]);
1590 }
1591 printf("\n");
1592 }
1593 printf("\n");
1594 #endif
1595 #ifdef KLONDIKE
1596 #define DO_HI(cursor) (cursor->pile == pile && (movable || empty))
1597 #define TOP_HI(c) 1 /* can't select partial stacks in KLONDIKE */
1598 #elif defined SPIDER || defined FREECELL
1599 int offset[NUM_PILES]={0}; /* first card to highlight */
1600 # ifdef FREECELL
1601 int bottom[NUM_PILES]; /* first movable card */
1602 for (int i=0; i<NUM_PILES; i++)
1603 bottom[i] = find_top(f.t[i]) - max_move(i,-1);
1604 # endif
1605 #define DO_HI(cursor) (cursor->pile == pile && (movable || empty) \
1606 && offset[pile] >= cursor->opt)
1607 #define TOP_HI(cursor) (cursor->pile == pile && movable \
1608 && offset[pile] == cursor->opt)
1609 #endif
1610 /* print tableu piles: */
1611 int row[NUM_PILES] = {0};
1612 int line[NUM_PILES]= {0};
1613 int label[NUM_PILES]={0};
1614 int line_had_card;
1615 int did_placeholders = 0;
1616 do {
1617 line_had_card = 0;
1618 for (int pile = 0; pile < NUM_PILES; pile++) {
1619 card_t card = f.t[pile][row[pile]];
1620 card_t next = f.t[pile][row[pile]+1];
1621 int movable = is_movable(f.t[pile], row[pile]);
1622 int do_blink = hls(card, f.h);
1623 #ifdef FREECELL
1624 if(row[pile] <= bottom[pile]) movable = 0;
1625 #endif
1626 int empty = !card && row[pile] == 0;
1627
1628 print_hi (DO_HI(active), DO_HI(inactive), movable, do_blink, (
1629 (!card && row[pile] == 0)?op.s->placeholder
1630 :(card<0)?op.s->facedown
1631 :op.s->card[card]
1632 )[line[pile]]);
1633
1634 int extreme_overlap = ( 3 /* spacer, labels, status */
1635 + 2 * op.s->height /* stock, top tableu card */
1636 + find_top(f.t[pile]) * op.s->overlap) >op.w[0];
1637 /* normal overlap: */
1638 if (++line[pile] >= (next?op.s->overlap:op.s->height)
1639 /* extreme overlap on closed cards: */
1640 || (extreme_overlap &&
1641 line[pile] >= 1 &&
1642 f.t[pile][row[pile]] < 0 &&
1643 f.t[pile][row[pile]+1] <0)
1644 /* extreme overlap on sequences: */
1645 || (extreme_overlap &&
1646 !TOP_HI(active) && /*always show top selected card*/
1647 line[pile] >= 1 && row[pile] > 0 &&
1648 f.t[pile][row[pile]-1] > NO_CARD &&
1649 is_consecutive (f.t[pile], row[pile]) &&
1650 is_consecutive (f.t[pile], row[pile]-1) &&
1651 f.t[pile][row[pile]+1] != NO_CARD)
1652 ) {
1653 line[pile]=0;
1654 row[pile]++;
1655 #if defined SPIDER || defined FREECELL
1656 if (movable) offset[pile]++;
1657 #endif
1658 }
1659 /* tableu labels: */
1660 if(!card && !label[pile] && row[pile]>0&&line[pile]>0) {
1661 label[pile] = 1;
1662 printf ("\b\b%d ", (pile+1) % 10); //XXX: hack
1663 }
1664 line_had_card |= !!card;
1665 did_placeholders |= row[pile] > 0;
1666 }
1667 printf ("\n");
1668 } while (line_had_card || !did_placeholders);
1669 }
1670
1671 void visbell (void) {
1672 if (!op.v) return;
1673 printf ("\033[?5h"); fflush (stdout);
1674 usleep (100000);
1675 printf ("\033[?5l"); fflush (stdout);
1676 }
1677 void win_anim(void) {
1678 printf ("\033[?25l"); /* hide cursor */
1679 for (;;) {
1680 /* set cursor to random location */
1681 int row = 1+rand()%(1+op.w[0]-op.s->height);
1682 int col = 1+rand()%(1+op.w[1]-op.s->width);
1683
1684 /* draw random card */
1685 int face = 1 + rand() % 52;
1686 for (int l = 0; l < op.s->height; l++) {
1687 printf ("\033[%d;%dH", row+l, col);
1688 printf ("%s", op.s->card[face][l]);
1689 }
1690 fflush (stdout);
1691
1692 /* exit on keypress */
1693 struct pollfd p = {STDIN_FILENO, POLLIN, 0};
1694 if (poll (&p, 1, 80)) goto fin;
1695 }
1696 fin:
1697 printf ("\033[?25h"); /* show cursor */
1698 return;
1699 }
1700 //}}}
1701
1702 // undo logic {{{
1703 void undo_push (int _f, int t, int n, int o) {
1704 struct undo* new = malloc(sizeof(struct undo));
1705 new->f = _f;
1706 new->t = t;
1707 new->n = n;
1708 new->o = o;
1709 new->prev = f.u;
1710 new->next = NULL;
1711 f.u->next = new;
1712 f.u = f.u->next;
1713 }
1714 void undo_pop (struct undo* u) {
1715 if (u == &undo_sentinel) return;
1716
1717 #ifdef KLONDIKE
1718 if (u->f == FOUNDATION) {
1719 /* foundation -> tableu */
1720 int top_f = find_top(f.f[u->n]);
1721 int top_t = find_top(f.t[u->t]);
1722 f.f[u->n][top_f+1] = f.t[u->t][top_t];
1723 f.t[u->t][top_t] = NO_CARD;
1724 } else if (u->f == WASTE && u->t == FOUNDATION) {
1725 /* waste -> foundation */
1726 /* split u->n into wst and fnd: */
1727 int wst = u->n & 0xffff;
1728 int fnd = u->n >> 16;
1729 /* move stock cards one position up to make room: */
1730 for (int i = f.z; i >= wst; i--) f.s[i+1] = f.s[i];
1731 /* move one card from foundation to waste: */
1732 int top = find_top(f.f[fnd]);
1733 f.s[wst] = f.f[fnd][top];
1734 f.f[fnd][top] = NO_CARD;
1735 f.z++;
1736 f.w++;
1737 } else if (u->f == WASTE) {
1738 /* waste -> tableu */
1739 /* move stock cards one position up to make room: */
1740 for (int i = f.z-1; i >= u->n; i--) f.s[i+1] = f.s[i];
1741 /* move one card from tableu to waste: */
1742 int top = find_top(f.t[u->t]);
1743 f.s[u->n] = f.t[u->t][top];
1744 f.t[u->t][top] = NO_CARD;
1745 f.z++;
1746 f.w++;
1747 } else if (u->t == FOUNDATION) {
1748 /* tableu -> foundation */
1749 int top_f = find_top(f.t[u->f]);
1750 int top_t = find_top(f.f[u->n]);
1751 /* close topcard if previous action caused turn_over(): */
1752 if (u->o) f.t[u->f][top_f] *= -1;
1753 /* move one card from foundation to tableu: */
1754 f.t[u->f][top_f+1] = f.f[u->n][top_t];
1755 f.f[u->n][top_t] = NO_CARD;
1756 } else {
1757 /* tableu -> tableu */
1758 int top_f = find_top(f.t[u->f]);
1759 int top_t = find_top(f.t[u->t]);
1760 /* close topcard if previous action caused turn_over(): */
1761 if (u->o) f.t[u->f][top_f] *= -1;
1762 /* move n cards from tableu[f] to tableu[t]: */
1763 for (int i = 0; i < u->n; i++) {
1764 f.t[u->f][top_f+u->n-i] = f.t[u->t][top_t-i];
1765 f.t[u->t][top_t-i] = NO_CARD;
1766 }
1767 }
1768 #elif defined SPIDER
1769 if (u->f == STOCK) {
1770 /* stock -> tableu */
1771 /*remove a card from each pile and put it back onto the stock:*/
1772 for (int pile = NUM_PILES-1; pile >= 0; pile--) {
1773 int top = find_top(f.t[pile]);
1774 f.s[f.z++] = f.t[pile][top];
1775 f.t[pile][top] = NO_CARD;
1776 }
1777 } else if (u->t == FOUNDATION) {
1778 /* tableu -> foundation */
1779 int top = find_top(f.t[u->f]);
1780 /* close topcard if previous action caused turn_over(): */
1781 if (u->o) f.t[u->f][top] *= -1;
1782 /* append cards from foundation to tableu */
1783 for (int i = RANK_K; i >= RANK_A; i--) {
1784 f.t[u->f][++top] = f.f[u->n][i];
1785 f.f[u->n][i] = NO_CARD;
1786 }
1787 f.w--; /* decrement complete-foundation-counter */
1788
1789 } else {
1790 /* tableu -> tableu */
1791 int top_f = find_top(f.t[u->f]);
1792 int top_t = find_top(f.t[u->t]);
1793 /* close topcard if previous action caused turn_over(): */
1794 if (u->o) f.t[u->f][top_f] *= -1;
1795 /* move n cards from tableu[f] to tableu[t]: */
1796 for (int i = 0; i < u->n; i++) {
1797 f.t[u->f][top_f+u->n-i] = f.t[u->t][top_t-i];
1798 f.t[u->t][top_t-i] = NO_CARD;
1799 }
1800 }
1801 #elif defined FREECELL
1802 /*NOTE: if from and to are both stock/foundation, opt = from | to<<16 */
1803 if (u->f == STOCK && u->t == FOUNDATION) {
1804 /* free cells -> foundation */
1805 /* split u->n into cll and fnd: */
1806 int cll = u->n & 0xffff;
1807 int fnd = u->n >> 16;
1808 /* move one card from foundation to free cell: */
1809 int top = find_top(f.f[fnd]);
1810 f.s[cll] = f.f[fnd][top];
1811 f.f[fnd][top] = NO_CARD;
1812 f.w |= 1<<cll; /* mark cell as occupied */
1813 } else if (u->f == STOCK) {
1814 /* free cells -> cascade */
1815 int top_t = find_top(f.t[u->t]);
1816 f.s[u->n] = f.t[u->t][top_t];
1817 f.t[u->t][top_t] = NO_CARD;
1818 f.w |= 1<<u->n; /* mark cell as occupied */
1819 } else if (u->f == FOUNDATION && u->t == STOCK) {
1820 /* foundation -> free cells */
1821 /* split u->n into cll and fnd: */
1822 int cll = u->n >> 16;
1823 int fnd = u->n & 0xffff;
1824 /* move 1 card from free cell to foundation: */
1825 int top_f = find_top(f.f[fnd]);
1826 f.f[fnd][top_f+1] = f.s[cll];
1827 f.s[cll] = NO_CARD;
1828 f.w &= ~(1<<cll); /* mark cell as free */
1829 } else if (u->f == FOUNDATION) {
1830 /* foundation -> cascade */
1831 int top_f = find_top(f.f[u->n]);
1832 int top_t = find_top(f.t[u->t]);
1833 f.f[u->n][top_f+1] = f.t[u->t][top_t];
1834 f.t[u->t][top_t] = NO_CARD;
1835 } else if (u->t == STOCK) {
1836 /* cascade -> free cells */
1837 int top_f = find_top(f.t[u->f]);
1838 f.t[u->f][top_f+1] = f.s[u->n];
1839 f.s[u->n] = NO_CARD;
1840 f.w &= ~(1<<u->n); /* mark cell as free */
1841 } else if (u->t == FOUNDATION) {
1842 /* cascade -> foundation */
1843 int top_f = find_top(f.t[u->f]);
1844 int top_t = find_top(f.f[u->n]);
1845 /* move one card from foundation to cascade: */
1846 f.t[u->f][top_f+1] = f.f[u->n][top_t];
1847 f.f[u->n][top_t] = NO_CARD;
1848 } else {
1849 /* cascade -> cascade */
1850 int top_f = find_top(f.t[u->f]);
1851 int top_t = find_top(f.t[u->t]);
1852 /* move n cards from tableu[f] to tableu[t]: */
1853 for (int i = 0; i < u->n; i++) {
1854 f.t[u->f][top_f+u->n-i] = f.t[u->t][top_t-i];
1855 f.t[u->t][top_t-i] = NO_CARD;
1856 }
1857 }
1858 #endif
1859
1860 void* old = f.u;
1861 f.u = f.u->prev;
1862 free(old);
1863 }
1864 void free_undo (struct undo* u) {
1865 while (u && u != &undo_sentinel) {
1866 void* old = u;
1867 u = u->prev;
1868 free (old);
1869 }
1870 }
1871 //}}}
1872
1873 // initialization stuff {{{
1874 void screen_setup (int enable) {
1875 if (enable) {
1876 raw_mode(1);
1877 printf ("\033[s\033[?47h"); /* save cursor, alternate screen */
1878 printf ("\033[H\033[J"); /* reset cursor, clear screen */
1879 printf ("\033[?1000h"); /* enable mouse */
1880 } else {
1881 printf ("\033[?1000l"); /* disable mouse */
1882 printf ("\033[?47l\033[u"); /* primary screen, restore cursor */
1883 raw_mode(0);
1884 }
1885 }
1886
1887 void raw_mode(int enable) {
1888 static struct termios saved_term_mode;
1889 struct termios raw_term_mode;
1890
1891 if (enable) {
1892 if (saved_term_mode.c_lflag == 0)/*don't overwrite stored mode*/
1893 tcgetattr(STDIN_FILENO, &saved_term_mode);
1894 raw_term_mode = saved_term_mode;
1895 raw_term_mode.c_lflag &= ~(ICANON | ECHO);
1896 raw_term_mode.c_cc[VMIN] = 1 ;
1897 raw_term_mode.c_cc[VTIME] = 0;
1898 tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term_mode);
1899 } else {
1900 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term_mode);
1901 }
1902 }
1903
1904 void signal_handler (int signum) {
1905 struct winsize w;
1906 switch (signum) {
1907 case SIGTSTP:
1908 screen_setup(0);
1909 signal(SIGTSTP, SIG_DFL); /* NOTE: assumes SysV semantics! */
1910 raise(SIGTSTP);
1911 break;
1912 case SIGCONT:
1913 screen_setup(1);
1914 print_table(NO_HI, NO_HI);
1915 break;
1916 case SIGINT: //TODO: don't exit; just warn like vim does
1917 exit(128+SIGINT);
1918 case SIGWINCH:
1919 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
1920 op.w[0] = w.ws_row;
1921 op.w[1] = w.ws_col;
1922 break;
1923 }
1924 }
1925 void signal_setup(void) {
1926 struct sigaction saction;
1927
1928 saction.sa_handler = signal_handler;
1929 sigemptyset(&saction.sa_mask);
1930 saction.sa_flags = 0;
1931 if (sigaction(SIGTSTP, &saction, NULL) < 0) {
1932 perror ("SIGTSTP");
1933 exit (1);
1934 }
1935 if (sigaction(SIGCONT, &saction, NULL) < 0) {
1936 perror ("SIGCONT");
1937 exit (1);
1938 }
1939 if (sigaction(SIGINT, &saction, NULL) < 0) {
1940 perror ("SIGINT");
1941 exit (1);
1942 }
1943 if (sigaction(SIGWINCH, &saction, NULL) < 0) {
1944 perror ("SIGWINCH");
1945 exit (1);
1946 }
1947 }
1948 //}}}
1949
1950 //vim: foldmethod=marker
Imprint / Impressum