]> git.cworth.org Git - loudgame/blob - lg-set.c
Mark emptyslots explicitly in the show output
[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         } else {
264             *s++ = '-';
265             *s++ = '-';
266             *s++ = '-';
267             *s++ = ' ';
268         }
269     }
270     *s = '\0';
271
272     loudgame_send (&game->lg, peer, board_str);
273 }
274
275 static void
276 set_game_handle_hint (set_game_t *game,
277                       const char *peer)
278 {
279     loudgame_sendf (&game->lg, peer, "Sets possible: %d",
280                     game->board.sets_possible);
281 }
282
283 static void
284 set_game_handle_shuffle (set_game_t *game,
285                          const char *peer)
286 {
287     if (game->board.sets_possible) {
288         loudgame_sendf (&game->lg, peer,
289                         "There are %d sets, refusing to shuffle.",
290                         game->board.sets_possible);
291         return;
292     }
293
294     if (game->deck.num_cards == 0) {
295         loudgame_sendf (&game->lg, peer,
296                         "Deck exhausted, starting a new game.");
297         set_game_new_game (game);
298     } else {
299         set_game_shuffle (game);
300     }
301 }
302
303 static void
304 set_game_handle_set (set_game_t *game,
305                      const char *peer,
306                      const char *message)
307 {
308     const char *s;
309     char *end = NULL;
310     int i;
311     int slots[3];
312     card_t cards[3];
313
314     s = message;
315     i = 0;
316     while (*s && i < 3) {
317         slots[i++] = strtoul (s, &end, 10);
318         if (end == s) {
319             loudgame_sendf (&game->lg, peer,
320                             "Error: Not an integer: %s", s);
321             return;
322         }
323         s = end;
324     }
325
326     while (end && *end && isspace (*end))
327         end++;
328
329     if (i != 3 || *end != '\0') {
330         loudgame_sendf (&game->lg, peer,
331                         "Error: The 'set' command requires exactly 3 integers");
332         return;
333     }
334
335     for (i = 0; i < 3; i++) {
336         if (slots[i] < 0 || slots[i] > BOARD_MAX_SLOTS) {
337             loudgame_sendf (&game->lg, peer,
338                             "Error: Value %d is out of range (0-%d)",
339                             slots[i], BOARD_MAX_SLOTS);
340             return;
341         }
342     }
343
344     if (slots[0] == slots[1] ||
345         slots[0] == slots[2] ||
346         slots[1] == slots[2])
347     {
348             loudgame_sendf (&game->lg, peer,
349                             "Error: All 3 values must be unique");
350             return;
351     }
352
353     for (i=0; i < 3; i++) {
354         if (game->board.slots[slots[i]].has_card) {
355             cards[i] = game->board.slots[slots[i]].card;
356         } else {
357             loudgame_sendf (&game->lg, peer,
358                             "Error: There's no card at position %d", i);
359             return;
360         }
361     }
362
363     if (! is_set (cards, 3)) {
364         loudgame_sendf (&game->lg, peer,
365                         "Sorry, that's not a set");
366         return;
367     }
368
369     loudgame_sendf (&game->lg, peer,
370                     "Yes, that's a set!");
371
372     for (i = 0; i < 3; i++)
373         game->board.slots[slots[i]].has_card = 0;
374
375     board_count_sets_possible (&game->board);
376
377     deal (&game->deck, &game->board);
378 }
379
380 static void
381 set_game_handle_message (loudgame_t *lg,
382                          const char *peer,
383                          const char *message)
384 {
385     set_game_t *game = (set_game_t*) lg;
386
387     if (strcmp (message, "show") == 0)
388         set_game_handle_show (game, peer);
389     else if (strcmp (message, "hint") == 0)
390         set_game_handle_hint (game, peer);
391     else if (strcmp (message, "shuffle") == 0)
392         set_game_handle_shuffle (game, peer);
393     else if (strncmp (message, "set", 3) == 0)
394         set_game_handle_set (game, peer, message + 3);
395     else
396         loudgame_sendf (lg, peer, "Unknown command: '%s'", message);
397 }
398
399 /* Begin a new game */
400 static void
401 set_game_new_game (set_game_t *game)
402 {
403     deck_init (&game->deck);
404     board_init (&game->board);
405     deal (&game->deck, &game->board);
406 }
407
408 static int
409 set_game_init (set_game_t *game, int argc, char *argv[])
410 {
411     int err;
412
413     err = loudgame_init (&game->lg, argc, argv);
414     if (err)
415         return err;
416
417     set_game_new_game (game);
418
419     return 0;
420 }
421
422 /* Return the dealt cards to the deck, reshuffle, and deal again. */
423 static int
424 set_game_shuffle (set_game_t *game)
425 {
426     board_t *board = &game->board;
427     deck_t *deck = &game->deck;
428     int i;
429
430     for (i=0; i < board->num_slots; i++) {
431         if (board->slots[i].has_card) {
432             deck->cards[++deck->num_cards - 1] = board->slots[i].card;
433             board->slots[i].has_card = 0;
434             board->slots[i].selected = 0;
435         }
436     }
437
438     deck_shuffle (deck);
439     deal (deck, board);
440
441     return 1;
442 }
443
444 int
445 main (int argc, char *argv[])
446 {
447     set_game_t game;
448     int err;
449
450     srand (time(0));
451
452     err = set_game_init (&game, argc, argv);
453     if (err)
454         return err;
455
456     game.lg.handle_message = set_game_handle_message;
457
458     err = loudgame_run (&game.lg);
459     if (err)
460         return err;
461
462     return 0;
463 }