init
This commit is contained in:
commit
c1c4586abd
12
CMakeLists.txt
Normal file
12
CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
cmake_minimum_required(VERSION 3.29)
|
||||
project(cmpsc330hw5)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
add_executable(cmpsc330hw5
|
||||
main.cxx
|
||||
board.cxx
|
||||
dotsboxesgm.cxx
|
||||
strategic_player.h
|
||||
strategic_player.cxx
|
||||
)
|
74
board.cxx
Normal file
74
board.cxx
Normal file
@ -0,0 +1,74 @@
|
||||
#include <iostream>
|
||||
#include <assert.h>
|
||||
#include "common.h"
|
||||
#include "board.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void Board::AllocateBoard(int dots_in_rows, int dots_in_cols, int& blanklinecount)
|
||||
{
|
||||
assert(board == nullptr);
|
||||
rows = dots_in_rows * 2 - 1;
|
||||
cols = dots_in_cols * 2 - 1;
|
||||
|
||||
board = new char* [rows];
|
||||
for(int r = 0; r < rows; r++)
|
||||
board[r] = new char[cols];
|
||||
|
||||
blanklinecount = 0;
|
||||
for(int r = 0; r < rows; r ++)
|
||||
for(int c = 0; c < cols; c ++)
|
||||
{
|
||||
board[r][c] = ' ';
|
||||
if(Loc(r, c).IsLineLocation())
|
||||
blanklinecount++;
|
||||
}
|
||||
|
||||
for(int r = 0; r < rows; r += 2)
|
||||
for(int c = 0; c < cols; c += 2)
|
||||
board[r][c] = '.';
|
||||
}
|
||||
|
||||
void Board::FreeBoard()
|
||||
{
|
||||
if(board != nullptr)
|
||||
{
|
||||
for(int r = 0; r < rows; r++)
|
||||
delete[] board[r];
|
||||
delete[] board;
|
||||
board = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ostream& operator << (ostream& os, const Board& board)
|
||||
{
|
||||
cout << " ";
|
||||
for(int i=0; i<board.cols; i++)
|
||||
{
|
||||
if(i % 10 == 0) cout << (i/10);
|
||||
else cout << ' ';
|
||||
}
|
||||
cout << endl;
|
||||
cout << " ";
|
||||
for(int i=0; i<board.cols; i++)
|
||||
cout << (i%10);
|
||||
cout << endl;
|
||||
|
||||
for(int r=0; r<board.GetRows(); r++)
|
||||
{
|
||||
if(r%10 == 0) cout << (r/10) << (r%10) << ' ';
|
||||
else cout << ' ' << (r%10) << ' ';
|
||||
|
||||
for(int c=0; c<board.GetCols(); c++)
|
||||
{
|
||||
Loc loc(r,c);
|
||||
char b_rc = board(r,c);
|
||||
if(b_rc == ' ') cout << b_rc;
|
||||
else if(loc.IsLineVerticalLocation () && b_rc != ' ') cout << "|";
|
||||
else if(loc.IsLineHorizontalLocation() && b_rc != ' ') cout << "-";
|
||||
else cout << b_rc;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
return os;
|
||||
}
|
48
board.h
Normal file
48
board.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef __BOARD__
|
||||
#define __BOARD__
|
||||
|
||||
class IBoard
|
||||
{
|
||||
public:
|
||||
virtual int GetRows() const = 0;
|
||||
virtual int GetCols() const = 0;
|
||||
virtual char operator()(int row, int col) const = 0;
|
||||
virtual char operator()(const Loc& loc ) const = 0;
|
||||
virtual char& operator()(int row, int col) = 0;
|
||||
virtual char& operator()(const Loc& loc ) = 0;
|
||||
};
|
||||
|
||||
class Board : public IBoard
|
||||
{
|
||||
char** board;
|
||||
int rows, cols;
|
||||
public:
|
||||
Board()
|
||||
{
|
||||
rows = cols = -1;
|
||||
board = nullptr;
|
||||
}
|
||||
//Board(int dots_in_rows, int dots_in_cols)
|
||||
//{
|
||||
// AllocateBoard(dots_in_rows, dots_in_cols);
|
||||
//}
|
||||
~Board()
|
||||
{
|
||||
FreeBoard();
|
||||
}
|
||||
void AllocateBoard(int dots_in_rows, int dots_in_cols, int& blanklinecount);
|
||||
void FreeBoard();
|
||||
void AllocateBoard(int dots_in_rows, int dots_in_cols) { int blanklinecount; AllocateBoard(dots_in_rows, dots_in_cols, blanklinecount); }
|
||||
|
||||
int GetRows() const { return rows; }
|
||||
int GetCols() const { return cols; }
|
||||
|
||||
char& operator()(int row, int col) { return board[ row][ col]; }
|
||||
char operator()(int row, int col) const { return board[ row][ col]; }
|
||||
char& operator()(const Loc& loc ) { return board[loc.row][loc.col]; }
|
||||
char operator()(const Loc& loc ) const { return board[loc.row][loc.col]; }
|
||||
|
||||
friend ostream& operator << (ostream& os, const Board& board);
|
||||
};
|
||||
|
||||
#endif
|
22
common.h
Normal file
22
common.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef __COMMON__
|
||||
#define __COMMON__
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
class Loc
|
||||
{
|
||||
public:
|
||||
int row, col;
|
||||
Loc() { row = -1; col = -1; }
|
||||
Loc(const Loc& loc) { row = loc.row; col = loc.col; }
|
||||
Loc(int nr, int nc) { row = nr; col = nc; }
|
||||
bool IsDotLocation() const { return ((row % 2 == 0) && (col % 2 == 0)); }
|
||||
bool IsBoxLocation() const { return ((row % 2 == 1) && (col % 2 == 1)); }
|
||||
bool IsLineHorizontalLocation() const { return ((row % 2 == 0) && (col % 2 == 1)); }
|
||||
bool IsLineVerticalLocation() const { return ((row % 2 == 1) && (col % 2 == 0)); }
|
||||
bool IsLineLocation() const { return IsLineHorizontalLocation() || IsLineVerticalLocation(); }
|
||||
};
|
||||
|
||||
#endif
|
306
dotsboxesgm.cxx
Normal file
306
dotsboxesgm.cxx
Normal file
@ -0,0 +1,306 @@
|
||||
#include <iostream>
|
||||
#include <time.h>
|
||||
#include <chrono>
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
#include "common.h"
|
||||
#include "board.h"
|
||||
#include "player.h"
|
||||
|
||||
|
||||
void ClearConsole();
|
||||
void SleepInSec(double sleep_sec);
|
||||
extern void* a_player_libhandle;
|
||||
extern void* b_player_libhandle;
|
||||
string LoadPlayer(IPlayer*& ab_player, string libpath, int board_rows, int board_cols, char box_type, char line_type);
|
||||
void CloseLibs();
|
||||
|
||||
struct PlayerInfo
|
||||
{
|
||||
public:
|
||||
IPlayer* iplayer;
|
||||
int score;
|
||||
char box;
|
||||
char line;
|
||||
double timespent;
|
||||
PlayerInfo* next; // play-list (linked list)
|
||||
PlayerInfo* prev; // play-list (linked list)
|
||||
PlayerInfo(IPlayer* p, char b, char l)
|
||||
{
|
||||
iplayer = p;
|
||||
score = 0;
|
||||
box = b;
|
||||
line = l;
|
||||
timespent = 0;
|
||||
}
|
||||
};
|
||||
|
||||
void PrintBoard(int iter, const Board& board, const vector<PlayerInfo>& players)
|
||||
{
|
||||
// print board
|
||||
cout << board;
|
||||
cout << endl;
|
||||
|
||||
// print iteration number
|
||||
cout << "iter : " << iter << endl;
|
||||
|
||||
// find winning scores and winner numbers
|
||||
int maxscore = 0;
|
||||
int maxscore_cnt = 0;
|
||||
for(int i=0; i<players.size(); i++)
|
||||
{
|
||||
if(maxscore < players[i].score)
|
||||
{
|
||||
maxscore = players[i].score;
|
||||
maxscore_cnt = 0;
|
||||
}
|
||||
if(maxscore == players[i].score)
|
||||
maxscore_cnt ++;
|
||||
}
|
||||
|
||||
// print player status (boxes, win/tie)
|
||||
for(int i=0; i<players.size(); i++)
|
||||
{
|
||||
cout << "Player " << players[i].box << " (" << players[i].iplayer->PlayerInfo() << ") has ";
|
||||
if(players[i].score >= 2) cout << players[i].score << " boxes";
|
||||
else cout << players[i].score << " box";
|
||||
cout << " and spent " << players[i].timespent*100 << " msec";
|
||||
if(maxscore == players[i].score)
|
||||
{
|
||||
if(maxscore_cnt == 1) cout << " (win)." << endl;
|
||||
else cout << " (tie)." << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "." << endl;
|
||||
}
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
// send add-line event to players
|
||||
void EventAddLine(const vector<PlayerInfo>& players, char line, const Loc& loc)
|
||||
{
|
||||
for(int j = 0; j < players.size(); j++)
|
||||
{
|
||||
if(players[j].next == nullptr)
|
||||
continue;
|
||||
players[j].iplayer->EventAddLine(line, loc);
|
||||
}
|
||||
}
|
||||
|
||||
// send add-box event to players
|
||||
void EventAddBox(const vector<PlayerInfo>& players, char box, const Loc& loc)
|
||||
{
|
||||
for(int j = 0; j < players.size(); j++)
|
||||
{
|
||||
if(players[j].next == nullptr)
|
||||
continue;
|
||||
players[j].iplayer->EventAddBox(box, loc);
|
||||
}
|
||||
}
|
||||
|
||||
// update box with player box info, and return true (for gaining box) or false (for not gaining box)
|
||||
bool AddBox(Board& board, int row, int col, char box, const vector<PlayerInfo>& players)
|
||||
{
|
||||
assert(Loc(row,col).IsBoxLocation());
|
||||
if(board(row,col) == ' '
|
||||
&& board(row+1,col) != ' '
|
||||
&& board(row-1,col) != ' '
|
||||
&& board(row,col+1) != ' '
|
||||
&& board(row,col-1) != ' ')
|
||||
{
|
||||
board(row,col) = box;
|
||||
EventAddBox(players, box, Loc(row,col));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
string GameMaster(const vector<string>& libpaths, vector<int>& scores, vector<double>& timespents, double sleep_sec)
|
||||
{
|
||||
///////////////////////////////////////////////////
|
||||
// create board
|
||||
int board_rows, board_cols, board_blanklinecount;
|
||||
cin >> board_rows >> board_cols;
|
||||
cout << endl;
|
||||
Board board;
|
||||
board.AllocateBoard(board_rows, board_cols, board_blanklinecount);
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// load players
|
||||
vector<PlayerInfo> players;
|
||||
int ibox = 0;
|
||||
for(int i=0; i<libpaths.size(); i++)
|
||||
{
|
||||
string libpath = libpaths[i];
|
||||
IPlayer* player = nullptr;
|
||||
char box_type = 'A' + ibox;
|
||||
char line_type = 'a' + ibox;
|
||||
ibox++;
|
||||
if(box_type == 'E' || box_type == 'X')
|
||||
{
|
||||
box_type = 'A' + ibox;
|
||||
line_type = 'a' + ibox;
|
||||
ibox++;
|
||||
}
|
||||
|
||||
string message = "";
|
||||
try
|
||||
{
|
||||
message = LoadPlayer(player, libpath, board_rows, board_cols, box_type, line_type);
|
||||
if(message != "")
|
||||
{
|
||||
cout << message << endl;
|
||||
cout << "Library error while loading a player (" << libpath << ")." << endl;
|
||||
if(player != nullptr)
|
||||
player->Close();
|
||||
continue;
|
||||
}
|
||||
players.push_back(PlayerInfo(player, box_type, line_type));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
cout << "Exception while loading a player (" << libpath << ")." << endl;
|
||||
if(player != nullptr)
|
||||
player->Close();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// set player sequence and first player
|
||||
int iter = 0;
|
||||
for(int i=0; i<players.size(); i++)
|
||||
{
|
||||
int next_idx = (i+1) % players.size();
|
||||
int prev_idx = (i-1) % players.size();
|
||||
players[i].next = &(players[next_idx]);
|
||||
players[i].prev = &(players[prev_idx]);
|
||||
}
|
||||
PlayerInfo* player = &(players[0]);
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// play game
|
||||
while(true)
|
||||
{
|
||||
iter++;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// stop if there is no player to play
|
||||
if(player == nullptr)
|
||||
break;
|
||||
// stop if the board is full
|
||||
if(board_blanklinecount == 0)
|
||||
break;
|
||||
|
||||
// print play-log
|
||||
if(sleep_sec == 0)
|
||||
cout << "Player " << player->box;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// determine player's line location
|
||||
// if the player put a line into a invalid location, then makr the player lose
|
||||
Loc loc;
|
||||
double timespent = 0;
|
||||
try
|
||||
{
|
||||
auto begin = chrono::high_resolution_clock::now();
|
||||
loc = player->iplayer->SelectLineLocation();
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
|
||||
player->timespent += (elapsed.count() * 1e-9);
|
||||
if(loc.IsBoxLocation()) throw "lose";
|
||||
if(loc.IsDotLocation()) throw "lose";
|
||||
if(board(loc) != ' ' ) throw "lose";
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// if the player put line into a wrong location or throw exception,
|
||||
// make the player have -1 score (invalid move) and
|
||||
// remove the player from play-list (linked-list)
|
||||
player->score = -1;
|
||||
if(player->next == player)
|
||||
{
|
||||
// if player is the last player, make the linked-list empty
|
||||
player = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the player is not the last player, remove the player in the linked-list, and select the next player
|
||||
PlayerInfo* next_player = player->next;
|
||||
player->next->prev = player->prev;
|
||||
player->prev->next = player->next;
|
||||
player->next = nullptr;
|
||||
player->prev = nullptr;
|
||||
player = next_player;
|
||||
}
|
||||
// print play-log
|
||||
if(sleep_sec == 0)
|
||||
cout << " made an invalid move." << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// add the line into the board
|
||||
assert(loc.IsLineLocation());
|
||||
assert(board(loc) == ' ' );
|
||||
board(loc) = player->line;
|
||||
board_blanklinecount --;
|
||||
EventAddLine(players, player->line, loc);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// if the player gain a box, then
|
||||
// mark the box owner and
|
||||
// make gainbox true
|
||||
int gainbox = 0;
|
||||
if(loc.IsLineHorizontalLocation())
|
||||
{
|
||||
if(loc.row-1 >= 0 ) if(AddBox(board, loc.row-1, loc.col, player->box, players)) { player->score++; gainbox++; }
|
||||
if(loc.row+1 < board.GetRows()) if(AddBox(board, loc.row+1, loc.col, player->box, players)) { player->score++; gainbox++; }
|
||||
}
|
||||
if(loc.IsLineVerticalLocation())
|
||||
{
|
||||
if(loc.col-1 >= 0 ) if(AddBox(board, loc.row, loc.col-1, player->box, players)) { player->score++; gainbox++; }
|
||||
if(loc.col+1 < board.GetCols()) if(AddBox(board, loc.row, loc.col+1, player->box, players)) { player->score++; gainbox++; }
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// print board or play-log
|
||||
if(sleep_sec == 0)
|
||||
{
|
||||
cout << " add a line at (" << loc.row << "," << loc.col << ")";
|
||||
if(gainbox == 1)
|
||||
cout << " and gain " << gainbox << " box." << endl;
|
||||
else if(gainbox >= 2)
|
||||
cout << " and gain " << gainbox << " boxes." << endl;
|
||||
else
|
||||
cout << "." << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearConsole();
|
||||
PrintBoard(iter, board, players);
|
||||
SleepInSec(sleep_sec);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// if the player does not gain a box
|
||||
// move to the next player
|
||||
if(gainbox == 0)
|
||||
player = player->next;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// print board
|
||||
if(sleep_sec != 0) ClearConsole();
|
||||
else cout << endl;
|
||||
PrintBoard(iter, board, players);
|
||||
|
||||
for(int i = 0; i < players.size(); i++)
|
||||
{
|
||||
scores [i] = players[i].score;
|
||||
timespents[i] = players[i].timespent;
|
||||
}
|
||||
return "";
|
||||
}
|
3
input1.txt
Normal file
3
input1.txt
Normal file
@ -0,0 +1,3 @@
|
||||
3 8
|
||||
A Strategic
|
||||
B Random
|
78
main.cxx
Normal file
78
main.cxx
Normal file
@ -0,0 +1,78 @@
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include "player.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <unistd.h> // usleep()
|
||||
#include <dlfcn.h>
|
||||
|
||||
string GameMaster(const vector<string> &libpaths, vector<int> &scores, vector<double> ×pents, double sleep_sec);
|
||||
|
||||
void CloseLibs();
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// argv[0] : program name
|
||||
// argv[1] : 1st player library path
|
||||
// argv[2] : 2nd player library path
|
||||
// argv[3] : 3rd player library path
|
||||
if (argc <= 2)
|
||||
return 0;
|
||||
|
||||
vector<string> libpaths;
|
||||
vector<int> scores;
|
||||
vector<double> timespents;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
libpaths.push_back(argv[i]);
|
||||
scores.push_back(0);
|
||||
timespents.push_back(0);
|
||||
}
|
||||
|
||||
double sleep_sec = 0.00;
|
||||
string result = GameMaster(libpaths, scores, timespents, sleep_sec);
|
||||
CloseLibs();
|
||||
}
|
||||
|
||||
void ClearConsole() {
|
||||
system("clear");
|
||||
}
|
||||
|
||||
void SleepInSec(double sleep_sec) {
|
||||
int sleep_microsecond = sleep_sec * 1000000;
|
||||
usleep(sleep_microsecond);
|
||||
}
|
||||
|
||||
vector<void *> lst_player_libhandle;
|
||||
|
||||
typedef IPlayer *(*CreatePlayerType)();
|
||||
|
||||
string LoadPlayer
|
||||
(IPlayer *&iplayer, string libpath, int board_rows // the size of board (including dots, lines, and boxes)
|
||||
, int board_cols // the size of board (including dots, lines, and boxes)
|
||||
, char box_type // the character for the player's boxes
|
||||
, char line_type // the character for the player's lines
|
||||
) {
|
||||
void *player_libhandle = dlopen(libpath.c_str(), RTLD_LAZY);
|
||||
if (player_libhandle == nullptr) {
|
||||
return "cannot load " + libpath;
|
||||
}
|
||||
lst_player_libhandle.push_back(player_libhandle);
|
||||
void *pfunc = dlsym(player_libhandle, "PlayerFactory");
|
||||
if (pfunc == nullptr) {
|
||||
return "cannot find PlayerFactory() function defined as extern C";
|
||||
}
|
||||
CreatePlayerType CreatePlayer = (CreatePlayerType) pfunc;
|
||||
iplayer = CreatePlayer();
|
||||
iplayer->Init(board_rows, board_cols, box_type, line_type);
|
||||
if (iplayer == nullptr) {
|
||||
return "cannot create player using PlayerFactory() function";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void CloseLibs() {
|
||||
for (void *player_libhandle: lst_player_libhandle)
|
||||
dlclose(player_libhandle);
|
||||
lst_player_libhandle.clear();
|
||||
}
|
||||
|
35
player.h
Normal file
35
player.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef __PLAYER__
|
||||
#define __PLAYER__
|
||||
|
||||
#include <iostream>
|
||||
#include "common.h"
|
||||
#include "board.h"
|
||||
using namespace std;
|
||||
|
||||
class IPlayer
|
||||
{
|
||||
public:
|
||||
// return the player information that includes full name and email address,
|
||||
// such that "Micky Mouse (mm123@psu.edu)"
|
||||
virtual string PlayerInfo() = 0;
|
||||
// Init(const int,const int) will be called before playing the game
|
||||
// You can create your own data-structure
|
||||
virtual void Init
|
||||
( int board_rows // the size of board (including dots, lines, and boxes)
|
||||
, int board_cols // the size of board (including dots, lines, and boxes)
|
||||
, char box_type // the character for the player's boxes
|
||||
, char line_type // the character for the player's lines
|
||||
) = 0;
|
||||
// Close() will be called after finishing playing the game
|
||||
// You can remove all dynamically allocated memories
|
||||
virtual void Close() = 0;
|
||||
// EventAddLine() and EventAddBox() will be called
|
||||
// when a player adds a line or when a system assign a box's owner
|
||||
virtual void EventAddLine(char bar, const Loc& loc) = 0;
|
||||
virtual void EventAddBox (char box, const Loc& loc) = 0;
|
||||
// Loc SelectLineLocation() will be called
|
||||
// when the game system ask where your player want to add a line
|
||||
virtual Loc SelectLineLocation() = 0;
|
||||
};
|
||||
|
||||
#endif
|
79
random_player.cxx
Normal file
79
random_player.cxx
Normal file
@ -0,0 +1,79 @@
|
||||
#include <iostream>
|
||||
#include <assert.h>
|
||||
#include <cstdlib> // for srand and rand
|
||||
#include <ctime> // for time
|
||||
|
||||
#include "common.h"
|
||||
#include "player.h"
|
||||
#include "random_player.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
extern "C" IPlayer* PlayerFactory()
|
||||
{
|
||||
return new RandomPlayer();
|
||||
}
|
||||
|
||||
RandomPlayer::RandomPlayer()
|
||||
{
|
||||
srand(time(0));
|
||||
}
|
||||
|
||||
RandomPlayer::~RandomPlayer()
|
||||
{
|
||||
}
|
||||
|
||||
void RandomPlayer::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;
|
||||
emptylines = new Loc[board.GetRows() * board.GetCols()];
|
||||
}
|
||||
|
||||
void RandomPlayer::Close()
|
||||
{
|
||||
board.FreeBoard();
|
||||
int emptylines_cnt = board.GetRows() * board.GetCols();
|
||||
delete[] emptylines;
|
||||
}
|
||||
|
||||
void RandomPlayer::EventAddLine(const char bar, const Loc& loc)
|
||||
{
|
||||
assert(loc.IsLineLocation());
|
||||
assert(board(loc) == ' ');
|
||||
board(loc) = bar;
|
||||
}
|
||||
|
||||
void RandomPlayer::EventAddBox(const char box, const Loc& loc)
|
||||
{
|
||||
assert(loc.IsBoxLocation());
|
||||
assert(board(loc) == ' ');
|
||||
board(loc) = box;
|
||||
}
|
||||
|
||||
Loc RandomPlayer::SelectLineLocation()
|
||||
{
|
||||
ListEmptyLines();
|
||||
int randloc = rand() % emptylines_cnt;
|
||||
return emptylines[randloc];
|
||||
}
|
||||
|
||||
void RandomPlayer::ListEmptyLines()
|
||||
{
|
||||
emptylines_cnt = 0;
|
||||
for(int row=0; row<board.GetRows(); row++)
|
||||
{
|
||||
for(int col=0; col<board.GetCols(); col++)
|
||||
{
|
||||
if((row % 2 == 0) && (col % 2 == 0)) continue; // dot
|
||||
if((row % 2 == 1) && (col % 2 == 1)) continue; // box
|
||||
if(board(row, col) == ' ')
|
||||
{
|
||||
emptylines[emptylines_cnt].row = row;
|
||||
emptylines[emptylines_cnt].col = col;
|
||||
emptylines_cnt ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
32
random_player.h
Normal file
32
random_player.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef __RANDOM_PLAYER__
|
||||
#define __RANDOM_PLAYER__
|
||||
|
||||
#include <iostream>
|
||||
#include "common.h"
|
||||
#include "player.h"
|
||||
#include "board.h"
|
||||
using namespace std;
|
||||
|
||||
class RandomPlayer : public IPlayer
|
||||
{
|
||||
private:
|
||||
Board board;
|
||||
char player_box;
|
||||
char player_line;
|
||||
Loc* emptylines;
|
||||
int emptylines_cnt;
|
||||
public:
|
||||
RandomPlayer();
|
||||
~RandomPlayer();
|
||||
string PlayerInfo() { return "random player"; }
|
||||
void Init(int dots_in_rows, int dots_in_cols, char player_box, char player_line);
|
||||
// Close() will be called after finishing playing the game
|
||||
// You can remove all dynamically allocated memories
|
||||
void Close();
|
||||
void EventAddLine(char bar, const Loc& loc);
|
||||
void EventAddBox(char box, const Loc& loc);
|
||||
Loc SelectLineLocation();
|
||||
void ListEmptyLines();
|
||||
};
|
||||
|
||||
#endif
|
10
readme.txt
Normal file
10
readme.txt
Normal file
@ -0,0 +1,10 @@
|
||||
Build mazerunner program and player_randommove.so library
|
||||
|
||||
g++ -ansi -pedantic -std=c++14 board.cxx dotsboxesgm.cxx main.cxx -o dotsboxes
|
||||
g++ -shared -fPIC -ansi -pedantic -std=c++14 random_player.cxx board.cxx -o random_player.so
|
||||
|
||||
|
||||
playing the dots and boxes games with multiple players
|
||||
|
||||
./dotsboxes ./strategic_player.so ./random_player.so ./strategic_player.so ./random_player.so
|
||||
|
164
strategic_player.cxx
Normal file
164
strategic_player.cxx
Normal file
@ -0,0 +1,164 @@
|
||||
#include "strategic_player.h"
|
||||
#include "common.h"
|
||||
|
||||
extern "C" IPlayer* PlayerFactory()
|
||||
{
|
||||
return new StrategicPlayer();
|
||||
}
|
||||
|
||||
|
||||
string StrategicPlayer::PlayerInfo() {
|
||||
return "Sapan Shah (scs6041@psu.edu), Sandipsinh Rathod (sdr5549@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();
|
||||
}
|
||||
|
||||
int normalize(int x) {
|
||||
return (x + 1) >> 1;
|
||||
}
|
||||
|
||||
char getPoint(Board &board, const int row, const int col) {
|
||||
return board(row, col);
|
||||
}
|
||||
|
||||
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) == ' ');
|
||||
}
|
||||
|
||||
void set(Board &board, int r, int c, char ch) {
|
||||
board(r, c) = ch;
|
||||
}
|
||||
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
void selectLine(Board &board, int &row, int &col, int rows, int cols, char name) {
|
||||
// Step 1: Try to complete a box
|
||||
for (int r = 1; r < 2 * rows - 2; r += 2) {
|
||||
// Iterate over box centers (odd rows)
|
||||
for (int c = 1; c < 2 * cols - 2; 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
|
||||
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 < 2 * rows - 2; nr += 2) {
|
||||
// Iterate over box centers
|
||||
for (int nc = 1; nc < 2 * cols - 2; nc += 2) {
|
||||
if (getPoint(board, nr, nc) == ' ') {
|
||||
int adjacentLines = 0;
|
||||
if (nr > 0 && getPoint(board, nr - 1, nc) != ' ') adjacentLines++; // Top line
|
||||
if (nr < 2 * rows - 2 && getPoint(board, nr + 1, nc) != ' ') adjacentLines++; // Bottom line
|
||||
if (nc > 0 && getPoint(board, nr, nc - 1) != ' ') adjacentLines++; // Left line
|
||||
if (nc < 2 * cols - 2 && 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
|
||||
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);
|
||||
}
|
38
strategic_player.h
Normal file
38
strategic_player.h
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
#ifndef HW4_STRATEGIC_PLAYER_H
|
||||
#define HW4_STRATEGIC_PLAYER_H
|
||||
|
||||
#include "common.h"
|
||||
#include "board.h"
|
||||
#include "player.h"
|
||||
|
||||
class StrategicPlayer: public IPlayer {
|
||||
char name;
|
||||
char box_name;
|
||||
|
||||
Board board;
|
||||
public:
|
||||
string PlayerInfo();
|
||||
// Init(const int,const int) will be called before playing the game
|
||||
// You can create your own data-structure
|
||||
void Init
|
||||
( int board_rows // the size of board (including dots, lines, and boxes)
|
||||
, int board_cols // the size of board (including dots, lines, and boxes)
|
||||
, char box_type // the character for the player's boxes
|
||||
, char line_type // the character for the player's lines
|
||||
);
|
||||
// Close() will be called after finishing playing the game
|
||||
// You can remove all dynamically allocated memories
|
||||
void Close();
|
||||
// EventAddLine() and EventAddBox() will be called
|
||||
// when a player adds a line or when a system assign a box's owner
|
||||
void EventAddLine(char bar, const Loc& loc);
|
||||
void EventAddBox (char box, const Loc& loc);
|
||||
// Loc SelectLineLocation() will be called
|
||||
// when the game system ask where your player want to add a line
|
||||
Loc SelectLineLocation();
|
||||
|
||||
~StrategicPlayer();
|
||||
};
|
||||
|
||||
#endif //HW4_STRATEGIC_PLAYER_H
|
Loading…
Reference in New Issue
Block a user