summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobby Zambito <contact@robbyzambito.me>2021-07-25 23:45:33 -0400
committerRobby Zambito <contact@robbyzambito.me>2021-07-25 23:45:33 -0400
commitbabc796fdbc3f571c18c841a33d0af37ea768dca (patch)
treeb46dec93772e6bc0027c4dd43a82825ba8fd66cc
Initial commit
-rw-r--r--.ccls4
-rw-r--r--.gitignore1
-rw-r--r--Makefile16
-rw-r--r--cmd/main.c31
-rw-r--r--include/othello.h19
-rw-r--r--src/game_loop.c175
-rw-r--r--src/initialize_board.c17
-rw-r--r--src/print_board.c16
-rw-r--r--src/valid_moves.c105
9 files changed, 384 insertions, 0 deletions
diff --git a/.ccls b/.ccls
new file mode 100644
index 0000000..a0e6d19
--- /dev/null
+++ b/.ccls
@@ -0,0 +1,4 @@
+clang
+%c -std=c11
+-Iinclude
+-I/usr/include/guile/2.2/
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e660fd9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+bin/
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..88e9cfa
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+LIBRARIES = -lm `pkg-config guile-2.2 --cflags` -Iinclude
+SOURCES = ./src/* ./cmd/main.c
+OUTPUT_DIR = bin
+OUTPUT = -o $(OUTPUT_DIR)/PROG
+
+build: output_dir
+ gcc -Wall $(LIBRARIES) $(SOURCES) $(OUTPUT:PROG=othello)
+
+debug: output_dir
+ gcc -Wall -g $(LIBRARIES) $(SOURCES) $(OUTPUT:PROG=othello)
+
+output_dir:
+ mkdir -p $(OUTPUT_DIR)
+
+clean:
+ rm -rf $(OUTPUT_DIR) **.h.gch
diff --git a/cmd/main.c b/cmd/main.c
new file mode 100644
index 0000000..164b32d
--- /dev/null
+++ b/cmd/main.c
@@ -0,0 +1,31 @@
+#include <libguile.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "othello.h"
+
+int main(int argc, char **argv) {
+ player_color board[8][8];
+ initialize_board(board);
+
+ /*print_board(board);*/
+
+ char winner = game_loop(board);
+
+ switch (winner) {
+ case WHITE:
+ puts("Congratulations for winning player 1!");
+ break;
+ case BLACK:
+ puts("Congratulations for winning player 2!");
+ break;
+ default:
+ puts("The game ended with no winner.");
+ }
+
+ puts("Here was the final board:");
+ print_board(board);
+
+ return EXIT_SUCCESS;
+}
diff --git a/include/othello.h b/include/othello.h
new file mode 100644
index 0000000..aed7302
--- /dev/null
+++ b/include/othello.h
@@ -0,0 +1,19 @@
+#include <stdbool.h>
+
+typedef char player_color;
+#define EMPTY (player_color)'*'
+#define WHITE (player_color)'w'
+#define BLACK (player_color)'b'
+
+struct move {
+ int row;
+ int col;
+};
+
+void initialize_board(player_color board[8][8]);
+bool is_valid_move(const player_color board[8][8],
+ const player_color current_player, const struct move move);
+bool has_valid_moves(const player_color board[8][8],
+ const player_color current_player);
+char game_loop(player_color board[8][8]);
+void print_board(player_color board[8][8]);
diff --git a/src/game_loop.c b/src/game_loop.c
new file mode 100644
index 0000000..9cbd5e2
--- /dev/null
+++ b/src/game_loop.c
@@ -0,0 +1,175 @@
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "othello.h"
+
+#define STREQ(a, b) (strcmp(a, b) == 0)
+
+static player_color other_player(player_color current_player) {
+ switch (current_player) {
+ case WHITE:
+ return BLACK;
+ case BLACK:
+ return WHITE;
+ default:
+ return EMPTY;
+ }
+}
+
+static int player_number_from_color(player_color player) {
+ switch (player) {
+ case WHITE:
+ return 1;
+ case BLACK:
+ return 2;
+ default:
+ return -1;
+ }
+}
+
+static void prompt_player(player_color current_player) {
+ printf("Player %d [h for help] > ", player_number_from_color(current_player));
+}
+
+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"
+ "To print the board again, enter `p`\n\n"
+ "To drop into a guile repl enter `g`.\n\n"
+ "In the guile repl you can define an AI to use for the current player.\n\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.");
+}
+
+static struct move get_move(player_color board[8][8],
+ player_color current_player) {
+ // Initialize move to an invalid move.
+ struct move move = {-1, -1};
+
+ do {
+ prompt_player(current_player);
+ char *input = NULL;
+ size_t input_length = 0;
+
+ errno = 0;
+ while (getline(&input, &input_length, stdin) <= 0) {
+ if (errno != 0) {
+ perror("Error getting input");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (STREQ(input, "h\n")) {
+ print_help();
+ } else if (STREQ(input, "p\n")) {
+ print_board(board);
+ } else if (STREQ(input, "g\n")) {
+ puts("NOT IMPLEMENTED YET!");
+ } else {
+ sscanf(input, "%d %d", &move.row, &move.col);
+ }
+
+ free(input);
+ } while (!is_valid_move(board, current_player, move));
+
+ return move;
+}
+
+static void apply_move(player_color board[8][8], player_color current_player,
+ struct move move) {
+ if (!is_valid_move(board, current_player, move)) {
+
+ // We should have checked for the validity of the move before we got here.
+ fprintf(stderr, "The move %d %d was not valid for player %d\n", move.row,
+ move.col, player_number_from_color(current_player) + 1);
+ exit(EXIT_FAILURE);
+ }
+
+ // Flip in up direction
+ int up_end = move.row;
+ for (int i = move.row - 1; i > 0; i--) {
+ if (board[i][move.col] == EMPTY) {
+ break;
+ } else if (board[i][move.col] == current_player) {
+ /*up_end = i + 1;*/
+ up_end = i;
+ break;
+ }
+ }
+
+ // Flip in down direction
+ int down_end = move.row;
+ for (int i = move.row + 1; i < 8; i++) {
+ if (board[i][move.col] == EMPTY) {
+ break;
+ } else if (board[i][move.col] == current_player) {
+ /*down_end = i - 1;*/
+ down_end = i;
+ break;
+ }
+ }
+
+ // Flip in left direction
+ int left_end = move.col;
+ for (int i = move.col - 1; i > 0; i--) {
+ if (board[move.row][i] == EMPTY) {
+ break;
+ } else if (board[move.row][i] == current_player) {
+ /*left_end = i + 1;*/
+ left_end = i;
+ break;
+ }
+ }
+
+ // Flip in right direction
+ int right_end = move.col;
+ for (int i = move.col + 1; i < 8; i++) {
+ if (board[move.row][i] == EMPTY) {
+ break;
+ } else if (board[move.row][i] == current_player) {
+ /*right_end = i - 1;*/
+ right_end = i;
+ break;
+ }
+ }
+
+ for (int i = move.row; i > up_end; i--) {
+ board[i][move.col] = current_player;
+ }
+ for (int i = move.row; i < down_end; i++) {
+ board[i][move.col] = current_player;
+ }
+ for (int i = move.col; i > left_end; i--) {
+ board[move.row][i] = current_player;
+ }
+ for (int i = move.col; i < right_end; i++) {
+ board[move.row][i] = current_player;
+ }
+}
+
+char game_loop(player_color board[8][8]) {
+ player_color current_player = WHITE;
+
+ while (has_valid_moves(board, current_player)) {
+ struct move move = get_move(board, current_player);
+ apply_move(board, current_player, move);
+ /*board[move.row][move.col] = current_player;*/
+ 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 += board[row][col] == WHITE;
+ black_score += board[row][col] == BLACK;
+ }
+ }
+
+ return white_score > black_score ? WHITE
+ : black_score > white_score ? BLACK
+ : EMPTY;
+}
diff --git a/src/initialize_board.c b/src/initialize_board.c
new file mode 100644
index 0000000..0172bfb
--- /dev/null
+++ b/src/initialize_board.c
@@ -0,0 +1,17 @@
+#include "othello.h"
+
+void initialize_board(player_color board[8][8]) {
+
+ // 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/print_board.c b/src/print_board.c
new file mode 100644
index 0000000..6624341
--- /dev/null
+++ b/src/print_board.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+#include "othello.h"
+
+void print_board(player_color board[8][8]) {
+ puts(" 0 1 2 3 4 5 6 7");
+
+ for (int row = 0; row < 8; row++) {
+ printf("%d ", row);
+
+ for (int col = 0; col < 8; col++) {
+ printf("%c ", board[row][col]);
+ }
+ printf("\n");
+ }
+}
diff --git a/src/valid_moves.c b/src/valid_moves.c
new file mode 100644
index 0000000..bcbbd46
--- /dev/null
+++ b/src/valid_moves.c
@@ -0,0 +1,105 @@
+#include <stdbool.h>
+
+#include "othello.h"
+
+bool is_valid_move(const player_color board[8][8],
+ const player_color current_player, const struct move move) {
+ // The move must be a positive position
+ if (move.row < 0 || move.col < 0) {
+ return false;
+ }
+ // The move must be an empty spot to be valid
+ if (board[move.row][move.col] != EMPTY) {
+ return false;
+ }
+
+ const player_color other_color = current_player == WHITE ? BLACK : WHITE;
+
+ bool is_valid_up = false;
+ bool is_valid_down = false;
+ bool is_valid_left = false;
+ bool is_valid_right = false;
+
+ if (!is_valid_up && !is_valid_down && !is_valid_left && !is_valid_right) {
+ // Check if valid up
+ bool would_flip_oppenent = false;
+ bool has_other_end = false;
+ for (int i = move.row - 1; i > 0 && !has_other_end; i--) {
+ if (board[i][move.col] == current_player) {
+ has_other_end = true;
+ } else if (board[i][move.col] == other_color) {
+ would_flip_oppenent = true;
+ } else {
+ // We have reached an empty tile
+ break;
+ }
+ }
+ is_valid_up = has_other_end && would_flip_oppenent;
+ }
+
+ if (!is_valid_up && !is_valid_down && !is_valid_left && !is_valid_right) {
+ // Check if valid down
+ bool would_flip_oppenent = false;
+ bool has_other_end = false;
+ for (int i = move.row + 1; i < 8 && !has_other_end; i++) {
+ if (board[i][move.col] == current_player) {
+ has_other_end = true;
+ } else if (board[i][move.col] == other_color) {
+ would_flip_oppenent = true;
+ } else {
+ // We have reached an empty tile
+ break;
+ }
+ }
+ is_valid_down = has_other_end && would_flip_oppenent;
+ }
+
+ if (!is_valid_up && !is_valid_down && !is_valid_left && !is_valid_right) {
+ // Check if valid left
+ bool would_flip_oppenent = false;
+ bool has_other_end = false;
+ for (int i = move.col - 1; i > 0 && !has_other_end; i--) {
+ if (board[move.row][i] == current_player) {
+ has_other_end = true;
+ } else if (board[move.row][i] == other_color) {
+ would_flip_oppenent = true;
+ } else {
+ // We have reached an empty tile
+ break;
+ }
+ }
+ is_valid_left = has_other_end && would_flip_oppenent;
+ }
+
+ if (!is_valid_up && !is_valid_down && !is_valid_left && !is_valid_right) {
+ // Check if valid right
+ bool would_flip_oppenent = false;
+ bool has_other_end = false;
+ for (int i = move.col + 1; i < 8 && !has_other_end; i++) {
+ if (board[move.row][i] == current_player) {
+ has_other_end = true;
+ } else if (board[move.row][i] == other_color) {
+ would_flip_oppenent = true;
+ } else {
+ // We have reached an empty tile
+ break;
+ }
+ }
+ is_valid_right = has_other_end && would_flip_oppenent;
+ }
+
+ return is_valid_up || is_valid_down || is_valid_left || is_valid_right;
+}
+
+bool has_valid_moves(const player_color board[8][8],
+ const player_color current_player) {
+ bool result = false;
+ struct move move;
+ for (move.row = 0; move.row < 8 && !result; move.row++) {
+ for (move.col = 0; move.col < 8 && !result; move.col++) {
+ result = is_valid_move(board, current_player, move);
+ }
+ }
+
+ return result;
+}