]> git.gir.st - solVItaire.git/blob - sol.c
find and search commands prototypes
[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 if (is_tableu(active.pile))
975 active.opt = 0;
976
977 /***/
978 from_l: print_table(&active, &inactive);
979 _f = getch(mouse);
980
981 switch (_f) {
982 /* direct addressing: */
983 case '1': *from = TAB_1; break;
984 case '2': *from = TAB_2; break;
985 case '3': *from = TAB_3; break;
986 case '4': *from = TAB_4; break;
987 case '5': *from = TAB_5; break;
988 case '6': *from = TAB_6; break;
989 case '7': *from = TAB_7; break;
990 #ifdef SPIDER
991 case '8': *from = TAB_8; break;
992 case '9': *from = TAB_9; break;
993 case '0': *from = TAB_10;break;
994 #elif defined FREECELL
995 case '8': *from = TAB_8; break;
996 case '9': *from = STOCK; break;
997 case '0': *from = FOUNDATION; break;
998 #elif defined KLONDIKE
999 case '9': *from = WASTE; break;
1000 case '0': *from = FOUNDATION; break;
1001 case '8': /* fallthrough */
1002 #endif
1003 #ifndef FREECELL
1004 case '\n': *from = STOCK; break;
1005 #endif
1006 /* cursor keys addressing: */
1007 case KEY_LEFT:
1008 case 'h': cursor_left (&active); goto from_l;
1009 case KEY_DOWN:
1010 case 'j': cursor_down (&active); goto from_l;
1011 case KEY_UP:
1012 case 'k': cursor_up (&active); goto from_l;
1013 case KEY_RIGHT:
1014 case 'l': cursor_right(&active); goto from_l;
1015 case KEY_HOME:
1016 case 'H': cursor_to(&active,TAB_1); goto from_l; /* leftmost tableu */
1017 case KEY_END:
1018 case 'L': cursor_to(&active,TAB_MAX);goto from_l; /* rigthmost tableu */
1019 case KEY_INS:
1020 case 'M': cursor_to(&active,TAB_MAX/2); goto from_l; /* center tableu */
1021 case ' ': /* continue with second cursor */
1022 *from = active.pile;
1023 #ifdef KLONDIKE
1024 *opt = active.opt; /* when FOUNDATION */
1025 #endif
1026 inactive = active;
1027 break;
1028 /* mouse addressing: */
1029 case MOUSE_MIDDLE: return CMD_NONE;
1030 case MOUSE_RIGHT:
1031 if (set_mouse(term2pile(mouse), to, opt))
1032 return CMD_INVAL;
1033 return CMD_JOIN;
1034 case MOUSE_LEFT:
1035 if (set_mouse(term2pile(mouse), from, opt))
1036 return CMD_INVAL;
1037 if (!is_tableu(*from))
1038 inactive.opt = *opt; /* prevents card selector dialog */
1039 break;
1040 /* misc keys: */
1041 case ':':
1042 {char buf[256];
1043 fprintf (stderr, ":");
1044 raw_mode(0); /* turn on echo */
1045 fgets (buf, 256, stdin);
1046 raw_mode(1);
1047 switch(buf[0]) {
1048 case 'q': return CMD_QUIT;
1049 case 'n': return CMD_NEW;
1050 case 'r': return CMD_AGAIN;
1051 case 'h': return CMD_HELP;
1052 default: return CMD_INVAL;
1053 }}
1054 case 'J':
1055 *to = active.pile;
1056 return CMD_JOIN;
1057 case 'K': /* fallthrough */
1058 case '?': return CMD_HINT;
1059 case 'f': return CMD_FIND;
1060 case '/': return CMD_SEARCH;
1061 case 'u': return CMD_UNDO;
1062 case 002: return CMD_NONE; /* sent by SIGWINCH */
1063 case EOF: return CMD_NONE; /* sent by SIGCONT */
1064 default: return CMD_INVAL;
1065 }
1066 inactive.pile = *from; /* for direct addressing highlighting */
1067 if (is_tableu(*from) && f.t[*from][0] == NO_CARD) return CMD_INVAL;
1068 //TODO: freecell: if from==stock && stock[x] == empty: return inval
1069
1070 #ifndef FREECELL
1071 if (*from == STOCK) {
1072 *to = WASTE;
1073 return CMD_MOVE;
1074 }
1075 #endif
1076
1077 /***/
1078 to_l: print_table(&active, &inactive);
1079 t = getch(mouse);
1080
1081 switch (t) {
1082 case KEY_LEFT:
1083 case 'h': cursor_left (&active); goto to_l;
1084 case KEY_DOWN:
1085 case 'j': cursor_down (&active); goto to_l;
1086 case KEY_UP:
1087 case 'k': cursor_up (&active); goto to_l;
1088 case KEY_RIGHT:
1089 case 'l': cursor_right(&active); goto to_l;
1090 case KEY_HOME:
1091 case 'H': cursor_to(&active,TAB_1); goto to_l;
1092 case KEY_END:
1093 case 'L': cursor_to(&active,TAB_MAX); goto to_l;
1094 case KEY_INS:
1095 case 'M': cursor_to(&active,TAB_MAX/2); goto to_l;
1096 case 'J': /* fallthrough; just join selected pile */
1097 case ' ':
1098 *to = active.pile;
1099 break; /* continues with the foundation/empty tableu check */
1100 case MOUSE_MIDDLE:
1101 case MOUSE_RIGHT: return CMD_NONE;
1102 case MOUSE_LEFT:
1103 if (set_mouse(term2pile(mouse), to, opt))
1104 return CMD_INVAL;
1105 break;
1106 case 'K': /* fallthrough */
1107 case '?': return CMD_HINT;
1108 case 'f': return CMD_FIND; // XXX: will cancel from-card
1109 case '/': return CMD_SEARCH; //ditto.
1110 case 'u': return CMD_NONE; /* cancel selection */
1111 case EOF: return CMD_NONE; /* sent by SIGCONT */
1112 default:
1113 if (t < '0' || t > '9') return CMD_INVAL;
1114 if (t == '0')
1115 #ifdef KLONDIKE
1116 *to = FOUNDATION;
1117 #elif defined SPIDER
1118 *to = TAB_10;
1119 #elif defined FREECELL
1120 *to = FOUNDATION;
1121 else if (t == '9')
1122 *to = STOCK;
1123 #endif
1124 else
1125 *to = t-'1';
1126 }
1127
1128 /***/
1129 /* direct addressing post-processing stage:
1130 because foundations/freecells share the same key (and you can't select
1131 partial piles) there are sometimes ambiguous situations where it isn't
1132 clear from which pile (or how many cards) to take. the code below will
1133 only ask the user if there are at least two possible moves and
1134 automatically choose otherwise. */
1135 #ifdef FREECELL
1136 /* if it was selected with a cursor, it's obvious: */
1137 if (inactive.opt >= 0) {
1138 if (is_tableu(*from)) {
1139 /* NOTE: max_move same as in cursor_down() */
1140 *opt = max_move(*from, -1) - inactive.opt;
1141 } else {
1142 *opt = inactive.opt;
1143 }
1144 /* moving from tableu to empty tableu? */
1145 } else if(is_tableu(*from) && is_tableu(*to) && f.t[*to][0] == NO_CARD){
1146 int top = find_top(f.t[*from]);
1147 int max = max_move(*from, *to);
1148 int rank;
1149 if (top < 0) return CMD_INVAL;
1150 if (max == 1) { /* only 1 movable? */
1151 return *opt = 1, CMD_MOVE;
1152 } else { /* only ask the user if it's unclear: */
1153 int bottom = top - (max-1);
1154 printf ("\rup to ([a23456789xjqk] or space/return): ");
1155 rank = getch(NULL);
1156 switch (rank) {
1157 case ' ': rank = get_rank(f.t[*from][top]); break;
1158 case'\n': rank = get_rank(f.t[*from][bottom]); break;
1159 case 'a': case 'A': rank = RANK_A; break;
1160 case '0': /* fallthrough */
1161 case 'x': case 'X': rank = RANK_X; break;
1162 case 'j': case 'J': rank = RANK_J; break;
1163 case 'q': case 'Q': rank = RANK_Q; break;
1164 case 'k': case 'K': rank = RANK_K; break;
1165 default: rank -= '1';
1166 }
1167 if (rank < RANK_A || rank > RANK_K) return CMD_INVAL;
1168
1169 for (int i = 0; max--; i++)
1170 if (get_rank(f.t[*from][top-i]) == rank)
1171 return *opt = 1+i, CMD_MOVE;
1172
1173 return CMD_INVAL;
1174 }
1175 /* `opt` is the number of cards to move */
1176 /* moving between stock/foundation? */
1177 } else if (*from == FOUNDATION && *to == FOUNDATION) {
1178 return CMD_INVAL; /* nonsensical */
1179 } else if (*from == FOUNDATION && *to == STOCK) {
1180 if (f.w == (1<<NUM_CELLS)-1) return CMD_INVAL; /*no free cells*/
1181 int ok_foundation; /* find compatible (non-empty) foundations:*/
1182 int used_fs=0; for (int i = 0; i < NUM_SUITS; i++)
1183 if (!!f.f[i][0]) ok_foundation = i, used_fs++;
1184
1185 if (used_fs == 0) return CMD_INVAL; /* nowhere to take from */
1186 if (used_fs == 1) { /* take from the only one */
1187 return *opt = ok_foundation, CMD_MOVE;
1188 } else { /* ask user */
1189 printf ("take from (1-4): "); fflush (stdout);
1190 *opt = getch(NULL) - '1';
1191 if (*opt < 0 || *opt > 3) return CMD_INVAL;
1192 }
1193 /* `opt` is the foundation index (0..3) */
1194 } else if (*from == STOCK) { /* cell -> foundation/tableu */
1195 if (!f.w) return CMD_INVAL; /* no cell to take from */
1196 int ok_cell; /* find compatible (non-empty) cells: */
1197 int tab = is_tableu(*to);
1198 int used_cs=0; for (int i = 0; i < NUM_CELLS; i++) {
1199 card_t* pile = (tab?f.t[*to]:f.f[get_suit(f.s[i])]);
1200 int top_to = find_top(pile);
1201 if (tab? /* to tableu? */
1202 ((top_to<0 && f.s[i] > NO_CARD)
1203 ||(top_to>=0 && rank_next(f.s[i], pile[top_to])
1204 && color_ok(f.s[i], pile[top_to])))
1205 : /* to foundation? */
1206 ((top_to<0 && get_rank(f.s[i]) == RANK_A)
1207 ||(top_to>=0 && rank_next(pile[top_to],f.s[i])))
1208 )
1209 ok_cell = i, used_cs++;
1210 }
1211
1212 if (used_cs == 0) return CMD_INVAL; /* nowhere to take from */
1213 if (used_cs == 1) { /* take from the only one */
1214 return *opt = ok_cell, CMD_MOVE;
1215 } else { /* ask user */
1216 printf ("take from (1-4): "); fflush (stdout);
1217 *opt = getch(NULL) - '1';
1218 if (*opt < 0 || *opt > 3) return CMD_INVAL;
1219 }
1220 /* `opt` is the cell index (0..3) */
1221 } else
1222 #endif
1223 //TODO: mouse-friendly "up to?" dialog
1224 #if defined KLONDIKE || defined FREECELL
1225 if (*from == FOUNDATION) {
1226 if (inactive.opt >= 0) {
1227 *opt = inactive.opt;
1228 return CMD_MOVE;
1229 }
1230 int top = find_top(f.t[*to]);
1231 if (top < 0) return CMD_INVAL;
1232 int color = get_color(f.t[*to][top]);
1233 int choice_1 = 1-color; /* selects piles of */
1234 int choice_2 = 2+color; /* the opposite color */
1235 int top_c1 = find_top(f.f[choice_1]);
1236 int top_c2 = find_top(f.f[choice_2]);
1237
1238 switch ((rank_next(f.f[choice_1][top_c1], f.t[*to][top])
1239 && top_c1 >= 0 ) << 0
1240 |(rank_next(f.f[choice_2][top_c2], f.t[*to][top])
1241 && top_c2 >= 0 ) << 1) {
1242 case ( 1<<0): *opt = choice_1; break; /* choice_1 only */
1243 case (1<<1 ): *opt = choice_2; break; /* choice_2 only */
1244 case (1<<1 | 1<<0): /* both, ask user which to pick from */
1245 printf ("take from (1-4): "); fflush (stdout);
1246 *opt = getch(NULL) - '1';
1247 if (*opt < 0 || *opt > 3) return CMD_INVAL;
1248 break;
1249 default: return CMD_INVAL; /* none matched */
1250 }
1251 /* `opt` is the foundation index (0..3) */
1252 }
1253 #elif defined SPIDER
1254 /* moving to empty tableu? */
1255 if (is_tableu(*to) && f.t[*to][0] == NO_CARD) {
1256 int bottom = first_movable(f.t[*from]);
1257 if (inactive.opt >= 0) { /*if from was cursor addressed: */
1258 *opt = get_rank(f.t[*from][bottom + inactive.opt]);
1259 return CMD_MOVE;
1260 }
1261 int top = find_top(f.t[*from]);
1262 if (top < 0) return CMD_INVAL;
1263 if (top >= 0 && !is_movable(f.t[*from], top-1)) {
1264 *opt = get_rank(f.t[*from][top]);
1265 } else { /* only ask the user if it's unclear: */
1266 printf ("\rup to ([a23456789xjqk] or space/return): ");
1267 *opt = getch(NULL);
1268 switch (*opt) {
1269 case ' ': *opt = get_rank(f.t[*from][top]); break;
1270 case'\n': *opt = get_rank(f.t[*from][bottom]); break;
1271 case 'a': case 'A': *opt = RANK_A; break;
1272 case '0': /* fallthrough */
1273 case 'x': case 'X': *opt = RANK_X; break;
1274 case 'j': case 'J': *opt = RANK_J; break;
1275 case 'q': case 'Q': *opt = RANK_Q; break;
1276 case 'k': case 'K': *opt = RANK_K; break;
1277 default: *opt -= '1';
1278 }
1279 if (*opt < RANK_A || *opt > RANK_K) return CMD_INVAL;
1280 }
1281 /* `opt` is the rank of the highest card to move */
1282 }
1283 #endif
1284 return CMD_MOVE;
1285 }
1286
1287 int getctrlseq(unsigned char* buf) {
1288 int c;
1289 enum esc_states {
1290 START,
1291 ESC_SENT,
1292 CSI_SENT,
1293 MOUSE_EVENT,
1294 } state = START;
1295 int offset = 0x20; /* never sends control chars as data */
1296 while ((c = getchar()) != EOF) {
1297 switch (state) {
1298 case START:
1299 switch (c) {
1300 case '\033': state=ESC_SENT; break;
1301 default: return c;
1302 }
1303 break;
1304 case ESC_SENT:
1305 switch (c) {
1306 case '[': state=CSI_SENT; break;
1307 default: return KEY_INVAL;
1308 }
1309 break;
1310 case CSI_SENT:
1311 switch (c) {
1312 case 'A': return KEY_UP;
1313 case 'B': return KEY_DOWN;
1314 case 'C': return KEY_RIGHT;
1315 case 'D': return KEY_LEFT;
1316 /*NOTE: home/end send ^[[x~ . no support for modifiers*/
1317 case 'H': return KEY_HOME;
1318 case 'F': return KEY_END;
1319 case '2': getchar(); return KEY_INS;
1320 case '5': getchar(); return KEY_PGUP;
1321 case '6': getchar(); return KEY_PGDN;
1322 case 'M': state=MOUSE_EVENT; break;
1323 default: return KEY_INVAL;
1324 }
1325 break;
1326 case MOUSE_EVENT:
1327 if (buf == NULL) return KEY_INVAL;
1328 buf[0] = c - offset;
1329 buf[1] = getchar() - offset;
1330 buf[2] = getchar() - offset;
1331 return MOUSE_ANY;
1332 default:
1333 return KEY_INVAL;
1334 }
1335 }
1336 return 2;
1337 }
1338 int term2pile(unsigned char *mouse) {
1339 int line = (mouse[2]-1);
1340 int column = (mouse[1]-1) / op.s->width;
1341
1342 if (line < op.s->height) { /* first line */
1343 #ifdef KLONDIKE
1344 switch (column) {
1345 case 0: return STOCK;
1346 case 1: return WASTE;
1347 case 2: return -1; /* spacer */
1348 case 3: return FOUNDATION+0;
1349 case 4: return FOUNDATION+1;
1350 case 5: return FOUNDATION+2;
1351 case 6: return FOUNDATION+3;
1352 }
1353 #elif defined SPIDER
1354 if (column < 3) return STOCK;
1355 return -1;
1356 #elif defined FREECELL
1357 if (column < NUM_SUITS + NUM_CELLS) return STOCK+column;
1358 return -1;
1359 #endif
1360 } else if (line > op.s->height) { /* tableu */
1361 if (column <= TAB_MAX) return column;
1362 }
1363 return -1;
1364 }
1365 int wait_mouse_up(unsigned char* mouse) {
1366 //TODO: mouse drag: start gets inactive, hovering gets active cursors
1367 struct cursor cur = {-1,-1};
1368 int level = 1;
1369 /* note: if dragged [3]==1 and second position is in mouse[0,4,5] */
1370
1371 /* display a cursor while mouse button is pushed: */
1372 int pile = term2pile(mouse);
1373 cur.pile = pile;
1374 #ifdef KLONDIKE
1375 if (pile >= FOUNDATION) {
1376 cur.pile = FOUNDATION;
1377 cur.opt = pile-FOUNDATION;
1378 }
1379 #elif defined FREECELL
1380 if (pile > TAB_MAX) {
1381 cur.pile = pile-STOCK < NUM_CELLS? STOCK : FOUNDATION;
1382 cur.opt = (pile-STOCK) % 4;
1383 }
1384 #endif
1385 /* need to temporarily show the cursor, then revert to last state: */
1386 int old_show_cursor_hi = op.h; //TODO: ARGH! that's awful!
1387 op.h = 1;
1388 print_table(&cur, NO_HI); //TODO: should not overwrite inactive cursor!
1389 op.h = old_show_cursor_hi;
1390
1391 while (level > 0) {
1392 if (getctrlseq (mouse+3) == MOUSE_ANY) {
1393 /* ignore mouse wheel events: */
1394 if (mouse[3] & 0x40) continue;
1395
1396 else if((mouse[3]&3) == 3) level--; /* release event */
1397 else level++; /* another button pressed */
1398 }
1399 }
1400
1401 int success = mouse[1] == mouse[4] && mouse[2] == mouse[5];
1402 if (success) {
1403 mouse[3] = 0;
1404 }
1405 return success;
1406 }
1407
1408 int getch(unsigned char* buf) {
1409 //TODO: if buf==NULL disable mouse input
1410 /* returns a character, EOF, or constant for an escape/control sequence - NOT
1411 compatible with the ncurses implementation of same name */
1412 int action;
1413 if (buf && buf[3]) {
1414 /* mouse was dragged; return 'ungetted' previous destination */
1415 action = MOUSE_DRAG;
1416 /* keep original [0], as [3] only contains release event */
1417 buf[1] = buf[4];
1418 buf[2] = buf[5];
1419 buf[3] = 0;
1420 } else {
1421 action = getctrlseq(buf);
1422 }
1423
1424 switch (action) {
1425 case MOUSE_ANY:
1426 if (buf[0] > 3) break; /* ignore wheel events */
1427 wait_mouse_up(buf);
1428 /* fallthrough */
1429 case MOUSE_DRAG:
1430 switch (buf[0]) {
1431 case 0: return MOUSE_LEFT;
1432 case 1: return MOUSE_MIDDLE;
1433 case 2: return MOUSE_RIGHT;
1434 }
1435 }
1436
1437 return action;
1438 }
1439 // }}}
1440
1441 // shuffling and dealing {{{
1442 void deal(long seed) {
1443 //TODO: clear hls/f.h
1444 f = (const struct playfield){0}; /* clear playfield */
1445 card_t deck[DECK_SIZE*NUM_DECKS];
1446 int avail = DECK_SIZE*NUM_DECKS;
1447 for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) deck[i] = (i%DECK_SIZE)+1;
1448 #ifdef SPIDER
1449 if (op.m != NORMAL) for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) {
1450 if (op.m == MEDIUM) deck[i] = 1+((deck[i]-1) | 2);
1451 if (op.m == EASY) deck[i] = 1+((deck[i]-1) | 2 | 1);
1452 /* the 1+ -1 dance gets rid of the offset created by NO_CARD */
1453 }
1454 #endif
1455 srand (seed);
1456 for (int i = DECK_SIZE*NUM_DECKS-1; i > 0; i--) { /* fisher-yates */
1457 int j = rand() % (i+1);
1458 if (j-i) deck[i]^=deck[j],deck[j]^=deck[i],deck[i]^=deck[j];
1459 }
1460
1461 /* deal cards: */
1462 for (int i = 0; i < NUM_PILES; i++) {
1463 #ifdef KLONDIKE
1464 #define SIGN -
1465 int count = i; /* pile n has n closed cards, then 1 open */
1466 #elif defined SPIDER
1467 #define SIGN -
1468 int count = i<4?5:4; /* pile 1-4 have 5, 5-10 have 4 closed */
1469 #elif defined FREECELL
1470 #define SIGN +
1471 int count = i<4?6:5;/*like spider, but cards are dealt face-up*/
1472 #endif
1473 /* "SIGN": face down cards are negated */
1474 for (int j = 0; j < count; j++) f.t[i][j] = SIGN deck[--avail];
1475 f.t[i][count] = deck[--avail]; /* the face-up card */
1476 #undef SIGN
1477 }
1478 /* rest of the cards to the stock: */
1479 /* NOTE: assert(avail==50) for spider, assert(avail==0) for freecell */
1480 for (f.z = 0; avail; f.z++) f.s[f.z] = deck[--avail];
1481 #ifdef KLONDIKE
1482 f.w = -1; /* @start: nothing on waste */
1483 #elif defined SPIDER
1484 f.w = 0; /* number of used foundations */
1485 #elif defined FREECELL
1486 f.w = 0; /* bitmask of used free cells */
1487 #endif
1488
1489 f.u = &undo_sentinel;
1490 }
1491 //}}}
1492
1493 // screen drawing routines {{{
1494 void print_hi(int invert, int grey_bg, int bold, int blink, char* str) {
1495 if (!op.h) invert = 0; /* don't show invert if we used the mouse last */
1496 if (bold && op.s == &unicode_large_color){ //awful hack for bold + faint
1497 int offset = str[3]==017?16:str[4]==017?17:0;
1498 printf ("%s%s%s%s""%.*s%s%s""%s%s%s%s",
1499 "\033[1m", invert?"\033[7m":"", grey_bg?"\033[100m":"", blink?"\033[5m":"",
1500 offset, str, bold?"\033[1m":"", str+offset,
1501 blink?"\033[25m":"", grey_bg?"\033[49m":"", invert?"\033[27m":"","\033[22m");
1502 return;
1503 }
1504 printf ("%s%s%s%s%s%s%s%s%s",
1505 bold?"\033[1m":"", invert?"\033[7m":"", grey_bg?"\033[100m":"", blink?"\033[5m":"",
1506 str,
1507 blink?"\033[25m":"", grey_bg?"\033[49m":"", invert?"\033[27m":"",bold?"\033[22m":"");
1508 }
1509 void print_table(const struct cursor* active, const struct cursor* inactive) {
1510 int do_blink = 0; //XXX: remove
1511 printf("\033[2J\033[H"); /* clear screen, reset cursor */
1512 #ifdef KLONDIKE
1513 /* print stock, waste and foundation: */
1514 for (int line = 0; line < op.s->height; line++) {
1515 /* stock: */
1516 print_hi (active->pile == STOCK, inactive->pile == STOCK, 1, do_blink, (
1517 (f.w < f.z-1)?op.s->facedown
1518 :op.s->placeholder)[line]);
1519 /* waste: */
1520 print_hi (active->pile == WASTE, inactive->pile == WASTE, 1, do_blink, (
1521 /* NOTE: cast, because f.w sometimes is (short)-1 !? */
1522 ((short)f.w >= 0)?op.s->card[f.s[f.w]]
1523 :op.s->placeholder)[line]);
1524 printf ("%s", op.s->card[NO_CARD][line]); /* spacer */
1525 /* foundation: */
1526 for (int pile = 0; pile < NUM_SUITS; pile++) {
1527 int card = find_top(f.f[pile]);
1528 print_hi (active->pile==FOUNDATION && active->opt==pile,
1529 inactive->pile==FOUNDATION && (
1530 /* cursor addr. || direct addr. */
1531 inactive->opt==pile || inactive->opt < 0
1532 ), !!f.f[pile][0], do_blink,
1533 (card < 0)?op.s->foundation[line]
1534 :op.s->card[f.f[pile][card]][line]);
1535 }
1536 printf("\n");
1537 }
1538 printf("\n");
1539 #elif defined SPIDER
1540 int fdone; for (fdone = NUM_DECKS*NUM_SUITS; fdone; fdone--)
1541 if (f.f[fdone-1][RANK_K]) break; /*number of completed stacks*/
1542 int spacer_from = f.z?(f.z/10-1) * op.s->halfwidth[0] + op.s->width:0;
1543 int spacer_to = NUM_PILES*op.s->width -
1544 ((fdone?(fdone-1) * op.s->halfwidth[1]:0)+op.s->width);
1545 for (int line = 0; line < op.s->height; line++) {
1546 /* available stock: */
1547 for (int i = f.z/10; i; i--) {
1548 if (i==1) printf ("%s", op.s->facedown[line]);
1549 else printf ("%s", op.s->halfstack[line]);
1550 }
1551 /* spacer: */
1552 for (int i = spacer_from; i < spacer_to; i++) printf (" ");
1553 /* foundation (overlapping): */
1554 for (int i = NUM_DECKS*NUM_SUITS-1, half = 0; i >= 0; i--) {
1555 int overlap = half? op.s->halfcard[line]: 0;
1556 if (f.f[i][RANK_K]) printf ("%.*s", op.s->halfwidth[2],
1557 op.s->card[f.f[i][RANK_K]][line]+overlap),
1558 half++;
1559 }
1560 printf("\n");
1561 }
1562 printf("\n");
1563 #elif defined FREECELL
1564 /* print open cells, foundation: */
1565 for (int line = 0; line < op.s->height; line++) {
1566 for (int pile = 0; pile < NUM_CELLS; pile++)
1567 print_hi (active->pile==STOCK && active->opt==pile,
1568 inactive->pile==STOCK && (
1569 /* cursor addr. || direct addr. */
1570 inactive->opt==pile || inactive->opt < 0
1571 ), !!f.s[pile], do_blink,
1572 ((f.s[pile])?op.s->card[f.s[pile]]
1573 :op.s->placeholder)[line]);
1574 for (int pile = 0; pile < NUM_SUITS; pile++) {
1575 int card = find_top(f.f[pile]);
1576 print_hi (active->pile==FOUNDATION && active->opt==pile,
1577 inactive->pile==FOUNDATION && (
1578 /* cursor addr. || direct addr. */
1579 inactive->opt==pile || inactive->opt < 0
1580 ), !!f.f[pile][0], do_blink,
1581 (card < 0)?op.s->foundation[line]
1582 :op.s->card[f.f[pile][card]][line]);
1583 }
1584 printf("\n");
1585 }
1586 printf("\n");
1587 #endif
1588 #ifdef KLONDIKE
1589 #define DO_HI(cursor) (cursor->pile == pile && (movable || empty))
1590 #define TOP_HI(c) 1 /* can't select partial stacks in KLONDIKE */
1591 #elif defined SPIDER || defined FREECELL
1592 int offset[NUM_PILES]={0}; /* first card to highlight */
1593 # ifdef FREECELL
1594 int bottom[NUM_PILES]; /* first movable card */
1595 for (int i=0; i<NUM_PILES; i++)
1596 bottom[i] = find_top(f.t[i]) - max_move(i,-1);
1597 # endif
1598 #define DO_HI(cursor) (cursor->pile == pile && (movable || empty) \
1599 && offset[pile] >= cursor->opt)
1600 #define TOP_HI(cursor) (cursor->pile == pile && movable \
1601 && offset[pile] == cursor->opt)
1602 #endif
1603 /* print tableu piles: */
1604 int row[NUM_PILES] = {0};
1605 int line[NUM_PILES]= {0};
1606 int label[NUM_PILES]={0};
1607 int line_had_card;
1608 int did_placeholders = 0;
1609 do {
1610 line_had_card = 0;
1611 for (int pile = 0; pile < NUM_PILES; pile++) {
1612 card_t card = f.t[pile][row[pile]];
1613 card_t next = f.t[pile][row[pile]+1];
1614 int movable = is_movable(f.t[pile], row[pile]);
1615 int do_blink = hls(card, f.h);
1616 #ifdef FREECELL
1617 if(row[pile] <= bottom[pile]) movable = 0;
1618 #endif
1619 int empty = !card && row[pile] == 0;
1620
1621 print_hi (DO_HI(active), DO_HI(inactive), movable, do_blink, (
1622 (!card && row[pile] == 0)?op.s->placeholder
1623 :(card<0)?op.s->facedown
1624 :op.s->card[card]
1625 )[line[pile]]);
1626
1627 int extreme_overlap = ( 3 /* spacer, labels, status */
1628 + 2 * op.s->height /* stock, top tableu card */
1629 + find_top(f.t[pile]) * op.s->overlap) >op.w[0];
1630 /* normal overlap: */
1631 if (++line[pile] >= (next?op.s->overlap:op.s->height)
1632 /* extreme overlap on closed cards: */
1633 || (extreme_overlap &&
1634 line[pile] >= 1 &&
1635 f.t[pile][row[pile]] < 0 &&
1636 f.t[pile][row[pile]+1] <0)
1637 /* extreme overlap on sequences: */
1638 || (extreme_overlap &&
1639 !TOP_HI(active) && /*always show top selected card*/
1640 line[pile] >= 1 && row[pile] > 0 &&
1641 f.t[pile][row[pile]-1] > NO_CARD &&
1642 is_consecutive (f.t[pile], row[pile]) &&
1643 is_consecutive (f.t[pile], row[pile]-1) &&
1644 f.t[pile][row[pile]+1] != NO_CARD)
1645 ) {
1646 line[pile]=0;
1647 row[pile]++;
1648 #if defined SPIDER || defined FREECELL
1649 if (movable) offset[pile]++;
1650 #endif
1651 }
1652 /* tableu labels: */
1653 if(!card && !label[pile] && row[pile]>0&&line[pile]>0) {
1654 label[pile] = 1;
1655 printf ("\b\b%d ", (pile+1) % 10); //XXX: hack
1656 }
1657 line_had_card |= !!card;
1658 did_placeholders |= row[pile] > 0;
1659 }
1660 printf ("\n");
1661 } while (line_had_card || !did_placeholders);
1662 }
1663
1664 void visbell (void) {
1665 if (!op.v) return;
1666 printf ("\033[?5h"); fflush (stdout);
1667 usleep (100000);
1668 printf ("\033[?5l"); fflush (stdout);
1669 }
1670 void win_anim(void) {
1671 printf ("\033[?25l"); /* hide cursor */
1672 for (;;) {
1673 /* set cursor to random location */
1674 int row = 1+rand()%(1+op.w[0]-op.s->height);
1675 int col = 1+rand()%(1+op.w[1]-op.s->width);
1676
1677 /* draw random card */
1678 int face = 1 + rand() % 52;
1679 for (int l = 0; l < op.s->height; l++) {
1680 printf ("\033[%d;%dH", row+l, col);
1681 printf ("%s", op.s->card[face][l]);
1682 }
1683 fflush (stdout);
1684
1685 /* exit on keypress */
1686 struct pollfd p = {STDIN_FILENO, POLLIN, 0};
1687 if (poll (&p, 1, 80)) goto fin;
1688 }
1689 fin:
1690 printf ("\033[?25h"); /* show cursor */
1691 return;
1692 }
1693 //}}}
1694
1695 // undo logic {{{
1696 void undo_push (int _f, int t, int n, int o) {
1697 struct undo* new = malloc(sizeof(struct undo));
1698 new->f = _f;
1699 new->t = t;
1700 new->n = n;
1701 new->o = o;
1702 new->prev = f.u;
1703 new->next = NULL;
1704 f.u->next = new;
1705 f.u = f.u->next;
1706 }
1707 void undo_pop (struct undo* u) {
1708 if (u == &undo_sentinel) return;
1709
1710 #ifdef KLONDIKE
1711 if (u->f == FOUNDATION) {
1712 /* foundation -> tableu */
1713 int top_f = find_top(f.f[u->n]);
1714 int top_t = find_top(f.t[u->t]);
1715 f.f[u->n][top_f+1] = f.t[u->t][top_t];
1716 f.t[u->t][top_t] = NO_CARD;
1717 } else if (u->f == WASTE && u->t == FOUNDATION) {
1718 /* waste -> foundation */
1719 /* split u->n into wst and fnd: */
1720 int wst = u->n & 0xffff;
1721 int fnd = u->n >> 16;
1722 /* move stock cards one position up to make room: */
1723 for (int i = f.z; i >= wst; i--) f.s[i+1] = f.s[i];
1724 /* move one card from foundation to waste: */
1725 int top = find_top(f.f[fnd]);
1726 f.s[wst] = f.f[fnd][top];
1727 f.f[fnd][top] = NO_CARD;
1728 f.z++;
1729 f.w++;
1730 } else if (u->f == WASTE) {
1731 /* waste -> tableu */
1732 /* move stock cards one position up to make room: */
1733 for (int i = f.z-1; i >= u->n; i--) f.s[i+1] = f.s[i];
1734 /* move one card from tableu to waste: */
1735 int top = find_top(f.t[u->t]);
1736 f.s[u->n] = f.t[u->t][top];
1737 f.t[u->t][top] = NO_CARD;
1738 f.z++;
1739 f.w++;
1740 } else if (u->t == FOUNDATION) {
1741 /* tableu -> foundation */
1742 int top_f = find_top(f.t[u->f]);
1743 int top_t = find_top(f.f[u->n]);
1744 /* close topcard if previous action caused turn_over(): */
1745 if (u->o) f.t[u->f][top_f] *= -1;
1746 /* move one card from foundation to tableu: */
1747 f.t[u->f][top_f+1] = f.f[u->n][top_t];
1748 f.f[u->n][top_t] = NO_CARD;
1749 } else {
1750 /* tableu -> tableu */
1751 int top_f = find_top(f.t[u->f]);
1752 int top_t = find_top(f.t[u->t]);
1753 /* close topcard if previous action caused turn_over(): */
1754 if (u->o) f.t[u->f][top_f] *= -1;
1755 /* move n cards from tableu[f] to tableu[t]: */
1756 for (int i = 0; i < u->n; i++) {
1757 f.t[u->f][top_f+u->n-i] = f.t[u->t][top_t-i];
1758 f.t[u->t][top_t-i] = NO_CARD;
1759 }
1760 }
1761 #elif defined SPIDER
1762 if (u->f == STOCK) {
1763 /* stock -> tableu */
1764 /*remove a card from each pile and put it back onto the stock:*/
1765 for (int pile = NUM_PILES-1; pile >= 0; pile--) {
1766 int top = find_top(f.t[pile]);
1767 f.s[f.z++] = f.t[pile][top];
1768 f.t[pile][top] = NO_CARD;
1769 }
1770 } else if (u->t == FOUNDATION) {
1771 /* tableu -> foundation */
1772 int top = find_top(f.t[u->f]);
1773 /* close topcard if previous action caused turn_over(): */
1774 if (u->o) f.t[u->f][top] *= -1;
1775 /* append cards from foundation to tableu */
1776 for (int i = RANK_K; i >= RANK_A; i--) {
1777 f.t[u->f][++top] = f.f[u->n][i];
1778 f.f[u->n][i] = NO_CARD;
1779 }
1780 f.w--; /* decrement complete-foundation-counter */
1781
1782 } else {
1783 /* tableu -> tableu */
1784 int top_f = find_top(f.t[u->f]);
1785 int top_t = find_top(f.t[u->t]);
1786 /* close topcard if previous action caused turn_over(): */
1787 if (u->o) f.t[u->f][top_f] *= -1;
1788 /* move n cards from tableu[f] to tableu[t]: */
1789 for (int i = 0; i < u->n; i++) {
1790 f.t[u->f][top_f+u->n-i] = f.t[u->t][top_t-i];
1791 f.t[u->t][top_t-i] = NO_CARD;
1792 }
1793 }
1794 #elif defined FREECELL
1795 /*NOTE: if from and to are both stock/foundation, opt = from | to<<16 */
1796 if (u->f == STOCK && u->t == FOUNDATION) {
1797 /* free cells -> foundation */
1798 /* split u->n into cll and fnd: */
1799 int cll = u->n & 0xffff;
1800 int fnd = u->n >> 16;
1801 /* move one card from foundation to free cell: */
1802 int top = find_top(f.f[fnd]);
1803 f.s[cll] = f.f[fnd][top];
1804 f.f[fnd][top] = NO_CARD;
1805 f.w |= 1<<cll; /* mark cell as occupied */
1806 } else if (u->f == STOCK) {
1807 /* free cells -> cascade */
1808 int top_t = find_top(f.t[u->t]);
1809 f.s[u->n] = f.t[u->t][top_t];
1810 f.t[u->t][top_t] = NO_CARD;
1811 f.w |= 1<<u->n; /* mark cell as occupied */
1812 } else if (u->f == FOUNDATION && u->t == STOCK) {
1813 /* foundation -> free cells */
1814 /* split u->n into cll and fnd: */
1815 int cll = u->n >> 16;
1816 int fnd = u->n & 0xffff;
1817 /* move 1 card from free cell to foundation: */
1818 int top_f = find_top(f.f[fnd]);
1819 f.f[fnd][top_f+1] = f.s[cll];
1820 f.s[cll] = NO_CARD;
1821 f.w &= ~(1<<cll); /* mark cell as free */
1822 } else if (u->f == FOUNDATION) {
1823 /* foundation -> cascade */
1824 int top_f = find_top(f.f[u->n]);
1825 int top_t = find_top(f.t[u->t]);
1826 f.f[u->n][top_f+1] = f.t[u->t][top_t];
1827 f.t[u->t][top_t] = NO_CARD;
1828 } else if (u->t == STOCK) {
1829 /* cascade -> free cells */
1830 int top_f = find_top(f.t[u->f]);
1831 f.t[u->f][top_f+1] = f.s[u->n];
1832 f.s[u->n] = NO_CARD;
1833 f.w &= ~(1<<u->n); /* mark cell as free */
1834 } else if (u->t == FOUNDATION) {
1835 /* cascade -> foundation */
1836 int top_f = find_top(f.t[u->f]);
1837 int top_t = find_top(f.f[u->n]);
1838 /* move one card from foundation to cascade: */
1839 f.t[u->f][top_f+1] = f.f[u->n][top_t];
1840 f.f[u->n][top_t] = NO_CARD;
1841 } else {
1842 /* cascade -> cascade */
1843 int top_f = find_top(f.t[u->f]);
1844 int top_t = find_top(f.t[u->t]);
1845 /* move n cards from tableu[f] to tableu[t]: */
1846 for (int i = 0; i < u->n; i++) {
1847 f.t[u->f][top_f+u->n-i] = f.t[u->t][top_t-i];
1848 f.t[u->t][top_t-i] = NO_CARD;
1849 }
1850 }
1851 #endif
1852
1853 void* old = f.u;
1854 f.u = f.u->prev;
1855 free(old);
1856 }
1857 void free_undo (struct undo* u) {
1858 while (u && u != &undo_sentinel) {
1859 void* old = u;
1860 u = u->prev;
1861 free (old);
1862 }
1863 }
1864 //}}}
1865
1866 // initialization stuff {{{
1867 void screen_setup (int enable) {
1868 if (enable) {
1869 raw_mode(1);
1870 printf ("\033[s\033[?47h"); /* save cursor, alternate screen */
1871 printf ("\033[H\033[J"); /* reset cursor, clear screen */
1872 printf ("\033[?1000h"); /* enable mouse */
1873 } else {
1874 printf ("\033[?1000l"); /* disable mouse */
1875 printf ("\033[?47l\033[u"); /* primary screen, restore cursor */
1876 raw_mode(0);
1877 }
1878 }
1879
1880 void raw_mode(int enable) {
1881 static struct termios saved_term_mode;
1882 struct termios raw_term_mode;
1883
1884 if (enable) {
1885 if (saved_term_mode.c_lflag == 0)/*don't overwrite stored mode*/
1886 tcgetattr(STDIN_FILENO, &saved_term_mode);
1887 raw_term_mode = saved_term_mode;
1888 raw_term_mode.c_lflag &= ~(ICANON | ECHO);
1889 raw_term_mode.c_cc[VMIN] = 1 ;
1890 raw_term_mode.c_cc[VTIME] = 0;
1891 tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term_mode);
1892 } else {
1893 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term_mode);
1894 }
1895 }
1896
1897 void signal_handler (int signum) {
1898 struct winsize w;
1899 switch (signum) {
1900 case SIGTSTP:
1901 screen_setup(0);
1902 signal(SIGTSTP, SIG_DFL); /* NOTE: assumes SysV semantics! */
1903 raise(SIGTSTP);
1904 break;
1905 case SIGCONT:
1906 screen_setup(1);
1907 print_table(NO_HI, NO_HI);
1908 break;
1909 case SIGINT: //TODO: don't exit; just warn like vim does
1910 exit(128+SIGINT);
1911 case SIGWINCH:
1912 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
1913 op.w[0] = w.ws_row;
1914 op.w[1] = w.ws_col;
1915 break;
1916 }
1917 }
1918 void signal_setup(void) {
1919 struct sigaction saction;
1920
1921 saction.sa_handler = signal_handler;
1922 sigemptyset(&saction.sa_mask);
1923 saction.sa_flags = 0;
1924 if (sigaction(SIGTSTP, &saction, NULL) < 0) {
1925 perror ("SIGTSTP");
1926 exit (1);
1927 }
1928 if (sigaction(SIGCONT, &saction, NULL) < 0) {
1929 perror ("SIGCONT");
1930 exit (1);
1931 }
1932 if (sigaction(SIGINT, &saction, NULL) < 0) {
1933 perror ("SIGINT");
1934 exit (1);
1935 }
1936 if (sigaction(SIGWINCH, &saction, NULL) < 0) {
1937 perror ("SIGWINCH");
1938 exit (1);
1939 }
1940 }
1941 //}}}
1942
1943 //vim: foldmethod=marker
Imprint / Impressum