From 927e065f9829045247be7c0b3296408b6f577c1f Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 12 Jun 2022 13:44:58 +0200 Subject: feat: add reading RLE files --- src/game/RLE_reader.cpp | 133 ++++++++++++++++++++++++++++++++++++++++++++++++ src/game/RLE_reader.hpp | 24 +++++++++ src/game/game.cpp | 77 +++++++++++++++++++++++++--- src/game/game.hpp | 5 +- 4 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 src/game/RLE_reader.cpp create mode 100644 src/game/RLE_reader.hpp (limited to 'src/game') diff --git a/src/game/RLE_reader.cpp b/src/game/RLE_reader.cpp new file mode 100644 index 0000000..f971966 --- /dev/null +++ b/src/game/RLE_reader.cpp @@ -0,0 +1,133 @@ +#include "RLE_reader.hpp" + +#include "engine/data/bounds.hpp" +#include "engine/data/vector2.hpp" +#include "errors/RLE_reader.hpp" +#include "util/algorithm.hpp" +#include "util/io.hpp" +#include "util/string.hpp" + +#include + +#include +#include +#include +#include + +#include + +RLEReader::RLEReader(const IMatrixFactory &matrix_factory) noexcept + : _matrix_factory(matrix_factory) +{ +} + +auto RLEReader::read_RLE_file(const std::filesystem::path &path) const + -> std::unique_ptr> +{ + auto content_lines = read_file_lines>(path); + + if (content_lines.empty()) + { + throw InvalidRLEFileError(path, "File is empty"); + } + + if (content_lines.back().empty()) + { + content_lines.pop_back(); + } + + const auto header_line_iter = container_find( + content_lines, + [](const std::string &line) + { + return ctre::starts_with<"x = \\d+, y = \\d+">(line); + // return ctre::match<"x = \\d+, y = \\d+(, rule = [a-zA-Z0-9/]+)?">(line); + }); + + if (header_line_iter == content_lines.end()) + { + throw InvalidRLEFileError(path, "No header line"); + } + + const auto &header_line = *header_line_iter; + + const auto header_parts = split_string>(header_line, ','); + + const auto &header_x_part = header_parts[0]; + const auto &header_y_part = header_parts[1].substr(1); + + const auto pattern_width = + static_cast(std::stoul(header_x_part.substr(4))); + + const auto pattern_height = + static_cast(std::stoul(header_y_part.substr(4))); + + const auto pattern_size = Bounds({.width = pattern_width, .height = pattern_height}); + + auto pattern_matrix = _matrix_factory(pattern_size); + + auto pattern_pos = Vector2({.x = 0, .y = 0}); + + const auto first_pattern_line_iter = header_line_iter + 1; + + for (auto pattern_line_iter = first_pattern_line_iter; + pattern_line_iter != content_lines.end(); + ++pattern_line_iter) + { + if (!ctre::match<"[bo$0-9!]+">(*pattern_line_iter)) + { + throw InvalidRLEFileError(path, "Invalid pattern line"); + } + } + + auto pattern = std::string(); + + for (auto pattern_line_iter = first_pattern_line_iter; + pattern_line_iter != content_lines.end(); + ++pattern_line_iter) + { + pattern.append(*pattern_line_iter); + } + + auto run_count_str = std::string(); + + for (const auto &character : pattern) + { + if (static_cast(isdigit(character))) + { + run_count_str += character; + continue; + } + + if (character == '!') + { + break; + } + + const auto run_count = run_count_str.empty() ? 1 : std::stoi(run_count_str); + + run_count_str.clear(); + + if (character == '$') + { + pattern_pos.set_x(0); + pattern_pos += Vector2({.x = 0, .y = run_count}); + continue; + } + + for (auto run_index = 0; run_index < run_count; run_index++) + { + if (pattern_size.validate_coords(pattern_pos) != CoordsValidation::VALID) + { + throw InvalidRLEFileError(path, "Pattern goes out of bounds"); + } + + pattern_matrix->set(pattern_pos, character == 'o' ? 'x' : ' '); + + pattern_pos += Vector2::right(); + } + } + + return pattern_matrix; +} + diff --git a/src/game/RLE_reader.hpp b/src/game/RLE_reader.hpp new file mode 100644 index 0000000..797575e --- /dev/null +++ b/src/game/RLE_reader.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "interfaces/RLE_reader.hpp" +#include "interfaces/matrix.hpp" + +#include + +#include +#include + +class RLEReader + : public IRLEReader, + public yacppdic:: + AutoWirable> +{ +public: + explicit RLEReader(const IMatrixFactory &matrix_factory) noexcept; + + [[nodiscard]] auto read_RLE_file(const std::filesystem::path &path) const + -> std::unique_ptr> override; + +private: + IMatrixFactory _matrix_factory; +}; diff --git a/src/game/game.cpp b/src/game/game.cpp index 73ab7f5..8d15b86 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -3,7 +3,10 @@ #include "engine/data/bounds.hpp" #include "engine/escape.hpp" #include "engine/keycodes.hpp" +#include "errors/RLE_reader.hpp" +#include "errors/io.hpp" #include "util/algorithm.hpp" +#include "util/fs.hpp" #include "util/string.hpp" #include @@ -21,7 +24,8 @@ Game::Game( std::shared_ptr generation_tracker, std::shared_ptr status_manager, std::shared_ptr user_input_observer, - std::shared_ptr cell_helper) noexcept + std::shared_ptr cell_helper, + std::shared_ptr rle_reader) noexcept : _statusline_factory(std::move(statusline_factory)), _scene(std::move(scene)), _cursor_controller(std::move(cursor_controller)), @@ -29,6 +33,7 @@ Game::Game( _status_manager(std::move(status_manager)), _user_input_observer(std::move(user_input_observer)), _cell_helper(std::move(cell_helper)), + _rle_reader(std::move(rle_reader)), _current_mode(Mode::NORMAL), _minimum_cursor_pos_y(0) { @@ -89,18 +94,74 @@ void Game::on_start() noexcept scene_size.get_width(), scene_size.get_height())); - _commands["ping"] = CommandInfo( - {.option_cnt = 0, - .function = [this](CommandInfo::Options /*options*/) + _commands["open"] = CommandInfo( + {.option_cnt = 1U, + .function = [this](CommandInfo::Options options) { - _erase_entire_line(); + const auto rle_file_path = + expand_path_home(std::filesystem::path(options[0])); - const auto position = _cursor_controller->where(); + std::unique_ptr> rle_matrix; - _cursor_controller->move_to(Vector2({.x = 0, .y = position.get_y()})); + try + { + rle_matrix = _rle_reader->read_RLE_file(rle_file_path); + } + catch (const InvalidRLEFileError &error) + { + _show_command_error(fmt::format("Error: {}", error.what())); + return; + } + catch (const IOError &error) + { + _show_command_error(fmt::format("Error: {}", error.what())); + return; + } + + if (rle_matrix == nullptr) + { + _show_command_error(fmt::format( + "A unknown error occurred while reading RLE file with path '{}'", + rle_file_path.string())); + return; + } + + _return_to_normal_mode(); + + const auto previous_pos = _cursor_controller->where(); + + auto scene_matrix = _scene->get_matrix(); + + for (auto row : *rle_matrix) + { + for (auto &col : row) + { + const auto col_pos = _cursor_controller->where(); + + std::cout.put(col); + + _cursor_controller->move_to(col_pos); + + scene_matrix->set(col_pos, col); + + if (!container_has(_living_cell_positions, col_pos)) + { + _living_cell_positions.push_back(col_pos); + } + + _cursor_controller->move(Vector2::right(), 1U); + } + + fmt::print("\n"); + const auto current_pos = _cursor_controller->where(); + + _cursor_controller->move_to( + Vector2({.x = previous_pos.get_x(), .y = current_pos.get_y() - 1})); + } - std::cout << "pong!"; std::cout.flush(); + + _cursor_controller->move_to(previous_pos); }}); } diff --git a/src/game/game.hpp b/src/game/game.hpp index 4d83fe5..abee6ec 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -1,5 +1,6 @@ #pragma once +#include "interfaces/RLE_reader.hpp" #include "interfaces/cell_helper.hpp" #include "interfaces/cursor.hpp" #include "interfaces/game.hpp" @@ -59,7 +60,8 @@ public: std::shared_ptr generation_tracker, std::shared_ptr status_manager, std::shared_ptr user_input_observer, - std::shared_ptr cell_helper) noexcept; + std::shared_ptr cell_helper, + std::shared_ptr rle_reader) noexcept; void on_start() noexcept override; @@ -76,6 +78,7 @@ private: std::shared_ptr _status_manager; std::shared_ptr _user_input_observer; std::shared_ptr _cell_helper; + std::shared_ptr _rle_reader; Mode _current_mode; -- cgit v1.2.3-18-g5258