From 6a35a3cffd976f6fa9d96759e2f2bb0f784b92ac Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Thu, 30 Dec 2021 00:02:22 -0500 Subject: Pulled lots of things out of game loop --- include/othello.h | 13 +--- include/othello_move.h | 37 +++++++++++ manifest.scm | 2 + src/board.c | 118 ++++++++++++++++++++++++++++++++++ src/game_loop.c | 170 ++++--------------------------------------------- src/initialize_board.c | 43 ------------- src/move.c | 95 +++++++++++++++++++++++++++ 7 files changed, 269 insertions(+), 209 deletions(-) create mode 100644 include/othello_move.h create mode 100644 src/board.c delete mode 100644 src/initialize_board.c create mode 100644 src/move.c diff --git a/include/othello.h b/include/othello.h index ec1449a..3c3027c 100644 --- a/include/othello.h +++ b/include/othello.h @@ -38,16 +38,7 @@ 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(enum player_color ***board); - -// True if move is valid for current_player. -// Otherwise false. -bool is_valid_move(const enum player_color current_player, - const struct move move); - -// True if current_player has any valid moves. -// Otherwise false. -bool has_valid_moves(const enum player_color current_player); +void initialize_board(void); // Plays a game to completion, starting with board. enum player_color game_loop(void); @@ -60,3 +51,5 @@ enum player_color get_current_player(void); // Returns the current board. const enum player_color **get_board(void); + +enum player_color get_winner(void); diff --git a/include/othello_move.h b/include/othello_move.h new file mode 100644 index 0000000..0bab641 --- /dev/null +++ b/include/othello_move.h @@ -0,0 +1,37 @@ +/* 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 . + */ + +#define _GNU_SOURCE + +#pragma once + +#include "othello.h" + +struct move prompt_get_move(enum player_color current_player); + +// True if move is valid for current_player. +// Otherwise false. +bool is_valid_move(const enum player_color current_player, + const struct move move); + +// True if current_player has any valid moves. +// Otherwise false. +bool has_valid_moves(const enum player_color current_player); + +/* Returns true if the move was valid */ +bool apply_move(enum player_color current_player, struct move move); diff --git a/manifest.scm b/manifest.scm index abafda9..4867e3f 100644 --- a/manifest.scm +++ b/manifest.scm @@ -15,6 +15,7 @@ (specifications->manifest '("readline" "guile" + "guile:debug" "gcc-toolchain" "git" "glibc-locales" @@ -24,3 +25,4 @@ "grep" "procps" "make")) + diff --git a/src/board.c b/src/board.c new file mode 100644 index 0000000..12b5384 --- /dev/null +++ b/src/board.c @@ -0,0 +1,118 @@ +/* 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 . + */ + +#define _GNU_SOURCE + +#include + +#include "othello.h" +#include "othello_move.h" + +static enum player_color **board; + +const enum player_color **get_board(void) { + return (const enum player_color **)board; +} + +void initialize_board() { + board = malloc(8 * sizeof(enum player_color *)); + for (int i = 0; i < 8; i++) { + board[i] = malloc(8 * sizeof(enum player_color)); + } + + // Set all the positions to empty + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) { + board[i][j] = EMPTY; + } + } + + // Fill the starting positions + board[3][3] = WHITE; + board[3][4] = BLACK; + board[4][3] = BLACK; + board[4][4] = WHITE; +} + +/* Returns true if the move was valid */ +bool apply_move(enum player_color current_player, struct move move) { + + // Flip in up direction + bool flipped_up = false; + for (int i = move.row - 1; + i > 0 && board[i][move.col] != EMPTY && !flipped_up; i--) { + if (board[i][move.col] == current_player) { + for (int j = move.row; j > i; j--) { + board[j][move.col] = current_player; + } + flipped_up = true; + } + } + + // Flip in down direction + bool flipped_down = false; + for (int i = move.row + 1; + i < 8 && board[i][move.col] != EMPTY && !flipped_down; i++) { + if (board[i][move.col] == current_player) { + for (int j = move.row; j < i; j++) { + board[j][move.col] = current_player; + } + flipped_down = true; + } + } + + // Flip in left direction + bool flipped_left = false; + for (int i = move.col - 1; + i > 0 && board[move.row][i] != EMPTY && !flipped_left; i--) { + if (board[move.row][i] == current_player) { + for (int j = move.col; j > i; j--) { + board[move.row][j] = current_player; + } + flipped_left = true; + } + } + + // Flip in right direction + bool flipped_right = false; + for (int i = move.col + 1; + i < 8 && board[move.row][i] != EMPTY && !flipped_right; i++) { + if (board[move.row][i] == current_player) { + for (int j = move.col; j < i; j++) { + board[move.row][j] = current_player; + } + flipped_right = true; + } + } + + return flipped_up || flipped_down || flipped_left || flipped_right; +} + +enum player_color get_winner() { + int white_score = 0, black_score = 0; + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + white_score += (int)(board[row][col] == WHITE); + black_score += (int)(board[row][col] == BLACK); + } + } + + return white_score > black_score ? WHITE + : black_score > white_score ? BLACK + : EMPTY; +} diff --git a/src/game_loop.c b/src/game_loop.c index c11f473..bdb37f5 100644 --- a/src/game_loop.c +++ b/src/game_loop.c @@ -28,6 +28,7 @@ #include #include "othello.h" +#include "othello_move.h" #define STREQ(a, b) (strcmp(a, b) == 0) @@ -35,169 +36,26 @@ static enum player_color current_player; enum player_color get_current_player(void) { return current_player; } -static enum player_color **board; - -const enum player_color **get_board(void) { - return (const enum player_color **)board; -} - struct move (*player_one_get_move)(); struct move (*player_two_get_move)(); -static enum player_color other_player(enum player_color current_player) { - switch (current_player) { - case WHITE: - return BLACK; - case BLACK: - return WHITE; - default: - return EMPTY; - } -} - -static char *prompt_player(enum player_color current_player) { - switch (current_player) { - case WHITE: - return "Player 1 [h for help] > "; - case BLACK: - return "Player 2 [h for help] > "; - default: - perror("There is no current player"); - exit(EXIT_FAILURE); - } -} - -static void print_help() { - puts("To enter a move, enter the two row and column seperated by spaces.\n" - "For example `0 0` will select the top left corner.\n\n" - "p - Print the board again\n" - "g - Drop into a guile repl\n" - "\n" - "In the guile repl you can define an AI to use for the current" - "player.\n" - "After you define an AI for that player, it will be used for the rest " - "of the game instead of prompting for input for that user.\n"); -} - -static void inner_main(void *closure, int argc, char **argv) { - (void)closure; - scm_shell(argc, argv); -} - -static struct move prompt_get_move(enum player_color current_player) { - // Initialize move to an invalid move. - struct move move = {-1, -1}; - - char *prompt = prompt_player(current_player); - do { - char *input = readline(prompt); - - if (input != NULL) { - add_history(input); - if (STREQ(input, "h")) { - print_help(); - } else if (STREQ(input, "p")) { - print_board(); - } else if (STREQ(input, "g")) { - int argc = 1; - char **argv = calloc(1, sizeof(char *)); - argv[0] = "othello"; - /*scm_with_guile(void *(*func)(void *), void *data)*/ - scm_boot_guile(argc, argv, inner_main, NULL); - free(argv); - } else { - sscanf(input, "%d %d", &move.row, &move.col); - } - } else { - // If input was NULL, we have reached an EOF and would like to exit. - free(input); - exit(0); - } - - free(input); - } while (!is_valid_move(current_player, move)); - - return move; -} - -/* Returns true if the move was valid */ -static bool apply_move(enum player_color current_player, struct move move) { - - // Flip in up direction - bool flipped_up = false; - for (int i = move.row - 1; - i > 0 && board[i][move.col] != EMPTY && !flipped_up; i--) { - if (board[i][move.col] == current_player) { - for (int j = move.row - 1; j > i; j--) { - board[j][move.col] = current_player; - } - flipped_up = true; - } - } - - // Flip in down direction - bool flipped_down = false; - for (int i = move.row + 1; - i < 8 && board[i][move.col] == EMPTY && !flipped_down; i++) { - if (board[i][move.col] == current_player) { - for (int j = move.row + 1; j < i; j++) { - board[j][move.col] = current_player; - } - flipped_down = true; - } - } - - // Flip in left direction - bool flipped_left = false; - for (int i = move.col - 1; - i > 0 && board[move.row][i] == EMPTY && !flipped_left; i--) { - if (board[move.row][i] == current_player) { - for (int j = move.col - 1; j > i; j--) { - board[move.row][j] = current_player; - } - flipped_left = true; - } - } - - // Flip in right direction - bool flipped_right = false; - for (int i = move.col + 1; - i < 8 && board[move.row][i] == EMPTY && !flipped_right; i++) { - if (board[move.row][i] == current_player) { - for (int j = move.col + 1; j < i; j++) { - board[move.row][j] = current_player; - } - flipped_right = true; - } - } - - return flipped_up || flipped_down || flipped_left || flipped_right; -} - - enum player_color game_loop() { - initialize_board(&board); - using_history(); + initialize_board(); + using_history(); - current_player = WHITE; + current_player = WHITE; +#define other_player (current_player == WHITE ? BLACK : WHITE) - while (has_valid_moves(current_player)) { - struct move move = prompt_get_move(current_player); - apply_move(current_player, move); - current_player = other_player(current_player); - } - - int white_score = 0, black_score = 0; - for (int row = 0; row < 8; row++) { - for (int col = 0; col < 8; col++) { - white_score += (int)(board[row][col] == WHITE); - black_score += (int)(board[row][col] == BLACK); + while (has_valid_moves(current_player)) { + struct move move = prompt_get_move(current_player); + if (apply_move(current_player, move)) { + current_player = other_player; + } } - } - rl_clear_history(); +#undef other_player + + rl_clear_history(); - return white_score > black_score ? WHITE - : black_score > white_score ? BLACK - : EMPTY; + return get_winner(); } diff --git a/src/initialize_board.c b/src/initialize_board.c deleted file mode 100644 index 18b1468..0000000 --- a/src/initialize_board.c +++ /dev/null @@ -1,43 +0,0 @@ -/* 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 . - */ - -#define _GNU_SOURCE - -#include - -#include "othello.h" - -void initialize_board(enum player_color ***board) { - *board = malloc(8 * sizeof(enum player_color *)); - for (int i = 0; i < 8; i++) { - (*board)[i] = malloc(8 * sizeof(enum player_color)); - } - - // Set all the positions to empty - for (int i = 0; i < 8; i++) { - for (int j = 0; j < 8; j++) { - (*board)[i][j] = EMPTY; - } - } - - // Fill the starting positions - (*board)[3][3] = WHITE; - (*board)[3][4] = BLACK; - (*board)[4][3] = BLACK; - (*board)[4][4] = WHITE; -} diff --git a/src/move.c b/src/move.c new file mode 100644 index 0000000..a22cb59 --- /dev/null +++ b/src/move.c @@ -0,0 +1,95 @@ +/* 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 . + */ + +#define _GNU_SOURCE + +#include + +#include +#include +#include + +#include "othello.h" +#include "othello_move.h" + +#define STREQ(a, b) (strcmp(a, b) == 0) + +static void print_help() { + puts("To enter a move, enter the two row and column seperated by spaces.\n" + "For example `0 0` will select the top left corner.\n\n" + "p - Print the board again\n" + "g - Drop into a guile repl\n" + "\n" + "In the guile repl you can define an AI to use for the current" + "player.\n" + "After you define an AI for that player, it will be used for the rest " + "of the game instead of prompting for input for that user.\n"); +} + +static char *prompt_player(enum player_color current_player) { + switch (current_player) { + case WHITE: + return "Player 1 [h for help] > "; + case BLACK: + return "Player 2 [h for help] > "; + default: + perror("There is no current player"); + exit(EXIT_FAILURE); + } +} + +static void inner_main(void *closure, int argc, char **argv) { + (void)closure; + scm_shell(argc, argv); +} + +struct move prompt_get_move(enum player_color current_player) { + // Initialize move to an invalid move. + struct move move = {-1, -1}; + + char *prompt = prompt_player(current_player); + do { + char *input = readline(prompt); + + if (input != NULL) { + add_history(input); + if (STREQ(input, "h")) { + print_help(); + } else if (STREQ(input, "p")) { + print_board(); + } else if (STREQ(input, "g")) { + int argc = 1; + char **argv = calloc(1, sizeof(char *)); + argv[0] = "othello"; + /*scm_with_guile(void *(*func)(void *), void *data)*/ + scm_boot_guile(argc, argv, inner_main, NULL); + free(argv); + } else { + sscanf(input, "%d %d", &move.row, &move.col); + } + } else { + // If input was NULL, we have reached an EOF and would like to exit. + free(input); + exit(0); + } + + free(input); + } while (!is_valid_move(current_player, move)); + + return move; +} -- cgit v1.2.3