Compare commits

5 Commits

9 changed files with 66 additions and 19 deletions

View File

@@ -5,6 +5,9 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 -Wall")
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
include_directories(
include
)

25
doc/maze.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -3,14 +3,15 @@
## Протокол пользовательского уровня
UML-диаграмма протокола пользовательского уровня представлена на рисунке ниже.
![UML-диаграмма протокола пользовательского уровня](echo-protocol.svg)
![UML-диаграмма протокола пользовательского уровня](maze.svg)
## Описание работы приложения
Сервер `maze-server` ожидает подключения клиентов по протоколу TCP на заданный порт (по умолчанию 1024). После установления соединения сервер запрашивает имя игрока, а затем начинает игру в лабиринте. Лабиринт генерируется случайным образом при старте каждой новой игровой сессии. Гарантируется наличие хотя бы одного пути из начальной точки `(0, 0)` в конечную точку `(2, 2)`. Добавление дополнительных стен происходит с соблюдением условия достижимости цели.
Сервер `MazeServer` ожидает подключения клиентов по протоколу TCP на заданный порт (по умолчанию 2000). После установления соединения сервер запрашивает имя игрока, а затем начинает игру в лабиринте. Лабиринт генерируется случайным образом при старте каждой новой игровой сессии. Гарантируется наличие хотя бы одного пути из начальной точки `(0, 0)` в конечную точку `(2, 2)`. Добавление дополнительных стен происходит с соблюдением условия достижимости цели.
Cхема и нумерация клеток лабиринта представлена на рисунке ниже.
![Схема лабиринта](maze_numeration.svg)
![Схема лабиринта](numeration_maze.svg)
Клиент отправляет команды движения ("вперёд", "направо", "налево", "назад") или команду "сдаюсь" для завершения игры. Сервер обрабатывает команды, проверяет возможность хода, обновляет состояние лабиринта и отправляет клиенту текстовый ответ с результатом хода, количеством оставшихся ходов и текущей позицией (в формате координат). Игра завершается, если игрок достигает конечной точки (позиция 8), исчерпывает ходы или сдаётся.
@@ -24,9 +25,9 @@ Cхема и нумерация клеток лабиринта представ
- `-s` — включает сервисный режим (без ограничений по количеству шагов).
```bash
./maze-server -h localhost -p 1024 -n 10 -s
./maze-server -h localhost -p 2000 -n 10 -s
```
По умолчанию сокет сервера связывается с адресом `localhost:1024`, игрокам разрешено 10 шагов, сервисный режим выключен.
По умолчанию сокет сервера связывается с адресом `localhost:2000`, игрокам разрешено 10 шагов, сервисный режим выключен.
Завершение работы сервера — `Ctrl+C`.
@@ -47,7 +48,7 @@ Cхема и нумерация клеток лабиринта представ
- `-p` — задаёт номер порта сервера.
```bash
./maze_client -h localhost -p 1024
./maze_client -h localhost -p 2000
```
По умолчанию клиент пытается подключиться к адресу `localhost:1024`.
По умолчанию клиент пытается подключиться к адресу `localhost:2000`.

View File

