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