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