]> git.gir.st - solVItaire.git/blob - sol.c
freecell: join() to foundation: fix fencepost error
[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 nop /* for join()'s "to foundation" */
556 #define w2t nop /* ditto. */
557 #endif
558
559 //TODO: generalize prediction engine for CMD_HINT
560 #ifdef KLONDIKE
561 #define would_complete(pile) 0
562 #elif defined SPIDER
563 #define would_complete(pile) \
564 (get_rank(f.t[pile][r[pile].top]) == RANK_A \
565 && get_rank(f.t[to][bottom_to]) == RANK_K)
566 #elif defined FREECELL
567 #define would_complete(pile) 0
568 #endif
569 #define would_turn(pile) \
570 (f.t[pile][r[pile].pos-1] < 0)
571 #define would_empty(pile) \
572 (r[pile].pos == 0)
573
574 int join(int to) {
575 int top_to = find_top(f.t[to]);
576 #ifdef SPIDER
577 int bottom_to = first_movable(f.t[to]);
578 #endif
579
580 #if defined KLONDIKE || defined FREECELL
581 if (to == WASTE || to == STOCK) return ERR; /*why would you do that!?*/
582
583 if (to == FOUNDATION) {
584 int status = ERR;
585 for (int i = 0; i <= TAB_MAX+1; i++)
586 switch ((i?t2f:w2f)(i-1, FOUNDATION, 0)) {
587 case WON: return WON;
588 case OK: status = OK;
589 case ERR: /* nop */;
590 }
591 return status;
592 }
593 #endif
594
595 #ifdef KLONDIKE
596 if (top_to < 0) { /* move a king to empty pile: */
597 for (int i = 0; i <= TAB_MAX; i++) {
598 if (f.t[i][0] < 0) /* i.e. would turn? */
599 if (t2t(i, to, 0) == OK) return OK;
600 }
601 return w2t(WASTE, to, 0);
602 }
603 #elif defined FREECELL
604 if (top_to < 0) { /* move longest cascade to empty tableu: */ //TODO FREECELL:
605 int longest = -1;
606 int length = -1;
607 for (int i = 0; i <= TAB_MAX; i++) {
608 int m = max_move(i, to);
609 /*longest cascade that won't uncover another free pile*/
610 //TODO: don't rip apart cascades
611 if (m >= length && m <= find_top(f.t[i]))
612 length = m, longest = i;
613 }
614 if (longest < 0) return ERR;
615 return t2t(longest, to, length);
616 }
617 #endif
618
619 struct rating {
620 int ok:1; /* card to move in pile? */
621 int above; /* number of movable cards above */
622 int below; /* number of cards below ours */
623 int pos; /* where the card to move is in the pile */
624 int top; /* find_top() */
625 } r[NUM_PILES] = {{0}};
626 int complete = 0;/* SPIDER: true if any pile would complete a stack */
627 int turn = 0; /* SPIDER: true if any pile would turn_over */
628 int empty = 0; /* true if any pile would become empty */
629
630 /* 1. rate each pile: */
631 #ifdef SPIDER
632 if (top_to < 0) {
633 for (int pile = 0; pile < NUM_PILES; pile++) {
634 if (pile == to) continue;
635 int top = find_top(f.t[pile]);
636 int bottom = first_movable(f.t[pile]);
637 r[pile].pos = bottom; /* need for would_empty */
638
639 if (top < 0) continue; /* no cards to move */
640 if (would_empty(pile)) continue; /* doesn't help */
641
642 r[pile].ok++;
643 r[pile].above = 0; /* always take as many as possible */
644 r[pile].below = top - bottom;
645 r[pile].top = top;
646 complete |= would_complete(pile); /* never happens */
647 turn |= would_turn(pile);
648 empty |= would_empty(pile);
649 }
650 } else
651 #endif
652 for (int pile = 0; pile < NUM_PILES; pile++) {
653 r[pile].top = r[pile].pos = find_top(f.t[pile]);
654 /* backtrack until we find a compatible-to-'to'-pile card: */
655 #ifdef FREECELL
656 int maxmove = max_move(pile, -1);
657 #endif
658 while (r[pile].pos >= 0 && is_movable(f.t[pile], r[pile].pos)) {
659 int rankdiff = get_rank(f.t[pile][r[pile].pos])
660 - get_rank(f.t[to][top_to]);
661 if (rankdiff >= 0) break; /* past our card */
662 #ifdef FREECELL
663 if (!maxmove--) break; /* can't move this many cards */
664 #endif
665 if (rankdiff == -1 && /* rank matches */
666 color_ok(f.t[pile][r[pile].pos], f.t[to][top_to])
667 ) {
668 r[pile].ok++;
669 complete |= would_complete(pile);
670 turn |= would_turn(pile);
671 empty |= would_empty(pile);
672 for (int i = r[pile].pos; i >= 0; i--)
673 if (is_movable(f.t[pile], i-1))
674 r[pile].above++;
675 else break;
676 break;
677 }
678 r[pile].pos--;
679 r[pile].below++;
680 }
681 }
682
683 /* 2. find optimal pile: (optimized for spider) */
684 //todo: in spider, prefer longest piles if above==0 (faster completions)
685 int from = -1;
686 for (int pile = 0, above = 99, below = 99; pile < NUM_PILES; pile++) {
687 if (!r[pile].ok) continue;
688 /* don't bother if another pile would be better: prefer ... */
689 /* ... to complete a stack: */
690 if (!would_complete(pile) && complete) continue;
691 /* ... emptying piles: */
692 if (!would_empty(pile) && empty && !complete) continue;
693 /* ... to turn_over: */
694 if (!would_turn(pile) && turn && !complete && !empty) continue;
695 /* ... not to rip apart too many cards: */
696 if (r[pile].above > above) continue;
697 /* if tied, prefer ... */
698 if (r[pile].above == above
699 /* ... larger pile if destination is empty */
700 && (top_to < 0? r[pile].below < below
701 /* ... shorter pile otherwise */
702 : r[pile].below > below))
703 continue;
704
705 from = pile;
706 above = r[pile].above;
707 below = r[pile].below;
708 }
709
710 /* 3. move cards over and return: */
711 #ifdef KLONDIKE
712 /* prefer waste if it wouldn't turn_over: */
713 /* NOTE: does not attempt to take from froundation */
714 if (!empty && !turn && w2t(WASTE, to, 0) == OK)
715 return OK;
716 if (from < 0) /* nothing found */
717 return ERR;
718 return t2t(from, to, 0);
719 #elif defined SPIDER
720 if (from < 0) /* nothing found */
721 return ERR;
722 int bottom = first_movable(f.t[from]);
723 return t2t(from, to, get_rank(f.t[from][bottom]));
724 #elif defined FREECELL
725 //TODO: if would rip apart, try freecells first (instead after)
726 if (from < 0) /* no tableu move found */ {
727 /* try all free cells before giving up: */
728 for (int i = 0; i < NUM_CELLS; i++)
729 if (c2t(STOCK, to, i) == OK) return OK;
730 return ERR;
731 }
732 return t2t(from, to, 0);
733 #endif
734 }
735 #undef would_empty
736 #undef would_turn
737 #undef would_complete
738 int nop(int from, int to, int opt) { (void)from;(void)to;(void)opt;return ERR; }
739 // }}}
740
741 // keyboard input handling {{{
742 // cursor functions{{{
743 #ifdef KLONDIKE
744 void cursor_left (struct cursor* cursor) {
745 op.h = 1;
746 if (is_tableu(cursor->pile)) {
747 if (cursor->pile > 0) cursor->pile--;
748 cursor->opt = 0;
749 } else { /* stock/waste/foundation*/
750 switch (cursor->pile) {
751 case WASTE: cursor->pile = STOCK; cursor->opt = 0; break;
752 case FOUNDATION:
753 if (cursor->opt <= 0)
754 cursor->pile = WASTE;
755 else
756 cursor->opt--;
757 }
758 }
759 }
760 void cursor_down (struct cursor* cursor) {
761 op.h = 1;
762 if (!is_tableu(cursor->pile)) {
763 switch (cursor->pile) {
764 case STOCK: cursor->pile = TAB_1; break;
765 case WASTE: cursor->pile = TAB_2; break;
766 case FOUNDATION:
767 cursor->pile = TAB_4 + cursor->opt;
768 }
769 cursor->opt = 0;
770 }
771 }
772 void cursor_up (struct cursor* cursor) {
773 op.h = 1;
774 if (is_tableu(cursor->pile)) {
775 switch (cursor->pile) { //ugly :|
776 case TAB_1: cursor->pile = STOCK; break;
777 case TAB_2: cursor->pile = WASTE; break;
778 case TAB_3: cursor->pile = WASTE; break;
779 case TAB_4: case TAB_5: case TAB_6: case TAB_7:
780 cursor->opt=cursor->pile-TAB_4;
781 cursor->pile = FOUNDATION;
782 break;
783 }
784 }
785 }
786 void cursor_right (struct cursor* cursor) {
787 op.h = 1;
788 if (is_tableu(cursor->pile)) {
789 if (cursor->pile < TAB_MAX) cursor->pile++;
790 cursor->opt = 0;
791 } else {
792 switch (cursor->pile) {
793 case STOCK: cursor->pile = WASTE; break;
794 case WASTE: cursor->pile = FOUNDATION;cursor->opt = 0; break;
795 case FOUNDATION:
796 if (cursor->opt < NUM_SUITS-1)
797 cursor->opt++;
798 }
799 }
800 }
801 #elif defined SPIDER
802 /*NOTE: one can't highlight the stock due to me being too lazy to implement it*/
803 void cursor_left (struct cursor* cursor) {
804 op.h = 1;
805 if (cursor->pile > 0) cursor->pile--;
806 cursor->opt = 0;
807 }
808 void cursor_down (struct cursor* cursor) {
809 op.h = 1;
810 int first = first_movable(f.t[cursor->pile]);
811 int top = find_top(f.t[cursor->pile]);
812 if (first + cursor->opt < top)
813 cursor->opt++;
814 }
815 void cursor_up (struct cursor* cursor) {
816 op.h = 1;
817 if (cursor->opt > 0) cursor->opt--;
818 }
819 void cursor_right (struct cursor* cursor) {
820 op.h = 1;
821 if (cursor->pile < TAB_MAX) cursor->pile++;
822 cursor->opt = 0;
823 }
824 #elif defined FREECELL
825 void cursor_left (struct cursor* cursor) {
826 op.h = 1;
827 if (is_tableu(cursor->pile)) {
828 if (cursor->pile > 0) cursor->pile--;
829 cursor->opt = 0;
830 } else { /* cells/foundation*/
831 switch (cursor->pile) {
832 case STOCK:
833 if (cursor->opt > 0)
834 cursor->opt--;
835 break;
836 case FOUNDATION:
837 if (cursor->opt <= 0) {
838 cursor->pile = STOCK;
839 cursor->opt = 3;
840 } else {
841 cursor->opt--;
842 }
843 }
844 }
845 }
846 void cursor_down (struct cursor* cursor) {
847 op.h = 1;
848 if (is_tableu(cursor->pile)) {
849 if (cursor->opt < max_move(cursor->pile, -1)-1)
850 cursor->opt++;
851 } else {
852 cursor->pile = cursor->opt+NUM_CELLS*(cursor->pile==FOUNDATION);
853 cursor->opt = 0;
854 }
855 }
856 void cursor_up (struct cursor* cursor) {
857 op.h = 1;
858 if (is_tableu(cursor->pile)) {
859 if (cursor->opt > 0) {
860 cursor->opt--;
861 } else {
862 switch (cursor->pile) {
863 case TAB_1: case TAB_2: case TAB_3: case TAB_4:
864 cursor->opt = cursor->pile; /*assumes TAB_1==0*/
865 cursor->pile = STOCK;
866 break;
867 case TAB_5: case TAB_6: case TAB_7: case TAB_8:
868 cursor->opt = cursor->pile - NUM_CELLS;
869 cursor->pile = FOUNDATION;
870 }
871 }
872 }
873 }
874 void cursor_right (struct cursor* cursor) {
875 op.h = 1;
876 if (is_tableu(cursor->pile)) {
877 if (cursor->pile < TAB_MAX) cursor->pile++;
878 cursor->opt = 0;
879 } else {
880 switch (cursor->pile) {
881 case STOCK:
882 if (cursor->opt < NUM_SUITS-1) {
883 cursor->opt++;
884 } else {
885 cursor->pile = FOUNDATION;
886 cursor->opt = 0;
887 } break;
888 case FOUNDATION:
889 if (cursor->opt < NUM_SUITS-1)
890 cursor->opt++;
891 }
892 }
893 }
894 #endif
895 void cursor_to (struct cursor* cursor, int pile) {
896 op.h = 1;
897 cursor->pile = pile;
898 cursor->opt = 0;
899 }
900 int set_mouse(int pile, int* main, int* opt) {
901 //TODO: this should set cursor.opt, so card selector choice dialog does not trigger!
902 op.h = 0;
903 if (pile < 0) return 1;
904 *main = pile;
905 #ifdef KLONDIKE
906 if (pile >= FOUNDATION)//TODO: check upper bound!
907 *main = FOUNDATION,
908 *opt = pile - FOUNDATION;
909 #elif defined SPIDER
910 (void)opt;
911 #elif defined FREECELL
912 if (pile > TAB_MAX) {
913 *main = pile-STOCK < NUM_CELLS? STOCK : FOUNDATION;
914 *opt = (pile-STOCK) % 4;
915 }
916 #endif
917 return 0;
918 }
919 //}}}
920 int get_cmd (int* from, int* to, int* opt) {
921 int _f, t;
922 unsigned char mouse[6] = {0}; /* must clear [3]! */
923 struct cursor inactive = {-1,-1};
924 static struct cursor active = {0,0};
925 if (is_tableu(active.pile))
926 active.opt = 0;
927
928 /***/
929 from_l: print_table(&active, &inactive);
930 _f = getch(mouse);
931
932 switch (_f) {
933 /* direct addressing: */
934 case '1': *from = TAB_1; break;
935 case '2': *from = TAB_2; break;
936 case '3': *from = TAB_3; break;
937 case '4': *from = TAB_4; break;
938 case '5': *from = TAB_5; break;
939 case '6': *from = TAB_6; break;
940 case '7': *from = TAB_7; break;
941 #ifdef SPIDER
942 case '8': *from = TAB_8; break;
943 case '9': *from = TAB_9; break;
944 case '0': *from = TAB_10;break;
945 #elif defined FREECELL
946 case '8': *from = TAB_8; break;
947 case '9': *from = STOCK; break;
948 case '0': *from = FOUNDATION; break;
949 #elif defined KLONDIKE
950 case '9': *from = WASTE; break;
951 case '0': *from = FOUNDATION; break;
952 case '8': /* fallthrough */
953 #endif
954 #ifndef FREECELL
955 case '\n': *from = STOCK; break;
956 #endif
957 /* cursor keys addressing: */
958 case KEY_LEFT:
959 case 'h': cursor_left (&active); goto from_l;
960 case KEY_DOWN:
961 case 'j': cursor_down (&active); goto from_l;
962 case KEY_UP:
963 case 'k': cursor_up (&active); goto from_l;
964 case KEY_RIGHT:
965 case 'l': cursor_right(&active); goto from_l;
966 case KEY_HOME:
967 case 'H': cursor_to(&active,TAB_1); goto from_l; /* leftmost tableu */
968 case KEY_END:
969 case 'L': cursor_to(&active,TAB_MAX);goto from_l; /* rigthmost tableu */
970 case KEY_INS:
971 case 'M': cursor_to(&active,TAB_MAX/2); goto from_l; /* center tableu */
972 case ' ': /* continue with second cursor */
973 *from = active.pile;
974 #ifdef KLONDIKE
975 *opt = active.opt; /* when FOUNDATION */
976 #endif
977 inactive = active;
978 break;
979 /* mouse addressing: */
980 case MOUSE_MIDDLE: return CMD_NONE;
981 case MOUSE_RIGHT:
982 if (set_mouse(term2pile(mouse), to, opt))
983 return CMD_INVAL;
984 return CMD_JOIN;
985 case MOUSE_LEFT:
986 if (set_mouse(term2pile(mouse), from, opt))
987 return CMD_INVAL;
988 if (!is_tableu(*from))
989 inactive.opt = *opt; /* prevents card selector dialog */
990 break;
991 /* misc keys: */
992 case ':':
993 {char buf[256];
994 fprintf (stderr, ":");
995 raw_mode(0); /* turn on echo */
996 fgets (buf, 256, stdin);
997 raw_mode(1);
998 switch(buf[0]) {
999 case 'q': return CMD_QUIT;
1000 case 'n': return CMD_NEW;
1001 case 'r': return CMD_AGAIN;
1002 case 'h': return CMD_HELP;
1003 default: return CMD_INVAL;
1004 }}
1005 case 'J':
1006 *to = active.pile;
1007 return CMD_JOIN;
1008 case 'K': /* fallthrough */
1009 case '?': return CMD_HINT;
1010 case 'u': return CMD_UNDO;
1011 case 002: return CMD_NONE; /* sent by SIGWINCH */
1012 case EOF: return CMD_NONE; /* sent by SIGCONT */
1013 default: return CMD_INVAL;
1014 }
1015 inactive.pile = *from; /* for direct addressing highlighting */
1016 if (is_tableu(*from) && f.t[*from][0] == NO_CARD) return CMD_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