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