From 01fa749e3100fb67ae31b2dcc28e79a87bb03917 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=9F=D0=B5=D1=87=D0=B5=D0=BD=D0=B8=D0=BD=20=D0=94=D0=B0?=
 =?UTF-8?q?=D0=BD=D0=B8=D0=BB=D0=B0=20=D0=9C=D0=B8=D1=85=D0=B0=D0=B9=D0=BB?=
 =?UTF-8?q?=D0=BE=D0=B2=D0=B8=D1=87?= <dmpechenin@edu.hse.ru>
Date: Sat, 15 Mar 2025 21:20:49 +0300
Subject: [PATCH] Standard ComplexBuilder was implemented and previous
 ComplexBuilder -> ComplexRandomBuilder. Final was added to some classes.

---
 source/Application/Application.h              |   2 +-
 source/BasicAbstractions/Button/Button.h      |   2 +-
 source/BasicAbstractions/Font.h               |   2 +-
 source/Configuration.h                        |   5 +-
 source/States/ExitState/ExitState.h           |   2 +-
 .../States/GameState/Context/ContextManager.h |   2 +-
 source/States/GameState/Context/GameContext.h |   2 +-
 .../DynamicEntities/DynamicEntities.h         |   2 +-
 .../States/GameState/Entities/Pacman/Pacman.h |   2 +-
 .../Entities/StaticEntities/StaticEntities.h  |   2 +-
 .../GameState/GameBuilder/GameBuilder.cpp     | 119 ++++++++++++++++--
 .../GameState/GameBuilder/GameBuilder.h       |  12 +-
 .../GameBuilder/GameBuilderDirector.h         |   2 +-
 source/States/GameState/GameState.h           |   2 +-
 source/States/GameState/Maze/Maze.h           |   2 +-
 source/States/GameState/Maze/Room/Room.h      |   2 +-
 .../GameState/Maze/Room/RoomSide/RoomSide.h   |   4 +-
 source/States/SelectState/SelectState.cpp     |  14 ++-
 source/States/SelectState/SelectState.h       |   6 +-
 19 files changed, 153 insertions(+), 33 deletions(-)

diff --git a/source/Application/Application.h b/source/Application/Application.h
index a813170..0900b6f 100644
--- a/source/Application/Application.h
+++ b/source/Application/Application.h
@@ -1,7 +1,7 @@
 #pragma once
 #include <States/SelectState/SelectState.h>
 
