Создание массива, заполненного последовательно 1,..,15,0 |
Перемешать массив |
Перемешанный массив, число ходов |
Нажата клавиша |
Победа? |
Переместить пустую клетку в противоположном направлении |
нет |
Alt + R |
Начать новую игру (перемешать таблицу, счетчик ходов = 0) |
Alt + X |
Выход из программы |
Стрелка влево / вправо / вверх / вниз |
да |
Описание классов
Для решения задачи создано два класса. Один класс – базовый – описывает игровое поле, в нем содержатся методы, связанные с отображением таблицы на экране. Второй – производный с обобществленным типом наследования – содержит поля и методы, связанные с ходом игры. Именно в нем содержатся методы, двигающие пустую клетку; ожидание нажатия клавиши, проверка на завершение игры.
Диаграмма классов
Структура классов
Класс GameField отвечает за формирование таблицы и за вывод информации на экран.
Поля класса GameField:
- int Field[4][4] – игровое поле
- int izero – номер строки, в которой находится пустая ячейка
- int jzero – номер столбца, в котором находится пустая ячейка
- int RightField[4][4] – эталон победного положения чисел для победы
- int Step – число сделанных игроком ходов
Методы класса GameField:
- GameField() – конструктор, заполняет таблицу в правильную последовательность
- void MoveElements (int i1, int j1, int i2, int j2) – меняет местами значения ячеек таблицы, находящиеся в [i1][j1] и [i2][j2].
- bool CheckForSolution() – проверяет, решаема ли данная конфигурация чисел в таблице. Если решаема, возвращает 1, иначе 0.
- void Mix() – случайным образом перемешивает значения клеток поля так, чтобы в итоге получилась решаемая комбинация.
- void Draw() – выводит на экран картинку игры.
Класс Game отвечает за ход игры, за передвижение клеток при нажатии клавиш, за контроль за завершением текущей игры.
Поля класса Game:
- bool EndGame – признак завершения игры
Методы класса Game:
- Game() – конструктор, инициализирует таблицу и задает EndGame = FALSE
- void Mix() – перемешивает таблицу при запросе на новую игру
- void CheckEndGame() – устанавливает флаг признака завершения игры
- void MoveDown() – перемещает в пустую клетку ячейку сверху
- void MoveUp() – перемещает пустую ячейку вниз
- void MoveLeft() – перемещает пустую ячейку вправо
- void MoveRight() – перемещает пустую ячейку влево
- bool EndGameIs() - проверяет таблицу на правильность
- void TheEnd() – выводит на экран сообщение о победе
- void GetInstruction() – ожидает нажатия клавиши и реагирует на нажатие
Общие сведения о программе
Игровая программа «Пятнадцать»
Для функционирования программы необходимо следующее программное обеспечение:
Windows 7/ Windows 8/Windows 8.1/Windows 10;
Программа написана на языке программирования C++. Использовалась интегрированная среда разработки Visual Studio 2015.
Функциональное назначение
Функциональное назначение программы – реализация игровой программы «Пятнадцать».
Программа должна выполнять следующие действия:
- Взаимодействие с игроком;
- Чтение данных с клавиатуры;
- Перемещение пустой клетки;
- Проверка на завершение игры;
- Оповещать игрока о завершении игры;
- Перемешивать таблицу для начала новой игры.
Кроме того необходимо предусмотреть:
- Краткую справку, позволяющую понять управление в игре.
- Выход из программы.
Используемые технические средства
При создании программного проекта использовались следующие технические средства:
· Персональный компьютер с процессором AMD A6-5200 APU with Radeon (TM) HD Graphics 2.00GHz. 64-разрядная операционная система, процессор x64. Установленная память 6.00 ГБ.
Вызов и загрузка
Запуск программы осуществляется запуском файла Пятнашки.exe. Также возможно запускать непосредственно через интегрированную систему разработки программного обеспечения Visual Studio 2015 Community.
EXE-файл занимает 14 336 байт.
Размер модулей, входящих в состав проекта:
- GameField.h – 498 байт.
- GameField.cpp – 3 083 байт.
- Game.h – 367 байт.
- Game.cpp – 2 201 байт
- Main.cpp – 133 байт.
Общий размер проекта – 35 977 978 байт
Потребление памяти – 0,4 МБ
Выводы
В ходе выполнения курсовой работы углублены и закреплены теоретические знания, навыки практического применения основных принципов объективно-ориентированных систем: наследования, инкапсуляции, полиморфизма, а также сформированы новые взгляды на процессы программирования с учетом абстракции данных.
Список литературы
1. [Интернет-ресурс] https://ru.wikipedia.org/wiki/Игра_в_15
2. [Интернет-ресурс] https://msdn.microsoft.com/ru-ru/library/3bstk3k5.aspx
Тестовые данные
При запуске программы открывается окно, в котором отображается таблица с перемешанными числами.
Рис.3. Начальное положение игрового поля
При нажатии стрелки влево «8» поменяется местами с пустой клеткой, количество ходов увеличится на 1
Рис.4. После первого хода
При нажатии сочетания клавиш Alt+R числа в таблице перемешиваются, количество ходов обнуляется
Рис.5. После перемешивания
После выстраивания чисел в правильном порядке игра завершится и на экран выведет сообщение о победе
Рис.6. Победа
Тексты программ
GameField.h
#include <iostream>
#include <time.h>
#include <conio.h>
using namespace std;
class GameField {
protected:
int Field[4][4];
int Step;
int izero;
int jzero;
int RightField[4][4];
public:
GameField();
bool CheckForSolution();
virtual void Mix();
void MoveElements(int, int, int, int);
void Draw();
};
GameField.cpp
#include "GameField.h"
GameField::GameField() {
for (int i = 0; i < 16; i++) {
Field[i / 4][i % 4] = i + 1;
if (i == 15)
Field[i / 4][i % 4] = 0;
}
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
RightField[i][j] = Field[i][j];
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
UnCalcField[i][j] = Field[i][j];
int t = UnCalcField[3][2];
izero = 3;
jzero = 3;
UnCalcField[3][2] = UnCalcField[3][1];
UnCalcField[3][1] = t;
Step = 0;
}
bool GameField::CheckForSolution() {
int a[16];
int inv = 0;
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
a[i * 4 + j] = Field[i][j];
for (int i = 16; i < 16; ++i)
if (a[i]!= 0)
for (int j = 0; j < i; ++j)
if (a[j] > a[i])
++inv;
for (int i = 0; i < 16; ++i)
if (a[i] == 0)
inv += 1 + i / 4;
return (inv & 1)? false: true;
}
void GameField::Mix() {
for (int i = 0; i < 16; i++) {
Field[i / 4][i % 4] = i + 1;
if (i == 15)
Field[i / 4][i % 4] = 0;
}
srand(time(0));
for (int i = 0; i < 16; i++) {
int j = rand() % (16 - i) + i;
int tmp = Field[j / 4][j % 4];
Field[j / 4][j % 4] = Field[i / 4][i % 4];
Field[i / 4][i % 4] = tmp;
}
int i1, i2, j1, j2;
bool Solution = CheckForSolution();
if (!Solution) {
for (int i = 0; i < 16; ++i) {
if (Field[i / 4][i % 4] == 14) {
i1 = i / 4;
j1 = i % 4;
}
else
if (Field[i / 4][i % 4] == 15) {
i2 = i / 4;
j2 = i % 4;
}
}
MoveElements(i1, j1, i2, j2);
}
}
void GameField::MoveElements(int i1, int j1, int i2, int j2) {
int tmp = Field[i1][j1];
Field[i1][j1] = Field[i2][j2];
Field[i2][j2] = tmp;
}
void GameField::Draw() {
system("cls");
setlocale(0, "rus");
cout << " ALT+X - выход из программы ALT+R - перемешать" << endl;
cout << endl << endl;
cout << " Игровая программа \"Пятнадцать\"" << endl;
cout << " ___________________________" << endl;
for (int i = 0; i < 4; i++) {
cout << " | | | | |" << endl << " |";
for (int j = 0; j < 4; j++) {
if (Field[i][j] == 0) {
cout << " |";
izero = i;
jzero = j;
}
else
if (Field[i][j] <= 9) cout << " " << Field[i][j] << " |";
else
cout << " " << Field[i][j] << " |";
}
cout << endl;
cout << " |______|______|______|______|" << endl;
}
cout << endl << " Количество ходов: " << Step << endl;
}
Game.h
#include "GameField.h"
class Game: public GameField {
protected:
bool EndGame;
public:
Game();
void GetInstruction();
void MoveDown();
void MoveUp();
void MoveLeft();
void MoveRight();
void Mix();
bool EndGameIs();
void CheckEndGame();
void TheEnd();
};
Game.cpp
#include "Game.h"
Game::Game() {
GameField();
GameField::Mix();
EndGame = false;
};
void Game::GetInstruction() {
char c;
c = _getch();
while ((int)c!= 120) { //alt+X
c = _getch();
switch (c) {
case 72: if (EndGame) break; //стрелка вверх
else {
MoveUp(); break;
}
case 80: if (EndGame) break; //стрелка вниз
else {
MoveDown(); break;
}
case 75: if (EndGame) break; //стрелка влево
else {
MoveLeft(); break;
}
case 77: if (EndGame) break; //стрелка вправо
else {
MoveRight(); break;
}
case 114: Mix(); break; //alt+R
default: break;
}
}
}
void Game::MoveDown() {
if (izero - 1 >= 0) {
++Step;
MoveElements(izero, jzero, izero - 1, jzero);
Draw();
}
CheckEndGame();
}
void Game::MoveUp() {
if (izero + 1 < 4) {
++Step;
MoveElements(izero, jzero, izero + 1, jzero);
Draw();
}
CheckEndGame();
}
void Game::MoveRight() {
if (jzero - 1 >= 0) {
++Step;
MoveElements(izero, jzero, izero, jzero - 1);
Draw();
}
CheckEndGame();
}
void Game::MoveLeft() {
if (jzero + 1 < 4) {
++Step;
MoveElements(izero, jzero, izero, jzero + 1);
Draw();
}
CheckEndGame();
}
void Game::Mix() {
GameField::Mix();
Step = 0;
EndGame = false;
Draw();
}
bool Game::EndGameIs() {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (Field[i][j]!= RightField[i][j])
return false;
return true;
}
void Game::CheckEndGame() {
EndGame = EndGameIs();
if (EndGame) TheEnd();
}
void Game::TheEnd() {
cout << endl;
cout << " Победа! "<< endl;//29-7=22
}