Implement simple show, hint, and shuffle commands for lg-set.
[loudgame] / lg-set.c
1 /*
2  * Copyright (C) 2008 Carl Worth
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see http://www.gnu.org/licenses/ .
16  *
17  * Author: Carl Worth <cworth@cworth.org>
18  */
19
20 #include "loudgame.h"
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <math.h>
27 #include <time.h>
28
29 #include <assert.h>
30
31 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
32
33 #define NUM_ATTRIBUTES 4
34 #define NUM_VALUES 3
35
36 char *attribute_names[NUM_ATTRIBUTES] = {
37     "number", "color", "shading", "symbol"
38 };
39
40 typedef enum { ATTRIBUTE_INDEX_NUMBER,
41                ATTRIBUTE_INDEX_COLOR,
42                ATTRIBUTE_INDEX_SHADING,
43                ATTRIBUTE_INDEX_SYMBOL } attribute_index_t;
44
45 char *attribute_values[NUM_ATTRIBUTES][NUM_VALUES] = {
46     { "1", "2", "3" },
47     { "red", "green", "purple" },
48     { "solid", "open", "striped" },
49     { "oval", "squiggle", "diamond" }
50 };
51
52 typedef enum { COLOR_RED, COLOR_GREEN, COLOR_PURPLE } color_t;
53 typedef enum { SHADING_OPEN, SHADING_STRIPED, SHADING_SOLID } shading_t;
54 typedef enum { SYMBOL_OVAL, SYMBOL_SQUIGGLE, SYMBOL_DIAMOND } symbol_t;
55
56 typedef struct card {
57     int attributes[NUM_ATTRIBUTES];
58 } card_t;
59
60 #define DECK_MAX_CARDS ((int) (pow (NUM_VALUES, NUM_ATTRIBUTES)))
61 typedef struct deck {
62     int num_cards;
63     card_t cards[DECK_MAX_CARDS];
64 } deck_t;
65
66 typedef struct slot {
67     int has_card;
68     card_t card;
69     int selected;
70 } slot_t;
71
72 #define BOARD_COLS 3
73 #define BOARD_ROWS 4
74 #define BOARD_MAX_SLOTS (BOARD_COLS * BOARD_ROWS)
75 typedef struct board {
76     int num_slots;
77     slot_t slots[BOARD_MAX_SLOTS];
78     int needs_deal;
79     int sets_possible;
80 } board_t;
81
82 typedef struct _set_game {
83     loudgame_t lg;
84     deck_t deck;
85     board_t board;
86 } set_game_t;
87
88 static void
89 board_count_sets_possible (board_t *board);
90
91 static int
92 set_game_shuffle (set_game_t *game);
93
94 static int
95 attribute_all_same (card_t *cards, int num_cards, int attr)
96 {
97     int i, value;
98
99     if (num_cards == 0)
100         return 1;
101
102     value = cards[0].attributes[attr];
103
104     for (i = 1; i < num_cards; i++)
105         if (cards[i].attributes[attr] != value)
106             return 0;
107
108     return 1;
109 }
110
111 static int
112 attribute_all_different (card_t *cards, int num_cards, int attr)
113 {
114     int i, seen[NUM_VALUES];
115
116     if (num_cards == 0)
117         return 1;
118
119     memset (seen, 0, sizeof (seen));
120
121     for (i = 0; i < num_cards; i++) {
122         if (seen[cards[i].attributes[attr]])
123             return 0;
124         seen[cards[i].attributes[attr]] = 1;
125     }
126
127     return 1;
128 }
129
130 static int
131 is_set (card_t *cards, int num_cards)
132 {
133     int attr;
134
135     for (attr = 0; attr < NUM_ATTRIBUTES; attr++)
136         if (! attribute_all_same (cards, num_cards, attr) &&
137             ! attribute_all_different (cards, num_cards, attr))
138         {
139             return 0;
140         }
141
142     return 1;
143 }
144
145 static int
146 check_selected_for_set (board_t *board)
147 {
148     int i, num_selected;
149     card_t cards[3];
150
151     num_selected = 0;
152     for (i=0; i < board->num_slots; i++) {
153         if (board->slots[i].selected) {
154             if (num_selected >= 3)
155                 return 0;
156             cards[num_selected++] = board->slots[i].card;
157         }
158     }
159
160     if (num_selected !=3)
161         return 0;
162
163     if (! is_set (cards, num_selected))
164         return 0;
165
166     board_count_sets_possible (board);
167
168     board->needs_deal = 1;
169
170     return 1;
171 }
172
173 static void
174 deck_shuffle (deck_t *deck)
175 {
176     int i, r;
177     card_t tmp;
178
179     assert (deck->num_cards <= DECK_MAX_CARDS);
180
181     for (i=deck->num_cards - 1; i>=0; i--) {
182         r = (int) i * (rand() / (RAND_MAX + 1.0));
183         assert (r >= 0);
184         assert (r <= i);
185         tmp = deck->cards[i];
186         deck->cards[i] = deck->cards[r];
187         deck->cards[r] = tmp;
188     }
189 }
190
191 static void
192 deck_init (deck_t *deck)
193 {
194     int card;
195     int number;
196     color_t color;
197     shading_t shading;
198     symbol_t symbol;
199
200     card = 0;
201     for (number = 0; number < NUM_VALUES; number++)
202         for (color = 0; color < NUM_VALUES; color++)
203             for (shading = 0; shading < NUM_VALUES; shading++)
204                 for (symbol = 0; symbol < NUM_VALUES; symbol++) {
205                     deck->cards[card].attributes[ATTRIBUTE_INDEX_NUMBER] = number;
206                     deck->cards[card].attributes[ATTRIBUTE_INDEX_COLOR] = color;
207                     deck->cards[card].attributes[ATTRIBUTE_INDEX_SHADING] = shading;
208                     deck->cards[card].attributes[ATTRIBUTE_INDEX_SYMBOL] = symbol;
209                     card++;
210                 }
211     deck->num_cards = card;
212
213     deck_shuffle (deck);
214 }
215
216 static void
217 board_count_sets_possible (board_t *board)
218 {
219     int i, j, k;
220     int sets_possible = 0;
221     card_t cards[3];
222
223     for (i = 0; i < board->num_slots; i++) {
224         if (! board->slots[i].has_card)
225             continue;
226         for (j = i+1; j < board->num_slots; j++) {
227             if (! board->slots[j].has_card)
228                 continue;
229             for (k = j+1; k < board->num_slots; k++) {
230                 if (! board->slots[k].has_card)
231                     continue;
232                 cards[0] = board->slots[i].card;
233                 cards[1] = board->slots[j].card;
234                 cards[2] = board->slots[k].card;
235                 if (is_set (cards, 3))
236                     sets_possible++;
237             }
238         }
239     }
240
241     board->sets_possible = sets_possible;
242 }
243
244 static void
245 board_init (board_t *board)
246 {
247     int i;
248     board->num_slots = BOARD_MAX_SLOTS;
249     for (i=0; i < board->num_slots; i++) {
250         board->slots[i].has_card = 0;
251         board->slots[i].selected = 0;
252     }
253     board->needs_deal = 0;
254
255     board_count_sets_possible (board);
256 }
257
258 static void
259 deal (deck_t *deck, board_t *board)
260 {
261     int i;
262
263     for (i=0; i < board->num_slots; i++)
264         if (! board->slots[i].has_card) {
265             if (deck->num_cards > 0) {
266                 board->slots[i].card = deck->cards[deck->num_cards-- -1];
267                 board->slots[i].has_card = 1;
268             }
269         }
270
271     board_count_sets_possible (board);
272
273     board->needs_deal = 0;
274 }
275
276 static void
277 set_game_handle_show (set_game_t        *game,
278                       const char        *peer)
279 {
280     board_t *board = &game->board;
281     char board_str[BOARD_MAX_SLOTS * (NUM_ATTRIBUTES + 1)];
282     char *s;
283     int i, j;
284
285     s = board_str;
286     for (i = 0; i < board->num_slots; i++)
287         if (board->slots[i].has_card) {
288             for (j = 0; j < NUM_ATTRIBUTES; j++)
289                 *s++ = '0' + board->slots[i].card.attributes[j];
290             *s++ = ' ';
291         }
292     *s = '\0';
293
294     loudgame_send (&game->lg, peer, board_str);
295 }
296
297 static void
298 set_game_handle_hint (set_game_t *game,
299                       const char *peer)
300 {
301     loudgame_sendf (&game->lg, peer, "Sets possible: %d",
302                     game->board.sets_possible);
303 }
304
305 static void
306 set_game_handle_shuffle (set_game_t *game,
307                          const char *peer)
308 {
309     if (game->board.sets_possible) {
310         loudgame_sendf (&game->lg, peer,
311                         "There are %d sets, refusing to shuffle.",
312                         game->board.sets_possible);
313         return;
314     }
315
316     set_game_shuffle (game);
317 }
318
319 static void
320 set_game_handle_message (loudgame_t *lg,
321                          const char *peer,
322                          const char *message)
323 {
324     set_game_t *game = (set_game_t*) lg;
325
326     if (strcmp (message, "show") == 0)
327         set_game_handle_show (game, peer);
328     else if (strcmp (message, "hint") == 0)
329         set_game_handle_hint (game, peer);
330     else if (strcmp (message, "shuffle") == 0)
331         set_game_handle_shuffle (game, peer);
332     else
333         loudgame_sendf (lg, peer, "Unknown command: '%s'", message);
334 }
335
336 /* Begin a new game */
337 static int
338 set_game_init (set_game_t *game, int argc, char *argv[])
339 {
340     int err;
341
342     err = loudgame_init (&game->lg, argc, argv);
343     if (err)
344         return err;
345
346     deck_init (&game->deck);
347     board_init (&game->board);
348     deal (&game->deck, &game->board);
349
350     return 0;
351 }
352
353 /* Return the dealt cards to the deck, reshuffle, and deal again. */
354 static int
355 set_game_shuffle (set_game_t *game)
356 {
357     board_t *board = &game->board;
358     deck_t *deck = &game->deck;
359     int i;
360
361     for (i=0; i < board->num_slots; i++) {
362         if (board->slots[i].has_card) {
363             deck->cards[++deck->num_cards - 1] = board->slots[i].card;
364             board->slots[i].has_card = 0;
365             board->slots[i].selected = 0;
366         }
367     }
368
369     deck_shuffle (deck);
370     deal (deck, board);
371
372     return 1;
373 }
374
375 int
376 main (int argc, char *argv[])
377 {
378     set_game_t game;
379     int err;
380
381     srand (time(0));
382
383     err = set_game_init (&game, argc, argv);
384     if (err)
385         return err;
386
387     game.lg.handle_message = set_game_handle_message;
388
389     err = loudgame_run (&game.lg);
390     if (err)
391         return err;
392
393     return 0;
394 }