]> git.cworth.org Git - loudgame/blob - lg-set.c
Add support for claiming a 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 #include <ctype.h>
29
30 #include <assert.h>
31
32 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
33
34 #define NUM_ATTRIBUTES 4
35 #define NUM_VALUES 3
36
37 char *attribute_names[NUM_ATTRIBUTES] = {
38     "number", "color", "shading", "symbol"
39 };
40
41 typedef enum { ATTRIBUTE_INDEX_NUMBER,
42                ATTRIBUTE_INDEX_COLOR,
43                ATTRIBUTE_INDEX_SHADING,
44                ATTRIBUTE_INDEX_SYMBOL } attribute_index_t;
45
46 char *attribute_values[NUM_ATTRIBUTES][NUM_VALUES] = {
47     { "1", "2", "3" },
48     { "red", "green", "purple" },
49     { "solid", "open", "striped" },
50     { "oval", "squiggle", "diamond" }
51 };
52
53 typedef enum { COLOR_RED, COLOR_GREEN, COLOR_PURPLE } color_t;
54 typedef enum { SHADING_OPEN, SHADING_STRIPED, SHADING_SOLID } shading_t;
55 typedef enum { SYMBOL_OVAL, SYMBOL_SQUIGGLE, SYMBOL_DIAMOND } symbol_t;
56
57 typedef struct card {
58     int attributes[NUM_ATTRIBUTES];
59 } card_t;
60
61 #define DECK_MAX_CARDS ((int) (pow (NUM_VALUES, NUM_ATTRIBUTES)))
62 typedef struct deck {
63     int num_cards;
64     card_t cards[DECK_MAX_CARDS];
65 } deck_t;
66
67 typedef struct slot {
68     int has_card;
69     card_t card;
70     int selected;
71 } slot_t;
72
73 #define BOARD_COLS 3
74 #define BOARD_ROWS 4
75 #define BOARD_MAX_SLOTS (BOARD_COLS * BOARD_ROWS)
76 typedef struct board {
77     int num_slots;
78     slot_t slots[BOARD_MAX_SLOTS];
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 void
95 set_game_new_game (set_game_t *game);
96
97 static int
98 attribute_all_same (card_t *cards, int num_cards, int attr)
99 {
100     int i, value;
101
102     if (num_cards == 0)
103         return 1;
104
105     value = cards[0].attributes[attr];
106
107     for (i = 1; i < num_cards; i++)
108         if (cards[i].attributes[attr] != value)
109             return 0;
110
111     return 1;
112 }
113
114 static int
115 attribute_all_different (card_t *cards, int num_cards, int attr)
116 {
117     int i, seen[NUM_VALUES];
118
119     if (num_cards == 0)
120         return 1;
121
122     memset (seen, 0, sizeof (seen));
123
124     for (i = 0; i < num_cards; i++) {
125         if (seen[cards[i].attributes[attr]])
126             return 0;
127         seen[cards[i].attributes[attr]] = 1;
128     }
129
130     return 1;
131 }
132
133 static int
134 is_set (card_t *cards, int num_cards)
135 {
136     int attr;
137
138     for (attr = 0; attr < NUM_ATTRIBUTES; attr++)
139         if (! attribute_all_same (cards, num_cards, attr) &&
140             ! attribute_all_different (cards, num_cards, attr))
141         {
142             return 0;
143         }
144
145     return 1;
146 }
147
148 static void
149 deck_shuffle (deck_t *deck)
150 {
151     int i, r;
152     card_t tmp;
153
154     assert (deck->num_cards <= DECK_MAX_CARDS);
155
156     for (i=deck->num_cards - 1; i>=0; i--) {
157         r = (int) i * (rand() / (RAND_MAX + 1.0));
158         assert (r >= 0);
159         assert (r <= i);
160         tmp = deck->cards[i];
161         deck->cards[i] = deck->cards[r];
162         deck->cards[r] = tmp;
163     }
164 }
165
166 static void
167 deck_init (deck_t *deck)
168 {
169     int card;
170     int number;
171     color_t color;
172     shading_t shading;
173     symbol_t symbol;
174
175     card = 0;
176     for (number = 0; number < NUM_VALUES; number++)
177         for (color = 0; color < NUM_VALUES; color++)
178             for (shading = 0; shading < NUM_VALUES; shading++)
179                 for (symbol = 0; symbol < NUM_VALUES; symbol++) {
180                     deck->cards[card].attributes[ATTRIBUTE_INDEX_NUMBER] = number;
181                     deck->cards[card].attributes[ATTRIBUTE_INDEX_COLOR] = color;
182                     deck->cards[card].attributes[ATTRIBUTE_INDEX_SHADING] = shading;
183                     deck->cards[card].attributes[ATTRIBUTE_INDEX_SYMBOL] = symbol;
184                     card++;
185                 }
186     deck->num_cards = card;
187
188     deck_shuffle (deck);
189 }
190
191 static void
192 board_count_sets_possible (board_t *board)
193 {
194     int i, j, k;
195     int sets_possible = 0;
196     card_t cards[3];
197
198     for (i = 0; i < board->num_slots; i++) {
199         if (! board->slots[i].has_card)
200             continue;
201         for (j = i+1; j < board->num_slots; j++) {
202             if (! board->slots[j].has_card)
203                 continue;
204             for (k = j+1; k < board->num_slots; k++) {
205                 if (! board->slots[k].has_card)
206                     continue;
207                 cards[0] = board->slots[i].card;
208                 cards[1] = board->slots[j].card;
209                 cards[2] = board->slots[k].card;
210                 if (is_set (cards, 3))
211                     sets_possible++;
212             }
213         }
214     }
215
216     board->sets_possible = sets_possible;
217 }
218
219 static void
220 board_init (board_t *board)
221 {
222     int i;
223     board->num_slots = BOARD_MAX_SLOTS;
224     for (i=0; i < board->num_slots; i++) {
225         board->slots[i].has_card = 0;
226         board->slots[i].selected = 0;
227     }
228
229     board_count_sets_possible (board);
230 }
231
232 static void
233 deal (deck_t *deck, board_t *board)
234 {
235     int i;
236
237     for (i=0; i < board->num_slots; i++)
238         if (! board->slots[i].has_card) {
239             if (deck->num_cards > 0) {
240                 board->slots[i].card = deck->cards[deck->num_cards-- -1];
241                 board->slots[i].has_card = 1;
242             }
243         }
244
245     board_count_sets_possible (board);
246 }
247
248 static void
249 set_game_handle_show (set_game_t        *game,
250                       const char        *peer)
251 {
252     board_t *board = &game->board;
253     char board_str[BOARD_MAX_SLOTS * (NUM_ATTRIBUTES + 1)];
254     char *s;
255     int i, j;
256
257     s = board_str;
258     for (i = 0; i < board->num_slots; i++)
259         if (board->slots[i].has_card) {
260             for (j = 0; j < NUM_ATTRIBUTES; j++)
261                 *s++ = '0' + board->slots[i].card.attributes[j];
262             *s++ = ' ';
263         }
264     *s = '\0';
265
266     loudgame_send (&game->lg, peer, board_str);
267 }
268
269 static void
270 set_game_handle_hint (set_game_t *game,
271                       const char *peer)
272 {
273     loudgame_sendf (&game->lg, peer, "Sets possible: %d",
274                     game->board.sets_possible);
275 }
276
277 static void
278 set_game_handle_shuffle (set_game_t *game,
279                          const char *peer)
280 {
281     if (game->board.sets_possible) {
282         loudgame_sendf (&game->lg, peer,
283                         "There are %d sets, refusing to shuffle.",
284                         game->board.sets_possible);
285         return;
286     }
287
288     if (game->deck.num_cards == 0) {
289         loudgame_sendf (&game->lg, peer,
290                         "Deck exhausted, starting a new game.");
291         set_game_new_game (game);
292     } else {
293         set_game_shuffle (game);
294     }
295 }
296
297 static void
298 set_game_handle_set (set_game_t *game,
299                      const char *peer,
300                      const char *message)
301 {
302     const char *s;
303     char *end = NULL;
304     int i;
305     int slots[3];
306     card_t cards[3];
307
308     s = message;
309     i = 0;
310     while (*s && i < 3) {
311         slots[i++] = strtoul (s, &end, 10);
312         if (end == s) {
313             loudgame_sendf (&game->lg, peer,
314                             "Error: Not an integer: %s", s);
315             return;
316         }
317         s = end;
318     }
319
320     while (end && *end && isspace (*end))
321         end++;
322
323     if (i != 3 || *end != '\0') {
324         loudgame_sendf (&game->lg, peer,
325                         "Error: The 'set' command requires exactly 3 integers");
326         return;
327     }
328
329     for (i = 0; i < 3; i++) {
330         if (slots[i] < 0 || slots[i] > BOARD_MAX_SLOTS) {
331             loudgame_sendf (&game->lg, peer,
332                             "Error: Value %d is out of range (0-%d)",
333                             slots[i], BOARD_MAX_SLOTS);
334             return;
335         }
336     }
337
338     if (slots[0] == slots[1] ||
339         slots[0] == slots[2] ||
340         slots[1] == slots[2])
341     {
342             loudgame_sendf (&game->lg, peer,
343                             "Error: All 3 values must be unique");
344             return;
345     }
346
347     for (i=0; i < 3; i++) {
348         if (game->board.slots[slots[i]].has_card) {
349             cards[i] = game->board.slots[slots[i]].card;
350         } else {
351             loudgame_sendf (&game->lg, peer,
352                             "Error: There's no card at position %d", i);
353             return;
354         }
355     }
356
357     if (! is_set (cards, 3)) {
358         loudgame_sendf (&game->lg, peer,
359                         "Sorry, that's not a set");
360         return;
361     }
362
363     loudgame_sendf (&game->lg, peer,
364                     "Yes, that's a set!");
365
366     for (i = 0; i < 3; i++)
367         game->board.slots[slots[i]].has_card = 0;
368
369     board_count_sets_possible (&game->board);
370
371     deal (&game->deck, &game->board);
372 }
373
374 static void
375 set_game_handle_message (loudgame_t *lg,
376                          const char *peer,
377                          const char *message)
378 {
379     set_game_t *game = (set_game_t*) lg;
380
381     if (strcmp (message, "show") == 0)
382         set_game_handle_show (game, peer);
383     else if (strcmp (message, "hint") == 0)
384         set_game_handle_hint (game, peer);
385     else if (strcmp (message, "shuffle") == 0)
386         set_game_handle_shuffle (game, peer);
387     else if (strncmp (message, "set", 3) == 0)
388         set_game_handle_set (game, peer, message + 3);
389     else
390         loudgame_sendf (lg, peer, "Unknown command: '%s'", message);
391 }
392
393 /* Begin a new game */
394 static void
395 set_game_new_game (set_game_t *game)
396 {
397     deck_init (&game->deck);
398     board_init (&game->board);
399     deal (&game->deck, &game->board);
400 }
401
402 static int
403 set_game_init (set_game_t *game, int argc, char *argv[])
404 {
405     int err;
406
407     err = loudgame_init (&game->lg, argc, argv);
408     if (err)
409         return err;
410
411     set_game_new_game (game);
412
413     return 0;
414 }
415
416 /* Return the dealt cards to the deck, reshuffle, and deal again. */
417 static int
418 set_game_shuffle (set_game_t *game)
419 {
420     board_t *board = &game->board;
421     deck_t *deck = &game->deck;
422     int i;
423
424     for (i=0; i < board->num_slots; i++) {
425         if (board->slots[i].has_card) {
426             deck->cards[++deck->num_cards - 1] = board->slots[i].card;
427             board->slots[i].has_card = 0;
428             board->slots[i].selected = 0;
429         }
430     }
431
432     deck_shuffle (deck);
433     deal (deck, board);
434
435     return 1;
436 }
437
438 int
439 main (int argc, char *argv[])
440 {
441     set_game_t game;
442     int err;
443
444     srand (time(0));
445
446     err = set_game_init (&game, argc, argv);
447     if (err)
448         return err;
449
450     game.lg.handle_message = set_game_handle_message;
451
452     err = loudgame_run (&game.lg);
453     if (err)
454         return err;
455
456     return 0;
457 }