122 lines
3.8 KiB
C++
122 lines
3.8 KiB
C++
#include "custom_player.h"
|
|
#include <iostream>
|
|
#include <cstdlib> // for rand
|
|
#include <ctime> // 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];
|
|
} |