* Author: Carl Worth <cworth@cworth.org>
*/
+#include "loudgame.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <time.h>
+#include <ctype.h>
#include <assert.h>
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 {
+typedef struct _set_game {
+ loudgame_t lg;
deck_t deck;
board_t board;
-} game_t;
-
-static void game_init (game_t *game);
-static void game_fini (game_t *game);
+} set_game_t;
static void
board_count_sets_possible (board_t *board);
+static int
+set_game_shuffle (set_game_t *game);
+
+static void
+set_game_new_game (set_game_t *game);
+
static int
attribute_all_same (card_t *cards, int num_cards, int attr)
{
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)
{
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)
{
}
board_count_sets_possible (board);
+}
+
+static void
+set_game_handle_show (set_game_t *game,
+ const char *peer)
+{
+ LmMessage *reply;
+ LmMessageNode *html, *body, *span;
+ gboolean result;
+ GError *error = NULL;
+ board_t *board = &game->board;
+ char board_str[BOARD_MAX_SLOTS * (NUM_ATTRIBUTES + 1)];
+ char *s;
+ int slot, pos, attr;
+
+ reply = lm_message_new (peer, LM_MESSAGE_TYPE_MESSAGE);
+ html = lm_message_node_add_child (reply->node, "html", "");
+ lm_message_node_set_attribute (html,
+ "xmlns",
+ "http://jabber.org/protocol/xhtml-im");
+ body = lm_message_node_add_child (html, "body", "");
+ lm_message_node_set_attribute (body,
+ "xmlns",
+ "http://www.w3.org/1999/xhtml");
+
+ s = board_str;
+ for (slot = 0; slot < board->num_slots; slot++) {
+ if (slot % 4 == 0)
+ lm_message_node_add_child (body, "br", "");
+ if (board->slots[slot].has_card) {
+ char card_str[5];
+ char *style;
+ card_t card = board->slots[slot].card;
+ for (pos = 0; pos < 3; pos++) {
+ if (pos <= card.attributes[ATTRIBUTE_INDEX_NUMBER]) {
+ switch (card.attributes[ATTRIBUTE_INDEX_SYMBOL]) {
+ case SYMBOL_OVAL:
+ card_str[pos] = 'O';
+ break;
+ case SYMBOL_SQUIGGLE:
+ card_str[pos] = 'S';
+ break;
+ case SYMBOL_DIAMOND:
+ default:
+ card_str[pos] = 'X';
+ break;
+ }
+ } else {
+ card_str[pos] = ' ';
+ }
+ }
+ card_str[3] = ' ';
+ card_str[4] = '\0';
+ span = lm_message_node_add_child (body, "span",
+ card_str);
+ style = g_strdup_printf ("font-family: Monospace; color: %s;%s%s",
+ attribute_values[ATTRIBUTE_INDEX_COLOR][
+ card.attributes[ATTRIBUTE_INDEX_COLOR]],
+ card.attributes[ATTRIBUTE_INDEX_SHADING]
+ == SHADING_SOLID ? "font-weight: bold;" : "",
+ card.attributes[ATTRIBUTE_INDEX_SHADING]
+ == SHADING_STRIPED ? "text-decoration: underline;" : "");
+ lm_message_node_set_attribute (span, "style", style);
+ free (style);
+ for (attr = 0; attr < NUM_ATTRIBUTES; attr++)
+ *s++ = '0' + board->slots[slot].card.attributes[attr];
+ *s++ = ' ';
+ } else {
+ span = lm_message_node_add_child (body, "span", " ");
+ lm_message_node_set_attribute (span, "style",
+ "font-family: Monospace;");
+ *s++ = '-';
+ *s++ = '-';
+ *s++ = '-';
+ *s++ = ' ';
+ }
+ }
+ *s = '\0';
+
+ lm_message_node_add_child (reply->node, "body", board_str);
+
+ result = lm_connection_send (game->lg.connection, reply, &error);
+ lm_message_unref (reply);
+
+ if (! result) {
+ g_error ("lm_connection_send failed: %s\n",
+ error->message);
+ loudgame_quit (&game->lg, 1);
+ }
+}
- board->needs_deal = 0;
+static void
+set_game_handle_hint (set_game_t *game,
+ const char *peer)
+{
+ loudgame_sendf (&game->lg, peer, "Sets possible: %d",
+ game->board.sets_possible);
+}
+
+static void
+set_game_handle_shuffle (set_game_t *game,
+ const char *peer)
+{
+ if (game->board.sets_possible) {
+ loudgame_sendf (&game->lg, peer,
+ "There are %d sets, refusing to shuffle.",
+ game->board.sets_possible);
+ return;
+ }
+
+ if (game->deck.num_cards == 0) {
+ loudgame_sendf (&game->lg, peer,
+ "Deck exhausted, starting a new game.");
+ set_game_new_game (game);
+ } else {
+ set_game_shuffle (game);
+ }
+}
+
+static void
+set_game_handle_set (set_game_t *game,
+ const char *peer,
+ const char *message)
+{
+ const char *s;
+ char *end = NULL;
+ int i;
+ int slots[3];
+ card_t cards[3];
+
+ s = message;
+ i = 0;
+ while (*s && i < 3) {
+ slots[i++] = strtoul (s, &end, 10);
+ if (end == s) {
+ loudgame_sendf (&game->lg, peer,
+ "Error: Not an integer: %s", s);
+ return;
+ }
+ s = end;
+ }
+
+ while (end && *end && isspace (*end))
+ end++;
+
+ if (i != 3 || *end != '\0') {
+ loudgame_sendf (&game->lg, peer,
+ "Error: The 'set' command requires exactly 3 integers");
+ return;
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (slots[i] < 0 || slots[i] > BOARD_MAX_SLOTS) {
+ loudgame_sendf (&game->lg, peer,
+ "Error: Value %d is out of range (0-%d)",
+ slots[i], BOARD_MAX_SLOTS);
+ return;
+ }
+ }
+
+ if (slots[0] == slots[1] ||
+ slots[0] == slots[2] ||
+ slots[1] == slots[2])
+ {
+ loudgame_sendf (&game->lg, peer,
+ "Error: All 3 values must be unique");
+ return;
+ }
+
+ for (i=0; i < 3; i++) {
+ if (game->board.slots[slots[i]].has_card) {
+ cards[i] = game->board.slots[slots[i]].card;
+ } else {
+ loudgame_sendf (&game->lg, peer,
+ "Error: There's no card at position %d", i);
+ return;
+ }
+ }
+
+ if (! is_set (cards, 3)) {
+ loudgame_sendf (&game->lg, peer,
+ "Sorry, that's not a set");
+ return;
+ }
+
+ loudgame_sendf (&game->lg, peer,
+ "Yes, that's a set!");
+ loudgame_broadcastf (&game->lg,
+ "%s found the set: %d %d %d. Please send a new 'show' command.",
+ peer,
+ slots[0], slots[1], slots[2]);
+
+ for (i = 0; i < 3; i++)
+ game->board.slots[slots[i]].has_card = 0;
+
+ board_count_sets_possible (&game->board);
+
+ deal (&game->deck, &game->board);
+}
+
+static void
+set_game_handle_help (set_game_t *game,
+ const char *peer)
+{
+ loudgame_sendf (&game->lg, peer,
+ "I'm a bot that allows you to play the game of SET.\n"
+ "Here are some generic commands I understand:\n"
+ LOUDGAME_HELP
+ "\n"
+ "And some game-specific commands:\n"
+ "\tshow \tShow the current cards on the board\n"
+ "\thint \tIndicate how many sets are currently possible\n"
+ "\tshuffle \tReturn the cards to the deck, shuffle and deal\n"
+ "\t \t(this is only allowed if no sets are possible)\n"
+ "\tset X Y Z\tClaim three cards as a set. The cards are numbered\n"
+ "\t \tleft-to-right, and top-to-bottom from 0 to 11.\n"
+ "\n"
+ "For more information about SET, or to purchase your own deck, visit\n"
+ "http://setgame.com . Please note that this server is unaffiliated\n"
+ "with Set Enterprises, Inc., who publish the SET card game.");
+}
+
+static void
+set_game_handle_message (loudgame_t *lg,
+ const char *peer,
+ const char *message)
+{
+ set_game_t *game = (set_game_t*) lg;
+
+ if (strcmp (message, "show") == 0)
+ set_game_handle_show (game, peer);
+ else if (strcmp (message, "help") == 0)
+ set_game_handle_help (game, peer);
+ else if (strcmp (message, "hint") == 0)
+ set_game_handle_hint (game, peer);
+ else if (strcmp (message, "shuffle") == 0)
+ set_game_handle_shuffle (game, peer);
+ else if (strncmp (message, "set", 3) == 0)
+ set_game_handle_set (game, peer, message + 3);
+ else
+ loudgame_sendf (lg, peer, "Unknown command: '%s'. Use 'help' to get a list of valid commands.", message);
}
/* Begin a new game */
static void
-game_init (game_t *game)
+set_game_new_game (set_game_t *game)
{
deck_init (&game->deck);
board_init (&game->board);
deal (&game->deck, &game->board);
}
-static void
-game_fini (game_t *game)
+static int
+set_game_init (set_game_t *game, int argc, char *argv[])
{
- /* Nothing to do. */
+ int err;
+
+ err = loudgame_init (&game->lg, argc, argv);
+ if (err)
+ return err;
+
+ set_game_new_game (game);
+
+ return 0;
}
/* Return the dealt cards to the deck, reshuffle, and deal again. */
static int
-reshuffle (deck_t *deck, board_t *board)
+set_game_shuffle (set_game_t *game)
{
+ board_t *board = &game->board;
+ deck_t *deck = &game->deck;
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;
}
int
-main (void)
+main (int argc, char *argv[])
{
- game_t game;
+ set_game_t game;
+ int err;
srand (time(0));
- game_init (&game);
+ err = set_game_init (&game, argc, argv);
+ if (err)
+ return err;
- board_print (&game.board);
+ game.lg.handle_message = set_game_handle_message;
- game_fini (&game);
+ err = loudgame_run (&game.lg);
+ if (err)
+ return err;
return 0;
}