-class Application : public IStateManager {
+class Application final : public IStateManager {
 public:
     Application() = default;
     int run();
diff --git a/source/BasicAbstractions/Button/Button.h b/source/BasicAbstractions/Button/Button.h
index add9902..5695c94 100644
--- a/source/BasicAbstractions/Button/Button.h
+++ b/source/BasicAbstractions/Button/Button.h
@@ -2,7 +2,7 @@
 #include <BasicAbstractions/Command.h>
 #include <BasicAbstractions/IDrawable.h>
 
-class Button: public IDrawable {
+class Button final : public IDrawable {
 public:
     Button() = default;
     void set(sf::Vector2f pos, sf::Vector2f size, const std::string& text, size_t font_size, std::unique_ptr<ISelectCommand> ptr_command);
diff --git a/source/BasicAbstractions/Font.h b/source/BasicAbstractions/Font.h
index 4fd6264..d77f027 100644
--- a/source/BasicAbstractions/Font.h
+++ b/source/BasicAbstractions/Font.h
@@ -1,7 +1,7 @@
 #pragma once
 #include <Configuration.h>
 
-class MyFont {
+class MyFont final {
 public:
     static sf::Font& Instance() {
         static MyFont instance;
diff --git a/source/Configuration.h b/source/Configuration.h
index 676104d..84d12d4 100644
--- a/source/Configuration.h
+++ b/source/Configuration.h
@@ -5,7 +5,7 @@ namespace config {
     // Общее:
     constexpr unsigned int FRAME_RATE_LIMIT = 60;
     // Меню:
-    const sf::Vector2f BUTTON_SIZE = { 250, 100 };
+    const sf::Vector2f BUTTON_SIZE = { 250, 80 };
     const size_t BUTTON_FONT_SIZE = static_cast<size_t>(BUTTON_SIZE.y / 1.5f);
     constexpr float BUTTON_FRAME_THICKNESS = 2.0f;
     constexpr char FONT_FILE[] = ASSETS_PATH "calibril.ttf";
@@ -14,6 +14,7 @@ namespace config {
     constexpr char BUTTON_TEXT_EASY[] = "Easy";
     constexpr char BUTTON_TEXT_MEDIUM[] = "Medium";
     constexpr char BUTTON_TEXT_HARD[] = "Hard";
+    constexpr char BUTTON_TEXT_EXTREME[] = "Extreme";
     constexpr char BUTTON_TEXT_EXIT[] = "Exit";
     // Игра:
     const sf::VideoMode GAME_VIDEO_MODE{ 1080, 720 };
@@ -25,7 +26,7 @@ namespace config {
     constexpr float HARD_GAME_ENEMY_RATIO = 0.07f;
     constexpr float ROOM_SIZE = 50;
     constexpr float GAME_ENEMY_SIZE = ROOM_SIZE * 0.7;
-    constexpr float GAME_FOOD_SIZE = ROOM_SIZE * 0.2;
+    constexpr float GAME_FOOD_SIZE = ROOM_SIZE * 0.1;
     // Пакмэн:
     constexpr float GAME_PACMAN_SIZE = ROOM_SIZE * 0.8;
     // constexpr sf::Keyboard::Key KEY_LEFT = sf::Keyboard::A;
diff --git a/source/States/ExitState/ExitState.h b/source/States/ExitState/ExitState.h
index f886cd4..ed914a5 100644
--- a/source/States/ExitState/ExitState.h
+++ b/source/States/ExitState/ExitState.h
@@ -1,7 +1,7 @@
 #pragma once
 #include <BasicAbstractions/IState.h>
 
-struct ExitState : IState {
+struct ExitState final : IState {
     using IState::IState;
     bool do_step() noexcept override { return false; }
 };
\ No newline at end of file
diff --git a/source/States/GameState/Context/ContextManager.h b/source/States/GameState/Context/ContextManager.h
index 9c84bc3..8ad7e9f 100644
--- a/source/States/GameState/Context/ContextManager.h
+++ b/source/States/GameState/Context/ContextManager.h
@@ -2,7 +2,7 @@
 #include <States/GameState/Context/GameContext.h>
 #include <stack>
 
-class ContextManager {
+class ContextManager final {
 public:
     void reset(GameContext&& context);
     GameContext& get_current_context() { return m_contexts.top(); }
diff --git a/source/States/GameState/Context/GameContext.h b/source/States/GameState/Context/GameContext.h
index 81b3bdc..21022b2 100644
--- a/source/States/GameState/Context/GameContext.h
+++ b/source/States/GameState/Context/GameContext.h
@@ -3,7 +3,7 @@
 #include <States/GameState/Entities/StaticEntities/StaticEntities.h>
 #include <States/GameState/Entities/DynamicEntities/DynamicEntities.h>
 
-struct GameContext {
+struct GameContext final {
     [[nodiscard]] GameContext clone() const;
     Pacman pacman;
     std::vector<std::unique_ptr<IStaticEntity>> static_objects;
diff --git a/source/States/GameState/Entities/DynamicEntities/DynamicEntities.h b/source/States/GameState/Entities/DynamicEntities/DynamicEntities.h
index b8be739..2033a0f 100644
--- a/source/States/GameState/Entities/DynamicEntities/DynamicEntities.h
+++ b/source/States/GameState/Entities/DynamicEntities/DynamicEntities.h
@@ -8,7 +8,7 @@ struct IDynamicEntity : IEntity {
     ~IDynamicEntity() override = default;
 };
 
-class Enemy : public IDynamicEntity {
+class Enemy final : public IDynamicEntity {
 public:
     Enemy();
     [[nodiscard]] std::unique_ptr<IDynamicEntity> clone() const override;
diff --git a/source/States/GameState/Entities/Pacman/Pacman.h b/source/States/GameState/Entities/Pacman/Pacman.h
index 827fb79..03772e0 100644
--- a/source/States/GameState/Entities/Pacman/Pacman.h
+++ b/source/States/GameState/Entities/Pacman/Pacman.h
@@ -1,7 +1,7 @@
 #pragma once
 #include <States/GameState/Entities/IEntity.h>
 
-class Pacman : public IEntity {
+class Pacman final : public IEntity {
 public:
     Pacman();
     void move(Room::Direction direction);
diff --git a/source/States/GameState/Entities/StaticEntities/StaticEntities.h b/source/States/GameState/Entities/StaticEntities/StaticEntities.h
index d005d7d..193f3fc 100644
--- a/source/States/GameState/Entities/StaticEntities/StaticEntities.h
+++ b/source/States/GameState/Entities/StaticEntities/StaticEntities.h
@@ -6,7 +6,7 @@ struct IStaticEntity : IEntity {
    ~IStaticEntity() override = default;
 };
 
-class Food : public IStaticEntity {
+class Food final : public IStaticEntity {
 public:
     Food();
     [[nodiscard]] std::unique_ptr<IStaticEntity> clone() const override;
diff --git a/source/States/GameState/GameBuilder/GameBuilder.cpp b/source/States/GameState/GameBuilder/GameBuilder.cpp
index 0c4502f..db301b6 100644
--- a/source/States/GameState/GameBuilder/GameBuilder.cpp
+++ b/source/States/GameState/GameBuilder/GameBuilder.cpp
@@ -57,7 +57,7 @@ void SimpleBuilder::create_rooms() {
     const auto number_of_rooms_in_row = static_cast<size_t>(m_width / m_room_size),
         number_of_rooms_in_col = static_cast<size_t>(m_height / m_room_size);
 
-    sf::Vector2f gap(
+    const sf::Vector2f gap(
         (m_width - static_cast<float>(number_of_rooms_in_row) * m_room_size + m_room_size) / 2.f,
         (m_height - static_cast<float>(number_of_rooms_in_col) * m_room_size + m_room_size) / 2.f
     );
@@ -78,8 +78,8 @@ void SimpleBuilder::create_rooms() {
 void SimpleBuilder::set_rooms_sides() {
     if (m_rooms.empty()) return;
     if (m_rooms[0].empty()) return;
-    for (size_t i = 0; i < m_rooms[0].size(); ++i)
-        m_rooms[0][i]->set_side(Room::UP, std::make_shared<Wall>(*m_rooms[0][i]));
+    for (const auto & room : m_rooms[0])
+        room->set_side(Room::UP, std::make_shared<Wall>(*room));
     m_rooms[0][0]->set_side(Room::LEFT, std::make_shared<Wall>(*m_rooms[0][0]));
     for (size_t i = 1; i < m_rooms[0].size() - 1; ++i) {
         auto tmp_side_ptr = std::make_shared<Pass>(*m_rooms[0][i], *m_rooms[0][i-1]);
@@ -110,19 +110,122 @@ void SimpleBuilder::set_rooms_sides() {
     }
 }
 
-ComplexBuilder::ComplexBuilder(const float width, const float height, const float room_size, const sf::Color room_color) :
+void ComplexBuilder::create_rooms() {
+    const auto number_of_rooms_in_row = static_cast<size_t>(m_width / m_room_size),
+        number_of_rooms_in_col = static_cast<size_t>(m_height / m_room_size);
+
+    const sf::Vector2f gap(
+        (m_width - static_cast<float>(number_of_rooms_in_row) * m_room_size + m_room_size) / 2.f,
+        (m_height - static_cast<float>(number_of_rooms_in_col) * m_room_size + m_room_size) / 2.f
+    );
+
+    for (size_t i = 0; i < number_of_rooms_in_col; ++i) {
+        std::vector<std::unique_ptr<Room>> room_row;
+        room_row.reserve(number_of_rooms_in_row);
+        for (size_t j = 0; j < number_of_rooms_in_row; ++j) {
+            if (i % 2 == 0 && j % 2 == 0) continue;
+            auto tmp_room = std::make_unique<Room>(m_room_size);
+            tmp_room->set_color(m_room_color);
+            tmp_room->set_position(gap + sf::Vector2f{ m_room_size * static_cast<float>(j), m_room_size * static_cast<float>(i) });
+            room_row.emplace_back(std::move(tmp_room));
+        }
+        m_rooms.emplace_back(std::move(room_row));
+    }
+}
+
+void ComplexBuilder::set_rooms_sides() {
+    if (m_rooms.empty()) return;
+    if (m_rooms[0].empty()) return;
+    for (size_t i = 0; i < m_rooms.size(); i+=2) {
+        for (const auto & room : m_rooms[i]) {
+            room->set_side(Room::LEFT, std::make_shared<Wall>(*room));
+            room->set_side(Room::RIGHT, std::make_shared<Wall>(*room));
+        }
+    }
+    for (const auto & room : m_rooms[0])
+        room->set_side(Room::UP, std::make_shared<Wall>(*room));
+    for (size_t i = 1; i < m_rooms.size() - 1; i+=2) {
+        m_rooms[i][0]->set_side(Room::LEFT, std::make_shared<Wall>(*m_rooms[i][0]));
+        m_rooms[i][0]->set_side(Room::UP, std::make_shared<Wall>(*m_rooms[i][0]));
+        m_rooms[i][0]->set_side(Room::DOWN, std::make_shared<Wall>(*m_rooms[i][0]));
+        for (size_t j = 1; j < m_rooms[i].size() - 1; ++j) {
+            if (j % 2 == 0) {
+                m_rooms[i][j]->set_side(Room::UP, std::make_shared<Wall>(*m_rooms[i][j]));
+                m_rooms[i][j]->set_side(Room::DOWN, std::make_shared<Wall>(*m_rooms[i][j]));
+                continue;
+            }
+            auto side_ptr_temp = std::make_shared<Pass>(*m_rooms[i][j], *m_rooms[i-1][j-j/2-1]);
+            m_rooms[i][j]->set_side(Room::UP, side_ptr_temp);
+            m_rooms[i-1][j-j/2-1]->set_side(Room::DOWN, side_ptr_temp);
+            side_ptr_temp = std::make_shared<Pass>(*m_rooms[i][j], *m_rooms[i][j-1]);
+            m_rooms[i][j]->set_side(Room::LEFT, side_ptr_temp);
+            m_rooms[i][j-1]->set_side(Room::RIGHT, side_ptr_temp);
+            side_ptr_temp = std::make_shared<Pass>(*m_rooms[i][j], *m_rooms[i][j+1]);
+            m_rooms[i][j]->set_side(Room::RIGHT, side_ptr_temp);
+            m_rooms[i][j+1]->set_side(Room::LEFT, side_ptr_temp);
+            side_ptr_temp = std::make_shared<Pass>(*m_rooms[i][j], *m_rooms[i+1][j-j/2-1]);
+            m_rooms[i][j]->set_side(Room::DOWN, side_ptr_temp);
+            m_rooms[i+1][j-j/2-1]->set_side(Room::UP, side_ptr_temp);
+        }
+        if (m_rooms[i].size() % 2 == 1) {
+            m_rooms[i][m_rooms[i].size()-1]->set_side(Room::UP, std::make_shared<Wall>(*m_rooms[i][m_rooms[i].size()-1]));
+            m_rooms[i][m_rooms[i].size()-1]->set_side(Room::DOWN, std::make_shared<Wall>(*m_rooms[i][m_rooms[i].size()-1]));
+        }
+        else {
+            auto side_ptr_temp = std::make_shared<Pass>(*m_rooms[i][m_rooms[i].size()-1], *m_rooms[i][m_rooms[i].size()-2]);
+            m_rooms[i][m_rooms[i].size()-1]->set_side(Room::LEFT, side_ptr_temp);
+            m_rooms[i][m_rooms[i].size()-2]->set_side(Room::RIGHT, side_ptr_temp);
+            side_ptr_temp = std::make_shared<Pass>(*m_rooms[i][m_rooms[i].size()-1], *m_rooms[i-1][m_rooms[i].size()-1]);
+            m_rooms[i][m_rooms[i].size()-1]->set_side(Room::UP, side_ptr_temp);
+            m_rooms[i-1][m_rooms[i].size()-1]->set_side(Room::DOWN, side_ptr_temp);
+        }
+        m_rooms[i][m_rooms[i].size()-1]->set_side(Room::RIGHT, std::make_shared<Wall>(*m_rooms[i][m_rooms[i].size()-1]));
+    }
+    for (auto& room : m_rooms[m_rooms.size()-1])
+        room->set_side(Room::DOWN, std::make_shared<Wall>(*room));
+    if (m_rooms.size() % 2 == 0) {
+        m_rooms[m_rooms.size()-1][0]->set_side(Room::UP, std::make_shared<Wall>(*m_rooms[m_rooms.size()-1][0]));
+        m_rooms[m_rooms.size()-1][0]->set_side(Room::LEFT, std::make_shared<Wall>(*m_rooms[m_rooms.size()-1][0]));
+        for (size_t i = 1; i < m_rooms[m_rooms.size()-1].size() - 1; ++i) {
+            if (i % 2 == 0) {
+                m_rooms[m_rooms.size()-1][i]->set_side(Room::UP, std::make_shared<Wall>(*m_rooms[m_rooms.size()-1][i]));
+                continue;
+            }
+            auto side_ptr_temp = std::make_shared<Pass>(*m_rooms[m_rooms.size()-1][i], *m_rooms[m_rooms.size()-2][i-i/2-1]);
+            m_rooms[m_rooms.size()-1][i]->set_side(Room::UP, side_ptr_temp);
+            m_rooms[m_rooms.size()-2][i-i/2-1]->set_side(Room::DOWN, side_ptr_temp);
+            side_ptr_temp = std::make_shared<Pass>(*m_rooms[m_rooms.size()-1][i-1], *m_rooms[m_rooms.size()-1][i]);
+            m_rooms[m_rooms.size()-1][i]->set_side(Room::LEFT, side_ptr_temp);
+            m_rooms[m_rooms.size()-1][i-1]->set_side(Room::RIGHT, side_ptr_temp);
+            side_ptr_temp = std::make_shared<Pass>(*m_rooms[m_rooms.size()-1][i+1], *m_rooms[m_rooms.size()-1][i]);
+            m_rooms[m_rooms.size()-1][i]->set_side(Room::RIGHT, side_ptr_temp);
+            m_rooms[m_rooms.size()-1][i+1]->set_side(Room::LEFT, side_ptr_temp);
+        }
+        if (m_rooms[m_rooms.size()-1].size() % 2 == 1) {
+            m_rooms[m_rooms.size()-1][m_rooms[m_rooms.size()-1].size()-1]->set_side(Room::UP, std::make_shared<Wall>(*m_rooms[m_rooms.size()-1][m_rooms[m_rooms.size()-1].size()-1]));
+        }
+        else {
+            auto side_ptr_temp = std::make_shared<Pass>(*m_rooms[m_rooms.size()-1][m_rooms[m_rooms.size()-1].size()-1], *m_rooms[m_rooms.size()-2][m_rooms[m_rooms.size()-1].size()-1]);
+            m_rooms[m_rooms.size()-1][m_rooms[m_rooms.size()-1].size()-1]->set_side(Room::UP, side_ptr_temp);
+            m_rooms[m_rooms.size()-2][m_rooms[m_rooms.size()-1].size()-1]->set_side(Room::DOWN, side_ptr_temp);
+        }
+        m_rooms[m_rooms.size()-1][m_rooms[m_rooms.size()-1].size()-1]->set_side(Room::RIGHT, std::make_shared<Wall>(*m_rooms[m_rooms.size()-1][m_rooms[m_rooms.size()-1].size()-1]));
+    }
+}
+
+ComplexRandomBuilder::ComplexRandomBuilder(const float width, const float height, const float room_size, const sf::Color room_color) :
     CommonBuilder(width, height, room_size, room_color),
     m_rooms_grid(static_cast<size_t>(height / m_room_size), std::vector(static_cast<size_t>(width / m_room_size), false)),
     m_number_of_rooms_in_row(static_cast<size_t>(width / m_room_size)),
     m_number_of_rooms_in_col(static_cast<size_t>(height / m_room_size)),
     gen(rd()) {}
 
-bool ComplexBuilder::is_valid(const int x, const int y) const {
+bool ComplexRandomBuilder::is_valid(const int x, const int y) const {
     if (x >= m_number_of_rooms_in_row || y >= m_number_of_rooms_in_col || x < 0 || y < 0) return false;
     return true;
 }
 
-void ComplexBuilder::collapse_function() {
+void ComplexRandomBuilder::collapse_function() {
     std::uniform_int_distribution<size_t> row_dist(0, m_number_of_rooms_in_row-1);
     std::uniform_int_distribution<size_t> col_dist(0, m_number_of_rooms_in_col-1);
     std::normal_distribution prob_dist(0.67f, 0.25f);
@@ -162,7 +265,7 @@ void ComplexBuilder::collapse_function() {
     }
 }
 
-void ComplexBuilder::create_rooms() {
+void ComplexRandomBuilder::create_rooms() {
     collapse_function();
 
     const sf::Vector2f gap(
@@ -189,7 +292,7 @@ void ComplexBuilder::create_rooms() {
     }
 }
 
-void ComplexBuilder::set_rooms_sides() {
+void ComplexRandomBuilder::set_rooms_sides() {
     for (size_t i = 0; i < m_number_of_rooms_in_col; ++i) {
         for (size_t j = 0; j < m_number_of_rooms_in_row; ++j) {
             if (!m_rooms_grid[i][j]) continue;
diff --git a/source/States/GameState/GameBuilder/GameBuilder.h b/source/States/GameState/GameBuilder/GameBuilder.h
index eee5204..3188076 100644
--- a/source/States/GameState/GameBuilder/GameBuilder.h
+++ b/source/States/GameState/GameBuilder/GameBuilder.h
@@ -31,15 +31,21 @@ protected:
     std::unique_ptr<GameState> m_game_state;
 };
 
-struct SimpleBuilder : CommonBuilder {
+struct SimpleBuilder final : CommonBuilder {
     using CommonBuilder::CommonBuilder;
     void create_rooms() override;
     void set_rooms_sides() override;
 };
 
-class ComplexBuilder : public CommonBuilder {
+struct ComplexBuilder final : CommonBuilder {
+    using CommonBuilder::CommonBuilder;
+    void create_rooms() override;
+    void set_rooms_sides() override;
+};
+
+class ComplexRandomBuilder final : public CommonBuilder {
 public:
-    ComplexBuilder(float width, float height, float room_size, sf::Color room_color);
+    ComplexRandomBuilder(float width, float height, float room_size, sf::Color room_color);
     void create_rooms() override;
     void set_rooms_sides() override;
 private:
diff --git a/source/States/GameState/GameBuilder/GameBuilderDirector.h b/source/States/GameState/GameBuilder/GameBuilderDirector.h
index 0f39088..407f38d 100644
--- a/source/States/GameState/GameBuilder/GameBuilderDirector.h
+++ b/source/States/GameState/GameBuilder/GameBuilderDirector.h
@@ -1,7 +1,7 @@
 #pragma once
 #include <States/GameState/GameBuilder/GameBuilder.h>
 
-class GameBuilderDirector {
+class GameBuilderDirector final {
 public:
     GameBuilderDirector(std::unique_ptr<IGameBuilder>&& ptr_builder, const sf::VideoMode& mode, std::string  window_title, const float dynamic_object_ratio) :
         m_window_title(std::move(window_title)), m_mode(mode), m_dynamic_object_ratio(dynamic_object_ratio), m_ptr_builder(std::move(ptr_builder)) {}
diff --git a/source/States/GameState/GameState.h b/source/States/GameState/GameState.h
index 16359aa..cc5a479 100644
--- a/source/States/GameState/GameState.h
+++ b/source/States/GameState/GameState.h
@@ -5,7 +5,7 @@
 #include <States/GameState/Maze/Maze.h>
 #include <States/GameState/Context/ContextManager.h>
 
-class GameState : public IState, public IWindowKeeper {
+class GameState final : public IState, public IWindowKeeper {
 public:
     explicit GameState(IStateManager& state_manager, const sf::VideoMode& video_mode, const sf::String& window_title) :
         IState(state_manager), IWindowKeeper(video_mode, window_title) {
diff --git a/source/States/GameState/Maze/Maze.h b/source/States/GameState/Maze/Maze.h
index ee99379..e6d29e6 100644
--- a/source/States/GameState/Maze/Maze.h
+++ b/source/States/GameState/Maze/Maze.h
@@ -4,7 +4,7 @@
 
 #include <vector>
 
-class Maze : public IDrawable {
+class Maze final : public IDrawable {
 public:
     Maze() = default;
     explicit Maze(std::vector<std::unique_ptr<Room>>&& rooms) : m_rooms(std::move(rooms)) {}
diff --git a/source/States/GameState/Maze/Room/Room.h b/source/States/GameState/Maze/Room/Room.h
index 844107c..a6780f3 100644
--- a/source/States/GameState/Maze/Room/Room.h
+++ b/source/States/GameState/Maze/Room/Room.h
@@ -7,7 +7,7 @@
 
 struct IRoomSide;
 
-class Room: public IDrawable {
+class Room final : public IDrawable {
 public:
     enum Direction { INVALID = -1, LEFT, RIGHT, UP, DOWN };
     explicit Room(float size);
diff --git a/source/States/GameState/Maze/Room/RoomSide/RoomSide.h b/source/States/GameState/Maze/Room/RoomSide/RoomSide.h
index 411fc35..07a452b 100644
--- a/source/States/GameState/Maze/Room/RoomSide/RoomSide.h
+++ b/source/States/GameState/Maze/Room/RoomSide/RoomSide.h
@@ -9,7 +9,7 @@ struct IRoomSide : IPreparable {
     ~IRoomSide() override = default;
 };
 
-class Pass : public IRoomSide {
+class Pass final : public IRoomSide {
 public:
     explicit Pass(Room& room1, Room& room2) : m_room1(room1), m_room2(room2) {}
     void enter(IEntity* entity) const override;
@@ -20,7 +20,7 @@ private:
     Room& m_room2;
 };
 
-class Wall : public IRoomSide {
+class Wall final : public IRoomSide {
 public:
     explicit Wall(Room& room) : m_room(room) {}
     void enter(IEntity* entity) const override {}
diff --git a/source/States/SelectState/SelectState.cpp b/source/States/SelectState/SelectState.cpp
index 28fa929..8332659 100644
--- a/source/States/SelectState/SelectState.cpp
+++ b/source/States/SelectState/SelectState.cpp
@@ -4,8 +4,8 @@
 
 Menu::Menu(IStateManager& state_manager) {
     const float pos_left = (static_cast<float>(config::SELECT_LEVEL_VIDEO_MODE.width) - config::BUTTON_SIZE.x) / 2;
-    const float pos_diff = (static_cast<float>(config::SELECT_LEVEL_VIDEO_MODE.height) - config::BUTTON_SIZE.y * 4) / 10;
-    const float pos_top = (static_cast<float>(config::SELECT_LEVEL_VIDEO_MODE.height) - config::BUTTON_SIZE.y*4 - pos_diff*3) / 2;
+    const float pos_diff = (static_cast<float>(config::SELECT_LEVEL_VIDEO_MODE.height) - config::BUTTON_SIZE.y * 5) / 10;
+    const float pos_top = (static_cast<float>(config::SELECT_LEVEL_VIDEO_MODE.height) - config::BUTTON_SIZE.y*5 - pos_diff*4) / 2;
 
     auto easy_level_command = std::make_unique<GameCommand>(
         state_manager,
@@ -31,6 +31,14 @@ Menu::Menu(IStateManager& state_manager) {
             config::HARD_GAME_TITLE,
             config::HARD_GAME_ENEMY_RATIO));
 
+    auto extreme_level_command = std::make_unique<GameCommand>(
+        state_manager,
+        std::make_unique<GameBuilderDirector>(
+            std::make_unique<ComplexRandomBuilder>(config::GAME_VIDEO_MODE.width, config::GAME_VIDEO_MODE.height, config::ROOM_SIZE, config::GAME_COLOR_ROOM),
+            config::GAME_VIDEO_MODE,
+            config::HARD_GAME_TITLE,
+            config::HARD_GAME_ENEMY_RATIO));
+
     m_buttons[0].set(sf::Vector2f{pos_left, pos_top}, config::BUTTON_SIZE, config::BUTTON_TEXT_EASY,
         config::BUTTON_FONT_SIZE, std::move(easy_level_command));
     m_buttons[1].set(sf::Vector2f{pos_left, pos_top + config::BUTTON_SIZE.y + pos_diff}, config::BUTTON_SIZE,
@@ -38,6 +46,8 @@ Menu::Menu(IStateManager& state_manager) {
     m_buttons[2].set(sf::Vector2f{pos_left, pos_top + 2*(config::BUTTON_SIZE.y + pos_diff)}, config::BUTTON_SIZE,
      config::BUTTON_TEXT_HARD, config::BUTTON_FONT_SIZE, std::move(hard_level_command));
     m_buttons[3].set(sf::Vector2f{pos_left, pos_top + 3*(config::BUTTON_SIZE.y + pos_diff)}, config::BUTTON_SIZE,
+    config::BUTTON_TEXT_EXTREME, config::BUTTON_FONT_SIZE, std::move(extreme_level_command));
+    m_buttons[4].set(sf::Vector2f{pos_left, pos_top + 4*(config::BUTTON_SIZE.y + pos_diff)}, config::BUTTON_SIZE,
      config::BUTTON_TEXT_EXIT, config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(state_manager));
 }
 
diff --git a/source/States/SelectState/SelectState.h b/source/States/SelectState/SelectState.h
index f94f31c..fd99b81 100644
--- a/source/States/SelectState/SelectState.h
+++ b/source/States/SelectState/SelectState.h
@@ -5,16 +5,16 @@
 #include <BasicAbstractions/IDrawable.h>
 #include <BasicAbstractions/Button/Button.h>
 
-class Menu: public IDrawable {
+class Menu final : public IDrawable {
 public:
     explicit Menu(IStateManager& state_manager);
     void process_mouse(sf::Vector2f pos, bool is_pressed);
     void draw_into(sf::RenderWindow& window) const override;
 private:
-    std::array<Button, 4> m_buttons;
+    std::array<Button, 5> m_buttons;
 };
 
-class SelectState : public IState, public IWindowKeeper {
+class SelectState final : public IState, public IWindowKeeper {
 public:
     explicit SelectState(IStateManager& state_manager, const sf::VideoMode& video_mode, const sf::String& window_title) :
     IState(state_manager), IWindowKeeper(video_mode, window_title), m_menu(state_manager) {
-- 
GitLab