]> git.gir.st - solVItaire.git/blob - sol.c
updated foundation placeholder
[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 #define w2f nop /* for join()'s "to foundation" */
555 #define w2t nop /* ditto. */
556 #endif
557
558 //TODO: generalize prediction engine for CMD_HINT
559 #ifdef KLONDIKE
560 #define would_complete(pile) 0
561 #elif defined SPIDER
562 #define would_complete(pile) \
563 (get_rank(f.t[pile][r[pile].top]) == RANK_A \
564 && get_rank(f.t[to][bottom_to]) == RANK_K)
565 #elif defined FREECELL
566 #define would_complete(pile) 0
567 #endif
568 #define would_turn(pile) \
569 (f.t[pile][r[pile].pos-1] < 0)
570 #define would_empty(pile) \
571 (r[pile].pos == 0)
572
573 int join(int to) {
574 int top_to = find_top(f.t[to]);
575 #ifdef SPIDER
576 int bottom_to = first_movable(f.t[to]);
577 #endif
578
579 #if defined KLONDIKE || defined FREECELL
580 if (to == WASTE || to == STOCK) return ERR; /*why would you do that!?*/
581
582 if (to == FOUNDATION) {
583 int status = ERR;
584 for (int i = 0; i <= TAB_MAX; i++)
585 switch ((i?t2f:w2f)(i-1, FOUNDATION, 0)) {
586 case WON: return WON;
587 case OK: status = OK;
588 case ERR: /* nop */;
589 }
590 return status;
591 }
592 #endif
593
594 #ifdef KLONDIKE
595 if (top_to < 0) { /* move a king to empty pile: */
596 for (int i = 0; i <= TAB_MAX; i++) {
597 if (f.t[i][0] < 0) /* i.e. would turn? */
598 if (t2t(i, to, 0) == OK) return OK;
599 }
600 return w2t(WASTE, to, 0);
601 }
602 #elif defined FREECELL
603 if (top_to < 0) { /* move longest cascade to empty tableu: */ //TODO FREECELL:
604 int longest = -1;
605 int length = -1;
606 for (int i = 0; i <= TAB_MAX; i++) {
607 int m = max_move(i, to);
608 /*longest cascade that won't uncover another free pile*/
609 //TODO: don't rip apart cascades
610 if (m >= length && m <= find_top(f.t[i]))
611 length = m, longest = i;
612 }
613 if (longest < 0) return ERR;
614 return t2t(longest, to, length);
615 }
616 #endif
617
618 struct rating {
619 int ok:1; /* card to move in pile? */
620 int above; /* number of movable cards above */
621 int below; /* number of cards below ours */
622 int pos; /* where the card to move is in the pile */
623 int top; /* find_top() */
624 } r[NUM_PILES] = {{0}};
625 int complete = 0;/* SPIDER: true if any pile would complete a stack */
626 int turn = 0; /* SPIDER: true if any pile would turn_over */
627 int empty = 0; /* true if any pile would become empty */
628
629 /* 1. rate each pile: */
630 #ifdef SPIDER
631 if (top_to < 0) {
632 for (int pile = 0; pile < NUM_PILES; pile++) {
633 if (pile == to) continue;
634 int top = find_top(f.t[pile]);
635 int bottom = first_movable(f.t[pile]);
636 r[pile].pos = bottom; /* need for would_empty */
637
638 if (top < 0) continue; /* no cards to move */
639 if (would_empty(pile)) continue; /* doesn't help */
640
641 r[pile].ok++;
642 r[pile].above = 0; /* always take as many as possible */
643 r[pile].below = top - bottom;
644 r[pile].top = top;
645 complete |= would_complete(pile); /* never happens */
646 turn |= would_turn(pile);
647 empty |= would_empty(pile);
648 }
649 } else
650 #endif
651 for (int pile = 0; pile < NUM_PILES; pile++) {
652 r[pile].top = r[pile].pos = find_top(f.t[pile]);
653 /* backtrack until we find a compatible-to-'to'-pile card: */
654 #ifdef FREECELL
655 int maxmove = max_move(pile, -1);
656 #endif
657 while (r[pile].pos >= 0 && is_movable(f.t[pile], r[pile].pos)) {
658 int rankdiff = get_rank(f.t[pile][r[pile].pos])
659 - get_rank(f.t[to][top_to]);
660 if (rankdiff >= 0) break; /* past our card */
661 #ifdef FREECELL
662 if (!maxmove--) break; /* can't move this many cards */
663 #endif
664 if (rankdiff == -1 && /* rank matches */
665 color_ok(f.t[pile][r[pile].pos], f.t[to][top_to])
666 ) {
667 r[pile].ok++;
668 complete |= would_complete(pile);
669 turn |= would_turn(pile);
670 empty |= would_empty(pile);
671 for (int i = r[pile].pos; i >= 0; i--)
672 if (is_movable(f.t[pile], i-1))
673 r[pile].above++;
674 else break;
675 break;
676 }
677 r[pile].pos--;
678 r[pile].below++;
679 }
680 }
681
682 /* 2. find optimal pile: (optimized for spider) */
683 //todo: in spider, prefer longest piles if above==0 (faster completions)
684 int from = -1;
685 for (int pile = 0, above = 99, below = 99; pile < NUM_PILES; pile++) {
686 if (!r[pile].ok) continue;
687 /* don't bother if another pile would be better: prefer ... */
688 /* ... to complete a stack: */
689 if (!would_complete(pile) && complete) continue;
690 /* ... emptying piles: */
691 if (!would_empty(pile) && empty && !complete) continue;
692 /* ... to turn_over: */
693 if (!would_turn(pile) && turn && !complete && !empty) continue;
694 /* ... not to rip apart too many cards: */
695 if (r[pile].above > above) continue;
696 /* if tied, prefer ... */
697 if (r[pile].above == above
698 /* ... larger pile if destination is empty */
699 && (top_to < 0? r[pile].below < below
700 /* ... shorter pile otherwise */
701 : r[pile].below > below))
702 continue;
703
704 from = pile;
705 above = r[pile].above;
706 below = r[pile].below;
707 }
708
709 /* 3. move cards over and return: */
710 #ifdef KLONDIKE
711 /* prefer waste if it wouldn't turn_over: */
712 /* NOTE: does not attempt to take from froundation */
713 if (!empty && !turn && w2t(WASTE, to, 0) == OK)
714 return OK;
715 if (from < 0) /* nothing found */
716 return ERR;
717 return t2t(from, to, 0);
718 #elif defined SPIDER
719 if (from < 0) /* nothing found */
720 return ERR;
721 int bottom = first_movable(f.t[from]);
722 return t2t(from, to, get_rank(f.t[from][bottom]));
723 #elif defined FREECELL
724 if (from < 0) /* no tableu move found */ {
725 /* try all free cells before giving up: */
726 for (int i = 0; i < NUM_CELLS; i++)
727 if (c2t(STOCK, to, i) == OK) return OK;
728 return ERR;
729 }
730 return t2t(from, to, 0);
731 #endif
732 }
733 #undef would_empty
734 #undef would_turn
735 #undef would_complete
736 int nop(int from, int to, int opt) { (void)from;(void)to;(void)opt;return ERR; }
737 // }}}
738
739 // keyboard input handling {{{
740 // cursor functions{{{
741 #ifdef KLONDIKE
742 void cursor_left (struct cursor* cursor) {
743 op.h = 1;
744 if (is_tableu(cursor->pile)) {
745 if (cursor->pile > 0) cursor->pile--;
746 cursor->opt = 0;
747 } else { /* stock/waste/foundation*/
748 switch (cursor->pile) {
749 case WASTE: cursor->pile = STOCK; cursor->opt = 0; break;
750 case FOUNDATION:
751 if (cursor->opt <= 0)
752 cursor->pile = WASTE;
753 else
754 cursor->opt--;
755 }
756 }
757 }
758 void cursor_down (struct cursor* cursor) {
759 op.h = 1;
760 if (!is_tableu(cursor->pile)) {
761 switch (cursor->pile) {
762 case STOCK: cursor->pile = TAB_1; break;
763 case WASTE: cursor->pile = TAB_2; break;
764 case FOUNDATION:
765 cursor->pile = TAB_4 + cursor->opt;
766 }
767 cursor->opt = 0;
768 }
769 }
770 void cursor_up (struct cursor* cursor) {
771 op.h = 1;
772 if (is_tableu(cursor->pile)) {
773 switch (cursor->pile) { //ugly :|
774 case TAB_1: cursor->pile = STOCK; break;
775 case TAB_2: cursor->pile = WASTE; break;
776 case TAB_3: cursor->pile = WASTE; break;
777 case TAB_4: case TAB_5: case TAB_6: case TAB_7:
778 cursor->opt=cursor->pile-TAB_4;
779 cursor->pile = FOUNDATION;
780 break;
781 }
782 }
783 }
784 void cursor_right (struct cursor* cursor) {
785 op.h = 1;
786 if (is_tableu(cursor->pile)) {
787 if (cursor->pile < TAB_MAX) cursor->pile++;
788 cursor->opt = 0;
789 } else {
790 switch (cursor->pile) {
791 case STOCK: cursor->pile = WASTE; break;
792 case WASTE: cursor->pile = FOUNDATION;cursor->opt = 0; break;
793 case FOUNDATION:
794 if (cursor->opt < NUM_SUITS-1)
795 cursor->opt++;
796 }
797 }
798 }
799 #elif defined SPIDER
800 /*NOTE: one can't highlight the stock due to me being too lazy to implement it*/
801 void cursor_left (struct cursor* cursor) {
802 op.h = 1;
803 if (cursor->pile > 0) cursor->pile--;
804 cursor->opt = 0;
805 }
806 void cursor_down (struct cursor* cursor) {
807 op.h = 1;
808 int first = first_movable(f.t[cursor->pile]);
809 int top = find_top(f.t[cursor->pile]);
810 if (first + cursor->opt < top)
811 cursor->opt++;
812 }
813 void cursor_up (struct cursor* cursor) {
814 op.h = 1;
815 if (cursor->opt > 0) cursor->opt--;
816 }
817 void cursor_right (struct cursor* cursor) {
818 op.h = 1;
819 if (cursor->pile < TAB_MAX) cursor->pile++;
820 cursor->opt = 0;
821 }
822 #elif defined FREECELL
823 void cursor_left (struct cursor* cursor) {
824 op.h = 1;
825 if (is_tableu(cursor->pile)) {
826 if (cursor->pile > 0) cursor->pile--;
827 cursor->opt = 0;
828 } else { /* cells/foundation*/
829 switch (cursor->pile) {
830 case STOCK:
831 if (cursor->opt > 0)
832 cursor->opt--;
833 break;
834 case FOUNDATION:
835 if (cursor->opt <= 0) {
836 cursor->pile = STOCK;
837 cursor->opt = 3;
838 } else {
839 cursor->opt--;
840 }
841 }
842 }
843 }
844 void cursor_down (struct cursor* cursor) {
845 op.h = 1;
846 if (is_tableu(cursor->pile)) {
847 if (cursor->opt < max_move(cursor->pile, -1)-1)
848 cursor->opt++;
849 } else {
850 cursor->pile = cursor->opt+NUM_CELLS*(cursor->pile==FOUNDATION);
851 cursor->opt = 0;
852 }
853 }
854 void cursor_up (struct cursor* cursor) {
855 op.h = 1;
856 if (is_tableu(cursor->pile)) {
857 if (cursor->opt > 0) {
858 cursor->opt--;
859 } else {
860 switch (cursor->pile) {
861 case TAB_1: case TAB_2: case TAB_3: case TAB_4:
862 cursor->opt = cursor->pile; /*assumes TAB_1==0*/
863 cursor->pile = STOCK;
864 break;
865 case TAB_5: case TAB_6: case TAB_7: case TAB_8:
866 cursor->opt = cursor->pile - NUM_CELLS;
867 cursor->pile = FOUNDATION;
868 }
869 }
870 }
871 }
872 void cursor_right (struct cursor* cursor) {
873 op.h = 1;
874 if (is_tableu(cursor->pile)) {
875 if (cursor->pile < TAB_MAX) cursor->pile++;
876 cursor->opt = 0;
877 } else {
878 switch (cursor->pile) {
879 case STOCK:
880 if (cursor->opt < NUM_SUITS-1) {
881 cursor->opt++;
882 } else {
883 cursor->pile = FOUNDATION;
884 cursor->opt = 0;
885 } break;
886 case FOUNDATION:
887 if (cursor->opt < NUM_SUITS-1)
888 cursor->opt++;
889 }
890 }
891 }
892 #endif
893 void cursor_to (struct cursor* cursor, int pile) {
894 op.h = 1;
895 cursor->pile = pile;
896 cursor->opt = 0;
897 }
898 int set_mouse(int pile, int* main, int* opt) {
899 //TODO: this should set cursor.opt, so card selector choice dialog does not trigger!
900 op.h = 0;
901 if (pile < 0) return 1;
902 *main = pile;
903 #ifdef KLONDIKE
904 if (pile >= FOUNDATION)//TODO: check upper bound!
905 *main = FOUNDATION,
906 *opt = pile - FOUNDATION;
907 #elif defined SPIDER
908 (void)opt;
909 #elif defined FREECELL
910 if (pile > TAB_MAX) {
911 *main = pile-STOCK < NUM_CELLS? STOCK : FOUNDATION;
912 *opt = (pile-STOCK) % 4;
913 }
914 #endif
915 return 0;
916 }
917 //}}}
918 int get_cmd (int* from, int* to, int* opt) {
919 int _f, t;
920 unsigned char mouse[6] = {0}; /* must clear [3]! */
921 struct cursor inactive = {-1,-1};
922 static struct cursor active = {0,0};
923 if (is_tableu(active.pile))
924 active.opt = 0;
925
926 /***/
927 from_l: print_table(&active, &inactive);
928 _f = getch(mouse);
929
930 switch (_f) {
931 /* direct addressing: */
932 case '1': *from = TAB_1; break;
933 case '2': *from = TAB_2; break;
934 case '3': *from = TAB_3; break;
935 case '4': *from = TAB_4; break;
936 case '5': *from = TAB_5; break;
937 case '6': *from = TAB_6; break;
938 case '7': *from = TAB_7; break;
939 #ifdef SPIDER
940 case '8': *from = TAB_8; break;
941 case '9': *from = TAB_9; break;
942 case '0': *from = TAB_10;break;
943 #elif defined FREECELL
944 case '8': *from = TAB_8; break;
945 case '9': *from = STOCK; break;
946 case '0': *from = FOUNDATION; break;
947 #elif defined KLONDIKE
948 case '9': *from = WASTE; break;
949 case '0': *from = FOUNDATION; break;
950 case '8': /* fallthrough */
951 #endif
952 #ifndef FREECELL
953 case '\n': *from = STOCK; break;
954 #endif
955 /* cursor keys addressing: */
956 case KEY_LEFT:
957 case 'h': cursor_left (&active); goto from_l;
958 case KEY_DOWN:
959 case 'j': cursor_down (&active); goto from_l;
960 case KEY_UP:
961 case 'k': cursor_up (&active); goto from_l;
962 case KEY_RIGHT:
963 case 'l': cursor_right(&active); goto from_l;
964 case KEY_HOME:
965 case 'H': cursor_to(&active,TAB_1); goto from_l; /* leftmost tableu */
966 case KEY_END:
967 case 'L': cursor_to(&active,TAB_MAX);goto from_l; /* rigthmost tableu */
968 case KEY_INS:
969 case 'M': cursor_to(&active,TAB_MAX/2); goto from_l; /* center tableu */
970 case ' ': /* continue with second cursor */
971 *from = active.pile;
972 #ifdef KLONDIKE
973 *opt = active.opt; /* when FOUNDATION */
974 #endif
975 inactive = active;
976 break;
977 /* mouse addressing: */
978 case MOUSE_MIDDLE: return CMD_NONE;
979 case MOUSE_RIGHT:
980 if (set_mouse(term2pile(mouse), to, opt))
981 return CMD_INVAL;
982 return CMD_JOIN;
983 case MOUSE_LEFT:
984 if (set_mouse(term2pile(mouse), from, opt))
985 return CMD_INVAL;
986 if (!is_tableu(*from))
987 inactive.opt = *opt; /* prevents card selector dialog */
988 break;
989 /* misc keys: */
990 case ':':
991 {char buf[256];
992 fprintf (stderr, ":");
993 raw_mode(0); /* turn on echo */
994 fgets (buf, 256, stdin);
995 raw_mode(1);
996 switch(buf[0]) {
997 case 'q': return CMD_QUIT;
998 case 'n': return CMD_NEW;
999 case 'r': return CMD_AGAIN;
1000 case 'h': return CMD_HELP;
1001 default: return CMD_INVAL;
1002 }}
1003 case 'J':
1004 *to = active.pile;
1005 return CMD_JOIN;
1006 case 'K': /* fallthrough */
1007 case '?': return CMD_HINT;
1008 case 'u': return CMD_UNDO;
1009 case 002: return CMD_NONE; /* sent by SIGWINCH */
1010 case EOF: return CMD_NONE; /* sent by SIGCONT */
1011 default: return CMD_INVAL;
1012 }
1013 inactive.pile = *from; /* for direct addressing highlighting */
1014 if (is_tableu(*from) && f.t[*from][0] == NO_CARD) return CMD_INVAL;
1015
1016 #ifndef FREECELL
1017 if (*from == STOCK) {
1018 *to = WASTE;
1019 return CMD_MOVE;
1020 }
1021 #endif
1022
1023 /***/
1024 to_l: print_table(&active, &inactive);
1025 t = getch(mouse);
1026
1027 switch (t) {
1028 case KEY_LEFT:
1029 case 'h': cursor_left (&active); goto to_l;
1030 case KEY_DOWN:
1031 case 'j': cursor_down (&active); goto to_l;
1032 case KEY_UP:
1033 case 'k': cursor_up (&active); goto to_l;
1034 case KEY_RIGHT:
1035 case 'l': cursor_right(&active); goto to_l;
1036 case KEY_HOME:
1037 case 'H': cursor_to(&active,TAB_1); goto to_l;
1038 case KEY_END:
1039 case 'L': cursor_to(&active,TAB_MAX); goto to_l;
1040 case KEY_INS:
1041 case 'M': cursor_to(&active,TAB_MAX/2); goto to_l;
1042 case 'J': /* fallthrough; just join selected pile */
1043 case ' ':
1044 *to = active.pile;
1045 break; /* continues with the foundation/empty tableu check */
1046 case MOUSE_MIDDLE:
1047 case MOUSE_RIGHT: return CMD_NONE;
1048 case MOUSE_LEFT:
1049 if (set_mouse(term2pile(mouse), to, opt))
1050 return CMD_INVAL;
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 //TODO: mouse-friendly "up to?" dialog
1168 #if defined KLONDIKE || defined FREECELL
1169 if (*from == FOUNDATION) {
1170 if (inactive.opt >= 0) {
1171 *opt = inactive.opt;
1172 return CMD_MOVE;
1173 }
1174 int top = find_top(f.t[*to]);
1175 if (top < 0) return CMD_INVAL;
1176 int color = get_color(f.t[*to][top]);
1177 int choice_1 = 1-color; /* selects piles of */
1178 int choice_2 = 2+color; /* the opposite color */
1179 int top_c1 = find_top(f.f[choice_1]);
1180 int top_c2 = find_top(f.f[choice_2]);
1181
1182 switch ((rank_next(f.f[choice_1][top_c1], f.t[*to][top])
1183 && top_c1 >= 0 ) << 0
1184 |(rank_next(f.f[choice_2][top_c2], f.t[*to][top])
1185 && top_c2 >= 0 ) << 1) {
1186 case ( 1<<0): *opt = choice_1; break; /* choice_1 only */
1187 case (1<<1 ): *opt = choice_2; break; /* choice_2 only */
1188 case (1<<1 | 1<<0): /* both, ask user which to pick from */
1189 printf ("take from (1-4): "); fflush (stdout);
1190 *opt = getch(NULL) - '1';
1191 if (*opt < 0 || *opt > 3) return CMD_INVAL;
1192 break;
1193 default: return CMD_INVAL; /* none matched */
1194 }
1195 /* `opt` is the foundation index (0..3) */
1196 }
1197 #elif defined SPIDER
1198 /* moving to empty tableu? */
1199 if (is_tableu(*to) && f.t[*to][0] == NO_CARD) {
1200 int bottom = first_movable(f.t[*from]);
1201 if (inactive.opt >= 0) { /*if from was cursor addressed: */
1202 *opt = get_rank(f.t[*from][bottom + inactive.opt]);
1203 return CMD_MOVE;
1204 }
1205 int top = find_top(f.t[*from]);
1206 if (top < 0) return CMD_INVAL;
1207 if (top >= 0 && !is_movable(f.t[*from], top-1)) {
1208 *opt = get_rank(f.t[*from][top]);
1209 } else { /* only ask the user if it's unclear: */
1210 printf ("\rup to ([a23456789xjqk] or space/return): ");
1211 *opt = getch(NULL);
1212 switch (*opt) {
1213 case ' ': *opt = get_rank(f.t[*from][top]); break;
1214 case'\n': *opt = get_rank(f.t[*from][bottom]); break;
1215 case 'a': case 'A': *opt = RANK_A; break;
1216 case '0': /* fallthrough */
1217 case 'x': case 'X': *opt = RANK_X; break;
1218 case 'j': case 'J': *opt = RANK_J; break;
1219 case 'q': case 'Q': *opt = RANK_Q; break;
1220 case 'k': case 'K': *opt = RANK_K; break;
1221 default: *opt -= '1';
1222 }
1223 if (*opt < RANK_A || *opt > RANK_K) return CMD_INVAL;
1224 }
1225 /* `opt` is the rank of the highest card to move */
1226 }
1227 #endif
1228 return CMD_MOVE;
1229 }
1230
1231 int getctrlseq(unsigned char* buf) {
1232 int c;
1233 enum esc_states {
1234 START,
1235 ESC_SENT,
1236 CSI_SENT,
1237 MOUSE_EVENT,
1238 } state = START;
1239 int offset = 0x20; /* never sends control chars as data */
1240 while ((c = getchar()) != EOF) {
1241 switch (state) {
1242 case START:
1243 switch (c) {
1244 case '\033': state=ESC_SENT; break;
1245 default: return c;
1246 }
1247 break;
1248 case ESC_SENT:
1249 switch (c) {
1250 case '[': state=CSI_SENT; break;
1251 default: return KEY_INVAL;
1252 }
1253 break;
1254 case CSI_SENT:
1255 switch (c) {
1256 case 'A': return KEY_UP;
1257 case 'B': return KEY_DOWN;
1258 case 'C': return KEY_RIGHT;
1259 case 'D': return KEY_LEFT;
1260 /*NOTE: home/end send ^[[x~ . no support for modifiers*/
1261 case 'H': return KEY_HOME;
1262 case 'F': return KEY_END;
1263 case '2': getchar(); return KEY_INS;
1264 case '5': getchar(); return KEY_PGUP;
1265 case '6': getchar(); return KEY_PGDN;
1266 case 'M': state=MOUSE_EVENT; break;
1267 default: return KEY_INVAL;
1268 }
1269 break;
1270 case MOUSE_EVENT:
1271 if (buf == NULL) return KEY_INVAL;
1272 buf[0] = c - offset;
1273 buf[1] = getchar() - offset;
1274 buf[2] = getchar() - offset;
1275 return MOUSE_ANY;
1276 default:
1277 return KEY_INVAL;
1278 }
1279 }
1280 return 2;
1281 }
1282 int term2pile(unsigned char *mouse) {
1283 int line = (mouse[2]-1);
1284 int column = (mouse[1]-1) / op.s->width;
1285
1286 if (line < op.s->height) { /* first line */
1287 #ifdef KLONDIKE
1288 switch (column) {
1289 case 0: return STOCK;
1290 case 1: return WASTE;
1291 case 2: return -1; /* spacer */
1292 case 3: return FOUNDATION+0;
1293 case 4: return FOUNDATION+1;
1294 case 5: return FOUNDATION+2;
1295 case 6: return FOUNDATION+3;
1296 }
1297 #elif defined SPIDER
1298 if (column < 3) return STOCK;
1299 return -1;
1300 #elif defined FREECELL
1301 if (column < NUM_SUITS + NUM_CELLS) return STOCK+column;
1302 return -1;
1303 #endif
1304 } else if (line > op.s->height) { /* tableu */
1305 if (column <= TAB_MAX) return column;
1306 }
1307 return -1;
1308 }
1309 int wait_mouse_up(unsigned char* mouse) {
1310 //TODO: mouse drag: start gets inactive, hovering gets active cursors
1311 struct cursor cur = {-1,-1};
1312 int level = 1;
1313 /* note: if dragged [3]==1 and second position is in mouse[0,4,5] */
1314
1315 /* display a cursor while mouse button is pushed: */
1316 int pile = term2pile(mouse);
1317 cur.pile = pile;
1318 #ifdef KLONDIKE
1319 if (pile >= FOUNDATION) {
1320 cur.pile = FOUNDATION;
1321 cur.opt = pile-FOUNDATION;
1322 }
1323 #elif defined FREECELL
1324 if (pile > TAB_MAX) {
1325 cur.pile = pile-STOCK < NUM_CELLS? STOCK : FOUNDATION;
1326 cur.opt = (pile-STOCK) % 4;
1327 }
1328 #endif
1329 /* need to temporarily show the cursor, then revert to last state: */
1330 int old_show_cursor_hi = op.h; //TODO: ARGH! that's awful!
1331 op.h = 1;
1332 print_table(&cur, NO_HI); //TODO: should not overwrite inactive cursor!
1333 op.h = old_show_cursor_hi;
1334
1335 while (level > 0) {
1336 if (getctrlseq (mouse+3) == MOUSE_ANY) {
1337 /* ignore mouse wheel events: */
1338 if (mouse[3] & 0x40) continue;
1339
1340 else if((mouse[3]&3) == 3) level--; /* release event */
1341 else level++; /* another button pressed */
1342 }
1343 }
1344
1345 int success = mouse[1] == mouse[4] && mouse[2] == mouse[5];
1346 if (success) {
1347 mouse[3] = 0;
1348 }
1349 return success;
1350 }
1351
1352 int getch(unsigned char* buf) {
1353 //TODO: if buf==NULL disable mouse input
1354 /* returns a character, EOF, or constant for an escape/control sequence - NOT
1355 compatible with the ncurses implementation of same name */
1356 int action;
1357 if (buf && buf[3]) {
1358 /* mouse was dragged; return 'ungetted' previous destination */
1359 action = MOUSE_DRAG;
1360 /* keep original [0], as [3] only contains release event */
1361 buf[1] = buf[4];
1362 buf[2] = buf[5];
1363 buf[3] = 0;
1364 } else {
1365 action = getctrlseq(buf);
1366 }
1367
1368 switch (action) {
1369 case MOUSE_ANY:
1370 if (buf[0] > 3) break; /* ignore wheel events */
1371 wait_mouse_up(buf);
1372 /* fallthrough */
1373 case MOUSE_DRAG:
1374 switch (buf[0]) {
1375 case 0: return MOUSE_LEFT;
1376 case 1: return MOUSE_MIDDLE;
1377 case 2: return MOUSE_RIGHT;
1378 }
1379 }
1380
1381 return action;
1382 }
1383 // }}}
1384
1385 // shuffling and dealing {{{
1386 void deal(long seed) {
1387 f = (const struct playfield){0}; /* clear playfield */
1388 card_t deck[DECK_SIZE*NUM_DECKS];
1389 int avail = DECK_SIZE*NUM_DECKS;
1390 for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) deck[i] = (i%DECK_SIZE)+1;
1391 #ifdef SPIDER
1392 if (op.m != NORMAL) for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) {
1393 if (op.m == MEDIUM) deck[i] = 1+((deck[i]-1) | 2);
1394 if (op.m == EASY) deck[i] = 1+((deck[i]-1) | 2 | 1);
1395 /* the 1+ -1 dance gets rid of the offset created by NO_CARD */
1396 }
1397 #endif
1398 srand (seed);
1399 for (int i = DECK_SIZE*NUM_DECKS-1; i > 0; i--) { /* fisher-yates */
1400 int j = rand() % (i+1);
1401 if (j-i) deck[i]^=deck[j],deck[j]^=deck[i],deck[i]^=deck[j];
1402 }
1403
1404 /* deal cards: */
1405 for (int i = 0; i < NUM_PILES; i++) {
1406 #ifdef KLONDIKE
1407 #define SIGN -
1408 int count = i; /* pile n has n closed cards, then 1 open */
1409 #elif defined SPIDER
1410 #define SIGN -
1411 int count = i<4?5:4; /* pile 1-4 have 5, 5-10 have 4 closed */
1412 #elif defined FREECELL
1413 #define SIGN +
1414 int count = i<4?6:5;/*like spider, but cards are dealt face-up*/
1415 #endif
1416 /* "SIGN": face down cards are negated */
1417 for (int j = 0; j < count; j++) f.t[i][j] = SIGN deck[--avail];
1418 f.t[i][count] = deck[--avail]; /* the face-up card */
1419 #undef SIGN
1420 }
1421 /* rest of the cards to the stock: */
1422 /* NOTE: assert(avail==50) for spider, assert(avail==0) for freecell */
1423 for (f.z = 0; avail; f.z++) f.s[f.z] = deck[--avail];
1424 #ifdef KLONDIKE
1425 f.w = -1; /* @start: nothing on waste */
1426 #elif defined SPIDER
1427 f.w = 0; /* number of used foundations */
1428 #elif defined FREECELL
1429 f.w = 0; /* bitmask of used free cells */
1430 #endif
1431
1432 f.u = &undo_sentinel;
1433 }
1434 //}}}
1435
1436 // screen drawing routines {{{
1437 void print_hi(int invert, int grey_bg, int bold, char* str) {
1438 if (!op.h) invert = 0; /* don't show invert if we used the mouse last */
1439 if (bold && op.s == &unicode_large_color){ //awful hack for bold + faint
1440 int offset = str[3]==017?16:str[4]==017?17:0;
1441 printf ("%s%s%s""%.*s%s%s""%s%s%s",
1442 "\033[1m", invert?"\033[7m":"", grey_bg?"\033[100m":"",
1443 offset, str, bold?"\033[1m":"", str+offset,
1444 grey_bg?"\033[49m":"", invert?"\033[27m":"","\033[22m");
1445 return;
1446 }
1447 printf ("%s%s%s%s%s%s%s",
1448 bold?"\033[1m":"", invert?"\033[7m":"", grey_bg?"\033[100m":"",
1449 str,
1450 grey_bg?"\033[49m":"", invert?"\033[27m":"",bold?"\033[22m":"");
1451 }
1452 void print_table(const struct cursor* active, const struct cursor* inactive) {
1453 printf("\033[2J\033[H"); /* clear screen, reset cursor */
1454 #ifdef KLONDIKE
1455 /* print stock, waste and foundation: */
1456 for (int line = 0; line < op.s->height; line++) {
1457 /* stock: */
1458 print_hi (active->pile == STOCK, inactive->pile == STOCK, 1, (
1459 (f.w < f.z-1)?op.s->facedown
1460 :op.s->placeholder)[line]);
1461 /* waste: */
1462 print_hi (active->pile == WASTE, inactive->pile == WASTE, 1, (
1463 /* NOTE: cast, because f.w sometimes is (short)-1 !? */
1464 ((short)f.w >= 0)?op.s->card[f.s[f.w]]
1465 :op.s->placeholder)[line]);
1466 printf ("%s", op.s->card[NO_CARD][line]); /* spacer */
1467 /* foundation: */
1468 for (int pile = 0; pile < NUM_SUITS; pile++) {
1469 int card = find_top(f.f[pile]);
1470 print_hi (active->pile==FOUNDATION && active->opt==pile,
1471 inactive->pile==FOUNDATION && (
1472 /* cursor addr. || direct addr. */
1473 inactive->opt==pile || inactive->opt < 0
1474 ), !!f.f[pile][0],
1475 (card < 0)?op.s->foundation[line]
1476 :op.s->card[f.f[pile][card]][line]);
1477 }
1478 printf("\n");
1479 }
1480 printf("\n");
1481 #elif defined SPIDER
1482 int fdone; for (fdone = NUM_DECKS*NUM_SUITS; fdone; fdone--)
1483 if (f.f[fdone-1][RANK_K]) break; /*number of completed stacks*/
1484 int spacer_from = f.z?(f.z/10-1) * op.s->halfwidth[0] + op.s->width:0;
1485 int spacer_to = NUM_PILES*op.s->width -
1486 ((fdone?(fdone-1) * op.s->halfwidth[1]:0)+op.s->width);
1487 for (int line = 0; line < op.s->height; line++) {
1488 /* available stock: */
1489 for (int i = f.z/10; i; i--) {
1490 if (i==1) printf ("%s", op.s->facedown[line]);
1491 else printf ("%s", op.s->halfstack[line]);
1492 }
1493 /* spacer: */
1494 for (int i = spacer_from; i < spacer_to; i++) printf (" ");
1495 /* foundation (overlapping): */
1496 for (int i = NUM_DECKS*NUM_SUITS-1, half = 0; i >= 0; i--) {
1497 int overlap = half? op.s->halfcard[line]: 0;
1498 if (f.f[i][RANK_K]) printf ("%.*s", op.s->halfwidth[2],
1499 op.s->card[f.f[i][RANK_K]][line]+overlap),
1500 half++;
1501 }
1502 printf("\n");
1503 }
1504 printf("\n");
1505 #elif defined FREECELL
1506 /* print open cells, foundation: */
1507 for (int line = 0; line < op.s->height; line++) {
1508 for (int pile = 0; pile < NUM_CELLS; pile++)
1509 print_hi (active->pile==STOCK && active->opt==pile,
1510 inactive->pile==STOCK && (
1511 /* cursor addr. || direct addr. */
1512 inactive->opt==pile || inactive->opt < 0
1513 ), !!f.s[pile],
1514 ((f.s[pile])?op.s->card[f.s[pile]]
1515 :op.s->placeholder)[line]);
1516 for (int pile = 0; pile < NUM_SUITS; pile++) {
1517 int card = find_top(f.f[pile]);
1518 print_hi (active->pile==FOUNDATION && active->opt==pile,
1519 inactive->pile==FOUNDATION && (
1520 /* cursor addr. || direct addr. */
1521 inactive->opt==pile || inactive->opt < 0
1522 ), !!f.f[pile][0],
1523 (card < 0)?op.s->foundation[line]
1524 :op.s->card[f.f[pile][card]][line]);
1525 }
1526 printf("\n");
1527 }
1528 printf("\n");
1529 #endif
1530 #ifdef KLONDIKE
1531 #define DO_HI(cursor) (cursor->pile == pile && (movable || empty))
1532 #define TOP_HI(c) 1 /* can't select partial stacks in KLONDIKE */
1533 #elif defined SPIDER || defined FREECELL
1534 int offset[NUM_PILES]={0}; /* first card to highlight */
1535 # ifdef FREECELL
1536 int bottom[NUM_PILES]; /* first movable card */
1537 for (int i=0; i<NUM_PILES; i++)
1538 bottom[i] = find_top(f.t[i]) - max_move(i,-1);
1539 # endif
1540 #define DO_HI(cursor) (cursor->pile == pile && (movable || empty) \
1541 && offset[pile] >= cursor->opt)
1542 #define TOP_HI(cursor) (cursor->pile == pile && movable \
1543 && offset[pile] == cursor->opt)
1544 #endif
1545 /* print tableu piles: */
1546 int row[NUM_PILES] = {0};
1547 int line[NUM_PILES]= {0};
1548 int label[NUM_PILES]={0};
1549 int line_had_card;
1550 int did_placeholders = 0;
1551 do {
1552 line_had_card = 0;
1553 for (int pile = 0; pile < NUM_PILES; pile++) {
1554 card_t card = f.t[pile][row[pile]];
1555 card_t next = f.t[pile][row[pile]+1];
1556 int movable = is_movable(f.t[pile], row[pile]);
1557 #ifdef FREECELL
1558 if(row[pile] <= bottom[pile]) movable = 0;
1559 #endif
1560 int empty = !card && row[pile] == 0;
1561
1562 print_hi (DO_HI(active), DO_HI(inactive), movable, (
1563 (!card && row[pile] == 0)?op.s->placeholder
1564 :(card<0)?op.s->facedown
1565 :op.s->card[card]
1566 )[line[pile]]);
1567
1568 int extreme_overlap = ( 3 /* spacer, labels, status */
1569 + 2 * op.s->height /* stock, top tableu card */
1570 + find_top(f.t[pile]) * op.s->overlap) >op.w[0];
1571 /* normal overlap: */
1572 if (++line[pile] >= (next?op.s->overlap:op.s->height)
1573 /* extreme overlap on closed cards: */
1574 || (extreme_overlap &&
1575 line[pile] >= 1 &&
1576 f.t[pile][row[pile]] < 0 &&
1577 f.t[pile][row[pile]+1] <0)
1578 /* extreme overlap on sequences: */
1579 || (extreme_overlap &&
1580 !TOP_HI(active) && /*always show top selected card*/
1581 line[pile] >= 1 && row[pile] > 0 &&
1582 f.t[pile][row[pile]-1] > NO_CARD &&
1583 is_consecutive (f.t[pile], row[pile]) &&
1584 is_consecutive (f.t[pile], row[pile]-1) &&
1585 f.t[pile][row[pile]+1] != NO_CARD)
1586 ) {
1587 line[pile]=0;
1588 row[pile]++;
1589 #if defined SPIDER || defined FREECELL
1590 if (movable) offset[pile]++;
1591 #endif
1592 }
1593 /* tableu labels: */
1594 if(!card && !label[pile] && row[pile]>0&&line[pile]>0) {
1595 label[pile] = 1;
1596 printf ("\b\b%d ", (pile+1) % 10); //XXX: hack
1597 }
1598 line_had_card |= !!card;
1599 did_placeholders |= row[pile] > 0;
1600 }
1601 printf ("\n");
1602 } while (line_had_card || !did_placeholders);
1603 }
1604
1605 void visbell (void) {
1606 if (!op.v) return;
1607 printf ("\033[?5h"); fflush (stdout);
1608 usleep (100000);
1609 printf ("\033[?5l"); fflush (stdout);
1610 }
1611 void win_anim(void) {
1612 printf ("\033[?25l"); /* hide cursor */
1613 for (;;) {
1614 /* set cursor to random location */
1615 int row = 1+rand()%(1+op.w[0]-op.s->height);
1616 int col = 1+rand()%(1+op.w[1]-op.s->width);
1617
1618 /* draw random card */
1619 int face = 1 + rand() % 52;
1620 for (int l = 0; l < op.s->height; l++) {
1621 printf ("\033[%d;%dH", row+l, col);
1622 printf ("%s", op.s->card[face][l]);
1623 }
1624 fflush (stdout);
1625
1626 /* exit on keypress */
1627 struct pollfd p = {STDIN_FILENO, POLLIN, 0};
1628 if (poll (&p, 1, 80)) goto fin;
1629 }
1630 fin:
1631 printf ("\033[?25h"); /* show cursor */
1632 return;
1633 }
1634 //}}}
1635
1636 // undo logic {{{
1637 void undo_push (int _f, int t, int n, int o) {
1638 struct undo* new = malloc(sizeof(struct undo));
1639 new->f = _f;
1640 new->t = t;
1641 new->n = n;
1642 new->o = o;
1643 new->prev = f.u;
1644 new->next = NULL;
1645 f.u->next = new;
1646 f.u = f.u->next;
1647 }
1648 void undo_pop (struct undo* u) {
1649 if (u == &undo_sentinel) return;
1650
1651 #ifdef KLONDIKE
1652 if (u->f == FOUNDATION) {
1653 /* foundation -> tableu */
1654 int top_f = find_top(f.f[u->n]);
1655 int top_t = find_top(f.t[u->t]);
1656 f.f[u->n][top_f+1] = f.t[u->t][top_t];
1657 f.t[u->t][top_t] = NO_CARD;
1658 } else if (u->f == WASTE && u->t == FOUNDATION) {
1659 /* waste -> foundation */
1660 /* split u->n into wst and fnd: */
1661 int wst = u->n & 0xffff;
1662 int fnd = u->n >> 16;
1663 /* move stock cards one position up to make room: */
1664 for (int i = f.z; i >= wst; i--) f.s[i+1] = f.s[i];
1665 /* move one card from foundation to waste: */
1666 int top = find_top(f.f[fnd]);
1667 f.s[wst] = f.f[fnd][top];
1668 f.f[fnd][top] = NO_CARD;
1669 f.z++;
1670 f.w++;
1671 } else if (u->f == WASTE) {
1672 /* waste -> tableu */
1673 /* move stock cards one position up to make room: */
1674 for (int i = f.z-1; i >= u->n; i--) f.s[i+1] = f.s[i];
1675 /* move one card from tableu to waste: */
1676 int top = find_top(f.t[u->t]);
1677 f.s[u->n] = f.t[u->t][top];
1678 f.t[u->t][top] = NO_CARD;
1679 f.z++;
1680 f.w++;
1681 } else if (u->t == FOUNDATION) {
1682 /* tableu -> foundation */
1683 int top_f = find_top(f.t[u->f]);
1684 int top_t = find_top(f.f[u->n]);
1685 /* close topcard if previous action caused turn_over(): */
1686 if (u->o) f.t[u->f][top_f] *= -1;
1687 /* move one card from foundation to tableu: */
1688 f.t[u->f][top_f+1] = f.f[u->n][top_t];
1689 f.f[u->n][top_t] = NO_CARD;
1690 } else {
1691 /* tableu -> tableu */
1692 int top_f = find_top(f.t[u->f]);
1693 int top_t = find_top(f.t[u->t]);
1694 /* close topcard if previous action caused turn_over(): */
1695 if (u->o) f.t[u->f][top_f] *= -1;
1696 /* move n cards from tableu[f] to tableu[t]: */
1697 for (int i = 0; i < u->n; i++) {
1698 f.t[u->f][top_f+u->n-i] = f.t[u->t][top_t-i];
1699 f.t[u->t][top_t-i] = NO_CARD;
1700 }
1701 }
1702 #elif defined SPIDER
1703 if (u->f == STOCK) {
1704 /* stock -> tableu */
1705 /*remove a card from each pile and put it back onto the stock:*/
1706 for (int pile = NUM_PILES-1; pile >= 0; pile--) {
1707 int top = find_top(f.t[pile]);
1708 f.s[f.z++] = f.t[pile][top];
1709 f.t[pile][top] = NO_CARD;
1710 }
1711 } else if (u->t == FOUNDATION) {
1712 /* tableu -> foundation */
1713 int top = find_top(f.t[u->f]);
1714 /* close topcard if previous action caused turn_over(): */
1715 if (u->o) f.t[u->f][top] *= -1;
1716 /* append cards from foundation to tableu */
1717 for (int i = RANK_K; i >= RANK_A; i--) {
1718 f.t[u->f][++top] = f.f[u->n][i];
1719 f.f[u->n][i] = NO_CARD;
1720 }
1721 f.w--; /* decrement complete-foundation-counter */
1722
1723 } else {
1724 /* tableu -> tableu */
1725 int top_f = find_top(f.t[u->f]);
1726 int top_t = find_top(f.t[u->t]);
1727 /* close topcard if previous action caused turn_over(): */
1728 if (u->o) f.t[u->f][top_f] *= -1;
1729 /* move n cards from tableu[f] to tableu[t]: */
1730 for (int i = 0; i < u->n; i++) {
1731 f.t[u->f][top_f+u->n-i] = f.t[u->t][top_t-i];
1732 f.t[u->t][top_t-i] = NO_CARD;
1733 }
1734 }
1735 #elif defined FREECELL
1736 /*NOTE: if from and to are both stock/foundation, opt = from | to<<16 */
1737 if (u->f == STOCK && u->t == FOUNDATION) {
1738 /* free cells -> foundation */
1739 /* split u->n into cll and fnd: */
1740 int cll = u->n & 0xffff;
1741 int fnd = u->n >> 16;
1742 /* move one card from foundation to free cell: */
1743 int top = find_top(f.f[fnd]);
1744 f.s[cll] = f.f[fnd][top];
1745 f.f[fnd][top] = NO_CARD;
1746 f.w |= 1<<cll; /* mark cell as occupied */
1747 } else if (u->f == STOCK) {
1748 /* free cells -> cascade */
1749 int top_t = find_top(f.t[u->t]);
1750 f.s[u->n] = f.t[u->t][top_t];
1751 f.t[u->t][top_t] = NO_CARD;
1752 f.w |= 1<<u->n; /* mark cell as occupied */
1753 } else if (u->f == FOUNDATION && u->t == STOCK) {
1754 /* foundation -> free cells */
1755 /* split u->n into cll and fnd: */
1756 int cll = u->n >> 16;
1757 int fnd = u->n & 0xffff;
1758 /* move 1 card from free cell to foundation: */
1759 int top_f = find_top(f.f[fnd]);
1760 f.f[fnd][top_f+1] = f.s[cll];
1761 f.s[cll] = NO_CARD;
1762 f.w &= ~(1<<cll); /* mark cell as free */
1763 } else if (u->f == FOUNDATION) {
1764 /* foundation -> cascade */
1765 int top_f = find_top(f.f[u->n]);
1766 int top_t = find_top(f.t[u->t]);
1767 f.f[u->n][top_f+1] = f.t[u->t][top_t];
1768 f.t[u->t][top_t] = NO_CARD;
1769 } else if (u->t == STOCK) {
1770 /* cascade -> free cells */
1771 int top_f = find_top(f.t[u->f]);
1772 f.t[u->f][top_f+1] = f.s[u->n];
1773 f.s[u->n] = NO_CARD;
1774 f.w &= ~(1<<u->n); /* mark cell as free */
1775 } else if (u->t == FOUNDATION) {
1776 /* cascade -> foundation */
1777 int top_f = find_top(f.t[u->f]);
1778 int top_t = find_top(f.f[u->n]);
1779 /* move one card from foundation to cascade: */
1780 f.t[u->f][top_f+1] = f.f[u->n][top_t];
1781 f.f[u->n][top_t] = NO_CARD;
1782 } else {
1783 /* cascade -> cascade */
1784 int top_f = find_top(f.t[u->f]);
1785 int top_t = find_top(f.t[u->t]);
1786 /* move n cards from tableu[f] to tableu[t]: */
1787 for (int i = 0; i < u->n; i++) {
1788 f.t[u->f][top_f+u->n-i] = f.t[u->t][top_t-i];
1789 f.t[u->t][top_t-i] = NO_CARD;
1790 }
1791 }
1792 #endif
1793
1794 void* old = f.u;
1795 f.u = f.u->prev;
1796 free(old);
1797 }
1798 void free_undo (struct undo* u) {
1799 while (u && u != &undo_sentinel) {
1800 void* old = u;
1801 u = u->prev;
1802 free (old);
1803 }
1804 }
1805 //}}}
1806
1807 // initialization stuff {{{
1808 void screen_setup (int enable) {
1809 if (enable) {
1810 raw_mode(1);
1811 printf ("\033[s\033[?47h"); /* save cursor, alternate screen */
1812 printf ("\033[H\033[J"); /* reset cursor, clear screen */
1813 printf ("\033[?1000h"); /* enable mouse */
1814 } else {
1815 printf ("\033[?1000l"); /* disable mouse */
1816 printf ("\033[?47l\033[u"); /* primary screen, restore cursor */
1817 raw_mode(0);
1818 }
1819 }
1820
1821 void raw_mode(int enable) {
1822 static struct termios saved_term_mode;
1823 struct termios raw_term_mode;
1824
1825 if (enable) {
1826 if (saved_term_mode.c_lflag == 0)/*don't overwrite stored mode*/
1827 tcgetattr(STDIN_FILENO, &saved_term_mode);
1828 raw_term_mode = saved_term_mode;
1829 raw_term_mode.c_lflag &= ~(ICANON | ECHO);
1830 raw_term_mode.c_cc[VMIN] = 1 ;
1831 raw_term_mode.c_cc[VTIME] = 0;
1832 tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term_mode);
1833 } else {
1834 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term_mode);
1835 }
1836 }
1837
1838 void signal_handler (int signum) {
1839 struct winsize w;
1840 switch (signum) {
1841 case SIGTSTP:
1842 screen_setup(0);
1843 signal(SIGTSTP, SIG_DFL); /* NOTE: assumes SysV semantics! */
1844 raise(SIGTSTP);
1845 break;
1846 case SIGCONT:
1847 screen_setup(1);
1848 print_table(NO_HI, NO_HI);
1849 break;
1850 case SIGINT: //TODO: don't exit; just warn like vim does
1851 exit(128+SIGINT);
1852 case SIGWINCH:
1853 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
1854 op.w[0] = w.ws_row;
1855 op.w[1] = w.ws_col;
1856 break;
1857 }
1858 }
1859 void signal_setup(void) {
1860 struct sigaction saction;
1861
1862 saction.sa_handler = signal_handler;
1863 sigemptyset(&saction.sa_mask);
1864 saction.sa_flags = 0;
1865 if (sigaction(SIGTSTP, &saction, NULL) < 0) {
1866 perror ("SIGTSTP");
1867 exit (1);
1868 }
1869 if (sigaction(SIGCONT, &saction, NULL) < 0) {
1870 perror ("SIGCONT");
1871 exit (1);
1872 }
1873 if (sigaction(SIGINT, &saction, NULL) < 0) {
1874 perror ("SIGINT");
1875 exit (1);
1876 }
1877 if (sigaction(SIGWINCH, &saction, NULL) < 0) {
1878 perror ("SIGWINCH");
1879 exit (1);
1880 }
1881 }
1882 //}}}
1883
1884 //vim: foldmethod=marker
Imprint / Impressum