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