]> git.gir.st - solVItaire.git/blob - sol.c
spider works, minor print cleanup
[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 raw_mode(1); //TODO: alt.screen, etc.
94 sol(); //TODO: restart, etc.
95 raw_mode(0);
96 }
97
98 void sol(void) {
99 deal();
100
101 int from, to;
102 print_table();
103 while (!get_cmd(&from, &to)) {
104 if (action[from][to](from,to)) {
105 printf ("\033[?5h"); fflush (stdout);
106 usleep (100000);
107 printf ("\033[?5l"); fflush (stdout);
108 }
109 print_table();
110 }
111 }
112
113 int find_top(card_t* pile) {
114 int i;
115 for(i=MAX_HIDDEN+NUM_RANKS-1; i>=0 && !pile[i]; i--);
116 return i;
117 }
118 void turn_over(card_t* pile) {
119 int top = find_top(pile);
120 if (pile[top] < 0) pile[top] *= -1;
121 }
122 #ifdef KLONDIKE
123 card_t stack_take(void) { /*NOTE: assert(f.w >= 0) */
124 card_t card = f.s[f.w];
125 /* move stack one over, so there are no gaps in it: */
126 for (int i = f.w; i < f.z-1; i++)
127 f.s[i] = f.s[i+1];
128 f.z--;
129 f.w--; /* make previous card visible again */
130 return card;
131 }
132 int t2f(int from, int to) { /* tableu to foundation */
133 from--; //remove off-by-one
134 int top_from = find_top(f.t[from]);
135 to = get_suit(f.t[from][top_from]);
136 int top_to = find_top(f.f[to]);
137 if ((top_to < 0 && get_rank(f.t[from][top_from]) == RANK_A)
138 || (get_rank(f.f[to][top_to]) == get_rank(f.t[from][top_from])-1)) {
139 f.f[to][top_to+1] = f.t[from][top_from];
140 f.t[from][top_from] = NO_CARD;
141 turn_over(f.t[from]);
142 return 0;
143 } else return 1;
144 }
145 int w2f(int from, int to) { /* waste to foundation */
146 if (f.w < 0) return 1;
147 to = get_suit(f.s[f.w]);
148 int top_to = find_top(f.f[to]);
149 if ((top_to < 0 && get_rank(f.s[f.w]) == RANK_A)
150 || (get_rank(f.f[to][top_to]) == get_rank(f.s[f.w])-1)) {
151 f.f[to][top_to+1] = stack_take();
152 return 0;
153 } else return 1;
154
155 }
156 int s2w(int from, int to) { /* stock to waste */
157 if (f.z == 0) return 1;
158 f.w++;
159 if (f.w == f.z) f.w = -1;
160 return 0;
161 }
162 int w2s(int from, int to) { /* waste to stock (undoes stock to waste) */
163 if (f.z == 0) return 1;
164 f.w--;
165 if (f.w < -1) f.w = f.z-1;
166 return 0;
167 }
168 int f2t(int from, int to) { /* foundation to tableu */
169 //TODO: there are two possible cards one can take. choosing one isn't implemented yet!
170 to--; //remove off-by-one
171 int top_to = find_top(f.t[to]);
172 from = get_color(f.t[to][top_to]);
173 int top_from = find_top(f.f[from]);
174 if ((get_color(f.t[to][top_to]) != get_color(f.f[from][top_from]))
175 && (get_rank(f.t[to][top_to]) == get_rank(f.f[from][top_from])+1)) {
176 f.t[to][top_to+1] = stack_take(); //XXX: not stack_take!
177 return 0;
178 } else return 1;
179 }
180 int w2t(int from, int to) { //waste to tableu
181 to--; //remove off-by-one
182 int top_to = find_top(f.t[to]);
183 if (((get_color(f.t[to][top_to]) != get_color(f.s[f.w]))
184 && (get_rank(f.t[to][top_to]) == get_rank(f.s[f.w])+1))
185 || (top_to < 0 && get_rank(f.s[f.w]) == RANK_K)) {
186 f.t[to][top_to+1] = stack_take();
187 return 0;
188 } else return 1;
189 }
190 int t2t(int from, int to) {
191 from--; to--; //remove off-by-one
192 int top_to = find_top(f.t[to]);
193 int top_from = find_top(f.t[from]);
194 for (int i = top_from; i >=0; i--) {
195 //TODO: check that we aren't moving facedown cards!
196 if (((get_color(f.t[to][top_to]) != get_color(f.t[from][i]))
197 && (get_rank(f.t[to][top_to]) == get_rank(f.t[from][i])+1))
198 || (top_to < 0 && get_rank(f.t[from][i]) == RANK_K)) {
199 /* move cards [i..top_from] to their destination */
200 for (;i <= top_from; i++) {
201 top_to++;
202 f.t[to][top_to] = f.t[from][i];
203 f.t[from][i] = NO_CARD;
204 }
205 turn_over(f.t[from]);
206 return 0;
207 }
208 }
209 return 1; /* no such move possible */
210 }
211 #elif defined SPIDER
212 int t2t(int from, int to) { //TODO: in dire need of cleanup
213 from--; to--; //remove off-by-one
214 (from < 0) && (from = 9); // '0' is tenth ([9]) pile
215 (to < 0) && (to = 9); // ditto
216
217 int top_from = find_top(f.t[from]);
218 int top_to = find_top(f.t[to]);
219 for (int i = top_from; i >= 0; i--) {
220 if ((f.t[from][i+1] != NO_CARD) // if there is a card below (TODO: check maximum)
221 && (get_rank(f.t[from][i+1]) != get_rank(f.t[from][i])-1) //and cards not consecutive?
222 ) {
223 break;
224 }
225 if ((f.t[from][i+1] != NO_CARD) // if there is a card below (TODO: check maximum)
226 && (get_suit(f.t[from][i+1]) != get_suit(f.t[from][i])) //and cards not same suit?
227 ) {
228 break;
229 }
230
231 if(get_rank(f.t[from][i]) == get_rank(f.t[to][top_to])-1) { //TODO: to empty pile
232 for (;i <= top_from; i++) {
233 top_to++;
234 f.t[to][top_to] = f.t[from][i];
235 f.t[from][i] = NO_CARD;
236 }
237 turn_over(f.t[from]);
238 //TODO: test if k..a complete; move to foundation if so
239 return 0;
240 }
241 }
242
243 return 1; /* no such move possible */
244 }
245 int s2t(int from, int to) {
246 if (f.z <= 0) return 1; /* stack out of cards */
247 for (int pile = 0; pile < NUM_PILES; pile++)
248 if (f.t[pile][0] == NO_CARD) return 1; /*no piles may be empty*/
249 for (int pile = 0; pile < NUM_PILES; pile++) {
250 f.t[pile][find_top(f.t[pile])+1] = f.s[--f.z];
251 }
252 return 0;
253 }
254 #endif
255 int nop(int from, int to) { return 1; }
256
257 int get_cmd (int* from, int* to) {
258 //returns 0 on success or an error code indicating game quit, new game,...
259 //TODO: check validity
260 char f, t;
261 f = getchar();
262 #ifdef SPIDER
263 if (f=='\n') {
264 *from = 10;
265 *to = 0;
266 return CMD_MOVE;
267 }
268 #endif
269 switch (f) {
270 case 'q': return CMD_QUIT;
271 case 'r': return CMD_NEW;
272 default: if (f < '0' || f > '9') return CMD_INVAL;
273 }
274 t =
275 #ifdef KLONDIKE
276 (f=='8')?'9':
277 #endif
278 getchar();
279 *from = f-'0';
280 *to = t-'0';
281 return CMD_MOVE;
282 }
283
284 void deal(void) {
285 f = (const struct playfield){0}; /* clear playfield */
286 card_t deck[DECK_SIZE*NUM_DECKS];
287 int avail = DECK_SIZE*NUM_DECKS;
288 for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) deck[i] = (i%DECK_SIZE)+1;
289 #ifdef SPIDER
290 //if spider-mode easy/medium:
291 for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) {
292 int easy = 0;
293 int medium = 1; //XXX
294 if (easy||medium) deck[i] = 1+((deck[i]-1) | 2);
295 if (easy ) deck[i] = 1+((deck[i]-1) | 1);
296 // the 1+ -1 dance gets rid of the offset created by NO_CARD
297 }
298 #endif
299 srandom (time(NULL));
300 for (int i = DECK_SIZE*NUM_DECKS-1; i > 0; i--) { //fisher-yates
301 int j = random() % (i+1);
302 if (j-i) deck[i]^=deck[j],deck[j]^=deck[i],deck[i]^=deck[j];
303 }
304
305 /* deal cards: */
306 for (int i = 0; i < NUM_PILES; i++) {
307 #ifdef KLONDIKE
308 int closed = i; // tableu pile n has n closed cards, then 1 open
309 #elif defined SPIDER
310 int closed = i<4?5:4; //tableu pile 1-4 have 5, 5-10 have 4 closed cards
311 #endif
312
313 for (int j = 0; j < closed; j++) f.t[i][j] = -deck[--avail]; //face-down cards are negative
314 f.t[i][closed] = deck[--avail]; //the face-up card
315 }
316 //rest of the cards to the stock (should be 50 for spider):
317 for (f.z = 0; avail; f.z++) f.s[f.z] = deck[--avail];
318 f.w = -1; /* @start: nothing on waste (no waste in spider -> const) */
319 }
320
321 void print_table(void) { //{{{
322 printf("\033[2J\033[H");//TEMP XXX -- use raw term mode
323 #ifdef KLONDIKE
324 //print stock, waste and foundation:
325 for (int line = 0; line < op.s->height; line++) {
326 printf ("%s", ( /* stock */
327 (f.w < f.z-1)?op.s->facedown
328 :op.s->placeholder)[line]);
329 printf ("%s", ( /* waste */
330 ((short)f.w >= 0)?op.s->card[f.s[f.w]] //TODO: sometimes segfaults because f.w == (int)((short)-1)
331 :op.s->placeholder)[line]);
332 printf ("%s", op.s->card[NO_CARD][line]); /* spacer */
333 /* foundation: */
334 for (int pile = 0; pile < NUM_SUITS; pile++) {
335 for (int i = NUM_RANKS-1; i >=0; i--) {
336 if (f.f[pile][i]) {
337 printf ("%s", op.s->card[f.f[pile][i]][line]);
338 goto next;
339 }
340 }
341 printf ("%s", op.s->placeholder[line]);
342 next:;
343 }
344 printf("\n");
345 }
346 printf("\n");
347 #endif
348 //print tableu piles:
349 int row[NUM_PILES] = {0};
350 int line[NUM_PILES]= {0};
351 int line_had_card; // :|
352 int label[NUM_PILES] = {0};//XXX
353 do {
354 line_had_card = 0;
355 for (int pile = 0; pile < NUM_PILES; pile++) {
356 card_t card = f.t[pile][row[pile]];
357 card_t next = f.t[pile][row[pile]+1];
358 printf ("%s", (
359 (card<0)?op.s->facedown
360 :op.s->card[card]
361 )[line[pile]]);
362
363 if (++line[pile] >= (next?op.s->overlap:op.s->height)) {
364 //TODO: allow extreme overlap on sequences
365 line[pile]=0;
366 row[pile]++;
367 }
368 if(!card && !label[pile]) label[pile] = 1, printf ("\b\b%d ", (pile+1) % 10);//XXX: print tableu labels
369 line_had_card |= !!card;
370 }
371 printf ("\n");
372 } while (line_had_card);
373 }//}}}
374
375 void append_undo (int n, int f, int t) {
376 //check if we have to free redo buffer (.next)
377 //malloc
378 //update pointers
379 *NULL;
380 }
381
382 void raw_mode(int enable) { //{{{
383 static struct termios saved_term_mode;
384 struct termios raw_term_mode;
385
386 if (enable) {
387 tcgetattr(STDIN_FILENO, &saved_term_mode);
388 raw_term_mode = saved_term_mode;
389 raw_term_mode.c_lflag &= ~(ICANON | ECHO);
390 raw_term_mode.c_cc[VMIN] = 1 ;
391 raw_term_mode.c_cc[VTIME] = 0;
392 tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term_mode);
393 } else {
394 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term_mode);
395 }
396 } //}}}
397
398 //vim: foldmethod=marker
Imprint / Impressum