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