From babc796fdbc3f571c18c841a33d0af37ea768dca Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Sun, 25 Jul 2021 23:45:33 -0400 Subject: Initial commit --- .ccls | 4 ++ .gitignore | 1 + Makefile | 16 +++++ cmd/main.c | 31 +++++++++ include/othello.h | 19 ++++++ src/game_loop.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++ src/initialize_board.c | 17 +++++ src/print_board.c | 16 +++++ src/valid_moves.c | 105 +++++++++++++++++++++++++++++ 9 files changed, 384 insertions(+) create mode 100644 .ccls create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 cmd/main.c create mode 100644 include/othello.h create mode 100644 src/game_loop.c create mode 100644 src/initialize_board.c create mode 100644 src/print_board.c create mode 100644 src/valid_moves.c 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 +#include +#include +#include + +#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 + +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 +#include +#include +#include + +#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 + +#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 + +#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; +} -- cgit v1.2.3