/*
"Крестики - Нолики"
автор: Матвей Фордулясов
ПАЧНОУТЫ:
20.11.2018 - старт проекта и продумывание плана проекта
21.11.2018 - интерфейс и режим игры против человека
24.11.2018 - добавленна возможность играть партии подряд без перезапуска приложения, добавлен легкий и средний бот,
можно выбрать кто ходит первым.
25.11.2018 - добавлен сложный бот. начато продумывание реализации игры по локальной сети, дебаг и допиливание визуализации
дописаны недостающие "n" и system("pause")
так же мне помогли исправить ошибку которая происходила в случае ввода символов вместо интежер
27.11.2018 - допил сложного бота (функция mnogohodovochka которая проверяет один из сценариев и не дает игроку выграть по нему)
*/
/*
------ШПОРГААААЛКИ, ШПОРГААААЛОЧКИ!--------
рандом
{
#include <cstdlib> (библиотека для rand() и srand() )
srand(time(nullptr)); (запихать в начало функции мейн, для нормального рандома)
x=(rand() % *длинна промежутка* + *сдвиг относительно нуля*);
}
консолечка
{
system("cls"); (отчистка консоли)
system("pause"); (нажмите кнопку чтоб продолжить)
}
описание логики ботов:
бот всегда нолик
игрок всегда крестик
для атаки и защиты считывается состояние поля, и формируются строки 3 по горизонтали и вертикали, 2 накрест.
крестик равен 10, нолик равен единице, если строка равняется 2- производится атака, если строка равняется 20 - производится защита
в остальных случаях делается рандомный ход (средний бот) либо запускается алгоритм выйгршной стратегии - занять центр и углы
после того как я доделал код, стало ясно что можно было сделать несколько функций (атаки, защиты, случайного хода, очереди игрока)
в таком случае код был бы в два раза короче.
*/
#include <iostream>
#include <cstdlib> //для random
#include <ctime> //опять-же для random (используем системное время для генерации действительно случайных чисел)
using namespace std;
short int fieldUp[9]; //информация о состоянии строк для ботов бот=1, человек= 10(да, это костыль, мне лень делать компактнее)
char field[9]; //информация о игровом поле ('_'- пусто; 'x'- крестик; 'o'- нолик) эта переменная обязанна быть глобальной
bool turn; //чья очередь ходить (в botGM; bot - 1, человек - 0) а остальные переменные мне было лень прописывать каждый раз в функциях GM )))0)0)
short int gameStatus; //статус игры (победа одной из сторон-0/1, ничья-2, игра в процессе-3)
short int choice; //клетка которую выбрал игрок/бот для хода
bool botAD; //произведенна ли атака/защита
short safe_input() {
short val = 0;
while (!(cin >> val)) {
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), 'n');
cout << "Wrong input, only integers are expected!" << endl;
}
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), 'n');
return val;
//эту функцию я попросил написать отчима, она нужна для того чтоб в случае ввода не int программа не ошибалась.
// если вместо функции писать просто cin >> i; то в случае не пожохдящего ввода программа не правильно работала,
// а эта функция в случаее неверного ввода говорит об этом пользователю и запрашивает ввод вновь, пока он не введет int.
// это единственные семь строчек написанные не мной во всем коде.
}
void fieldUpdate()
//визуализация поля
{
cout << field[6] << "|" << field[7] << "|" << field[8] << " 6|7|8" << "n"
<< field[3] << "|" << field[4] << "|" << field[5] << " 3|4|5" << "n"
<< field[0] << "|" << field[1] << "|" << field[2] << " 0|1|2" << "n" << endl;
}
short int winner()
//проверка условий победы (0- нолики, 1-крестики, 2-ничья, 3-игра еще не окончена) ужаснейший костыль, можно было-бы уместить это все в botMain() но мне лень
{
if (((field[0] == 'x') && (field[1] == 'x') && (field[2] == 'x')) || ((field[3] == 'x') && (field[4] == 'x') && (field[5] == 'x'))
|| ((field[6] == 'x') && (field[7] == 'x') && (field[8] == 'x')) || ((field[0] == 'x') && (field[3] == 'x') && (field[6] == 'x'))
|| ((field[1] == 'x') && (field[4] == 'x') && (field[7] == 'x')) || ((field[2] == 'x') && (field[5] == 'x') && (field[8] == 'x'))
|| ((field[6] == 'x') && (field[4] == 'x') && (field[2] == 'x')) || ((field[8] == 'x') && (field[4] == 'x') && (field[0] == 'x')))
{
return 1;//победа крестиков
}
if (((field[0] == 'o') && (field[1] == 'o') && (field[2] == 'o')) || ((field[3] == 'o') && (field[4] == 'o') && (field[5] == 'o'))
|| ((field[6] == 'o') && (field[7] == 'o') && (field[8] == 'o')) || ((field[0] == 'o') && (field[3] == 'o') && (field[6] == 'o'))
|| ((field[1] == 'o') && (field[4] == 'o') && (field[7] == 'o')) || ((field[2] == 'o') && (field[5] == 'o') && (field[8] == 'o'))
|| ((field[6] == 'o') && (field[4] == 'o') && (field[2] == 'o')) || ((field[8] == 'o') && (field[4] == 'o') && (field[0] == 'o')))
{
return 0;//победа ноликов
}
short int checkup = 0;
for (short int i = 0; i < 9; i++)
if (field[i] != '_')
{
checkup++;
}
if (checkup == 9)
{
return 2; //ничья
}
else
{
return 3;//не все поля заполнены и пока никто не выйграл
}
}
void fieldCheckUp()
//"визуализация" поля для botMain() (да, это костыль... и что? :D )
{
for (short int i = 0; i < 9; i++)
{
switch (field[i])
{
case 'o': fieldUp[i] = 1;
break;
case 'x': fieldUp[i] = 10;
break;
case '_': fieldUp[i] = 0;
}
}
}
void botAction(short int a, short int b, short int c)
//вспомогательная функция вызывающаяся главной функцией бота для оказания защиты или атаки
//в качетсве параметров вводится три клетки(две из которых заняты), функция находит свободную и заполняет ее
{
if (field[a] == '_')
{
field[a] = 'o';
}
if (field[b] == '_')
{
field[b] = 'o';
}
if (field[c] == '_')
{
field[c] = 'o';
}
}
bool botMain(short int action)
//производит защиту(2), атаку(20), игру по стратегии (100)
{
if (action == 100) //действия умного бота
{
if (field[4] == '_')
{
field[4] = 'o';
return 1;
}
else
{
if (field[0] == '_')
{
field[0] = 'o';
return 1;
}
if (field[6] == '_')
{
field[6] = 'o';
return 1;
}
if (field[2] == '_')
{
field[2] = 'o';
return 1;
}
if (field[8] == '_')
{
field[8] = 'o';
return 1;
}
}
}
if ((fieldUp[0] + fieldUp[1] + fieldUp[2]) == action)
{
botAction(0, 1, 2);
return 1;
}
if ((fieldUp[3] + fieldUp[4] + fieldUp[5]) == action)
{
botAction(3, 4, 5);
return 1;
}
if ((fieldUp[6] + fieldUp[7] + fieldUp[8]) == action)
{
botAction(6, 7, 8);
return 1;
}
if ((fieldUp[6] + fieldUp[3] + fieldUp[0]) == action)
{
botAction(6, 3, 0);
return 1;
}
if ((fieldUp[7] + fieldUp[4] + fieldUp[1]) == action)
{
botAction(7, 4, 1);
return 1;
}
if ((fieldUp[8] + fieldUp[5] + fieldUp[2]) == action)
{
botAction(8, 5, 2);
return 1;
}
if ((fieldUp[0] + fieldUp[1] + fieldUp[2]) == action)
{
botAction(0, 1, 2);
return 1;
}
if ((fieldUp[2] + fieldUp[4] + fieldUp[6]) == action)
{
botAction(2, 4, 6);
return 1;
}
return 0;
}
void versusGM()
//игра против человека на одном экране
{
fieldUpdate();
gameStatus = 3;
while (gameStatus == 3)
{
cout << "select the position you want to make turn" << "n" << endl;
choice = safe_input();
system("cls");//отчистка консоли
if (turn)
{
if (field[choice] == '_') //проверяет свободна ли позиция которую выбрал игрок
{
field[choice] = 'x';
}
else
{
cout << "n" << "THIS POSITION IS ALREADY TAKEN!!!" << "n" << endl;
turn = !turn;
}
}
else
{
if (field[choice] == '_') //проверяет свободна ли позиция которую выбрал игрок
{
field[choice] = 'o';
}
else
{
cout << "n" << "THIS POSITION IS ALREADY TAKEN!!!" << "n" << endl;
turn = !turn;
}
}
turn = !turn;
fieldUpdate();
gameStatus = winner();
}
switch (gameStatus)
{
case 0: cout << "n" << "O - WIN!!!" << "n";
break;
case 1: cout << "n" << "X - WIN!!!" << "n";
break;
case 2: cout << "n" << "NOBODY WINS :c " << "n";
break;
}
}
bool mnogohodovochka()
{
if ((fieldUp[4] == 1) && ((fieldUp[6] + fieldUp[2] == 20) || (fieldUp[8] + fieldUp[0] == 20)) && (fieldUp[1] + fieldUp[3] + fieldUp[5] + fieldUp[7] == 0))
{
field[1] = 'o';
return 1;
}
else
{
return 0;
}
}
void botGM3()
//игра против сложного бота
{
gameStatus = 3;
while (gameStatus == 3)
{
if (turn == 1)
{
botAD = 0;
fieldCheckUp();
botAD = mnogohodovochka();
if (!botAD)
{
botAD = botMain(2);
}
if (!botAD)
{
botAD = botMain(20);
}
if (!botAD)
{
botAD = botMain(100);
}
if (botAD)
{
turn = !turn;
}
if (!botAD)
{
system("cls"); //отчистка консоли
while (turn)
{
choice = (rand() % 8);
if (field[choice] == '_')
{
field[choice] = 'o';
turn = !turn;
}
}
}
gameStatus = winner();
}
else
{
fieldUpdate();
cout << "select the position you want to make turn" << "n" << endl;
choice = safe_input();
system("cls"); //отчистка консоли
if (field[choice] == '_') //проверяет свободна ли позиция которую выбрал игрок
{
field[choice] = 'x';
gameStatus = winner();
}
else
{
cout << "n" << "THIS POSITION IS ALREADY TAKEN!!!" << "n" << endl;
turn = !turn;
}
turn = !turn;
}
}
system("cls");
fieldUpdate();
switch (gameStatus)
{
case 0: cout << "n" << "HARD BOT - WIN!!!" << "n";
break;
case 1: cout << "n" << "GENIUS!!!!!!!!!!!!!!!!!!!!" << "n"; //этого не может быть, но я добавил на всякий случай чтоб уидеть баг если он будет.
break;
case 2: cout << "n" << "NOBODY WINS :c " << "n";
break;
}
}
void botGM2()
//игра против среднего бота
{
gameStatus = 3;
while (gameStatus == 3)
{
if (turn == 1)
{
botAD = 0;
fieldCheckUp();
botAD = botMain(2);
if (botAD)
{
turn = !turn;
}
if (!botAD)
{
botAD = botMain(20);
}
if (botAD)
{
turn = !turn;
}
if (!botAD)
{
system("cls"); //отчистка консоли
while (turn)
{
choice = (rand() % 8);
if (field[choice] == '_')
{
field[choice] = 'o';
turn = !turn;
}
}
}
gameStatus = winner();
}
else
{
fieldUpdate();
cout << "select the position you want to make turn" << "n" << endl;
choice = safe_input();
system("cls"); //отчистка консоли
if (field[choice] == '_') //проверяет свободна ли позиция которую выбрал игрок
{
field[choice] = 'x';
gameStatus = winner();
}
else
{
cout << "n" << "THIS POSITION IS ALREADY TAKEN!!!" << "n" << endl;
turn = !turn;
}
turn = !turn;
}
}
system("cls");
fieldUpdate();
switch (gameStatus)
{
case 0: cout << "n" << "MEDIUM BOT - WIN!!!" << "n";
break;
case 1: cout << "n" << "YOU WIN!!!" << "n";
break;
case 2: cout << "n" << "NOBODY WINS :c " << "n";
break;
}
}
void botGM1()
//игра против легкого бота
{
gameStatus = 3;
while (gameStatus == 3)
{
if (turn == 1)
{
choice = (rand() % 8);
system("cls"); //отчистка консоли
if (field[choice] == '_') //проверяет свободна ли позиция которую выбрал бот
{
field[choice] = 'o';
gameStatus = winner();
}
else
{
turn = !turn;
}
turn = !turn;
}
else
{
fieldUpdate();
cout << "select the position you want to make turn" << "n" << endl;
choice = safe_input();
system("cls"); //отчистка консоли
if (field[choice] == '_') //проверяет свободна ли позиция которую выбрал игрок
{
field[choice] = 'x';
gameStatus = winner();
}
else
{
cout << "n" << "THIS POSITION IS ALREADY TAKEN!!!" << "n" << endl;
turn = !turn;
}
turn = !turn;
}
}
system("cls");
fieldUpdate();
switch (gameStatus)
{
case 0: cout << "n" << "EASY BOT - WIN!!!" << "n" << "seriously?.." << "n";
break;
case 1: cout << "n" << "YOU WIN!!!" << "n";
break;
case 2: cout << "n" << "NOBODY WINS :c " << "n";
break;
}
}
void localGM()
//в планах добавить режим игры по локальной сети
{
cout << " IN THE DEVELOPMENT!!!" << endl;
}
int main()
{
srand(time(nullptr)); //для random(), берет за онсову системное время
short int gm;
bool localMP;
bool end = 0; //условия нового матча (gameover)
while (!end)
{
for (short int clean = 0; clean < 9; clean++) //отчистка поля
{
field[clean] = '_';
}
system("cls");//отчистка консоли
cout << "mode?" << "n" << "versus(0)" << "n" << "easy bot(1)" << "n" << "medium bot(2)" << "n" << "hard bot(3)" << "n" << endl;
gm = safe_input();
system("cls");//отчистка консоли
if (gm != 0)
{
cout << "who goes first?" << "n" << "bot(0)" << "n" << "random(1)" << "n" << "player(2)" << endl;
short int randomTurn;
randomTurn = safe_input();
switch (randomTurn) //определение права первого хода
{
case 1: turn = (rand() % 2);
break;
case 2: turn = 0;
break;
case 0: turn = 1;
break;
}
system("cls");
switch (gm) //вызов функции определенного режима игры
{
case 1: botGM1();
break;
case 2: botGM2();
break;
case 3: botGM3();
}
}
else
{
cout << "split screan(0)" << "n" << "local multiplayer(1) (IN THE DEVELOPMENT!!!)" << endl;
localMP = safe_input();
if (localMP) //режимы мультиплеера
{
system("cls");
localGM();
}
else
{
system("cls");
versusGM();
}
}
cout << "n" << "gameover?" << "n" << "no(0)" << "n" << "yes(1)" << endl;
end = safe_input(); //узнаем, хочет ли пользователь закрыть программу или сыграть еще одну игру
}
return 0;
}