Базовий курс програмування на С++. Урок 15. Хрестики-нулики. Feel and relax
Сьогодні ми вивчимо
1. Розв’язок д/з минулого уроку
2. Що робимо?ma
3. За роботу!
4. Проблеми
5. Повний код
1. Вам дано масив. Поміняйте місцями елементи з парними і непарними індексами.
#include <iostream>
#include <conio
#include <time
#include <stdlib
using namespace std;
int main()
{
srand(time(NULL));
system("color A");
const short int size = 5;
int a[size];
cout<< "First array: " <<endl;
for (int i = 0; i <size; ++i)
{
a[i] = rand() % 2000;
cout<< a[i] << "\t";
}
cout<<endl;
int *pB = &a[1];
int *pE = &a[2];
while(pB != &a[size-1])
{
swap(*pB, *pE);
if (pE == &a[size - 1])
break;
pB += 2;
pE += 2;
}
cout<< "\n" << "New array: " <<endl;
for (int i(0); i<size; ++i)
cout<< a[i] << "\t";
_getch();
return 0;
}
2. Вам дано два масиви x[a], y[b]. Створіть третій масив, який буде вміщати:
- елементи обох попередніх масивів
- їх спільні елементи
/* Вам дано два масиви x[a], y[b] 1 елементи обох попередніх масивів 2 їх спільні елементи */ #include <iostream> #include <conio #include <algorithm> #include <time #include <stdlib #include <vector> using namespace std; int main () { srand(time(NULL)); system("color A"); int a,b; cout<< "Enter a, b --> "; cin>> a >> b; int *x = new int [a]; int *y = new int [b]; int size = 2*a+b; int *z = new int [size]; for (int i=0; i<size; ++i) z[i] = -1; cout<< "\nX array: "; for (int i=0; i<a; ++i) { x[i] = rand()%10; cout<< x[i] << "\t"; } cout<< "\nY array: "; for (int i=0; i<b; ++i) { y[i] = rand()%10; cout<< y[i] << "\t"; } cout<< "\nZ array: "; for (int i=0; i<a+b; ++i) { if (i<a) z[i] = x[i]; if (i>=a && i<b+a) z[i] = y[i-a]; } for (int i=0, c=0; i<a; ++i) { for (int j=0; j<b; ++j) { if (x[i] == y[j]) { z[a+b+c] = x[i]; c++; } } } for (int i=0; i<size; ++i) { if (z[i] != -1) cout<< z[i] << "\t"; } cout<<endl; delete[] x; delete[] y; delete[] z; _getch(); return 0; }
Що робимо?
У цьому уроці ми не будемо вивчати нічого нового (майже нічого). Я вирішив дещо узагальнити знання отримані з попередніх уроків у одному проекті. Цим проектом буде дуже примітивна гра. Ми спробуємо написати хрестики-нулики. Думаю, вам варто знову припинити читання статті і написати їх самим! До речі, найкращі ідеї приходять до голови під час відпочинку. Подивіться на що ви здатні. Ніхто не заперечує, що у вас може получитися краще ніж у мене, я теж вчуся.
За роботу!
Спершу потрібно визначити, що наша програма-гра буде робити. Думаю, що важко грати хрестики-нулики без таблиці. Давайте зробимо функцію яка буде виводити на екран таблицю і ті клітинки, що вже заповненні. В мене цей код виглядає так:
void Show_Matrix (charTable[][size])
{
// system("cls");
for (int i=1; i<size; ++i)
{
for (int j = 1; j<size; ++j)
{
cout<<Table[i][j] << ' ';
j<3 ? cout<< '|' : cout<<endl;
}
if(i<3)cout<< "--|--|--" <<endl;
}
}
В ній є масив Table[][size]. size – розмір масиву, дорівнює 4. Саме цей масив буде заповнюватися O та X. Нагадаю, що у функціях не обов’язково вказувати розмір у перших дужках.
Тепер треба подумати як ми будемо здійснювати ходи. Наш масив виглядає так:
Я вирішив, що буду вводити номер клітинки, використовуючи цифри у жовтому квадратику. Перший рядок і перший стовпець я абсолютно не використовую.
Ще на початку програми я написав функцію яка повідомить гравця, як правильно здійснювати хід. Код:
void Logo ()
{
cout<< "11|12|13" <<endl;
cout<< "--|--|--" <<endl;
cout<< "21|22|23" <<endl;
cout<< "--|--|--" <<endl;
cout<< "31|32|33" <<endl;
}
Тепер найважливіше. Ми повинні ходити. Не важко здогадатися, що у найгіршому випадку ми здійснимо 9 ходів. Першим у нашій грі буде здійснювати хід завжди гравець, і завжди хрестики ходять першими. Через це всі ходи можна реалізувати за допомогою такого циклу. Частина коду:
do{
if (count%2)
{
system("cls");
Show_Matrix(Table);
cout<< "\n" << "Хiд комп'ютер: ";
move = PC_move(Table);
cout<<move<<endl;
Show_Matrix(Table);
}
else
{
cout<< "\n" << "Хiд гравця --> ";
cin>>move;
Check_free(Table, move);
Table[move/10][move%10] = 'X';
}
count++;
// Інший код
…
}while (count< (size-1)*(size-1));
Добре тепер ми можемо ходити. Але тут ми зустріли ще декілька функцій. Першою є PC_move. Ця функція вираховує хід комп’ютера. Вона виглядає так:
int PC_move (charTable[][size])
{
// Горизонталь
int count = 0, key;
for (int i=1; i<size; ++i)
{
for (int j=1; j<size; ++j)
{
if (Table[i][j] == 'O')
count++;
else
key = i*10+j;
}
if (count == 2 &&Table[key/10][key%10] ==' ')
{
Table[key/10][key%10] = 'O';
return key;
}
count = 0;
}
// Вертикаль
for (int i=1; i<size; ++i)
{
count = 0, key = 0;
for (int j=1; j<size; ++j)
{
if (Table[j][i] == 'O')
count++;
else
key = j*10+i;
}
if (count == 2 &&Table[key/10][key%10] ==' ')
{
Table[key/10][key%10] = 'O';
return key;
}
}
// Діагональ
for (int i=1; i<size; ++i)
{
count = 0, key = 0;
if (Table[i][i] == 'O')
count++;
else
key = i*10+i;
}
if (count == 2 &&Table[key/10][key%10] ==' ')
{
Table[key/10][key%10] = 'O';
return key;
}
for (int i = 1, j = 3; i<size&& j>0; ++i, --j)
{
count = 0, key = 0;
if (Table[i][j] == 'O')
count++;
else
key = i*10+j;
}
if (count == 2 &&Table[key/10][key%10] ==' ')
{
Table[key/10][key%10] = 'O';
return key;
}
// Безвихідь
for (int i=1; i<size; ++i)
{
for (int j=1; j<size; ++j)
{
if (Table[i][j] == ' ')
{
Table[i][j] = 'O';
return i*10+j;
}
}
}
return -1;
}
Думаю, ви замітили, що наш бот дурний як дошка. По-перше, він довго думає через таку велику к-сть циклів. По-друге, він не вміє оборонятися. В ситуації наведеній на картинці він обере варіант «безвихідь» і поставить нуль в 1 ряд, 3 клітинку. Ну але він працює, що хоча б трішки радує.
Наступна функція Check_free. Її робота заключається у тому, щоб перевірити вміст клітинки і заблокувати роботу програми, коли гравець намагається заповнити вже використану клітинку.
Код:
int Check_free (charTable[][size], int &move)
{
while (!(Table[move/10][move%10] == ' '))
{
cerr<< "Вибачте, дана клiтинка вже занята!" <<endl;
cout<< "Ваш хiд --> ";
cin>>move;
}
return move;
}
Цикл використано не даремно. Звісно ми могли використати звичайну конструкцію if, але вона б працювала тільки один раз, що не гарантує повної безпеки.
Нам залишилося визначити переможця. Це я робив за допомогою функції Check.
Код:
int Check (charTable[][size]) // 1 - перемога хрестикiв | -1 - перемога нуликiв | 0 - без перемоги
{
// Горизонталь
int temp_X = 0, temp_O = 0;
for (int i=1; i<size; ++i)
{
for (int j=1; j<size; ++j)
{
if (Table[i][j] == 'X')
temp_X++;
if (Table[i][j] == 'O')
temp_O++;
}
if (temp_O == 3)
return -1;
if (temp_X == 3)
return 1;
temp_O = temp_X = 0;
}
// Вертикаль
temp_O = temp_X = 0;
for (int i=1; i<size; ++i)
{
for (int j=1; j<size; ++j)
{
if (Table[j][i] == 'X')
temp_X++;
if (Table[j][i] == 'O')
temp_O++;
}
if (temp_O == 3)
return -1;
if (temp_X == 3)
return 1;
temp_O = temp_X = 0;
}
// Дiагоналi
temp_O = temp_X = 0;
for (int i=0; i<size; ++i)
{
if (Table[i][i] == 'X')
temp_X++;
if (Table[i][i] == 'O')
temp_O++;
}
if (temp_O == 3)
return -1;
if (temp_X == 3)
return 1;
temp_O = temp_X = 0;
for (int i=1, j=3; i<size&& j>0; ++i, --j)
{
if (Table[i][j] == 'X')
temp_X++;
if (Table[i][j] == 'O')
temp_O++;
}
if (temp_O == 3)
return -1;
if (temp_X == 3)
return 1;
return 0;
}
Її код дуже схожий на той, що ж у функції PC_move. Перемога досягається, коли буде знайдено 3 однакові символи в певній послідовності. Дивлячись на значення яке повертає функція ми визначаємо переможця. Це вже у main. Ось повний код ф-ції main():
int main ()
{
system ("color A");
setlocale (LC_CTYPE, "ukr");
char Table[size][size];
for (int i=0; i<size; ++i)
{
for (int j=0; j<size; ++j)
Table[i][j] = ' ';
}
int move, count = 0;
Logo();
do{
if (count%2)
{
system("cls");
Show_Matrix(Table);
cout<< "\n" << "Хiд комп'ютер: ";
move = PC_move(Table);
cout<< move <<endl;
Show_Matrix(Table);
}
else
{
cout<< "\n" << "Хiд гравця --> ";
cin>> move;
Check_free(Table, move);
Table[move/10][move%10] = 'X';
}
count++;
//Show_Matrix(Table);
//Перевiрка перемоги
if (Check(Table) == 1)
{
system("cls");
clog<< "\n\nПеремiг гравець!!!" <<endl;
Show_Matrix(Table);
_getch();
return 0;
}
else if (Check(Table) == -1)
{
system("cls");
clog<< "\n\nПеремiг комп'ютер!!!" <<endl;
Show_Matrix(Table);
_getch();
return 0;
}
else
{}
}while (count < (size-1)*(size-1));
system("cls");
Show_Matrix(Table);
clog<< "\n\n" << "Нiчия!!! " <<endl;
_getch();
return 0;
}
Все, програма майже готова. Через велику к-сть функції я вирішив розділити код по двох окремих документах. Всі функції будуть зберігатися у файлі func.h , а ф-ція main у файлі main.cpp. Саме main.cpp є основним і він повинен мати підключення до func.h. Для цього потрібно, щоб обидва файли були у одній папці. Далі просто додаємо його за допомогою include. Ось так:
#include "func.h"
Проблеми
Наша гра досить далека від досконалості. Декілька із проблем:
- Бот грає не оптимально і довго думає. У інтернеті можна знати стратегію гри, і навчити бота грати за нею.
- Мало кому подобається грати у cmd (командному рядку), але ми поки що не вміємо робити щось цікавіше.
- Ввід клітинок теж не комільфо. Карл Дуглас придумав комп’ютерну мишку ще у 1964.
- Гравець може грати тільки з ботом.
- Гравець ходить завжди першим.
Все починається з малого!
Повний код
На випадок, якщо ви не зрозуміли щось вище і хотіли б все попробувати. Ось повний код:
- main.cpp
#include <iostream>
#include <conio.h>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring>
#include "func.h"
#define size 4
using namespace std;
int main ()
{
system ("color A");
setlocale (LC_CTYPE, "ukr");
charTable[size][size];
for (int i=0; i<size; ++i)
{
for (int j=0; j<size; ++j)
Table[i][j] = ' ';
}
int move, count = 0;
Logo();
do{
if (count%2)
{
system("cls");
Show_Matrix(Table);
cout<< "\n" << "Хiд комп'ютер: ";
move = PC_move(Table);
cout<<move<<endl;
Show_Matrix(Table);
}
else
{
cout<< "\n" << "Хiд гравця --> ";
cin>>move;
Check_free(Table, move);
Table[move/10][move%10] = 'X';
}
count++;
//Show_Matrix(Table);
//Перевiрка перемоги
if (Check(Table) == 1)
{
system("cls");
clog<< "\n\nПеремiг гравець!!!" <<endl;
Show_Matrix(Table);
_getch();
return 0;
}
elseif (Check(Table) == -1)
{
system("cls");
clog<< "\n\nПеремiг комп'ютер!!!" <<endl;
Show_Matrix(Table);
_getch();
return 0;
}
else
{}
}while (count< (size-1)*(size-1));
system("cls");
Show_Matrix(Table);
clog<< "\n\n" << "Нiчия!!! " <<endl;
_getch();
return 0;
}
- func.h
#include <iostream> #define size 4 using namespace std; void Logo () { cout<< "11|12|13" <<endl; cout<< "--|--|--" <<endl; cout<< "21|22|23" <<endl; cout<< "--|--|--" <<endl; cout<< "31|32|33" <<endl; } int Check_free (char Table[][size], int &move) { while (!(Table[move/10][move%10] == ' ')) { cerr<< "Вибачте, дана клiтинка вже занята!" <<endl; cout<< "Вашхiд --> "; cin>> move; } return move; } void Show_Matrix (char Table[][size]) { // system("cls"); for (int i=1; i<size; ++i) { for (int j = 1; j<size; ++j) { cout<< Table[i][j] << ' '; j<3 ?cout<< '|' : cout<<endl; } if(i<3)cout<< "--|--|--" <<endl; } } int PC_move (char Table[][size]) { // Горизонталь int count = 0, key; for (int i=1; i<size; ++i) { for (int j=1; j<size; ++j) { if (Table[i][j] == 'O') count++; else key = i*10+j; } if (count == 2 && Table[key/10][key%10] ==' ') { Table[key/10][key%10] = 'O'; return key; } count = 0; } // Вертикаль for (int i=1; i<size; ++i) { count = 0, key = 0; for (int j=1; j<size; ++j) { if (Table[j][i] == 'O') count++; else key = j*10+i; } if (count == 2 && Table[key/10][key%10] ==' ') { Table[key/10][key%10] = 'O'; return key; } } // Діагональ for (int i=1; i<size; ++i) { count = 0, key = 0; if (Table[i][i] == 'O') count++; else key = i*10+i; } if (count == 2 && Table[key/10][key%10] ==' ') { Table[key/10][key%10] = 'O'; return key; } for (int i = 1, j = 3; i<size && j>0; ++i, --j) { count = 0, key = 0; if (Table[i][j] == 'O') count++; else key = i*10+j; } if (count == 2 && Table[key/10][key%10] ==' ') { Table[key/10][key%10] = 'O'; return key; } // Безвихідь for (int i=1; i<size; ++i) { for (int j=1; j<size; ++j) { if (Table[i][j] == ' ') { Table[i][j] = 'O'; return i*10+j; } } } return -1; } int Check (char Table[][size]) // 1 – перемога хрестикiв | -1 – перемога нуликiв | 0 – без перемоги { // Горизонталь int temp_X = 0, temp_O = 0; for (int i=1; i<size; ++i) { for (int j=1; j<size; ++j) { if (Table[i][j] == 'X') temp_X++; if (Table[i][j] == 'O') temp_O++; } if (temp_O == 3) return -1; if (temp_X == 3) return 1; temp_O = temp_X = 0; } // Вертикаль temp_O = temp_X = 0; for (int i=1; i<size; ++i) { for (int j=1; j<size; ++j) { if (Table[j][i] == 'X') temp_X++; if (Table[j][i] == 'O') temp_O++; } if (temp_O == 3) return -1; if (temp_X == 3) return 1; temp_O = temp_X = 0; } // Дiагоналi temp_O = temp_X = 0; for (int i=0; i<size; ++i) { if (Table[i][i] == 'X') temp_X++; if (Table[i][i] == 'O') temp_O++; } if (temp_O == 3) return -1; if (temp_X == 3) return 1; temp_O = temp_X = 0; for (int i=1, j=3; i<size && j>0; ++i, --j) { if (Table[i][j] == 'X') temp_X++; if (Table[i][j] == 'O') temp_O++; } if (temp_O == 3) return -1; if (temp_X == 3) return 1; return 0; }
Оскільки ми просто частково узагальнили наші знання, то вправ для перевірки не буде!
Я не проти вдосконалити нашу гру, можемо обговорити проблеми і шляхи їх вирішення у коментарях.