#include "custom_player.h" #include #include // for rand #include // for time using namespace std; // Factory function for dynamic library loading extern "C" IPlayer* PlayerFactory() { return new CustomPlayer(); } // Constructor CustomPlayer::CustomPlayer() { srand(time(0)); } // Destructor CustomPlayer::~CustomPlayer() {} // Initializes the player with the board size and player symbols void CustomPlayer::Init(int dots_in_rows, int dots_in_cols, char _player_box, char _player_line) { board.AllocateBoard(dots_in_rows, dots_in_cols); player_box = _player_box; player_line = _player_line; empty_lines = new Loc[board.GetRows() * board.GetCols()]; } // Cleans up dynamically allocated memory void CustomPlayer::Close() { board.FreeBoard(); delete[] empty_lines; } // Returns the player info std::string CustomPlayer::PlayerInfo() { return "Custom Strategic Player"; } // Updates the board when a line is added void CustomPlayer::EventAddLine(char bar, const Loc& loc) { board(loc) = bar; } // Updates the board when a box is completed void CustomPlayer::EventAddBox(char box, const Loc& loc) { board(loc) = box; } // Updates the list of available line locations void CustomPlayer::ListEmptyLines() { empty_lines_count = 0; for (int r = 0; r < board.GetRows(); r++) { for (int c = 0; c < board.GetCols(); c++) { if (board(r, c) == ' ' && Loc(r, c).IsLineLocation()) { empty_lines[empty_lines_count++] = Loc(r, c); } } } } // Helper function to check if a line completes a box bool CustomPlayer::DoesLineCompleteBox(const Loc& loc) { if (loc.IsLineHorizontalLocation()) { if (loc.row > 0 && board(loc.row - 1, loc.col) != ' ' && board(loc.row - 1, loc.col - 1) != ' ' && board(loc.row - 1, loc.col + 1) != ' ') return true; if (loc.row < board.GetRows() - 1 && board(loc.row + 1, loc.col) != ' ' && board(loc.row + 1, loc.col - 1) != ' ' && board(loc.row + 1, loc.col + 1) != ' ') return true; } else if (loc.IsLineVerticalLocation()) { if (loc.col > 0 && board(loc.row, loc.col - 1) != ' ' && board(loc.row - 1, loc.col - 1) != ' ' && board(loc.row + 1, loc.col - 1) != ' ') return true; if (loc.col < board.GetCols() - 1 && board(loc.row, loc.col + 1) != ' ' && board(loc.row - 1, loc.col + 1) != ' ' && board(loc.row + 1, loc.col + 1) != ' ') return true; } return false; } // Helper function to avoid giving the opponent a chance to complete a box bool CustomPlayer::WouldGiveOpponentBox(const Loc& loc) { // Temporarily add the line to the board board(loc) = player_line; // Check if the opponent can complete a box in the next move for (int r = 0; r < board.GetRows(); r++) { for (int c = 0; c < board.GetCols(); c++) { if (board(r, c) == ' ' && Loc(r, c).IsLineLocation() && DoesLineCompleteBox(Loc(r, c))) { board(loc) = ' '; // Revert the move return true; } } } // Revert the move board(loc) = ' '; return false; } // Determines the next move Loc CustomPlayer::SelectLineLocation() { ListEmptyLines(); // Strategy: Prioritize moves that complete a box for (int i = 0; i < empty_lines_count; i++) { if (DoesLineCompleteBox(empty_lines[i])) { return empty_lines[i]; } } // Avoid moves that give the opponent a chance to complete a box for (int i = 0; i < empty_lines_count; i++) { if (!WouldGiveOpponentBox(empty_lines[i])) { return empty_lines[i]; } } // Otherwise, pick a random move return empty_lines[rand() % empty_lines_count]; }