From 88c5b7fb802cd1eab050dc67289d86dcb37dec6c Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Mon, 3 Mar 2008 09:48:48 -0800 Subject: [PATCH] Add support for a 'history' command --- lg-loa.c | 18 +++++- loa-board.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++--- loa-board.h | 38 ++++++++++++- 3 files changed, 205 insertions(+), 12 deletions(-) diff --git a/lg-loa.c b/lg-loa.c index 4bf6230..1749143 100644 --- a/lg-loa.c +++ b/lg-loa.c @@ -37,7 +37,7 @@ typedef struct _loa_game { static void loa_game_new_game (loa_game_t *game) { - loa_board_init (&game->board); + loa_board_reset (&game->board); } static loa_bool_t @@ -143,6 +143,17 @@ loa_game_handle_move (loa_game_t *game, 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) { @@ -168,6 +179,7 @@ loa_game_handle_help (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" @@ -244,6 +256,8 @@ loa_game_handle_message (loudgame_t *lg, 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) @@ -261,6 +275,8 @@ loa_game_init (loa_game_t *game, int argc, char *argv[]) if (err) return err; + loa_board_init (&game->board); + loa_game_new_game (game); return 0; diff --git a/loa-board.c b/loa-board.c index 8ea6f99..21f4302 100644 --- a/loa-board.c +++ b/loa-board.c @@ -17,14 +17,93 @@ * Author: Carl Worth */ +#include #include #include #include +#include +#include #include #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: \). @@ -89,6 +168,22 @@ loa_board_remove_piece (loa_board_t *board, int x, int y) 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; @@ -116,6 +211,9 @@ loa_board_init (loa_board_t *board) 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; } @@ -177,22 +275,25 @@ loa_board_is_won (loa_board_t *board, int x, int y) } 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"; @@ -285,6 +386,31 @@ loa_board_pass (loa_board_t *board) 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, @@ -292,13 +418,30 @@ loa_board_move (loa_board_t *board, 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); diff --git a/loa-board.h b/loa-board.h index 4532e01..4a4f690 100644 --- a/loa-board.h +++ b/loa-board.h @@ -40,6 +40,25 @@ typedef enum { 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 @@ -58,14 +77,29 @@ typedef struct { 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 -- 2.43.0