static void
loa_game_new_game (loa_game_t *game)
{
- loa_board_init (&game->board);
+ loa_board_reset (&game->board);
}
static loa_bool_t
loudgame_broadcastf (&game->lg, "%s wins", peer);
}
+static void
+loa_game_handle_history (loa_game_t *game,
+ const char *peer)
+{
+ int i;
+
+ for (i = 0; i < game->board.num_moves; i++)
+ loudgame_sendf (&game->lg, peer, "%s",
+ loa_move_to_string (&game->board.moves[i]));
+}
+
static void
loa_game_handle_pass (loa_game_t *game, const char *peer)
{
"\tmove aNbN\tMove a piece, (eg. 'move b1d3')\n"
"\tpass \t\tSkip a turn (only legal if no moves are possible)\n"
"\tnew \t\tBegin a new game\n"
+ "\thistory \t\tShow the move history of the game\n"
"\thelp \t\tThis help message\n"
"\trules \t\tA description of the Lines of Action rules\n"
"\n"
loa_game_handle_pass (game, peer);
else if (strcmp (message, "new") == 0)
loa_game_handle_new (game, peer);
+ else if (strcmp (message, "history") == 0)
+ loa_game_handle_history (game, peer);
else if (strcmp (message, "help") == 0)
loa_game_handle_help (game, peer);
else if (strcmp (message, "rules") == 0)
if (err)
return err;
+ loa_board_init (&game->board);
+
loa_game_new_game (game);
return 0;
* Author: Carl Worth <cworth@cworth.org>
*/
+#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
+#include <string.h>
+#include <ctype.h>
#include <glib.h>
#include "loa-board.h"
+static loa_bool_t
+loa_move_is_valid (const loa_move_t *move)
+{
+ return (move->x1 >= 0 && move->x1 < LOA_BOARD_SIZE &&
+ move->y1 >= 0 && move->y1 < LOA_BOARD_SIZE &&
+ move->x2 >= 0 && move->x2 < LOA_BOARD_SIZE &&
+ move->y2 >= 0 && move->y2 < LOA_BOARD_SIZE);
+}
+
+const char *
+loa_move_to_string (const loa_move_t *move)
+{
+#define LOA_MOVE_STRING_SIZE 6
+ static char move_string[LOA_MOVE_STRING_SIZE];
+
+ if (! loa_move_is_valid (move)) {
+ strcpy (move_string, "***");
+ return move_string;
+ }
+
+ snprintf (move_string, LOA_MOVE_STRING_SIZE,
+ "%c%d%c%c%d",
+ 'a' + move->x1, LOA_BOARD_SIZE - move->y1,
+ move->is_capture ? 'x' : '-',
+ 'a' + move->x2, LOA_BOARD_SIZE - move->y2);
+
+ return move_string;
+}
+
+loa_bool_t
+loa_move_init_from_string (loa_move_t *move, const char *string)
+{
+ char xc1, xc2, sep;
+ int x1, y1, x2, y2;
+ int matched;
+
+ /* Avoid returning uninitialized data on error. */
+ move->x1 = 0;
+ move->y1 = 0;
+ move->x2 = 0;
+ move->y2 = 0;
+ move->is_capture = 0;
+
+ matched = sscanf (string, "%c%d%c%c%d", &xc1, &y1, &sep, &xc2, &y2);
+ if (matched != 5)
+ return FALSE;
+
+ x1 = tolower (xc1) - 'a';
+ x2 = tolower (xc2) - 'a';
+ y1 = LOA_BOARD_SIZE - y1;
+ y2 = LOA_BOARD_SIZE - y2;
+
+ if (x1 < 0 || x1 >= LOA_BOARD_SIZE ||
+ y1 < 0 || y1 >= LOA_BOARD_SIZE ||
+ x2 < 0 || x2 >= LOA_BOARD_SIZE ||
+ y2 < 0 || y2 >= LOA_BOARD_SIZE)
+ {
+ return FALSE;
+ }
+
+ if (sep != '-' && sep != 'x' && sep != 'X')
+ return FALSE;
+
+ move->x1 = x1;
+ move->y1 = y1;
+ move->x2 = x2;
+ move->y2 = y2;
+
+ if (sep == 'x' || sep == 'X')
+ move->is_capture = TRUE;
+ else
+ move->is_capture = FALSE;
+
+ return TRUE;
+}
+
/* Given an (x,y) position on the board, return the index of the array
* used to count pieces in diagonal rows running from upper-left to
* lower-right, (like a grave accent: à or like a backslash: \).
void
loa_board_init (loa_board_t *board)
+{
+ board->moves = NULL;
+ board->moves_size = 0;
+
+ loa_board_reset (board);
+}
+
+void
+loa_board_fini (loa_board_t *board)
+{
+ if (board->moves)
+ free (board->moves);
+}
+
+void
+loa_board_reset (loa_board_t *board)
{
int i, x, y;
loa_board_add_piece (board, LOA_BOARD_SIZE - 1, i, LOA_CELL_WHITE);
}
+ /* Leave board->moves and board->moves_size as allocated */
+ board->num_moves = 0;
+
board->player = LOA_PLAYER_BLACK;
}
}
static loa_bool_t
-loa_board_move_legal (loa_board_t *board,
- int x1, int y1,
- int x2, int y2,
- char **error)
+loa_board_move_legal (loa_board_t *board,
+ const loa_move_t *move,
+ char **error)
{
int x, y;
+ int x1, y1, x2, y2;
int dx, dy;
int step_x, step_y;
- if (x1 < 0 || y1 < 0 || x1 >= LOA_BOARD_SIZE || y1 >= LOA_BOARD_SIZE ||
- x2 < 0 || y2 < 0 || x2 >= LOA_BOARD_SIZE || y2 >= LOA_BOARD_SIZE)
+ if (! loa_move_is_valid (move))
{
*error = "Invalid coordinates (not on board)";
return FALSE;
}
+ x1 = move->x1;
+ y1 = move->y1;
+ x2 = move->x2;
+ y2 = move->y2;
if (board->cells[x1][y1] == LOA_CELL_EMPTY) {
*error = "There is no piece there to move";
return TRUE;
}
+/* Once the move is validated and executed, append it to the moves
+ * array that stores the move history. */
+static void
+loa_board_add_move_to_history (loa_board_t *board,
+ const loa_move_t *move)
+{
+ board->num_moves++;
+
+ if (board->num_moves > board->moves_size) {
+ if (board->moves_size)
+ board->moves_size *= 2;
+ else
+ board->moves_size = 20;
+
+ board->moves = realloc (board->moves,
+ board->moves_size * sizeof (loa_move_t));
+ if (board->moves == NULL) {
+ fprintf (stderr, "Out of memory.\n");
+ exit (1);
+ }
+ }
+
+ board->moves[board->num_moves - 1] = *move;
+}
+
int
loa_board_move (loa_board_t *board,
int x1, int y1,
char **error)
{
loa_cell_t cell;
+ loa_move_t move;
- if (! loa_board_move_legal (board, x1, y1, x2, y2, error))
+ move.x1 = x1;
+ move.y1 = y1;
+ move.x2 = x2;
+ move.y2 = y2;
+
+ if (! loa_board_move_legal (board, &move, error))
return FALSE;
cell = loa_board_remove_piece (board, x1, y1);
- loa_board_remove_piece (board, x2, y2);
- loa_board_add_piece (board, x2, y2, cell);
+ assert (cell == board->player);
+
+ cell = loa_board_remove_piece (board, x2, y2);
+ if (cell == LOA_CELL_EMPTY) {
+ move.is_capture = FALSE;
+ } else {
+ assert (cell != board->player);
+ move.is_capture = TRUE;
+ }
+
+ loa_board_add_piece (board, x2, y2, board->player);
+
+ loa_board_add_move_to_history (board, &move);
loa_board_next_player (board);
LOA_CELL_EMPTY
} loa_cell_t;
+typedef struct {
+ int x1; int y1;
+ int x2; int y2;
+ loa_bool_t is_capture;
+} loa_move_t;
+
+/* Return a string representation of a move. The return value is a
+ * pointer to a static buffer that will be reused from one call to the
+ * next. So the contents should be copied if needed. This function
+ * call is not thread-safe. */
+const char *
+loa_move_to_string (const loa_move_t *move);
+
+/* Initialize an loa_move_t structure based on a string value,
+ * (presumably the result of loa_move_to_string). Returns TRUE if
+ * successful. */
+loa_bool_t
+loa_move_init_from_string (loa_move_t *move, const char *string);
+
/* The implementation of board_group_size depends on the square of
* BOARD_SIZE being less than or equal to 64. */
#define LOA_BOARD_SIZE 8
int diag_grave_pieces[LOA_DIAG_ARRAY_SIZE];
int diag_acute_pieces[LOA_DIAG_ARRAY_SIZE];
+ int num_moves;
+ loa_move_t *moves;
+ int moves_size;
+
loa_player_t player;
} loa_board_t;
-/* Initialize a board for a new game of Lines of Action. The 12 pieces
+/* Initialize an loa_board_t structure. This function must be called
+ * before passing the board to any other loa_board function. It will
+ * implicitly call loa_board_reset for you. When you are finished
+ * using the board, you should call loa_board_fini. */
+void
+loa_board_init (loa_board_t *board);
+
+/* Free all resources associated with a board. */
+void
+loa_board_fini (loa_board_t *board);
+
+/* Reset board for a new game of Lines of Action. The 12 pieces
* for black and white will be put in their initial places and black
* will be set as the current player. */
void
-loa_board_init (loa_board_t *board);
+loa_board_reset (loa_board_t *board);
/* Does the square at (x,y) belong to a winning group? That is, is
* there a piece at (x,y) that is 8-way connected to all pieces on the