From f1a4b5757f0d20739270b22726ebf8442b13cb50 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Fri, 21 Jan 2022 17:20:17 -0500 Subject: 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. --- src/board.c | 86 +++++++++++++++++++++++++++++++++-- src/game_loop.c | 25 ++++++++-- src/move.c | 134 ++++++++++++++++++++++++------------------------------ src/valid_moves.c | 1 + 4 files changed, 164 insertions(+), 82 deletions(-) (limited to 'src') 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 #include #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 #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); } diff --git a/src/move.c b/src/move.c index 8828ef0..d2a05dd 100644 --- a/src/move.c +++ b/src/move.c @@ -25,6 +25,7 @@ #include #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 #include "othello.h" +#include "othello_board.h" #include "othello_move.h" bool is_valid_move(enum player_color **board, -- cgit v1.2.3