+/*
+ * Copyright (C) 2008 Carl Worth
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+
+#include <assert.h>
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+#define NUM_ATTRIBUTES 4
+#define NUM_VALUES 3
+
+char *attribute_names[NUM_ATTRIBUTES] = {
+ "number", "color", "shading", "symbol"
+};
+
+typedef enum { ATTRIBUTE_INDEX_NUMBER,
+ ATTRIBUTE_INDEX_COLOR,
+ ATTRIBUTE_INDEX_SHADING,
+ ATTRIBUTE_INDEX_SYMBOL } attribute_index_t;
+
+char *attribute_values[NUM_ATTRIBUTES][NUM_VALUES] = {
+ { "1", "2", "3" },
+ { "red", "green", "purple" },
+ { "solid", "open", "striped" },
+ { "oval", "squiggle", "diamond" }
+};
+
+typedef enum { COLOR_RED, COLOR_GREEN, COLOR_PURPLE } color_t;
+typedef enum { SHADING_OPEN, SHADING_STRIPED, SHADING_SOLID } shading_t;
+typedef enum { SYMBOL_OVAL, SYMBOL_SQUIGGLE, SYMBOL_DIAMOND } symbol_t;
+
+typedef struct card {
+ int attributes[NUM_ATTRIBUTES];
+} card_t;
+
+#define DECK_MAX_CARDS ((int) (pow (NUM_VALUES, NUM_ATTRIBUTES)))
+typedef struct deck {
+ int num_cards;
+ card_t cards[DECK_MAX_CARDS];
+} deck_t;
+
+typedef struct slot {
+ int has_card;
+ card_t card;
+ int selected;
+} slot_t;
+
+#define BOARD_COLS 3
+#define BOARD_ROWS 4
+#define BOARD_MAX_SLOTS (BOARD_COLS * BOARD_ROWS)
+typedef struct board {
+ int num_slots;
+ slot_t slots[BOARD_MAX_SLOTS];
+ int needs_deal;
+ int sets_possible;
+ int display_sets_possible;
+} board_t;
+
+typedef struct game {
+ deck_t deck;
+ board_t board;
+} game_t;
+
+static void game_init (game_t *game);
+static void game_fini (game_t *game);
+
+static void
+board_count_sets_possible (board_t *board);
+
+static int
+attribute_all_same (card_t *cards, int num_cards, int attr)
+{
+ int i, value;
+
+ if (num_cards == 0)
+ return 1;
+
+ value = cards[0].attributes[attr];
+
+ for (i = 1; i < num_cards; i++)
+ if (cards[i].attributes[attr] != value)
+ return 0;
+
+ return 1;
+}
+
+static int
+attribute_all_different (card_t *cards, int num_cards, int attr)
+{
+ int i, seen[NUM_VALUES];
+
+ if (num_cards == 0)
+ return 1;
+
+ memset (seen, 0, sizeof (seen));
+
+ for (i = 0; i < num_cards; i++) {
+ if (seen[cards[i].attributes[attr]])
+ return 0;
+ seen[cards[i].attributes[attr]] = 1;
+ }
+
+ return 1;
+}
+
+static int
+is_set (card_t *cards, int num_cards)
+{
+ int attr;
+
+ for (attr = 0; attr < NUM_ATTRIBUTES; attr++)
+ if (! attribute_all_same (cards, num_cards, attr) &&
+ ! attribute_all_different (cards, num_cards, attr))
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+check_selected_for_set (board_t *board)
+{
+ int i, num_selected;
+ card_t cards[3];
+
+ num_selected = 0;
+ for (i=0; i < board->num_slots; i++) {
+ if (board->slots[i].selected) {
+ if (num_selected >= 3)
+ return 0;
+ cards[num_selected++] = board->slots[i].card;
+ }
+ }
+
+ if (num_selected !=3)
+ return 0;
+
+ if (! is_set (cards, num_selected))
+ return 0;
+
+ board_count_sets_possible (board);
+
+ board->needs_deal = 1;
+
+ return 1;
+}
+
+static void
+deck_shuffle (deck_t *deck)
+{
+ int i, r;
+ card_t tmp;
+
+ assert (deck->num_cards <= DECK_MAX_CARDS);
+
+ for (i=deck->num_cards - 1; i>=0; i--) {
+ r = (int) i * (rand() / (RAND_MAX + 1.0));
+ assert (r >= 0);
+ assert (r <= i);
+ tmp = deck->cards[i];
+ deck->cards[i] = deck->cards[r];
+ deck->cards[r] = tmp;
+ }
+}
+
+static void
+deck_init (deck_t *deck)
+{
+ int card;
+ int number;
+ color_t color;
+ shading_t shading;
+ symbol_t symbol;
+
+ card = 0;
+ for (number = 0; number < NUM_VALUES; number++)
+ for (color = 0; color < NUM_VALUES; color++)
+ for (shading = 0; shading < NUM_VALUES; shading++)
+ for (symbol = 0; symbol < NUM_VALUES; symbol++) {
+ deck->cards[card].attributes[ATTRIBUTE_INDEX_NUMBER] = number;
+ deck->cards[card].attributes[ATTRIBUTE_INDEX_COLOR] = color;
+ deck->cards[card].attributes[ATTRIBUTE_INDEX_SHADING] = shading;
+ deck->cards[card].attributes[ATTRIBUTE_INDEX_SYMBOL] = symbol;
+ card++;
+ }
+ deck->num_cards = card;
+
+ deck_shuffle (deck);
+}
+
+static void
+board_count_sets_possible (board_t *board)
+{
+ int i, j, k;
+ int sets_possible = 0;
+ card_t cards[3];
+
+ for (i = 0; i < board->num_slots; i++) {
+ if (! board->slots[i].has_card)
+ continue;
+ for (j = i+1; j < board->num_slots; j++) {
+ if (! board->slots[j].has_card)
+ continue;
+ for (k = j+1; k < board->num_slots; k++) {
+ if (! board->slots[k].has_card)
+ continue;
+ cards[0] = board->slots[i].card;
+ cards[1] = board->slots[j].card;
+ cards[2] = board->slots[k].card;
+ if (is_set (cards, 3))
+ sets_possible++;
+ }
+ }
+ }
+
+ board->sets_possible = sets_possible;
+}
+
+static void
+board_init (board_t *board)
+{
+ int i;
+ board->num_slots = BOARD_MAX_SLOTS;
+ for (i=0; i < board->num_slots; i++) {
+ board->slots[i].has_card = 0;
+ board->slots[i].selected = 0;
+ }
+ board->needs_deal = 0;
+
+ board_count_sets_possible (board);
+}
+
+static void
+board_print (board_t *board)
+{
+ int i, j;
+
+ printf ("Sets possible: %d\n", board->sets_possible);
+
+ for (i = 0; i < board->num_slots; i++) {
+ if (! board->slots[i].has_card)
+ for (j = 0; j < NUM_ATTRIBUTES; j++)
+ printf (" ");
+ else
+ for (j = 0; j < NUM_ATTRIBUTES; j++)
+ printf ("%d", board->slots[i].card.attributes[j]);
+ if ((i + 1) % 3 == 0)
+ printf ("\n");
+ else
+ printf (" ");
+ }
+}
+
+static void
+deal (deck_t *deck, board_t *board)
+{
+ int i;
+
+ for (i=0; i < board->num_slots; i++)
+ if (! board->slots[i].has_card) {
+ if (deck->num_cards > 0) {
+ board->slots[i].card = deck->cards[deck->num_cards-- -1];
+ board->slots[i].has_card = 1;
+ }
+ }
+
+ board_count_sets_possible (board);
+
+ board->needs_deal = 0;
+}
+
+/* Begin a new game */
+static void
+game_init (game_t *game)
+{
+ deck_init (&game->deck);
+ board_init (&game->board);
+ deal (&game->deck, &game->board);
+}
+
+static void
+game_fini (game_t *game)
+{
+ /* Nothing to do. */
+}
+
+/* Return the dealt cards to the deck, reshuffle, and deal again. */
+static int
+reshuffle (deck_t *deck, board_t *board)
+{
+ int i;
+
+ if (board->sets_possible) {
+ return 0;
+ }
+
+ for (i=0; i < board->num_slots; i++) {
+ if (board->slots[i].has_card) {
+ deck->cards[++deck->num_cards - 1] = board->slots[i].card;
+ board->slots[i].has_card = 0;
+ board->slots[i].selected = 0;
+ }
+ }
+
+ deck_shuffle (deck);
+ deal (deck, board);
+
+ return 1;
+}
+
+int
+main (void)
+{
+ game_t game;
+
+ srand (time(0));
+
+ game_init (&game);
+
+ board_print (&game.board);
+
+ game_fini (&game);
+
+ return 0;
+}