это быстро и бесплатно
Оформите заказ сейчас и получите скидку 100 руб.!
ID (номер) заказа
3680767
Ознакомительный фрагмент работы:
Введение
Одной из самых востребованных функций персонального компьютера,
являются игры. Ежегодно выпускается огромное количество новых
компьютерных игр, и каждая из них находит своего поклонника среди
пользователей. Многие IT-компании специализируются только на разработки
компьютерных игр.
Несмотря на кажущуюся «несерьезность» данного направления,
разработка компьютерной игры требует глубоких знаний в области
программирования, и большое количество иных ресурсов (работа
дизайнеров, тестировщиков и т.п.).
Целью исследования, проводимого в рамках настоящей курсовой
работы, является разработка программы игры «Морской бой».
Для достижения данной цели необходимо:
-провести анализ поставленной задачи;
-выполнить проектирование классов программы;
-выполнить разработку программы на языке С++;
-выполнить тестирование программы;
-составить руководство пользователя.
4
1 Анализ поставленной задачи
1.1 Постановка задачи
Разработка игры «Морской бой»
Реализовать консольную игру с компьютером. Пользователь
устанавливает свои корабли через консоль или загружает из файла.
Компьютер размещает корабли случайным образом (не нарушая правил
размещения). Компьютер не должен делать заведомо ошибочные выстрелы.
В конце игры отображаются карты пользователя и компьютера с
отмеченными выстрелами.
1.2 Описание предметной области
«Морской бой» - игра для двух участников, в которой игроки по
очереди называют координаты на неизвестной им карте соперника. Если у
соперника по этим координатам имеется корабль (координаты заняты), то
корабль или его часть «топится», а попавший получает право сделать ещё
один ход. Цель игрока - первым поразить все корабли противника.
Игра была «придумана» в 1931 году Милтоном Брэдли (Milton Bradley).
Именно тогда, его компанией была выпущена «настольная игра» как
коммерческий продукт. Идея игры пришла во время первой мировой войны,
в которой было много морских сражений. Есть данные, что игра
существовала в «бумажном» варианте существенно раньше, еще до Первой
мировой войны, и истинный автор неизвестен.
Игровое поле - обычно квадрат 10×10 каждого игрока, на котором
размещается флот кораблей. Горизонтали обычно нумеруются сверху вниз, а
вертикали помечаются буквами слева направо. При этом используются буквы
русского алфавита от «а» до «к» (буквы «ё» и «й» обычно пропускаются)
либо от «а» до «и» (с использованием буквы «ё»), либо буквы латинского
алфавита от «a» до «j».
5
На игровом поле размещаются:
-1 корабль — ряд из 4 клеток («четырёхпалубные»);
-2 корабля — ряд из 3 клеток («трёхпалубные»);
-3 корабля — ряд из 2 клеток («двухпалубные»);
-4 корабля — 1 клетка («однопалубные»).
При размещении корабли не могут касаться друг друга сторонами и
углами. Встречаются, однако, варианты, когда касание углами не
запрещается. Встречаются также варианты игры, когда корабли могут
размещаться буквой «Г» («трех-» и «четырехпалубные»), квадратом или
зигзагом («четырехпалубные»). Существуют варианты игры с другим
набором кораблей и/или другой формой поля.
Рядом со «своим» полем чертится «чужое» такого же размера, только
пустое. Это участок моря, где плавают чужие корабли противника.
При попадании в корабль противника - на чужом поле ставится
крестик, при холостом выстреле - точка. Попавший стреляет ещё раз.
Если выстрел пришёлся в клетку, не занятую ни одним кораблём
противника, то следует ответ «Мимо!» и стрелявший игрок ставит на чужом
квадрате в этом месте точку. Право хода переходит к сопернику.
Если выстрел пришёлся в клетку, где находится многопалубный
корабль (размером больше чем 1 клетка), то следует ответ «Ранил!» или
«Попал!». Стрелявший игрок ставит на чужом поле в эту клетку крестик, а
его противник ставит крестик на своём поле также в эту клетку. Стрелявший
игрок получает право на ещё один выстрел.
Если выстрел пришёлся в клетку, где находится однотрубный корабль
или последнюю непоражённую клетку многопалубного корабля, то следует
ответ «Убил!» или «Потопил!». Оба игрока отмечают потопленный корабль
на листе. Стрелявший игрок получает право на ещё один выстрел.
Победителем считается тот, кто первым потопит все 10 кораблей
противника.
6
Одна из выигрышных стратегий описана Я. И. Перельманом. Игрока,
использующего эту выигрышную стратегию, назовём Виктором; другого
игрока (не использующего выигрышную стратегию Я. И. Перельмана)
назовём Петром.
Вокруг каждого корабля можно нарисовать область (толщиной в одну
клетку), в которой не может быть других кораблей - эту область назовём
ореолом данного корабля. Выигрышная стратегия Перельмана состоит в том,
что Виктор свои многоклеточные корабли компактно располагает в одном из
углов поля, «вжимая» в этот угол так, как только возможно. Одноклеточные
корабли Виктор равномерно распределяет по оставшейся незанятой
многоклеточными кораблями части поля. Скорее всего, Пётр относительно
быстро обнаружит, что много кораблей Виктора компактно сосредоточенно в
этом углу, и быстро уничтожит все корабли Виктора, кроме одноклеточных.
После этого, чтобы найти одноклеточные корабли Виктора, Петру надо будет
исследовать своими ходами-выстрелами очень большую площадь, поскольку
ореолы многоклеточных кораблей Виктора перекрываются, плюс к тому
львиная доля площади ореолов кораблей, прижатых к кромке поля,
оказывается за пределами поля. Между тем, благодаря тому, что у Петра
перекрывается меньше площади ореолов, чем у Виктора, Виктору нужно
исследовать меньшую площадь поля Петра, чем Петру площадь поля
Виктора.
Существует много компьютерных программ, имитирующих игру.
Компьютерные реализации могут отличаться наличием звуков,
автоматическим обозначением полей, где не может быть корабля, и т. п.
Примеры реализаций:
-KBattleship (Naval Battle) - из набора игр KDE Games;
-Морской бой, разработанный по заказу Министерства обороны РФ;
-Battleship - разновидность морского боя для Linux.
7
2 Проектирование и разработка программы
2.1 Проектирование структуры приложения
В программе выделим следующие классы:
-Map – карта.
-Player – игрок (абстрактный класс);
-BotPlayer, HumanPlayer – игрок человек и игрок компьютер
(наследники);
Ship – корабль;
Graph –вывод графики в консольном окне.
Классы и отношения между ними показаны на рисунке 2.1.
Рисунок 2.1 – Диаграмма классов
2.2 Разработка классов приложения
Начнем рассмотрение классов с класса Graph. Класс содержит функции
для рисования на консоли.
Поля класса:
8
HWND hWnd; //handle консольного окна
HDC hDC; //Device Context консольного окна
HBRUSH Brush; //кисть
HPEN Pen; //Карандаш
COORD graphCurcor; //Курсор
const int Size = 20; //Размер
const int XPos = 0; //Положение по горизонтали
const int YPos = 0; //вертикали
Класс имеет конструктор по умолчанию Graph()
Методы класса
void InitGraph() – инициализация графики (закрытый метод, вызывается
из конструктора)
void gotoxyCrt(int x, int y) // помещает курсор на опред.координаты
(текстовый курсор)
void gotoxy(int x, int y) ) // помещает курсор на опред.координаты
(графический курсор)
void DrawRectangle(DWORD Color) //рисование прямоугольника
void DrawChar(char c, DWORD Color) //рисование символа
Класс Map описывает карту для вывода игрового поля.
Поля класса:
Graph *graph; //ссылка на класс для вывода графики
int X = 0; //координаты
int Y = 0; //координаты
static bool NeedID; //требует инициализации
public:
const int N = 12; //размеры 10+2 под границы
private:
vector<vector<int>> map; //вектор
vector<vector<bool>> mask; //маски , true - видимый
Класс имеет 2 конструктора и деструктор:
9
Map(bool visible)
Map(bool visible, Graph * graph, int x, int y):Map(visible)
~Map()
Методы класса:
void Cout(int id) //вывод карты
DWORD IntToColor(int value) //Заменить условное обозначение на цвет
void Init(bool visible) // Инициализация карты. Граница заполнена
значением 100, а все остальные элементы, обозначающие пустые клетки,
значением -1.
void SaveToFile(ofstream &stream) //сохранить в файл
bool LoadFromFile(ifstream &stream) //загрузить из файла
void Show() //Вывести на экран
void set(int x, int y, int value) //Установть значение
bool visible() //Проверка видимости
void setVisible(int x, int y, bool value) //Установить видимость
void setVisible(bool value) // Установить видимость
bool LostAll()//Все корабли уничтожены (нет ни одного
положительного id)
void OpenBorders(int x,int y, int value, int level) //Открыть границы, если
пометка на карте == value
Класс Player описывает игрока. Класс имеет двух наследников – игрок
человек и игрок компьютер.
Поля класса:
protected:
Map * Own; //Своя карта
Map * Enemy; //Карта противника
Graph * graph; //Куда рисовать
vector<int> enemyships0 = { 4,3,3,2,2,2,1,1,1,1 }; //список кораблей
vector<int> enemyships = { 4,3,3,2,2,2,1,1,1,1 };
Класс имеет конструктор и деструктор:
10
Player(Map * Own, Map * Enemy, Graph* graph)
~Player()
Методы класса:
virtual bool Move() = 0; //abstract //ход, определяется в
наследниках
ShotResult Shot(char letter, char digit) // стреляет по заданным
координатам на игровом поле. В случае попадания функция уменьшает
количество жизней у корабля по которому попали. Когда жизни станут равны
нулю то корабль потоплен. Функция возвращает 1 если промах, 2 – условный
промах для бота, 3 ранен, 4 убит. Также эта функция должна метит клетки на
поле значением -2 по которым уже стреляли.
Класс HumanPlayer наследник класса Player, описывает игрока
человека.
В классе описаны два дополнительных поля, для обработки ввода хода:
char letter = 'A'; //буква
char digit = '1'; //цифра
Класс имеет конструктор и деструктор:
HumanPlayer(Map * Own, Map * Enemy, Graph *
graph):Player(Own,Enemy,graph)
~HumanPlayer()
В классе переопределен метод выполняющий ход:
virtual bool Move() override
Класс BotPlayr наследник класса Player, описывает игрового бота
(игрок-компьютер).
В классе описаны дополнительные поля:
int x = 0; // текущие координаты для стрельбы
int y = 0; // текущие координаты для стрельбы
Direction dir; //направление
int mode = 0; // режим работы бота
11
int firstHitX = 0; // координаты палубы по которой попали первой
int firstHitY = 0; // координаты палубы по которой попали первой
vector <Direction> dirs; //вектор возможных направлений
Класс имеет конструктор и деструктор:
BotPlayer(Map * Own, Map * Enemy, Graph * graph) :Player(Own,
Enemy, graph)
~BotPlayer()
Методы класса:
void initDirsVec(vector <Direction> & dirs) // инициализация
вектора направлений стрельбы для бота
Direction getDirFromVec(vector <Direction> & dirs) // получение из
вектора очередного направлений стрельбы для бота
bool changeCoord(Direction dir, int &x, int &y) //изменение
координат прицела бота в зависимости от направления. Возвращает ложь
если менять направление поиска не надо. Возвращает истину если менять
направление поиска надо
В классе переопределен метод выполняющий ход:
virtual bool Move() override
Функция имеет два режима стрельбы: случайный и режим поиска после
первого попадания в корабль. После каждого попадания функция должна
проверять потоплены ли все корабли. Для этого используется функцию
playerIsWin(ships) которая получает в качестве параметра массив кораблей
противника и возвращает true если все корабли потоплены, false в обратном
случае.
Класс Ship описывает корабль.
Поля класса:
static const int NUM_SHIPS = 10; //количество кораблей
12
vector<int> shipsize = { 4, 3, 3, 2, 2, 2, 1, 1, 1, 1 }; //список
кораблей
int id;//номер корабля
public:
int x, y;//координаты начала
Direction dir; //направление
private:
Map *map; //Карта
int sizeShip; //Размер
Класс имеет конструктор и деструктор:
Ship(Map *map, int id)
~Ship()
Методы класса:
void MoveByDir(int *x, int *y, Direction dir) //переместить в
указанном напрвлении
bool setRandShip()//Функция использует функцию setShipInMap и
устанавливает по случайным координатам и случайному направлению
корабль заданной длины с заданным номером.
bool Place(int x, int y, Direction dir) //Использует функцию
setShipInMap и устанавливает корабль на карту. Перед установкой корабля с
помощью функции shipSettingIsPossible проверяется возможно ли установка
корабля по заданным координатам, в заданном направлении. Функция
принимает в качестве параметров карту, координаты, направление, размер и
номер корабля. Номер корабля записывается в каждую ячейку массива,
занимаемую кораблем. В случае успешности установки корабля функция
возвращает истину, в обратном случае ложь.
void Remove()//Удалить с карты
int check(int x, int y) //Проверяет возможно ли установить в
указанную клетку корабль
13
bool shipSettingIsPossible(int x,int y, Direction dir) // Функция
проверяет возможно ли установить корабль на карту по заданным
координатам, в заданном направлении, заданного размера. Возвращает
истину если постановка возможно и ложь, если не возможна. Функция не
записывает корабль в массив, только проверять возможность установки.
bool shipInMap(int x, int y, Direction dir) // Функция проверки
нахождения корабля в пределах карты. Эта функция возвращает true если
корабль помещается на карте и false в обратном случае.
static vector<Ship*> setRandShips(Map* map) //Функция в
случайном месте и в случайном направлении (горизонтальном или
вертикальном) расставляет корабли
static void SaveToFile(ofstream &stream, vector<Ship*> ships)
//Сохранение в файл
static bool LoadFromFile(ifstream &stream, vector<Ship*> &ships)
//Загрузка из файла
14
3 Описание разработанного приложения
В процессе работы над курсовым проектом разработана игры
«Морской бой» с консольной графикой.
Интерфейс программы сразу после запуска показан на рисунке 3.1.
Рисунок 3.1 – Интерфейс программы при старте
На этом этапе пользователю доступны следующие команды:
Цифра 0..9 – номер корабля для перемещения (если верная
расстановка);
R – поворот корабля;
Стрелки - перемещение корабля;
При повороте и перемещении может случиться «неверная
расстановка». Выбранный корабль не рисуется, управление продолжает
работать.
S – сохранить позицию (только если верная расстановка);
L – загрузить позицию;
G – начать игру;
Q – выход из программы.
Пример отредактированной расстановки и сохраненной в файл показан
на рисунке 3.2.
15
Рисунок 3.2 – Отредактированная расстановка и сохранение ее в файл
Содержание файла mychema:
-100 -100 -100 -100 -100 -100 -100 -100 -100 -100 -100 -100
-100 -1 -1 8 -1 -1 -1 -1 -1 -1 6 -100
-100 3 -1 -1 -1 -1 -1 -1 -1 -1 -1 -100
-100 3 -1 -1 -1 -1 1 1 1 -1 -1 -100
-100 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -100
-100 -1 0 -1 -1 -1 -1 -1 -1 5 -1 -100
-100 -1 0 -1 -1 -1 7 -1 -1 5 -1 -100
-100 -1 0 -1 2 -1 -1 -1 -1 -1 -1 -100
-100 -1 0 -1 2 -1 -1 -1 -1 -1 -1 -100
-100 -1 -1 -1 2 -1 -1 -1 -1 -1 9 -100
-100 -1 -1 -1 -1 -1 -1 4 4 -1 -1 -100
-100 -100 -100 -100 -100 -100 -100 -100 -100 -100 -100 -100
2 5 1
8 3 2
4 7 1
1 2 1
8 10 2
9 6 3
10 1 0
6 6 3
3 1 1
10 9 3
На рисунке 3.3 показан интерфейс программы после начала игры.
16
Рисунок 3.3 – Начало игры
При игре доступны следующие команды:
Esc – сдаться;
Буква A...J – выбрать строку для выстрела;
Цифра 0..9 – выбрать столбец для выстрела;
ENTER – сделать выстрел.
Программа помнит предыдущий выстрел, поэтому можно вводить
только одно значение (например, только «2», если предыдущий ход был A1, а
нужно сделать A2)
При попытке стрелять по уже проверенным клеткам ничего не
происходит, ход остается у игрока-человека.
При попадании (ранил или убил) дается дополнительный ход.
На рисунках 3.4-3.11 показана тестовая игра, которая закончена
выигрышем человека.
17
Рисунок 3.4 – Первый ход (ранение)
На «своем» поле корабли отмечены зеленым, белым пустые клетки. На
вражеском поле желтым отмечаются попадания, белым в клетки выстрел
куда не имеет смысла.
Рисунок 3.5 – Второй ход (мимо)
Красным отмечаются выстрелы мимо цели.
18
Рисунок 3.6 – Первый убитый корабль
Рисунок 3.7 – Продолжение игры 1
19
Рисунок 3.8 – Продолжение игры 2
Рисунок 3.9 – Продолжение игры 3
20
Рисунок 3.10– Продолжение игры 3
Рисунок 3.11 – Окончание игры (человек выиграл у компьютера)
21
Заключение
В процессе работы над курсовым проектом разработана игры
«Морской бой» с консольной графикой.
Программа предназначена для игры человека с компьютером. В начале
игрок имеет возможность расставить корабли на игровом поле и сохранить
расстановку в файл. Сохраненную расстановку можно загрузить из файла.
При разработке программы использовались объектно-ориентированные
технологии. В программе определено 6 классов, 3 из которых образуют
иерархию наследования.
Программа написана на языке С++ с использованием среды разработки
Visual Studio.
22
Список использованной литературы
1. Ашарина, И.В. Объектно-ориентированное программирование в
С++: лекции и упражнения: Учебное пособие для вузов / И.В. Ашарина. - М.:
РиС, 2015. - 336 c.
2. Брайан У. Язык программирования C / Брайан У., Керниган,
Деннис М. Ритчи. — М.: Вильямс, 2017. — 288с.
3. Бьерн Страуструп. Язык программирования C++. — Москва:
Издательство «Бином», 2015 — 1136 c.
4. Васильев А.Н. Программирование на C++ в примерах и задачах.
— Москва: Издательство «Эксмо», 2018 —368 c.
5. Готтшлинг П. Современный С++ для программистов, инженеров
и учёных. — М.: И. Д. Вильямс, 2016. — 512 с.: ил.
6. Комлев Н. Объектно Ориентированное Программирование.
Настольная книга программиста/ Н. Комлев – М.: Солон-Пресс, 2018 - 298с.
7. Лафоре, Р. Объектно-ориентированное программирование в С++ /
Р. Лафоре. - СПб.: Питер, 2018. - 928 c.
8. Липпман С. Язык программирования C++. Базовый курс / Стенли
Б. Липпман, Жози Лажойе, Барбара Э. Му – М.:Вильямс, 2017 - 1120с.
9. Павловская, Т. С/С++.Процедурное и объектно-ориентированное
программирование / Т. Павловская. - СПб.: Питер, 2018. - 496 c.
10. Пахомов Б. C/C++ и MS Visual C++ 2012 для начинающих /
Борис Пахомов –СПб.: БХВ-Петербург, 2015 - 518с.
11. 3. Перри Г. Программирование на C для начинающих / Грег
Перри, Дин Миллер – М.:Эсмо, 2015 - 368с.
12. Хорев, П.Б. Объектно-ориентированное программирование / П.Б.
Хорев. - М.: Academia, 2018. - 352 c.
13. Шилдт Г. C++. Полное руководство / Герберт Шилдт – М.:
Вильямс, 2017 - 704с.
23
Приложение А Исходный код программы
Модуль BotPlayer.h
#pragma once
#include "Player.h"
class BotPlayer :
public Player
{
public:
BotPlayer(Map * Own, Map * Enemy, Graph * graph) :Player(Own, Enemy,
graph)
{
}
~BotPlayer()
{
}
private:
// инициализация вектора направлений стрельбы для бота
void initDirsVec(vector <Direction> & dirs)
{
dirs.clear();
dirs.push_back(Right);
dirs.push_back(Down);
dirs.push_back(Left);
dirs.push_back(Up);
//Выбрать случайный порядок проверки (перетасовать)
for (int k = 0; k < 100; k++)
{
int index1 = rand() % 4;
int index2 = rand() % 4;
Direction temp = dirs[index1];
dirs[index1] = dirs[index2];
dirs[index2] = temp;
}
}
// получение из вектора очередного направлений стрельбы для бота
Direction getDirFromVec(vector <Direction> & dirs)
{
Direction dir = Left;
if (!dirs.empty())
{
dir = dirs.back();
dirs.pop_back();
}
24
return dir;
}
//изменение координат прицела бота в зависимости от направления.
//возвращает ложь если менять направление поиска не надо
//возвращает истину если менять направление поиска надо
bool changeCoord(Direction dir, int &x, int &y)
{
bool changeDir = false;
int tx = x;
int ty = y;
switch (dir)
{
case Right:
x++;
break;
case Down:
y++;
break;
case Left:
x--;
break;
case Up:
y--;
break;
}
if (Enemy->get(x,y) == -100)
{
x = tx;
y = ty;
changeDir = true;
}
//!! возможное улучшение работы бота:
//Проверить "бессмысленно стрелять" (например, уже)
return changeDir;
}
// текущие координаты для стрельбы
int x = 0;
int y = 0;
Direction dir;
// режим работы бота
int mode = 0;
// координаты палубы по которой попали первой
int firstHitX = 0;
int firstHitY = 0;
//вектор возможных направлений
vector <Direction> dirs;
25
public:
//Ход игрока (бота)
//Функция имеет два режима стрельбы: случайный и режим поиска после
первого попадания в корабль.
//После каждого попадания функция должна проверять потоплены ли все
корабли.
//Для этого используйте готовую функцию playerIsWin(ships) которая
получает в качетсве праметра массив кораблей противника
//и возвращает true если все корабли потоплены, false в обратном случае.
virtual bool Move() override
{
ShotResult resultShot = Miss;
char letter;
char digit;
do
{
//случайный режим стрельбы
if (mode == 0)
{
int count = 200;
do // стрелять до тех пор пока результат "условный
промах"
{
x = rand() % (Enemy->N - 2) + 1;
y = rand() % (Enemy->N - 2) + 1;
letter = x + 'A' - 1;
digit = y + '1' - 1;
count--;
resultShot = Shot(letter, digit);
} while (resultShot == 2 && count>0);
if (count <= 0) return false; //Нет хода
//cout << letter << digit<<" ";
switch (resultShot)
{
case Miss: //мимо
return true;
case Wound:// ранен
mode = 1; // переход в режим поиска остальных
палуб
initDirsVec(dirs);
firstHitX = x;
firstHitY = y;
dir = getDirFromVec(dirs);
break;
case Kill:
mode = 0; //Вернуться в режим случайной
стрельбы
if (Enemy->LostAll()) return false;
26
Sleep(1000);
continue;
};
}
//режим стрельбы после первого попадания в корабль
if (mode == 1)
{
bool changeDir = changeCoord(dir, x, y);
//изменить направление поиска палуб
if (changeDir)
{
dir = getDirFromVec(dirs);
// восстанавливаем координаты первого
попадания по кораблю и от них ищем в новом направлении
x = firstHitX;
y = firstHitY;
// смена направления происходит при
столкновении с границей
continue;
}
letter = x + 'A' - 1;
digit = y + '1' - 1;
// стрельба
resultShot = Shot(letter,digit);
if (resultShot == Miss) // промах
{
dir = getDirFromVec(dirs);
x = firstHitX;
y = firstHitY;
return true;
}
else if (resultShot == Wound) // ранен
{
continue; //Продолжать стрелять в том же
направлении
}
else if (resultShot == Kill) // убит наконец то
{
mode = 0;
if (Enemy->LostAll()) return false;
}
}
Sleep(1000);
} while (resultShot != Miss);
return true;
}
27
};
Модуль Graph.h
#pragma once
#include <windows.h>
#include <wingdi.h>
class Graph
{
HWND hWnd; //handle консольного окна
HDC hDC; //Device Context консольного окна
HBRUSH Brush; //кисть
HPEN Pen; //Карандаш
COORD graphCurcor;
const int Size = 20;
const int XPos = 0; //Положение по горизонтали
const int YPos = 0; //вертикали
void InitGraph()
{
hWnd = GetConsoleWindow(); //Получить handle консольного окна
Pen = CreatePen(PS_SOLID, 1, 0x0F0F0F); //Карандаш, почти черный
}
public:
Graph()
{
InitGraph();
}
//Текстовый курсор
void gotoxyCrt(int x, int y) // помещает курсор на опред.координаты
{
COORD p = { (short)x, (short)y };
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), p);
}
//Графический курсор
void gotoxy(int x, int y)
{
graphCurcor = COORD{ (short)x, (short)y };
}
~Graph()
{
DeleteObject(Pen);
}
28
void DrawRectangle(DWORD Color)
{
int col = graphCurcor.X;
int row = graphCurcor.Y;
hDC = GetDC(hWnd); //Получить Device Context консольного окна
//Обновить кисть
Brush = CreateSolidBrush(Color);
//Выбрать карандаш и кисть
SelectObject(hDC, Pen);
SelectObject(hDC, Brush);
//Нарисовать
Rectangle(hDC,
XPos + col*Size, YPos + row*Size,
XPos + (col + 1)*Size, YPos + (row + 1)*Size
);
DeleteObject(Brush);
ReleaseDC(hWnd, hDC);
}
void DrawChar(char c, DWORD Color)
{
int col = graphCurcor.X;
int row = graphCurcor.Y;
hDC = GetDC(hWnd); //Получить Device Context консольного окна
Brush = CreateSolidBrush(Color);
//Выбрать карандаш и кисть
SelectObject(hDC, Pen);
SelectObject(hDC, Brush);
RECT R = {
XPos + col*Size,
YPos + row*Size,
XPos + (col + 1)*Size,
YPos + (row + 1)*Size
};
//Нарисовать
DrawTextA(hDC, &c, 1, &R, DT_CENTER);
DeleteObject(Brush);
ReleaseDC(hWnd, hDC);
}
};
Модуль HumanPlayer.h
#pragma once
#include <conio.h>
#include "Player.h"
#include "Graph.h"
#include <iostream>
using namespace std;
29
class HumanPlayer :
public Player
{
public:
HumanPlayer(Map * Own, Map * Enemy, Graph *
graph):Player(Own,Enemy,graph)
{
}
~HumanPlayer()
{
}
char letter = 'A';
char digit = '1';
//Ход игрока (человека)
virtual bool Move() override
{
while (true)
{
graph->gotoxyCrt(0, 0); cout.width(77);
cout << ""; //Очистить строку
graph->gotoxyCrt(0, 0);
cout<<"Буква, цифра, <ENTER> :
"<<letter<<(digit==':'?'0':digit);
//Читать цифру или букву или Enter
char c = _getch();
if (c == 27) return false; //Досрочный выход
if (c >= '0' && c <= '9') digit = c;
if (digit == '0') digit = '0' + 10; //Это 10-я клетка
if (c >= 'A' && c <= 'J') letter = c;
if (c >= 'a' && c <= 'j') letter = c - 'a' + 'A';
if (c == 13)
switch (Shot(letter, digit))
{
case Miss:
Enemy->Show();
return true;
case Reshot:
Enemy->Show();
break;
case Wound:
case Kill:
Enemy->Show();
break;
}
}
}
};
30
Модуль Map.h
#pragma once
#include <vector>
#include <fstream>
using namespace std;
#include "Graph.h"
class Map
{
public:
Graph *graph;
int X = 0;
int Y = 0;
static bool NeedID;
public:
const int N = 12; //10+2 под границы
private:
vector<vector<int>> map;
vector<vector<bool>> mask; //true - видимый
void Cout(int id)
{
DWORD color = IntToColor(id);
graph->DrawRectangle(color);
}
//Заменить условное обозначение на цвет
DWORD IntToColor(int value)
{
if (value == -3) return 0x00FFFF;// подбитая палуба
if (value == -2) return 0x0000FF;// промах
if (value == -1) return 0xFFFFFF;// пустая клетка
if (value == -100) return 0xFF0000;// граница поля
if (value == -4) return 0x808080; // туман войны
if (value >= 0 && value<=9) return 0x00FF00;// корабль
return 0;
}
public:
// Инициализация карты
// Граница заполнена значением 100, а все остальные элементы,
обозначающие пустые клетки, значением -1.
void Init(bool visible)
{
//размеры карты
map.resize(N);
31
for (int i = 0; i < N; i++)
{
map[i].resize(N);
for (int j = 0; j < N; j++)
map[i][j] = (i == 0 || j == 0 || i == N - 1 || j == N - 1) ? -100 :
-1;
}
//размеры маски (видимости)
mask.resize(N);
for (int i = 0; i < N; i++) mask[i].resize(N);
setVisible(visible);
}
public:
Map(bool visible)
{
Init(visible);
}
Map(bool visible, Graph * graph, int x, int y):Map(visible)
{
this->graph = graph;
this->X = x;
this->Y = y;
}
~Map()
{
for (int i = 0; i < N; i++) map[i].resize(0);
map.resize(0);
}
void SaveToFile(ofstream &stream)
{
for (int r = 0; r < N; r++)
{
for (int c = 0; c < N; c++)
stream << map[r][c] << " ";
stream << endl;
}
}
bool LoadFromFile(ifstream &stream)
{
if (!stream.is_open()) return false;
for (int r = 0; r < N; r++)
for (int c = 0; c < N; c++)
stream >> map[r][c];
return true;
}
32
// Функцию Show игровое поле на экран.
void Show()
{
int id;
for (int y = 0; y < N; y++)
{
for (int x = 0; x < N; x++)
{
graph->gotoxy(X + x, Y + y);
id = map[y][x];
if (mask[y][x] || id==-100)
{
Cout(id);
if (NeedID && id >= 0 && id <= 9)
{
graph->gotoxy(X + x, Y + y);
graph->DrawChar('0' + id, 0);
//IntToColor(id)
}
}
else //Туман войны
Cout(-4);
}
}
//Надписи
for (int k = 0; k < 10; k++)
{
graph->gotoxy(X+k+1,Y);
graph->DrawChar(k + 'A', 0xFFFFFF);
graph->gotoxy(X + k + 1, Y + N-1);
graph->DrawChar(k + 'A', 0xFFFFFF);
graph->gotoxy(X, Y + k+1);
graph->DrawChar((k+1)%10 + '0' , 0xFFFFFF);
graph->gotoxy(X + N-1, Y + k + 1);
graph->DrawChar((k + 1) % 10 + '0', 0xFFFFFF);
}
}
void set(int x, int y, int value)
{
if (x == 0 || y == 0 || x == N - 1 || y == N - 1) map[y][x] = -100; //Не
менять границу
map[y][x] = value;
}
int get(int x, int y)
{
return map[y][x];
}
33
bool visible()
{
return mask[0][0];
}
void setVisible(int x, int y, bool value)
{
mask[y][x] = value;
}
void setVisible(bool value)
{
for (int y = 0; y < N; y++)
for (int x = 0; x < N; x++)
mask[y][x] = value;
}
//Все корабли уничтожены (нет ни одного положительного id)
bool LostAll()
{
for (int x = 0; x < N; x++)
for (int y = 0; y < N; y++)
if (map[y][x]>0) return false;
return true;
}
//Открыть границы, если пометка на карте == value
void OpenBorders(int x,int y, int value, int level)
{
if (map[y][x] != value) return;
if (level < 0) return;
setVisible(x, y, true);
setVisible(x+1, y, true);
setVisible(x-1, y, true);
setVisible(x, y+1, true);
setVisible(x, y-1, true);
//Рекурсивный вызов
OpenBorders(x + 1, y, value, level-1);
OpenBorders(x - 1, y, value, level - 1);
OpenBorders(x, y+1, value, level - 1);
OpenBorders(x, y - 1, value, level - 1);
}
};
Модуль Player.h
#pragma once
#include "Graph.h"
34
#include "Map.h"
#include "Ship.h"
#include <iostream>
using namespace std;
enum ShotResult {Miss=1,Reshot,Wound,Kill};
class Player
{
protected:
Map * Own; //Своя карта
Map * Enemy; //Карта противника
Graph * graph; //Куда рисовать
vector<int> enemyships0 = { 4,3,3,2,2,2,1,1,1,1 };
vector<int> enemyships = { 4,3,3,2,2,2,1,1,1,1 };
public:
Player(Map * Own, Map * Enemy, Graph* graph)
{
this->Enemy = Enemy;
this->Own = Own;
this->graph = graph;
for (unsigned int k = 0; k < enemyships.size(); k++)
enemyships[k] = enemyships0[k];
}
~Player()
{
}
virtual bool Move() = 0; //abstract
//Функцию shot стреляет по заданным координатм на игровом поле.
//В случае попадания функция уменьшает количество жизней у корабля по
которому попали.
//Когда жизни станут равны нулю то корабль потоплен. Функция возвращает
1 если промах, 2 – условный промах для бота, 3 ранен, 4 убит.
//Также эта функция должна метит клетки на поле значением -2 по которым
уже стреляли
ShotResult Shot(char letter, char digit)
{
int x = letter - 'A';
int y = digit - '1';
if (y == 9) digit = '0';
x++; //от границы
y++;
Enemy->setVisible(x,y,true);
int id = Enemy->get(x, y); //Что там?
// выстрел по пустой клетке, чистый промах
if (id == -1)
{
35
Enemy->set(x, y, -2);//пустая клетка по которой уже стреляли
cout << " "<<letter<<digit<<" Мимо";
Sleep(1000);
return Miss;
}
// выстрел по клетке по которой уже стреляли
// это условие нужно для правильной работы бота чтобы он не
стрелял по клеткам повторно
if (id == -2 || id == -3)
return Reshot;// условный промах
if (id >= 0)// попадание по кораблю
{
enemyships[id]--;
Enemy->set(x, y, -3);//подбитая палуба
//Показать запрещенные клетки
Enemy->setVisible(x - 1, y-1, true);
Enemy->setVisible(x + 1, y-1, true);
Enemy->setVisible(x - 1 , y+1, true);
Enemy->setVisible(x + 1, y+1, true);
Enemy->Show();
if (enemyships[id] > 0)
{
cout << " " << letter << digit << " Ранен";
Sleep(1000);
return Wound;//ранен
}
else
{
cout << " " << letter << digit << " Убит";
Enemy->OpenBorders(x, y, -3, 5);
Enemy->Show();
Sleep(1000);
return Kill;//убит
}
}
return Miss; //Не бывает
}
};
Модуль Ship.h
#pragma once
/*
Класс "корабль"
*/
36
#include <vector>
using namespace std;
#include "Map.h"
#include "Graph.h"
enum Direction {Right,Down,Left,Up};
class Ship
{
static const int NUM_SHIPS = 10;
vector<int> shipsize = { 4, 3, 3, 2, 2, 2, 1, 1, 1, 1 };
int id;//номер
public:
int x, y;//координаты начала
Direction dir; //направление
private:
Map *map; //Карта
int sizeShip; //Размер
void MoveByDir(int *x, int *y, Direction dir)
{
switch (dir)
{
case Right:(*x)++; break;
case Down: (*y)++; break;
case Left: (*x)--; break;
case Up: (*y)--; break;
}
}
//Функция setRandShip использует функцию setShipInMap и устанавливает
по случайным координатам
//и случайному направлению корабль заданной длины с заданым номером.
bool setRandShip()
{
for (int k = 0; k < 10000; k++) //попыток разместить
{
// первичная позиция
x = rand() % (map->N - 2) + 1;
y = rand() % (map->N - 2) + 1;
// генерация направления
dir = (Direction)(rand() % 4);
if (Place(x,y,dir))
return true; //получилось
}
return false;
}
public:
//Функцию setShipInMap устанавливает корабль на карту.
//Перед установкой корабля с помощью функции shipSettingIsPossible
проверяется возможно ли установка корабля по заданным координатам,
37
//в заданном напрвлении. Функция принимает в качестве параметров карту,
координаты, направление, размер и номер корабля.
//Номер корабля записвается в каждую ячейку массива, занимаемую
кораблем.
//В случае успешности установки корабля функция возвращает истину, в
обратном случае ложь.
bool Place(int x, int y, Direction dir)
{
if (shipSettingIsPossible(x,y,dir))
{
this->x = x;
this->y = y;
this->dir = dir;
for (int i = 0; i < sizeShip; i++)
{
map->set(x, y, id);
MoveByDir(&x, &y, dir);
}
return true;
}
return false;
}
//Удалить с карты
void Remove()
{
int x = this->x;
int y = this->y;
for (int i = 0; i < sizeShip; i++)
{
map->set(x, y, -1);//Свободен
MoveByDir(&x, &y, dir);
}
}
private:
int check(int x, int y)
{
for (int dx = -1; dx <= 1; dx++)
for (int dy = -1; dy <= 1; dy++)
if (map->get(x+dx, y+dy) >= 0) //Корабль!!
return true;
return false;
}
// Функция проверяет возможно ли установить корабль на карту по
заданным координатам, в заданном направлении, заданного размера.
// Возвращает истину если постановка возможно и ложь, если не возможна.
// Функция не записывает корабль в массив, только проверять возможность
установки.
bool shipSettingIsPossible(int x,int y, Direction dir)
38
{
for (int i = 0; i < sizeShip; i++)
{
if (map->get(x,y) == -100) return false; //Нельзя - это граница
if (check(x, y)) return false; //Нельзя - по соседству корабль
MoveByDir(&x, &y, dir);
}
return true;
}
public:
Ship(Map *map, int id)
{
this->map = map;
this->id = id;
this->sizeShip = shipsize[id];
}
~Ship()
{
}
// функция проверки нахождения корабля в пределах карты.
// Эта функция возвращает true если корабль помещается на карте и false в
обратном случае.
bool shipInMap(int x, int y, Direction dir)
{
bool in_map = true;
for (int i = 0; i < sizeShip; i++)
{
if (x < 1 || y < 1 || x >= map->N - 1 || y >= map->N - 1)
return false;
if (check(x, y)) return false;
MoveByDir(&x, &y, dir);
}
return in_map;
}
//Функция setRandShips() в случайном месте
//и в случайном направлении (горизонтальном или вертикальном)
//расставляет корабли
static vector<Ship*> setRandShips(Map* map)
{
vector<Ship*> result(NUM_SHIPS);
bool OK = false;
while (!OK)
{
OK = true;
map->Init(map->visible()); //Очистка карты
39
for (int id = 0; id < NUM_SHIPS; id++)
{
Ship* S = new Ship(map, id);
OK &= S->setRandShip();
result[id] = S;
}
}
return result;
}
static void SaveToFile(ofstream &stream, vector<Ship*> ships)
{
for (unsigned k = 0; k < ships.size(); k++)
{
stream << ships[k]->x << " ";
stream << ships[k]->y << " ";
stream << ships[k]->dir << " ";
stream << endl;
}
}
static bool LoadFromFile(ifstream &stream, vector<Ship*> &ships)
{
if (!stream.is_open()) return false;
for (unsigned k = 0; k < ships.size(); k++)
{
stream >> ships[k]->x;
stream >> ships[k]->y;
int dir; stream >> dir;
ships[k]->dir = (Direction)dir;
}
return true;
}
};
Модуль Source.cpp
#include <time.h>
#include <iostream>
#include <conio.h>
using namespace std;
#include "Map.h"
#include "Graph.h"
#include "Ship.h"
#include "BotPlayer.h"
40
#include "HumanPlayer.h"
void Help()
{
cout << "Управление:" << endl;
cout << "L - загрузить расстановку из файла" << endl;
cout << "S - сохранить расстановку в файл" << endl;
cout << "цифры 0..9 - выбрать корабль для установки" << endl;
cout << "стрелки - переместить выбранный корабль" << endl;
cout << "R - повернуть выбранный корабль" << endl;
cout << "G - Начать игру" << endl;
cout << "Q - Выйти" << endl;
}
bool Start(Map *map, vector<Ship*> ship, Graph*graph)
{
system("cls");
int ch; //введенный символ
int id = 0;
int x = ship[id]->x;
int y = ship[id]->y; //положение курсора
Direction dir = ship[id]->dir; //положение корабля
char FileName[256];
Map::NeedID = true;
Help();
while (true)
{
map->Show();
ch = _getch();
if (ch == 224) ch = _getch(); //Это стрелка
int temp_x = x;
int temp_y = y;
Direction temp_dir = dir;
bool valid = true;
switch (ch)
{
case 'L':case 'l':
{
system("cls");
cout << "Имя файла = ";
cin.getline(FileName, sizeof(FileName));
ifstream stream(FileName);
if (!map->LoadFromFile(stream))
{
cout << "Нет файла "<<FileName;
_getch();
};
Ship::LoadFromFile(stream, ship);
stream.close();
41
map->Show();
}
break;
case 'S':case 's':
{
system("cls");
if (!valid)
{
cout << "Неверная расстановка!";
break;
}
cout << "Имя файла = ";
cin.getline(FileName, sizeof(FileName));
ofstream stream(FileName);
map->SaveToFile(stream);
Ship::SaveToFile(stream, ship);
stream.close();
}
break;
case 'G':case 'g':
if (!valid) break;
return true;
case 'Q':case 'q':
return false;
//Управление расстановкой:
case '0':case '1':case '2':case '3':case '4':
case '5':case '6':case '7':case '8':case '9':
if (!valid) break;
id = ch - '0';
x = ship[id]->x;
y = ship[id]->y; //положение курсора
dir = ship[id]->dir; //положение корабля
break;
case 77://вправо
if (++x >= map->N-1) x= map->N - 1; break;
case 80://вниз
if (++y >= map->N - 1) y = map->N - 1; break;
case 75://влево
if (--x < 1) x = 1; break;
case 72://вверх
if (--y < 1) y = 1; break;
case 'r': case 'R':// r поворот текущего
dir = (Direction)((dir+1) % 4); break;
default:
graph->gotoxyCrt(0, 0);
Help();
break;
}
if (valid)
ship[id]->Remove(); //Удалить выбранный с карты
42
// проверка попадания корабля в поле после изменения координат
valid = ship[id]->shipInMap(x, y, dir);
ship[id]->Remove(); //Удалить выбранный с карты
valid = ship[id]->Place(x, y, dir); //Поставить выбранный на карту
map->Show();
graph->gotoxyCrt(0, 20);
if (!valid)
cout << "Неверная расстановка ";
else
cout << " ";
graph->gotoxyCrt(0, 0);
}
}
int main()
{
system("chcp 1251>nul"); //Кодировка
//srand(0); //Для отладки
srand((unsigned)time(0));
Graph *graph = new Graph();
//Пока пользователю не надоест
while (true)
{
Map *mapHuman = new Map(true, graph, 15, 1);
Map *mapBot = new Map(false, graph,15,1);
//Map *mapBot = new Map(true, graph, 15, 1);
vector<Ship*> Human = Ship::setRandShips(mapHuman);
vector<Ship*> Bot = Ship::setRandShips(mapBot);
if (!Start(mapHuman, Human, graph)) return 0;
Map::NeedID = false;
HumanPlayer * H = new HumanPlayer(mapHuman, mapBot, graph);
BotPlayer * B = new BotPlayer(mapBot, mapHuman, graph);
//Пока кто нибудь не выиграл - делать ходы
mapHuman->X = 0;
system("cls");
while (true)
{
mapHuman->Show();
mapBot->Show();
//Ход игрока
if (!H->Move())
{
system("cls");
43
cout << "Человек сдался";
break;
};
//Проверка на победу
if (mapBot->LostAll())
{
system("cls");
cout << "Человек выиграл";
break;
}
//Ход бота
if (!B->Move())
{
system("cls");
cout << "Бот сдался";
_getch();
break;
}
//Проверка на победу
if (mapHuman->LostAll())
{
system("cls");
cout << "Бот выиграл";
break;
}
}
cout << "Обе карты";
//Показать финальное положение
mapBot->setVisible(true);
mapBot->Show();
mapHuman->Show();
//подождать
_getch();
//Освободить память
delete mapHuman;
delete mapBot;
//Спросить об окончании
graph->gotoxyCrt(0, 0);
cout.width(40);
cout << "";
graph->gotoxyCrt(0, 0);
cout << "Закончить ?Y/[N] ";
char answer = _getch();
if (answer == 'Y' || answer == 'y') break;
}
}
Модуль Static.cpp
#include "Graph.h"
44
#include "Map.h"
//static Graph Map:: *graph;
bool Map::NeedID = true;
Сделайте индивидуальный заказ на нашем сервисе. Там эксперты помогают с учебой без посредников
Разместите задание – сайт бесплатно отправит его исполнителя, и они предложат цены.
Цены ниже, чем в агентствах и у конкурентов
Вы работаете с экспертами напрямую. Поэтому стоимость работ приятно вас удивит
Бесплатные доработки и консультации
Исполнитель внесет нужные правки в работу по вашему требованию без доплат. Корректировки в максимально короткие сроки
Гарантируем возврат
Если работа вас не устроит – мы вернем 100% суммы заказа
Техподдержка 7 дней в неделю
Наши менеджеры всегда на связи и оперативно решат любую проблему
Строгий отбор экспертов
К работе допускаются только проверенные специалисты с высшим образованием. Проверяем диплом на оценки «хорошо» и «отлично»
Работы выполняют эксперты в своём деле. Они ценят свою репутацию, поэтому результат выполненной работы гарантирован
Ежедневно эксперты готовы работать над 1000 заданиями. Контролируйте процесс написания работы в режиме онлайн
Выполнить 2 контрольные работы по Информационные технологии и сети в нефтегазовой отрасли. М-07765
Контрольная, Информационные технологии
Срок сдачи к 12 дек.
Архитектура и организация конфигурации памяти вычислительной системы
Лабораторная, Архитектура средств вычислительной техники
Срок сдачи к 12 дек.
Организации профилактики травматизма в спортивных секциях в общеобразовательной школе
Курсовая, профилактики травматизма, медицина
Срок сдачи к 5 дек.
краткая характеристика сбербанка анализ тарифов РКО
Отчет по практике, дистанционное банковское обслуживание
Срок сдачи к 5 дек.
Исследование методов получения случайных чисел с заданным законом распределения
Лабораторная, Моделирование, математика
Срок сдачи к 10 дек.
Проектирование заготовок, получаемых литьем в песчано-глинистые формы
Лабораторная, основы технологии машиностроения
Срок сдачи к 14 дек.
Вам необходимо выбрать модель медиастратегии
Другое, Медиапланирование, реклама, маркетинг
Срок сдачи к 7 дек.
Ответить на задания
Решение задач, Цифровизация процессов управления, информатика, программирование
Срок сдачи к 20 дек.
Написать реферат по Информационные технологии и сети в нефтегазовой отрасли. М-07764
Реферат, Информационные технологии
Срок сдачи к 11 дек.
Написать реферат по Информационные технологии и сети в нефтегазовой отрасли. М-07764
Реферат, Геология
Срок сдачи к 11 дек.
Разработка веб-информационной системы для автоматизации складских операций компании Hoff
Диплом, Логистические системы, логистика, информатика, программирование, теория автоматического управления
Срок сдачи к 1 мар.
Нужно решить задание по информатике и математическому анализу (скрин...
Решение задач, Информатика
Срок сдачи к 5 дек.
Заполните форму и узнайте цену на индивидуальную работу!