summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobby Zambito <contact@robbyzambito.me>2021-12-30 00:02:22 -0500
committerRobby Zambito <contact@robbyzambito.me>2021-12-30 00:02:22 -0500
commit6a35a3cffd976f6fa9d96759e2f2bb0f784b92ac (patch)
tree8c1f87ff19ab3cdb35a3450942846e1b1c736aa6
parentb74a45d66405d59ea3f2891a10ee2c62f12134d9 (diff)
Pulled lots of things out of game loop
-rw-r--r--include/othello.h13
-rw-r--r--include/othello_move.h (renamed from src/initialize_board.c)30
-rw-r--r--manifest.scm2
-rw-r--r--src/board.c118
-rw-r--r--src/game_loop.c170
-rw-r--r--src/move.c95
6 files changed, 244 insertions, 184 deletions
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/src/initialize_board.c b/include/othello_move.h
index 18b1468..0bab641 100644
--- a/src/initialize_board.c
+++ b/include/othello_move.h
@@ -18,26 +18,20 @@
#define _GNU_SOURCE
-#include <stdlib.h>
+#pragma once
#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));
- }
+struct move prompt_get_move(enum player_color current_player);
- // Set all the positions to empty
- for (int i = 0; i < 8; i++) {
- for (int j = 0; j < 8; j++) {
- (*board)[i][j] = EMPTY;
- }
- }
+// True if move is valid for current_player.
+// Otherwise false.
+bool is_valid_move(const enum player_color current_player,
+ const struct move move);
- // Fill the starting positions
- (*board)[3][3] = WHITE;
- (*board)[3][4] = BLACK;
- (*board)[4][3] = BLACK;
- (*board)[4][4] = WHITE;
-}
+// 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 <https://www.gnu.org/licenses/>.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+
+#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 <string.h>
#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/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 <https://www.gnu.org/licenses/>.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+
+#include <libguile.h>
+#include <readline/history.h>
+#include <readline/readline.h>
+
+#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;
+}