1 /*
2  * Gyatzee: Gnomified Yahtzee game.
3  * (C) 1998 the Free Software Foundation
4  *
5  * File:   yahtzee.c
6  *
7  * Author: Scott Heavner
8  *
9  *   Window manager independent yahtzee routines.
10  *
11  *   Variables are exported in yahtzee.h
12  *
13  *   This program is based on based on orest zborowski's curses based
14  *   yahtze (c)1992.
15  *
16  *   This program is free software; you can redistribute it and/or modify
17  *   it under the terms of the GNU General Public License as published by
18  *   the Free Software Foundation; either version 2 of the License, or
19  *   (at your option) any later version.
20  *
21  *   This program is distributed in the hope that it will be useful,
22  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
23  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  *   GNU General Public License for more details.
25  *
26  *   You should have received a copy of the GNU General Public License
27  *   along with this program; if not, write to the Free Software
28  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29  */
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <time.h>
35 #include <glib.h>
36 
37 #include "yahtzee.h"
38 
39 char *ProgramHeader = "Yahtzee Version 2.00 (c)1998 SDH, (c)1992 by zorst";
40 
41 GList *UndoList = NULL;
42 GList *RedoList = NULL;
43 UndoScoreElement lastRoll;
44 
45 /*=== Exported variables ===*/
46 DiceInfo DiceValues[NUMBER_OF_DICE] = { {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0} };
47 Player players[MAX_NUMBER_OF_PLAYERS] = {
48   {NULL, {0}, {0}, 0, 0},
49   {NULL, {0}, {0}, 0, 0},
50   {NULL, {0}, {0}, 0, 0},
51   {NULL, {0}, {0}, 0, 0},
52   {NULL, {0}, {0}, 0, 0},
53   {NULL, {0}, {0}, 0, 0}
54 };
55 int NumberOfPlayers = 0;
56 int NumberOfComputers = 0;
57 int NumberOfHumans = 0;
58 int DoDelay = 0;
59 int NumberOfRolls;
60 int LastHumanNumberOfRolls;
61 int WinningScore;
62 int DisplayComputerThoughts = 0;
63 int CurrentPlayer;
64 GameType game_type = GAME_YAHTZEE;
65 int NUM_FIELDS = NUM_FIELDS_YAHTZEE;
66 int NUM_LOWER = NUM_LOWER_YAHTZEE;
67 
68 char *DefaultPlayerNames[MAX_NUMBER_OF_PLAYERS] = { N_("Human"),
69   "Alice",
70   "Bob",
71   "Carol",
72   "Dave",
73   "Eve"
74 };
75 
76 typedef struct field_info_t {
77   char *label;
78   int   yahtzee_row;
79   int   kismet_row;
80   int (*score_func) (int);
81 } FieldInfo;
82 
83 char *FieldLabelsYahtzee[NUM_FIELDS_YAHTZEE + EXTRA_FIELDS] = {
84   N_("1s [total of 1s]"),
85   N_("2s [total of 2s]"),
86   N_("3s [total of 3s]"),
87   N_("4s [total of 4s]"),
88   N_("5s [total of 5s]"),
89   N_("6s [total of 6s]"),
90   /* End of upper panel */
91   N_("3 of a Kind [total]"),
92   N_("4 of a Kind [total]"),
93   N_("Full House [25]"),
94   N_("Small Straight [30]"),
95   N_("Large Straight [40]"),
96   N_("5 of a Kind [50]"),
97   N_("Chance [total]"),
98   /* End of lower panel */
99   N_("Lower Total"),
100   N_("Grand Total"),
101   /* Need to squish between upper and lower pannel */
102   N_("Upper total"),
103   N_("Bonus if >62"),
104 };
105 
106 char *FieldLabelsKismet[NUM_FIELDS_KISMET+EXTRA_FIELDS] =
107 {
108   N_("1s [total of 1s]"),
109   N_("2s [total of 2s]"),
110   N_("3s [total of 3s]"),
111   N_("4s [total of 4s]"),
112   N_("5s [total of 5s]"),
113   N_("6s [total of 6s]"),
114   /* End of upper panel */
115   N_("2 pair Same Color [total]"),
116   N_("3 of a Kind [total]"),
117   N_("Full House [15 + total]"),
118   N_("Full House Same Color [20 + total]"),
119   N_("Flush (all same color) [35]"),
120   N_("Large Straight [40]"),
121   N_("4 of a Kind [25 + total]"),
122   N_("5 of a Kind [50 + total]"),
123   N_("Chance [total]"),
124   /* End of lower panel */
125   N_("Lower Total"),
126   N_("Grand Total"),
127   /* Need to squish between upper and lower pannel */
128   N_("Upper total"),
129   N_("Bonus if >62"),
130 };
131 
132 char **FieldLabels = FieldLabelsKismet;
133 
134 int
NoDiceSelected(void)135 NoDiceSelected (void)
136 {
137   int i, j = 1;
138   for (i = 0; i < NUMBER_OF_DICE; i++)
139     if (DiceValues[i].sel)
140       j = 0;
141   return j;
142 }
143 
144 void
SelectAllDice(void)145 SelectAllDice (void)
146 {
147   int i;
148   for (i = 0; i < NUMBER_OF_DICE; i++)
149     DiceValues[i].sel = 1;
150 }
151 
152 void
YahtzeeInit(void)153 YahtzeeInit (void)
154 {
155   int i;
156 
157   srand (time (NULL));
158 
159   for (i = 0; i < MAX_NUMBER_OF_PLAYERS; ++i) {
160     players[i].name = _(DefaultPlayerNames[i]);
161     players[i].comp = 1;
162   }
163 
164   /* Make player number one human */
165   players[0].comp = 0;
166 
167 }
168 
169 /* Must be called after window system is initted */
170 void
NewGame(void)171 NewGame (void)
172 {
173   int i, j;
174 
175   if (game_type == GAME_YAHTZEE) {
176     FieldLabels = FieldLabelsYahtzee;
177     NUM_FIELDS = NUM_FIELDS_YAHTZEE;
178     NUM_LOWER = NUM_LOWER_YAHTZEE;
179   }
180   else if (game_type == GAME_KISMET) {
181     FieldLabels = FieldLabelsKismet;
182     NUM_FIELDS = NUM_FIELDS_KISMET;
183     NUM_LOWER = NUM_LOWER_KISMET;
184   }
185 
186   CurrentPlayer = 0;
187   NumberOfRolls = 0;
188   LastHumanNumberOfRolls = 0;
189   FreeUndoRedoLists ();
190 
191   NumberOfPlayers = NumberOfComputers + NumberOfHumans;
192 
193   for (i = 0; i < MAX_NUMBER_OF_PLAYERS; ++i) {
194     players[i].finished = 0;
195     players[i].comp = 1;
196 
197     for (j = 0; j < NUM_FIELDS; ++j) {
198       players[i].score[j] = 0;
199       players[i].used[j] = 0;
200     }
201   }
202 
203   /* Possibly 0 humans? */
204   for (i = 0; i < NumberOfHumans; i++)
205     players[i].comp = 0;
206 
207   SelectAllDice ();
208   RollSelectedDice ();
209 }
210 
211 int
RollDie(void)212 RollDie (void)
213 {
214   return rand () % 6 + 1;
215 }
216 
217 void
RollSelectedDice(void)218 RollSelectedDice (void)
219 {
220   int i, cnt = 0;
221 
222   if (NumberOfRolls >= NUM_ROLLS) {
223     return;
224   }
225 
226   for (i = 0; i < NUMBER_OF_DICE; i++) {
227     if (DiceValues[i].sel) {
228       DiceValues[i].val = RollDie ();
229       DiceValues[i].sel = 0;
230       cnt++;
231       lastRoll.DiceValues[i] = DiceValues[i].val;
232     }
233   }
234 
235   /* If no dice is selcted roll them all */
236   if (cnt == 0) {
237     for (i = 0; i < NUMBER_OF_DICE; i++) {
238       DiceValues[i].val = RollDie ();
239       lastRoll.DiceValues[i] = DiceValues[i].val;
240     }
241   }
242 
243   UpdateAllDicePixmaps ();
244   DeselectAllDice ();
245 
246   NumberOfRolls++;
247 
248   if (NumberOfRolls >= NUM_ROLLS) {
249     say (_("Choose a score slot."));
250   }
251 
252   lastRoll.roll = NumberOfRolls;
253   lastRoll.player = CurrentPlayer;
254 }
255 
256 int
GameIsOver(void)257 GameIsOver (void)
258 {
259   int i;
260 
261   for (i = 0; i < NumberOfPlayers; i++)
262     if (players[i].finished == 0)
263       return 0;
264   return 1;
265 }
266 
267 int
upper_total(int num)268 upper_total (int num)
269 {
270   int val;
271   int i;
272 
273   val = 0;
274 
275   for (i = 0; i < NUM_UPPER; ++i)
276     val += players[num].score[i];
277 
278   return (val);
279 }
280 
281 int
lower_total(int num)282 lower_total (int num)
283 {
284   int val;
285   int i;
286 
287   val = 0;
288 
289   for (i = 0; i < NUM_LOWER; ++i)
290     val += players[num].score[i + NUM_UPPER];
291 
292   return (val);
293 }
294 
295 int
total_score(int num)296 total_score (int num)
297 {
298   int upper_tot;
299   int lower_tot;
300 
301   lower_tot = lower_total (num);
302   upper_tot = upper_total (num);
303 
304   if (game_type == GAME_KISMET && upper_tot >= 78)
305     upper_tot += 75;
306   else if (game_type == GAME_KISMET && upper_tot >= 71)
307     upper_tot += 55;
308   else if (upper_tot >= 63)
309     upper_tot += 35;
310 
311   return (upper_tot + lower_tot);
312 }
313 
314 int
count(int val)315 count (int val)
316 {
317   int i;
318   int num;
319 
320   num = 0;
321 
322   for (i = 0; i < NUMBER_OF_DICE; ++i)
323     if (DiceValues[i].val == val)
324       ++num;
325 
326   return (num);
327 }
328 
329 int
find_n_of_a_kind(int n,int but_not)330 find_n_of_a_kind (int n, int but_not)
331 {
332   int i;
333 
334   for (i = 0; i < NUMBER_OF_DICE; ++i) {
335     if (DiceValues[i].val == but_not)
336       continue;
337 
338     if (count (DiceValues[i].val) >= n)
339       return (DiceValues[i].val);
340   }
341 
342   return (0);
343 }
344 
345 int
find_straight(int run,int notstart,int notrun)346 find_straight (int run, int notstart, int notrun)
347 {
348   int i;
349   int j;
350 
351   for (i = 1; i < 7; ++i) {
352     if (i >= notstart && i < notstart + notrun)
353       continue;
354 
355     for (j = 0; j < run; ++j)
356       if (!count (i + j))
357 	break;
358 
359     if (j == run)
360       return (i);
361   }
362 
363   return (0);
364 }
365 
366 int
find_yahtzee(void)367 find_yahtzee (void)
368 {
369   int i;
370 
371   for (i = 1; i < 7; ++i)
372     if (count (i) == 5)
373       return (i);
374 
375   return (0);
376 }
377 
378 int
add_dice(void)379 add_dice (void)
380 {
381   int i;
382   int val;
383 
384   val = 0;
385 
386   for (i = 0; i < NUMBER_OF_DICE; ++i)
387     val += DiceValues[i].val;
388 
389   return (val);
390 }
391 
392 static int
score_basic(int field)393 score_basic (int field) {
394   field++;
395   return count (field) * field;
396 }
397 
398 static int
score_3_of_a_kind(int field)399 score_3_of_a_kind (int field) {
400   if (find_n_of_a_kind (3, 0))
401     return add_dice ();
402   return 0;
403 }
404 
405 static int
score_4_of_a_kind(int field)406 score_4_of_a_kind (int field) {
407   if (find_n_of_a_kind (4, 0))
408     return add_dice ();
409   return 0;
410 }
411 
412 static int
score_full_house(int field)413 score_full_house (int field) {
414   int i = find_n_of_a_kind (3, 0);
415   if (i) {
416     if (find_n_of_a_kind (2, i) || find_n_of_a_kind (5, 0))
417       return 25;
418   }
419 
420   return 0;
421 }
422 
423 static int
score_small_straight(int field)424 score_small_straight (int field) {
425   if (find_straight (4, 0, 0))
426     return 30;
427 
428   return 0;
429 }
430 
431 static int
score_large_straight(int field)432 score_large_straight (int field) {
433   if (find_straight (5, 0, 0))
434     return 40;
435 
436   return 0;
437 }
438 
439 static int
score_yahtzee(int field)440 score_yahtzee (int field) {
441   if (find_n_of_a_kind (5, 0))
442     return 50;
443 
444   return 0;
445 }
446 
447 static int
score_chance(int field)448 score_chance (int field) {
449   return add_dice ();
450 }
451 
452 static int
score_2_pair_same_color(int field)453 score_2_pair_same_color (int field) {
454   int i = find_n_of_a_kind (2, 0);
455   if (i) {
456      if (find_n_of_a_kind (2, i) + i == 7 || find_n_of_a_kind (4, 0))
457        return add_dice ();
458   }
459 
460   return 0;
461 }
462 
463 static int
score_full_house_kismet(int field)464 score_full_house_kismet (int field) {
465   int i = find_n_of_a_kind (3, 0);
466   if (i) {
467     if (find_n_of_a_kind (2, i) || find_n_of_a_kind (5, 0))
468       return 15 + add_dice ();
469   }
470 
471   return 0;
472 }
473 
474 static int
score_full_house_same_color(int field)475 score_full_house_same_color (int field) {
476   int i = find_n_of_a_kind (3, 0);
477   if (i) {
478     if (find_n_of_a_kind (2, i) + i == 7 || find_n_of_a_kind (5, 0))
479       return 20 + add_dice ();
480   }
481 
482   return 0;
483 }
484 
485 static int
score_flush(int field)486 score_flush (int field) {
487   int i = find_n_of_a_kind (3, 0);
488 
489   if (i && i + find_n_of_a_kind (2, i) == 7) return 35;
490   i = find_n_of_a_kind (4, 0);
491   if (i && i + find_n_of_a_kind (1, i) == 7) return 35;
492   if (find_n_of_a_kind (5, 0)) return 35;
493 
494   return 0;
495 }
496 
497 static int
score_4_of_a_kind_kismet(int field)498 score_4_of_a_kind_kismet (int field) {
499   if (find_n_of_a_kind (4, 0))
500     return 25 + add_dice ();
501   return 0;
502 }
503 
504 static int
score_kismet(int field)505 score_kismet (int field) {
506   if (find_n_of_a_kind (5, 0))
507     return 50 + add_dice ();
508   return 0;
509 }
510 
511 FieldInfo field_table[] = {
512   { N_("1s [total of 1s]"),                   0,  0, score_basic },
513   { N_("2s [total of 2s]"),                   1,  1, score_basic },
514   { N_("3s [total of 3s]"),                   2,  2, score_basic },
515   { N_("4s [total of 4s]"),                   3,  3, score_basic },
516   { N_("5s [total of 5s]"),                   4,  4, score_basic },
517   { N_("6s [total of 6s]"),                   5,  5, score_basic },
518   { N_("3 of a Kind [total]"),                6,  7, score_3_of_a_kind },
519   { N_("4 of a Kind [total]"),                7, -1, score_4_of_a_kind },
520   { N_("Full House [25]"),                    8, -1, score_full_house },
521   { N_("Small Straight [30]"),                9, -1, score_small_straight },
522   { N_("Large Straight [40]"),               10, 11, score_large_straight },
523   { N_("5 of a Kind [total]"),               11, -1, score_yahtzee },
524   { N_("Chance [total]"),                    12, 14, score_chance },
525   { N_("2 pair Same Color [total]"),         -1,  6, score_2_pair_same_color },
526   { N_("Full House [15 + total]"),            -1,  8, score_full_house_kismet },
527   { N_("Full House Same Color [20 + total]"), -1,  9, score_full_house_same_color },
528   { N_("Flush (all same color) [35]"),       -1, 10, score_flush },
529   { N_("4 of a Kind [25 + total]"),          -1, 12, score_4_of_a_kind_kismet },
530   { N_("5 of a Kind [50 + total]"),          -1, 13, score_kismet },
531 };
532 
533 #define FIELD_TABLE_SIZE (sizeof (field_table) / sizeof (FieldInfo))
534 
535 static FieldInfo
get_field_info(int field)536 *get_field_info (int field)
537 {
538   gint ii;
539 
540   for (ii = 0; ii < FIELD_TABLE_SIZE; ii++)
541     if (field == (game_type == GAME_KISMET ? field_table[ii].kismet_row :
542                                              field_table[ii].yahtzee_row))
543       return &field_table[ii];
544 
545   return NULL;
546 }
547 
548 gint
field_score(gint field)549 field_score (gint field)
550 {
551   FieldInfo *info = get_field_info (field);
552   gint rval = 0;
553 
554   if (info) {
555     return info->score_func (field);
556   }
557 
558   return rval;
559 }
560 
561 gint
player_field_score(gint player,gint field)562 player_field_score (gint player, gint field)
563 {
564   /* A player can still score in H_YA even if it's used in the
565    * regular game, but only if they have a non-zero value there */
566   if (field == H_YA && game_type == GAME_YAHTZEE) {
567     if (players[player].used[field]) {
568       if (field_score (field) > 0 && players[player].score[field] > 0)
569         return field_score (field);
570       else
571         return -1;
572     }
573   }
574   else if (players[player].used[field])
575     return -1;
576 
577   return field_score (field);
578 }
579 
580 void
PrependUndoList(gint player,gint field,gint score)581 PrependUndoList (gint player, gint field, gint score) {
582   UndoScoreElement *elem = g_new0 (UndoScoreElement, 1);
583   gint ii;
584   elem->player = player;
585   elem->field  = field;
586   elem->score  = score;
587 
588   for (ii = 0; ii < NUMBER_OF_DICE; ii++)
589     elem->DiceValues[ii] = DiceValues[ii].val;
590   elem->roll = NumberOfRolls;
591 
592   if (!players[player].comp)
593     FreeUndoList ();
594   UndoList = g_list_prepend (UndoList, elem);
595 }
596 
597 void
ResetDiceState(UndoScoreElement * elem)598 ResetDiceState (UndoScoreElement *elem) {
599   gint ii;
600   for (ii = 0; ii < NUMBER_OF_DICE; ii++) {
601     DiceValues[ii].val = elem->DiceValues[ii];
602     DiceValues[ii].sel = 0;
603   }
604   NumberOfRolls = elem->roll;
605   ShowPlayer (elem->player, elem->field);
606 }
607 
608 gint
UndoLastMove(void)609 UndoLastMove (void) {
610   if (UndoList) {
611     UndoScoreElement *elem = UndoList->data;
612     if (elem->field == H_YA && game_type == GAME_YAHTZEE) {
613       if (players[elem->player].score[elem->field] != 0)
614         players[elem->player].score[elem->field] -= 50;
615       players[elem->player].used [elem->field] = players[elem->player].score[elem->field] > 0;
616     } else {
617       players[elem->player].score[elem->field] = 0;
618       players[elem->player].used [elem->field] = 0;
619     }
620 
621     ResetDiceState (elem);
622     UndoList = g_list_remove (UndoList, elem);
623     RedoList = g_list_prepend (RedoList, elem);
624     return elem->player;
625   }
626 
627   return CurrentPlayer;
628 }
629 
630 gint
RedoLastMove(void)631 RedoLastMove (void) {
632   gint rval = (CurrentPlayer + 1) % NumberOfPlayers;
633   if (RedoList) {
634     gint ii;
635     UndoScoreElement *elem = RedoList->data;
636 
637     for (ii = 0; ii < NUMBER_OF_DICE; ii++) {
638       DiceValues[ii].val = elem->DiceValues[ii];
639       DiceValues[ii].sel = 0;
640     }
641 
642     RedoList = g_list_remove (RedoList, elem);
643     play_score (elem->player, elem->field);
644     rval = (elem->player + 1) % NumberOfPlayers;
645     g_free (elem);
646     if (RedoList) {
647       elem = RedoList->data;
648       rval = elem->player;
649     }
650   }
651 
652   return rval;
653 }
654 
655 void
RestoreLastRoll(void)656 RestoreLastRoll (void) {
657   ResetDiceState (&lastRoll);
658 }
659 
660 UndoScoreElement*
RedoHead(void)661 RedoHead (void) {
662   if (RedoList) {
663     UndoScoreElement *elem = RedoList->data;
664     return elem;
665   }
666 
667   return NULL;
668 }
669 
670 void
FreeUndoList(void)671 FreeUndoList (void) {
672   while (UndoList) {
673     UndoScoreElement *elem = UndoList->data;
674     UndoList = g_list_remove (UndoList, elem);
675     g_free (elem);
676   }
677 }
678 
679 void
FreeRedoList(void)680 FreeRedoList (void) {
681   while (RedoList) {
682     UndoScoreElement *elem = RedoList->data;
683     RedoList = g_list_remove (RedoList, elem);
684     g_free (elem);
685   }
686 }
687 
688 void
FreeUndoRedoLists(void)689 FreeUndoRedoLists (void) {
690   FreeUndoList ();
691   FreeRedoList ();
692 }
693 
694 void
FreeRedoListHead(void)695 FreeRedoListHead (void) {
696   if (RedoList) {
697     UndoScoreElement *elem = RedoList->data;
698     RedoList = g_list_remove (RedoList, elem);
699     g_free (elem);
700   }
701 }
702 
703 /* Test if we can use suggested score slot */
704 int
play_score(int player,int field)705 play_score (int player, int field)
706 {
707   int i;
708 
709   /* Special case for yahtzee, allow multiple calls if 1st wasn't 0 */
710 
711   /* This, however, was broken: it didn't actually check to see if the
712    * user had a yahtzee if this wasn't their first time clicking on it.
713    * Bad. -- pschwan@cmu.edu */
714   if (field == 11 && game_type == GAME_YAHTZEE) {
715     if ((players[player].used[11] && (players[player].score[11] == 0)))
716       return SLOT_USED;
717 
718     if ((players[player].used[11] && !find_yahtzee ()))
719       return SLOT_USED;
720   } else if (players[player].used[field])
721     return SLOT_USED;
722 
723   players[player].used[field] = 1;
724   players[player].score[field] = players[player].score[field] + field_score (field);
725   PrependUndoList (player, field, players[player].score[field]);
726 
727   ShowPlayer (player, field);
728 
729   for (i = 0; i < NUM_FIELDS; ++i)
730     if (!players[player].used[i])
731       return SCORE_OK;
732 
733   players[player].finished = 1;
734 
735   return SCORE_OK;
736 }
737 
738 int
FindWinner(void)739 FindWinner (void)
740 {
741   int i;
742   int winner = 0;
743   int total;
744 
745   WinningScore = 0;
746   FreeUndoRedoLists ();
747 
748   for (i = 0; i < NumberOfPlayers; ++i) {
749     total = total_score (i);
750     if (total > WinningScore) {
751       WinningScore = total_score (i);
752       winner = i;
753     }
754   }
755 
756   /* Detect a drawn game. Returning the negative of the score
757    * is a bit of a hack, but it allows us to find out who the winners
758    * were without having to pass around a list. */
759   for (i = 0; i < NumberOfPlayers; ++i) {
760     total = total_score (i);
761     if ((total == WinningScore) && (i != winner))
762       return -total;
763   }
764 
765   return winner;
766 }
767 
768 /* Undo is possible when the Undo List isn't NULL */
769 int
UndoPossible(void)770 UndoPossible (void)
771 {
772   return UndoList != NULL;
773 }
774 
775 /* Undo option should be visible only when the player is human */
776 int
UndoVisible(void)777 UndoVisible (void)
778 {
779   return UndoPossible () && !players[CurrentPlayer].comp;
780 }
781 
782 int
RedoPossible(void)783 RedoPossible (void)
784 {
785   return RedoList != NULL;
786 }
787