172 lines
5.6 KiB
C++
172 lines
5.6 KiB
C++
#include "strategic_player.h"
|
|
#include "common.h"
|
|
|
|
extern "C" IPlayer* PlayerFactory()
|
|
{
|
|
return new StrategicPlayer();
|
|
}
|
|
|
|
// Helper functions
|
|
inline int normalize(int x) {
|
|
return (x + 1) >> 1;
|
|
}
|
|
|
|
inline char getPoint(Board &board, const int row, const int col) {
|
|
return board(row, col);
|
|
}
|
|
|
|
inline bool isLineValid(Board &board, const int row, const int col) {
|
|
return (row > -1 && row < board.GetRows() && col > -1 && col < board.GetCols()) &&
|
|
((row & 1) != (col & 1)) && (getPoint(board, row, col) == ' ');
|
|
}
|
|
|
|
inline void set(Board &board, int r, int c, char ch) {
|
|
board(r, c) = ch;
|
|
}
|
|
//
|
|
|
|
|
|
string StrategicPlayer::PlayerInfo() {
|
|
return "Sandipsinh Rathod (sdr5549@psu.edu), Sapan Shah (scs6041@psu.edu)";
|
|
}
|
|
|
|
void StrategicPlayer::Init(int board_rows, int board_cols, char box_type, char line_type) {
|
|
this->name = box_type;
|
|
this->box_name = line_type;
|
|
this->board.AllocateBoard(board_rows, board_cols);
|
|
}
|
|
|
|
|
|
StrategicPlayer::~StrategicPlayer() {
|
|
board.FreeBoard();
|
|
}
|
|
|
|
void StrategicPlayer::Close() {
|
|
board.FreeBoard();
|
|
}
|
|
|
|
/// TODO: check if we needs checks :)
|
|
void StrategicPlayer::EventAddLine(char bar, const Loc &loc) {
|
|
set(board, loc.row, loc.col, bar);
|
|
}
|
|
|
|
void StrategicPlayer::EventAddBox(char box, const Loc &loc) {
|
|
set(board, loc.row, loc.col, box);
|
|
}
|
|
|
|
inline void selectLine(Board &board, int &row, int &col, int rows, int cols, char name) {
|
|
int max_row = 2 * rows - 2;
|
|
int max_col = 2 * cols - 2;
|
|
|
|
// Step 1: Try to complete a box
|
|
#pragma omp parallel for collapse(2)
|
|
for (int r = 1; r < max_row; r += 2) {
|
|
// Iterate over box centers (odd rows)
|
|
for (int c = 1; c < max_col; c += 2) {
|
|
// Iterate over box centers (odd cols)
|
|
// Check adjacent lines for an opportunity to complete a box
|
|
if (isLineValid(board, r - 1, c) && // Top line
|
|
getPoint(board, r + 1, c) != ' ' && // Bottom line
|
|
getPoint(board, r, c - 1) != ' ' && // Left line
|
|
getPoint(board, r, c + 1) != ' ') {
|
|
// Right line
|
|
row = r - 1;
|
|
col = c;
|
|
return;
|
|
}
|
|
if (isLineValid(board, r + 1, c) && // Bottom line
|
|
getPoint(board, r - 1, c) != ' ' && // Top line
|
|
getPoint(board, r, c - 1) != ' ' && // Left line
|
|
getPoint(board, r, c + 1) != ' ') {
|
|
// Right line
|
|
row = r + 1;
|
|
col = c;
|
|
return;
|
|
}
|
|
if (isLineValid(board, r, c - 1) && // Left line
|
|
getPoint(board, r - 1, c) != ' ' && // Top line
|
|
getPoint(board, r + 1, c) != ' ' && // Bottom line
|
|
getPoint(board, r, c + 1) != ' ') {
|
|
// Right line
|
|
row = r;
|
|
col = c - 1;
|
|
return;
|
|
}
|
|
if (isLineValid(board, r, c + 1) && // Right line
|
|
getPoint(board, r - 1, c) != ' ' && // Top line
|
|
getPoint(board, r + 1, c) != ' ' && // Bottom line
|
|
getPoint(board, r, c - 1) != ' ') {
|
|
// Left line
|
|
row = r;
|
|
col = c + 1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Step 2: Avoid moves that leave a box with one line remaining
|
|
#pragma omp parallel for collapse(2)
|
|
for (int r = 0; r < 2 * rows - 1; ++r) {
|
|
// Iterate over all valid rows
|
|
for (int c = 0; c < 2 * cols - 1; ++c) {
|
|
// Iterate over all valid cols
|
|
if (isLineValid(board, r, c)) {
|
|
// Simulate placing the line
|
|
set(board, r, c, name);
|
|
|
|
// Check if this move leaves a box with only one line remaining
|
|
bool createsOpportunity = false;
|
|
for (int nr = 1; nr < max_row; nr += 2) {
|
|
// Iterate over box centers
|
|
for (int nc = 1; nc < max_col; nc += 2) {
|
|
if (getPoint(board, nr, nc) == ' ') {
|
|
int adjacentLines = 0;
|
|
if (nr > 0 && getPoint(board, nr - 1, nc) != ' ') adjacentLines++; // Top line
|
|
if (nr < max_row && getPoint(board, nr + 1, nc) != ' ') adjacentLines++; // Bottom line
|
|
if (nc > 0 && getPoint(board, nr, nc - 1) != ' ') adjacentLines++; // Left line
|
|
if (nc < max_col && getPoint(board, nr, nc + 1) != ' ') adjacentLines++; // Right line
|
|
|
|
if (adjacentLines == 3) {
|
|
// Opponent can complete this box
|
|
createsOpportunity = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (createsOpportunity) break;
|
|
}
|
|
|
|
set(board, r, c, ' '); // Undo the simulated move
|
|
|
|
if (!createsOpportunity) {
|
|
row = r;
|
|
col = c;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Step 3: Fallback to the first valid move
|
|
#pragma omp parallel for collapse(2)
|
|
for (int r = 0; r < 2 * rows - 1; ++r) {
|
|
for (int c = 0; c < 2 * cols - 1; ++c) {
|
|
if (isLineValid(board, r, c)) {
|
|
row = r;
|
|
col = c;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Loc StrategicPlayer::SelectLineLocation() {
|
|
int rows = normalize(board.GetRows());
|
|
int cols = normalize(board.GetCols());
|
|
|
|
int row, col;
|
|
selectLine(board, row, col, rows, cols, name);
|
|
|
|
return Loc(row, col);
|
|
}
|