2 * Copyright (C) 2008 Carl Worth
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.
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.
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/ .
17 * Author: Carl Worth <cworth@cworth.org>
32 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
34 #define NUM_ATTRIBUTES 4
37 char *attribute_names[NUM_ATTRIBUTES] = {
38 "number", "color", "shading", "symbol"
41 typedef enum { ATTRIBUTE_INDEX_NUMBER,
42 ATTRIBUTE_INDEX_COLOR,
43 ATTRIBUTE_INDEX_SHADING,
44 ATTRIBUTE_INDEX_SYMBOL } attribute_index_t;
46 char *attribute_values[NUM_ATTRIBUTES][NUM_VALUES] = {
48 { "red", "green", "purple" },
49 { "solid", "open", "striped" },
50 { "oval", "squiggle", "diamond" }
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;
58 int attributes[NUM_ATTRIBUTES];
61 #define DECK_MAX_CARDS ((int) (pow (NUM_VALUES, NUM_ATTRIBUTES)))
64 card_t cards[DECK_MAX_CARDS];
75 #define BOARD_MAX_SLOTS (BOARD_COLS * BOARD_ROWS)
76 typedef struct board {
78 slot_t slots[BOARD_MAX_SLOTS];
82 typedef struct _set_game {
89 board_count_sets_possible (board_t *board);
92 set_game_shuffle (set_game_t *game);
95 set_game_new_game (set_game_t *game);
98 attribute_all_same (card_t *cards, int num_cards, int attr)
105 value = cards[0].attributes[attr];
107 for (i = 1; i < num_cards; i++)
108 if (cards[i].attributes[attr] != value)
115 attribute_all_different (card_t *cards, int num_cards, int attr)
117 int i, seen[NUM_VALUES];
122 memset (seen, 0, sizeof (seen));
124 for (i = 0; i < num_cards; i++) {
125 if (seen[cards[i].attributes[attr]])
127 seen[cards[i].attributes[attr]] = 1;
134 is_set (card_t *cards, int num_cards)
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))
149 deck_shuffle (deck_t *deck)
154 assert (deck->num_cards <= DECK_MAX_CARDS);
156 for (i=deck->num_cards - 1; i>=0; i--) {
157 r = (int) i * (rand() / (RAND_MAX + 1.0));
160 tmp = deck->cards[i];
161 deck->cards[i] = deck->cards[r];
162 deck->cards[r] = tmp;
167 deck_init (deck_t *deck)
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;
186 deck->num_cards = card;
192 board_count_sets_possible (board_t *board)
195 int sets_possible = 0;
198 for (i = 0; i < board->num_slots; i++) {
199 if (! board->slots[i].has_card)
201 for (j = i+1; j < board->num_slots; j++) {
202 if (! board->slots[j].has_card)
204 for (k = j+1; k < board->num_slots; k++) {
205 if (! board->slots[k].has_card)
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))
216 board->sets_possible = sets_possible;
220 board_init (board_t *board)
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;
229 board_count_sets_possible (board);
233 deal (deck_t *deck, board_t *board)
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;
245 board_count_sets_possible (board);
249 set_game_handle_show (set_game_t *game,
253 LmMessageNode *html, *body, *span;
255 GError *error = NULL;
256 board_t *board = &game->board;
257 char board_str[BOARD_MAX_SLOTS * (NUM_ATTRIBUTES + 1)];
261 reply = lm_message_new (peer, LM_MESSAGE_TYPE_MESSAGE);
262 html = lm_message_node_add_child (reply->node, "html", "");
263 lm_message_node_set_attribute (html,
265 "http://jabber.org/protocol/xhtml-im");
266 body = lm_message_node_add_child (html, "body", "");
267 lm_message_node_set_attribute (body,
269 "http://www.w3.org/1999/xhtml");
272 for (slot = 0; slot < board->num_slots; slot++) {
274 lm_message_node_add_child (body, "br", "");
275 if (board->slots[slot].has_card) {
278 card_t card = board->slots[slot].card;
279 for (pos = 0; pos < 3; pos++) {
280 if (pos <= card.attributes[ATTRIBUTE_INDEX_NUMBER]) {
281 switch (card.attributes[ATTRIBUTE_INDEX_SYMBOL]) {
285 case SYMBOL_SQUIGGLE:
299 span = lm_message_node_add_child (body, "span",
301 style = g_strdup_printf ("font-family: Monospace; color: %s;%s%s",
302 attribute_values[ATTRIBUTE_INDEX_COLOR][
303 card.attributes[ATTRIBUTE_INDEX_COLOR]],
304 card.attributes[ATTRIBUTE_INDEX_SHADING]
305 == SHADING_SOLID ? "font-weight: bold;" : "",
306 card.attributes[ATTRIBUTE_INDEX_SHADING]
307 == SHADING_STRIPED ? "text-decoration: underline;" : "");
308 lm_message_node_set_attribute (span, "style", style);
310 for (attr = 0; attr < NUM_ATTRIBUTES; attr++)
311 *s++ = '0' + board->slots[slot].card.attributes[attr];
314 span = lm_message_node_add_child (body, "span", " ");
315 lm_message_node_set_attribute (span, "style",
316 "font-family: Monospace;");
325 lm_message_node_add_child (reply->node, "body", board_str);
327 result = lm_connection_send (game->lg.connection, reply, &error);
328 lm_message_unref (reply);
331 g_error ("lm_connection_send failed: %s\n",
333 loudgame_quit (&game->lg, 1);
338 set_game_handle_hint (set_game_t *game,
341 loudgame_sendf (&game->lg, peer, "Sets possible: %d",
342 game->board.sets_possible);
346 set_game_handle_shuffle (set_game_t *game,
349 if (game->board.sets_possible) {
350 loudgame_sendf (&game->lg, peer,
351 "There are %d sets, refusing to shuffle.",
352 game->board.sets_possible);
356 if (game->deck.num_cards == 0) {
357 loudgame_sendf (&game->lg, peer,
358 "Deck exhausted, starting a new game.");
359 set_game_new_game (game);
361 set_game_shuffle (game);
366 set_game_handle_set (set_game_t *game,
378 while (*s && i < 3) {
379 slots[i++] = strtoul (s, &end, 10);
381 loudgame_sendf (&game->lg, peer,
382 "Error: Not an integer: %s", s);
388 while (end && *end && isspace (*end))
391 if (i != 3 || *end != '\0') {
392 loudgame_sendf (&game->lg, peer,
393 "Error: The 'set' command requires exactly 3 integers");
397 for (i = 0; i < 3; i++) {
398 if (slots[i] < 0 || slots[i] > BOARD_MAX_SLOTS) {
399 loudgame_sendf (&game->lg, peer,
400 "Error: Value %d is out of range (0-%d)",
401 slots[i], BOARD_MAX_SLOTS);
406 if (slots[0] == slots[1] ||
407 slots[0] == slots[2] ||
408 slots[1] == slots[2])
410 loudgame_sendf (&game->lg, peer,
411 "Error: All 3 values must be unique");
415 for (i=0; i < 3; i++) {
416 if (game->board.slots[slots[i]].has_card) {
417 cards[i] = game->board.slots[slots[i]].card;
419 loudgame_sendf (&game->lg, peer,
420 "Error: There's no card at position %d", i);
425 if (! is_set (cards, 3)) {
426 loudgame_sendf (&game->lg, peer,
427 "Sorry, that's not a set");
431 loudgame_sendf (&game->lg, peer,
432 "Yes, that's a set!");
434 for (i = 0; i < 3; i++)
435 game->board.slots[slots[i]].has_card = 0;
437 board_count_sets_possible (&game->board);
439 deal (&game->deck, &game->board);
443 set_game_handle_message (loudgame_t *lg,
447 set_game_t *game = (set_game_t*) lg;
449 if (strcmp (message, "show") == 0)
450 set_game_handle_show (game, peer);
451 else if (strcmp (message, "hint") == 0)
452 set_game_handle_hint (game, peer);
453 else if (strcmp (message, "shuffle") == 0)
454 set_game_handle_shuffle (game, peer);
455 else if (strncmp (message, "set", 3) == 0)
456 set_game_handle_set (game, peer, message + 3);
458 loudgame_sendf (lg, peer, "Unknown command: '%s'", message);
461 /* Begin a new game */
463 set_game_new_game (set_game_t *game)
465 deck_init (&game->deck);
466 board_init (&game->board);
467 deal (&game->deck, &game->board);
471 set_game_init (set_game_t *game, int argc, char *argv[])
475 err = loudgame_init (&game->lg, argc, argv);
479 set_game_new_game (game);
484 /* Return the dealt cards to the deck, reshuffle, and deal again. */
486 set_game_shuffle (set_game_t *game)
488 board_t *board = &game->board;
489 deck_t *deck = &game->deck;
492 for (i=0; i < board->num_slots; i++) {
493 if (board->slots[i].has_card) {
494 deck->cards[++deck->num_cards - 1] = board->slots[i].card;
495 board->slots[i].has_card = 0;
496 board->slots[i].selected = 0;
507 main (int argc, char *argv[])
514 err = set_game_init (&game, argc, argv);
518 game.lg.handle_message = set_game_handle_message;
520 err = loudgame_run (&game.lg);