From e282072e93e76d7ea7b8600fd54821d8915d47c3 Mon Sep 17 00:00:00 2001 From: "vadim.bochkov" Date: Thu, 7 Nov 2024 11:43:09 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=BA=D0=BE=D0=BD=D1=87=D0=B0=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BD=D0=B0=D1=8F=20=D0=BD=D0=B5=D0=BE=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=87=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8F.=20=D0=9C=D1=8D?= =?UTF-8?q?=D0=B9=D0=BD=20=D1=80=D0=B0=D1=81=D1=88=D0=B8=D1=82=20=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5?= =?UTF-8?q?=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=B4=D0=BB=D1=8F=20=D0=BA?= =?UTF-8?q?=D0=BB=D0=B0=D1=81=D1=81=D0=BE=D0=B2.=20=D0=94=D0=B0=D0=BB?= =?UTF-8?q?=D1=8C=D1=88=D0=B5=20=D1=80=D0=B0=D1=81=D1=88=D0=B8=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BB=D0=B5=D0=BD=D1=8C.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 ++ BoardClass/Board.cpp | 71 +++++++++++++++ BoardClass/Board.h | 40 ++++++++ CMakeLists.txt | 6 +- TurnClass/Turn.cpp | 37 ++++++++ TurnClass/Turn.h | 29 ++++++ main.cpp | 213 ++++++++++++++----------------------------- 7 files changed, 254 insertions(+), 148 deletions(-) create mode 100644 BoardClass/Board.cpp create mode 100644 BoardClass/Board.h create mode 100644 TurnClass/Turn.cpp create mode 100644 TurnClass/Turn.h diff --git a/.gitignore b/.gitignore index f26bb15..da7ef93 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,9 @@ /.idea/modules.xml /.idea/TicTacToe.iml /.idea/vcs.xml +/cmake-build-debug/.cmake/api/v1/reply/cmakeFiles-v1-4d89b3faad81e294474a.json +/cmake-build-debug/.cmake/api/v1/reply/codemodel-v2-6554dfae7ce0093c2287.json +/cmake-build-debug/.cmake/api/v1/reply/index-2024-11-07T06-29-40-0054.json +/cmake-build-debug/.cmake/api/v1/reply/target-TicTacToe-Debug-d8c9700c76828098b2bd.json +/cmake-build-debug/CMakeFiles/TicTacToe.dir/Board.cpp.obj +/cmake-build-debug/CMakeFiles/TicTacToe.dir/Turn.cpp.obj diff --git a/BoardClass/Board.cpp b/BoardClass/Board.cpp new file mode 100644 index 0000000..b15d855 --- /dev/null +++ b/BoardClass/Board.cpp @@ -0,0 +1,71 @@ +#include "Board.h" +// +// +//Функция графического вывода поля. +//Пустая клетка (со значением "е") выводится координатами, а занятая - своим знаком. +void Board::BoardShow() { + for (unint countlines = 0; countlines < LINES; ++countlines) { + for (unint countcolumns = 0; countcolumns < COLUMNS; ++countcolumns) { + if (board[countlines][countcolumns]=="e") { + cout << countlines << countcolumns << " "; + } + else { + cout << board[countlines][countcolumns] << " "; + } + } + cout << endl; + } +} +//Функция очистки поля. Вызывается в новой итерации игрового цикла. +void Board::BoardClr() { + for (unint countlines = 0; countlines < LINES; ++countlines) { + for (unint countcolumns = 0; countcolumns < COLUMNS; ++countcolumns) { + board[countlines][countcolumns] = "e"; + } + } +} +//Функция проверки победы Х. +void Board::XWon() { + if (drawcounter>=4) { + if ((board[0][0])=="'X'") { + if ((board[0][1])=="'X'" and (board[0][2])=="'X'") {Xwonflag=true;} + else if ((board[1][1])=="'X'" and (board[2][2])=="'X'") {Xwonflag=true;} + else if ((board[1][0])=="'X'" and (board[2][0])=="'X'") {Xwonflag=true;} + } + if (((board[1][0])=="'X'") and ((board[1][1])=="'X'") and ((board[1][2]))=="'X'") {Xwonflag=true;} + if (((board[0][1])=="'X'") and ((board[1][1])=="'X'") and ((board[2][1]))=="'X'") {Xwonflag=true;} + if (((board[0][2])=="'X'") and ((board[1][2])=="'X'") and ((board[2][2]))=="'X'") {Xwonflag=true;} + if ((board[2][0])=="'X'") { + if ((board[2][1])=="'X'" and (board[2][2])=="'X'") {Xwonflag=true;} + else if ((board[1][1])=="'X'" and (board[0][2])=="'X'") {Xwonflag=true;} + } + } +} +//Функция проверки победы О. +void Board::OWon() { + if (drawcounter>=4) { + if ((board[0][0])=="'O'") { + if ((board[0][1])=="'O'" and (board[0][2])=="'O'") {Owonflag=true;} + else if ((board[1][1])=="'O'" and (board[2][2])=="'O'") {Owonflag=true;} + else if ((board[1][0])=="'O'" and (board[2][0])=="'O'") {Owonflag=true;} + } + if (((board[1][0])=="'O'") and ((board[1][1])=="'O'") and ((board[1][2]))=="'O'") {Owonflag=true;} + if (((board[0][1])=="'O'") and ((board[1][1])=="'O'") and ((board[2][1]))=="'O'") {Owonflag=true;} + if (((board[0][2])=="'O'") and ((board[1][2])=="'O'") and ((board[2][2]))=="'O'") {Owonflag=true;} + if ((board[2][0])=="'O'") { + if ((board[2][1])=="'O'" and (board[2][2])=="'O'") {Owonflag=true;} + else if ((board[1][1])=="'O'" and (board[0][2])=="'O'") {Owonflag=true;} + } + } +} +//Функция, считающая заполненные клетки. +void Board::Draw() { + drawcounter=0; + for (int countlines = 0; countlines < LINES; ++countlines) { + for (int countcolumns = 0; countcolumns < COLUMNS; ++countcolumns) { + if (board[countlines][countcolumns]!="e") { + drawcounter++; + } + } + } +} \ No newline at end of file diff --git a/BoardClass/Board.h b/BoardClass/Board.h new file mode 100644 index 0000000..380fa86 --- /dev/null +++ b/BoardClass/Board.h @@ -0,0 +1,40 @@ +#pragma once +#ifndef TICTACTOE_BOARD_H +#define TICTACTOE_BOARD_H +#include +#include +typedef unsigned int unint; +using namespace std; +// +const int LINES = 3, COLUMNS = 3; +// +//Класс, отвечающий за формирование и вывод поля +class Board { +public: + // + //ПЕРЕМЕННЫЕ + //Флаги победы первого и второго номеров, а также счётчик занятых клеток + bool Xwonflag=false; + bool Owonflag=false; + unint drawcounter=1; + //ИГРОВОЕ ПОЛЕ + //первое число LINES характеризует строку + //второе число COLUMNS характеризует столбик + string board[LINES][COLUMNS] = { + {"e", "e", "e"}, + {"e", "e", "e"}, + {"e", "e", "e"} + }; + void BoardShow(); + + void BoardClr(); + + void XWon(); + + void OWon(); + + void Draw(); +}; + + +#endif //TICTACTOE_BOARD_H diff --git a/CMakeLists.txt b/CMakeLists.txt index c2e5edc..64b5bb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,4 +3,8 @@ project(TicTacToe) set(CMAKE_CXX_STANDARD 17) -add_executable(TicTacToe main.cpp) +add_executable(TicTacToe main.cpp + BoardClass/Board.cpp + BoardClass/Board.h + TurnClass/Turn.cpp + TurnClass/Turn.h) diff --git a/TurnClass/Turn.cpp b/TurnClass/Turn.cpp new file mode 100644 index 0000000..2d0c901 --- /dev/null +++ b/TurnClass/Turn.cpp @@ -0,0 +1,37 @@ +#include "Turn.h" +// +// +//Функция хода игрока: +//алгоритм на вычленение первого и второго значений координат +//получает строку вида "XY", первый символ кидает через at в строку для Х, +//второй символ также символ кидает в строку для Y, +//затем переводит обе строки в числа, с которыми можно вести вычисления. +void Turn::HumanTurn() { + cin >> Humancount; + turnline=Humancount.at(0); + turncol=Humancount.at(1); + turnLINEint=stoi(turnline); + turnCOLint=stoi(turncol); +} +//Функция хода бота +void Turn::BotTurn() { + //Со случайной вероятностью своим первым ходом может занять центральную клетку, если та свободна. + //На занятии центральной клетки весь ИИ заканчивается. + srand((time(0))); + if ((turnLINEint!=1) and (turnCOLint!=1) and ((rand())%2==1)) { + turnLINEint=1; + turnCOLint=1; + } + //Рандомит первое значение (строку), засыпает, рандомит второе значение (столбец), + //присваивает рандомные значения в переменные. + else { + srand((time(0))); + turnLINEint=(rand()%3); + chrono::milliseconds timespan((rand()/10)); + this_thread::sleep_for(timespan); + mt19937 rng; + rng.seed(time(nullptr)); + turnCOLint=(rng()%3); + } + //cout << "debugbotturn:" << turnLINEint << turnCOLint << endl; +} \ No newline at end of file diff --git a/TurnClass/Turn.h b/TurnClass/Turn.h new file mode 100644 index 0000000..1edc3d3 --- /dev/null +++ b/TurnClass/Turn.h @@ -0,0 +1,29 @@ +#pragma once +#ifndef TICTACTOE_TURN_H +#define TICTACTOE_TURN_H +#include +#include +#include "../BoardClass/Board.h" +#include +#include +#include +#include + +//Ходы игрока и бота +class Turn { +public: + //Сюда кладутся координаты, которые скармливаются игровому полю. + unint turnLINEint, turnCOLint; + //Строковые для ввода человека и перевода этого ввода в числа + string Humancount; + string turnline, turncol; + bool firstnumber, defenceturn; + Board BoardObj; + + void HumanTurn(); + + void BotTurn(); +}; + + +#endif //TICTACTOE_TURN_H diff --git a/main.cpp b/main.cpp index 0290e41..e3b2d0d 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,6 @@ #include -#include -#include -#include -#include -#include -#include - +#include "BoardClass/Board.h" +#include "TurnClass/Turn.h" using namespace std; typedef unsigned int unint; @@ -14,9 +9,8 @@ int main() { //ПЕРЕМЕННЫЕ // //МЭЙН - const int LINES = 3, COLUMNS = 3; - bool newgame=true, firstnumber; - int turn, humanwin=0, botwin=0, draw; + bool newgame=true; + int humanwin=0, botwin=0; string wannaplay="y"; const string INCORRECT="INCORRECT VALUE, TRY AGAIN: ", ENDGAME="\nGame says: ''The game ends here.''"; const string XWON="Game says: ''X wins!''", OWON="Game says: ''O wins!''", DRAW="Game says: ''Nobody wins - DRAW!''"; @@ -24,120 +18,9 @@ int main() { const string SEP(40, '-'); const string BOTTHINK="Bot says: ''Wait, I'm thinking.''"; // - //КЛАССЫ - //Класс, отвечающий за формирование и вывод поля - class Board { - public: - bool Xwonflag=false; - bool Owonflag=false; - int drawcounter=1; - //ИГРОВОЕ ПОЛЕ - //первое число LINES характеризует строку - //второе число COLUMNS характеризует столбик - string board[LINES][COLUMNS] = { - {"e", "e", "e"}, - {"e", "e", "e"}, - {"e", "e", "e"} - }; - //Функция вывода поля. - //Пустая клетка ("е") выводится координатами, а занятая - своим знаком. - void BoardShow() { - for (int countlines = 0; countlines < LINES; ++countlines) { - for (int countcolumns = 0; countcolumns < COLUMNS; ++countcolumns) { - if (board[countlines][countcolumns]=="e") { - cout << countlines << countcolumns << " "; - } - else { - cout << board[countlines][countcolumns] << " "; - } - } - cout << endl; - } - } - void BoardClr() { - for (int countlines = 0; countlines < LINES; ++countlines) { - for (int countcolumns = 0; countcolumns < COLUMNS; ++countcolumns) { - board[countlines][countcolumns] = "e"; - } - } - } - void XWon() { - if (drawcounter>=4) { - if ((board[0][0])=="'X'") { - if ((board[0][1])=="'X'" and (board[0][2])=="'X'") {Xwonflag=true;} - else if ((board[1][1])=="'X'" and (board[2][2])=="'X'") {Xwonflag=true;} - else if ((board[1][0])=="'X'" and (board[2][0])=="'X'") {Xwonflag=true;} - } - if (((board[1][0])=="'X'") and ((board[1][1])=="'X'") and ((board[1][2]))=="'X'") {Xwonflag=true;} - if (((board[0][1])=="'X'") and ((board[1][1])=="'X'") and ((board[2][1]))=="'X'") {Xwonflag=true;} - if (((board[0][2])=="'X'") and ((board[1][2])=="'X'") and ((board[2][2]))=="'X'") {Xwonflag=true;} - if ((board[2][0])=="'X'") { - if ((board[2][1])=="'X'" and (board[2][2])=="'X'") {Xwonflag=true;} - else if ((board[1][1])=="'X'" and (board[0][2])=="'X'") {Xwonflag=true;} - } - } - } - void OWon() { - if (drawcounter>=4) { - if ((board[0][0])=="'O'") { - if ((board[0][1])=="'O'" and (board[0][2])=="'O'") {Owonflag=true;} - else if ((board[1][1])=="'O'" and (board[2][2])=="'O'") {Owonflag=true;} - else if ((board[1][0])=="'O'" and (board[2][0])=="'O'") {Owonflag=true;} - } - if (((board[1][0])=="'O'") and ((board[1][1])=="'O'") and ((board[1][2]))=="'O'") {Owonflag=true;} - if (((board[0][1])=="'O'") and ((board[1][1])=="'O'") and ((board[2][1]))=="'O'") {Owonflag=true;} - if (((board[0][2])=="'O'") and ((board[1][2])=="'O'") and ((board[2][2]))=="'O'") {Owonflag=true;} - if ((board[2][0])=="'O'") { - if ((board[2][1])=="'O'" and (board[2][2])=="'O'") {Owonflag=true;} - else if ((board[1][1])=="'O'" and (board[0][2])=="'O'") {Owonflag=true;} - } - } - } - void Draw() { - drawcounter=1; - for (int countlines = 0; countlines < LINES; ++countlines) { - for (int countcolumns = 0; countcolumns < COLUMNS; ++countcolumns) { - if (board[countlines][countcolumns]!="e") { - drawcounter++; - } - } - } - } - }; + //Объявлен объект игровой доски Board BoardObj; - //Ходы игрока и бота - class Turn { - public: - int turnLINEint, turnCOLint; - string Humancount, turnline, turncol; - Board BoardObj; - void HumanTurn() { - //ХОД ИГРОКА - //алгоритм на вычленение первого и второго значений координат - //получает строку вида "XY", первый символ кидает через at в строку для Х, - //второй символ также символ кидает в строку для Y, - //затем переводит обе строки в числа, с которыми можно вести вычисления. - cin >> Humancount; - turnline=Humancount.at(0); - turncol=Humancount.at(1); - turnLINEint=stoi(turnline); - turnCOLint=stoi(turncol); - //cout << "\nlineHUMAN: " << turnLINEint << " colHUMAN: " << turnCOLint << endl; - } - void BotTurn() { - //ХОД БОТА - //рандомит первое значение (строку), засыпает, - //рандомит второе значение (столбец) - srand((time(0))); - turnLINEint=(rand()%3); - chrono::milliseconds timespan((rand()/9)); - this_thread::sleep_for(timespan); - mt19937 rng; - rng.seed(time(nullptr)); - turnCOLint=(rng()%3); - cout << "Bot says:''I've occupied this cell:" << turnLINEint << turnCOLint << "''" << endl; - } - }; + //Объявлен объект Game Turn Game; // // @@ -159,25 +42,26 @@ int main() { //становится FALSE в начале цикла после присвоения переменных, //и снова становится TRUE при окончании цикла (т.е. при чьей-то победе или ничье). if (newgame) { - // //Запрос на то, кто первый. 1=игрок, 0=бот. //Выводит соответствующее сообщение после выбора. - //Обнуляет общие переменные. - //Также снимает флаг newgame, который возвращается в конце игры. + //Снимает флаг newgame, который возвращается в конце игры. cout << "Bot asks: Wanna be an X? (1/0):"; - cin >> firstnumber; - if (firstnumber) {cout << "Bot says: ''Well, u'll be an X.''" << endl; BoardObj.BoardShow();} - else {cout << "Bot says: ''OK, I'll be an X.''" << endl;} - turn=1; + cin >> Game.firstnumber; + if (Game.firstnumber) { + cout << "Bot says: ''Well, u'll be an X.''" << endl; + BoardObj.BoardShow(); + } + else { + cout << "Bot says: ''OK, I'll be an X.''" << endl; + } newgame=false; } cout << SEP << endl; - //cout << "Game says: ''Now it's " << turn << " turn.''" << endl; - //turn++; Game.turnLINEint=3; Game.turnCOLint=3; - //Если первым номером (Х) выступает игрок - while (firstnumber) { + // + //Алгоритм игры, если первым номером (Х) выступает игрок + while (Game.firstnumber) { cout << "HUMAN TURN!\n" << "Enter ur coords:"; while ((BoardObj.board[Game.turnLINEint][Game.turnCOLint]!="e")) { Game.HumanTurn(); @@ -191,25 +75,38 @@ int main() { if ((BoardObj.Xwonflag) or (BoardObj.drawcounter==9)) {break;} Game.turnLINEint=3; Game.turnCOLint=3; + //Сообщение гласит о том, что ходит бот cout << "BOT TURN!\n" << BOTTHINK << endl; - while ((BoardObj.board[Game.turnLINEint][Game.turnCOLint]!="e")) {Game.BotTurn();} + //Повторяет функцию хода бота, пока та не нарандомит пустую клетку. + while ((BoardObj.board[Game.turnLINEint][Game.turnCOLint]!="e")) { + Game.BotTurn(); + } + //Присваивает свой знак и сообщает, какую клетку оккупировал. BoardObj.board[Game.turnLINEint][Game.turnCOLint] = "'O'"; + cout << "Bot says:''I've occupied this cell:" << Game.turnLINEint << Game.turnCOLint << "''" << endl; + //Вывод поля после хода бота. BoardObj.BoardShow(); cout << SEP << endl; + //Проверка на победу О (которым выступает бот) и на ничью. BoardObj.OWon(); BoardObj.Draw(); + //Наличие флага Xwonflag или занятость всех клетки = полом подцикла и выход в основной цикл. if ((BoardObj.Owonflag) or (BoardObj.drawcounter==9)) {break;} } - //Если игрок выступает вторым номером (O) - while (!firstnumber) { + // + //Алгоритм игры, если игрок выступает вторым номером (O) + while (!Game.firstnumber) { cout << "BOT TURN!\n" << BOTTHINK << endl; while ((BoardObj.board[Game.turnLINEint][Game.turnCOLint]!="e")) {Game.BotTurn();} BoardObj.board[Game.turnLINEint][Game.turnCOLint] = "'X'"; + cout << "Bot says:''I've occupied this cell:" << Game.turnLINEint << Game.turnCOLint << "''" << endl; BoardObj.BoardShow(); cout << SEP << endl; BoardObj.XWon(); BoardObj.Draw(); - if ((BoardObj.Xwonflag) or (BoardObj.drawcounter==9)) {break;} + if ((BoardObj.Xwonflag) or (BoardObj.drawcounter==9)) { + break; + } cout << "HUMAN TURN!\n" << "Enter ur coords:"; Game.turnLINEint=3; Game.turnCOLint=3; @@ -222,18 +119,40 @@ int main() { cout << SEP << endl; BoardObj.OWon(); BoardObj.Draw(); - if ((BoardObj.Owonflag) or (BoardObj.drawcounter==9)) {break;} + if ((BoardObj.Owonflag) or (BoardObj.drawcounter==9)) { + break; + } } + //Сообщение о конце игры и объявление флага новой игры для входа в запрос, кем хочет быть игрок cout << ENDGAME << endl; newgame=true; - if (BoardObj.Xwonflag) {cout << XWON << endl;} - else if (BoardObj.Owonflag) {cout << OWON << endl;} - else if ((BoardObj.drawcounter==9) and (!BoardObj.Xwonflag) and (!BoardObj.Owonflag)) {cout << DRAW << endl;} - - if ((firstnumber) and (BoardObj.Xwonflag)) {cout << BOTLOST << endl; humanwin++;} - else if ((!firstnumber) and (BoardObj.Owonflag)) {cout << BOTLOST << endl; humanwin++;} - else if (BoardObj.drawcounter==9) {cout << "Bot says: ''yeah dat was fairplay.''" << endl;} - else {cout << HUMANLOST << endl; botwin++;} + //Сообщения от игры в зависимости от победившего знака + if (BoardObj.Xwonflag) { + cout << XWON << endl; + } + else if (BoardObj.Owonflag) { + cout << OWON << endl; + } + else if ((BoardObj.drawcounter==9) and (!BoardObj.Xwonflag) and (!BoardObj.Owonflag)) { + cout << DRAW << endl; + } + //Сообщения от бота в зависимости от того, выиграл он или нет + if ((Game.firstnumber) and (BoardObj.Xwonflag)) { + cout << BOTLOST << endl; + humanwin++; + } + else if ((!Game.firstnumber) and (BoardObj.Owonflag)) { + cout << BOTLOST << endl; + humanwin++; + } + else if (BoardObj.drawcounter==9) { + cout << "Bot says: ''yeah dat was fairplay.''" << endl; + } + else { + cout << HUMANLOST << endl; + botwin++; + } + //Очистка поля, сброс флагов и предложение сыграть снова BoardObj.BoardClr(); BoardObj.Xwonflag=false; BoardObj.Owonflag=false;