From 9b68b97ae6945783aee5f6e1b7ab11a2c818b8f4 Mon Sep 17 00:00:00 2001 From: Ramtin_Jafari Date: Fri, 17 Apr 2026 20:07:57 +0330 Subject: [PATCH] Initial commit on develop branch --- Othello/Game.cpp | 83 +++ Othello/codes/ConfigModifier.cpp | 214 ++++++++ Othello/codes/ConfigModifier.h | 3 + Othello/codes/Database/Config.txt | 10 + Othello/codes/Database/ConfigManager.cpp | 201 ++++++++ Othello/codes/Database/ConfigManager.h | 21 + Othello/codes/Database/GameHistory.txt | 3 + Othello/codes/Database/GameHistoryManager.cpp | 391 ++++++++++++++ Othello/codes/Database/GameHistoryManager.h | 17 + Othello/codes/Database/GameLog.txt | 2 + Othello/codes/Database/GameLogManager.cpp | 77 +++ Othello/codes/Database/GameLogManager.h | 8 + Othello/codes/Database/tempo.txt | 3 + Othello/codes/GameMaster.cpp | 483 ++++++++++++++++++ Othello/codes/GameMaster.h | 3 + Othello/codes/GameReporter.cpp | 36 ++ Othello/codes/GameReporter.h | 3 + Othello/codes/GameReviewer.cpp | 63 +++ Othello/codes/GameReviewer.h | 5 + Othello/codes/Helper.cpp | 8 + Othello/codes/Helper.h | 3 + Othello/codes/Structures/Board.cpp | 422 +++++++++++++++ Othello/codes/Structures/Board.h | 35 ++ Othello/codes/Structures/Bot.cpp | 372 ++++++++++++++ Othello/codes/Structures/Bot.h | 26 + Othello/codes/Structures/Config.cpp | 58 +++ Othello/codes/Structures/Config.h | 24 + Othello/codes/Structures/Move.cpp | 44 ++ Othello/codes/Structures/Move.h | 16 + Othello/codes/Structures/MultiPlayerGame.cpp | 190 +++++++ Othello/codes/Structures/MultiPlayerGame.h | 26 + Othello/codes/Structures/Player.cpp | 27 + Othello/codes/Structures/Player.h | 14 + Othello/codes/Structures/SinglePlayerGame.cpp | 213 ++++++++ Othello/codes/Structures/SinglePlayerGame.h | 27 + Othello/codes/Utilities/GetSavedBoardSize.cpp | 10 + Othello/codes/Utilities/GetSavedBoardSize.h | 5 + Othello/codes/Utilities/IntSqrt.cpp | 10 + Othello/codes/Utilities/IntSqrt.h | 3 + Othello/codes/Utilities/IntToStr.cpp | 73 +++ Othello/codes/Utilities/IntToStr.h | 5 + Othello/codes/Utilities/Print.cpp | 7 + Othello/codes/Utilities/Print.h | 5 + Othello/codes/Utilities/StrToInt.cpp | 51 ++ Othello/codes/Utilities/StrToInt.h | 5 + Othello/codes/Utilities/StrToSymbol.cpp | 25 + Othello/codes/Utilities/StrToSymbol.h | 5 + Othello/codes/Utilities/SymbolToStr.cpp | 25 + Othello/codes/Utilities/SymbolToStr.h | 5 + .../Utilities/outputDecoratedSavedGame.cpp | 129 +++++ .../Utilities/outputDecoratedSavedGame.h | 5 + Othello/myprogram.exe | Bin 0 -> 262256 bytes 52 files changed, 3499 insertions(+) create mode 100644 Othello/Game.cpp create mode 100644 Othello/codes/ConfigModifier.cpp create mode 100644 Othello/codes/ConfigModifier.h create mode 100644 Othello/codes/Database/Config.txt create mode 100644 Othello/codes/Database/ConfigManager.cpp create mode 100644 Othello/codes/Database/ConfigManager.h create mode 100644 Othello/codes/Database/GameHistory.txt create mode 100644 Othello/codes/Database/GameHistoryManager.cpp create mode 100644 Othello/codes/Database/GameHistoryManager.h create mode 100644 Othello/codes/Database/GameLog.txt create mode 100644 Othello/codes/Database/GameLogManager.cpp create mode 100644 Othello/codes/Database/GameLogManager.h create mode 100644 Othello/codes/Database/tempo.txt create mode 100644 Othello/codes/GameMaster.cpp create mode 100644 Othello/codes/GameMaster.h create mode 100644 Othello/codes/GameReporter.cpp create mode 100644 Othello/codes/GameReporter.h create mode 100644 Othello/codes/GameReviewer.cpp create mode 100644 Othello/codes/GameReviewer.h create mode 100644 Othello/codes/Helper.cpp create mode 100644 Othello/codes/Helper.h create mode 100644 Othello/codes/Structures/Board.cpp create mode 100644 Othello/codes/Structures/Board.h create mode 100644 Othello/codes/Structures/Bot.cpp create mode 100644 Othello/codes/Structures/Bot.h create mode 100644 Othello/codes/Structures/Config.cpp create mode 100644 Othello/codes/Structures/Config.h create mode 100644 Othello/codes/Structures/Move.cpp create mode 100644 Othello/codes/Structures/Move.h create mode 100644 Othello/codes/Structures/MultiPlayerGame.cpp create mode 100644 Othello/codes/Structures/MultiPlayerGame.h create mode 100644 Othello/codes/Structures/Player.cpp create mode 100644 Othello/codes/Structures/Player.h create mode 100644 Othello/codes/Structures/SinglePlayerGame.cpp create mode 100644 Othello/codes/Structures/SinglePlayerGame.h create mode 100644 Othello/codes/Utilities/GetSavedBoardSize.cpp create mode 100644 Othello/codes/Utilities/GetSavedBoardSize.h create mode 100644 Othello/codes/Utilities/IntSqrt.cpp create mode 100644 Othello/codes/Utilities/IntSqrt.h create mode 100644 Othello/codes/Utilities/IntToStr.cpp create mode 100644 Othello/codes/Utilities/IntToStr.h create mode 100644 Othello/codes/Utilities/Print.cpp create mode 100644 Othello/codes/Utilities/Print.h create mode 100644 Othello/codes/Utilities/StrToInt.cpp create mode 100644 Othello/codes/Utilities/StrToInt.h create mode 100644 Othello/codes/Utilities/StrToSymbol.cpp create mode 100644 Othello/codes/Utilities/StrToSymbol.h create mode 100644 Othello/codes/Utilities/SymbolToStr.cpp create mode 100644 Othello/codes/Utilities/SymbolToStr.h create mode 100644 Othello/codes/Utilities/outputDecoratedSavedGame.cpp create mode 100644 Othello/codes/Utilities/outputDecoratedSavedGame.h create mode 100644 Othello/myprogram.exe diff --git a/Othello/Game.cpp b/Othello/Game.cpp new file mode 100644 index 0000000..2d804ef --- /dev/null +++ b/Othello/Game.cpp @@ -0,0 +1,83 @@ +#include "codes/Database/GameHistoryManager.h" +#include "codes/Structures/Config.h" +#include "codes/ConfigModifier.h" +#include "codes/GameMaster.h" +#include "codes/GameReporter.h" +#include "codes/GameReviewer.h" +#include "codes/Helper.h" +#include +#include +#include + +int main() +{ + system("cls"); + + Config::initialize(); + syncGameHistoryWithLimit(); + + int chosenOption = 0; + + while (true) + { + system("cls"); + + std::cout << "Welcome to Othello game!" << std::endl + << (chosenOption == 0 ? " > ":" ") << " new game" << std::endl + << (chosenOption == 1 ? " > ":" ") << " load game" << std::endl + << (chosenOption == 2 ? " > ":" ") << " help" << std::endl + << (chosenOption == 3 ? " > ":" ") << " games history" << std::endl + << (chosenOption == 4 ? " > ":" ") << " settings" << std::endl + << (chosenOption == 5 ? " > ":" ") << " exit" << std::endl; + + int userInput = getch(); + + if (userInput == static_cast('w') || userInput == 72) + { + if (chosenOption > 0) + { + chosenOption--; + continue; + } + } + + if (userInput == static_cast('s') || userInput == 80) + { + if (chosenOption < 5) + { + chosenOption++; + continue; + } + } + + if (userInput == 13) + { + if (chosenOption == 0) + { + executeGameMaster(false); + } + else if (chosenOption == 1) + { + executeGameMaster(true); + } + else if (chosenOption == 2) + { + executeHelper(); + } + else if (chosenOption == 3) + { + executeGameReporter(); + } + else if (chosenOption == 4) + { + executeConfigModifier(); + } + else if (chosenOption == 5) + { + break; + } + } + } + + return 0; +} \ No newline at end of file diff --git a/Othello/codes/ConfigModifier.cpp b/Othello/codes/ConfigModifier.cpp new file mode 100644 index 0000000..48a96b6 --- /dev/null +++ b/Othello/codes/ConfigModifier.cpp @@ -0,0 +1,214 @@ +#include "ConfigModifier.h" +#include "Database/ConfigManager.h" +#include "Database/GameHistoryManager.h" +#include +#include +#include +#include + + + +bool madeChanges = false; + + + +void handleGameHistoryLimit(); +void handleBoardSize(); +void handlShowAvailablePlacesToPieces(); + + + +void executeConfigModifier() +{ + int chosenOption = 0; + + while (true) + { + system("cls"); + + std::cout << "current setting are:" << std::endl + << "The number of games inside game history: " << getGameHistoryLimit() << std::endl + << "The size of the board: " << getBoardSize() << std::endl + << "Show available places for pieces: " << (getShowAvailablePlacesForPieces() ? "Yes":"No") << std::endl << std::endl; + + std::cout << "Which of these properties do you wish to change?" << std::endl; + + std::cout << (chosenOption == 0 ? " >":" ") << " The number of games inside game history" << std::endl + << (chosenOption == 1 ? " >":" ") << " The size of the board" << std::endl + << (chosenOption == 2 ? " >":" ") << " Showing avaibale places for pieces" << std::endl + << (chosenOption == 3 ? " >":" ") << " Apply the changes and exit" << std::endl; + + int userInput = getch(); + + if (userInput == static_cast('w') || userInput == 72) + { + if (chosenOption > 0) + { + chosenOption--; + continue; + } + } + + if (userInput == static_cast('s') || userInput == 80) + { + if (chosenOption < 3) + { + chosenOption++; + continue; + } + } + + if (userInput == 13) + { + if (chosenOption == 0) + { + handleGameHistoryLimit(); + } + else if (chosenOption == 1) + { + handleBoardSize(); + } + else if (chosenOption == 2) + { + handlShowAvailablePlacesToPieces(); + } + else if (chosenOption == 3) + { + break; + } + } + } + + system("cls"); + + if (madeChanges == false) std::cout << "No changes has been made\nPress any key to continue"; + else if (madeChanges == true) std::cout << "Please restart the game for changes to be applied to the game\nPress any key to continue"; + + getch(); + system("cls"); + return; +} + + + +void handleGameHistoryLimit() +{ + system("cls"); + + std::cout << "the current value of the number of games inside game history is " + << getGameHistoryLimit() << std::endl + << "Please set the new value: " << std::endl; + + int newValue; + + std::cin >> newValue; + + if (getGameHistoryLimit() == newValue) + { + return handleGameHistoryLimit(); + } + + if (newValue > 100) + { + std::cout << "Inputed number is too big, you might face slow loading when recieving reports or loading games" + << std::endl << "Do you procced? [Y/N]" << std::endl; + + char procceded; + std::cin >> procceded; + + if (!(procceded == 'Y' || procceded == 'y')) + { + return handleGameHistoryLimit(); + } + } + + modifyGameHistoryLimit(newValue); + syncGameHistoryWithLimit(); + std::cout << "Change has been made\nPress any key to continue"; + getch(); + + return; +} + + + +void handleBoardSize() +{ + system("cls"); + + std::cout << "the current value of the size of the board is " + << getBoardSize() << std::endl + << "Please set the new value: " << std::endl; + + int newValue; + + std::cin >> newValue; + + if (getBoardSize() == newValue) + { + return handleBoardSize(); + } + + if (newValue > 50) + { + std::cout << "Inputed number is too big, please set a number lower than or equal to 50\nPress any key to continue" << std::endl; + + getch(); + + return handleBoardSize(); + } + + if (newValue % 2 == 1) + { + std::cout << "Board size cannot be an odd number, please set an even number\nPress any key to continue"; + + getch(); + + return handleBoardSize(); + } + + modifyBoardSize(newValue); + std::cout << "Change has been made\nPress any key to continue"; + getch(); + + return; +} + + + +void handlShowAvailablePlacesToPieces() +{ + system("cls"); + + bool curValue = getShowAvailablePlacesForPieces(); + + std::cout << (curValue ? + "Currently, you will see available places for putting your piece durning the game": + "Currently, you won't see available places for putting your piece durning the game" + ) << std::endl; + + std::cout << "Do you proceed to change the behavour? [Y/N]" << std::endl; + + char procceded; + std::cin >> procceded; + + if (!(procceded == 'Y' || procceded == 'y')) + { + return; + } + + madeChanges = true; + if (curValue) + { + modifyShowAvailablePlacesForPieces(false); + } + else + { + modifyShowAvailablePlacesForPieces(true); + } + + std::cout << "Change has been made\nPress any key to continue"; + getch(); + + return; +} diff --git a/Othello/codes/ConfigModifier.h b/Othello/codes/ConfigModifier.h new file mode 100644 index 0000000..9cddb1a --- /dev/null +++ b/Othello/codes/ConfigModifier.h @@ -0,0 +1,3 @@ +#pragma once + +void executeConfigModifier(); \ No newline at end of file diff --git a/Othello/codes/Database/Config.txt b/Othello/codes/Database/Config.txt new file mode 100644 index 0000000..3d4652b --- /dev/null +++ b/Othello/codes/Database/Config.txt @@ -0,0 +1,10 @@ +{ + GAME_HISTORY_LIMIT = 100 + BOARD_SIZE = 8 + BOARD_COLOR = black + BOARD_BORDER_COLOR = white + SOUND = 0 + SHOW_AVAILABLE_PLACES_FOR_PIECES = 1 + HINT = 0 + HINT_LIMIT = 3 +} diff --git a/Othello/codes/Database/ConfigManager.cpp b/Othello/codes/Database/ConfigManager.cpp new file mode 100644 index 0000000..a5877dc --- /dev/null +++ b/Othello/codes/Database/ConfigManager.cpp @@ -0,0 +1,201 @@ +#include +#include +#include "../Utilities/StrToInt.h" +#include "../utilities/IntToStr.h" +#include "ConfigManager.h" + +using namespace std; + + + +string getElementOfConfig(int index); +int getGameHistoryLimit(); +int getBoardSize(); +std::string getBoardColor(); +std::string getBoardBorderColor(); +bool getSound(); +bool getShowAvailablePlacesForPieces(); +bool getHint(); +int getHintLimit(); +void modifyElementOfConfig(int index, string newValue); +void modifyGameHistoryLimit(int); +void modifyBoardSize(int); +void modifyBoardColor(std::string); +void modifyBoardBorderColor(std::string); +void modifySound(bool); +void modifyShowAvailablePlacesForPieces(bool); +void modifyHint(bool); +void modifyHintLimit(int); + + + +string getElementOfConfig(int index) +{ + if (index > 7) + { + throw 2; // out of bond + } + + ifstream file ("codes/Database/Config.txt"); + if (!file.is_open()) + { + throw 1; // failed to read the file + } + + string line; + + for (int i{0}; i <= index + 1; i++) + { + getline(file, line); + } + + int startingIndex = line.find("=") + 2; + string result = line.substr(startingIndex, line.size()); + + file.close(); + + return result; +} + + +void modifyElementOfConfig(int index, string newValue) +{ + if (index > 7) + { + throw 2; // out of bounds + } + + std::ifstream fileToRead("codes/Database/Config.txt"); + if (!fileToRead.is_open()) + { + throw 1; // failed to read the file + } + + std::string lines[10]; + + for (int i{0}; i < 10; i++) + { + getline(fileToRead, lines[i]); + } + + fileToRead.close(); + + string targetLine = lines[index + 1]; + int startingIndex = targetLine.find("=") + 2; + targetLine = targetLine.substr(0, startingIndex) + newValue; + lines[index + 1] = targetLine; + + std::ofstream fileToWrite ("codes/Database/Config.txt"); + if (!fileToWrite.is_open()) + { + throw 1; // failed to read the file + } + + for (std::string l : lines) + { + fileToWrite << l << "\n"; + } + fileToWrite.close(); +} + + +int getGameHistoryLimit() +{ + return strToInt(getElementOfConfig(0)); +} + + +int getBoardSize() +{ + return strToInt(getElementOfConfig(1)); +} + + +string getBoardColor() +{ + return getElementOfConfig(2); +} + + +string getBoardBorderColor() +{ + return getElementOfConfig(3); +} + + +bool getSound() +{ + int intBool = strToInt(getElementOfConfig(4)); + return static_cast(intBool); +} + + +bool getShowAvailablePlacesForPieces() +{ + int intBool = strToInt(getElementOfConfig(5)); + return static_cast(intBool); +} + + +bool getHint() +{ + int intBool = strToInt(getElementOfConfig(6)); + return static_cast(intBool); +} + + +int getHintLimit() +{ + return strToInt(getElementOfConfig(7)); +} + + +void modifyGameHistoryLimit(int gameHistoryLimit) +{ + modifyElementOfConfig(0, intToStr(gameHistoryLimit)); +} + + +void modifyBoardSize(int boardSize) +{ + modifyElementOfConfig(1, intToStr(boardSize)); +} + + +void modifyBoardColor(string color) +{ + modifyElementOfConfig(2, color); +} + + +void modifyBoardBorderColor(string color) +{ + modifyElementOfConfig(3, color); +} + + +void modifySound(bool sound) +{ + int soundToSave = static_cast(sound); + modifyElementOfConfig(4, intToStr(soundToSave)); +} + + +void modifyShowAvailablePlacesForPieces(bool newValue) +{ + int newValueToSave = static_cast(newValue); + modifyElementOfConfig(5, intToStr(newValueToSave)); +} + + +void modifyHint(bool hint) +{ + int hintToSave = static_cast(hint); + modifyElementOfConfig(6, intToStr(hintToSave)); +} + + +void modifyHintLimit(int hintLimit) +{ + modifyElementOfConfig(7, intToStr(hintLimit)); +} \ No newline at end of file diff --git a/Othello/codes/Database/ConfigManager.h b/Othello/codes/Database/ConfigManager.h new file mode 100644 index 0000000..2a6863e --- /dev/null +++ b/Othello/codes/Database/ConfigManager.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +int getGameHistoryLimit(); +int getBoardSize(); +std::string getBoardColor(); +std::string getBoardBorderColor(); +bool getSound(); +bool getShowAvailablePlacesForPieces(); +bool getHint(); +int getHintLimit(); + +void modifyGameHistoryLimit(int); +void modifyBoardSize(int); +void modifyBoardColor(std::string); +void modifyBoardBorderColor(std::string); +void modifySound(bool); +void modifyShowAvailablePlacesForPieces(bool); +void modifyHint(bool); +void modifyHintLimit(int); \ No newline at end of file diff --git a/Othello/codes/Database/GameHistory.txt b/Othello/codes/Database/GameHistory.txt new file mode 100644 index 0000000..8d5cc42 --- /dev/null +++ b/Othello/codes/Database/GameHistory.txt @@ -0,0 +1,3 @@ +[0][.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|W|B|.|.|.|.|.|.|W|B|B|.|.|.|.|.|W|B|W|.|.|.|.|.|B|.|.|.|.|.|.|B|W|.|.|.|.|.|.|.|.|.|.|.|.|.][2Player][ramtin|W][farzad|W][W][0] +[1][.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|W|B|.|.|.|.|.|B|B|B|B|.|.|.|.|W|W|W|W|.|.|.|.|.|.|B|B|W|.|.|.|.|W|B|B|.|.|.|.|.|.|.|.|.|.|.][2Player][ramtin|W][farzad|B][B][0] +[2][W|W|W|W|W|W|W|W|W|B|B|B|B|W|B|W|W|W|W|W|B|B|W|W|W|B|W|W|W|W|B|W|W|B|W|W|W|W|B|W|W|W|W|W|B|W|W|W|W|B|B|B|W|W|W|W|W|W|W|W|W|W|B|W][1Player][ramtin|B][Hard Bot|3][W][2] diff --git a/Othello/codes/Database/GameHistoryManager.cpp b/Othello/codes/Database/GameHistoryManager.cpp new file mode 100644 index 0000000..2133f72 --- /dev/null +++ b/Othello/codes/Database/GameHistoryManager.cpp @@ -0,0 +1,391 @@ +// a saved game will follow this patter: +// [gameId][.|.|.|.][1Player][PlayerName|PlayerColor][BotName or PlayerName|BotColor or PlayerColor]['B'][0] and winner at the end +// GameBoard mode Player1 Player2 or Bot current turn + +#include "../Structures/Config.h" +#include "../Structures/SinglePlayerGame.h" +#include "../Structures/MultiPlayerGame.h" +#include "../Utilities/StrToInt.h" +#include "../Utilities/IntSqrt.h" +#include "../Utilities/StrToSymbol.h" +#include "GameHistoryManager.h" +#include + +// file managing +void writeTempoToGameHistory(); +void updateLine(int lineIndex, std::string str); +void addLine(std::string str); +void freeLastLine(); + +// internal uses +int getLinesCount(); +std::string getProperty(int propertyId, std::string game); +int getGameId(std::string game); +int getSavedBoardElementCount(std::string board); + +// adding data +void saveGame(SinglePlayerGame* game); +void saveGame(MultiPlayerGame* game); + +// extracting data +std::string getGameById(int gameId); +int getLineIndexById(int gameId); +std::string getLineByLineIndex(int lineIndex); + +// external uses +SinglePlayerGame getSinglePlayerGame(int id); +MultiPlayerGame getMultiPlayerGame(int id); +void syncGameHistoryWithLimit(); +int getFirstGameId(); +int getLastGameId(); + + +//----------------------------------- file managing -------------------------------------------------------------- +void writeTempoToGameHistory() +{ + std::ifstream tempoFileToRead("codes/Database/tempo.txt"); + std::ofstream GameHistoryToWrite("codes/Database/GameHistory.txt"); + + if (!GameHistoryToWrite.is_open() && !tempoFileToRead.is_open()) + { + throw 1; // faild to read file + } + + std::string line; + + while (getline(tempoFileToRead, line)) + { + GameHistoryToWrite << line << "\n"; + } + + tempoFileToRead.close(); + GameHistoryToWrite.close(); +} + + + +void freeLastLine() +{ + std::ifstream GameHistoryToRead("codes/Database/GameHistory.txt"); + std::ofstream tempoFileToWrite("codes/Database/tempo.txt"); + + if (!GameHistoryToRead.is_open() && !tempoFileToWrite.is_open()) + { + throw 1; // faild to read file + } + + std::string line; + + getline(GameHistoryToRead, line); // skipping the first line + + while (getline(GameHistoryToRead, line)) + { + tempoFileToWrite << line << "\n"; + } + + tempoFileToWrite.close(); + GameHistoryToRead.close(); + + writeTempoToGameHistory(); +} + + + +void updateLine(int lineIndex, std::string str) +{ + std::ifstream GameHistoryToRead("codes/Database/GameHistory.txt"); + std::ofstream tempoFileToWrite("codes/Database/tempo.txt"); + + if (!GameHistoryToRead.is_open() && !tempoFileToWrite.is_open()) + { + throw 1; // faild to read file + } + + int curLineIndex = 0; + std::string line; + + while (getline(GameHistoryToRead, line)) + { + if (curLineIndex == lineIndex) + { + tempoFileToWrite << str << "\n"; + } + else + { + tempoFileToWrite << line << "\n"; + } + curLineIndex++; + } + + tempoFileToWrite.close(); + GameHistoryToRead.close(); + + writeTempoToGameHistory(); +} + + +void addLine(std::string str) +{ + std::ofstream GameHistory("codes/Database/GameHistory.txt", std::ios::app); + + GameHistory << str << "\n"; + + GameHistory.close(); +} + + + +//----------------------------------- internal use ----------------------------------------------- +int getLinesCount() +{ + int count = 0; + + std::string line; + std::ifstream fileToRead("codes/Database/GameHistory.txt"); + + if (!fileToRead.is_open()) + { + throw 1; // faild to read file + } + + while (getline(fileToRead, line)) + { + if (!line.empty()) count++; + } + + return count; +} + + +std::string getProperty(int propertyId, std::string game) +{ + for (int i = 0; i < propertyId; i++) + { + int endingIndex = game.find("]"); + game = game.substr(endingIndex + 1, game.size()); + } + + int EndingIndex = game.find("]"); + std::string property = game.substr( + 1, EndingIndex - 1 + ); + + return property; +} + + +int getGameId(std::string game) +{ + return strToInt(getProperty(0, game)); +} + + +void syncGameHistoryWithLimit() +// has to be called when limit changes, so GameHistory.txt stays within its new limit +{ + int curCount = getLinesCount(); + int limit = Config::getInstance() -> GAME_HISTORY_LIMIT; + + if (curCount > limit) + { + int toRemove = curCount - limit; + + for (int i = 0; i < toRemove; i++) + { + freeLastLine(); + } + } +} + + +int getSavedBoardElementCount(std::string board) +{ + int count = 0; + + for (char element : board) + { + if (element == '|') count++; + } + + return count + 1; +} + + +//--------------------------------------------- adding data -------------------------------------------------- +void saveGame(SinglePlayerGame* game) +{ + int thisGameId = game -> id; + std::string gameToSave = game -> retrieveGame(); + + int lineIndex = getLineIndexById(thisGameId); + + if (lineIndex == -1) + { + addLine(gameToSave); + syncGameHistoryWithLimit(); + } + else + { + updateLine(lineIndex, gameToSave); + } +} + + +void saveGame(MultiPlayerGame* game) +{ + int thisGameId = game -> id; + std::string gameToSave = game -> retrieveGame(); + + int lineIndex = getLineIndexById(thisGameId); + + if (lineIndex == -1) + { + addLine(gameToSave); + syncGameHistoryWithLimit(); + } + else + { + updateLine(lineIndex, gameToSave); + } +} + +//--------------------------------------------- extraxting data -------------------------------------------------- +std::string getGameById(int gameId) +{ + std::string line; + std::ifstream fileToRead("codes/Database/GameHistory.txt"); + + if (!fileToRead.is_open()) + { + throw 1; // faild to read file + } + + while (getline(fileToRead, line)) + { + if (getGameId(line) == gameId) + { + return line; + } + } + + return ""; +} + + +int getLineIndexById(int gameId) +{ + std::string line; + std::ifstream fileToRead("codes/Database/GameHistory.txt"); + + if (!fileToRead.is_open()) + { + throw 1; // faild to read file + } + + int id = 0; + while (getline(fileToRead, line)) + { + if (getGameId(line) == gameId) + { + return id; + } + id++; + } + + return -1; +} + + +std::string getLineByLineIndex(int lineIndex) +{ + std::string line; + std::ifstream fileToRead("codes/Database/GameHistory.txt"); + + if (!fileToRead.is_open()) + { + throw 1; // faild to read file + } + + int linesCount = getLinesCount(); + for (int i = 0; i < linesCount; i++) + { + getline(fileToRead, line); + if (i == lineIndex) return line; + } + + return ""; +} +//-------------------------------------------- external uses ----------------------------------------- +int getFirstGameId() +{ + int liensCount = getLinesCount(); + + if (liensCount == 0) return -1; // file is empty + + std::string game = getLineByLineIndex(0); + return getGameId(game); +} + +int getLastGameId() +{ + int liensCount = getLinesCount(); + + if (liensCount == 0) return -1; // file is empty + + std::string game = getLineByLineIndex(liensCount - 1); + return getGameId(game); +} + + +SinglePlayerGame getSinglePlayerGame(int id) +{ + std::string savedGame = getGameById(id); + + std::string savedBoard = getProperty(1, savedGame); + int boardSize = intSqrt( + getSavedBoardElementCount(savedBoard) + ); + Board board {boardSize}; + board.loadBoard(savedBoard); + + Player player {"", 0}; + player.loadPlayer(getProperty(3, savedGame)); + + Bot bot {"", 0}; + bot.loadBot(getProperty(4, savedGame)); + + char turn = strToSymbol(getProperty(5, savedGame)); + + SinglePlayerGame game {&board, &player, &bot, turn}; + + game.id = id; + + return game; +} + + +MultiPlayerGame getMultiPlayerGame(int id) +{ + std::string savedGame = getGameById(id); + + std::string savedBoard = getProperty(1, savedGame); + int boardSize = intSqrt( + getSavedBoardElementCount(savedBoard) + ); + Board board {boardSize}; + board.loadBoard(savedBoard); + + Player player1 {"", 0}; + player1.loadPlayer(getProperty(3, savedGame)); + + Player player2 {"", 0}; + player2.loadPlayer(getProperty(4, savedGame)); + + char turn = strToSymbol(getProperty(5, savedGame)); + + MultiPlayerGame game {&board, &player1, &player2, turn}; + + game.id = id; + + return game; +} \ No newline at end of file diff --git a/Othello/codes/Database/GameHistoryManager.h b/Othello/codes/Database/GameHistoryManager.h new file mode 100644 index 0000000..432fc2f --- /dev/null +++ b/Othello/codes/Database/GameHistoryManager.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include "../Structures/MultiPlayerGame.h" +#include "../Structures/SinglePlayerGame.h" + +void saveGame(SinglePlayerGame* game); +void saveGame(MultiPlayerGame* game); +SinglePlayerGame getSinglePlayerGame(int id); +MultiPlayerGame getMultiPlayerGame(int id); +std::string getGameById(int gameId); +void syncGameHistoryWithLimit(); +std::string getProperty(int propertyId, std::string game); +int getSavedBoardElementCount(std::string board); +int getGameId(std::string game); +int getFirstGameId(); +int getLastGameId(); \ No newline at end of file diff --git a/Othello/codes/Database/GameLog.txt b/Othello/codes/Database/GameLog.txt new file mode 100644 index 0000000..f9a009e --- /dev/null +++ b/Othello/codes/Database/GameLog.txt @@ -0,0 +1,2 @@ +1 3 B +1 4 W diff --git a/Othello/codes/Database/GameLogManager.cpp b/Othello/codes/Database/GameLogManager.cpp new file mode 100644 index 0000000..6ee0251 --- /dev/null +++ b/Othello/codes/Database/GameLogManager.cpp @@ -0,0 +1,77 @@ +#include "../Structures/Move.h" +#include +#include + + + +void clearGameLog(); +void addLog(Move move); +int countLogs(); +Move getLog(int logIndex); + + + +void clearGameLog() +{ + std::ofstream file ("codes/Database/GameLog.txt", std::ios::trunc); + if (!file.is_open()) + { + throw 1; // failed to read file + } + + file.close(); +} + + +void addLog(Move move) +{ + std::ofstream file("codes/Database/GameLog.txt", std::ios::app); + if (!file.is_open()) + { + throw 1; // failed to read file + } + + file << move.retrieveMove() << "\n"; + + file.close(); +} + + +int countLogs() +{ + int logCount = 0; + std::ifstream file("codes/Database/GameLog.txt"); + std::string line; + + while(std::getline(file, line)) + { + if (line.empty()) continue; + + logCount++; + } + + return logCount; +} + + +Move getLog(int logIndex) +{ + std::ifstream file ("codes/Database/GameLog.txt"); + if (!file.is_open()) + { + throw 1; // failed to read the file + } + + std::string line; + + for (int i{0}; i <= logIndex; i++) + { + std::getline(file, line); + } + + Move move{0, 0, '0'}; + + move.loadMove(line); + + return move; +} \ No newline at end of file diff --git a/Othello/codes/Database/GameLogManager.h b/Othello/codes/Database/GameLogManager.h new file mode 100644 index 0000000..e4b8621 --- /dev/null +++ b/Othello/codes/Database/GameLogManager.h @@ -0,0 +1,8 @@ +#pragma once + +#include "../Structures/Move.h" + +void clearGameLog(); +void addLog(Move move); +int countLogs(); +Move getLog(int logIndex); \ No newline at end of file diff --git a/Othello/codes/Database/tempo.txt b/Othello/codes/Database/tempo.txt new file mode 100644 index 0000000..8d5cc42 --- /dev/null +++ b/Othello/codes/Database/tempo.txt @@ -0,0 +1,3 @@ +[0][.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|W|B|.|.|.|.|.|.|W|B|B|.|.|.|.|.|W|B|W|.|.|.|.|.|B|.|.|.|.|.|.|B|W|.|.|.|.|.|.|.|.|.|.|.|.|.][2Player][ramtin|W][farzad|W][W][0] +[1][.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|W|B|.|.|.|.|.|B|B|B|B|.|.|.|.|W|W|W|W|.|.|.|.|.|.|B|B|W|.|.|.|.|W|B|B|.|.|.|.|.|.|.|.|.|.|.][2Player][ramtin|W][farzad|B][B][0] +[2][W|W|W|W|W|W|W|W|W|B|B|B|B|W|B|W|W|W|W|W|B|B|W|W|W|B|W|W|W|W|B|W|W|B|W|W|W|W|B|W|W|W|W|W|B|W|W|W|W|B|B|B|W|W|W|W|W|W|W|W|W|W|B|W][1Player][ramtin|B][Hard Bot|3][W][2] diff --git a/Othello/codes/GameMaster.cpp b/Othello/codes/GameMaster.cpp new file mode 100644 index 0000000..6b1f55d --- /dev/null +++ b/Othello/codes/GameMaster.cpp @@ -0,0 +1,483 @@ +#include +#include +#include +#include +#include "GameMaster.h" +#include "Database/GameLogManager.h" +#include "GameReviewer.h" +#include "Database/GameHistoryManager.h" +#include "Structures/Board.h" +#include "Structures/Player.h" +#include "Structures/Bot.h" +#include "Structures/SinglePlayerGame.h" +#include "Structures/MultiPlayerGame.h" +#include "Structures/Config.h" +#include "Utilities/outputDecoratedSavedGame.h" +#include "Utilities/GetSavedBoardSize.h" +#include + +void handleLoadGame(); +void handleNewGame(); +void loadSinglePlayerGame(int gameId); +void loadMultiPlayerGame(int gameId); +void handleSinglePlayerNewGame(); +void handleMultiPlayerNewGame(); + +void executeGameMaster(bool loadGameMode) +{ + clearGameLog(); // emptying GameLog for the new game + + if (loadGameMode) handleLoadGame(); + else handleNewGame(); +} + + + +void handleLoadGame() +{ + system("cls"); + + std::cout << "You've chosen to load a previous unfinished game\n" + << "Previous unfunished games are: \n"; + + int startingGameId = getFirstGameId(); + int lastGameId = getLastGameId(); + + if (startingGameId == -1) + { + std::cout << "\n There are no games inside game history\n Press any key to continue"; + getch(); + return; + } + + for (int i = startingGameId; i <= lastGameId; i++) + { + if (getProperty(6, getGameById(i)) == "0") + { + outputDecoratedSavedGame(getGameById(i)); + } + } + + int gameId; + std::string wantedGame; + while (true) + { + std::cout << "\n Please type the id of the game you wish to continue playing: "; + + std::cin >> gameId; + + std::string savedGame = getGameById(gameId); + + if (getProperty(6, savedGame) != "0") + { + std::cout << "The id entered belong to a finished game, please choose an id from the given list\n"; + continue; + } + + wantedGame = savedGame; + break; + } + + if (getProperty(2, wantedGame) == "1Player") + { + loadSinglePlayerGame(gameId); + } + else + { + loadMultiPlayerGame(gameId); + } +} + + + +void loadSinglePlayerGame(int gameId) +{ + int id = getSinglePlayerGame(gameId).id; + Board board = *(getSinglePlayerGame(gameId).GameBoard); + Player player = *(getSinglePlayerGame(gameId).Player1); + Bot bot = *(getSinglePlayerGame(gameId).GameBot); + char CurrentTurnColor = getSinglePlayerGame(gameId).CurrentTurnColor; + + SinglePlayerGame game {&board, &player, &bot, CurrentTurnColor}; + game.id = id; + game.Winner = 0; + game.mode = "1Player"; + + game.start(); + + std::cout << "\n The game has finished, Press any key to continue" << std::endl; + + getch(); + + game.GameBoard -> deleteBoardMemory(); +} + + + +void loadMultiPlayerGame(int gameId) +{ + int id = getMultiPlayerGame(gameId).id; + Board board = *(getMultiPlayerGame(gameId).GameBoard); + Player player1 = *(getMultiPlayerGame(gameId).Player1); + Player player2 = *(getMultiPlayerGame(gameId).Player2); + char CurrentTurnColor = getMultiPlayerGame(gameId).CurrentTurnColor; + + MultiPlayerGame game {&board, &player1, &player2, CurrentTurnColor}; + game.id = id; + game.Winner = 0; + game.mode = "2Player"; + + game.start(); + + std::cout << "\n The game has finished, Press any key to continue" << std::endl; + + getch(); + + game.GameBoard -> deleteBoardMemory(); +} + + + +void handleNewGame() +{ + int chosenOption = 0; + while (true) + { + system("cls"); + + std::cout << (chosenOption == 0 ? " > ":" ") << " Single Player" << std::endl + << (chosenOption == 1 ? " > ":" ") << " MultiPlayer" << std::endl; + + int userInput = getch(); + + if (userInput == static_cast('w') || userInput == 72) + { + if (chosenOption == 1) + { + chosenOption--; + continue; + } + } + + if (userInput == static_cast('s') || userInput == 80) + { + if (chosenOption == 0) + { + chosenOption++; + continue; + } + } + + if (userInput == 13) + { + if (chosenOption == 0) + { + handleSinglePlayerNewGame(); + } + else if (chosenOption == 1) + { + handleMultiPlayerNewGame(); + } + break; + } + } +} + + + +void handleSinglePlayerNewGame() +{ + Bot bot{"0", 0}; + + int chosenOption = 0; + while (true) + { + system("cls"); + + std::cout << "Please choose the difficulty: \n" + << (chosenOption == 0 ? " > ":" ") << " Easy" << std::endl + << (chosenOption == 1 ? " > ":" ") << " Medium" << std::endl + << (chosenOption == 2 ? " > ":" ") << " Hard" << std::endl; + + int userInput = getch(); + + if (userInput == static_cast('w') || userInput == 72) + { + if (chosenOption != 0) + { + chosenOption--; + continue; + } + } + + if (userInput == static_cast('s') || userInput == 80) + { + if (chosenOption != 2) + { + chosenOption++; + continue; + } + } + + if (userInput == 13) + { + if (chosenOption == 0) + { + bot.difficulty = 1; + bot.name = "Easy Bot"; + } + else if (chosenOption == 1) + { + bot.difficulty = 2; + bot.name = "Medium Bot"; + } + else if (chosenOption == 2) + { + bot.difficulty = 3; + bot.name = "Hard Bot"; + } + break; + } + } + + Player player{"0", 'W'}; + while (true) + { + system("cls"); + + std::string name; + std::cout << "Please set your name: "; + std::cin >> name; + std::cout << std::endl; + + if (name.find("|") != -1 || name.find("[") != -1 || name.find("]") != -1) + { + std::cout << "One day you will die" + << "\nSome poeple will attend your funeral only for the food" + << "\nYour friends will hang out again after 1 month" + << "\nYour kids will go to work casually after 2 months" + << "\nYour wife/husband will laugh with a comedy movie after 6 month" + << "\nEventually, your name will be forgotten and memories of you fade away" + << "\nYet some poeple like you think they can escape this fact by putting | or [ or ] inside their names" + << "\nYou guys think noone is going to forget your name because they be like:" + << "\n Damn bro this guy has a | or [ or ] inside their name! I'm going to remember this name till my last breath" + << "\nLet's be real, nobody will say that unless they're high and high poeple can't remember anything more than 5 seconds" + << "\nSo stop annoying me and choose a name without | or [ or ]" + << "\nNow click any freaking key to continue..."; + + getch(); + continue; + } + + player.name = name; + + system("cls"); + break; + } + + chosenOption = 0; + while (true) + { + system("cls"); + + std::cout << "Please choose your color\n" + << (chosenOption == 0 ? " > ":" ") << " Black" << std::endl + << (chosenOption == 1 ? " > ":" ") << " White" << std::endl; + + int userInput = getch(); + + if (userInput == static_cast('w') || userInput == 72) + { + if (chosenOption == 1) + { + chosenOption--; + continue; + } + } + + if (userInput == static_cast('s') || userInput == 80) + { + if (chosenOption == 0) + { + chosenOption++; + continue; + } + } + + if (userInput == 13) + { + if (chosenOption == 0) + { + player.color = 'B'; + } + else if (chosenOption == 1) + { + player.color = 'W'; + } + break; + } + } + + system("cls"); + + Board board{Config::instance -> BOARD_SIZE}; + + board.newGameSetup(); + + SinglePlayerGame game{&board, &player, &bot, 'B'}; + + game.start(); + + std::cout << "\n The game has finished, would you like to see the replay? [Y/N]" << std::endl; + + char procceded; + std::cin >> procceded; + + if (procceded == 'Y' || procceded == 'y') + { + executeGameReviewer(*game.GameBoard); + } + + game.GameBoard -> deleteBoardMemory(); +} + + + +void handleMultiPlayerNewGame() +{ + Player player1{"0", 'W'}; + while (true) + { + system("cls"); + + std::string name; + std::cout << "Player1, Please set your name: "; + std::cin >> name; + std::cout << std::endl; + + if (name.find("|") != -1 || name.find("[") != -1 || name.find("]") != -1) + { + std::cout << "One day you will die" + << "\nSome poeple will attend your funeral only for the food" + << "\nYour friends will hang out again after 1 month" + << "\nYour kids will go to work casually after 2 months" + << "\nYour wife/husband will laugh with a comedy movie after 6 month" + << "\nEventually, your name will be forgotten and memories of you fade away" + << "\nYet some poeple like you think they can escape this fact by putting | or [ or ] inside their names" + << "\nYou guys think noone is going to forget your name because they be like:" + << "\n Damn bro this guy has a | or [ or ] inside their name! I'm going to remember this name till my last breath" + << "\nLet's be real, nobody will say that unless they're high and high poeple can't remember anything more than 5 seconds" + << "\nSo stop annoying me and choose a name without | or [ or ]" + << "\nNow click any freaking key to continue..."; + + getch(); + continue; + } + + player1.name = name; + + system("cls"); + break; + } + + int chosenOption = 0; + while (true) + { + system("cls"); + + std::cout << "Player1, please choose your color\n" + << (chosenOption == 0 ? " > ":" ") << " Black" << std::endl + << (chosenOption == 1 ? " > ":" ") << " White" << std::endl; + + int userInput = getch(); + + if (userInput == static_cast('w') || userInput == 72) + { + if (chosenOption == 1) + { + chosenOption--; + continue; + } + } + + if (userInput == static_cast('s') || userInput == 80) + { + if (chosenOption == 0) + { + chosenOption++; + continue; + } + } + + if (userInput == 13) + { + if (chosenOption == 0) + { + player1.color = 'B'; + } + else if (chosenOption == 1) + { + player1.color = 'W'; + } + break; + } + } + + Player player2{"0", 'W'}; + while (true) + { + system("cls"); + + std::string name; + std::cout << "Player2, Please set your name: "; + std::cin >> name; + std::cout << std::endl; + + if (name.find("|") != -1 || name.find("[") != -1 || name.find("]") != -1) + { + std::cout << "One day you will die" + << "\nSome poeple will attend your funeral only for the food" + << "\nYour friends will hang out again after 1 month" + << "\nYour kids will go to work casually after 2 months" + << "\nYour wife/husband will laugh with a comedy movie after 6 month" + << "\nEventually, your name will be forgotten and memories of you fade away" + << "\nYet some poeple like you think they can escape this fact by putting | or [ or ] inside their names" + << "\nYou guys think noone is going to forget your name because they be like:" + << "\n Damn bro this guy has a | or [ or ] inside their name! I'm going to remember this name till my last breath" + << "\nLet's be real, nobody will say that unless they're high and high poeple can't remember anything more than 5 seconds" + << "\nSo stop annoying me and choose a name without | or [ or ]" + << "\nNow click any freaking key to continue..."; + + getch(); + continue; + } + + player2.name = name; + player2.color = (player1.color == 'W' ? 'B':'W'); + + system("cls"); + break; + } + + system("cls"); + + Board board{Config::getInstance() -> BOARD_SIZE}; + + board.newGameSetup(); + + MultiPlayerGame game{&board, &player1, &player2, 'B'}; + + game.start(); + + std::cout << "\n The game has finished, would you like to see the replay? [Y/N]" << std::endl; + + char procceded; + std::cin >> procceded; + + if (procceded == 'Y' || procceded == 'y') + { + executeGameReviewer(*game.GameBoard); + } + + game.GameBoard -> deleteBoardMemory(); +} \ No newline at end of file diff --git a/Othello/codes/GameMaster.h b/Othello/codes/GameMaster.h new file mode 100644 index 0000000..41e0b58 --- /dev/null +++ b/Othello/codes/GameMaster.h @@ -0,0 +1,3 @@ +#pragma once + +void executeGameMaster(bool loadGameMode); \ No newline at end of file diff --git a/Othello/codes/GameReporter.cpp b/Othello/codes/GameReporter.cpp new file mode 100644 index 0000000..30453c9 --- /dev/null +++ b/Othello/codes/GameReporter.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include "GameReporter.h" +#include "Structures/Config.h" +#include "DataBase/GameHistoryManager.h" +#include "Utilities/outputDecoratedSavedGame.h" + + +void executeGameReporter() +{ + system("cls"); + + std::cout << "Here is the history of the last " + << Config::getInstance() -> GAME_HISTORY_LIMIT + << " Played Games" << std::endl + << "If you wish to change the number of saved games inside the history, you can change this number inside settings section of the game"; + + int firstId = getFirstGameId(); + int lastId = getLastGameId(); + + if (firstId == -1) + { + std::cout << "\n There are no games inside game history\n Press any key to continue"; + getch(); + return; + } + + for (int id = lastId; id >= firstId; id--) + { + outputDecoratedSavedGame(getGameById(id)); + } + + std::cout << "\nPress any key to continue"; + getch(); +} \ No newline at end of file diff --git a/Othello/codes/GameReporter.h b/Othello/codes/GameReporter.h new file mode 100644 index 0000000..cd1b71a --- /dev/null +++ b/Othello/codes/GameReporter.h @@ -0,0 +1,3 @@ +#pragma once + +void executeGameReporter(); \ No newline at end of file diff --git a/Othello/codes/GameReviewer.cpp b/Othello/codes/GameReviewer.cpp new file mode 100644 index 0000000..7121f62 --- /dev/null +++ b/Othello/codes/GameReviewer.cpp @@ -0,0 +1,63 @@ +#include "GameReviewer.h" +#include "Database/GameLogManager.h" +#include "Structures/Move.h" +#include "Structures/Board.h" +#include "conio.h" +#include +#include + +void executeGameReviewer(Board board) +{ + board.CursorX = -1; + board.CursorY = -1; + + int logsCount = countLogs(); + + int logId = -1; + + while (true) + { + system("cls"); + + std::cout << "Press any key for next move, ESC to exit...\n"; + + + if (logId == -1) + { + board.newGameSetup(); + board.prepareBoardForMove('W'); + } + else + { + Move move = getLog(logId); + board.putPiece(move.x, move.y, move.color); + if (move.color == 'B') + { + board.prepareBoardForMove('W'); + } + else + { + board.prepareBoardForMove('B'); + } + } + + board.display(); + + int userInput = _getch(); + + if(userInput == 27) + { + break; + } + else + { + if (logId == logsCount - 1) + { + break; + } + logId++; + } + } + + system("cls"); +} \ No newline at end of file diff --git a/Othello/codes/GameReviewer.h b/Othello/codes/GameReviewer.h new file mode 100644 index 0000000..400cdcc --- /dev/null +++ b/Othello/codes/GameReviewer.h @@ -0,0 +1,5 @@ +#pragma once + +#include "Structures/Board.h" + +void executeGameReviewer(Board board); \ No newline at end of file diff --git a/Othello/codes/Helper.cpp b/Othello/codes/Helper.cpp new file mode 100644 index 0000000..d8f3823 --- /dev/null +++ b/Othello/codes/Helper.cpp @@ -0,0 +1,8 @@ +#include "Helper.h" +#include "Structures/Board.h" +#include + +void executeHelper() +{ + +} \ No newline at end of file diff --git a/Othello/codes/Helper.h b/Othello/codes/Helper.h new file mode 100644 index 0000000..9a69cd8 --- /dev/null +++ b/Othello/codes/Helper.h @@ -0,0 +1,3 @@ +#pragma once + +void executeHelper(); \ No newline at end of file diff --git a/Othello/codes/Structures/Board.cpp b/Othello/codes/Structures/Board.cpp new file mode 100644 index 0000000..907fce7 --- /dev/null +++ b/Othello/codes/Structures/Board.cpp @@ -0,0 +1,422 @@ +#include "Config.h" +#include "Board.h" +#include "Move.h" +#include "../Database/GameLogManager.h" +#include "../Utilities/SymbolToStr.h" +#include + + +//------------------------------- 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(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 = ■ \ No newline at end of file diff --git a/Othello/codes/Structures/Board.h b/Othello/codes/Structures/Board.h new file mode 100644 index 0000000..2976bb3 --- /dev/null +++ b/Othello/codes/Structures/Board.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +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(); +}; \ No newline at end of file diff --git a/Othello/codes/Structures/Bot.cpp b/Othello/codes/Structures/Bot.cpp new file mode 100644 index 0000000..5737ddf --- /dev/null +++ b/Othello/codes/Structures/Bot.cpp @@ -0,0 +1,372 @@ +#include "Bot.h" +#include "Board.h" +#include "Move.h" +#include "../Utilities/IntToStr.h" +#include "../Utilities/StrToInt.h" +#include +#include +#include + + + +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 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]; + } + } +} \ No newline at end of file diff --git a/Othello/codes/Structures/Bot.h b/Othello/codes/Structures/Bot.h new file mode 100644 index 0000000..aae85b0 --- /dev/null +++ b/Othello/codes/Structures/Bot.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#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); +}; \ No newline at end of file diff --git a/Othello/codes/Structures/Config.cpp b/Othello/codes/Structures/Config.cpp new file mode 100644 index 0000000..c429c14 --- /dev/null +++ b/Othello/codes/Structures/Config.cpp @@ -0,0 +1,58 @@ +#include "../Database/ConfigManager.h" +#include "Config.h" +#include +#include + + +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(); +} \ No newline at end of file diff --git a/Othello/codes/Structures/Config.h b/Othello/codes/Structures/Config.h new file mode 100644 index 0000000..47b0225 --- /dev/null +++ b/Othello/codes/Structures/Config.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +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(); +}; \ No newline at end of file diff --git a/Othello/codes/Structures/Move.cpp b/Othello/codes/Structures/Move.cpp new file mode 100644 index 0000000..49aee94 --- /dev/null +++ b/Othello/codes/Structures/Move.cpp @@ -0,0 +1,44 @@ +#include "Move.h" +#include "../Utilities/StrToInt.h" +#include "../Utilities/IntToStr.h" +#include "../Utilities/StrToSymbol.h" +#include "../Utilities/SymbolToStr.h" +#include + + +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); +}; diff --git a/Othello/codes/Structures/Move.h b/Othello/codes/Structures/Move.h new file mode 100644 index 0000000..e768ebd --- /dev/null +++ b/Othello/codes/Structures/Move.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +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); +}; \ No newline at end of file diff --git a/Othello/codes/Structures/MultiPlayerGame.cpp b/Othello/codes/Structures/MultiPlayerGame.cpp new file mode 100644 index 0000000..f3a8171 --- /dev/null +++ b/Othello/codes/Structures/MultiPlayerGame.cpp @@ -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 +#include +#include + + +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('w') || userInput == 72) + { + if (GameBoard -> CursorY != 0) + { + GameBoard -> CursorY--; + } + } + + else if (userInput == static_cast('s') || userInput == 80) + { + if (GameBoard -> CursorY != GameBoard -> BoardSize - 1) + { + GameBoard -> CursorY++; + } + } + + else if (userInput == static_cast('a') || userInput == 75) + { + if (GameBoard -> CursorX != 0) + { + GameBoard -> CursorX--; + } + } + + else if (userInput == static_cast('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 + "]"; +} diff --git a/Othello/codes/Structures/MultiPlayerGame.h b/Othello/codes/Structures/MultiPlayerGame.h new file mode 100644 index 0000000..9a72a43 --- /dev/null +++ b/Othello/codes/Structures/MultiPlayerGame.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Board.h" +#include "Player.h" +#include + +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(); +}; diff --git a/Othello/codes/Structures/Player.cpp b/Othello/codes/Structures/Player.cpp new file mode 100644 index 0000000..1df0c5b --- /dev/null +++ b/Othello/codes/Structures/Player.cpp @@ -0,0 +1,27 @@ +#include +#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); +} diff --git a/Othello/codes/Structures/Player.h b/Othello/codes/Structures/Player.h new file mode 100644 index 0000000..b6b8f51 --- /dev/null +++ b/Othello/codes/Structures/Player.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +struct Player +{ + std::string name; + char color; + +public: + Player(std::string name, char color); + std::string retrievePlayer(); + void loadPlayer(std::string player); +}; \ No newline at end of file diff --git a/Othello/codes/Structures/SinglePlayerGame.cpp b/Othello/codes/Structures/SinglePlayerGame.cpp new file mode 100644 index 0000000..06dcd3d --- /dev/null +++ b/Othello/codes/Structures/SinglePlayerGame.cpp @@ -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 +#include +#include + + +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('w') || userInput == 72) + { + if (GameBoard -> CursorY != 0) + { + GameBoard -> CursorY--; + } + } + + else if (userInput == static_cast('s') || userInput == 80) + { + if (GameBoard -> CursorY != GameBoard -> BoardSize - 1) + { + GameBoard -> CursorY++; + } + } + + else if (userInput == static_cast('a') || userInput == 75) + { + if (GameBoard -> CursorX != 0) + { + GameBoard -> CursorX--; + } + } + + else if (userInput == static_cast('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 + "]"; +} \ No newline at end of file diff --git a/Othello/codes/Structures/SinglePlayerGame.h b/Othello/codes/Structures/SinglePlayerGame.h new file mode 100644 index 0000000..17f11be --- /dev/null +++ b/Othello/codes/Structures/SinglePlayerGame.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Board.h" +#include "Bot.h" +#include "Player.h" +#include + +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(); +}; diff --git a/Othello/codes/Utilities/GetSavedBoardSize.cpp b/Othello/codes/Utilities/GetSavedBoardSize.cpp new file mode 100644 index 0000000..f30f7f9 --- /dev/null +++ b/Othello/codes/Utilities/GetSavedBoardSize.cpp @@ -0,0 +1,10 @@ +#include "../Database/GameHistoryManager.h" +#include "IntSqrt.h" +#include "GetSavedBoardSize.h" +#include + +int getSavedBoardSize(std::string board) +{ + int elementCount = getSavedBoardElementCount(board); + return intSqrt(elementCount); +} \ No newline at end of file diff --git a/Othello/codes/Utilities/GetSavedBoardSize.h b/Othello/codes/Utilities/GetSavedBoardSize.h new file mode 100644 index 0000000..c15cc16 --- /dev/null +++ b/Othello/codes/Utilities/GetSavedBoardSize.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +int getSavedBoardSize(std::string board); \ No newline at end of file diff --git a/Othello/codes/Utilities/IntSqrt.cpp b/Othello/codes/Utilities/IntSqrt.cpp new file mode 100644 index 0000000..ca5c3a0 --- /dev/null +++ b/Othello/codes/Utilities/IntSqrt.cpp @@ -0,0 +1,10 @@ +#include "IntSqrt.h" + +int intSqrt(int number) +{ + for (int i = 1; i < 100000000; i++) + { + if (i * i == number) return i; + } + return -1; +} \ No newline at end of file diff --git a/Othello/codes/Utilities/IntSqrt.h b/Othello/codes/Utilities/IntSqrt.h new file mode 100644 index 0000000..626ad82 --- /dev/null +++ b/Othello/codes/Utilities/IntSqrt.h @@ -0,0 +1,3 @@ +#pragma once + +int intSqrt(int); \ No newline at end of file diff --git a/Othello/codes/Utilities/IntToStr.cpp b/Othello/codes/Utilities/IntToStr.cpp new file mode 100644 index 0000000..7f54c04 --- /dev/null +++ b/Othello/codes/Utilities/IntToStr.cpp @@ -0,0 +1,73 @@ +#include +#include "IntToStr.h" + + + +char getChar(int digit); +int getDigitsCount(int number); + + + +std::string intToStr(int number) +{ + int numberLen = getDigitsCount(number); + + char result[numberLen]; + + for (int i = numberLen - 1; i >= 0; i--) + { + result[i] = getChar(number % 10); + number /= 10; + } + + std::string resultStr = ""; + + for (int i = 0; i < numberLen; i++) + { + resultStr += result[i]; + } + + return resultStr; +} + +char getChar(int digit) +{ + switch (digit) + { + case 0: + return '0'; + case 1: + return '1'; + case 2: + return '2'; + case 3: + return '3'; + case 4: + return '4'; + case 5: + return '5'; + case 6: + return '6'; + case 7: + return '7'; + case 8: + return '8'; + case 9: + return '9'; + } +} + +int getDigitsCount(int number) +{ + if (number == 0) return 1; + + int count = 0; + + while(number != 0) + { + number /= 10; + count++; + } + + return count; +} \ No newline at end of file diff --git a/Othello/codes/Utilities/IntToStr.h b/Othello/codes/Utilities/IntToStr.h new file mode 100644 index 0000000..db72168 --- /dev/null +++ b/Othello/codes/Utilities/IntToStr.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +std::string intToStr(int); \ No newline at end of file diff --git a/Othello/codes/Utilities/Print.cpp b/Othello/codes/Utilities/Print.cpp new file mode 100644 index 0000000..d443a9f --- /dev/null +++ b/Othello/codes/Utilities/Print.cpp @@ -0,0 +1,7 @@ +#include +#include "Print.h" + +void print(std::string subject, int count) +{ + for (int i = 0; i < count; i++) std::cout << subject; +} \ No newline at end of file diff --git a/Othello/codes/Utilities/Print.h b/Othello/codes/Utilities/Print.h new file mode 100644 index 0000000..57f11c6 --- /dev/null +++ b/Othello/codes/Utilities/Print.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +void print(std::string subject, int count); \ No newline at end of file diff --git a/Othello/codes/Utilities/StrToInt.cpp b/Othello/codes/Utilities/StrToInt.cpp new file mode 100644 index 0000000..6130301 --- /dev/null +++ b/Othello/codes/Utilities/StrToInt.cpp @@ -0,0 +1,51 @@ +#include +#include "StrToInt.h" + + + +int getNumber(char digit); + + + +// turns a string containing digits into a integer +int strToInt(std::string number) +{ + int result = 0; + + while (number != "") + { + result = result*10 + getNumber((char) number[0]); + number = number.substr(1, number.size()); + } + + return result; +} + +int getNumber(char digit) +{ + switch (digit) + { + case '0': + return 0; + case '1': + return 1; + case '2': + return 2; + case '3': + return 3; + case '4': + return 4; + case '5': + return 5; + case '6': + return 6; + case '7': + return 7; + case '8': + return 8; + case '9': + return 9; + default: + return 0; + } +} \ No newline at end of file diff --git a/Othello/codes/Utilities/StrToInt.h b/Othello/codes/Utilities/StrToInt.h new file mode 100644 index 0000000..4a9d929 --- /dev/null +++ b/Othello/codes/Utilities/StrToInt.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +int strToInt(std::string number); \ No newline at end of file diff --git a/Othello/codes/Utilities/StrToSymbol.cpp b/Othello/codes/Utilities/StrToSymbol.cpp new file mode 100644 index 0000000..9511b41 --- /dev/null +++ b/Othello/codes/Utilities/StrToSymbol.cpp @@ -0,0 +1,25 @@ +#include + +char strToSymbol(std::string character) +{ + if (character == "W") + { + return 'W'; + } + else if (character == "B") + { + return 'B'; + } + else if (character == ".") + { + return '.'; + } + else if (character == "X") + { + return 'X'; + } + else if (character == "O") + { + return 'O'; + } +} \ No newline at end of file diff --git a/Othello/codes/Utilities/StrToSymbol.h b/Othello/codes/Utilities/StrToSymbol.h new file mode 100644 index 0000000..142a6b1 --- /dev/null +++ b/Othello/codes/Utilities/StrToSymbol.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +char strToSymbol(std::string character); \ No newline at end of file diff --git a/Othello/codes/Utilities/SymbolToStr.cpp b/Othello/codes/Utilities/SymbolToStr.cpp new file mode 100644 index 0000000..7f95a82 --- /dev/null +++ b/Othello/codes/Utilities/SymbolToStr.cpp @@ -0,0 +1,25 @@ +#include + +std::string symbolToStr(char character) +{ + if (character == 'W') + { + return "W"; + } + else if (character == 'B') + { + return "B"; + } + else if (character == '.') + { + return "."; + } + else if (character == 'X') + { + return "X"; + } + else if (character == 'O') + { + return "O"; + } +} \ No newline at end of file diff --git a/Othello/codes/Utilities/SymbolToStr.h b/Othello/codes/Utilities/SymbolToStr.h new file mode 100644 index 0000000..2231312 --- /dev/null +++ b/Othello/codes/Utilities/SymbolToStr.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +std::string symbolToStr(char); \ No newline at end of file diff --git a/Othello/codes/Utilities/outputDecoratedSavedGame.cpp b/Othello/codes/Utilities/outputDecoratedSavedGame.cpp new file mode 100644 index 0000000..8fbc0c4 --- /dev/null +++ b/Othello/codes/Utilities/outputDecoratedSavedGame.cpp @@ -0,0 +1,129 @@ +#include "../Database/GameHistoryManager.h" +#include "../Utilities/IntToStr.h" +#include "../Structures/Board.h" +#include "../Structures/Player.h" +#include "../Structures/Bot.h" +#include "../Structures/SinglePlayerGame.h" +#include "../Structures/MultiPlayerGame.h" +#include "GetSavedBoardSize.h" +#include "Print.h" +#include "outputDecoratedSavedGame.h" +#include + + + +void outputDecoratedSavedSinglePlayerGame(std::string savedGame); +void outputDecoratedSavedMultiPlayerGame(std::string savedGame); + + + +void outputDecoratedSavedGame(std::string savedGame) +{ + std::string gameMode = getProperty(2, savedGame); + + if (gameMode == "1Player") + { + return outputDecoratedSavedSinglePlayerGame(savedGame); + } + else + { + return outputDecoratedSavedMultiPlayerGame(savedGame); + } +} + + + +void outputDecoratedSavedSinglePlayerGame(std::string savedGame) +{ + int id = getGameId(savedGame); + Board board = *(getSinglePlayerGame(id).GameBoard); + Player player = *(getSinglePlayerGame(id).Player1); + Bot bot = *(getSinglePlayerGame(id).GameBot); + char CurrentTurnColor = getSinglePlayerGame(id).CurrentTurnColor; + int Winner = getSinglePlayerGame(id).Winner; + + SinglePlayerGame game {&board, &player, &bot, CurrentTurnColor}; + game.id = id; + game.Winner = Winner; + game.mode = "1Player"; + game.GameBoard -> CursorX = -1; + game.GameBoard -> CursorY = -1; + + + std::cout << "{\n id = " << intToStr(game.id) << "\n"; + + std::cout << " game board = { \n"; + game.GameBoard -> display(); + + std::cout << " mode = single player\n"; + + std::cout << " Player = {\n" + << " name = " << game.Player1 -> name << std::endl + << " color = " << (game.Player1 -> color == 'W' ? "White":"Black") << std::endl + << " }\n"; + + + int botDifficulty = game.GameBot -> difficulty; + std::string difficultyToOutput = ( + botDifficulty == 1 ? + "easy" : botDifficulty == 2 ? + "medium" : "hard" + ); + std::cout << " Bot = {\n" + << " difficulty = " << difficultyToOutput << std::endl + << " }\n"; + + int winner = game.Winner; + std::string winnerToOutput = ( + winner == 0 ? + "The game is unfinished" : winner == 1 ? + "Player has won this game" : winner == 2 ? + "Bot has won this game" : "The game resulted in a draw" + ); + std::cout << " " << winnerToOutput << "\n}\n"; +} + + + +void outputDecoratedSavedMultiPlayerGame(std::string savedGame) +{ + int id = getGameId(savedGame); + Board board = *(getMultiPlayerGame(id).GameBoard); + Player player1 = *(getMultiPlayerGame(id).Player1); + Player player2 = *(getMultiPlayerGame(id).Player2); + char CurrentTurnColor = getMultiPlayerGame(id).CurrentTurnColor; + int Winner = getMultiPlayerGame(id).Winner; + + MultiPlayerGame game {&board, &player1, &player2, CurrentTurnColor}; + game.id = id; + game.Winner = Winner; + game.mode = "2Player"; + game.GameBoard -> CursorX = -1; + game.GameBoard -> CursorY = -1; + + std::cout << "{\n id = " << intToStr(game.id) << "\n"; + + std::cout << " game board = { \n"; + game.GameBoard -> display(); + + std::cout << " mode = Multiplayer\n"; + + std::cout << " Player1 = {\n" + << " name = " << game.Player1 -> name << std::endl + << " color = " << (game.Player1 -> color == 'W' ? "White":"Black") << std::endl + << " }\n"; + + std::cout << " Player2 = {\n" + << " name = " << game.Player2 -> name << std::endl + << " color = " << (game.Player2 -> color == 'W' ? "White":"Black") << std::endl + << " }\n"; + + int winner = game.Winner; + std::string winnerToOutput = ( + winner == 0 ? + "The game is unfinished" : winner == 1 ? + "Player1 has won this game" : winner == 2 ? + "Player2 has won this game" : "The game resulted in a draw" + ); + std::cout << " " << winnerToOutput << "\n}\n"; +} \ No newline at end of file diff --git a/Othello/codes/Utilities/outputDecoratedSavedGame.h b/Othello/codes/Utilities/outputDecoratedSavedGame.h new file mode 100644 index 0000000..186c208 --- /dev/null +++ b/Othello/codes/Utilities/outputDecoratedSavedGame.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +void outputDecoratedSavedGame(std::string savedGame); \ No newline at end of file diff --git a/Othello/myprogram.exe b/Othello/myprogram.exe new file mode 100644 index 0000000000000000000000000000000000000000..78908901e457f225f05bcba8b358496bf06d895c GIT binary patch literal 262256 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 literal 0 HcmV?d00001