Міністерство освіти і науки України
Тернопільський національний технічний університет
імені Івана Пулюя
Кафедра комп’ютерних наук
Курсовий проект
з дисципліни
«Технології створення програмних продуктів»
на тему:
"Гра Морський бій"
ЗМІСТ
ВСТУП 4
1 АНАЛІЗ ТЕХНІЧНОГО ЗАВДАННЯ 6
2 ОБГРУНТУВАННЯ АЛГОРИТМУ ТА СТРУКТУРИ ПРОГРАМИ 7
3 РОЗРОБКА ПРОГРАМИ 8
3.1 Розробка системи класів 8
3.2 Розробка UML - діаграм 10
4 ТЕСТУВАННЯ ПРОГРАМИ І РЕЗУЛЬТАТИ ЇЇ ВИКОНАННЯ 12
ВИСНОВКИ 18
ПЕРЕЛІК ВИКОРИСТАНОЇ ЛІТЕРАТУРИ 19
ДОДАТОК А 20
ВСТУП
Всі, хто має справу з комп'ютером, так чи інакше стикалися з комп'ютерними іграми, і практично більшість може назвати декілька ігор, які їм особливо сподобалися. Ті, хто вже зовсім награвся, майже награвся або ще не награвся, але в процесі спілкування з комп'ютером вже почав суміщати ігри з чим-небудь кориснішим, можливо, хотіли б придумати які-небудь свої, не схожі ні на які інші ігри
Багато що захоплює в такій творчості і не сам процес гри, а розробка ігрового всесвіту, її проектування і реалізація. Коли можна злити воєдино сценарій, графіку, музику, майстерно задуманий і вміло запрограмований алгоритм — створити єдиний фантастичний, що живе за законами, які ти ж для нього і придумав.
У даній курсовій роботі мова піде про створення нескладної ігрової програми «Морської бій», яка і буде об'єктом дослідження.
Гра «Морський бій» відома мабуть всім ще з дитинства. Суть гри полягає в тому, щоб потопити всі кораблі на невідомій карті противника, по черзі називаючи координати. Ігрове поле – квадрат 10×10 кожного гравця, на якому розміщується флот кораблів. Перемагає той, хто першим потопить всі кораблі противника.
АНАЛІЗ ТЕХНІЧНОГО ЗАВДАННЯ
Назва задачі: “Реалізація інтерактивних ігрових програм методами ООП, мовою програмування С++” мета задачі полягає у реалізації інтерактивної ігрової програми «Морський бій» – ця програма відома всім з дитинства і для її реалізації було використано середовище програмування Microsoft Visual Studio 2010.
Постановка задачі Розробити ігрову програму «Морської бій», яка включає ігрову панель, що складається з двох полів для людини і комп'ютера. На полі розставляються 10 кораблів. Чотири кораблі розміром в одну клітинку, три кораблі розміром в дві клітинки, два кораблі розміром в три клітинки, і один корабель розміром в чотири клітинки. «Палуби» кораблів на полі зображені літерою «K». По черзі супротивникам надається можливість пострілу по чужому полю. При попаданні клітинка збитої «палуби» корабля замінюється літерою «X», і надається можливість додаткового пострілу, в протилежному випадку пуста клітинка змінюється на літеру «O», і хід переходить до противника. Перемога присуджується гравцю, що потопив всі кораблі супротивника.
Запуск програми здійснюється таким чином: необхідно відкрити папку, що містить дану гру, і знайти виконуваний файл SeaBattle.exe. На цьому файлі слід виконати подвійне клацання лівою клавішею маніпулятора мишки.
Розроблена програма може працювати на будь-якому ІВМ-сумісному комп’ютері починаючи з 80286 і вище. Робота програми не обмежена об’ємом ОЗП (оперативного запам’ятовуючого пристрою) чи жорсткого диску, але рекомендується мати не менше 2 мегабайтів вільного дискового простору.
ОБГРУНТУВАННЯ АЛГОРИТМУ І СТРУКТУРИ ПРОГРАМИ
На початку гри на екран виводиться запитання про вибір автоматичної чи самостійної розстановки кораблів. При виборі автоматичної розстановки автоматично вибирається напрям корабля і його координати. Якщо ж вибрати самостійну розстановку, то з’явиться повідомлення про вибір напряму і координати першої «палуби» корабля, при цьому здійснюється перевірка суміжних клітин. Корабель не розміститься, якщо на сусідніх клітинах буде виявлено інший корабель. Кораблі комп’ютера розставляються автоматично.
Після розміщення кораблів по полю, починається сама гра. Першим «стріляє» гравець. Надається вибір координати на карті комп’ютера, при цьому вносяться зміни в масиви кораблів комп’ютера, та пострілів гравця. Координати за якими стріляє комп’ютер вибираються випадково, при цьому перевіряється, чи вибрав вже комп’ютер ці координати.
В масив карти добавляється інформація з масиву кораблів гравця, де вони зображені літерами «K». Потім в туди заноситься інформація про постріли зроблені гравцями, і відбувається виведення на екран масиву карти, при цьому використовуються масиви кораблів і пострілів гравця і комп’ютера.
Потім встановлюються і перевіряються прапори гравців. Якщо в комп’ютера не залишилося неушкоджених кораблів, на екран виводиться повідомлення «Переміг гравець!», якщо ж кораблів не залишилось в гравця то з’являється «Переміг комп’ютер!».
РОЗРОБКА ПРОГРАМИ
Розробка системи класів
Для того щоб забезпечити виконання поставлених завдань у програмі використовується два основних класи: клас ігрового поля – class BattleField, та похідний від нього клас гравця – class Player. В класі BattleField ініціалізуються масиви кораблів і пострілів (hits, ships), розставляються кораблі, і здійснюється перевірка можливості розташування кораблів в даній координаті.
Клас BattleField є батьківським класом для класу Player. В даному класі вносяться зміни в масиви hits і ships після здійснення ходів комп’ютера та гравця. Для збереження напрямків (горизонтального та вертикального) використовується перечислення enum.
Розробка методів
Клас BattleField використовує методи Set, ShipPlase та ShipInit.
Void ShipsInit – здійснює ініціалізацію кораблів.
void ShipsInit(bool autoPlace)
{
// Ініціалізація масивів hits і ships
for (int i = 0; i < 10; i++)
{
for (int j = 0; j< 10; j++)
{
Ships[i][j] = 1;
Hits[i][j] = 0;
}
}
// Розстановка кораблів
Set(4, autoPlace);
Set(3, autoPlace);
Set(3, autoPlace);
Set(2, autoPlace);
Set(2, autoPlace);
Set(2, autoPlace);
Set(1, autoPlace);
Set(1, autoPlace);
Set(1, autoPlace);
Set(1, autoPlace);
}
Void Set – розташовує кораблі, та здійснює перевірку суміжних клітин.
void Set (int deck, bool automatically)
{
int my = deck - 1;
bool isset = 0;
int s,c;
Direction dir = Direction::H;
while (isset == 0) // перевірка чи вдалося розмістити корабель
{
if (automatically)
{
dir = static_cast<Direction>(rand()%2); // вибираємо напрям
s = rand()%10; //випадковим чином визначаються координати
c = rand()%10;
}
else
{
char tempDir;
cout << "Напрям H/V: ";
cin >> tempDir;
switch (tempDir)
{
case 'H':
case 'h':
dir = Direction::H;
break;
case 'V':
case 'v':
dir = Direction::V;
break;
}
cout << "Рядок: ";
cin >> s;
cout << "Стовбець: ";
cin >> c;
}
int e = 0;
switch (dir)
{
case H:
if (Ships[s][c+deck-1] == 1)
{
e = ShipPlace(s,c,dir,deck); // перевірка суміжних клітин
if (e == 0)
{
for (int i = 0; i < deck; i++)
{
Ships[s][c+i] = 2; // розміщуємо корабель в масиві ships
}
isset = 1;
}
}
break;
case V:
if (Ships[s+deck-1][c] == 1)
{
e = ShipPlace(s,c,dir,deck);
if (e == 0)
{
for (int i = 0; i < deck; i++)
{
Ships[s+i][c] = 2;
}
isset = 1;
}
}
break;
}
}
MapInit(map);
Show(map);
Int ShipPlace – перевіряє, чи можна в даній координаті [s] [c] розмістити корабель з палубами deck.
int ShipPlace(int s, int c, Direction dir, int deck)
{
int e = 0;
switch (dir)
{
case H:
if (Ships[s-1][c-1] == 2)
{
e = 1;
}
if (Ships[s-1][c+deck] == 2)
{
e = 1;
}
if (Ships[s+1][c-1] == 2)
{
e = 1;
}
if (Ships[s+1][c+deck] == 2)
{
e = 1;
}
if (Ships[s][c-1] == 2)
{
e = 1;
}
if (Ships[s][c+deck] == 2)
{
e = 1;
}
for (int i = 0; i < deck; i++)
{
if (Ships[s-1][c+i] == 2)
{
e = 1;
}
if (Ships[s+1][c+i] == 2)
{
e = 1;
}
}
break;
case V:
if (Ships[s-1][c-1] == 2)
{
e = 1;
}
if (Ships[s-1][c+1] == 2)
{
e = 1;
}
if (Ships[s+deck][c-1] == 2)
{
e = 1;
}
if (Ships[s+deck][c+1] == 2)
{
e = 1;
}
if (Ships[s-1][c] == 2)
{
e = 1;
}
if (Ships[s+deck][c] == 2)
{
e = 1;
}
for (int i = 0; i < deck; i++)
{
if (Ships[s+i][c-1] == 2)
{
e = 1;
}
if (Ships[s+i][c+1] == 2)
{
e = 1;
}
}
break;
}
return e;
}
Клас Player використовує конструктор Player:
Player (): DefeatFlag(0) {}
та метод Turn, який вносить зміни в масиви ships та hits.
class Player : public BattleField
{
public:
bool DefeatFlag; // змінна в якій зберігаються дані про поразку
Player (): DefeatFlag(0) {} // конструктор
// Хід комп'ютера: вносимо зміни в масиви human.ships і computer.hits
void Turn(Player& enemy)
{
bool flag = 0;
while (flag == 0)
{
int character = rand()%10; // Координати за якими буде стріляти комп'ютер
int digit = rand()%10; // вибираються випадково
if (Hits[character][digit] != 1) // перевірка: вибирав чи вже комп'ютер ці координати
{
Hits[character][digit] = 1;
flag = 1;
if (enemy.Ships[character][digit] == 2)
{
enemy.Ships[character][digit] = 3;
}
}
}
}
// Хід гравця: вносимо зміни в масиви computer.ships і human.hits
void Turn(Player& enemy, int character, int digit)
{
Hits[character][digit] = 1;
if (enemy.Ships[character][digit] == 2)
{
enemy.Ships[character][digit] = 3;
}
}
};
Функціїя CheckEnding встановлює і перевіряє прапори defeat_flag гравців.
int CheckEnding()
{
int flag = 0;
int human_flag = 0;
int computer_flag = 0;
for (int i=0; i<10;i++)
{
for (int j=0; j<10;j++)
{
if (player1.Ships[i][j] == 2)
human_flag = 1; // у користувача ще залишилися неушкоджені кораблі
if (player2.Ships[i][j] == 2)
computer_flag = 1; // у комп'ютера ще залишилися неушкоджені кораблі
}
}
if (human_flag == 0)
flag = 2;
if (computer_flag == 0)
flag = 1;
if (flag == 1)
{
cout << "Переміг гравець!" << endl;
return 2;
}
if (flag == 2)
{
cout << "Переміг комп'ютер!" << endl;
return 2;
}
}
UML ДІАГРАМА КЛАСІВ
/
ТЕСТУВАННЯ ПРОГРАМИ ТА РЕЗУЛЬТАТ ЇЇ ВИКОНАННЯ
Тестування програми здійснювалося на комп’ютерах в яких була встановлена операційна система Windows 7 або Windows XP.
При запуску програми відкривалось вікно з повідомленням про вибір автоматичного та самостійного розташування кораблів (мал. 5.1).
/
Мал 5.1 – запуск гри
/
Мал 5.2 – процес гри
/
Мал 5.3 – кінець гри (Переміг гравець)
ВИСНОВКИ
Мова програмування С++ є універсальною. Вона створена з метою зробити процес написання програм більш приємним для програмістів. Завдяки використанню визначення нових типів програміст може розділяти програму на частини, що робить програму гнучкішою. Також ООП дає можливість створення власних класів. Саме ці властивості були використані в даній програмі.
Застосувавши для розробки власної програми середовище Microsoft Visual Studio 2010 я переконався в тому, що використання методів об’єктно – орієнтованого програмування приводить до:
Скорочення часу розробки програми;
Скорочення програмного коду;
Зменшення часу виконання програми;
Збільшення ефективності програмування;
Збільшення гнучкості програми;
СПИСОК ВИКОРИСТАНОЇ ЛІТЕРАТУРИ
Белецкий Я. Энциклопедия языка Си: Пер. с польск. — М.: Мир, 1992. — 687 с.
Дьюрхаст С., Старк К. Программирование на С++ – К.: “ДіаСофт”, 1993.
Сван Т. Освоение Borland C++ 4.5.Практический курс. –К.: “Діалектика ”
Уейт М., Прата С., Мартін Д. Язык Си. – Москва: “Мир” , 1988.
Страустрап Б. Язык программирования С++ – К.: “ДіаСофт”, 1993.`
Фейсон Т. Объектно-ориентированое программирование на Borland C++ 4.5. – К.: “Діалектика”, 1996.
Вайнер Р. Пінсон Л. С++ изнутри – К.: “ДіаСофт”, 1993.
ДОДАТОК А
Текст програми
#include <clocale>
#include <conio.h>
#include <stdlib.h>
#include <iostream>
#include <ctime>
using namespace std;
// enum зберігає напрямки: горизонтальний, вертикальний
enum Direction{H, V};
char numbers[10] = { '0','1','2','3','4','5','6','7','8','9'};
// карта разміром в 13 стрічок та 29 стовбців
const int s = 13, c = 29;
char map[s][c] = {
" 0123456789 0123456789 ",
" #----------# #----------#",
"0| | 0| |",
"1| | 1| |",
"2| | 2| |",
"3| | 3| |",
"4| | 4| |",
"5| | 5| |",
"6| | 6| |",
"7| | 7| |",
"8| | 8| |",
"9| | 9| |",
" #----------# #----------#" };
void MapInit(char map[s][c]);
void Show(char map[s][c]);
int Input(char& , char&);
void Test();
int CheckEnding();
// Клас Бойового поля
class BattleField
{
public:
int Ships[10][10]; // масив кораблів
int Hits[10][10]; // масив вистрілів
void ShipsInit(bool autoPlace)
{
// Ініціалізація масивів hits і ships
for (int i = 0; i < 10; i++)
{
for (int j = 0; j< 10; j++)
{
Ships[i][j] = 1;
Hits[i][j] = 0;
}
}
// Розстановка кораблів
Set(4, autoPlace);
Set(3, autoPlace);
Set(3, autoPlace);
Set(2, autoPlace);
Set(2, autoPlace);
Set(2, autoPlace);
Set(1, autoPlace);
Set(1, autoPlace);
Set(1, autoPlace);
Set(1, autoPlace);
}
void Set (int deck, bool automatically)
{
int my = deck - 1;
bool isset = 0;
int s,c;
Direction dir = Direction::H;
while (isset == 0) // перевірка чи вдалося розмістити корабель
{
if (automatically)
{
dir = static_cast<Direction>(rand()%2); // вибираємо напрям
s = rand()%10; //випадковим чином визначаються координати
c = rand()%10;
}
else
{
char tempDir;
cout << "Напрям H/V: ";
cin >> tempDir;
switch (tempDir)
{
case 'H':
case 'h':
dir = Direction::H;
break;
case 'V':
case 'v':
dir = Direction::V;
break;
}
cout << "Рядок: ";
cin >> s;
cout << "Стовбець: ";
cin >> c;
}
int e = 0;
switch (dir)
{
case H:
if (Ships[s][c+deck-1] == 1)
{
e = ShipPlace(s,c,dir,deck); // перевірка суміжних клітин
if (e == 0)
{
for (int i = 0; i < deck; i++)
{
Ships[s][c+i] = 2; // розміщуємо корабель в масиві ships
}
isset = 1;
}
}
break;
case V:
if (Ships[s+deck-1][c] == 1)
{
e = ShipPlace(s,c,dir,deck);
if (e == 0)
{
for (int i = 0; i < deck; i++)
{
Ships[s+i][c] = 2;
}
isset = 1;
}
}
break;
}
}
MapInit(map);
Show(map);
//system("pause");
//system("cls");
}
/*
Функція перевіряє, чи можна в даній координаті [s] [c]
розмістити корабель з палубами deck.
У коментарях показано які клітини перевіряються при установці
чотирипалубному корабля в [4] [3]
*/
int ShipPlace(int s, int c, Direction dir, int deck)
{
int e = 0;
switch (dir)
{
case H:
if (Ships[s-1][c-1] == 2)
{
e = 1;
}
if (Ships[s-1][c+deck] == 2)
{
e = 1;
}
if (Ships[s+1][c-1] == 2)
{
e = 1;
}
if (Ships[s+1][c+deck] == 2)
{
e = 1;
}
if (Ships[s][c-1] == 2)
{
e = 1;
}
if (Ships[s][c+deck] == 2)
{
e = 1;
}
for (int i = 0; i < deck; i++)
{
if (Ships[s-1][c+i] == 2)
{
e = 1;
}
if (Ships[s+1][c+i] == 2)
{
e = 1;
}
}
break;
case V:
if (Ships[s-1][c-1] == 2)
{
e = 1;
}
if (Ships[s-1][c+1] == 2)
{
e = 1;
}
if (Ships[s+deck][c-1] == 2)
{
e = 1;
}
if (Ships[s+deck][c+1] == 2)
{
e = 1;
}
if (Ships[s-1][c] == 2)
{
e = 1;
}
if (Ships[s+deck][c] == 2)
{
e = 1;
}
for (int i = 0; i < deck; i++)
{
if (Ships[s+i][c-1] == 2)
{
e = 1;
}
if (Ships[s+i][c+1] == 2)
{
e = 1;
}
}
break;
}
return e;
}
};
class Player : public BattleField
{
public:
bool DefeatFlag; // змінна в якій зберігаються дані про поразку
Player (): DefeatFlag(0) {} // конструктор
// Хід комп'ютера: вносимо зміни в масиви human.ships і computer.hits
void Turn(Player& enemy)
{
bool flag = 0;
while (flag == 0)
{
int character = rand()%10; // Координати за якими буде стріляти комп'ютер
int digit = rand()%10; // вибираються випадково
if (Hits[character][digit] != 1) // перевірка: вибирав чи вже комп'ютер ці координати
{
Hits[character][digit] = 1;
flag = 1;
if (enemy.Ships[character][digit] == 2)
{
enemy.Ships[character][digit] = 3;
}
}
}
}
// Хід гравця: вносимо зміни в масиви computer.ships і human.hits
void Turn(Player& enemy, int character, int digit)
{
Hits[character][digit] = 1;
if (enemy.Ships[character][digit] == 2)
{
enemy.Ships[character][digit] = 3;
}
}
};
Player player1;
Player player2;
// добавлення в масив map інформації із human.ships.
void MapInit(char map[s][c])
{
for (int i=0;i < 10; i++)
{
for (int j=0; j < 10; j++)
{
if (player1.Ships[i][j] == 2)
map[i+2][j+2] = 'К';
}
}
}
/*
Спочатку в масив maps заноситься інформація про постріли зроблених
противниками.
Потім