]> git.gir.st - solVItaire.git/blob - sol.c
cleanup print function
[solVItaire.git] / sol.c
1 #define _DEFAULT_SOURCE
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <time.h>
5 #include <termios.h>
6 #include <unistd.h>
7
8 #include "sol.h"
9 #include "schemes.h"
10
11 #ifdef KLONDIKE
12 #define NUM_PILES 7
13 #define MAX_HIDDEN 6 /*how many cards are turned over at most in a tableu pile*/
14 #define MAX_STOCK 24 /*how many cards can be in the stock at most (=@start)*/
15 #define NUM_DECKS 1
16 #elif defined SPIDER
17 #define MAX_HIDDEN 5
18 #define NUM_PILES 10
19 #define MAX_STOCK 50 /*how many cards can be dealt onto the piles*/
20 #define NUM_DECKS 2
21 #endif
22
23 #define get_suit(card) \
24 ((card-1) % NUM_SUITS)
25 #define get_rank(card) \
26 ((card-1) / NUM_SUITS)
27 #define get_color(card) \
28 ((get_suit(card) ^ get_suit(card)>>1) & 1)
29
30 struct playfield {
31 //TODO: stock and waste are incompatible with undo{}
32 card_t s[MAX_STOCK]; /* stock */
33 int z; /* stock size */
34 int w; /* waste; index into stock (const -1 in spider) */
35 #ifdef KLONDIKE
36 card_t f[NUM_SUITS][MAX_HIDDEN+NUM_RANKS]; /* foundation (oversized to ease find_top()) */
37 card_t t[NUM_PILES][MAX_HIDDEN+NUM_RANKS]; /* tableu piles */
38 #elif defined SPIDER
39 card_t f[NUM_DECKS*NUM_COLORS][MAX_HIDDEN+NUM_RANKS]; //each completed set gets put on its own pile, so undo is possible
40 card_t t[NUM_PILES][MAX_HIDDEN+NUM_RANKS];
41 #endif
42 struct undo {
43 int from; /* pile cards were taken from */
44 int to; /* pile cards were moved to */
45 int n; /* number of cards moved */
46 struct undo* prev;
47 struct undo* next;
48 } u;
49 } f;
50 struct opts {
51 const struct scheme* s;
52 } op;
53
54 #ifdef KLONDIKE
55 //declare action as array 10 of array 10 of pointer to function (int,int) returning int
56 // this stores a function pointer for every takeable action that is then called by execute()
57 //lines = from, cols = to
58 int (*action[10][10])(int,int) = {
59 /* 0 1 2 3 4 5 6 7 8 9 */
60 /* 0 */ { nop, f2t, f2t, f2t, f2t, f2t, f2t, f2t, nop, nop },
61 /* 1 */ { t2f, nop, t2t, t2t, t2t, t2t, t2t, t2t, nop, nop },
62 /* 2 */ { t2f, t2t, nop, t2t, t2t, t2t, t2t, t2t, nop, nop },
63 /* 3 */ { t2f, t2t, t2t, nop, t2t, t2t, t2t, t2t, nop, nop },
64 /* 4 */ { t2f, t2t, t2t, t2t, nop, t2t, t2t, t2t, nop, nop },
65 /* 5 */ { t2f, t2t, t2t, t2t, t2t, nop, t2t, t2t, nop, nop },
66 /* 6 */ { t2f, t2t, t2t, t2t, t2t, t2t, nop, t2t, nop, nop },
67 /* 7 */ { t2f, t2t, t2t, t2t, t2t, t2t, t2t, nop, nop, nop },
68 /* 8 */ { nop, nop, nop, nop, nop, nop, nop, nop, nop, s2w },
69 /* 9 */ { w2f, w2t, w2t, w2t, w2t, w2t, w2t, w2t, w2s, nop },
70 };
71 #elif defined SPIDER
72 int (*action[11][10])(int,int) = {
73 //piles are zero-indexed in spider; stk=stock
74 /* 0 1 2 3 4 5 6 7 8 9 */
75 /* 0 */ { nop, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
76 /* 1 */ { t2t, nop, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
77 /* 2 */ { t2t, t2t, nop, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
78 /* 3 */ { t2t, t2t, t2t, nop, t2t, t2t, t2t, t2t, t2t, t2t },
79 /* 4 */ { t2t, t2t, t2t, t2t, nop, t2t, t2t, t2t, t2t, t2t },
80 /* 5 */ { t2t, t2t, t2t, t2t, t2t, nop, t2t, t2t, t2t, t2t },
81 /* 6 */ { t2t, t2t, t2t, t2t, t2t, t2t, nop, t2t, t2t, t2t },
82 /* 7 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, nop, t2t, t2t },
83 /* 8 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, nop, t2t },
84 /* 9 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, nop },
85 /*stk*/ { s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t },
86 };
87 #endif
88
89 int main(int argc, char** argv) {
90 //op.s = &unicode_small_mono;
91 //op.s = &unicode_large_mono;
92 op.s = &unicode_large_color;
93 screen_setup(1);
94 sol(); //TODO: restart, etc.
95 screen_setup(0);
96 }
97
98 void sol(void) {
99 deal();
100
101 int from, to;
102 print_table();
103 for(;;) {
104 switch (get_cmd(&from, &to)) {
105 case CMD_MOVE:
106 if (action[from][to](from,to))
107 visbell();
108 break;
109 case CMD_QUIT: return;
110 }
111 print_table();
112 }
113 }
114
115 int find_top(card_t* pile) {
116 int i;
117 for(i=MAX_HIDDEN+NUM_RANKS-1; i>=0 && !pile[i]; i--);
118 return i;
119 }
120 void turn_over(card_t* pile) {
121 int top = find_top(pile);
122 if (pile[top] < 0) pile[top] *= -1;
123 }
124 #ifdef KLONDIKE
125 card_t stack_take(void) { /*NOTE: assert(f.w >= 0) */
126 card_t card = f.s[f.w];
127 /* move stack one over, so there are no gaps in it: */
128 for (int i = f.w; i < f.z-1; i++)
129 f.s[i] = f.s[i+1];
130 f.z--;
131 f.w--; /* make previous card visible again */
132 return card;
133 }
134 int t2f(int from, int to) { /* tableu to foundation */
135 from--; //remove off-by-one
136 int top_from = find_top(f.t[from]);
137 to = get_suit(f.t[from][top_from]);
138 int top_to = find_top(f.f[to]);
139 if ((top_to < 0 && get_rank(f.t[from][top_from]) == RANK_A)
140 || (get_rank(f.f[to][top_to]) == get_rank(f.t[from][top_from])-1)) {
141 f.f[to][top_to+1] = f.t[from][top_from];
142 f.t[from][top_from] = NO_CARD;
143 turn_over(f.t[from]);
144 return 0;
145 } else return 1;
146 }
147 int w2f(int from, int to) { /* waste to foundation */
148 if (f.w < 0) return 1;
149 to = get_suit(f.s[f.w]);
150 int top_to = find_top(f.f[to]);
151 if ((top_to < 0 && get_rank(f.s[f.w]) == RANK_A)
152 || (get_rank(f.f[to][top_to]) == get_rank(f.s[f.w])-1)) {
153 f.f[to][top_to+1] = stack_take();
154 return 0;
155 } else return 1;
156
157 }
158 int s2w(int from, int to) { /* stock to waste */
159 if (f.z == 0) return 1;
160 f.w++;
161 if (f.w == f.z) f.w = -1;
162 return 0;
163 }
164 int w2s(int from, int to) { /* waste to stock (undoes stock to waste) */
165 if (f.z == 0) return 1;
166 f.w--;
167 if (f.w < -1) f.w = f.z-1;
168 return 0;
169 }
170 int f2t(int from, int to) { /* foundation to tableu */
171 //TODO: there are two possible cards one can take. choosing one isn't implemented yet!
172 to--; //remove off-by-one
173 int top_to = find_top(f.t[to]);
174 from = get_color(f.t[to][top_to]);
175 int top_from = find_top(f.f[from]);
176 if ((get_color(f.t[to][top_to]) != get_color(f.f[from][top_from]))
177 && (get_rank(f.t[to][top_to]) == get_rank(f.f[from][top_from])+1)) {
178 f.t[to][top_to+1] = stack_take(); //XXX: not stack_take!
179 return 0;
180 } else return 1;
181 }
182 int w2t(int from, int to) { //waste to tableu
183 to--; //remove off-by-one
184 int top_to = find_top(f.t[to]);
185 if (((get_color(f.t[to][top_to]) != get_color(f.s[f.w]))
186 && (get_rank(f.t[to][top_to]) == get_rank(f.s[f.w])+1))
187 || (top_to < 0 && get_rank(f.s[f.w]) == RANK_K)) {
188 f.t[to][top_to+1] = stack_take();
189 return 0;
190 } else return 1;
191 }
192 int t2t(int from, int to) {
193 from--; to--; //remove off-by-one
194 int top_to = find_top(f.t[to]);
195 int top_from = find_top(f.t[from]);
196 for (int i = top_from; i >=0; i--) {
197 //TODO: check that we aren't moving facedown cards!
198 if (((get_color(f.t[to][top_to]) != get_color(f.t[from][i]))
199 && (get_rank(f.t[to][top_to]) == get_rank(f.t[from][i])+1))
200 || (top_to < 0 && get_rank(f.t[from][i]) == RANK_K)) {
201 /* move cards [i..top_from] to their destination */
202 for (;i <= top_from; i++) {
203 top_to++;
204 f.t[to][top_to] = f.t[from][i];
205 f.t[from][i] = NO_CARD;
206 }
207 turn_over(f.t[from]);
208 return 0;
209 }
210 }
211 return 1; /* no such move possible */
212 }
213 #elif defined SPIDER
214 int t2t(int from, int to) { //TODO: in dire need of cleanup
215 from--; to--; //remove off-by-one
216 (from < 0) && (from = 9); // '0' is tenth ([9]) pile
217 (to < 0) && (to = 9); // ditto
218
219 int top_from = find_top(f.t[from]);
220 int top_to = find_top(f.t[to]);
221 for (int i = top_from; i >= 0; i--) {
222 if ((f.t[from][i+1] != NO_CARD) // if there is a card below (TODO: check maximum)
223 && (get_rank(f.t[from][i+1]) != get_rank(f.t[from][i])-1) //and cards not consecutive?
224 ) {
225 break;
226 }
227 if ((f.t[from][i+1] != NO_CARD) // if there is a card below (TODO: check maximum)
228 && (get_suit(f.t[from][i+1]) != get_suit(f.t[from][i])) //and cards not same suit?
229 ) {
230 break;
231 }
232
233 if(get_rank(f.t[from][i]) == get_rank(f.t[to][top_to])-1) { //TODO: to empty pile
234 for (;i <= top_from; i++) {
235 top_to++;
236 f.t[to][top_to] = f.t[from][i];
237 f.t[from][i] = NO_CARD;
238 }
239 turn_over(f.t[from]);
240 //TODO: test if k..a complete; move to foundation if so
241 return 0;
242 }
243 }
244
245 return 1; /* no such move possible */
246 }
247 int s2t(int from, int to) {
248 if (f.z <= 0) return 1; /* stack out of cards */
249 for (int pile = 0; pile < NUM_PILES; pile++)
250 if (f.t[pile][0] == NO_CARD) return 1; /*no piles may be empty*/
251 for (int pile = 0; pile < NUM_PILES; pile++) {
252 f.t[pile][find_top(f.t[pile])+1] = f.s[--f.z];
253 }
254 return 0;
255 }
256 #endif
257 int nop(int from, int to) { return 1; }
258
259 int get_cmd (int* from, int* to) {
260 //returns 0 on success or an error code indicating game quit, new game,...
261 //TODO: check validity
262 char f, t;
263 f = getchar();
264 #ifdef SPIDER
265 if (f=='\n') {
266 *from = 10;
267 *to = 0;
268 return CMD_MOVE;
269 }
270 #endif
271 switch (f) {
272 case 'q': return CMD_QUIT;
273 case 'r': return CMD_NEW;
274 default: if (f < '0' || f > '9') return CMD_INVAL;
275 }
276 t =
277 #ifdef KLONDIKE
278 (f=='8')?'9':
279 #endif
280 getchar();
281 *from = f-'0';
282 *to = t-'0';
283 return CMD_MOVE;
284 }
285
286 void deal(void) {
287 f = (const struct playfield){0}; /* clear playfield */
288 card_t deck[DECK_SIZE*NUM_DECKS];
289 int avail = DECK_SIZE*NUM_DECKS;
290 for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) deck[i] = (i%DECK_SIZE)+1;
291 #ifdef SPIDER
292 //if spider-mode easy/medium:
293 for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) {
294 int easy = 0;
295 int medium = 1; //XXX
296 if (easy||medium) deck[i] = 1+((deck[i]-1) | 2);
297 if (easy ) deck[i] = 1+((deck[i]-1) | 1);
298 // the 1+ -1 dance gets rid of the offset created by NO_CARD
299 }
300 #endif
301 srandom (time(NULL));
302 for (int i = DECK_SIZE*NUM_DECKS-1; i > 0; i--) { //fisher-yates
303 int j = random() % (i+1);
304 if (j-i) deck[i]^=deck[j],deck[j]^=deck[i],deck[i]^=deck[j];
305 }
306
307 /* deal cards: */
308 for (int i = 0; i < NUM_PILES; i++) {
309 #ifdef KLONDIKE
310 int closed = i; // tableu pile n has n closed cards, then 1 open
311 #elif defined SPIDER
312 int closed = i<4?5:4; //tableu pile 1-4 have 5, 5-10 have 4 closed cards
313 #endif
314
315 for (int j = 0; j < closed; j++) f.t[i][j] = -deck[--avail]; //face-down cards are negative
316 f.t[i][closed] = deck[--avail]; //the face-up card
317 }
318 //rest of the cards to the stock (should be 50 for spider):
319 for (f.z = 0; avail; f.z++) f.s[f.z] = deck[--avail];
320 f.w = -1; /* @start: nothing on waste (no waste in spider -> const) */
321 }
322
323 #define print_hi(test, str) /*for highlighting during get_cmd() */ \
324 printf ("%s%s%s", test?"\033[7m":"", str, test?"\033[27m":"") //TODO
325 void print_table(void) { //{{{
326 printf("\033[2J\033[H"); /* clear screen, reset cursor */
327 #ifdef KLONDIKE
328 /* print stock, waste and foundation: */
329 for (int line = 0; line < op.s->height; line++) {
330 printf ("%s", ( /* stock */
331 (f.w < f.z-1)?op.s->facedown
332 :op.s->placeholder)[line]);
333 printf ("%s", ( /* waste */
334 /* NOTE: cast, because f.w sometimes is (short)-1 !? */
335 ((short)f.w >= 0)?op.s->card[f.s[f.w]]
336 :op.s->placeholder)[line]);
337 printf ("%s", op.s->card[NO_CARD][line]); /* spacer */
338 /* foundation: */
339 for (int pile = 0; pile < NUM_SUITS; pile++) {
340 int card = find_top(f.f[pile]);
341 printf ("%s",
342 (card < 0)?op.s->placeholder[line]
343 :op.s->card[f.f[pile][card]][line]);
344 }
345 printf("\n");
346 }
347 printf("\n");
348 #endif
349 /* print tableu piles: */
350 int row[NUM_PILES] = {0};
351 int line[NUM_PILES]= {0};
352 int label[NUM_PILES]={0};// :|
353 int line_had_card; // :|
354 do {
355 line_had_card = 0;
356 for (int pile = 0; pile < NUM_PILES; pile++) {
357 card_t card = f.t[pile][row[pile]];
358 card_t next = f.t[pile][row[pile]+1];
359 printf ("%s", (
360 (card<0)?op.s->facedown
361 :op.s->card[card]
362 )[line[pile]]);
363
364 if (++line[pile] >= (next?op.s->overlap:op.s->height) //normal overlap
365 || (line[pile] >= 1 && f.t[pile][row[pile]] < 0)) { //extreme overlap (on closed)
366 //TODO: allow extreme overlap on sequences
367 line[pile]=0;
368 row[pile]++;
369 }
370 if(!card && !label[pile]) { /* tableu labels: */
371 label[pile] = 1;
372 printf ("\b\b%d ", (pile+1) % 10); //XXX: hack
373 }
374 line_had_card |= !!card;
375 }
376 printf ("\n");
377 } while (line_had_card);
378 }//}}}
379
380 void visbell (void) {
381 printf ("\033[?5h"); fflush (stdout);
382 usleep (100000);
383 printf ("\033[?5l"); fflush (stdout);
384 }
385
386 void append_undo (int n, int f, int t) {
387 //check if we have to free redo buffer (.next)
388 //malloc
389 //update pointers
390 *NULL;
391 }
392
393 void screen_setup (int enable) {
394 if (enable) {
395 raw_mode(1);
396 printf ("\033[s\033[?47h"); /* save cursor, alternate screen */
397 printf ("\033[H\033[J"); /* reset cursor, clear screen */
398 printf ("\033[?1000h\033[?25l"); /* enable mouse, hide cursor */
399 if (op.s->init_seq)
400 printf (op.s->init_seq); /*swich charset, if necessary*/
401 } else {
402 if (op.s->reset_seq)
403 printf (op.s->reset_seq);/*reset charset, if necessary*/
404 printf ("\033[?9l\033[?25h"); /* disable mouse, show cursor */
405 printf ("\033[?47l\033[u"); /* primary screen, restore cursor */
406 raw_mode(0);
407 }
408 }
409
410 void raw_mode(int enable) { //{{{
411 static struct termios saved_term_mode;
412 struct termios raw_term_mode;
413
414 if (enable) {
415 tcgetattr(STDIN_FILENO, &saved_term_mode);
416 raw_term_mode = saved_term_mode;
417 raw_term_mode.c_lflag &= ~(ICANON | ECHO);
418 raw_term_mode.c_cc[VMIN] = 1 ;
419 raw_term_mode.c_cc[VTIME] = 0;
420 tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term_mode);
421 } else {
422 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term_mode);
423 }
424 } //}}}
425
426 //vim: foldmethod=marker
Imprint / Impressum