From cc1bb796330c461e965550fe919ce09b1c70dffb Mon Sep 17 00:00:00 2001 From: ParkSuMin Date: Tue, 29 Apr 2025 17:45:27 +0300 Subject: [PATCH] Initial commit --- .gitignore | 6 ++ CMakeLists.txt | 21 ++++++ include/client.hpp | 21 ++++++ include/maze.hpp | 38 ++++++++++ include/server.hpp | 26 +++++++ src/client/client.cpp | 79 ++++++++++++++++++++ src/client/client_main.cpp | 14 ++++ src/server/maze.cpp | 79 ++++++++++++++++++++ src/server/server.cpp | 148 +++++++++++++++++++++++++++++++++++++ src/server/server_main.cpp | 9 +++ 10 files changed, 441 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 include/client.hpp create mode 100644 include/maze.hpp create mode 100644 include/server.hpp create mode 100644 src/client/client.cpp create mode 100644 src/client/client_main.cpp create mode 100644 src/server/maze.cpp create mode 100644 src/server/server.cpp create mode 100644 src/server/server_main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a6d195e --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +build/ +CMakeFiles/ +cmake* +CMakeC* +Makefile +maze_* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5d4b15b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.5) +project(maze LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 23) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++23") + +include_directories( + include +) + +add_executable(maze_server + src/server/maze.cpp + src/server/server.cpp + src/server/server_main.cpp +) + +add_executable(maze_client + src/client/client.cpp + src/client/client_main.cpp +) \ No newline at end of file diff --git a/include/client.hpp b/include/client.hpp new file mode 100644 index 0000000..a70d040 --- /dev/null +++ b/include/client.hpp @@ -0,0 +1,21 @@ +#ifndef CLIENT +#define CLIENT + +#include +#include +#include +#include +#include + +const int PORT = 8080; + +class Client{ +private: + int sock; +public: + Client() : sock(0){}; + void run(); + int ping(struct sockaddr_in); +}; + +#endif \ No newline at end of file diff --git a/include/maze.hpp b/include/maze.hpp new file mode 100644 index 0000000..3c14a1d --- /dev/null +++ b/include/maze.hpp @@ -0,0 +1,38 @@ +#ifndef MAZE_HPP +#define MAZE_HPP + +#include +#include +#include +#include +#include + +const int MAZE_SIZE = 9; +const int DIRECTIONS = 4; +const int MIN_WALLS = 3; +const int MAX_WALLS = 5; +const int DEFAULT_MOVES = 10; + +class Maze{ + private: + std::unordered_map> graph; + int moves_left; + bool is_path_exists(int, int); + + struct Edge { + int node1, dir1; + int node2, dir2; + }; + public: + Maze(bool); + bool test_mode; + int get_moves_left() const; + bool is_wall(int, int) const; + + void set_moves_left(int); + + //void generate_maze(); + //void print_maze_info(); + //void play_game(); +}; +#endif diff --git a/include/server.hpp b/include/server.hpp new file mode 100644 index 0000000..af190d0 --- /dev/null +++ b/include/server.hpp @@ -0,0 +1,26 @@ +#ifndef SERVER_HPP +#define SERVER_HPP + +#include "maze.hpp" + +#include +#include +#include +#include +#include +#include + +const int PORT = 8080; +const int MAX_CLIENTS = 100; + +class Server { +private: + bool service_mode; + void handle_client(int client_socket, bool _mode); // Принимает клиентский сокет + bool check_status(Maze& maze); +public: + Server() : service_mode(false){}; + void start(); // Запуск сервера +}; + +#endif \ No newline at end of file diff --git a/src/client/client.cpp b/src/client/client.cpp new file mode 100644 index 0000000..77abbed --- /dev/null +++ b/src/client/client.cpp @@ -0,0 +1,79 @@ +#include "client.hpp" + +void print_instructions() { + std::cout << "Доступные команды:\n"; + std::cout << " - вперёд\n"; + std::cout << " - направо\n"; + std::cout << " - налево\n"; + std::cout << " - назад\n"; + std::cout << " - сдаюсь\n"; + std::cout << "Введите команду для хода.\n"; +} + +int Client::ping(struct sockaddr_in address){ + return connect(sock, (struct sockaddr*)&address, sizeof(address)) > 0; +} + +void Client::run() { + struct sockaddr_in serv_addr; + + // Создание сокета + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + throw std::runtime_error("[Client::request] connect(2) call error"); + } + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(PORT); + + // Преобразование IP-адреса из текстового вида в бинарный + if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { + throw std::runtime_error("[Client::request] connect(2) call error"); + } + + // Подключение к серверу + if (ping(serv_addr)) { + throw std::runtime_error("Connection lost!"); + } + + std::cout << "Подключено к серверу. Введите ваше имя: "; + std::string player_name; + std::getline(std::cin, player_name); + + if (ping(serv_addr)) { + throw std::runtime_error("Connection lost!"); + } + // Отправка имени игрока на сервер + send(sock, player_name.c_str(), player_name.size(), 0); + std::cout << "Игра началась!\n"; + + print_instructions(); + + char buffer[1024] = {0}; + while (true) { + std::cout << "Введите команду: "; + std::string command; + std::getline(std::cin, command); + + // Отправка команды на сервер + 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) { + throw std::runtime_error("Connection lost!"); + //break; + } + + std::string response(buffer); + std::cout << "Ответ сервера: " << response; + + // Проверка завершения игры + if (response.find("вы выиграли") != std::string::npos || + response.find("вы проиграли") != std::string::npos) { + break; + } + } + + close(sock); +} \ No newline at end of file diff --git a/src/client/client_main.cpp b/src/client/client_main.cpp new file mode 100644 index 0000000..15b6c27 --- /dev/null +++ b/src/client/client_main.cpp @@ -0,0 +1,14 @@ +#include "client.hpp" + +int main(){ + try { + Client client; + client.run(); + } catch (const std::runtime_error& e){ + std::cerr << "Client application error: " << e.what() << std::endl; + return 1; + } catch (...){ + std::cerr << "Unexpected error in client application" << std::endl; + return 2; + } +} \ No newline at end of file diff --git a/src/server/maze.cpp b/src/server/maze.cpp new file mode 100644 index 0000000..7225f6c --- /dev/null +++ b/src/server/maze.cpp @@ -0,0 +1,79 @@ +#include "maze.hpp" +#include + +Maze::Maze(bool _test_mode){ + std::vector 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} + }; + test_mode = _test_mode; + moves_left = test_mode ? INT_MAX : DEFAULT_MOVES; + + for (int i = 0; i < MAZE_SIZE; ++i) { + graph[i] = std::vector(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(MIN_WALLS, MAX_WALLS)(rng); + + for (int i = 0; i < walls_to_add && i < edges.size(); ++i) { + 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; + } + } +} + +bool Maze::is_path_exists(int start, int end) { + std::vector visited(MAZE_SIZE, false); + std::vector queue; + queue.push_back(start); + visited[start] = true; + + while (!queue.empty()) { + int node = queue.back(); + queue.pop_back(); + if (node == end) return true; + + const auto& directions = graph.at(node); + if (directions[0] && node + 3 < MAZE_SIZE && !visited[node + 3]) { + queue.push_back(node + 3); + visited[node + 3] = true; + } + if (directions[1] && (node % 3) < 2 && !visited[node + 1]) { + queue.push_back(node + 1); + visited[node + 1] = true; + } + if (directions[2] && node - 3 >= 0 && !visited[node - 3]) { + queue.push_back(node - 3); + visited[node - 3] = true; + } + if (directions[3] && (node % 3) > 0 && !visited[node - 1]) { + queue.push_back(node - 1); + visited[node - 1] = true; + } + } + return false; +} + +int Maze::get_moves_left() const{ + return moves_left; +} + +bool Maze::is_wall(int node, int direction) const { + return !graph.at(node)[direction]; +} + +void Maze::set_moves_left(int moves) { + moves_left = moves; +} \ No newline at end of file diff --git a/src/server/server.cpp b/src/server/server.cpp new file mode 100644 index 0000000..668a6e3 --- /dev/null +++ b/src/server/server.cpp @@ -0,0 +1,148 @@ +#include "server.hpp" + +bool Server::check_status(Maze &maze) { + return (!maze.test_mode && maze.get_moves_left() > 0); +} + +void Server::handle_client(int client_socket, bool mode) { + Maze maze(mode); // Создаем экземпляр лабиринта для клиента + int current_position = 0; // Начальная позиция игрока + std::string player_name; + + char buffer[1024] = {0}; + std::string response; + + // Получение имени игрока + int bytes_received = recv(client_socket, buffer, sizeof(buffer), 0); + if (bytes_received <= 0) { + std::cerr << "Ошибка получения имени игрока.\n"; + close(client_socket); + return; + } + + player_name = std::string(buffer); + std::cout << "Новый игрок: " << player_name << "\n"; + + while (true) { + int moves_left = maze.get_moves_left(); + memset(buffer, 0, sizeof(buffer)); + bytes_received = recv(client_socket, buffer, sizeof(buffer), 0); + + if (bytes_received <= 0) { + break; + } + + std::string command(buffer); + std::cout << "Получена команда от игрока " << player_name << ": " << command << "\n"; + + int direction = -1; + int new_position = current_position; + + if (command == "вперёд") { + direction = 0; + new_position = current_position + 3; + } else if (command == "направо") { + direction = 1; + new_position = current_position + 1; + } else if (command == "налево") { + direction = 3; + new_position = current_position - 1; + } else if (command == "назад") { + direction = 2; + new_position = current_position - 3; + } else if (command == "сдаюсь") { + break; + } else { + response = "Неверная команда. Попробуйте снова.\n"; + send(client_socket, response.c_str(), response.size(), 0); + continue; + } + + // Проверка выхода за границы + if ((direction == 0 && new_position >= MAZE_SIZE) || + (direction == 1 && (current_position % 3 == 2)) || + (direction == 2 && new_position < 0) || + (direction == 3 && (current_position % 3 == 0))) + { + maze.set_moves_left(moves_left - 1); + if (!check_status(maze)) + break; + response = "там стена, осталось " + std::to_string(maze.get_moves_left()) + " ходов\n"; + 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"; + send(client_socket, response.c_str(), response.size(), 0); + continue; + } else { + // Успешный ход + current_position = new_position; + if (current_position == 8) { + response = "вы выиграли\n"; + send(client_socket, response.c_str(), response.size(), 0); + break; + } + + int x = current_position % 3; + int y = current_position / 3; + std::string text("(" + std::to_string(x) + ", " + std::to_string(y) + ")\n"); + maze.set_moves_left(moves_left - 1); + response = "успешно, осталось " + std::to_string(maze.get_moves_left()) + " ходов. Вы находитесь в " + text; + send(client_socket, response.c_str(), response.size(), 0); + } + } + + if (current_position != 8) { + response = "вы проиграли\n"; + send(client_socket, response.c_str(), response.size(), 0); + } + + close(client_socket); + std::cout << "Игрок " << player_name << " отключился.\n"; +} + +void Server::start() { + int server_fd, new_socket; + struct sockaddr_in address; + int opt = 1; + int addrlen = sizeof(address); + + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { + perror("Ошибка создания сокета"); + exit(EXIT_FAILURE); + } + + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { + perror("Ошибка настройки сокета"); + exit(EXIT_FAILURE); + } + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(PORT); + + if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) { + perror("Ошибка привязки сокета"); + exit(EXIT_FAILURE); + } + + if (listen(server_fd, MAX_CLIENTS) < 0) { + perror("Ошибка прослушивания"); + exit(EXIT_FAILURE); + } + + std::cout << "Сервер запущен на порту " << PORT << "\n"; + + while (true) { + if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) { + perror("Ошибка принятия соединения"); + exit(EXIT_FAILURE); + } + + std::cout << "Новое соединение установлено.\n"; + std::thread(&Server::handle_client, this, new_socket, true).detach(); + } +} \ No newline at end of file diff --git a/src/server/server_main.cpp b/src/server/server_main.cpp new file mode 100644 index 0000000..15946c9 --- /dev/null +++ b/src/server/server_main.cpp @@ -0,0 +1,9 @@ +#include "server.hpp" + +int main(int argc, char **argv) { + + Server server; + server.start(); + + return 0; +} \ No newline at end of file