]> git.cworth.org Git - loudgame/blobdiff - lg-loa.c
Add support for a 'history' command
[loudgame] / lg-loa.c
index d2660d2a4675f8b36a674bdd247c9ee5fe6a14c9..174914307f71ca72faf74a496b08ce00e2a2d336 100644 (file)
--- a/lg-loa.c
+++ b/lg-loa.c
 #include <ctype.h>
 #include <assert.h>
 
 #include <ctype.h>
 #include <assert.h>
 
-typedef int loa_bool_t;
-
-#ifndef FALSE
-#define FALSE 0
-#endif
-#ifndef TRUE
-#define TRUE  1
-#endif
-
-typedef enum {
-    LOA_PLAYER_BLACK,
-    LOA_PLAYER_WHITE
-} loa_player_t;
-
-typedef enum {
-    LOA_CELL_BLACK = LOA_PLAYER_BLACK,
-    LOA_CELL_WHITE = LOA_PLAYER_WHITE,
-    LOA_CELL_EMPTY
-} loa_cell_t;
-
-/* 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
-#define LOA_DIAG_ARRAY_SIZE (2 * LOA_BOARD_SIZE - 1)
-
-typedef struct {
-    loa_cell_t cells[LOA_BOARD_SIZE][LOA_BOARD_SIZE];
-
-    /* Number of black and white pieces */
-    int num_pieces[2];
-
-    /* Number of pieces (of either color) in each row, column, and
-     * diagonal. */
-    int row_pieces[LOA_BOARD_SIZE];
-    int col_pieces[LOA_BOARD_SIZE];
-    int diag_grave_pieces[LOA_DIAG_ARRAY_SIZE];
-    int diag_acute_pieces[LOA_DIAG_ARRAY_SIZE];
-
-    loa_player_t player;
-} loa_board_t;
+#include "loa-board.h"
 
 typedef struct _loa_game {
     loudgame_t lg;
     loa_board_t board;
 } loa_game_t;
 
 
 typedef struct _loa_game {
     loudgame_t lg;
     loa_board_t board;
 } loa_game_t;
 