@@ -13,6 +13,9 @@
#include <unistd.h>
#include <netdb.h>
/*! Размер буфера для обмена данными между сервером и клиентом. */
const int BUFFER_SIZE = 1024;
/*! Класс клиента для взаимодействия с сервером игры в лабиринт. */
class Client{
private:

View File

@@ -49,6 +49,10 @@ void Client::run(const std::string& h, const unsigned short p) {
std::string player_name;
std::getline(std::cin, player_name);
if (player_name.empty() || player_name.size() > BUFFER_SIZE) {
throw std::runtime_error("Incorrect name!");
}
if (!ping(serv_addr)) {
throw std::runtime_error("Connection lost!");
}
@@ -61,25 +65,30 @@ void Client::game(){
std::cout << "Игра началась!" << std::endl;
//print_instructions();
char buffer[1024] = {0};
char buffer[BUFFER_SIZE] = {0};
while (true) {
std::cout << "Введите команду: ";
std::string command;
std::getline(std::cin, command);
if (command.empty() || command.size() > BUFFER_SIZE) {
std::cout << "Некорректная команда" << std::endl;
continue;
}
// Отправка команды на сервер
send(sock, command.c_str(), command.size(), 0);
// Получение ответа от сервера
memset(buffer, 0, sizeof(buffer));
int bytes_received = recv(sock, buffer, sizeof(buffer), 0);
if (bytes_received <= 0) {
if (bytes_received == 0 || bytes_received > BUFFER_SIZE) {
throw std::runtime_error("Connection lost!");
//break;
}
std::string response(buffer);
std::cout << "Ответ сервера: " << response;
std::cout << "Ответ сервера: " << response << std::endl;
// Проверка завершения игры
if (response.find("вы выиграли") != std::string::npos ||

View File

@@ -28,7 +28,7 @@
int main(int argc, char** argv){
int opt;
std::string host = "localhost";
short unsigned port = 1024u;
short unsigned port = 2000u;
while ((opt = getopt(argc, argv, "h:p:")) != -1) {
switch (opt) {

View File

@@ -6,7 +6,9 @@
#include "maze.hpp"
Maze::Maze(bool _test_mode, int _steps){
std::vector<Edge> edges = {
// Инициализация графа лабиринта и его ребер
// Индексы направлений: 0 - север, 1 - восток, 2 - юг, 3 - запад
std::vector<Edge> edges = {
{0, 1, 1, 3}, {0, 0, 3, 2}, {1, 1, 2, 3}, {1, 0, 4, 2},
{2, 0, 5, 2}, {3, 1, 4, 3}, {3, 0, 6, 2}, {4, 1, 5, 3},
{4, 0, 7, 2}, {5, 0, 8, 2}, {6, 1, 7, 3}, {7, 1, 8, 3}
@@ -18,11 +20,14 @@ Maze::Maze(bool _test_mode, int _steps){
graph[i] = std::vector<bool>(DIRECTIONS, true);
}
// Выставление внешних стен
graph[6][0] = graph[7][0] = graph[8][0] = false;
graph[0][2] = graph[1][2] = graph[2][2] = false;
graph[0][3] = graph[3][3] = graph[6][3] = false;
graph[2][1] = graph[5][1] = graph[8][1] = false;
// Добавление внутренних стен
std::mt19937 rng(time(nullptr));
std::shuffle(edges.begin(), edges.end(), rng);
int walls_to_add = std::uniform_int_distribution<int>(MIN_WALLS, MAX_WALLS)(rng);
@@ -31,6 +36,7 @@ Maze::Maze(bool _test_mode, int _steps){
const auto& edge = edges[i];
graph[edge.node1][edge.dir1] = false;
graph[edge.node2][edge.dir2] = false;
// Проверка на существование пути
if (!is_path_exists(0, 8)) {
graph[edge.node1][edge.dir1] = true;
graph[edge.node2][edge.dir2] = true;

View File

@@ -41,7 +41,7 @@ void Server::handle_client(int client_socket, bool mode, int steps) {
std::string response;
int bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);
if (bytes_received <= 0) {
if (bytes_received <= 0 || bytes_received > BUFFER_SIZE) {
std::cout << "Error in getting player name" << std::endl;;
close(client_socket);
return;
@@ -55,7 +55,7 @@ void Server::handle_client(int client_socket, bool mode, int steps) {
memset(buffer, 0, sizeof(buffer));
bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);
if (bytes_received <= 0) {
if (bytes_received <= 0 || bytes_received > BUFFER_SIZE) {
break;
}
@@ -65,7 +65,7 @@ void Server::handle_client(int client_socket, bool mode, int steps) {
int direction = -1;
int new_position = current_position;
if (command == "вперёд") {
if (command == "вперед") {
direction = 0;
new_position = current_position + 3;
} else if (command == "направо") {
@@ -94,14 +94,14 @@ void Server::handle_client(int client_socket, bool mode, int steps) {
maze.set_moves_left(moves_left - 1);
if (!check_status(maze))
break;
response = "там стена, осталось " + std::to_string(maze.get_moves_left()) + " ходов\n";
response = "там стена, осталось " + std::to_string(maze.get_moves_left()) + " ходов";
send(client_socket, response.c_str(), response.size(), 0);
continue;
} else if (maze.is_wall(current_position, direction)) {
maze.set_moves_left(moves_left - 1);
if (!check_status(maze))
break;
response = "там стена, осталось " + std::to_string(maze.get_moves_left()) + " ходов\n";
response = "там стена, осталось " + std::to_string(maze.get_moves_left()) + " ходов";
send(client_socket, response.c_str(), response.size(), 0);
continue;
} else {
@@ -119,7 +119,7 @@ void Server::handle_client(int client_socket, bool mode, int steps) {
int x = current_position % 3;
int y = current_position / 3;
std::string text("(" + std::to_string(x) + ", " + std::to_string(y) + ")\n");
std::string text("(" + std::to_string(x) + ", " + std::to_string(y) + ")");
response = "успешно, осталось " + std::to_string(maze.get_moves_left()) + " ходов. Вы находитесь в " + text;
send(client_socket, response.c_str(), response.size(), 0);
}

View File

@@ -30,7 +30,7 @@ int main(int argc, char **argv) {
std::string host = "localhost";
int steps = 10;
bool service_mode = false;
short unsigned port = 1024u;
short unsigned port = 2000u;
while ((opt = getopt(argc, argv, "h:p:sn:")) != -1) {
switch (opt) {