From e817edb7598c76ea123708c4f39efc0dfa4e6a3e Mon Sep 17 00:00:00 2001 From: dm1sh Date: Wed, 23 Feb 2022 22:40:21 +0300 Subject: [PATCH] Add project files. --- README.md | 1 + TicTacToeApp.cpp | 14 +++ TicTacToeApp.h | 8 ++ TicTacToeDialog.rc | 116 +++++++++++++++++++++++ TicTacToeDialog.sln | 31 ++++++ TicTacToeDialog.vcxproj | 162 ++++++++++++++++++++++++++++++++ TicTacToeDialog.vcxproj.filters | 47 +++++++++ TicTacToeDlg.cpp | 60 ++++++++++++ TicTacToeDlg.h | 20 ++++ TicTacToeGame.cpp | 126 +++++++++++++++++++++++++ TicTacToeGame.h | 22 +++++ resource.h | 25 +++++ 12 files changed, 632 insertions(+) create mode 100644 README.md create mode 100644 TicTacToeApp.cpp create mode 100644 TicTacToeApp.h create mode 100644 TicTacToeDialog.rc create mode 100644 TicTacToeDialog.sln create mode 100644 TicTacToeDialog.vcxproj create mode 100644 TicTacToeDialog.vcxproj.filters create mode 100644 TicTacToeDlg.cpp create mode 100644 TicTacToeDlg.h create mode 100644 TicTacToeGame.cpp create mode 100644 TicTacToeGame.h create mode 100644 resource.h diff --git a/README.md b/README.md new file mode 100644 index 0000000..22a1b48 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +TicTacToeDialog \ No newline at end of file diff --git a/TicTacToeApp.cpp b/TicTacToeApp.cpp new file mode 100644 index 0000000..e2e4005 --- /dev/null +++ b/TicTacToeApp.cpp @@ -0,0 +1,14 @@ +#include "TicTacToeApp.h" + +#include "TicTacToeDlg.h" + +CTicTacToeApp app; + +BOOL CTicTacToeApp::InitInstance() +{ + CTicTacToeDlg* dlg = new CTicTacToeDlg(); + + dlg->DoModal(); + + return TRUE; +} diff --git a/TicTacToeApp.h b/TicTacToeApp.h new file mode 100644 index 0000000..5c1188e --- /dev/null +++ b/TicTacToeApp.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +class CTicTacToeApp : public CWinApp { +public: + virtual BOOL InitInstance(); +}; diff --git a/TicTacToeDialog.rc b/TicTacToeDialog.rc new file mode 100644 index 0000000..ad69849 --- /dev/null +++ b/TicTacToeDialog.rc @@ -0,0 +1,116 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_TIC_TAC_TOE_DIALOG DIALOGEX 0, 0, 311, 204 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Game from Dmitriy Shishkov" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Yes",IDOK,199,183,50,14 + PUSHBUTTON "No",IDCANCEL,254,183,50,14 + PUSHBUTTON "",IDC_BUTTON1,107,59,32,29,BS_FLAT + LTEXT "Can you beat this AI in Tic-Tac-Toe?",IDC_STATIC,7,7,297,18 + PUSHBUTTON "",IDC_BUTTON2,139,59,32,29,BS_FLAT + PUSHBUTTON "",IDC_BUTTON3,171,59,32,29,BS_FLAT + PUSHBUTTON "",IDC_BUTTON4,107,88,32,29,BS_FLAT + PUSHBUTTON "",IDC_BUTTON5,139,88,32,29,BS_FLAT + PUSHBUTTON "",IDC_BUTTON6,171,88,32,29,BS_FLAT + PUSHBUTTON "",IDC_BUTTON7,107,117,32,29,BS_FLAT + PUSHBUTTON "",IDC_BUTTON8,139,117,32,29,BS_FLAT + PUSHBUTTON "",IDC_BUTTON9,171,117,32,29,BS_FLAT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_TIC_TAC_TOE_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 304 + TOPMARGIN, 7 + BOTTOMMARGIN, 197 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_TIC_TAC_TOE_DIALOG AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/TicTacToeDialog.sln b/TicTacToeDialog.sln new file mode 100644 index 0000000..670ac60 --- /dev/null +++ b/TicTacToeDialog.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32210.238 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TicTacToeDialog", "TicTacToeDialog.vcxproj", "{FA0F9820-6113-481C-8A13-11612ACADAD6}" +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 + {FA0F9820-6113-481C-8A13-11612ACADAD6}.Debug|x64.ActiveCfg = Debug|x64 + {FA0F9820-6113-481C-8A13-11612ACADAD6}.Debug|x64.Build.0 = Debug|x64 + {FA0F9820-6113-481C-8A13-11612ACADAD6}.Debug|x86.ActiveCfg = Debug|Win32 + {FA0F9820-6113-481C-8A13-11612ACADAD6}.Debug|x86.Build.0 = Debug|Win32 + {FA0F9820-6113-481C-8A13-11612ACADAD6}.Release|x64.ActiveCfg = Release|x64 + {FA0F9820-6113-481C-8A13-11612ACADAD6}.Release|x64.Build.0 = Release|x64 + {FA0F9820-6113-481C-8A13-11612ACADAD6}.Release|x86.ActiveCfg = Release|Win32 + {FA0F9820-6113-481C-8A13-11612ACADAD6}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8FAD6C10-D566-41D4-8299-9E507E3F8EA8} + EndGlobalSection +EndGlobal diff --git a/TicTacToeDialog.vcxproj b/TicTacToeDialog.vcxproj new file mode 100644 index 0000000..d600e30 --- /dev/null +++ b/TicTacToeDialog.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {d77535d7-7828-4913-91de-08c41d804f8f} + TicTacToeDialog + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + Dynamic + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + 0x0419 + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TicTacToeDialog.vcxproj.filters b/TicTacToeDialog.vcxproj.filters new file mode 100644 index 0000000..a54fd11 --- /dev/null +++ b/TicTacToeDialog.vcxproj.filters @@ -0,0 +1,47 @@ + + + + + {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 + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/TicTacToeDlg.cpp b/TicTacToeDlg.cpp new file mode 100644 index 0000000..fe8842e --- /dev/null +++ b/TicTacToeDlg.cpp @@ -0,0 +1,60 @@ +#include "TicTacToeDlg.h" + +void CTicTacToeDlg::OnOK() +{ + if (player == SqState::O) + MessageBox(L"Who are you trying to trick?", L"Game result"); + else + CDialog::OnOK(); +} + +void CTicTacToeDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + + for (UINT i = 0; i < 9; i++) + DDX_Control(pDX, IDC_BUTTON1 + i, buttons[i]); +} + +BEGIN_MESSAGE_MAP(CTicTacToeDlg, CDialog) + ON_CONTROL_RANGE(BN_CLICKED, IDC_BUTTON1, IDC_BUTTON9, CTicTacToeDlg::OnBnClickedSquare) +END_MESSAGE_MAP() + +void CTicTacToeDlg::OnBnClickedSquare(UINT nID) +{ + if (ended) return; + + UINT btn_id = nID - IDC_BUTTON1; + if (field[btn_id] == SqState::E) { + CString msg; + msg.Format(L"Pressed %u button", nID); + TRACE(msg); + + player = SqState::X; + playerMove(btn_id); + buttons[btn_id].SetWindowTextW(L"X"); + + if (checkWin(SqState::X)) { + ended = TRUE; + MessageBox(L"You have been able to do this! It's really cool, because my algorithm must be invincible.", L"Game result"); + return; + } + + player = SqState::O; + + if (checkDraw()) { + MessageBox(L"It's draw. You are still good enogh and didn't do mistakes during the game. This AI is powered by minimax algorithm, so it is invincible by design.", L"Game result"); + return; + } + + UINT comp_id = findComputerMove(); + playerMove(comp_id); + buttons[comp_id].SetWindowTextW(L"O"); + + if (checkWin(SqState::O)) { + ended = TRUE; + MessageBox(L"Unfortunatly, you lost. If it'll make you feel any better, this AI is powered by minimax algorithm, so it is invincible by design."); + return; + } + } +} diff --git a/TicTacToeDlg.h b/TicTacToeDlg.h new file mode 100644 index 0000000..82436e6 --- /dev/null +++ b/TicTacToeDlg.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "TicTacToeGame.h" +#include "resource.h" + +class CTicTacToeDlg : public CDialog, public TicTacToeGame { +public: + CTicTacToeDlg() : CDialog(IDD_TIC_TAC_TOE_DIALOG, NULL) {}; + +private: + void OnOK(); + void DoDataExchange(CDataExchange* pDX); + + CButton buttons[9]; + + DECLARE_MESSAGE_MAP() + afx_msg void OnBnClickedSquare(UINT nID); +}; diff --git a/TicTacToeGame.cpp b/TicTacToeGame.cpp new file mode 100644 index 0000000..ba64ee2 --- /dev/null +++ b/TicTacToeGame.cpp @@ -0,0 +1,126 @@ +#include "TicTacToeGame.h" + +/** + * @brief Sets field cell to current player + * @param coord - Cell coordinate + */ +void TicTacToeGame::playerMove(UINT coord) +{ + field[coord] = player; +} + +/** + * @brief Calculates best move for computer using minimax algorithm + */ +UINT TicTacToeGame::findComputerMove() +{ + INT bestScore = INT_MIN, score; + UINT bestCell = -1; + + for (UINT i = 0; i < 9; i++) + if (field[i] == SqState::E) { + field[i] = SqState::O; + + score = minimax(0, SqState::O); + + if (score > bestScore) { + bestScore = score; + bestCell = i; + } + + field[i] = SqState::E; + } + + return bestCell; +} + +/** + * @brief Calculates score of current game board for player. If the game is ended, it returns its result minus depth of this state in game tree. Else, it returns + * @param depth - Number of steps made in game graph + * @param player - Player for which we want to calculate current score + */ +INT TicTacToeGame::minimax(UINT depth, SqState player) +{ + if (player == SqState::E) throw "Called with Empty player"; + + SqState enemy = getEnemy(player); + + INT winScore = checkWin(player); + + if (winScore) return winScore - depth; + + INT maxEnemyScore = INT_MIN; + + for (int i = 0; i < 9; i++) + if (field[i] == SqState::E) { + field[i] = enemy; + INT score = minimax(depth + 1, enemy); + + if (score > maxEnemyScore) + maxEnemyScore = score; + + field[i] = SqState::E; + } + + if (maxEnemyScore == INT_MIN) return 0; + + return -maxEnemyScore; +} + +/** + * @brief Checks if current game state is wining for player or its enemy + * @param player - Player for which we check + * @returns 10 means win for player, -10 - for enemy, 0 - draw or unfinished + */ +INT TicTacToeGame::checkWin(SqState player) +{ + if (player == SqState::E) throw "Called with Empty player"; + + SqState enemy = getEnemy(player); + + for (INT i = 0; i < 3; i++) { + if ((field[i * 3] == field[1 + i * 3] && field[1 + i * 3] == field[2 + i * 3]) || // Row + (field[i] == field[3 + i] && field[3 + i] == field[6 + i])) { // Column + if (field[i * 4] == player) + return 10; + if (field[i * 4] == enemy) + return -10; + } + } + + if ((field[0] == field[4] && field[4] == field[8]) || // first diagonal + (field[2] == field[4] && field[4] == field[6])) { // second diagonal + if (field[4] == player) + return 10; + if (field[4] == enemy) + return -10; + } + + return 0; +} + +/** + * @brief Checks if current game state is a draw + */ +BOOL TicTacToeGame::checkDraw() +{ + for (int i = 0; i < 9; i++) + if (field[i] == SqState::E) + return FALSE; + + ended = TRUE; + return TRUE; +} + +/** + * @brief Utility for getting enemy of player + */ +SqState TicTacToeGame::getEnemy(SqState player) +{ + if (player == SqState::E) throw "Called with Empty player"; + + if (player == SqState::X) + return SqState::O; + else + return SqState::X; +} diff --git a/TicTacToeGame.h b/TicTacToeGame.h new file mode 100644 index 0000000..a665863 --- /dev/null +++ b/TicTacToeGame.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +enum class SqState { + E = -1, X, O +}; + +class TicTacToeGame { +public: + SqState field[9] = { SqState::E, SqState::E, SqState::E, SqState::E, SqState::E, SqState::E, SqState::E, SqState::E, SqState::E }; + SqState player = SqState::X; + BOOL ended = FALSE; + + void playerMove(UINT coord); + UINT findComputerMove(); + + INT minimax(UINT depth, SqState player); + INT checkWin(SqState player); + BOOL checkDraw(); + SqState getEnemy(SqState player); +}; diff --git a/resource.h b/resource.h new file mode 100644 index 0000000..513d2ba --- /dev/null +++ b/resource.h @@ -0,0 +1,25 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by TicTacToeDialog.rc +// +#define IDD_TIC_TAC_TOE_DIALOG 101 +#define IDC_BUTTON1 1001 +#define IDC_BUTTON2 1002 +#define IDC_BUTTON3 1003 +#define IDC_BUTTON4 1004 +#define IDC_BUTTON5 1005 +#define IDC_BUTTON6 1006 +#define IDC_BUTTON7 1007 +#define IDC_BUTTON8 1008 +#define IDC_BUTTON9 1009 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 106 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1005 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif