diff options
author | Robby Zambito <contact@robbyzambito.me> | 2022-01-21 17:20:17 -0500 |
---|---|---|
committer | Robby Zambito <contact@robbyzambito.me> | 2022-01-21 17:20:17 -0500 |
commit | f1a4b5757f0d20739270b22726ebf8442b13cb50 (patch) | |
tree | 93e170ddebc79cf5a41777fc040797bc6dfa5d85 | |
parent | 3a85be307b6240525f032b9fad5114bdf172bdc1 (diff) |
Fleshed out Scheme primitives
* Moved board specific functions from othello.h to new othello_board.h
* Removed unused function pointer for player moves
* Added prototypes in headers for SCM functions, including
scm_get_current_player,
* Made get_winner accept a board and current player. This is useful for
predictions to see if some move would make the other player a winner.
* get_winner also writes the players scores to provided pointers.
-rw-r--r-- | cmd/main.c | 1 | ||||
-rw-r--r-- | include/othello.h | 26 | ||||
-rw-r--r-- | include/othello_board.h | 43 | ||||
-rw-r--r-- | include/othello_move.h | 8 | ||||
-rw-r--r-- | src/board.c | 86 | ||||
-rw-r--r-- | src/game_loop.c | 25 | ||||
-rw-r--r-- | src/move.c | 134 | ||||
-rw-r--r-- | src/valid_moves.c | 1 | ||||
-rw-r--r-- | strategies/first-valid-move.scm | 26 |
9 files changed, 226 insertions, 124 deletions
@@ -28,6 +28,7 @@ #include <unistd.h> #include "othello.h" +#include "othello_board.h" int main(int argc, char **argv) { int exit_status = EXIT_SUCCESS; diff --git a/include/othello.h b/include/othello.h index 40a36c4..e46c13e 100644 --- a/include/othello.h +++ b/include/othello.h @@ -20,6 +20,7 @@ #pragma once +#include <libguile.h> #include <stdbool.h> #include <stdio.h> @@ -34,30 +35,19 @@ struct move { int col; }; -// Function pointers for each players move function -extern struct move (*player_one_get_move)(void); -extern struct move (*player_two_get_move)(void); - -// Set a board to a new game state. -void initialize_board(void); - -// Copy the specified board. It must be freed by the caller. -enum player_color **copy_board(enum player_color **board); - -// Free the specified board. -void free_board(enum player_color **board); // Plays a game to completion, starting with board. enum player_color game_loop(char *player_one_strategy, char *player_two_strategy); -// Prints the current state of the board, including coordinates in the margins. -void print_board(enum player_color **board); - // Returns the color of the player whose turn it is. enum player_color get_current_player(void); +SCM scm_get_current_player(void); -// Returns the current board. -enum player_color **get_board(void); +enum player_color scm_player_to_c_player(SCM player); -enum player_color get_winner(void); +enum player_color get_winner(enum player_color **board, + enum player_color current_player, + int *white_score, + int *black_score); +SCM scm_get_winner(SCM board, SCM current_player); diff --git a/include/othello_board.h b/include/othello_board.h new file mode 100644 index 0000000..8852ad0 --- /dev/null +++ b/include/othello_board.h @@ -0,0 +1,43 @@ +/* This file is a part of othello-ai-guile-c + * + * Copyright (C) 2021 Robby Zambito + * + * othello-ai-guile-c is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * othello-ai-guile-c is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#define _GNU_SOURCE + +#pragma once + +#include "othello.h" + +// Set a board to a new game state. +void initialize_board(void); + +// Copy the specified board. It must be freed by the caller. +enum player_color **copy_board(enum player_color **board); + +// Free the specified board. +void free_board(enum player_color **board); + +// Prints the current state of the board, including coordinates in the margins. +void print_board(enum player_color **board); + +// Returns the current board. +enum player_color **get_board(void); +SCM scm_get_board(void); + +SCM scm_board_from_c_board(enum player_color **board); + +enum player_color **scm_board_to_c_board(SCM board); diff --git a/include/othello_move.h b/include/othello_move.h index 281cb9b..2e2974b 100644 --- a/include/othello_move.h +++ b/include/othello_move.h @@ -30,6 +30,9 @@ struct move get_scm_move(char *strategy_path); bool is_valid_move(enum player_color **board, const enum player_color current_player, const struct move move); +SCM scm_is_valid_move(SCM board, SCM current_player, SCM move); + +SCM scm_valid_moves(SCM scm_board, SCM player); // True if current_player has any valid moves. // Otherwise false. @@ -39,3 +42,8 @@ bool has_valid_moves(enum player_color **board, /* Returns true if the move was valid */ bool apply_move(enum player_color **board, enum player_color current_player, struct move move); +// Does not actually modify the current board state. Returns a new board with +// the move applied. If the move was not valid, an empty list (nil) is returned. +SCM scm_apply_move(SCM move, SCM board, SCM current_player); + +struct move scm_move_to_c_move(SCM scm_move); diff --git a/src/board.c b/src/board.c index 6859952..a03fddd 100644 --- a/src/board.c +++ b/src/board.c @@ -18,14 +18,20 @@ #define _GNU_SOURCE +#include <libguile.h> #include <stdlib.h> #include "othello.h" +#include "othello_board.h" #include "othello_move.h" static enum player_color **board; -enum player_color **get_board(void) { return board; } +enum player_color **get_board() { return board; } + +SCM scm_get_board() { + return scm_board_from_c_board(board); +} void initialize_board() { board = calloc(8, sizeof(enum player_color *)); @@ -47,23 +53,42 @@ void initialize_board() { board[4][4] = WHITE; } -enum player_color get_winner() { - int white_score = 0, black_score = 0; +enum player_color get_winner(enum player_color **board, + enum player_color current_player, int *white_score, + int *black_score) { + + int ws = 0, bs = 0; for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { // This takes advantage of the fact that the 1 is the same thing as a // true, and 0 is the same thing as a false. This will count the number of // white and black spaces. - white_score += (int)(board[row][col] == WHITE); - black_score += (int)(board[row][col] == BLACK); + ws += (int)(board[row][col] == WHITE); + bs += (int)(board[row][col] == BLACK); } } + if (white_score != NULL) { + *white_score = ws; + } + if (black_score != NULL) { + *black_score = bs; + } + return white_score > black_score ? WHITE : black_score > white_score ? BLACK : EMPTY; } +SCM scm_get_winner(SCM scm_board, SCM scm_current_player) { + enum player_color **board = scm_board_to_c_board(scm_board); + enum player_color current_player = scm_player_to_c_player(scm_current_player); + + bool res = get_winner(board, current_player, NULL, NULL); + free_board(board); + return scm_from_bool(res); +} + // Should be freed be the caller enum player_color **copy_board(enum player_color **other) { enum player_color **board = calloc(8, sizeof(enum player_color *)); @@ -84,3 +109,54 @@ void free_board(enum player_color **board) { } free(board); } + +SCM scm_board_from_c_board(enum player_color **board) { + // 2D list of empty rows + SCM scm_board = + scm_make_list(scm_from_int(8), SCM_EOL); + + for (int i = 0; i < 8; i++) { + // cons up each row. + for (int j = 7; j >= 0; j--) { + if (board[i][j] == WHITE) { + scm_list_set_x(scm_board, scm_from_int(i), + scm_cons(scm_from_utf8_symbol("white"), + scm_list_ref(scm_board, scm_from_int(i)))); + } else if (board[i][j] == BLACK) { + scm_list_set_x(scm_board, scm_from_int(i), + scm_cons(scm_from_utf8_symbol("black"), + scm_list_ref(scm_board, scm_from_int(i)))); + } else { + scm_list_set_x(scm_board, scm_from_int(i), + scm_cons(scm_from_utf8_symbol("empty"), + scm_list_ref(scm_board, scm_from_int(i)))); + } + } + } + + return scm_board; +} + +enum player_color **scm_board_to_c_board(SCM scm_board) { + enum player_color **board = calloc(8, sizeof(enum player_color *)); + for (int i = 0; i < 8; i++) { + board[i] = calloc(8, sizeof(enum player_color)); + for (int j = 0; j < 8; j++) { + if (scm_is_true( + scm_eq_p(scm_list_ref(scm_list_ref(scm_board, scm_from_int(i)), + scm_from_int(j)), + scm_from_utf8_symbol("white")))) { + board[i][j] = WHITE; + } else if (scm_is_true(scm_eq_p( + scm_list_ref(scm_list_ref(scm_board, scm_from_int(i)), + scm_from_int(j)), + scm_from_utf8_symbol("black")))) { + board[i][j] = BLACK; + } else { + board[i][j] = EMPTY; + } + } + } + + return board; +} diff --git a/src/game_loop.c b/src/game_loop.c index 8186623..f52bdb7 100644 --- a/src/game_loop.c +++ b/src/game_loop.c @@ -28,6 +28,7 @@ #include <string.h> #include "othello.h" +#include "othello_board.h" #include "othello_move.h" #define STREQ(a, b) (strcmp(a, b) == 0) @@ -36,8 +37,26 @@ static enum player_color current_player; enum player_color get_current_player(void) { return current_player; } -struct move (*player_one_get_move)(); -struct move (*player_two_get_move)(); +SCM scm_get_current_player(void) { + switch (current_player) { + case WHITE: + return scm_from_utf8_symbol("white"); + case BLACK: + return scm_from_utf8_symbol("black"); + default: + return scm_from_utf8_symbol("empty"); + } +} + +enum player_color scm_player_to_c_player(SCM player) { + if (scm_is_true(scm_eq_p(player, scm_from_utf8_symbol("white")))) { + return WHITE; + } else if (scm_is_true(scm_eq_p(player, scm_from_utf8_symbol("black")))) { + return BLACK; + } else { + return EMPTY; + } +} static struct move current_player_move(enum player_color current_player, char *player_one_strategy_path, @@ -81,5 +100,5 @@ enum player_color game_loop(char *player_one_strategy_path, rl_clear_history(); } - return get_winner(); + return get_winner(get_board(), current_player, NULL, NULL); } @@ -25,6 +25,7 @@ #include <readline/readline.h> #include "othello.h" +#include "othello_board.h" #include "othello_move.h" #define STREQ(a, b) (strcmp(a, b) == 0) @@ -175,104 +176,87 @@ bool apply_move(enum player_color **board, enum player_color current_player, return flipped_up || flipped_down || flipped_left || flipped_right; } -// Return the current board as a list of lists -static SCM scm_get_board(void) { - enum player_color **board = get_board(); - // 2D list of empty spaces - SCM scm_board = - scm_make_list(scm_from_int(8), scm_make_list(scm_from_int(0), NULL)); - for (int i = 0; i < 8; i++) { - for (int j = 7; j >= 0; j--) { - if (board[i][j] == WHITE) { - scm_list_set_x(scm_board, scm_from_int(i), - scm_cons(scm_from_utf8_symbol("white"), - scm_list_ref(scm_board, scm_from_int(i)))); - } else if (board[i][j] == BLACK) { - scm_list_set_x(scm_board, scm_from_int(i), - scm_cons(scm_from_utf8_symbol("black"), - scm_list_ref(scm_board, scm_from_int(i)))); - } else { - scm_list_set_x(scm_board, scm_from_int(i), - scm_cons(scm_from_utf8_symbol("empty"), - scm_list_ref(scm_board, scm_from_int(i)))); - } - } +SCM scm_apply_move(SCM scm_move, SCM scm_board, SCM scm_player) { + struct move move = scm_move_to_c_move(scm_move); + + enum player_color **board; + if (scm_is_null(scm_board)) { + board = copy_board(get_board()); + } else { + board = scm_board_to_c_board(scm_board); + } + + enum player_color player; + if (scm_is_null(scm_player)) { + player = get_current_player(); + } else { + player = scm_player_to_c_player(scm_player); } - return scm_board; + + SCM res_board; + + if (apply_move(board, player, move)) { + res_board = scm_board_from_c_board(board); + } else { + res_board = SCM_EOL; + } + + free_board(board); + return res_board; } -static struct move scm_move_to_c_move(SCM scm_move) { +struct move scm_move_to_c_move(SCM scm_move) { struct move move = {-1, -1}; move.row = scm_to_int(scm_car(scm_move)); move.col = scm_to_int(scm_cdr(scm_move)); return move; } -static SCM scm_current_player(void) { - switch (get_current_player()) { - case WHITE: - return scm_from_utf8_symbol("white"); - case BLACK: - return scm_from_utf8_symbol("black"); - default: - return scm_from_utf8_symbol("empty"); - } +SCM scm_move_from_c_move(struct move move) { + return scm_cons(scm_from_int(move.row), scm_from_int(move.col)); } -static SCM scm_is_valid_move(SCM scm_board, SCM scm_player, SCM scm_move) { - enum player_color **board = calloc(8, sizeof(enum player_color *)); - for (int i = 0; i < 8; i++) { - board[i] = calloc(8, sizeof(enum player_color)); - for (int j = 0; j < 8; j++) { - if (scm_is_true( - scm_eq_p(scm_list_ref(scm_list_ref(scm_board, scm_from_int(i)), - scm_from_int(j)), - scm_from_utf8_symbol("white")))) { - board[i][j] = WHITE; - } else if (scm_is_true(scm_eq_p( - scm_list_ref(scm_list_ref(scm_board, scm_from_int(i)), - scm_from_int(j)), - scm_from_utf8_symbol("black")))) { - board[i][j] = BLACK; - } else { - board[i][j] = EMPTY; - } - } - } +SCM scm_is_valid_move(SCM scm_move, SCM scm_board, SCM scm_player) { + enum player_color **board = + SCM_UNBNDP(scm_board) ? get_board() : scm_board_to_c_board(scm_board); - enum player_color current_player = BLACK; + enum player_color current_player = SCM_UNBNDP(scm_player) + ? get_current_player() + : scm_player_to_c_player(scm_player); struct move move = scm_move_to_c_move(scm_move); - return scm_from_bool(is_valid_move(board, current_player, move)); -} + SCM is_valid = scm_from_bool(is_valid_move(board, current_player, move)); -// Return the list of valid moves on a board for a player -static SCM scm_valid_moves(SCM scm_board, SCM player) { - enum player_color **board = calloc(8, sizeof(enum player_color *)); - for (int i = 0; i < 8; i++) { - board[i] = calloc(8, sizeof(enum player_color)); + if (!SCM_UNBNDP(scm_board)) { + free_board(board); } - enum player_color current_player = EMPTY; + return is_valid; +} - if (scm_is_true(scm_eq_p(player, scm_from_utf8_symbol("white")))) { - current_player = WHITE; - } else if (scm_is_true(scm_eq_p(player, scm_from_utf8_symbol("black")))) { - current_player = BLACK; - } - current_player = BLACK; +// Return the list of valid moves on a board for a player +SCM scm_valid_moves(SCM scm_board, SCM player) { + enum player_color **board = + SCM_UNBNDP(scm_board) ? get_board() : scm_board_to_c_board(scm_board); - SCM result = scm_make_list(scm_from_int(0), NULL); + enum player_color current_player = SCM_UNBNDP(player) + ? get_current_player() + : scm_player_to_c_player(player); + + SCM result = SCM_EOL; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { struct move move = {i, j}; if (is_valid_move(board, current_player, move)) { - result = scm_cons(scm_cons(scm_from_int(i), scm_from_int(j)), result); + result = scm_cons(scm_move_from_c_move(move), result); } } } - free_board(board); + if (!SCM_UNBNDP(scm_board)) { + free_board(board); + } + return result; } @@ -283,9 +267,11 @@ struct move get_scm_move(char *strategy_path) { // Initialize primitives scm_c_define_gsubr("get-board", 0, 0, 0, scm_get_board); - scm_c_define_gsubr("current-player", 0, 0, 0, scm_current_player); - scm_c_define_gsubr("valid-move?", 3, 0, 0, scm_is_valid_move); - scm_c_define_gsubr("valid-moves", 2, 0, 0, scm_valid_moves); + scm_c_define_gsubr("current-player", 0, 0, 0, scm_get_current_player); + scm_c_define_gsubr("valid-move?", 1, 2, 0, scm_is_valid_move); + scm_c_define_gsubr("valid-moves", 0, 2, 0, scm_valid_moves); + scm_c_define_gsubr("apply-move", 1, 2, 0, scm_apply_move); + scm_c_define_gsubr("get-winner", 2, 0, 0, scm_get_winner); // Read the move from scheme SCM scm_move = scm_c_primitive_load(strategy_path); diff --git a/src/valid_moves.c b/src/valid_moves.c index 5913fb8..11f5926 100644 --- a/src/valid_moves.c +++ b/src/valid_moves.c @@ -21,6 +21,7 @@ #include <stdbool.h> #include "othello.h" +#include "othello_board.h" #include "othello_move.h" bool is_valid_move(enum player_color **board, diff --git a/strategies/first-valid-move.scm b/strategies/first-valid-move.scm index e9a9bfb..e682873 100644 --- a/strategies/first-valid-move.scm +++ b/strategies/first-valid-move.scm @@ -1,25 +1,3 @@ -(use-modules (ice-9 pretty-print) - (srfi srfi-1)) - -(define (make-point row col) - (cons row col)) - -(define (point->row point) - (car point)) - -(define (point->col point) - (cdr point)) - -(define (valid-move-current-board? move) - (valid-move? (get-board) (current-player) move)) - -(define all-spots - (concatenate - (map (lambda (row) (map (lambda (col) (make-point row col)) (iota 8))) - (iota 8)))) - -(define valid-next-moves - (filter valid-move-current-board? all-spots)) - +(use-modules (srfi srfi-1)) ; Return the first valid move we find. -(first valid-next-moves) +(first (valid-moves)) |