X-Git-Url: https://git.cworth.org/git?p=loudgame;a=blobdiff_plain;f=lg-set.c;h=a6594b27a3de4bc9ad9a621f68755f97e9a985ec;hp=145120092f20a91cb8d8aead43e9e1313861b5f0;hb=84366ba467d765ba7a14084b8b8c49fb1c61ab07;hpb=067f67d8b3d5b4a05c406f23e8129eb08413eb2f diff --git a/lg-set.c b/lg-set.c index 1451200..a6594b2 100644 --- a/lg-set.c +++ b/lg-set.c @@ -17,12 +17,15 @@ * Author: Carl Worth */ +#include "loudgame.h" + #include #include #include #include #include #include +#include #include @@ -73,22 +76,24 @@ typedef struct slot { 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) { @@ -140,34 +145,6 @@ is_set (card_t *cards, int num_cards) 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) { @@ -248,32 +225,10 @@ board_init (board_t *board) 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) { @@ -288,35 +243,277 @@ 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 commands I understand:\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; @@ -332,17 +529,22 @@ reshuffle (deck_t *deck, board_t *board) } 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; }