#include "game.hpp" #include "engine/data/bounds.hpp" #include "util/algorithm.hpp" #include #include #include #include #include Game::Game( IStatusLineFactory statusline_factory, std::shared_ptr scene, std::shared_ptr cursor_controller, std::shared_ptr generation_tracker, std::shared_ptr status_manager, std::shared_ptr user_input_observer, std::shared_ptr cell_helper) noexcept : _statusline_factory(std::move(statusline_factory)), _scene(std::move(scene)), _cursor_controller(std::move(cursor_controller)), _generation_tracker(std::move(generation_tracker)), _status_manager(std::move(status_manager)), _user_input_observer(std::move(user_input_observer)), _cell_helper(std::move(cell_helper)) { } void Game::on_start() noexcept { const auto scene_size = _scene->size(); std::shared_ptr statusline = _statusline_factory(Bounds({.width = scene_size.get_width(), .height = 1})); _scene->register_component(statusline, Vector2({0, 0})); _status_manager->bind(statusline); _status_manager->set_section_title(StatusLineSection::A, ""); _status_manager->set_section_title(StatusLineSection::B, "X: "); _status_manager->set_section_title(StatusLineSection::C, "Y: "); _status_manager->set_section_title(StatusLineSection::D, "Paused: "); _status_manager->set_section_title(StatusLineSection::E, "Generation: "); _status_manager->set_section_title( StatusLineSection::F, "Minimum time since last generation: "); _status_manager->set_section_title(StatusLineSection::G, "Living cells: "); _status_manager->set_section_title(StatusLineSection::H, "Window size: "); const auto center_position = Vector2( {.x = static_cast(scene_size.get_width()) / 2, .y = static_cast(scene_size.get_height()) / 2}); _cursor_controller->move_to(center_position); _status_manager->set_section_body( StatusLineSection::B, fmt::format("{}", center_position.get_x())); _status_manager->set_section_body( StatusLineSection::C, fmt::format("{}", center_position.get_y())); _status_manager->set_section_body( StatusLineSection::D, _generation_tracker->get_is_paused() ? "yes" : "no"); _status_manager->set_section_body(StatusLineSection::E, "0"); _status_manager->set_section_body(StatusLineSection::F, "0"); _status_manager->set_section_body(StatusLineSection::G, "0"); _status_manager->set_section_body( StatusLineSection::H, fmt::format( "Width {} Height {}", scene_size.get_width(), scene_size.get_height())); _last_update_time = std::chrono::system_clock::now(); } void Game::on_update() noexcept { const auto pressed_key = _user_input_observer->get_currently_pressed_key(); auto cursor_has_moved = false; auto is_generation_stepping = false; switch (pressed_key) { case 'h': if (_move_cursor(Vector2::left())) { cursor_has_moved = true; } break; case 'j': if (_move_cursor(Vector2::down())) { cursor_has_moved = true; } break; case 'k': if (_move_cursor(Vector2::up())) { cursor_has_moved = true; } break; case 'l': if (_move_cursor(Vector2::right())) { cursor_has_moved = true; } break; case 'q': std::exit(EXIT_SUCCESS); case 'i': { const auto position = _cursor_controller->where(); const auto matrix = _scene->get_matrix(); if (matrix->get(position) == 'x') { break; } _set_space(matrix, position, 'x'); _living_cell_positions.push_back(position); break; } case 'x': { const auto position = _cursor_controller->where(); const auto matrix = _scene->get_matrix(); if (matrix->get(position) == ' ') { break; } _set_space(matrix, position, ' '); _living_cell_positions.remove(position); break; } case 'p': { auto onoff = !_generation_tracker->get_is_paused(); _generation_tracker->set_is_paused(onoff); _status_manager->set_section_body(StatusLineSection::D, onoff ? "yes" : "no"); break; } case 's': is_generation_stepping = true; break; case '+': if (_min_time_since_last_gen_millis > 0) { _min_time_since_last_gen_millis -= MIN_TIME_SINCE_LAST_GEN_INCREMENT; } break; case '-': _min_time_since_last_gen_millis += MIN_TIME_SINCE_LAST_GEN_DECREMENT; break; default: break; } if (cursor_has_moved) { const auto current_pos = _cursor_controller->where(); _status_manager->set_section_body( StatusLineSection::B, fmt::format("{}", current_pos.get_x())); _status_manager->set_section_body( StatusLineSection::C, fmt::format("{}", current_pos.get_y())); } _status_manager->set_section_body( StatusLineSection::F, fmt::format("{} milliseconds", _min_time_since_last_gen_millis)); _status_manager->set_section_body( StatusLineSection::G, fmt::format("{}", _living_cell_positions.size())); const auto time_now = std::chrono::system_clock::now(); if (_generation_tracker->get_is_paused() && !is_generation_stepping) { _last_update_time = time_now; return; } const auto time_since_last_gen = std::chrono::duration_cast( time_now - _last_gen_update_time); if (time_since_last_gen.count() <= _min_time_since_last_gen_millis) { _last_update_time = time_now; return; } const auto new_current_gen = _generation_tracker->get_current_generation() + 1U; _generation_tracker->set_current_generation(new_current_gen); _status_manager->set_section_body( StatusLineSection::E, fmt::format("{}", new_current_gen)); _last_gen_update_time = time_now; auto matrix = _scene->get_matrix(); const auto dying_cell_positions = container_filter( _living_cell_positions, [this](const Vector2 &cell_pos) { return _cell_helper->is_cell_dying(cell_pos); }); auto birth_cell_positions = _cell_helper->get_birth_cell_positions(_living_cell_positions); for (const auto &dying_cell_pos : dying_cell_positions) { _set_space(matrix, dying_cell_pos, ' '); const auto cell_found = container_find(_living_cell_positions, dying_cell_pos); if (cell_found != _living_cell_positions.end()) { _living_cell_positions.erase(cell_found); } } for (const auto &birth_cell_pos : birth_cell_positions) { _set_space(matrix, birth_cell_pos, 'x'); _living_cell_positions.push_back(birth_cell_pos); } _last_update_time = time_now; } void Game::on_exit() const noexcept { for (auto row : *_scene->get_matrix()) { for (auto &col : row) { fmt::print("{}", col); } fmt::print("\n"); } std::cout.flush(); } auto Game::_move_cursor(const Vector2 &direction) noexcept -> bool { const auto current_position = _cursor_controller->where(); const auto dest_position = current_position + direction; const auto scene_size = _scene->size(); if (scene_size.validate_coords(dest_position) != CoordsValidation::VALID) { return false; } if (dest_position.get_y() < 1) { return false; } _cursor_controller->move_to(dest_position); return true; } void Game::_set_space( const std::shared_ptr> &matrix, const Vector2 &position, char character) noexcept { const auto prev_position = _cursor_controller->where(); _cursor_controller->move_to(position); std::cout.put(character); std::cout.flush(); matrix->set(position, character); _cursor_controller->move_to(prev_position); }