Лабы по ООП 2023 / Baraeva_Elizaveta_lb4
.docМИНОБРНАУКИ РОССИИ
Санкт-Петербургский государственный
электротехнический университет
«ЛЭТИ» им. В. И. Ульянова (Ленина)
Кафедра МО ЭВМ
отчет
по лабораторной работе №4
по дисциплине «Объектно-ориентированное программирование»
Тема: Уровни абстракции
Студентка гр. 2383 |
|
Бараева Е. Н. |
Преподаватель |
|
Жангиров Т. Р. |
Санкт-Петербург
2023
Цель работы.
Создание класса игры, отвечающего за запуск и логику, класса считывания ввода пользователя, отвечающего за контроль пользователем игрока.
Задание.
а) Создать класс игры. Класс игры отвечает за запуск игры (в начале и во время текущей сессии), выход из игры, выбор уровня, а также инициализирующего перемещение игрока. Также класс должен проводить проверку проигрыша или выигрыша игрока и давать возможность начать новую игру или завершить работу программы.
б) Создать класс (или набор классов) считывания ввода пользователя. Данный класс(ы) должен сопоставить ввод пользователя с командой, которую необходимо выполнить. И передать эту информацию в класс игры. Клавиши управления (то на какую клавишу назначено определенное действия) должны считываться из файла. Класс(ы) должен быть разработан так, чтобы:
потенциально можно было масштабировать управление с минимальным количеством изменений в коде
можно было заменить схему управления, например, заменить ввод команду через терминал на чтение команд из файла или из сети с другого устройства
Примечания:
После считывания клавиши, считанный символ должен сразу обрабатываться, и далее работа должна проводить с сущностью, которая представляет команду.
Для представления команды можно разработать системы классов или использовать перечисление enum.
Хорошей практикой является создание “прослойки” между считыванием/обработкой команды и классом игры, которая сопоставляет команду и вызываемым методом игры. Существуют альтернативные решения без явной “прослойки”
При считывании управления необходимо делать проверку, что на все команды назначена клавиша, что на одну клавишу не назначено две команды, что на одну команду не назначено две клавиши.
При работе с файлом используйте идиому RAII.
Выполнение работы.
Class ReadingFromConsole
В данном классе был переопределен оператор (), который отвечает за считывание клавиш пользователя с консоли без нажатия на Enter. Для него была подключена библиотека windows.h, и использованы из данной библиотеки функции. Таже у этого класса есть деструктор, в котором закрывается файл с ключами клавиш. В добавок этот класс является дочерним к классу Reading.
Class Reading
Данный класс считывает команды пользователя. Имеет два поля – файл, откуда считываются пользовательские ключи, и словарь численных представлений символов клавиш и команд. Конструктор класса Reading() считывает клавиши из файла "keys.txt" и сопоставляет им команды из enum commands. В случае, если клавиш меньше должного количества и есть повторения, то клавиши задаются по умолчанию. Также был переопределен оператор () для объектов данного класса. Еще у этого класса есть деструктор, в котором закрывается файл с ключами клавиш.
Class Game
Класс, создающий все элементы игры. Имеет 3 поля – игровое поле, игрок, контроллер и указатель на объект класса считывания. В конструкторе класса генерируется поле игры и присваивается field. Метод меню игры считывает команды игрока с помощью функционального класса Reading и позволяет либо продолжить игру, либо начать игру сначала, либо выйти из игры. Death_menu() – метод меню смерти персонажа, считывающий команды игрока с помощью функционального класса Reading и позволяющая либо начать игру сначала, либо выйти из игры. Game_play() – метод, считывающий команды и передвигающий игрока в соответствующих направлениях или вызывающий меню. Run() – основной метод игры, устанавливающий игрока на начальные координаты, запускающий Game_play(), завершающий свою работу в случае, если Game_play() вернул 0, и создающий второй уровень и снова запускающий Game_play() в обратном.
Тестирование.
Результаты тестирования представлены в табл. 1.
Таблица 1 – Результаты тестирования
№ п/п |
Входные данные |
Выходные данные |
Комментарии |
1. |
wsddwpq |
|
Проверка передвижения. Верно. |
2. |
prprq |
|
Проверка меню. Верно. |
Вывод.
Был создан класс игры, отвечающий за запуск и логику всей игры, также реализован класс считывания ввода пользователя клавиш с консоли, отвечающего за контроль пользователем игрока. В классе считывания ввода были созданы методы, которые в дальнейшем будут использованы для меню игры.
ПРИЛОЖЕНИЕ А ИСХОДНЫЙ КОД ПРОГРАММЫ
Название файла: Game.h
#ifndef OOP_GAME_H
#define OOP_GAME_H
#include "CreationLevels.h"
#include "Controller.h"
#include "Reading.h"
#include <thread>
class Game {
private:
Field field;
Player player;
Controller controller;
Reading* reader;
protected:
public:
explicit Game(class Reading* reader);
bool Menu();
bool Death_menu();
int Game_play();
void Run();
~Game();
};
#endif //OOP_GAME_H
Название файла: Game.cpp
#include "Game.h"
Game::Game(class Reading * reader) : field(), player(), controller(player, field, {0, 0}), reader(reader) {
CreationLevels creator;
field = creator.GetLevel1();
}
bool Game::Menu(){
command action = reader->operator()();
std::cout << "Menu" << '\n';
while (action != restart && action != pause && action != quit)
action = reader->operator()();
// go on
if (action == pause)
return true;
// play again
if (action == restart) {
player.Revive();
Run();
return false;
}
// exit
if (action == quit)
exit(0);
return false;
}
bool Game::Death_menu(){
command action = reader->operator()();
while (action != restart && action != quit)
action = reader->operator()();
// play again
if (action == restart) {
player.Revive();
Run();
return false;
}
// exit
if (action == quit)
exit(0);
return false;
}
int Game::Game_play(){
while (controller.Alive()) {
command action = reader->operator()();
switch (action) {
case move_down:
std::cout << "Down" << '\n';
controller.Move(down);
break;
case move_up:
std::cout << "Up" << '\n';
controller.Move(up);
break;
case move_right:
std::cout << "Right" << '\n';
controller.Move(right);
break;
case move_left:
std::cout << "Left" << '\n';
controller.Move(left);
break;
case pause:
std::cout << "Pause" << '\n';
if (Menu())
break;
else
return 0;
case restart:
break;
case quit:
break;
}
Coordinates cur_cords = controller.GetCoordinates();
if (cur_cords.x == field.GetExit().x && cur_cords.y == field.GetExit().y)
return 1;
}
if (!controller.Alive())
return Death_menu();
return -1;
}
void Game::Run(){
controller.SetCoordinates({field.GetEntry().x, field.GetEntry().y});
if (Game_play() == 0)
return;
CreationLevels creator;
field = creator.GetLevel2();
if (Game_play() == 0)
return;
}
Game::~Game() {
delete reader;
reader = nullptr;
}
Название файла: Reading.h
#ifndef OOP_READING_H
#define OOP_READING_H
#include <fstream>
#include <iostream>
#include <vector>
#include <map>
#define COMMAND_LEN 7
enum command {move_up, move_down, move_right, move_left, pause, restart, quit};
class Reading {
protected:
std::ifstream user_keys;
std::map<int, command> commands;
public:
Reading();
virtual command operator()() = 0;
virtual ~Reading();
};
#endif //OOP_READING_H
Название файла: Reading.cpp
#include "Reading.h"
Reading::Reading() : user_keys("keys.txt") {
if (!user_keys.is_open())
throw std::invalid_argument("User keys file do not open!");
std::vector<int> keys(COMMAND_LEN);
bool ok = true;
// checking for the correct number of commands
int ind = 0;
while (ind != COMMAND_LEN && !user_keys.eof()) {
int intBuf;
user_keys >> intBuf;
keys[ind] = intBuf;
ind += 1;
}
if (ind != COMMAND_LEN)
ok = false;
else { // checking for repetitions of commands
int counter = 0;
for (int i = 0; i < COMMAND_LEN; i++) {
counter = 0;
for (int j = 0; j < COMMAND_LEN; j++) {
if (keys[i] == keys[j])
counter += 1;
}
if (counter > 1) {
ok = false;
break;
}
}
}
if (ok) {
commands[keys[0]] = move_up;
commands[keys[1]] = move_down;
commands[keys[2]] = move_right;
commands[keys[3]] = move_left;
commands[keys[4]] = pause;
commands[keys[5]] = restart;
commands[keys[6]] = quit;
}
else {
commands[119] = move_up; // w
commands[115] = move_down; // s
commands[100] = move_right; // d
commands[97] = move_left; // a
commands[112] = pause; // p
commands[114] = restart; // r
commands[113] = quit; // q
}
}
Reading::~Reading(){
user_keys.close();
}
Название файла: ReadingFromConsole.h
#ifndef OOP_READINGFROMCONSOLE_H
#define OOP_READINGFROMCONSOLE_H
#include "Reading.h"
class ReadingFromConsole : public Reading {
public:
command operator()() final;
~ReadingFromConsole() final;
};
#endif //OOP_READINGFROMCONSOLE_H
Название файла: ReadingFromConsole.cpp
#include "ReadingFromConsole.h"
#include <windows.h>
command ReadingFromConsole::operator()(){
HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode;
GetConsoleMode(hConsole, &mode);
SetConsoleMode(hConsole, mode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT));
int ch = getchar();
while(true){
if (commands.count(ch)){
SetConsoleMode(hConsole, mode);
return commands[ch];
}
ch = getchar();
}
}
ReadingFromConsole::~ReadingFromConsole(){
user_keys.close();
}
