Initial commit on develop branch

This commit is contained in:
2026-04-17 20:07:57 +03:30
commit 9b68b97ae6
52 changed files with 3499 additions and 0 deletions
+422
View File
@@ -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 = ■
+35
View File
@@ -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();
};
+372
View File
@@ -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];
}
}
}
+26
View File
@@ -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);
};
+58
View File
@@ -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();
}
+24
View File
@@ -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();
};
+44
View File
@@ -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);
};
+16
View File
@@ -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();
};
+27
View File
@@ -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);
}
+14
View File
@@ -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();
};