-static void
-loa_board_next_player (loa_board_t *board)
-{
-    if (board->player == LOA_PLAYER_BLACK)
-       board->player = LOA_PLAYER_WHITE;
-    else
-       board->player = LOA_PLAYER_BLACK;
-}
-
-static int
-loa_board_group_size_recursive (loa_board_t *board, int x, int y,
-                               loa_cell_t cell,
-                               uint64_t *visited)
-{
-    uint64_t bit;
-
-    if (x < 0 || y < 0)
-       return 0;
-
-    if (x >= LOA_BOARD_SIZE || y >= LOA_BOARD_SIZE)
-       return 0;
-
-    bit = 1ll << (x * LOA_BOARD_SIZE + y);
-    if (*visited & bit)
-       return 0;
-
-    *visited |= bit;
-
-    if (board->cells[x][y] != cell)
-       return 0;
-
-    return 1 +
-       loa_board_group_size_recursive (board, x-1, y-1, cell, visited) +
-       loa_board_group_size_recursive (board, x-1, y  , cell, visited) +
-       loa_board_group_size_recursive (board, x-1, y+1, cell, visited) +
-       loa_board_group_size_recursive (board, x  , y-1, cell, visited) +
-       loa_board_group_size_recursive (board, x  , y  , cell, visited) +
-       loa_board_group_size_recursive (board, x  , y+1, cell, visited) +
-       loa_board_group_size_recursive (board, x+1, y-1, cell, visited) +
-       loa_board_group_size_recursive (board, x+1, y  , cell, visited) +
-       loa_board_group_size_recursive (board, x+1, y+1, cell, visited);
-}
-
-static int
-loa_board_group_size (loa_board_t *board, int x, int y)
-{
-    uint64_t visited = 0ll;
-    loa_cell_t cell = board->cells[x][y];
-
-    return loa_board_group_size_recursive (board, x, y, cell, &visited);
-}
-
-static int
-loa_board_is_won (loa_board_t *board, int x, int y)
-{
-    loa_cell_t cell = board->cells[x][y];
-
-    if (cell == LOA_CELL_EMPTY)
-       return 0;
-
-    if (loa_board_group_size (board, x, y) == board->num_pieces[cell])
-       return 1;
-
-    return 0;
-}
-
-/* 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: \).
- *
- * This is the array to look into when a move occurs with dx and dy of
- * the same sign. */
-static int
-_grave_index (int x, int y)
-{
-    return x - y + LOA_BOARD_SIZE - 1;
-}
-
-/* Given an (x,y) position on the board, return the index of the array
- * used to count pieces in diagonal rows running from lower-left to
- * upper-right, (like an acute accent: á or like a forward slash: /).
- *
- * This is the array to look into when a move occurs with dx and dy of
- * opposite sign, (one positive, one negative). */
-static int
-_acute_index (int x, int y)
-{
-    return x + y;
-}
-
-static loa_bool_t
-loa_board_move_legal (loa_board_t *board,
-                     int x1, int y1,
-                     int x2, int y2,
-                     char **error)
-{
-    int x, y;
-    int dx, dy;
-    int step_x, step_y;
-
-    if (board->cells[x1][y1] == LOA_CELL_EMPTY) {
-       *error = "There is no piece there to move";
-       return FALSE;
-    }
-
-    if (board->cells[x2][y2] == board->cells[x1][y1]) {
-       *error = "You cannot capture your own piece";
-       return FALSE;
-    }
-
-    dx = x2 - x1;
-    dy = y2 - y1;
-
-    /* Here's the meat of Lines of Action legaility: Does the distance
-     * moved exactly match the number of pieces (of either color) in
-     * the row, column, or diagonal of the movement. */
-    if (dx == 0) { 
-       /* Column */
-       if (abs (dy) != board->col_pieces[x1]) {
-           *error = "The move distance does not match the number of pieces in that column";
-           return FALSE;
-       }
-    } else if (dy == 0) {
-       /* Row */
-       if (abs (dx) != board->row_pieces[y1]) {
-           *error = "The move distance does not match the number of pieces in that row";
-           return FALSE;
-       }
-    } else {
-       if (abs (dx) != abs (dy)) {
-           *error = "That move is not in a row, column, or diagonal";
-           return FALSE;
-       }
-       /* Diagonal */
-       if ((dx > 0) == (dy > 0)) {
-           if (abs (dx) != board->diag_grave_pieces[_grave_index (x1, y1)]) {
-               *error = "The move distance does not match the number of pieces in that diagonal";
-               return FALSE;
-           }
-       } else {
-           if (abs (dx) != board->diag_acute_pieces[_acute_index (x1, y1)]) {
-               *error = "The move distance does not match the number of pieces in that diagonal";
-               return FALSE;
-           }
-       }
-    }
-
-    /* Finally, we have to ensure that no opponent pieces are being
-     * jumped. */
-    step_x = dx ? ((dx < 0) ? -1 : +1) : 0;
-    step_y = dy ? ((dy < 0) ? -1 : +1) : 0;
-    for (x = x1 + step_x, y = y1 + step_y;
-        x != x2 || y != y2;
-        x += step_x, y += step_y)
-    {
-       if (board->cells[x][y] != LOA_CELL_EMPTY &&
-           board->cells[x][y] != board->cells[x1][y1])
-       {
-           *error = "You cannot jump an opponent's piece";
-           return FALSE;
-       }
-    }
-
-    return TRUE;
-}
-
-static void
-loa_board_add_piece (loa_board_t *board, int x, int y, loa_cell_t cell)
-{
-    assert (cell == LOA_CELL_BLACK || cell == LOA_CELL_WHITE);
-    assert (board->cells[x][y] == LOA_CELL_EMPTY);
-
-    board->col_pieces[x]++;
-    board->row_pieces[y]++;
-    board->diag_grave_pieces[_grave_index (x, y)]++;
-    board->diag_acute_pieces[_acute_index (x, y)]++;
-
-    board->num_pieces[cell]++;
-
-    board->cells[x][y] = cell;
-}
-
-static loa_cell_t
-loa_board_remove_piece (loa_board_t *board, int x, int y)
-{
-    loa_cell_t cell;
-
-    cell = board->cells[x][y];
-
-    if (cell == LOA_CELL_EMPTY)
-       return LOA_CELL_EMPTY;
-
-    board->col_pieces[x]--;
-    board->row_pieces[y]--;
-    board->diag_grave_pieces[_grave_index (x, y)]--;
-    board->diag_acute_pieces[_acute_index (x, y)]--;
-
-    board->num_pieces[cell]--;
-
-    board->cells[x][y] = LOA_CELL_EMPTY;
-
-    return cell;
-}
-
-static void
-loa_board_init (loa_board_t *board)
-{
-    int i, x, y;
-
-    for (x = 0; x < LOA_BOARD_SIZE; x++)
-       for (y = 0; y < LOA_BOARD_SIZE; y++)
-           board->cells[x][y] = LOA_CELL_EMPTY;
-
-    board->num_pieces[LOA_CELL_BLACK] = 0;
-    board->num_pieces[LOA_CELL_WHITE] = 0;
-
-    for (i = 0; i < LOA_BOARD_SIZE; i++) {
-       board->row_pieces[i] = 0;
-       board->col_pieces[i] = 0;
-    }
-
-    for (i = 0; i < LOA_DIAG_ARRAY_SIZE; i++) {
-       board->diag_grave_pieces[i] = 0;
-       board->diag_acute_pieces[i] = 0;
-    }
-
-    for (i = 1; i < LOA_BOARD_SIZE - 1; i++) {
-       loa_board_add_piece (board, i, 0, LOA_CELL_BLACK);
-       loa_board_add_piece (board, i, LOA_BOARD_SIZE - 1, LOA_CELL_BLACK);
-       loa_board_add_piece (board, 0, i, LOA_CELL_WHITE);
-       loa_board_add_piece (board, LOA_BOARD_SIZE - 1, i, LOA_CELL_WHITE);
-    }
-
-    board->player = LOA_PLAYER_BLACK;
-}
-
-/* A few different ideas for displaying boards:
- *
- * 8│ │●│●│●│●│●│●│ |      8| |*|*|*|*|*|*| |
- * 7│○│ │ │ │ │ │ │○|      7|o| | | | | | |o|
- * 6│○│ │ │ │ │ │ │○|      6|o| | | | | | |o|
- * 5│○│ │ │ │ │ │ │○|      5|o| | | | | | |o|
- * 4│○│ │ │ │ │ │ │○|      4|o| | | | | | |o|
- * 3│○│ │ │ │ │ │ │○|      3|o| | | | | | |o|
- * 2│○│ │ │ │ │ │ │○|      2|o| | | | | | |o|
- * 1│ │●│●│●│●│●│●│ |      1| |*|*|*|*|*|*| |
- *   A B C D E F G H     A B C D E F G H
- *
- *  ┌───┬───┬───┬───┬───┬───┬───┬───┐          ------------------------------- 
- * 8│   │ ● │ ● │ ● │ ● │ ● │ ● │   │    8|   | * | * | * | * | * | * |   |
- *  ├───┼───┼───┼───┼───┼───┼───┼───┤         |---+---+---+---+---+---+---+---|
- * 7│ ○ │   │   │   │   │   │   │ ○ │    7| o |   |   |   |   |   |   | o |
- *  ├───┼───┼───┼───┼───┼───┼───┼───┤         |---+---+---+---+---+---+---+---|
- * 6│ ○ │   │   │   │   │   │   │ ○ │    6| o |   |   |   |   |   |   | o |
- *  ├───┼───┼───┼───┼───┼───┼───┼───┤         |---+---+---+---+---+---+---+---|
- * 5│ ○ │   │   │   │   │   │   │ ○ │    5| o |   |   |   |   |   |   | o |
- *  ├───┼───┼───┼───┼───┼───┼───┼───┤         |---+---+---+---+---+---+---+---|
- * 4│ ○ │   │   │   │   │   │   │ ○ │    4| o |   |   |   |   |   |   | o |
- *  ├───┼───┼───┼───┼───┼───┼───┼───┤         |---+---+---+---+---+---+---+---|
- * 3│ ○ │   │   │   │   │   │   │ ○ │    3| o |   |   |   |   |   |   | o |
- *  ├───┼───┼───┼───┼───┼───┼───┼───┤         |---+---+---+---+---+---+---+---|
- * 2│ ○ │   │   │   │   │   │   │ ○ │    2| o |   |   |   |   |   |   | o |
- *  ├───┼───┼───┼───┼───┼───┼───┼───┤         |---+---+---+---+---+---+---+---|
- * 1│   │ ● │ ● │ ● │ ● │ ● │ ● │   │    1|   | * | * | * | * | * | * |   |
- *  └───┴───┴───┴───┴───┴───┴───┴───┘          ------------------------------- 
- *    A   B   C   D   E   F   G   H       A   B   C   D   E   F   G   H
- */
-static char *
-loa_board_to_string (loa_board_t *board)
-{
-    int x, y;
-    /* In order of BLACK, WHITE, EMPTY */
-    const char* cell_strings[] = {"●","○"," "};
-    const char   board_header[] = "┌───┬───┬───┬───┬───┬───┬───┬───┐";
-    const char     row_header[] = "│ ";
-    const char cell_separator[] =    " │ ";
-    const char     row_footer[] =                                " │";
-    const char  row_separator[] = "├───┼───┼───┼───┼───┼───┼───┼───┤";
-    const char   board_footer[] = "└───┴───┴───┴───┴───┴───┴───┴───┘";
-    char *board_string = g_strdup ("");
-
-#define APPEND(str) do {                                       \
-    char *_new = g_strconcat (board_string, (str), NULL);      \
-    free (board_string);                                       \
-    board_string = _new;                                       \
-} while (0)
-
-#define APPENDF(format, ...) do {                              \
-    char *str = g_strdup_printf (format, ## __VA_ARGS__);      \
-    APPEND (str);                                              \
-    free (str);                                                        \
-} while (0)
-
-    APPENDF (" %s\n", board_header);
-    for (y = 0; y < LOA_BOARD_SIZE; y++) {
-       APPENDF ("%d%s", LOA_BOARD_SIZE - y, row_header);
-       for (x = 0; x < LOA_BOARD_SIZE; x++) {
-           APPENDF ("%s", cell_strings[board->cells[x][y]]);
-           if (x != LOA_BOARD_SIZE - 1)
-               APPENDF ("%s", cell_separator);
-       }
-       APPENDF ("%s\n", row_footer);
-       if (y != LOA_BOARD_SIZE -1)
-           APPENDF (" %s\n", row_separator);
-    }
-    APPENDF (" %s\n", board_footer);
-
-    APPENDF ("   ");
-    for (x = 0; x < LOA_BOARD_SIZE; x++) {
-       APPENDF ("%c", 'A' + x);
-       if (x != LOA_BOARD_SIZE - 1)
-           APPENDF ("   ");
-    }
-    APPENDF ("\n");
-
-    return board_string;
-}
-
 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
 loa_game_move (loa_game_t *game, const char * peer,
               int x1, int y1, int x2, int y2)
 {
 }
 
 static loa_bool_t
 loa_game_move (loa_game_t *game, const char * peer,
               int x1, int y1, int x2, int y2)
 {
-    loa_board_t *board = &game->board;
-    loa_cell_t cell;
     char *error;
 
     char *error;
 
-    if (x1 < 0 || y1 < 0 || x1 >= LOA_BOARD_SIZE || y1 >= LOA_BOARD_SIZE) {
-       loudgame_sendf (&game->lg, peer, "Invalid coordinates (not on board).");
-       return FALSE;
-    }
-
-    if (board->cells[x1][y1] != board->player) {
-       loudgame_sendf (&game->lg, peer, "Cell at (%d,%d) does not belong to current player.",
-                       x1, y1);
-       return FALSE;
-    }
-
-    if (! loa_board_move_legal (&game->board, x1, y1, x2, y2, &error)) {
-       loudgame_sendf (&game->lg, peer, "Illegal move: %c%d%c%d",
+    if (! loa_board_move (&game->board, x1, y1, x2, y2, &error)) {
+       loudgame_sendf (&game->lg, peer, "Illegal move: %c%d%c%d: %s",
                        'a' + x1, LOA_BOARD_SIZE - y1,
                        'a' + x1, LOA_BOARD_SIZE - y1,
-                       'a' + x2, LOA_BOARD_SIZE - y2);
+                       'a' + x2, LOA_BOARD_SIZE - y2,
+                       error);
        return FALSE;
     }
 
        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);
-
-    loa_board_next_player (board);
-
     return TRUE;
 }
 
     return TRUE;
 }
 
@@ -518,12 +143,27 @@ 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)
 {
     loudgame_broadcastf (&game->lg, "%s passes", peer);
 
 static void
 loa_game_handle_pass (loa_game_t *game, const char *peer)
 {
     loudgame_broadcastf (&game->lg, "%s passes", peer);
 
-    loa_board_next_player (&game->board);
+    if (loa_board_pass (&game->board))
+       return;
+
+    loudgame_sendf (&game->lg, peer,
+                   "Error: You cannot pass since you have a legal move available");
 }
 
 static void
 }
 
 static void
@@ -539,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"
@@ -615,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)
@@ -632,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;