]> git.cworth.org Git - loudgame/commitdiff
Add support for a 'history' command
authorCarl Worth <cworth@cworth.org>
Mon, 3 Mar 2008 17:48:48 +0000 (09:48 -0800)
committerCarl Worth <cworth@cworth.org>
Mon, 3 Mar 2008 17:48:48 +0000 (09:48 -0800)
lg-loa.c
loa-board.c
loa-board.h

index 4bf623076442ea9d8c9d7e7369fb8a3226f3f355..174914307f71ca72faf74a496b08ce00e2a2d336 100644 (file)
--- 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)
 {
 static void
 loa_game_new_game (loa_game_t *game)
 {
-    loa_board_init (&game->board);
+    loa_board_reset (&game->board);
 }
 
 static loa_bool_t
 }
 
 static loa_bool_t
@@ -143,6 +143,17 @@ loa_game_handle_move (loa_game_t *game,
        loudgame_broadcastf (&game->lg, "%s wins", peer);
 }
 
        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)
 {
 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"
                    "\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"
                    "\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);
        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)
     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;
 
     if (err)
        return err;
 
+    loa_board_init (&game->board);
+
     loa_game_new_game (game);
 
     return 0;
     loa_game_new_game (game);
 
     return 0;
index 8ea6f99d36bb45ad758011bee975ef57faf3c86a..21f4302ffaf676283c0e7b1495f5839f191d0194 100644 (file)
  * Author: Carl Worth <cworth@cworth.org>
  */
 
  * Author: Carl Worth <cworth@cworth.org>
  */
 
+#include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <assert.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <assert.h>
+#include <string.h>
+#include <ctype.h>
 
 #include <glib.h>
 
 #include "loa-board.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: \).
 /* 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)
 
 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;
 
 {
     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);
     }
 
        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;
 }
 
     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
 }
 
 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 x, y;
+    int x1, y1, x2, y2;
     int dx, dy;
     int step_x, step_y;
 
     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;
     }
 
     {
        *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";
 
     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;
 }
 
     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,
 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;
                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);
        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_board_next_player (board);
 
index 4532e01f156c2d22c67ce7549e3c3f3d437cd517..4a4f690855fdd1541d365c29af262e6b5c0acfab 100644 (file)
@@ -40,6 +40,25 @@ typedef enum {
     LOA_CELL_EMPTY
 } loa_cell_t;
 
     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
 /* 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 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;
 
     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
  * 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
 
 /* 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