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