]> git.gir.st - solVItaire.git/blob - sol.c
fix spider:foundation-overlap for unicode_small_mono (req. cleanup)
[solVItaire.git] / sol.c
1 #define _DEFAULT_SOURCE
2 #include <poll.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <time.h>
6 #include <termios.h>
7 #include <unistd.h>
8
9 #include "sol.h"
10 #include "schemes.h"
11
12 #ifdef KLONDIKE
13 #define NUM_PILES 7
14 #define MAX_HIDDEN 6 /*how many cards are turned over at most in a tableu pile*/
15 #define MAX_STOCK 24 /*how many cards can be in the stock at most (=@start)*/
16 #define NUM_DECKS 1
17 #define PILE_SIZE MAX_HIDDEN+NUM_RANKS
18 #elif defined SPIDER
19 #define MAX_HIDDEN 5
20 #define NUM_PILES 10
21 #define MAX_STOCK 50 /*how many cards can be dealt onto the piles*/
22 #define NUM_DECKS 2
23 #define PILE_SIZE DECK_SIZE*NUM_DECKS /* no maximum stack size in spider :/ */
24 #endif
25
26 #define get_suit(card) \
27 ((card-1) % NUM_SUITS)
28 #define get_rank(card) \
29 ((card-1) / NUM_SUITS)
30 #define get_color(card) \
31 ((get_suit(card) ^ get_suit(card)>>1) & 1)
32
33 #define is_tableu(where) (where < STOCK)
34
35 struct playfield {
36 card_t s[MAX_STOCK]; /* stock */
37 int z; /* stock size */
38 int w; /* waste; index into stock (const -1 in spider) */
39 card_t f[NUM_DECKS*NUM_SUITS][PILE_SIZE]; /* foundation */
40 card_t t[NUM_PILES][PILE_SIZE]; /* tableu piles */
41 struct undo {
42 int f; /* pile cards were taken from */
43 int t; /* pile cards were moved to */
44 int n; /* number of cards moved (if tableu; else index of stock/foundation) */
45 struct undo* prev;
46 struct undo* next;
47 }* u;
48 } f;
49 struct opts {
50 #ifdef SPIDER
51 int m; /* difficulty mode */
52 #endif
53 const struct scheme* s;
54 } op;
55
56 // action table {{{
57 /* stores a function pointer for every takeable action; called by game loop */
58 int (*action[NUM_PLACES][10])(int,int,int) = {
59 #ifdef KLONDIKE
60 /* 1 2 3 4 5 6 7 stk wst fnd*/
61 /* 1 */ { t2f, t2t, t2t, t2t, t2t, t2t, t2t, nop, nop, t2f },
62 /* 2 */ { t2t, t2f, t2t, t2t, t2t, t2t, t2t, nop, nop, t2f },
63 /* 3 */ { t2t, t2t, t2f, t2t, t2t, t2t, t2t, nop, nop, t2f },
64 /* 4 */ { t2t, t2t, t2t, t2f, t2t, t2t, t2t, nop, nop, t2f },
65 /* 5 */ { t2t, t2t, t2t, t2t, t2f, t2t, t2t, nop, nop, t2f },
66 /* 6 */ { t2t, t2t, t2t, t2t, t2t, t2f, t2t, nop, nop, t2f },
67 /* 7 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2f, nop, nop, t2f },
68 /*stk*/ { nop, nop, nop, nop, nop, nop, nop, nop, s2w, nop },
69 /*wst*/ { w2t, w2t, w2t, w2t, w2t, w2t, w2t, w2s, w2f, w2f },
70 /*fnd*/ { f2t, f2t, f2t, f2t, f2t, f2t, f2t, nop, nop, nop },
71 #elif defined SPIDER
72 /* 1 2 3 4 5 6 7 8 9 10*/
73 /* 1 */ { nop, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
74 /* 2 */ { t2t, nop, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
75 /* 3 */ { t2t, t2t, nop, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
76 /* 4 */ { t2t, t2t, t2t, nop, t2t, t2t, t2t, t2t, t2t, t2t },
77 /* 5 */ { t2t, t2t, t2t, t2t, nop, t2t, t2t, t2t, t2t, t2t },
78 /* 6 */ { t2t, t2t, t2t, t2t, t2t, nop, t2t, t2t, t2t, t2t },
79 /* 7 */ { t2t, t2t, t2t, t2t, t2t, t2t, nop, t2t, t2t, t2t },
80 /* 8 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, nop, t2t, t2t },
81 /* 9 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, nop, t2t },
82 /*10 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, nop },
83 /*stk*/ { s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t },
84 #endif
85 };
86 // }}}
87
88 int main(int argc, char** argv) {
89 (void) argc;(void) argv;
90 op.s = &unicode_large_color;
91 #ifdef SPIDER
92 op.m = MEDIUM; //TODO: make configurable
93 op.m = EASY;
94 #endif
95 screen_setup(1);
96 sol(); //TODO: restart, etc.
97 screen_setup(0);
98 }
99
100 void sol(void) {
101 deal();
102
103 int from, to, opt;
104 print_table(NO_HI);
105 for(;;) {
106 switch (get_cmd(&from, &to, &opt)) {
107 case CMD_MOVE:
108 switch (action[from][to](from,to,opt)) {
109 case OK: break;
110 case ERR: visbell(); break;
111 case WON:
112 print_table(NO_HI);
113 win_anim();
114 getchar(); /* consume char left by win_anim() */
115 return;
116 }
117 break;
118 case CMD_INVAL:
119 visbell();
120 break;
121 case CMD_QUIT: return;
122 }
123 print_table(NO_HI);
124 }
125 }
126
127 int find_top(card_t* pile) {
128 int i;
129 for(i=PILE_SIZE-1; i>=0 && !pile[i]; i--);
130 return i;
131 }
132 void turn_over(card_t* pile) {
133 int top = find_top(pile);
134 if (pile[top] < 0) pile[top] *= -1;
135 }
136 int check_won(void) {
137 for (int pile = 0; pile < NUM_DECKS*NUM_SUITS; pile++)
138 if (f.f[pile][NUM_RANKS-1] == NO_CARD) return 0;
139
140 return 1;
141 }
142 void win_anim(void) {
143 printf ("\033[?25l"); /* hide cursor */
144 for (;;) {
145 /* set cursor to random location */
146 int row = 1+random()%(24-op.s->width);
147 int col = 1+random()%(80-op.s->height);
148
149 /* draw random card */
150 int face = 1 + random() % 52;
151 for (int l = 0; l < op.s->height; l++) {
152 printf ("\033[%d;%dH", row+l, col);
153 printf ("%s", op.s->card[face][l]);
154 }
155 fflush (stdout);
156
157 /* exit on keypress */
158 struct pollfd p = {STDIN_FILENO, POLLIN, 0};
159 if (poll (&p, 1, 80)) goto fin;
160 }
161 fin:
162 printf ("\033[?25h"); /* show cursor */
163 return;
164 }
165 // takeable actions {{{
166 #ifdef KLONDIKE
167 card_t stack_take(void) { /*NOTE: assert(f.w >= 0) */
168 card_t card = f.s[f.w];
169 /* move stack one over, so there are no gaps in it: */
170 for (int i = f.w; i < f.z-1; i++)
171 f.s[i] = f.s[i+1];
172 f.z--;
173 f.w--; /* make previous card visible again */
174 return card;
175 }
176 int t2f(int from, int to, int opt) { /* tableu to foundation */
177 (void) to; (void) opt; //don't need
178 int top_from = find_top(f.t[from]);
179 to = get_suit(f.t[from][top_from]);
180 int top_to = find_top(f.f[to]);
181 if ((top_to < 0 && get_rank(f.t[from][top_from]) == RANK_A)
182 || (top_to >= 0 && get_rank(f.f[to][top_to]) == get_rank(f.t[from][top_from])-1)) {
183 f.f[to][top_to+1] = f.t[from][top_from];
184 f.t[from][top_from] = NO_CARD;
185 turn_over(f.t[from]);
186 if (check_won()) return WON;
187 return OK;
188 } else return ERR;
189 }
190 int w2f(int from, int to, int opt) { /* waste to foundation */
191 (void) from; (void) to; (void) opt; //don't need
192 if (f.w < 0) return ERR;
193 to = get_suit(f.s[f.w]);
194 int top_to = find_top(f.f[to]);
195 if ((top_to < 0 && get_rank(f.s[f.w]) == RANK_A)
196 || (top_to >= 0 && get_rank(f.f[to][top_to]) == get_rank(f.s[f.w])-1)) {
197 f.f[to][top_to+1] = stack_take();
198 if (check_won()) return WON;
199 return OK;
200 } else return ERR;
201
202 }
203 int s2w(int from, int to, int opt) { /* stock to waste */
204 (void) from; (void) to; (void) opt; //don't need
205 if (f.z == 0) return ERR;
206 f.w++;
207 if (f.w == f.z) f.w = -1;
208 return OK;
209 }
210 int w2s(int from, int to, int opt) { /* waste to stock (undoes stock to waste) */
211 (void) from; (void) to; (void) opt; //don't need
212 if (f.z == 0) return ERR;
213 f.w--;
214 if (f.w < -1) f.w = f.z-1;
215 return OK;
216 }
217 int f2t(int from, int to, int opt) { /* foundation to tableu */
218 int top_to = find_top(f.t[to]);
219 from = opt;
220 int top_from = find_top(f.f[from]);
221
222 if ((get_color(f.t[to][top_to]) != get_color(f.f[from][top_from]))
223 && (get_rank(f.t[to][top_to]) == get_rank(f.f[from][top_from])+1)) {
224 f.t[to][top_to+1] = f.f[from][top_from];
225 f.f[from][top_from] = NO_CARD;
226 return OK;
227 } else return ERR;
228 }
229 int w2t(int from, int to, int opt) { /* waste to tableu */
230 (void) from; (void) opt; //don't need
231 int top_to = find_top(f.t[to]);
232 if (((get_color(f.t[to][top_to]) != get_color(f.s[f.w]))
233 && (get_rank(f.t[to][top_to]) == get_rank(f.s[f.w])+1))
234 || (top_to < 0 && get_rank(f.s[f.w]) == RANK_K)) {
235 f.t[to][top_to+1] = stack_take();
236 return OK;
237 } else return ERR;
238 }
239 int t2t(int from, int to, int opt) { /* tableu to tableu */
240 (void) opt; //don't need
241 int top_to = find_top(f.t[to]);
242 int top_from = find_top(f.t[from]);
243 for (int i = top_from; i >=0; i--) {
244 if (((get_color(f.t[to][top_to]) != get_color(f.t[from][i]))
245 && (get_rank(f.t[to][top_to]) == get_rank(f.t[from][i])+1)
246 && f.t[from][i] > NO_CARD) /* card face up? */
247 || (top_to < 0 && get_rank(f.t[from][i]) == RANK_K)) {
248 /* move cards [i..top_from] to their destination */
249 for (;i <= top_from; i++) {
250 top_to++;
251 f.t[to][top_to] = f.t[from][i];
252 f.t[from][i] = NO_CARD;
253 }
254 turn_over(f.t[from]);
255 return OK;
256 }
257 }
258 return ERR; /* no such move possible */
259 }
260 #elif defined SPIDER
261 int is_consecutive (card_t* pile, int pos) {
262 if (pos+1 >= PILE_SIZE) return 1; /* card is last */
263 if (pile[pos+1] == NO_CARD) return 1; /* card is first */
264
265 if (get_rank(pile[pos+1]) != get_rank(pile[pos])-1) return 0; /*rank consecutive?*/
266 if (get_suit(pile[pos+1]) != get_suit(pile[pos])) return 0; /*same suit?*/
267
268 return 1;
269 }
270 void remove_if_complete (card_t* pile) { //TODO: cleanup
271 static int foundation = 0; /* where to put pile onto (1 set per stack)*/
272 /* test if K...A complete; move to foundation if so */
273 int top_from = find_top(pile);
274 if (get_rank(pile[top_from]) != RANK_A) return;
275 for (int i = top_from; i>=0; i--) {
276 if (!is_consecutive (pile, i)) return;
277 if (i+RANK_K == top_from
278 && get_rank(pile[top_from-RANK_K]) == RANK_K) { //ace to king ok, remove it
279 for (int i = top_from, j = 0; i > top_from-NUM_RANKS; i--, j++) {
280 f.f[foundation][j] = pile[i];
281 pile[i] = NO_CARD;
282 }
283 foundation++;
284 turn_over(pile);
285 return;
286 }
287 }
288 }
289 int t2t(int from, int to, int opt) { //TODO: in dire need of cleanup
290 //TODO: segfaulted once on large column
291 //TODO: sometimes moving doesn't work (ERR when it should be OK) XXX
292
293 int top_from = find_top(f.t[from]);
294 int top_to = find_top(f.t[to]);
295 int empty_to = (top_to < 0)? opt: -1; /* empty pile? */
296
297 for (int i = top_from; i >= 0; i--) {
298 if (!is_consecutive(f.t[from], i)) break;
299
300 if ((get_rank(f.t[from][i]) == get_rank(f.t[to][top_to])-1) // consecutive?
301 || (empty_to >= RANK_A && get_rank(f.t[from][i]) == empty_to)) { //to empty pile and rank ok?
302 for (;i <= top_from; i++) {
303 top_to++;
304 f.t[to][top_to] = f.t[from][i];
305 f.t[from][i] = NO_CARD;
306 }
307 turn_over(f.t[from]);
308 remove_if_complete (f.t[to]);
309 if (check_won()) return WON;
310 return OK;
311 }
312 }
313
314 return ERR; /* no such move possible */
315 }
316 int s2t(int from, int to, int opt) {
317 (void) from; (void) to; (void) opt; //don't need
318 if (f.z <= 0) return ERR; /* stack out of cards */
319 for (int pile = 0; pile < NUM_PILES; pile++)
320 if (f.t[pile][0]==NO_CARD) return ERR; /*no piles may be empty*/
321 for (int pile = 0; pile < NUM_PILES; pile++) {
322 f.t[pile][find_top(f.t[pile])+1] = f.s[--f.z];
323 remove_if_complete (f.t[pile]); //XXX: needs testing
324 if (check_won()) return WON;
325 }
326 return OK;
327 }
328 #endif
329 int nop(int from, int to, int opt) { (void)from;(void)to;(void)opt;return ERR; }
330 // }}}
331
332 int get_cmd (int* from, int* to, int* opt) {
333 //returns 0 on success or an error code indicating game quit, new game,...
334 //TODO: escape sequences (mouse, cursor keys)
335 //TODO: don't allow taking from empty piles
336 int _f, t;
337 _f = getchar();
338
339 switch (_f) {
340 /* direct addressing: */ //TODO: cleanup empty pile check
341 case '1': *from = TAB_1; break;
342 case '2': *from = TAB_2; break;
343 case '3': *from = TAB_3; break;
344 case '4': *from = TAB_4; break;
345 case '5': *from = TAB_5; break;
346 case '6': *from = TAB_6; break;
347 case '7': *from = TAB_7; break;
348 #ifdef SPIDER
349 case '8': *from = TAB_8; break;
350 case '9': *from = TAB_9; break;
351 case '0': *from = TAB_10;break;
352 #elif defined KLONDIKE
353 case '9': *from = WASTE; break;
354 case '0': *from = FOUNDATION; break;
355 case '8': /* fallthrough */
356 #endif
357 case '\n': /* shortcut for dealing from stock */
358 *from = STOCK;
359 *to = WASTE;
360 return CMD_MOVE;
361 /* cursor keys addressing: */
362 //TODO
363 case 'q': return CMD_QUIT;
364 case 'r': return CMD_NEW; //TODO
365 case 'h': return CMD_HINT; //TODO
366 case '?': return CMD_HELP; //TODO
367 case '/': return CMD_FIND; //TODO: highlight card of given rank (even non-movable)
368 case '\033': return CMD_INVAL; //TODO: cntlseq
369 default: return CMD_INVAL;
370 }
371 if (is_tableu(*from) && f.t[*from][0] == NO_CARD) return CMD_INVAL;
372 print_table(*from);
373
374 t = getchar();
375 if (t < '0' || t > '9') return CMD_INVAL;
376 if (t == '0')
377 #ifdef KLONDIKE
378 *to = FOUNDATION;
379 #elif defined SPIDER
380 *to = TAB_10;
381 #endif
382 else
383 *to = t-'1';
384 #ifdef KLONDIKE
385 if (*from == FOUNDATION) {
386 int top = find_top(f.t[*to]);
387 if (top < 0) return CMD_INVAL;
388 int color = get_color(f.t[*to][top]);
389 int choice_1 = 1-color; /* selects piles of */
390 int choice_2 = 2+color; /* the opposite color */
391 int top_c1 = find_top(f.f[choice_1]);
392 int top_c2 = find_top(f.f[choice_2]);
393
394 switch ((top_c1 >= 0 && get_rank(f.t[*to][top])-1
395 == get_rank(f.f[choice_1][top_c1])) << 0 |
396 (top_c2 >= 0 && get_rank(f.t[*to][top])-1
397 == get_rank(f.f[choice_2][top_c2])) << 1) {
398 case ( 1<<0): *opt = choice_1; break; /* choice_1 only */
399 case (1<<1 ): *opt = choice_2; break; /* choice_2 only */
400 case (1<<1 | 1<<0): /* both, ask user which to pick from */
401 printf ("take from (1-4): "); fflush (stdout);
402 *opt = getchar() - '1';
403 if (*opt < 0 || *opt > 3) return CMD_INVAL;
404 break;
405 default: return CMD_INVAL; /* none matched */
406 }
407 /* `opt` is the foundation index (0..3) */
408 }
409 #elif defined SPIDER
410 if (is_tableu(*to) && f.t[*to][0] == NO_CARD) { /*moving to empty tableu?*/
411 int top = find_top(f.t[*from]);
412 if (top < 0) return CMD_INVAL;
413 if (top >= 0 && !is_movable(f.t[*from], top-1)) {
414 *opt = get_rank(f.t[*from][top]);
415 } else { /* only ask the user if it's unclear */
416 printf ("\rup to (a23456789xjqk): ");
417 *opt = getchar();
418 switch (*opt) {
419 case 'a': case 'A': *opt = RANK_A; break;
420 case '0': /* fallthrough */
421 case 'x': case 'X': *opt = RANK_X; break;
422 case 'j': case 'J': *opt = RANK_J; break;
423 case 'q': case 'Q': *opt = RANK_Q; break;
424 case 'k': case 'K': *opt = RANK_K; break;
425 default: *opt -= '1';
426 }
427 if (*opt < RANK_A || *opt > RANK_K) return ERR;
428 }
429 /* `opt` is the rank of the highest card to move */
430 }
431 #endif
432 return CMD_MOVE;
433 }
434
435 void deal(void) {
436 f = (const struct playfield){0}; /* clear playfield */
437 card_t deck[DECK_SIZE*NUM_DECKS];
438 int avail = DECK_SIZE*NUM_DECKS;
439 for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) deck[i] = (i%DECK_SIZE)+1;
440 #ifdef SPIDER
441 if (op.m != NORMAL) for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) {
442 if (op.m == MEDIUM) deck[i] = 1+((deck[i]-1) | 2);
443 if (op.m == EASY) deck[i] = 1+((deck[i]-1) | 2 | 1);
444 /* the 1+ -1 dance gets rid of the offset created by NO_CARD */
445 }
446 #endif
447 srandom (time(NULL));
448 long seed = time(NULL);
449 srandom (seed);
450 for (int i = DECK_SIZE*NUM_DECKS-1; i > 0; i--) { //fisher-yates
451 int j = random() % (i+1);
452 if (j-i) deck[i]^=deck[j],deck[j]^=deck[i],deck[i]^=deck[j];
453 }
454
455 /* deal cards: */
456 for (int i = 0; i < NUM_PILES; i++) {
457 #ifdef KLONDIKE
458 int closed = i; /* pile n has n closed cards, then 1 open */
459 #elif defined SPIDER
460 int closed = i<4?5:4; /* pile 1-4 have 5, 5-10 have 4 closed */
461 #endif
462 /* face down cards are negated: */
463 for (int j = 0; j < closed; j++) f.t[i][j] = -deck[--avail];
464 f.t[i][closed] = deck[--avail]; /* the face-up card */
465 }
466 /* rest of the cards to the stock; NOTE: assert(avail==50) for spider */
467 for (f.z = 0; avail; f.z++) f.s[f.z] = deck[--avail];
468 f.w = -1; /* @start: nothing on waste (no waste in spider -> const) */
469 }
470
471 int is_movable(card_t* pile, int n) { //TODO cleanup, code deduplication, needs entry in sol.h
472 #ifdef KLONDIKE
473 return(pile[n] > NO_CARD); /*non-movable cards don't exist in klondike*/
474 #elif defined SPIDER
475 int top = find_top(pile);
476 for (int i = top; i >= 0; i--) {
477 if (pile[i] <= NO_CARD) return 0; //card face down?
478 if (!is_consecutive(pile, i)) return 0;
479 if (i == n) return 1; //card reached, must be movable
480 }
481 return 0;
482 #endif
483 }
484 #define print_hi(invert, bold, str) /* for highlighting during get_cmd() */ \
485 printf ("%s%s%s%s%s", (bold)?"\033[1m":"", (invert)?"\033[7m":"", str, \
486 (invert)?"\033[27m":"", (bold)?"\033[22m":"")
487 void print_table(int highlight) { //{{{
488 printf("\033[2J\033[H"); /* clear screen, reset cursor */
489 #ifdef KLONDIKE
490 /* print stock, waste and foundation: */
491 for (int line = 0; line < op.s->height; line++) {
492 /* stock: */
493 print_hi (highlight == STOCK, 1, (
494 (f.w < f.z-1)?op.s->facedown
495 :op.s->placeholder)[line]);
496 /* waste: */
497 print_hi (highlight == WASTE, 1, (
498 /* NOTE: cast, because f.w sometimes is (short)-1 !? */
499 ((short)f.w >= 0)?op.s->card[f.s[f.w]]
500 :op.s->placeholder)[line]);
501 printf ("%s", op.s->card[NO_CARD][line]); /* spacer */
502 /* foundation: */
503 for (int pile = 0; pile < NUM_SUITS; pile++) {
504 int card = find_top(f.f[pile]);
505 print_hi (highlight == FOUNDATION, 1,
506 (card < 0)?op.s->placeholder[line]
507 :op.s->card[f.f[pile][card]][line]);
508 }
509 printf("\n");
510 }
511 printf("\n");
512 #elif SPIDER
513 for (int line = 0; line < op.s->height; line++) {
514 /* available stock: */
515 for (int i = f.z/10; i; i--) {
516 if (i==1) printf ("%s", op.s->facedown[line]);
517 else printf ("%s", op.s->halfstack[line]);
518 }
519 /* spacer: */ //TODO: urgh! cleanup! also breaks on unicode_small_mono!
520 int xx = 0; for(int i=0;i<NUM_DECKS*NUM_SUITS;i++)if(f.f[i][RANK_K])xx++; //XXX (number of finished foundations)
521 int HALFWIDTH = op.s->halfwidth[0]; //TODO: magic values!
522 int RIGHTWIDTH = op.s->halfwidth[1]; //TODO: magic values!
523 for (int i = f.z?(f.z/10-1)*HALFWIDTH + op.s->width:0;
524 i < NUM_PILES*op.s->width - ((xx?(xx-1)*RIGHTWIDTH:0)+op.s->width);
525 i++)
526 printf (" ");
527 /* foundation (overlapping): */
528 for (int i = 0; i < NUM_DECKS*NUM_SUITS; i++) {
529 int overlap = i? op.s->halfcard[line]: 0;
530 if (f.f[i][RANK_K]) printf ("%.*s", op.s->halfwidth[2],
531 op.s->card[f.f[i][RANK_K]][line]+overlap);
532 }
533 printf("\n");
534 }
535 printf("\n");
536 #endif
537 /* print tableu piles: */
538 int row[NUM_PILES] = {0};
539 int line[NUM_PILES]= {0};
540 int label[NUM_PILES]={0};// :|
541 int line_had_card; // :|
542 do {
543 line_had_card = 0;
544 for (int pile = 0; pile < NUM_PILES; pile++) {
545 card_t card = f.t[pile][row[pile]];
546 card_t next = f.t[pile][row[pile]+1];
547 int movable = is_movable(f.t[pile], row[pile]);
548
549 print_hi (highlight == pile && movable, movable, (
550 (card<0)?op.s->facedown
551 :op.s->card[card]
552 )[line[pile]]);
553
554 if (++line[pile] >= (next?op.s->overlap:op.s->height) //normal overlap
555 #if 0 //XXX
556 || (line[pile] >= 1 &&
557 f.t[pile][row[pile]] < 0 &&
558 f.t[pile][row[pile]+1] <0) //extreme overlap on closed
559 || (0) //extreme overlap on sequence TODO
560 #endif
561 ) {
562 line[pile]=0;
563 row[pile]++;
564 }
565 if(!card && !label[pile]) { /* tableu labels: */
566 label[pile] = 1;
567 printf ("\b\b%d ", (pile+1) % 10); //XXX: hack
568 }
569 line_had_card |= !!card;
570 }
571 printf ("\n");
572 } while (line_had_card);
573 }//}}}
574
575 void visbell (void) {
576 printf ("\033[?5h"); fflush (stdout);
577 usleep (100000);
578 printf ("\033[?5l"); fflush (stdout);
579 }
580
581 void append_undo (int n, int f, int t) {
582 (void)n;(void)f;(void)t;
583 //check if we have to free redo buffer (.next)
584 //malloc
585 //update pointers
586 //TODO: undo; needs operations to be written by x2y()
587 }
588
589 void screen_setup (int enable) {
590 if (enable) {
591 raw_mode(1);
592 printf ("\033[s\033[?47h"); /* save cursor, alternate screen */
593 printf ("\033[H\033[J"); /* reset cursor, clear screen */
594 //TODO//printf ("\033[?1000h\033[?25l"); /* enable mouse, hide cursor */
595 } else {
596 //TODO//printf ("\033[?9l\033[?25h"); /* disable mouse, show cursor */
597 printf ("\033[?47l\033[u"); /* primary screen, restore cursor */
598 raw_mode(0);
599 }
600 }
601
602 void raw_mode(int enable) { //{{{
603 static struct termios saved_term_mode;
604 struct termios raw_term_mode;
605
606 if (enable) {
607 tcgetattr(STDIN_FILENO, &saved_term_mode);
608 raw_term_mode = saved_term_mode;
609 raw_term_mode.c_lflag &= ~(ICANON | ECHO);
610 raw_term_mode.c_cc[VMIN] = 1 ;
611 raw_term_mode.c_cc[VTIME] = 0;
612 tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term_mode);
613 } else {
614 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term_mode);
615 }
616 } //}}}
617
618 //vim: foldmethod=marker
Imprint / Impressum