diff --git a/README.md b/README.md index f40c4b2..460d3d9 100644 --- a/README.md +++ b/README.md @@ -1 +1,28 @@ -# TicTacToe_class \ No newline at end of file +# Tic-Tac-Toe Game + +This is a C++ implementation of the Tic-Tac-Toe game where a human player plays against an AI. The game uses a 2D matrix to represent the board and players can input their desired position to place their symbol (O or X). The game constantly checks for a winning player or a tie and displays the final result. The code also includes a function to generate a random move for the AI at the start of the game. + +## Getting Started + +These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. + +### Prerequisites + +You will need to have a Windows operating system and Visual Studio installed on your machine. + +### Installing + +You can download the source code of the project from the GitHub repository. Once you have the files, you can open the solution in Visual Studio and build the project. + +### Running the Game + +Once the project is built and the executable is generated, you can run the game by executing the file in the command prompt. + +## Built With + +- [Visual Studio](https://visualstudio.microsoft.com/) - The IDE used for development +- [C++](http://www.cplusplus.com/) - The programming language used + +## Author + +- **Tugamer89** - _Initial work_ - [Tugamer89](https://github.com/Tugamer89) diff --git a/TicTacToe_class.sln b/TicTacToe_class.sln new file mode 100644 index 0000000..cff889d --- /dev/null +++ b/TicTacToe_class.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33205.214 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TicTacToe_class", "TicTacToe_class\TicTacToe_class.vcxproj", "{01785574-4FCB-41B4-94D0-DADD02222524}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {01785574-4FCB-41B4-94D0-DADD02222524}.Debug|x64.ActiveCfg = Debug|x64 + {01785574-4FCB-41B4-94D0-DADD02222524}.Debug|x64.Build.0 = Debug|x64 + {01785574-4FCB-41B4-94D0-DADD02222524}.Debug|x86.ActiveCfg = Debug|Win32 + {01785574-4FCB-41B4-94D0-DADD02222524}.Debug|x86.Build.0 = Debug|Win32 + {01785574-4FCB-41B4-94D0-DADD02222524}.Release|x64.ActiveCfg = Release|x64 + {01785574-4FCB-41B4-94D0-DADD02222524}.Release|x64.Build.0 = Release|x64 + {01785574-4FCB-41B4-94D0-DADD02222524}.Release|x86.ActiveCfg = Release|Win32 + {01785574-4FCB-41B4-94D0-DADD02222524}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7A6506B1-C571-4E6D-A5B7-4B3E658B4A8F} + EndGlobalSection +EndGlobal diff --git a/TicTacToe_class/TicTacToe_class.cpp b/TicTacToe_class/TicTacToe_class.cpp new file mode 100644 index 0000000..c1be8b8 --- /dev/null +++ b/TicTacToe_class/TicTacToe_class.cpp @@ -0,0 +1,223 @@ +#include +#include +#include +#include +#include +#include + +using namespace std; + +const int BOARD_DIMENSION = 3; +const int CLS_DELAY_MS = 1500; +enum States {LOST = -1, TIE, WON}; + +class TicTacToe { +public: + TicTacToe() : gameBoard(BOARD_DIMENSION, vector(BOARD_DIMENSION, " ")), gameResult(TIE) {} + + void playGame() { +#ifdef _WIN32 + system("cls"); +#else + system("clear"); +#endif + printBoard(); + + do { + string position; + + if (isHumanTurn) { + cout << endl << endl << "Place an " << humanSymbol << " to: "; + cin >> position; + + if (!isValidPosition(position)) { + cout << "Invalid position!" << endl; + this_thread::sleep_for(std::chrono::milliseconds(CLS_DELAY_MS)); + continue; + } + } + else if (isFirstMove) { + position = getRandomAIPosition(); + isFirstMove = false; + } + else { + position = getAIMove(); + } + + makeMove(position, (isHumanTurn ? humanSymbol : aiSymbol)); + isHumanTurn = !isHumanTurn; + +#ifdef _WIN32 + system("cls"); +#else + system("clear"); +#endif + printBoard(); + } while (!isGameOver()); + + switch (gameResult) { + case LOST: + cout << endl << "You lost!" << endl; + break; + case TIE: + cout << endl << "Tie!" << endl; + break; + case WON: + cout << endl << "You won!" << endl; + break; + default: + break; + } + } + + bool isHumanTurn; + bool isFirstMove; + string humanSymbol = "O"; + string aiSymbol = "X"; + +private: + vector> gameBoard; + States gameResult; + + void printBoard() const { + cout << " 1 2 3" << endl; + for (int i = 0; i < BOARD_DIMENSION; i++) { + cout << " " << i + 1; + for (int j = 0; j < BOARD_DIMENSION; j++) + cout << " " << gameBoard[i][j] << (j != BOARD_DIMENSION - 1 ? " |" : ""); + cout << endl << (i != BOARD_DIMENSION - 1 ? " -----------" : "") << endl; + } + } + bool isGameOver() { + if (gameBoard[1][1] != " " && ( + (gameBoard[0][0] == gameBoard[1][1] && gameBoard[1][1] == gameBoard[2][2]) || + (gameBoard[0][2] == gameBoard[1][1] && gameBoard[1][1] == gameBoard[2][0]) + )) { + gameResult = (gameBoard[1][1] == humanSymbol ? WON : (gameBoard[1][1] == aiSymbol ? LOST : TIE)); + return true; + } + + for (int i = 0; i < BOARD_DIMENSION; i++) { + if (gameBoard[i][0] != " " && gameBoard[i][0] == gameBoard[i][1] && gameBoard[i][0] == gameBoard[i][2]) { + gameResult = (gameBoard[i][0] == humanSymbol ? WON : (gameBoard[i][0] == aiSymbol ? LOST : TIE)); + return true; + } + + if (gameBoard[0][i] != " " && gameBoard[0][i] == gameBoard[1][i] && gameBoard[0][i] == gameBoard[2][i]) { + gameResult = (gameBoard[0][i] == humanSymbol ? WON : (gameBoard[0][i] == aiSymbol ? LOST : TIE)); + return true; + } + } + + for (int i = 0; i < BOARD_DIMENSION; i++) + for (int j = 0; j < BOARD_DIMENSION; j++) + if (gameBoard[i][j] == " ") + return false; + + gameResult = TIE; + return true; + } + bool isValidPosition(const string& pos) { + if (pos.length() != 2) + return false; + if (pos[0] < '1' || pos[0] > '3' || pos[1] < '1' || pos[1] > '3') + return false; + if (gameBoard[pos[0] - '1'][pos[1] - '1'] != " ") + return false; + return true; + } + void makeMove(const string& pos, const string& player) { + gameBoard[pos[0] - '1'][pos[1] - '1'] = player; + } + string intPos2String(int i, int j) { + return to_string(i + 1) + to_string(j + 1); + } + string getRandomAIPosition() { + random_device rd; + mt19937 mt(rd()); + uniform_int_distribution dist(0, 2); + int i, j; + do { + i = dist(mt); + j = dist(mt); + } while (gameBoard[i][j] != " "); + return intPos2String(i, j); + } + string getAIMove() { + int bestVal = INT_MIN; + string bestMove = ""; + + for (int i = 0; i < BOARD_DIMENSION; i++) { + for (int j = 0; j < BOARD_DIMENSION; j++) { + if (gameBoard[i][j] == " ") { + gameBoard[i][j] = aiSymbol; + int moveVal = minimax(0, false); + gameBoard[i][j] = " "; + + if (moveVal > bestVal) { + bestMove = intPos2String(i, j); + bestVal = moveVal; + } + } + } + } + + return bestMove; + } + int minimax(int depth, bool isMax) { + if (isGameOver()) + return (gameResult == WON ? -10 + depth : (gameResult == LOST ? 10 - depth : 0)); + + if (isMax) { + int best = INT_MIN; + for (int i = 0; i < BOARD_DIMENSION; i++) { + for (int j = 0; j < BOARD_DIMENSION; j++) { + if (gameBoard[i][j] == " ") { + gameBoard[i][j] = aiSymbol; + best = max(best, minimax(depth + 1, !isMax)); + gameBoard[i][j] = " "; + } + } + } + return best; + } + else { + int best = INT_MAX; + for (int i = 0; i < BOARD_DIMENSION; i++) { + for (int j = 0; j < BOARD_DIMENSION; j++) { + if (gameBoard[i][j] == " ") { + gameBoard[i][j] = humanSymbol; + best = min(best, minimax(depth + 1, !isMax)); + gameBoard[i][j] = " "; + } + } + } + return best; + } + } +}; + +int main() { +#ifdef _WIN32 + system("cls"); +#else + system("clear"); +#endif + TicTacToe game; + + string start; + cout << "Who starts: "; + cin >> start; + game.isHumanTurn = start != game.aiSymbol; + game.isFirstMove = !game.isHumanTurn; + + game.playGame(); + + string res; + cout << endl << "Play again (y/n): "; + cin >> res; + if (res == "y") + main(); + + return 0; +} diff --git a/TicTacToe_class/TicTacToe_class.vcxproj b/TicTacToe_class/TicTacToe_class.vcxproj new file mode 100644 index 0000000..0c4fb51 --- /dev/null +++ b/TicTacToe_class/TicTacToe_class.vcxproj @@ -0,0 +1,135 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {01785574-4fcb-41b4-94d0-dadd02222524} + TicTacToeclass + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + false + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + false + + + + + + + + + \ No newline at end of file diff --git a/TicTacToe_class/TicTacToe_class.vcxproj.filters b/TicTacToe_class/TicTacToe_class.vcxproj.filters new file mode 100644 index 0000000..c9c6f66 --- /dev/null +++ b/TicTacToe_class/TicTacToe_class.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + File di origine + + + \ No newline at end of file