Update documentation of move command for new syntax
[loudgame] / lg-loa.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 <stdint.h>
25
26 #include <string.h>
27 #include <ctype.h>
28 #include <assert.h>
29
30 #include "loa-board.h"
31
32 typedef struct _loa_game {
33     loudgame_t lg;
34     loa_board_t board;
35 } loa_game_t;
36
37 static void
38 loa_game_new_game (loa_game_t *game)
39 {
40     loa_board_reset (&game->board);
41 }
42
43 static loa_bool_t
44 loa_game_move (loa_game_t *game, const char * peer, loa_move_t *move)
45 {
46     char *error;
47
48     if (! loa_board_move (&game->board, move, &error)) {
49         loudgame_sendf (&game->lg, peer, "Illegal move: %s: %s",
50                         loa_move_to_string (move), error);
51         return FALSE;
52     }
53
54     return TRUE;
55 }
56
57 static void
58 loa_game_handle_show (loa_game_t *game,
59                       const char *peer)
60 {
61     LmMessage *reply;
62     LmMessageNode *html, *body, *span;
63     gboolean result;
64     GError *error = NULL;
65     char *board_string;
66     char *line, *newline;
67
68     reply = lm_message_new (peer, LM_MESSAGE_TYPE_MESSAGE);
69     html = lm_message_node_add_child (reply->node, "html", "");
70     lm_message_node_set_attribute (html,
71                                    "xmlns",
72                                    "http://jabber.org/protocol/xhtml-im");
73     body = lm_message_node_add_child (html, "body", "");
74     lm_message_node_set_attribute (body,
75                                    "xmlns",
76                                    "http://www.w3.org/1999/xhtml");
77
78     if (game->board.player == LOA_PLAYER_BLACK)
79         lm_message_node_add_child (body, "span", "Black to move:");
80     else
81         lm_message_node_add_child (body, "span", "White to move:");
82
83     board_string = loa_board_to_string (&game->board);
84
85     line = board_string;
86     while (1) {
87         newline = strchr (line, '\n');
88         if (newline)
89             *newline = '\0';
90         lm_message_node_add_child (body, "br", "");
91         span = lm_message_node_add_child (body, "span", line);
92         lm_message_node_set_attribute (span, "style",
93                                        "font-family: Monospace;");
94         if (! newline)
95             break;
96         line = newline + 1;
97     }
98
99     free (board_string);
100
101     result = lm_connection_send (game->lg.connection, reply, &error);
102     lm_message_unref (reply);
103
104     if (! result) {
105         g_error ("lm_connection_send failed: %s\n",
106                  error->message);
107         loudgame_quit (&game->lg, 1);
108     }
109 }
110
111 static void
112 loa_game_handle_move (loa_game_t *game,
113                       const char *peer,
114                       const char *move_string)
115 {
116     loa_move_t move;
117
118     if (! loa_move_init_from_string (&move, move_string)) {
119         loudgame_sendf (&game->lg, peer,
120                         "Error: The 'move' command requires a move of the form 'b1-d3'");
121         return;
122     }
123
124     if (! loa_game_move (game, peer, &move))
125         return;
126
127     loudgame_broadcastf (&game->lg, "%s", loa_move_to_string (&move));
128
129     if (loa_board_is_won (&game->board, move.x2, move.y2))
130         loudgame_broadcastf (&game->lg, "%s wins", peer);
131 }
132
133 static void
134 loa_game_handle_history (loa_game_t *game,
135                          const char *peer)
136 {
137     int i;
138
139     for (i = 0; i < game->board.num_moves; i++)
140         loudgame_sendf (&game->lg, peer, "%s",
141                         loa_move_to_string (&game->board.moves[i]));
142 }
143
144 static void
145 loa_game_handle_pass (loa_game_t *game, const char *peer)
146 {
147     loudgame_broadcastf (&game->lg, "%s passes", peer);
148
149     if (loa_board_pass (&game->board))
150         return;
151
152     loudgame_sendf (&game->lg, peer,
153                     "Error: You cannot pass since you have a legal move available");
154 }
155
156 static void
157 loa_game_handle_help (loa_game_t *game, const char *peer)
158 {
159     loudgame_sendf (&game->lg, peer,
160                     "I'm a bot that allows you to play the game Lines of Action.\n"
161                     "Here are some generic commands I understand:\n"
162                     LOUDGAME_HELP
163                     "\n"
164                     "And some game-specific commands:\n"
165                     "\tshow     \t\tShow the current board\n"
166                     "\tmove aNbN\tMove a piece, (eg. 'move b1-d3')\n"
167                     "\tpass     \t\tSkip a turn (only legal if no moves are possible)\n"
168                     "\tnew      \t\tBegin a new game\n"
169                     "\thistory  \t\tShow the move history of the game\n"
170                     "\thelp     \t\tThis help message\n"
171                     "\trules    \t\tA description of the Lines of Action rules\n"
172                     "\n"
173                     "Lines of Action was invented by Claude Soucie and first made popular\n"
174                     "when its rules were published by Sid Sackson in \"A Gamut of Games\" (1969).\n"
175                     "\n"
176                     "If you are new to Lines of Action, type 'rules' now to learn the rules.\n");
177 }
178
179 static void
180 loa_game_handle_rules (loa_game_t *game, const char *peer)
181 {
182     loudgame_sendf (&game->lg, peer,
183                     "Lines of Action can be played with a standard (English) checkers set,\n"
184                     "that is an 8x8 board and 12 markers each of contrasting colors ('black' and\n"
185                     "'white'). The initial placement has 6 each of the black pieces on the top\n"
186                     "and bottom rows, and 6 each of the white pieces on the left and right columns\n"
187                     "leaving the four corner spaces empty. Play begins with the black player and\n"
188                     "then alternates.\n"
189                     "\n"
190                     "On each move a piece is moved in a straight line in any of eight directions,\n"
191                     "(similar to a queen's move in chess), but must be moved exactly the same\n"
192                     "number of spaces as there are pieces (of either color) in the row, column,\n"
193                     "or diagonal of the move. A piece may jump over pieces of its own color, but\n"
194                     "may not jump a piece of the opposite color. The final square of the move can\n"
195                     "be either empty or can contain a piece of the opposing color, in which case\n"
196                     "that piece is removed from the game.\n"
197                     "\n"
198                     "If a player has no possible move, then that player must pass, (but if the\n"
199                     "player has a possible move, then the player cannot pass).\n"
200                     "\n"
201                     "The goal of the game is to connect all of your remaining pieces into\n"
202                     "a single, connected group. Pieces that are diagonally adjacent are\n"
203                     "considered connected.\n"
204                     "\n"
205                     "If a move simultaneously creates a winning condition for both players, this\n"
206                     "is considered a win for the player making the move.\n"
207                     "\n"
208                     "Notes on this implementation:\n"
209                     "\n"
210                     "This implementation enforces the move rules described above, but will\n"
211                     "allow any person to make moves, (that is, you can control both\n"
212                     "colors). Think of it as being somewhat like a physical board where\n"
213                     "any person could move the pieces at any time, but generally two people\n"
214                     "will take turns and others will politely watch.\n"
215                     "\n"
216                     "Also, the game won't currently show you the board again after a\n"
217                     "player makes a move, so you'll need to keep typing 'show' to see\n"
218                     "updates. Yes, this is a bug, and yes it will be fixed soon.\n"
219                     "\n"
220                     "Lines of Action was invented by Claude Soucie and first made popular\n"
221                     "when its rules were published by Sid Sackson in \"A Gamut of Games\" (1969).\n");
222 }
223
224 static void
225 loa_game_handle_new (loa_game_t *game, const char *peer)
226 {
227     loudgame_broadcastf (&game->lg, "%s has started a new game", peer);
228     loa_game_new_game (game);
229 }
230
231 static void
232 loa_game_handle_message (loudgame_t *lg,
233                          const char *peer,
234                          const char *message)
235 {
236     loa_game_t *game = (loa_game_t *) lg;
237
238     if (strcmp (message, "show") == 0)
239         loa_game_handle_show (game, peer);
240     else if (strncmp (message, "move", 4) == 0)
241         loa_game_handle_move (game, peer, message + 4);
242     else if (strcmp (message, "pass") == 0)
243         loa_game_handle_pass (game, peer);
244     else if (strcmp (message, "new") == 0)
245         loa_game_handle_new (game, peer);
246     else if (strcmp (message, "history") == 0)
247         loa_game_handle_history (game, peer);
248     else if (strcmp (message, "help") == 0)
249         loa_game_handle_help (game, peer);
250     else if (strcmp (message, "rules") == 0)
251         loa_game_handle_rules (game, peer);
252     else
253         loudgame_sendf (lg, peer, "Unknown command: '%s'. Use 'help' to get a list of valid commands.", message);
254 }
255
256 static int
257 loa_game_init (loa_game_t *game, int argc, char *argv[])
258 {
259     int err;
260
261     err = loudgame_init (&game->lg, argc, argv);
262     if (err)
263         return err;
264
265     loa_board_init (&game->board);
266
267     loa_game_new_game (game);
268
269     return 0;
270 }
271
272 int
273 main (int argc, char **argv)
274 {
275     loa_game_t game;
276     int err;
277
278     err = loa_game_init (&game, argc, argv);
279     if (err)
280         return err;
281
282     game.lg.handle_message = loa_game_handle_message;
283
284     err = loudgame_run (&game.lg);
285     if (err)
286         return err;
287
288     return 0;
289 }