Initial commit on develop branch
This commit is contained in:
@@ -0,0 +1,422 @@
|
||||
#include "Config.h"
|
||||
#include "Board.h"
|
||||
#include "Move.h"
|
||||
#include "../Database/GameLogManager.h"
|
||||
#include "../Utilities/SymbolToStr.h"
|
||||
#include <iostream>
|
||||
|
||||
|
||||
//------------------------------- Constructor & deconstructor ----------------------------------------
|
||||
|
||||
Board::Board(int boardSize)
|
||||
// grid holds the matrix used for game logic
|
||||
// validMovesGrid is only used to show valid moves
|
||||
// display grid is used to potentially combine grid and validMovesGrid for displaying the board
|
||||
{
|
||||
BoardSize = boardSize;
|
||||
|
||||
grid = new char*[boardSize];
|
||||
for (int i = 0; i < boardSize; ++i) {
|
||||
grid[i] = new char[boardSize];
|
||||
}
|
||||
|
||||
validMovesGrid = new char*[boardSize];
|
||||
for (int i = 0; i < boardSize; ++i) {
|
||||
validMovesGrid[i] = new char[boardSize];
|
||||
}
|
||||
|
||||
displayGrid = new char*[boardSize];
|
||||
for (int i = 0; i < boardSize; ++i) {
|
||||
displayGrid[i] = new char[boardSize];
|
||||
}
|
||||
|
||||
for (int y = 0; y < boardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < boardSize; x++)
|
||||
{
|
||||
grid[y][x] = '.';
|
||||
validMovesGrid[y][x] = '.';
|
||||
displayGrid[y][x] = '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Board::deleteBoardMemory()
|
||||
// when the game is over and maybe GameReviewer was executed
|
||||
// this method has to be called to free up the allocated memory
|
||||
// a destructor could do the job too
|
||||
{
|
||||
for (int i = 0; i < BoardSize; ++i)
|
||||
{
|
||||
delete[] grid[i];
|
||||
}
|
||||
delete[] grid;
|
||||
|
||||
for (int i = 0; i < BoardSize; ++i)
|
||||
{
|
||||
delete[] validMovesGrid[i];
|
||||
}
|
||||
delete[] validMovesGrid;
|
||||
|
||||
for (int i = 0; i < BoardSize; ++i)
|
||||
{
|
||||
delete[] displayGrid[i];
|
||||
}
|
||||
delete[] displayGrid;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------- Database & pre-game operations ------------------------------------
|
||||
|
||||
std::string Board::retrieveBoard()
|
||||
// turns grid into string format to be saved inside database
|
||||
// this string can be used to create a grid using loadBoard method
|
||||
{
|
||||
int size = BoardSize * BoardSize + BoardSize;
|
||||
std::string result = "";
|
||||
|
||||
for (int y = 0; y < BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < BoardSize; x++)
|
||||
{
|
||||
result += symbolToStr(grid[y][x]);
|
||||
|
||||
if (!(y == BoardSize-1 && x == BoardSize-1))
|
||||
{
|
||||
result += "|";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void Board::newGameSetup()
|
||||
// grid represents the starting state of the board when playing a new game
|
||||
// called for board before passing its pointer for creating a SinglePlayerGame/MultiPlayerGame instance
|
||||
{
|
||||
for (int y = 0; y < BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < BoardSize; x++)
|
||||
{
|
||||
grid[y][x] = '.';
|
||||
}
|
||||
}
|
||||
|
||||
grid[BoardSize/2 - 1][BoardSize/2 - 1] = 'W';
|
||||
grid[BoardSize/2][BoardSize/2] = 'W';
|
||||
grid[BoardSize/2][BoardSize/2 - 1] = 'B';
|
||||
grid[BoardSize/2 - 1][BoardSize/2] = 'B';
|
||||
|
||||
updateDisplayGrid();
|
||||
}
|
||||
|
||||
|
||||
void Board::loadBoard(std::string Board)
|
||||
// recreates grid based on a string inside database
|
||||
// this string is created by retrieveBoard method
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
for (int y = 0; y < BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < BoardSize; x++)
|
||||
{
|
||||
grid[y][x] = static_cast<char>(Board[index]);
|
||||
index += 2;
|
||||
}
|
||||
}
|
||||
|
||||
updateDisplayGrid();
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------- grid and game operations -----------------------------------------
|
||||
|
||||
void Board::prepareBoardForMove(char thisTurnColor)
|
||||
// this method has to be called when a valid move is made
|
||||
// updated displayGrid with the latest changes in grid
|
||||
// validMovesGrid will find valid moves based on the new grid
|
||||
// if user has chosen to see valid moves inside the board (SHOW_AVAILABLE_PLACES_FOR_PIECES is set to true)
|
||||
// then they are putted inside the displayGrid (combining grid and validMovesGrid)
|
||||
{
|
||||
updateDisplayGrid();
|
||||
prepareValidMovesGrid(thisTurnColor);
|
||||
if (Config::getInstance() -> SHOW_AVAILABLE_PLACES_FOR_PIECES)
|
||||
putValidMoves();
|
||||
}
|
||||
|
||||
|
||||
void Board::flip(int x, int y)
|
||||
// changes the color of the piece inside the given coordinates
|
||||
{
|
||||
if (grid[y][x] == 'B')
|
||||
grid[y][x] = 'W';
|
||||
else if (grid[y][x] == 'W')
|
||||
grid[y][x] = 'B';
|
||||
}
|
||||
|
||||
|
||||
void Board::putPiece(int x, int y, char color)
|
||||
{
|
||||
grid[y][x] = color;
|
||||
|
||||
Move move(x, y, color);
|
||||
addLog(move);
|
||||
|
||||
// each comnination of coresponding elements represent a diraction
|
||||
// left-up & left & left-down & up & down & right-uo & right & right-down
|
||||
const int dx[] = {-1, -1, -1, 0, 0, 1, 1, 1};
|
||||
const int dy[] = {-1, 0, 1, -1, 1, -1, 0, 1};
|
||||
|
||||
for (int d = 0; d < 8; ++d)
|
||||
{
|
||||
int flips = countFlipsInDirection(x, y, dx[d], dy[d], color);
|
||||
|
||||
if (flips > 0)
|
||||
{
|
||||
int xc = x + dx[d];
|
||||
int yc = y + dy[d];
|
||||
|
||||
for (int step = 0; step < flips; ++step)
|
||||
{
|
||||
flip(xc, yc);
|
||||
xc += dx[d];
|
||||
yc += dy[d];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int Board::countBlack()
|
||||
// counts the black pieces of the Board
|
||||
{
|
||||
int count = 0;
|
||||
for (int y = 0; y < BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < BoardSize; x++)
|
||||
{
|
||||
if (grid[y][x] == 'B') count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int Board::countWhite()
|
||||
// counts the white pieces of the Board
|
||||
{
|
||||
int count = 0;
|
||||
for (int y = 0; y < BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < BoardSize; x++)
|
||||
{
|
||||
if (grid[y][x] == 'W') count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------ validGrid operations --------------------------------------------
|
||||
|
||||
int Board::countValidMoves()
|
||||
// returns the number of valid moves that can be made for the current turn
|
||||
// should be called when validMovesGrid is created for the turn
|
||||
// in a nutshell, after prepareBoardForMove method is called
|
||||
// used to decide of a game has to end
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int y = 0; y < BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < BoardSize; x++)
|
||||
{
|
||||
if (validMovesGrid[y][x] == 'O') count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int Board::isValid(int x, int y, char color)
|
||||
// returns the number of gains (opponent pieces fliped) if a piece with the given color is put in the given coords
|
||||
// if it returns 0, then the move is inValid
|
||||
// used as the primary parameter for the medium bot
|
||||
{
|
||||
if (grid[y][x] != '.') return 0;
|
||||
|
||||
int totalFlips = 0;
|
||||
|
||||
// each comnination of coresponding elements represent a diraction
|
||||
// left-up & left & left-down & up & down & right-uo & right & right-down
|
||||
const int dx[] = {-1, -1, -1, 0, 0, 1, 1, 1};
|
||||
const int dy[] = {-1, 0, 1, -1, 1, -1, 0, 1};
|
||||
|
||||
for (int d = 0; d < 8; d++)
|
||||
{
|
||||
totalFlips += countFlipsInDirection(x, y, dx[d], dy[d], color);
|
||||
}
|
||||
|
||||
return totalFlips;
|
||||
}
|
||||
|
||||
|
||||
int Board::countFlipsInDirection(int x, int y, int dx, int dy, char color)
|
||||
// counts the number of flips happened by putting a piece with the given color inside the given coordinates
|
||||
// dx and dy represent the direction and more specificly, the steps of x and y
|
||||
{
|
||||
char oppColor = (color == 'B' ? 'W' : 'B');
|
||||
|
||||
int flips = 0;
|
||||
int xc = x + dx;
|
||||
int yc = y + dy;
|
||||
|
||||
|
||||
while ((xc >= 0 && xc < BoardSize) && (yc >= 0 && yc < BoardSize) && grid[yc][xc] == oppColor)
|
||||
{
|
||||
flips++;
|
||||
xc += dx;
|
||||
yc += dy;
|
||||
}
|
||||
|
||||
// if loop doesn't meet a same colored piece, one of x coords or y coords will get out of bond
|
||||
// or the element loop breaked for is whether a '.' (empty place)
|
||||
if ((xc < 0 || xc == BoardSize) || (yc < 0 || yc == BoardSize) || grid[yc][xc] != color)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return flips;
|
||||
}
|
||||
|
||||
|
||||
void Board::resetValidMovesGrid()
|
||||
// clears validGrid elements to empty places
|
||||
{
|
||||
for (int y = 0; y < BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < BoardSize; x++)
|
||||
{
|
||||
validMovesGrid[y][x] = '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Board::prepareValidMovesGrid(char color)
|
||||
// first ValidMovesGrid resets itself so previous data won't interupt the process
|
||||
// then check each place in grid, if it's valid, it'll put O inside the coresponding coordinates inside itself
|
||||
{
|
||||
resetValidMovesGrid();
|
||||
|
||||
for (int y = 0; y < BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < BoardSize; x++)
|
||||
{
|
||||
if (isValid(x, y, color) != 0)
|
||||
validMovesGrid[y][x] = 'O';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------- display and displayGrid operations -----------------------------------------
|
||||
void Board::updateDisplayGrid()
|
||||
// displayGrid be copied based on the elements (pieces) inside grid
|
||||
{
|
||||
for (int y = 0; y < BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < BoardSize; x++)
|
||||
{
|
||||
displayGrid[y][x] = grid[y][x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Board::putValidMoves()
|
||||
// validMovesGrid has the valid moves coordinates inside itself
|
||||
// by calling this method, displayGrid will mark valid moves which
|
||||
// validMovesGrid marks
|
||||
{
|
||||
for (int y = 0; y < BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < BoardSize; x++)
|
||||
{
|
||||
if (validMovesGrid[y][x] == 'O')
|
||||
{
|
||||
displayGrid[y][x] = 'O';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Board::display()
|
||||
// outputs displayGrid with viasual decorations
|
||||
{
|
||||
std::cout << " ";
|
||||
for (int i = 1; i <= BoardSize; i++)
|
||||
{
|
||||
std::cout << i << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
std::cout << " ";
|
||||
std::cout << (char)201;
|
||||
for (int i = 0; i < BoardSize; i++)
|
||||
{
|
||||
std::cout << (char)205 << (char)205 << (char)205;
|
||||
}
|
||||
std::cout << (char)187 << std::endl;
|
||||
|
||||
for (int y = 0; y < BoardSize; y++)
|
||||
{
|
||||
std::cout << y + 1 << " " << (char)186;
|
||||
for (int x = 0; x < BoardSize; x++)
|
||||
{
|
||||
if (y == CursorY && x == CursorX) std::cout << "[";
|
||||
else std::cout << " ";
|
||||
|
||||
char element = displayGrid[y][x];
|
||||
if (element == 'W') // white piece
|
||||
{
|
||||
std::cout << 'X';
|
||||
}
|
||||
else if (element == 'B') // black piece
|
||||
{
|
||||
std::cout << 'O';
|
||||
}
|
||||
else if (element == '.') // empty space
|
||||
{
|
||||
std::cout << ' ';
|
||||
}
|
||||
else if (element == 'O') // valid move
|
||||
{
|
||||
std::cout << (char)249; // ∙
|
||||
}
|
||||
|
||||
if (y == CursorY && x == CursorX) std::cout << "]";
|
||||
else std::cout << " ";
|
||||
}
|
||||
std::cout << (char)186 << std::endl;
|
||||
}
|
||||
|
||||
std::cout << " ";
|
||||
std::cout << (char)200;
|
||||
for (int i = 0; i < BoardSize; i++)
|
||||
{
|
||||
std::cout << (char)205 << (char)205 << (char)205;
|
||||
}
|
||||
std::cout << (char)188 << std::endl;
|
||||
}
|
||||
// (char)186 = ║ (char)200 = ╚ (char)188 = ╝ (char)205 = ═ (char)206 = ╦ (char)202 = ╩ (char)187 = ╗ (char)201 = ╔
|
||||
// (char)79 = O
|
||||
// (char)254 = ■
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Board
|
||||
{
|
||||
int BoardSize;
|
||||
char** grid;
|
||||
char** validMovesGrid;
|
||||
char** displayGrid;
|
||||
int CursorX = 0;
|
||||
int CursorY = 0;
|
||||
|
||||
public:
|
||||
Board(int boardSize);
|
||||
void deleteBoardMemory();
|
||||
std::string retrieveBoard();
|
||||
void newGameSetup();
|
||||
void loadBoard(std::string Board);
|
||||
void prepareBoardForMove(char thisTurnColor);
|
||||
void putPiece(int x, int y, char color);
|
||||
int countBlack();
|
||||
int countWhite();
|
||||
int isValid(int x, int y, char color);
|
||||
int countValidMoves();
|
||||
void display();
|
||||
|
||||
public:
|
||||
void flip(int x, int y);
|
||||
int countFlipsInDirection(int x, int y, int dx, int dy, char color);
|
||||
void resetValidMovesGrid();
|
||||
void prepareValidMovesGrid(char color);
|
||||
void updateDisplayGrid();
|
||||
void putValidMoves();
|
||||
};
|
||||
@@ -0,0 +1,372 @@
|
||||
#include "Bot.h"
|
||||
#include "Board.h"
|
||||
#include "Move.h"
|
||||
#include "../Utilities/IntToStr.h"
|
||||
#include "../Utilities/StrToInt.h"
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <climits>
|
||||
|
||||
|
||||
|
||||
Bot::Bot(std::string name, int difficulty)
|
||||
{
|
||||
this -> name = name;
|
||||
this -> difficulty = difficulty;
|
||||
}
|
||||
|
||||
|
||||
Move Bot::suggestMove(Board& board, char color)
|
||||
// the fucntion called by GameMaster when it's bot's turn to move
|
||||
// this function should be called when prepareBoardForMove is called
|
||||
// so bot will work as expected
|
||||
{
|
||||
if (difficulty == 1) // easy
|
||||
{
|
||||
return suggestEasyMove(board, color);
|
||||
}
|
||||
else if (difficulty == 2) // medium
|
||||
{
|
||||
return suggestMediumMove(board, color);
|
||||
}
|
||||
else if (difficulty == 3) // hard
|
||||
{
|
||||
return suggestHardMove(board, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
return suggestMediumMove(board, color); // fallback
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Move Bot::suggestEasyMove(Board& board, char color)
|
||||
// the decision a bot would make in easy mode
|
||||
// the act is based on a random choice
|
||||
{
|
||||
int validMovesCount = board.countValidMoves();
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<int> distrib(0, validMovesCount-1);
|
||||
int randomNumber = distrib(gen);
|
||||
|
||||
int moveNumber = 0;
|
||||
for (int y = 0; y < board.BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < board.BoardSize; x++)
|
||||
{
|
||||
if (board.validMovesGrid[y][x] == 'O')
|
||||
{
|
||||
if (moveNumber == randomNumber)
|
||||
{
|
||||
Move move{x, y, color};
|
||||
return move;
|
||||
}
|
||||
else
|
||||
{
|
||||
moveNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Move Bot::suggestMediumMove(Board& board, char color)
|
||||
// the decision a bot would make in medium mode
|
||||
// the act is based on how much gain each move will have
|
||||
// the move with the most gain is made
|
||||
{
|
||||
int moveX, moveY;
|
||||
int maxGain = 0;
|
||||
|
||||
for (int y = 0; y < board.BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < board.BoardSize; x++)
|
||||
{
|
||||
if (board.validMovesGrid[y][x] == 'O')
|
||||
{
|
||||
int gain = board.isValid(x, y, color);
|
||||
if (gain > maxGain)
|
||||
{
|
||||
moveY = y;
|
||||
moveX = x;
|
||||
maxGain = gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Move move{moveX, moveY, color};
|
||||
return move;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Move Bot::suggestHardMove(Board& board, char color)
|
||||
// the decision a bot would make in hard mode
|
||||
{
|
||||
char oppColor = (color == 'W' ? 'B':'W');
|
||||
int index = 0;
|
||||
int validMovesCount = board.countValidMoves();
|
||||
|
||||
Move moves[validMovesCount];
|
||||
|
||||
for (int y = 0; y < board.BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < board.BoardSize; x++)
|
||||
{
|
||||
if (board.validMovesGrid[y][x] == 'O')
|
||||
{
|
||||
Move move {x, y, color};
|
||||
moves[index] = move;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Move bestMove = moves[0];
|
||||
int bestScore = INT_MIN;
|
||||
for (int i = 0; i < validMovesCount; i++)
|
||||
{
|
||||
Move move = moves[i];
|
||||
|
||||
Board dummyBoard {board.BoardSize};
|
||||
|
||||
deepCopy(board, dummyBoard);
|
||||
|
||||
dummyBoard.putPiece(move.x, move.y, move.color);
|
||||
dummyBoard.prepareBoardForMove(oppColor);
|
||||
|
||||
int score = minimax(oppColor, dummyBoard, false, color); // start minimizing since its user's turn
|
||||
|
||||
if (score > bestScore)
|
||||
{
|
||||
bestScore = score;
|
||||
bestMove = move;
|
||||
}
|
||||
}
|
||||
|
||||
return bestMove;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string Bot::retrieveBot()
|
||||
// turns Bot structure into a string to be saved inside database
|
||||
// this string will be loaded using loadBot method
|
||||
{
|
||||
return name + "|" + intToStr(difficulty);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Bot::loadBot(std::string bot)
|
||||
// fills Bot's properties according to the given string
|
||||
// this string is bot's informating saved inside database
|
||||
// this string was made using retrieveBot method
|
||||
{
|
||||
int seperatorIndex = bot.find("|");
|
||||
name = bot.substr(0, seperatorIndex);
|
||||
difficulty = strToInt(bot.substr(seperatorIndex + 1, bot.size()));
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------- minimax algorithm ----------------------------------------------------
|
||||
int Bot::ratePlacement(int x, int y, int boardSize)
|
||||
// Board is passed by refrence for time optimization
|
||||
// if we put a piece with these information, what score would the piece get
|
||||
// for the coordinates itselves
|
||||
{
|
||||
// corners
|
||||
if ((x == boardSize - 1 || x == 0) && (y == boardSize - 1 || y == 0))
|
||||
return 100;
|
||||
|
||||
// leading to corner moves:
|
||||
else if (
|
||||
(x == boardSize - 2 && y == 1) ||
|
||||
(x == boardSize - 2 && y == boardSize - 2) ||
|
||||
(x == 1 && y == 1) ||
|
||||
(x == 1 && y == boardSize - 2)
|
||||
)
|
||||
return -50;
|
||||
else if (
|
||||
(x == boardSize - 2 && y == boardSize - 1) ||
|
||||
(x == boardSize - 2 && y == 0) ||
|
||||
(x == 1 && y == 0) ||
|
||||
(x == 1 && y == boardSize - 1) ||
|
||||
(x == boardSize - 1 && y == 1) ||
|
||||
(x == boardSize - 1 && y == boardSize - 2) ||
|
||||
(x == 0 && y == 1) ||
|
||||
(x == 0 && y == boardSize - 2)
|
||||
)
|
||||
return -30;
|
||||
|
||||
// edges (corners and corner neighbour already handled)
|
||||
else if (x == 0 || x == boardSize - 1 || y == 0 || y == boardSize - 1)
|
||||
return 3;
|
||||
|
||||
// center
|
||||
else if (
|
||||
(x == boardSize/2 && y == boardSize/2) ||
|
||||
(x == boardSize/2 && y == boardSize/2 - 1) ||
|
||||
(x == boardSize/2 - 1 && y == boardSize/2) ||
|
||||
(x == boardSize/2 - 1 && y == boardSize/2 - 1)
|
||||
)
|
||||
return 3;
|
||||
|
||||
// other places have no value on their own
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Bot::rateBoard(Board& board, char botColor)
|
||||
{
|
||||
char oppColor = (botColor == 'W' ? 'B':'W');
|
||||
|
||||
Board dummyBoard {board.BoardSize};
|
||||
deepCopy(board, dummyBoard);
|
||||
|
||||
int botScore = 0, playerScore = 0;
|
||||
|
||||
// possible moves rating
|
||||
dummyBoard.prepareBoardForMove(botColor);
|
||||
botScore += dummyBoard.countValidMoves();
|
||||
|
||||
dummyBoard.prepareBoardForMove(oppColor);
|
||||
botScore -= dummyBoard.countValidMoves();
|
||||
|
||||
// greedy strategy
|
||||
int blackCount = dummyBoard.countBlack();
|
||||
int whiteCount = dummyBoard.countWhite();
|
||||
|
||||
if (botColor == 'B')
|
||||
{
|
||||
botScore += blackCount;
|
||||
botScore -= whiteCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
botScore -= blackCount;
|
||||
botScore += whiteCount;
|
||||
}
|
||||
|
||||
// placement score
|
||||
for (int y = 0; y < board.BoardSize; y++)
|
||||
{
|
||||
for (int x =0; x < board.BoardSize; x++)
|
||||
{
|
||||
char element = board.grid[y][x];
|
||||
|
||||
if (element == 'W' || element == 'B')
|
||||
{
|
||||
int score = ratePlacement(x, y, board.BoardSize);
|
||||
|
||||
if (element == botColor)
|
||||
{
|
||||
botScore += score;
|
||||
}
|
||||
else
|
||||
{
|
||||
botScore -= score;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return botScore;
|
||||
}
|
||||
|
||||
|
||||
int Bot::minimax(char color, Board dummyBoard, bool isMaxAgent, char botColor, int depth, int upFloor, int downFloor)
|
||||
{
|
||||
int index = 0;
|
||||
int validMovesCount = dummyBoard.countValidMoves();
|
||||
|
||||
if (depth == 3 || validMovesCount == 0)
|
||||
{
|
||||
return rateBoard(dummyBoard, botColor);
|
||||
}
|
||||
|
||||
Move moves[validMovesCount];
|
||||
|
||||
for (int y = 0; y < dummyBoard.BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < dummyBoard.BoardSize; x++)
|
||||
{
|
||||
if (dummyBoard.validMovesGrid[y][x] == 'O')
|
||||
{
|
||||
Move move {x, y, color};
|
||||
moves[index] = move;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isMaxAgent) // maximizing
|
||||
{
|
||||
int curMax = INT_MIN;
|
||||
|
||||
// for each valid move in this turn
|
||||
for (int i = 0; i < validMovesCount; i++)
|
||||
{
|
||||
Move move = moves[i];
|
||||
|
||||
Board newBoard{dummyBoard.BoardSize};
|
||||
|
||||
deepCopy(dummyBoard, newBoard);
|
||||
newBoard.putPiece(move.x, move.y, move.color);
|
||||
newBoard.prepareBoardForMove(move.color == 'B' ? 'W':'B');
|
||||
|
||||
int minAgentAnswer = minimax((move.color == 'B' ? 'W':'B'), newBoard, false, botColor, depth + 1, upFloor, downFloor);
|
||||
|
||||
curMax = std::max(curMax, minAgentAnswer);
|
||||
upFloor = std::max(upFloor, curMax);
|
||||
|
||||
if (upFloor >= downFloor) break;
|
||||
}
|
||||
|
||||
return curMax;
|
||||
}
|
||||
|
||||
else // minimizing
|
||||
{
|
||||
int curMin = INT_MAX;
|
||||
|
||||
// for each valid move in this turn
|
||||
for (int i = 0; i < validMovesCount; i++)
|
||||
{
|
||||
Move move = moves[i];
|
||||
|
||||
Board newBoard{dummyBoard.BoardSize};
|
||||
|
||||
deepCopy(dummyBoard, newBoard);
|
||||
newBoard.putPiece(move.x, move.y, move.color);
|
||||
newBoard.prepareBoardForMove(move.color == 'B' ? 'W':'B');
|
||||
|
||||
int maxAgentAnswer = minimax((move.color == 'B' ? 'W':'B'), newBoard, true, botColor, depth + 1, upFloor, downFloor);
|
||||
|
||||
curMin = std::min(curMin, maxAgentAnswer);
|
||||
downFloor = std::min(downFloor, curMin);
|
||||
|
||||
if (upFloor >= downFloor) break;
|
||||
}
|
||||
|
||||
return curMin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Bot::deepCopy(Board& board, Board& newBoard)
|
||||
{
|
||||
for (int y = 0; y < board.BoardSize; y++)
|
||||
{
|
||||
for (int x = 0; x < board.BoardSize; x++)
|
||||
{
|
||||
newBoard.grid[y][x] = board.grid[y][x];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "Board.h"
|
||||
#include "Move.h"
|
||||
|
||||
struct Bot
|
||||
{
|
||||
std::string name;
|
||||
int difficulty;
|
||||
|
||||
public:
|
||||
Bot(std::string name, int difficulty);
|
||||
Move suggestMove(Board& board, char color);
|
||||
std::string retrieveBot();
|
||||
void loadBot(std::string bot);
|
||||
|
||||
private:
|
||||
Move suggestEasyMove(Board& borad, char color);
|
||||
Move suggestMediumMove(Board& board, char color);
|
||||
Move suggestHardMove(Board& board, char color);
|
||||
int ratePlacement(int x, int y, int boardSize);
|
||||
int rateBoard(Board& board, char botColor);
|
||||
int minimax(char color, Board dummyBoard, bool isMaxAgent, char botColor, int depth = 0, int upFloor = INT_MIN, int downFloor = INT_MAX);
|
||||
void deepCopy(Board& board, Board& newBoard);
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "../Database/ConfigManager.h"
|
||||
#include "Config.h"
|
||||
#include <iostream>
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
Config* Config::instance = nullptr;
|
||||
|
||||
Config::Config()
|
||||
{
|
||||
try
|
||||
{
|
||||
GAME_HISTORY_LIMIT = getGameHistoryLimit();
|
||||
BOARD_SIZE = getBoardSize();
|
||||
BOARD_COLOR = getBoardColor();
|
||||
BOARD_BORDER_COLOR = getBoardBorderColor();
|
||||
SOUND = getSound();
|
||||
SHOW_AVAILABLE_PLACES_FOR_PIECES = getShowAvailablePlacesForPieces();
|
||||
HINT = getHint();
|
||||
HINT_LIMIT = getHintLimit();
|
||||
}
|
||||
catch (int errorCode)
|
||||
{
|
||||
if (errorCode == 1)
|
||||
{
|
||||
system("cls");
|
||||
std::cout << "An error acoured while opening Config.txt \nPerhaps another program is using it\nPress any key to continue";
|
||||
getchar();
|
||||
}
|
||||
else if (errorCode == 2)
|
||||
{
|
||||
system("cls");
|
||||
std::cout << "the inputed index is out of the bond of Config, the last index is 7\nPress any key to continue";
|
||||
getchar();
|
||||
}
|
||||
else
|
||||
{
|
||||
system("cls");
|
||||
std::cout << "Unexpected error acoured\nPress any key to continue";
|
||||
getchar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Config* Config::getInstance()
|
||||
{
|
||||
if (!instance)
|
||||
throw 3; // config not initialized
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
void Config::initialize()
|
||||
{
|
||||
if (!instance)
|
||||
instance = new Config();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Config
|
||||
{
|
||||
static Config* instance;
|
||||
|
||||
int GAME_HISTORY_LIMIT;
|
||||
int BOARD_SIZE;
|
||||
std::string BOARD_COLOR;
|
||||
std::string BOARD_BORDER_COLOR;
|
||||
bool SOUND;
|
||||
bool SHOW_AVAILABLE_PLACES_FOR_PIECES;
|
||||
bool HINT;
|
||||
int HINT_LIMIT;
|
||||
|
||||
private:
|
||||
Config();
|
||||
|
||||
public:
|
||||
static Config* getInstance();
|
||||
static void initialize();
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "Move.h"
|
||||
#include "../Utilities/StrToInt.h"
|
||||
#include "../Utilities/IntToStr.h"
|
||||
#include "../Utilities/StrToSymbol.h"
|
||||
#include "../Utilities/SymbolToStr.h"
|
||||
#include <string>
|
||||
|
||||
|
||||
Move::Move()
|
||||
{
|
||||
this -> x = 0;
|
||||
this -> y = 0;
|
||||
this -> color = 'B';
|
||||
}
|
||||
|
||||
|
||||
Move::Move(int x, int y, char color)
|
||||
{
|
||||
this -> x = x;
|
||||
this -> y = y;
|
||||
this -> color = color;
|
||||
}
|
||||
|
||||
std::string Move::retrieveMove()
|
||||
{
|
||||
return intToStr(x) + " " + intToStr(y) + " " + symbolToStr(color);
|
||||
}
|
||||
|
||||
void Move::loadMove(std::string move)
|
||||
{
|
||||
int xLastIndex = move.find(" ");
|
||||
std::string x = move.substr(0, xLastIndex);
|
||||
move = move.substr(xLastIndex + 1, move.size());
|
||||
|
||||
int yLastIndex = move.find(" ");
|
||||
std::string y = move.substr(0, yLastIndex);
|
||||
move = move.substr(yLastIndex + 1, move.size());
|
||||
|
||||
std::string color = move.substr(0, move.size());
|
||||
|
||||
this -> x = strToInt(x);
|
||||
this -> y = strToInt(y);
|
||||
this -> color = strToSymbol(color);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Move
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
char color;
|
||||
|
||||
public:
|
||||
Move(int x, int y, char color);
|
||||
Move();
|
||||
std::string retrieveMove();
|
||||
void loadMove(std::string move);
|
||||
};
|
||||
@@ -0,0 +1,190 @@
|
||||
#include "../Database/GameHistoryManager.h"
|
||||
#include "../Database/GameLogManager.h"
|
||||
#include "../Utilities/IntToStr.h"
|
||||
#include "../Utilities/SymbolToStr.h"
|
||||
#include "Board.h"
|
||||
#include "Player.h"
|
||||
#include "MultiPlayerGame.h"
|
||||
#include <conio.h>
|
||||
#include <iostream>
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
MultiPlayerGame::MultiPlayerGame(Board* GameBoard, Player* Player1, Player* Player2, char CurrentTurnColor)
|
||||
{
|
||||
int lastId = getLastGameId();
|
||||
|
||||
this -> id = lastId + 1;
|
||||
this -> GameBoard = GameBoard;
|
||||
this -> mode = "2Player";
|
||||
this -> Player1 = Player1;
|
||||
this -> Player2 = Player2;
|
||||
this -> CurrentTurnColor = CurrentTurnColor;
|
||||
this -> Winner = 0;
|
||||
}
|
||||
|
||||
|
||||
void MultiPlayerGame::changeTurn()
|
||||
{
|
||||
if (CurrentTurnColor == 'B')
|
||||
{
|
||||
CurrentTurnColor = 'W';
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentTurnColor = 'B';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MultiPlayerGame::start()
|
||||
{
|
||||
clearGameLog();
|
||||
(*GameBoard).prepareBoardForMove(CurrentTurnColor);
|
||||
|
||||
this -> save();
|
||||
|
||||
while (true)
|
||||
{
|
||||
system("cls");
|
||||
std::cout << (CurrentTurnColor == Player1 -> color ? Player2 -> name + "it's your turn" : Player2 -> name + "it's your turn") << std::endl;
|
||||
|
||||
(*GameBoard).display();
|
||||
|
||||
int userInput = getch();
|
||||
|
||||
if (userInput == static_cast<int>('w') || userInput == 72)
|
||||
{
|
||||
if (GameBoard -> CursorY != 0)
|
||||
{
|
||||
GameBoard -> CursorY--;
|
||||
}
|
||||
}
|
||||
|
||||
else if (userInput == static_cast<int>('s') || userInput == 80)
|
||||
{
|
||||
if (GameBoard -> CursorY != GameBoard -> BoardSize - 1)
|
||||
{
|
||||
GameBoard -> CursorY++;
|
||||
}
|
||||
}
|
||||
|
||||
else if (userInput == static_cast<int>('a') || userInput == 75)
|
||||
{
|
||||
if (GameBoard -> CursorX != 0)
|
||||
{
|
||||
GameBoard -> CursorX--;
|
||||
}
|
||||
}
|
||||
|
||||
else if (userInput == static_cast<int>('d') || userInput == 77)
|
||||
{
|
||||
if (GameBoard -> CursorX != GameBoard -> BoardSize - 1)
|
||||
{
|
||||
GameBoard -> CursorX++;
|
||||
}
|
||||
}
|
||||
|
||||
else if (userInput == 13)
|
||||
{
|
||||
if ( (*GameBoard).validMovesGrid[GameBoard -> CursorY][GameBoard -> CursorX] == 'O')
|
||||
{
|
||||
(*GameBoard).putPiece(GameBoard -> CursorX, GameBoard -> CursorY, CurrentTurnColor);
|
||||
changeTurn();
|
||||
(*GameBoard).prepareBoardForMove(CurrentTurnColor);
|
||||
this -> save();
|
||||
|
||||
if ((*GameBoard).countValidMoves() == 0)
|
||||
{
|
||||
changeTurn();
|
||||
(*GameBoard).prepareBoardForMove(CurrentTurnColor);
|
||||
this -> save();
|
||||
|
||||
if ( (*GameBoard).countValidMoves() == 0)
|
||||
{
|
||||
end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MultiPlayerGame::end()
|
||||
{
|
||||
int blackCount = (*GameBoard).countBlack();
|
||||
int whiteCount = (*GameBoard).countWhite();
|
||||
|
||||
system("cls");
|
||||
(*GameBoard).display();
|
||||
|
||||
std::cout << "\nFinal Score: Black: " << blackCount
|
||||
<< " | White: " << whiteCount << "\n\n";
|
||||
|
||||
if (blackCount > whiteCount)
|
||||
{
|
||||
|
||||
if (Player1->color == 'B')
|
||||
{
|
||||
Winner = 1;
|
||||
std::cout << "Congratulations " << Player1->name << "! You won!\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
Winner = 2;
|
||||
std::cout << "Congratulations " << Player2->name << "! You won!\n";
|
||||
}
|
||||
}
|
||||
|
||||
else if (whiteCount > blackCount)
|
||||
{
|
||||
if (Player1->color == 'W')
|
||||
{
|
||||
Winner = 1;
|
||||
std::cout << "Congratulations " << Player1->name << "! You won!\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
Winner = 2;
|
||||
std::cout << "You lost. The bot won.\n";
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Winner = 3; // Draw
|
||||
std::cout << "It's a draw!\n";
|
||||
}
|
||||
|
||||
this->save();
|
||||
}
|
||||
|
||||
|
||||
void MultiPlayerGame::save()
|
||||
// saves the game inside GameHistory.txt
|
||||
{
|
||||
saveGame(this);
|
||||
}
|
||||
|
||||
|
||||
std::string MultiPlayerGame::retrieveGame()
|
||||
// turns current Game instance into a string
|
||||
// this string can be turned into a Game instance via loadGame method
|
||||
{
|
||||
std::string idToSave = intToStr(id);
|
||||
std::string boardToSave = (*GameBoard).retrieveBoard();
|
||||
std::string player1ToSave = Player1 -> retrievePlayer();
|
||||
std::string player2ToSave = Player2 -> retrievePlayer();
|
||||
std::string currentTurnToSave = symbolToStr(CurrentTurnColor);
|
||||
std::string winnerToSave = intToStr(Winner);
|
||||
|
||||
return "[" + idToSave + "]["
|
||||
+ boardToSave + "]["
|
||||
+ mode + "]["
|
||||
+ player1ToSave + "]["
|
||||
+ player2ToSave + "]["
|
||||
+ currentTurnToSave + "]["
|
||||
+ winnerToSave + "]";
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "Board.h"
|
||||
#include "Player.h"
|
||||
#include <string>
|
||||
|
||||
struct MultiPlayerGame
|
||||
{
|
||||
int id;
|
||||
Board* GameBoard;
|
||||
std::string mode;
|
||||
Player* Player1;
|
||||
Player* Player2;
|
||||
char CurrentTurnColor;
|
||||
int Winner;
|
||||
|
||||
public:
|
||||
MultiPlayerGame(Board* GameBoard, Player* Player1, Player* Player2, char CurrentTurnColor);
|
||||
void start();
|
||||
std::string retrieveGame();
|
||||
|
||||
private:
|
||||
void end();
|
||||
void save();
|
||||
void changeTurn();
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
#include <string>
|
||||
#include "Player.h"
|
||||
#include "../Utilities/SymbolToStr.h"
|
||||
#include "../Utilities/StrToSymbol.h"
|
||||
|
||||
|
||||
Player::Player(std::string name, char color)
|
||||
{
|
||||
this -> name = name;
|
||||
this -> color = color;
|
||||
}
|
||||
|
||||
std::string Player::retrievePlayer()
|
||||
{
|
||||
return name + "|" + symbolToStr(color);
|
||||
}
|
||||
|
||||
void Player::loadPlayer(std::string player)
|
||||
{
|
||||
int speratorIndex = player.find("|");
|
||||
|
||||
std::string name = player.substr(0, speratorIndex);
|
||||
std::string color = player.substr(speratorIndex + 1, player.size());
|
||||
|
||||
this -> name = name;
|
||||
this -> color = strToSymbol(color);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Player
|
||||
{
|
||||
std::string name;
|
||||
char color;
|
||||
|
||||
public:
|
||||
Player(std::string name, char color);
|
||||
std::string retrievePlayer();
|
||||
void loadPlayer(std::string player);
|
||||
};
|
||||
@@ -0,0 +1,213 @@
|
||||
#include "../Database/GameHistoryManager.h"
|
||||
#include "../Database/GameLogManager.h"
|
||||
#include "../Utilities/IntToStr.h"
|
||||
#include "../Utilities/SymbolToStr.h"
|
||||
#include "Board.h"
|
||||
#include "Bot.h"
|
||||
#include "Player.h"
|
||||
#include "SinglePlayerGame.h"
|
||||
#include <conio.h>
|
||||
#include <iostream>
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
SinglePlayerGame::SinglePlayerGame(Board *GameBoard, Player* Player1, Bot* GameBot, char CurrentTurnColor)
|
||||
{
|
||||
int lastId = getLastGameId();
|
||||
|
||||
this -> id = lastId + 1;
|
||||
this -> GameBoard = GameBoard;
|
||||
this -> mode = "1Player";
|
||||
this -> Player1 = Player1;
|
||||
this -> GameBot = GameBot;
|
||||
this -> CurrentTurnColor = CurrentTurnColor;
|
||||
this -> Winner = 0;
|
||||
}
|
||||
|
||||
|
||||
void SinglePlayerGame::changeTurn()
|
||||
{
|
||||
if (CurrentTurnColor == 'B')
|
||||
{
|
||||
CurrentTurnColor = 'W';
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentTurnColor = 'B';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SinglePlayerGame::start()
|
||||
{
|
||||
clearGameLog();
|
||||
(*GameBoard).prepareBoardForMove(CurrentTurnColor);
|
||||
|
||||
this -> save();
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (CurrentTurnColor != Player1 -> color) // Bot turn
|
||||
{
|
||||
Move botMove = GameBot -> suggestMove( (*GameBoard), CurrentTurnColor);
|
||||
GameBoard -> putPiece(botMove.x, botMove.y, CurrentTurnColor);
|
||||
|
||||
changeTurn();
|
||||
(*GameBoard).prepareBoardForMove(CurrentTurnColor);
|
||||
this -> save();
|
||||
|
||||
if ( (*GameBoard).countValidMoves() == 0)
|
||||
{
|
||||
changeTurn();
|
||||
(*GameBoard).prepareBoardForMove(CurrentTurnColor);
|
||||
this -> save();
|
||||
|
||||
if ( (*GameBoard).countValidMoves() == 0)
|
||||
{
|
||||
end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
system("cls");
|
||||
|
||||
(*GameBoard).display();
|
||||
|
||||
int userInput = getch();
|
||||
|
||||
if (userInput == static_cast<int>('w') || userInput == 72)
|
||||
{
|
||||
if (GameBoard -> CursorY != 0)
|
||||
{
|
||||
GameBoard -> CursorY--;
|
||||
}
|
||||
}
|
||||
|
||||
else if (userInput == static_cast<int>('s') || userInput == 80)
|
||||
{
|
||||
if (GameBoard -> CursorY != GameBoard -> BoardSize - 1)
|
||||
{
|
||||
GameBoard -> CursorY++;
|
||||
}
|
||||
}
|
||||
|
||||
else if (userInput == static_cast<int>('a') || userInput == 75)
|
||||
{
|
||||
if (GameBoard -> CursorX != 0)
|
||||
{
|
||||
GameBoard -> CursorX--;
|
||||
}
|
||||
}
|
||||
|
||||
else if (userInput == static_cast<int>('d') || userInput == 77)
|
||||
{
|
||||
if (GameBoard -> CursorX != GameBoard -> BoardSize - 1)
|
||||
{
|
||||
GameBoard -> CursorX++;
|
||||
}
|
||||
}
|
||||
|
||||
else if (userInput == 13)
|
||||
{
|
||||
if ( (*GameBoard).validMovesGrid[GameBoard -> CursorY][GameBoard -> CursorX] == 'O')
|
||||
{
|
||||
(*GameBoard).putPiece(GameBoard -> CursorX, GameBoard -> CursorY, CurrentTurnColor);
|
||||
changeTurn();
|
||||
(*GameBoard).prepareBoardForMove(CurrentTurnColor);
|
||||
this -> save();
|
||||
|
||||
if ((*GameBoard).countValidMoves() == 0)
|
||||
{
|
||||
changeTurn();
|
||||
(*GameBoard).prepareBoardForMove(CurrentTurnColor);
|
||||
this -> save();
|
||||
|
||||
if ( (*GameBoard).countValidMoves() == 0)
|
||||
{
|
||||
end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SinglePlayerGame::end()
|
||||
{
|
||||
int blackCount = (*GameBoard).countBlack();
|
||||
int whiteCount = (*GameBoard).countWhite();
|
||||
|
||||
system("cls");
|
||||
(*GameBoard).display();
|
||||
|
||||
std::cout << "\nFinal Score: Black: " << blackCount
|
||||
<< " | White: " << whiteCount << "\n\n";
|
||||
|
||||
if (blackCount > whiteCount)
|
||||
{
|
||||
|
||||
if (Player1->color == 'B')
|
||||
{
|
||||
Winner = 1;
|
||||
std::cout << "Congratulations " << Player1->name << "! You won!\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
Winner = 2;
|
||||
std::cout << "You lost. The bot won.\n";
|
||||
}
|
||||
}
|
||||
|
||||
else if (whiteCount > blackCount)
|
||||
{
|
||||
if (Player1->color == 'W')
|
||||
{
|
||||
Winner = 1;
|
||||
std::cout << "Congratulations " << Player1->name << "! You won!\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
Winner = 2;
|
||||
std::cout << "You lost. The bot won.\n";
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Winner = 3; // Draw
|
||||
std::cout << "It's a draw!\n";
|
||||
}
|
||||
|
||||
this->save();
|
||||
}
|
||||
|
||||
|
||||
void SinglePlayerGame::save()
|
||||
// saves the game inside GameHistory.txt
|
||||
{
|
||||
saveGame(this);
|
||||
}
|
||||
|
||||
|
||||
std::string SinglePlayerGame::retrieveGame()
|
||||
// turns current Game instance into a string
|
||||
// this string can be turned into a Game instance via loadGame method
|
||||
{
|
||||
std::string idToSave = intToStr(id);
|
||||
std::string boardToSave = (*GameBoard).retrieveBoard();
|
||||
std::string playerToSave = Player1 -> retrievePlayer();
|
||||
std::string botToSave = GameBot -> retrieveBot();
|
||||
std::string currentTurnToSave = symbolToStr(CurrentTurnColor);
|
||||
std::string winnerToSave = intToStr(Winner);
|
||||
|
||||
return "[" + idToSave + "]["
|
||||
+ boardToSave + "]["
|
||||
+ mode + "]["
|
||||
+ playerToSave + "]["
|
||||
+ botToSave + "]["
|
||||
+ currentTurnToSave + "]["
|
||||
+ winnerToSave + "]";
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Board.h"
|
||||
#include "Bot.h"
|
||||
#include "Player.h"
|
||||
#include <string>
|
||||
|
||||
struct SinglePlayerGame
|
||||
{
|
||||
int id;
|
||||
Board* GameBoard;
|
||||
std::string mode;
|
||||
Player* Player1;
|
||||
Bot* GameBot;
|
||||
char CurrentTurnColor;
|
||||
int Winner;
|
||||
|
||||
public:
|
||||
SinglePlayerGame(Board* GameBoard, Player* Player1, Bot* GameBot, char CurrentTurnColor);
|
||||
void start();
|
||||
std::string retrieveGame();
|
||||
|
||||
private:
|
||||
void end();
|
||||
void save();
|
||||
void changeTurn();
|
||||
};
|
||||
Reference in New Issue
Block a user