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