1 /*****************************************************************************/
2 /* */
3 /* */
4 /* X patience version 2 -- module moves.c */
5 /* */
6 /* General utility functions for all rule sets */
7 /* written by Heiko Eissfeldt and Michael Bischoff */
8 /* see COPYRIGHT.xpat2 for Copyright details */
9 /* */
10 /* */
11 /*****************************************************************************/
12 #include "xpatgame.h"
13
14 #include <time.h>
15 #include <limits.h> /* for INT_MAX */
16 #include <math.h>
17
18 /* RNG patch: */
19 int rng = 1;
myrand(int limit)20 static int myrand(int limit) {
21 /* select one of the RNGs. */
22 return rng ?
23 (int)(((double)(prand()) / (double)PRANDMAX) * (double)limit)
24 : prand() % limit;
25 }
26
Klondike_deal_cards(void)27 static Move Klondike_deal_cards(void) {
28 int rem = CARDS_ON_PILE(IDECK);
29
30 if (rem > rules.param[2])
31 rem = rules.param[2];
32 graphics_pile_control(Disable, IDECK);
33 graphics_pile_control(Disable, VDECK);
34 if (rem) {
35 int i;
36 /* without graphics: */
37 for (i = 0; i < rem; ++i)
38 do_move(INDEX_OF_LAST_CARD(IDECK), VDECK);
39 } else {
40 while (!EMPTY(VDECK))
41 do_move(INDEX_OF_LAST_CARD(VDECK), IDECK);
42 ++game.counter[1];
43 }
44 graphics_pile_control(EnableAndRedraw, IDECK);
45 graphics_pile_control(EnableAndRedraw, VDECK);
46 return NEW_CARDS_MOVE | MOVE(0, rem);
47 }
48
Klondike_undeal_cards(int num)49 static void Klondike_undeal_cards(int num) {
50 graphics_pile_control(Disable, IDECK);
51 graphics_pile_control(Disable, VDECK);
52 if (!num) {
53 /* undo flip */
54 while (!EMPTY(IDECK))
55 do_move(INDEX_OF_LAST_CARD(IDECK), VDECK);
56 --game.counter[1];
57 } else {
58 int i;
59 for (i = 0; i < num; ++i)
60 do_move(INDEX_OF_LAST_CARD(VDECK), IDECK);
61 }
62 graphics_pile_control(EnableAndRedraw, IDECK);
63 graphics_pile_control(EnableAndRedraw, VDECK);
64 }
65
SlotShuffle(int fwd)66 Move SlotShuffle(int fwd) {
67 int i, tab[MAXCARDS];
68 int num = INDEX_OF_FIRST_CARD(LAST_SLOT+1)
69 - INDEX_OF_FIRST_CARD(FIRST_SLOT);
70 Cardindex start = INDEX_OF_FIRST_CARD(FIRST_SLOT);
71
72 sprand(game.seed);
73 for (i = 0; i < num; ++i)
74 tab[i] = myrand(num - i) + i;
75 if (fwd) {
76 for (i = 0; i < num; ++i)
77 if (tab[i] != i) {
78 /* swap card */
79 Card c;
80 c = game.cards[start+i];
81 game.cards[start+i] = game.cards[start+tab[i]];
82 game.cards[start+tab[i]] = c;
83 }
84 } else {
85 for (i = num-1; i >= 0; --i)
86 if (tab[i] != i) {
87 /* swap card */
88 Card c;
89 c = game.cards[start+i];
90 game.cards[start+i] = game.cards[start+tab[i]];
91 game.cards[start+tab[i]] = c;
92 }
93 }
94 for (i = FIRST_SLOT; i <= LAST_SLOT; ++i)
95 draw_pileupdate(i, 0);
96 return SHUFFLING;
97 }
98
99 #ifdef DEBUG
100 static int type[200];
101
count(void)102 static void count(void) {
103 int i;
104 memset(type, 0, sizeof(type));
105 for (i = 0; i < rules.numcards; ++i)
106 ++type[game.cards[i]];
107 for (i = 0; i < 52; ++i)
108 if (type[i] != rules.numdecks)
109 fprintf(stderr, "Card %d: Number = %d\n", i, type[i]);
110 }
111 #endif
112
memo_alloc(int num)113 void memo_alloc(int num) {
114 game.numalloc = ((num - 1) | 0xf) + 1; /* multiple of 16 */
115 if (!(game.move = malloc(game.numalloc * sizeof(Move)))) {
116 fprintf(stderr, "out of memory\n");
117 exit(EXIT_FAILURE);
118 }
119 }
120
121
makespace(int n)122 static void makespace(int n) {
123 if (game.move_ptr + n > game.numalloc) {
124 Move *tmpnew;
125 if (!(tmpnew = realloc(game.move, (game.numalloc+256) * sizeof(Move)))) {
126 fprintf(stderr, "out of memory. saving game in xpat.tmp\n");
127 save_game("xpat.tmp");
128 exit(EXIT_FAILURE);
129 }
130 game.numalloc += 256;
131 game.move = tmpnew;
132 }
133 }
134
135 /* A new move is done. This resets the limit of redo to the current point. */
136 /* Also, the Bookmark is possibly taken back */
137 /* if the AUTOFILL_TMPS feature is set, empty tmps are filled from the deck */
store_move(Move m)138 void store_move(Move m) {
139 makespace(1);
140 if (game.bookmark > game.move_ptr) /* reset Bookmark position */
141 game.bookmark = game.move_ptr;
142 if ((rules.variant & AUTOFILL_TMPS) && (m & SPECIAL_MOVE) != SPECIAL_MOVE) {
143 Pileindex srcpile = DSTPILE(m >> 16);
144 if (game.piletype[srcpile] == Tmp && (!EMPTY(IDECK) || !EMPTY(VDECK))) {
145 /* this is the case we have to deal with */
146 makespace(3);
147 game.move[game.move_ptr++] = COMPOUND_BEGIN;
148 game.move[game.move_ptr++] = m;
149 game.move[game.move_ptr++] = !EMPTY(VDECK) ?
150 do_move(INDEX_OF_LAST_CARD(VDECK), srcpile) :
151 do_move(INDEX_OF_LAST_CARD(IDECK), srcpile);
152 m = COMPOUND_END;
153 }
154 }
155 game.move[game.move_ptr] = m;
156 game.stored_moves = ++game.move_ptr;
157 ++game.n_moves;
158
159 /* now that the work is done, check for finished game */
160 check_win_game();
161 }
162
undo_give_cards(int num)163 static void undo_give_cards(int num) {
164 /* num = number of cards */
165 /* printf("undo give %d cards\n", num); */
166 if (rules.undeal_cards)
167 (*rules.undeal_cards)(num);
168 else if (rules.variant & KLONDIKE_DEAL)
169 Klondike_undeal_cards(num);
170 else {
171 int i;
172 graphics_pile_control(Disable, IDECK);
173 for (i = FIRST_SLOT + num; i != FIRST_SLOT; --i)
174 /* slot i-1 back to deck */
175 do_move(game.ind[i]-1, IDECK);
176 graphics_pile_control(EnableAndRedraw, IDECK);
177 }
178 }
179
180
undo_move(void)181 int undo_move(void) {
182 Move m;
183 int retval = 1; /* no cheat */
184
185 if (!game.move_ptr) {
186 /* possibly undo restart game? */
187 if (game.stored_moves) {
188 jumpto_movenr(game.stored_moves); /* must replay the game */
189 return 1;
190 }
191 return 0;
192 }
193 m = game.move[--game.move_ptr];
194 --game.n_moves;
195 if ((m & SPECIAL_MOVE) == SPECIAL_MOVE) {
196 switch (m & SPECIAL_MASK) {
197 case COMPOUND_END:
198 {
199 int remember_count, remgraphic;
200 if ((remgraphic = game.graphic))
201 graphics_control(Disable);
202 /* undo multiple moves */
203 remember_count = game.n_moves;
204 while (game.move[game.move_ptr-1] != COMPOUND_BEGIN) {
205 if (undo_move() == 2)
206 retval = 2;
207 }
208 --game.move_ptr;
209 game.n_moves = remember_count;
210 if (remgraphic)
211 graphics_control(EnableAndRedraw);
212 }
213 return retval;
214 case ADD_CHEAT:
215 game.cheat_count += (int)(m & SPECIAL_ARGS);
216 return 2;
217 case NEW_CARDS_MOVE:
218 undo_give_cards(DSTPILE(m));
219 if (DSTPILE(m)) {
220 game.cheat_count += DSTPILE(m);
221 return 2;
222 }
223 return 1;
224 case ROTATE_UP:
225 /* change counter */
226 { int c;
227 for (c = 0; c < 4; ++c)
228 if (rules.paramstring[c] == TXTI_ROTATE)
229 game.counter[c] -= 2;
230 }
231 RotateDown(SRCIND(m));
232 return 1;
233 case ROTATE_DOWN:
234 /* change counter */
235 { int c;
236 for (c = 0; c < 4; ++c)
237 if (rules.paramstring[c] == TXTI_ROTATE)
238 game.counter[c] -= 2;
239 }
240 RotateUp(SRCIND(m));
241 return 1;
242 case SHUFFLING:
243 SlotShuffle(0);
244 return 1;
245 default:
246 /* printf("Move in error is %08lx\n", m); */
247 assert(0);
248 }
249 return 0;
250 }
251 /* standard move follows */
252 if (m & MOVE_TURNED) {
253 m >>= 16; /* upper part = undo information */
254 ++game.cheat_count;
255 game.visible[game.ind[DSTPILE(m)+1]-1] = 0; /* invisible again! */
256 retval = 2;
257 } else
258 m >>= 16; /* upper part = undo information */
259
260 (void)do_move(SRCIND(m), DSTPILE(m));
261 if (retval == 2)
262 draw_pileupdate(DSTPILE(m), 0); /* hide card again! */
263 return retval;
264 }
265
redo_move(void)266 int redo_move(void) {
267 Move m;
268 int retval = 1;
269 if (game.move_ptr == game.stored_moves)
270 return 0; /* no redo possible */
271 m = game.move[game.move_ptr++];
272 ++game.n_moves;
273 if ((m & SPECIAL_MOVE) == SPECIAL_MOVE) {
274 switch (m & SPECIAL_MASK) {
275 case COMPOUND_BEGIN:
276 {
277 int remember_count, remgraphic;
278 if ((remgraphic = game.graphic))
279 graphics_control(Disable);
280 /* redo multiple moves */
281 remember_count = game.n_moves;
282 while (game.move[game.move_ptr] != COMPOUND_END) {
283 if (redo_move() == 2)
284 retval = 2;
285 }
286 ++game.move_ptr;
287 game.n_moves = remember_count;
288 if (remgraphic)
289 graphics_control(EnableAndRedraw);
290 }
291 return retval;
292 case ADD_CHEAT:
293 game.cheat_count -= (int)(m & SPECIAL_ARGS);
294 return 2;
295 case ROTATE_UP:
296 RotateUp(SRCIND(m));
297 return 1;
298 case ROTATE_DOWN:
299 RotateDown(SRCIND(m));
300 return 1;
301 case NEW_CARDS_MOVE:
302 retval = DSTPILE(give_new_cards());
303 if (retval) {
304 game.cheat_count -= retval;
305 return 2;
306 }
307 return 1;
308 case SHUFFLING:
309 SlotShuffle(1);
310 return 1;
311 default:
312 assert(0);
313 }
314 }
315 if (m & MOVE_TURNED) {
316 --game.cheat_count;
317 retval = 2;
318 }
319 (void)do_move(SRCIND(m), DSTPILE(m));
320 return retval;
321 }
322
323
shuffle(void)324 static void shuffle(void) {
325 int i, d, v, c;
326 int tmp[MAXCARDS];
327
328 if (game.graphic)
329 show_message(" ");
330 i = 0;
331 for (d = 0; d < rules.numdecks; ++d)
332 for (v = 0; v < rules.cards_per_color; ++v)
333 for (c = 0; c < 4; ++c)
334 tmp[i++] = c + (v << 2);
335 assert(i <= rules.numcards);
336 c = 0;
337 while (i < rules.numcards)
338 tmp[i++] = JOKER + c++;
339 while (c < rules.numjokers)
340 tmp[myrand(rules.numcards)] = JOKER + c++; /* subst old cards */
341
342 for (i = rules.numcards; i; --i) {
343 v = myrand(i);
344 /* printf("rand() = %d\n", v); */
345 /* look for the vth non-empty card in tmp */
346 c = -1;
347 do {
348 while (tmp[++c] == -1)
349 ; /* skip card */
350 } while (v--);
351 game.cards[i-1] = tmp[c];
352 /* printf("c = %d, card = %d\n", c, game.cards[i-1]); */
353 tmp[c] = -1;
354 }
355 }
356
getpile(Cardindex ind)357 Pileindex getpile(Cardindex ind) {
358 int i;
359 assert(ind < rules.numcards);
360 i = 0;
361 while (ind >= game.ind[i+1])
362 ++i;
363 return i;
364 }
365
366 /* These are the central card moving routines, i.e. the only functions which */
367 /* change the contents of the game.cards array within a game. */
368 /* Every time anything is changed, one of these functions is called. */
369 /* We can reset hint tables at this point. */
370
RotateUp(Cardindex src)371 Move RotateUp(Cardindex src) {
372 Pileindex p = getpile(src);
373 int c;
374 Cardindex i;
375
376 cmd_CancelSelection();
377 i = INDEX_OF_LAST_CARD(p);
378 c = game.cards[i];
379 while (--i >= src)
380 game.cards[i+1] = game.cards[i];
381 game.cards[i+1] = c;
382 draw_pileupdate(p, 0);
383 /* change counter */
384 for (c = 0; c < 4; ++c)
385 if (rules.paramstring[c] == TXTI_ROTATE)
386 ++game.counter[c];
387 return ROTATE_UP | MOVE(src, 0);
388 }
RotateDown(Cardindex src)389 Move RotateDown(Cardindex src) {
390 Pileindex p = getpile(src);
391 int c;
392 Cardindex i;
393
394 cmd_CancelSelection();
395 c = game.cards[src];
396 for (i = src; i < INDEX_OF_LAST_CARD(p); ++i)
397 game.cards[i] = game.cards[i+1];
398 game.cards[i] = c;
399 draw_pileupdate(p, 0);
400 /* change counter */
401 for (c = 0; c < 4; ++c)
402 if (rules.paramstring[c] == TXTI_ROTATE)
403 ++game.counter[c];
404 return ROTATE_DOWN | MOVE(src, 0);
405 }
do_move(Cardindex srcindex,Pileindex dstpile)406 Move do_move(Cardindex srcindex, Pileindex dstpile) {
407 int srcpile, numcards;
408 int tmp[MAXCARDS];
409 int vis;
410 Move m;
411
412 cmd_CancelSelection(); /* calls cmd_ResetHints() */
413 if (srcindex == -1)
414 return NO_MOVE;
415
416 srcpile = getpile(srcindex);
417 numcards = game.ind[srcpile+1] - srcindex;
418 m = MOVE(srcindex, dstpile);
419 if (srcindex > game.ind[srcpile] && !game.visible[srcindex-1] &&
420 (srcpile != IDECK || rules.variant & DECK_VISIBLE)) {
421 game.visible[srcindex-1] = 1;
422 m |= MOVE_TURNED;
423 }
424
425 memcpy(tmp, game.cards+srcindex, numcards * sizeof(int));
426 /* moved cards are normally visible */
427 vis = dstpile != IDECK || rules.variant & DECK_VISIBLE;
428 /* printf("move %d to %d, %d cards\n", srcindex, dstpile, numcards); */
429 assert(srcpile != dstpile);
430 if (srcpile < dstpile) {
431 /* ldir */
432 int i;
433 m |= MOVE(game.ind[dstpile+1]-numcards, srcpile) << 16;
434 for (i = srcindex; i < game.ind[dstpile+1]-numcards; ++i)
435 move_card_data(i, i+numcards);
436 memcpy(game.cards+i, tmp, numcards * sizeof(int));
437 for (; i < game.ind[dstpile+1]; ++i)
438 game.visible[i] = vis;
439 for (i = srcpile + 1; i <= dstpile; ++i)
440 game.ind[i] -= numcards;
441 } else {
442 /* lddr ; shift a block backwards; begin at the tail */
443 int i;
444 m |= MOVE(game.ind[dstpile+1], srcpile) << 16;
445 for (i = srcindex - 1; i >= game.ind[dstpile+1]; --i)
446 move_card_data(i+numcards, i);
447 memcpy(game.cards + game.ind[dstpile+1], tmp, numcards * sizeof(int));
448 for (i = 0; i < numcards; ++i)
449 game.visible[game.ind[dstpile+1] + i] = vis;
450 for (i = dstpile+1; i <= srcpile; ++i)
451 game.ind[i] += numcards;
452 }
453 #ifdef DEBUG
454 count();
455 #endif
456 /* inform graphics interface of change */
457 draw_pileupdate(srcpile, -numcards);
458 draw_pileupdate(dstpile, numcards);
459 /* printf("Move done is %08lx\n", m); */
460 return m;
461 }
462
463
all_to_stack(void)464 int all_to_stack(void) {
465 int i, flag, anymove;
466
467 anymove = 0;
468 do {
469 flag = 0;
470 for (i = 0; i < game.numpiles; ++i) {
471 switch (game.piletype[i]) {
472 case Slot:
473 case Tmp:
474 case FaceupDeck:
475 if (move_to_stack(i)) {
476 flag = 1;
477 anymove = 1;
478 }
479 break;
480 default:
481 ;
482 }
483 }
484 } while (flag);
485 return anymove;
486 }
487
init_seed(long seed)488 static void init_seed(long seed) {
489 if (seed < 0) {
490 game.seed = -1L; /* to guarantee a mismatch later */
491 seed = (long)time(NULL);
492 }
493 seed %= PRANDMAX;
494 if (seed < 0L)
495 seed += PRANDMAX; /* I think this shouldn't happen */
496 if (seed == game.seed) { /* restart game */
497 /* stored_moves stays valid */
498 game.cheat_count += 1000;
499 } else {
500 game.seed = seed;
501 game.cheat_count = game.stored_moves = 0;
502 game.finished = False; /* this is really a new game */
503 }
504 game.n_moves = 0;
505 game.move_ptr = 0;
506 game.counter[0] = 0;
507 game.counter[1] = 0;
508 game.counter[2] = 0;
509 game.counter[3] = 0;
510 sprand(seed);
511 }
512
distribute(char * xx,int rest,int piles)513 static void distribute(char *xx, int rest, int piles)
514 { int i, k, invert = 0;
515
516 if (!piles)
517 return;
518 if (2 * rest > piles+1) {
519 rest = piles - rest;
520 invert = 1;
521 }
522 memset(xx, invert, piles);
523 if (rest) {
524 k = (piles-1) / rest + 1; /* ceil(slots/rest) */
525 for (i = 0; i < piles; i += k) {
526 xx[i] = 1-invert;
527 if (!--rest)
528 break;
529 }
530 /* the rest is distributed quite ugly */
531 for (i = 0; rest; ++i)
532 if (xx[i] == invert) {
533 xx[i] = 1-invert;
534 --rest;
535 }
536 }
537 }
538
gen_newgame(void)539 static void gen_newgame(void)
540 { int i, rest;
541 char xx[MAXPILES];
542
543 memset(xx, 0, MAXPILES);
544 if (rules.facedown && !rules.faceup) {
545 fprintf(stderr, "newgame(): topmost card must be face-up, correcting it\n");
546 --rules.facedown;
547 ++rules.faceup;
548 }
549 if (rules.newgame_bits & SLOTS_SAME)
550 rest = 0;
551 else
552 rest = rules.numcards % rules.numslots; /* cards that are too much */
553
554 if (rules.numcards < rest + rules.numslots * (rules.faceup + rules.facedown)) {
555 fprintf(stderr, "newgame(): too many cards specified, resetting to min values\n");
556 rules.faceup = 1;
557 rules.facedown = 0;
558 }
559
560 /* generate nice distribution of rest cards */
561 if (rest > rules.numtmps || (rules.newgame_bits & FORCE_SLOTS))
562 /* distribute on slots */
563 distribute(xx+FIRST_SLOT, rest, rules.numslots);
564 else
565 /* distribute on tmps */
566 distribute(xx+LAST_SLOT+1, rest, rules.numtmps);
567
568 for (i = 0; i <= FIRST_SLOT; ++i)
569 game.ind[i] = 0;
570 for (i = FIRST_SLOT; i <= LAST_SLOT; ++i) {
571 int j;
572 game.ind[i+1] = game.ind[i] + rules.facedown + rules.faceup + xx[i];
573 for (j = game.ind[i+1] - rules.faceup; j < game.ind[i+1]; ++j)
574 game.visible[j] = 1; /* card is turned */
575 }
576 while (++i < game.numpiles)
577 game.ind[i] = game.ind[i-1] + xx[i-1];
578 /* all cards on the tmps are faceup */
579 for (i = game.ind[LAST_SLOT+1]; i < game.ind[IDECK]; ++i)
580 game.visible[i] = 1;
581 if (rules.variant & DECK_VISIBLE)
582 game.visible[rules.numcards-1] = 1; /* topmost card on deck */
583 if (!rules.facedown)
584 for (i = game.ind[FIRST_SLOT]; i < game.ind[LAST_SLOT+1]; ++i)
585 game.visible[i] = 1; /* all cards on the slots visible */
586 }
587
588
MO_newgame(void)589 static void MO_newgame(void) {
590 int i, remcards = rules.numcards;
591 /* specific part: */
592 for (i = 0; i < rules.numslots; ++i) {
593 int here;
594 here = min(remcards, rules.faceup + rules.facedown);
595 remcards -= here;
596 game.ind[rules.numstacks+i+1] = game.ind[rules.numstacks+i] + here;
597 if (here) {
598 Cardindex j;
599 j = game.ind[rules.numstacks+i+1] - 1;
600 do
601 game.visible[j--] = 1;
602 while (j >= game.ind[rules.numstacks+i] + rules.facedown);
603 }
604 }
605 }
606
newgame(long seed)607 void newgame(long seed) {
608 int i;
609
610 game.randomgame = (seed == -1L);
611 game.savecount = 0;
612 game.bookmark = 0;
613 init_seed(seed);
614 shuffle(); /* initialize game.cards */
615
616 for (i = 0; i < rules.numcards; ++i)
617 game.visible[i] = 0;
618 for (i = 0; i < game.numpiles; ++i)
619 game.ind[i] = 0; /* no cards on the piles */
620 game.ind[i] = rules.numcards; /* rest on the deck */
621
622 if (rules.new_game)
623 (*rules.new_game)();
624 else if (rules.newgame_bits & SEQUENTIAL)
625 MO_newgame();
626 else
627 gen_newgame();
628
629 if (game.graphic)
630 for (i = 0; i < game.numpiles; ++i)
631 pile_resize(i);
632 /* (void)do_move(-1, -1); */ /* reset game.*/
633 cmd_ResetHints();
634 game.srcind = UNSELECTED;
635 }
636
637 /* generic give-cards routine */
638
give_new_cards(void)639 Move give_new_cards(void) {
640 if (rules.deal_cards)
641 return (*rules.deal_cards)();
642 else if (rules.variant & KLONDIKE_DEAL)
643 return Klondike_deal_cards();
644 else {
645 int i, rem;
646
647 graphics_pile_control(Disable, IDECK);
648 rem = CARDS_ON_PILE(IDECK);
649 if (rem > rules.numslots)
650 rem = rules.numslots;
651 for (i = 0; i < rem; ++i)
652 do_move(INDEX_OF_LAST_CARD(IDECK), rules.numstacks + i);
653 graphics_pile_control(EnableAndRedraw, IDECK);
654 return NEW_CARDS_MOVE | MOVE(0, rem);
655 }
656 }
657
658
659
660
661
662 extern struct rules Spider_rules, Gypsy_rules, Klondike_rules, modSpider_rules;
663 extern struct rules Seahaven_rules, FreeCell_rules, IdiotsDelight_rules,
664 MonteCarlo_rules, MidnightOil_rules, Calculation_rules, Michaels_rules,
665 Canfield_rules, modCanfield_rules, Royal_rules, Bakers_rules, Oonsoo_rules,
666 BlueMoon_rules;
667
668 struct game game;
669 struct rules rules;
670
671 struct rules *rulepool[] = {
672 &Spider_rules,
673 &Gypsy_rules,
674 &Klondike_rules,
675 &Seahaven_rules,
676 &FreeCell_rules,
677 &IdiotsDelight_rules,
678 &MonteCarlo_rules,
679 &MidnightOil_rules,
680 &Calculation_rules,
681 &modCanfield_rules,
682 &Michaels_rules,
683 &Canfield_rules,
684 &Royal_rules,
685 &Bakers_rules,
686 &Oonsoo_rules,
687 &BlueMoon_rules,
688 NULL
689 };
690
getrules(const char * ruleset)691 struct rules *getrules(const char *ruleset) {
692 int i;
693 for (i = 0; rulepool[i]; ++i)
694 if (!strcmp(ruleset, rulepool[i]->shortname) ||
695 (rulepool[i]->abbrev && !strcmp(ruleset, rulepool[i]->abbrev))) {
696 return rulepool[i];
697 }
698 return NULL;
699 }
700
new_rules(const char * ruleset,int decks,int slots,int faceup,int facedown,int jokers,int tmps,int param0,int param1,int param2,int param3)701 void new_rules(const char *ruleset, int decks, int slots, int faceup, int facedown,
702 int jokers, int tmps, int param0, int param1, int param2, int param3) {
703 int i;
704 struct rules *rp;
705 if (!game.numalloc)
706 memo_alloc(512);
707 game.seed = -1L; /* no replay is valid */
708 /* that will set finished to False */
709
710 if (!(rp = getrules(ruleset))) {
711 fprintf(stderr, "Unknown rule set. Known rules are:\n");
712 for (i = 0; rulepool[i]; ++i) {
713 char buff[20];
714 if (rulepool[i]->abbrev)
715 sprintf(buff, "(Abbrev %-2s)", rulepool[i]->abbrev);
716 else
717 strcpy(buff, " ");
718 fprintf(stderr, "%-15s %s -- %s\n", rulepool[i]->shortname, buff,
719 rulepool[i]->longname ? rulepool[i]->longname : "");
720 }
721 exit(EXIT_FAILURE);
722 }
723 rules = *rp; /* copy std game.*/
724
725 /* rule customization: */
726 if (!(rules.customizable & CUSTOM_DECKS )) decks = -1;
727 if (!(rules.customizable & CUSTOM_SLOTS )) slots = -1;
728 if (!(rules.customizable & CUSTOM_JOKERS)) jokers = -1;
729 if (!(rules.customizable & CUSTOM_TMPS )) tmps = -1;
730 if (!(rules.customizable & CUSTOM_FACEUP)) faceup = -1;
731 if (!(rules.customizable & CUSTOM_FACEDOWN)) facedown = -1;
732 if (!(rules.customizable & CUSTOM_PARAM0)) param0 = -1;
733 if (!(rules.customizable & CUSTOM_PARAM1)) param1 = -1;
734 if (!(rules.customizable & CUSTOM_PARAM2)) param2 = -1;
735 if (!(rules.customizable & CUSTOM_PARAM3)) param3 = -1;
736
737 if (jokers >= 0 && jokers != rules.numjokers) {
738 rules.numjokers = jokers;
739 rules.customized |= CUSTOM_JOKERS;
740 rules.maxscore = 0; /* custom: maxscore unknown */
741 }
742 if (param0 >= 0 && param0 != rules.param[0]) {
743 rules.param[0] = param0;
744 rules.customized |= CUSTOM_PARAM0;
745 rules.maxscore = 0; /* custom: maxscore unknown */
746 }
747 if (param1 >= 0 && param1 != rules.param[1]) {
748 rules.param[1] = param1;
749 rules.customized |= CUSTOM_PARAM1;
750 rules.maxscore = 0; /* custom: maxscore unknown */
751 }
752 if (param2 > 0 && param2 != rules.param[2]) {
753 rules.param[2] = param2;
754 rules.customized |= CUSTOM_PARAM2;
755 rules.maxscore = 0; /* custom: maxscore unknown */
756 }
757 if (param3 >= 0 && param3 != rules.param[3]) {
758 rules.param[3] = param3;
759 rules.customized |= CUSTOM_PARAM3;
760 }
761 if (faceup >= 0 && faceup != rules.faceup) {
762 rules.faceup = faceup;
763 rules.customized |= CUSTOM_FACEUP;
764 rules.maxscore = 0; /* custom: maxscore unknown */
765 }
766 if (facedown >= 0 && facedown != rules.facedown) {
767 rules.facedown = facedown;
768 rules.customized |= CUSTOM_FACEDOWN;
769 rules.maxscore = 0; /* custom: maxscore unknown */
770 }
771 if (slots > 0 && slots != rules.numslots) {
772 rules.numslots = slots;
773 rules.customized |= CUSTOM_SLOTS;
774 rules.maxscore = 0; /* custom: maxscore unknown */
775 }
776 if (decks > 0 && decks != rules.numdecks) {
777 rules.numdecks = decks;
778 rules.customized |= CUSTOM_DECKS;
779 rules.maxscore = 0; /* custom: maxscore unknown */
780 }
781 if (tmps >= 0 && tmps != rules.numtmps) {
782 rules.numtmps = tmps;
783 rules.customized |= CUSTOM_TMPS;
784 }
785
786 if (rules.numstacks >= 4)
787 rules.numstacks = rules.numdecks * 4;
788 rules.numcards = rules.cards_per_color * 4 * rules.numdecks + rules.numjokers;
789 if (rules.numcards > MAXCARDS) {
790 fprintf(stderr, "new_rules(): parameters give too many cards\n");
791 exit(EXIT_FAILURE);
792 }
793 if (rules.numslots + rules.numstacks + 1 > MAXPILES) {
794 fprintf(stderr, "new_rules(): parameters give too many slots\n");
795 exit(EXIT_FAILURE);
796 }
797 for (i = 0; i < rules.numstacks; ++i)
798 game.piletype[i] = Stack;
799 for (; i < rules.numstacks + rules.numslots; ++i)
800 game.piletype[i] = Slot;
801 for (; i < rules.numstacks + rules.numslots + rules.numtmps; ++i)
802 game.piletype[i] = Tmp;
803 if (rules.variant & DECK_SOURCE)
804 game.piletype[i++] = FaceupDeck;
805 game.piletype[i++] = FacedownDeck;
806 game.numpiles = i;
807 for (i = 0; i <= game.numpiles; ++i)/* this fixes coredump-bug */
808 game.ind[i] = 0; /* no card currently there */
809
810 if (rules.initfunc) /* initrules-hook */
811 (*rules.initfunc)();
812 rp->inited = 1;
813 }
814