diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4e93724ec77addecd64b1abcf2820a780180b54f..1f4b565ca2bed331562fc4489bf49fe6207ed606 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,7 +13,11 @@ add_executable(application source/main.cpp source/application.cpp
         source/preparable.cpp
         source/room.cpp
         source/gamecontext.cpp
-        source/gamestate.cpp)
+        source/gamestate.cpp
+        source/builders.cpp
+        source/events.cpp
+        source/visitor.cpp
+        source/buttonmenu.cpp)
 
 include(FetchContent)
 set(BUILD_SHARED_LIBS OFF) # Отключаем динамическую линковку (будет статическая)
diff --git a/source/builders.cpp b/source/builders.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1172d5f44161c9780da05cf02f9cf13919860d44
--- /dev/null
+++ b/source/builders.cpp
@@ -0,0 +1 @@
+#include "builders.hpp"
diff --git a/source/builders.hpp b/source/builders.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..aba5bb01999c1e8590af531d429a32a7f49ab58d
--- /dev/null
+++ b/source/builders.hpp
@@ -0,0 +1,59 @@
+#pragma once
+#include "states.hpp"
+#include "gamestate.hpp"
+
+class IGameBuilder {
+public:
+    virtual ~IGameBuilder() = default;
+    virtual void create_rooms() = 0;
+    virtual void set_room_sides() = 0;
+    virtual void create_context(float dynamic_object_ratio) = 0;
+    virtual void create_state(IStateManager& state_manager, sf::VideoMode mode,
+        std::string window_title) = 0;
+    virtual void set_all_to_state() = 0;
+    virtual std::unique_ptr<GameState> get_game() = 0;
+};
+
+class GameBuilderDirector {
+public:
+    ~GameBuilderDirector() = default;
+    GameBuilderDirector(std::shared_ptr<IGameBuilder> ptr_builder, sf::VideoMode mode,
+        std::string title, float dynamic_object_ratio);
+    std::unique_ptr<GameState> build();
+private:
+    std::string m_video_title;
+    sf::VideoMode m_mode;
+    float m_dynamic_object_ratio;
+    std::unique_ptr<IGameBuilder> m_ptr_builder;
+};
+
+class CommonBuilder : public IGameBuilder {
+public:
+    ~CommonBuilder() override = default;
+    void create_context(float dynamic_object_ratio) override;
+    void create_state(IStateManager& state_manager,
+        sf::VideoMode mode, std::string window_title) override;
+    void set_all_to_state() override;
+    std::unique_ptr<GameState> get_game() override;
+private:
+    float m_width;
+    float m_height;
+    float m_room_size;
+    std::vector<std::vector<std::unique_ptr<Room>>> m_rooms;
+    GameContext m_context;
+    std::unique_ptr<GameState> m_state;
+};
+
+class SimpleBuilder : public CommonBuilder {
+public:
+    ~SimpleBuilder() override = default;
+    void create_rooms() override;
+    void set_room_sides() override;
+};
+
+class ComplexBuilder : public CommonBuilder {
+public:
+    ~ComplexBuilder() override = default;
+    void create_rooms() override;
+    void set_room_sides() override;
+};
\ No newline at end of file
diff --git a/source/buttonmenu.cpp b/source/buttonmenu.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..617f095675fcc15a1a115af7e3d2082b30626c5e
--- /dev/null
+++ b/source/buttonmenu.cpp
@@ -0,0 +1,82 @@
+#include "buttonmenu.hpp"
+#include "config.hpp"
+
+void Button::draw_into(sf::RenderWindow& window) const {
+    window.draw(m_rectangle);
+    window.draw(m_text);
+}
+
+void Button::set(sf::Vector2f pos, sf::Vector2f button_size, std::string text,
+                 size_t font_size, std::unique_ptr<ISelectCommand> ptr_command) {
+    m_rectangle.setSize(button_size);
+    m_rectangle.setOrigin(button_size / 2.0f);
+    m_rectangle.setPosition(pos);
+    m_rectangle.setOutlineThickness(config::BUTTON_FRAME_THICKNESS);
+    m_rectangle.setOutlineColor(config::BUTTON_COLOR_FRAME);
+    m_rectangle.setFillColor(config::BUTTON_COLOR_FILL); //TODO:
+
+    m_text.setString(text);
+    m_text.setFont(MyFont::Instance());
+    m_text.setFillColor(config::BUTTON_COLOR_TEXT);
+    m_text.setCharacterSize(font_size);
+    auto bounds = m_text.getLocalBounds();
+    m_text.setOrigin(bounds.getPosition() + bounds.getSize() / 2.0f);
+    m_text.setPosition(pos);
+
+    m_ptr_command = std::move(ptr_command);
+}
+
+void Button::select() {
+    m_rectangle.setFillColor(config::BUTTON_COLOR_SELECTION);
+}
+
+void Button::unselect() {
+    m_rectangle.setFillColor(config::BUTTON_COLOR_FILL);
+}
+
+bool Button::is_position_in(sf::Vector2f pos) {
+    sf::Vector2f diff = pos - m_rectangle.getPosition();
+    return abs(diff.x) < m_rectangle.getSize().x / 2.0f && abs(diff.y) < m_rectangle.getSize().y / 2.0f;
+}
+
+void Button::push() {
+    m_ptr_command->execute();
+}
+
+Menu::Menu(IStateManager& stateManager): m_state_manager(stateManager) {
+    float vertical_padding = config::BUTTON_SIZE.x / 10.0f;
+    float first_pos = (config::SELECT_LEVEL_VIDEO_MODE.height - (m_buttons.size() - 1) * vertical_padding - m_buttons.size() * config::BUTTON_SIZE.y) / 2 + config::BUTTON_SIZE.y / 2;
+    float hor_pos = config::SELECT_LEVEL_VIDEO_MODE.width / 2.0f;
+    sf::Vector2f pos = sf::Vector2f(hor_pos, first_pos);
+    m_buttons[0].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_EASY,
+        config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager));
+    pos.y += vertical_padding + config::BUTTON_SIZE.y;
+    m_buttons[1].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_MEDIUM,
+        config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager));
+    pos.y += vertical_padding + config::BUTTON_SIZE.y;
+    m_buttons[2].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_HARD,
+        config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager));
+    pos.y += vertical_padding + config::BUTTON_SIZE.y;
+    m_buttons[3].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_EXIT,
+        config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager));
+}
+
+
+void Menu::draw_into(sf::RenderWindow& window) const {
+    for (auto& button: m_buttons) {
+        button.draw_into(window);
+    }
+}
+
+void Menu::process_mouse(sf::Vector2f pos, bool is_pressed) {
+    for (auto& button: m_buttons) {
+        if (button.is_position_in(pos)) {
+            button.select();
+            if (is_pressed) {
+                button.push();
+            }
+        } else {
+            button.unselect();
+        }
+    }
+}
\ No newline at end of file
diff --git a/source/buttonmenu.hpp b/source/buttonmenu.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..314fa9720a2c3fc67f75b0770528269434d97335
--- /dev/null
+++ b/source/buttonmenu.hpp
@@ -0,0 +1,31 @@
+#pragma once
+#include "drawable.hpp"
+#include "commands.hpp"
+
+class Button: IDrawable {
+public:
+    ~Button() override = default;
+    void draw_into(sf::RenderWindow& window) const override;
+    Button() = default;
+    void set(sf::Vector2f pos, sf::Vector2f button_size, std::string text,
+             size_t font_size, std::unique_ptr<ISelectCommand> ptr_command);
+    void select();
+    void unselect();
+    bool is_position_in(sf::Vector2f pos);
+    void push();
+private:
+    sf::RectangleShape m_rectangle {};
+    sf::Text m_text {};
+    std::unique_ptr<ISelectCommand> m_ptr_command {};
+};
+
+class Menu: public IDrawable {
+public:
+    ~Menu() override = default;
+    Menu(IStateManager& stateManager);
+    void draw_into(sf::RenderWindow& window) const override;
+    void process_mouse(sf::Vector2f pos, bool is_pressed);
+private:
+    std::array<Button, 4> m_buttons {};
+    IStateManager& m_state_manager;
+};
\ No newline at end of file
diff --git a/source/commands.cpp b/source/commands.cpp
index 6c022c72f1751141b0d37ddb23a244f018dd84aa..32f8f57f91f131336b8d3f0a23f70657e7d48be8 100644
--- a/source/commands.cpp
+++ b/source/commands.cpp
@@ -9,4 +9,3 @@ void ExitCommand::execute() {
 ExitCommand::ExitCommand(IStateManager& state_manager): ChangeStateCommand(state_manager) {}
 
 void GameCommand::execute() {}
-GameCommand::GameCommand(IStateManager& state_manager): ChangeStateCommand(state_manager) {}
diff --git a/source/commands.hpp b/source/commands.hpp
index 8d2fb265949ca5307b46e8dedfdb669c891183dd..27bb8a02e71b9bf9be302565b6231a67979ac650 100644
--- a/source/commands.hpp
+++ b/source/commands.hpp
@@ -1,4 +1,5 @@
 #pragma once
+#include "builders.hpp"
 #include "states.hpp"
 
 class ISelectCommand {
@@ -26,5 +27,8 @@ class GameCommand : public ChangeStateCommand {
     public:
     ~GameCommand() = default;
     void execute() override;
-    GameCommand(IStateManager& state_manager);
+    GameCommand(IStateManager& state_manager,
+        std::unique_ptr<GameBuilderDirector> ptr_director);
+private:
+    std::unique_ptr<GameBuilderDirector> m_ptr_director;
 };
\ No newline at end of file
diff --git a/source/drawable.cpp b/source/drawable.cpp
index 68432b3b34ccdf2dada5e0129e1a01c605ee0df4..29871162682c784679a002e85ddeaa338cbb13e6 100644
--- a/source/drawable.cpp
+++ b/source/drawable.cpp
@@ -12,83 +12,3 @@ MyFont::MyFont() {
         throw std::runtime_error("Failed to load font");
 
 }
-
-void Button::draw_into(sf::RenderWindow& window) const {
-    window.draw(m_rectangle);
-    window.draw(m_text);
-}
-
-void Button::set(sf::Vector2f pos, sf::Vector2f button_size, std::string text,
-                 size_t font_size, std::unique_ptr<ISelectCommand> ptr_command) {
-    m_rectangle.setSize(button_size);
-    m_rectangle.setOrigin(button_size / 2.0f);
-    m_rectangle.setPosition(pos);
-    m_rectangle.setOutlineThickness(config::BUTTON_FRAME_THICKNESS);
-    m_rectangle.setOutlineColor(config::BUTTON_COLOR_FRAME);
-    m_rectangle.setFillColor(config::BUTTON_COLOR_FILL); //TODO:
-
-    m_text.setString(text);
-    m_text.setFont(MyFont::Instance());
-    m_text.setFillColor(config::BUTTON_COLOR_TEXT);
-    m_text.setCharacterSize(font_size);
-    auto bounds = m_text.getLocalBounds();
-    m_text.setOrigin(bounds.getPosition() + bounds.getSize() / 2.0f);
-    m_text.setPosition(pos);
-
-    m_ptr_command = std::move(ptr_command);
-}
-
-void Button::select() {
-    m_rectangle.setFillColor(config::BUTTON_COLOR_SELECTION);
-}
-
-void Button::unselect() {
-    m_rectangle.setFillColor(config::BUTTON_COLOR_FILL);
-}
-
-bool Button::is_position_in(sf::Vector2f pos) {
-    sf::Vector2f diff = pos - m_rectangle.getPosition();
-    return abs(diff.x) < m_rectangle.getSize().x / 2.0f && abs(diff.y) < m_rectangle.getSize().y / 2.0f;
-}
-
-void Button::push() {
-    m_ptr_command->execute();
-}
-
-Menu::Menu(IStateManager& stateManager): m_state_manager(stateManager) {
-    float vertical_padding = config::BUTTON_SIZE.x / 10.0f;
-    float first_pos = (config::SELECT_LEVEL_VIDEO_MODE.height - (m_buttons.size() - 1) * vertical_padding - m_buttons.size() * config::BUTTON_SIZE.y) / 2 + config::BUTTON_SIZE.y / 2;
-    float hor_pos = config::SELECT_LEVEL_VIDEO_MODE.width / 2.0f;
-    sf::Vector2f pos = sf::Vector2f(hor_pos, first_pos);
-    m_buttons[0].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_EASY,
-        config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager));
-    pos.y += vertical_padding + config::BUTTON_SIZE.y;
-    m_buttons[1].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_MEDIUM,
-        config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager));
-    pos.y += vertical_padding + config::BUTTON_SIZE.y;
-    m_buttons[2].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_HARD,
-        config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager));
-    pos.y += vertical_padding + config::BUTTON_SIZE.y;
-    m_buttons[3].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_EXIT,
-        config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager));
-}
-
-
-void Menu::draw_into(sf::RenderWindow& window) const {
-    for (auto& button: m_buttons) {
-        button.draw_into(window);
-    }
-}
-
-void Menu::process_mouse(sf::Vector2f pos, bool is_pressed) {
-    for (auto& button: m_buttons) {
-        if (button.is_position_in(pos)) {
-            button.select();
-            if (is_pressed) {
-                button.push();
-            }
-        } else {
-            button.unselect();
-        }
-    }
-}
diff --git a/source/drawable.hpp b/source/drawable.hpp
index fb4cdc86e50d4fee0bbac6ba4664fe54fda09e66..e3d333ced90a6278b5c18cd6c94fdf9140221be8 100644
--- a/source/drawable.hpp
+++ b/source/drawable.hpp
@@ -1,7 +1,7 @@
 #pragma once
 #include <SFML/Graphics.hpp>
 #include <array>
-#include "commands.hpp"
+
 
 class MyFont {
 public:
@@ -14,32 +14,4 @@ private:
 struct IDrawable {
     virtual void draw_into(sf::RenderWindow& window) const = 0;
     virtual ~IDrawable() = default;
-};
-
-class Button: IDrawable {
-public:
-    ~Button() override = default;
-    void draw_into(sf::RenderWindow& window) const override;
-    Button() = default;
-    void set(sf::Vector2f pos, sf::Vector2f button_size, std::string text,
-             size_t font_size, std::unique_ptr<ISelectCommand> ptr_command);
-    void select();
-    void unselect();
-    bool is_position_in(sf::Vector2f pos);
-    void push();
-private:
-    sf::RectangleShape m_rectangle {};
-    sf::Text m_text {};
-    std::unique_ptr<ISelectCommand> m_ptr_command {};
-};
-
-class Menu: public IDrawable {
-public:
-    ~Menu() = default;
-    Menu(IStateManager& stateManager);
-    void draw_into(sf::RenderWindow& window) const override;
-    void process_mouse(sf::Vector2f pos, bool is_pressed);
-private:
-    std::array<Button, 4> m_buttons {};
-    IStateManager& m_state_manager;
 };
\ No newline at end of file
diff --git a/source/entities.hpp b/source/entities.hpp
index fcf6d0bef20a3c01bfe430988e07d6b0e62de8d0..5b73db4a87dbe461bfc870ceb34096730b1add5a 100644
--- a/source/entities.hpp
+++ b/source/entities.hpp
@@ -1,6 +1,7 @@
 #pragma once
 #include "preparable.hpp"
 #include "room.hpp"
+#include "visitor.hpp"
 
 class IEntity: public IPreparable {
 public:
@@ -11,37 +12,44 @@ protected:
     std::shared_ptr<Room> m_ptr_room;
 };
 
-class Pacman: public IEntity {
+class Pacman: public IEntity, public IVisitor {
 public:
     ~Pacman() override = default;
     void prepare_for_drawing() override;
     void draw_into(sf::RenderWindow& window) const override;
     void move(Room::Direction direction);
+    std::unique_ptr<IGameEvent> visit(std::unique_ptr<Food> ptr_food) override;
+    std::unique_ptr<IGameEvent> visit(std::unique_ptr<Enemy> ptr_enemy) override;
 };
 
-class IStaticEntity: public IEntity {
+class IStaticEntity: public IEntity, public IVisitable {
 public:
     ~IStaticEntity() override = default;
     virtual std::unique_ptr<IStaticEntity> clone() = 0;
 };
 
-class IDynamicEntity: public IEntity {
+class IDynamicEntity: public IEntity, public IVisitable {
+public:
     ~IDynamicEntity() override = default;
     virtual std::unique_ptr<IDynamicEntity> clone() = 0;
     virtual void action() = 0;
 };
 
 class Food: public IStaticEntity {
+public:
     ~Food() override = default;
     void prepare_for_drawing() override;
     void draw_into(sf::RenderWindow& window) const override;
     std::unique_ptr<IStaticEntity> clone() override;
+    std::unique_ptr<IGameEvent> accept(std::unique_ptr<IVisitor> ptr_visitor) override;
 };
 
 class Enemy: public IDynamicEntity {
+public:
     ~Enemy() override = default;
     void prepare_for_drawing() override;
     void draw_into(sf::RenderWindow& window) const override;
     std::unique_ptr<IDynamicEntity> clone() override;
     void action() override;
+    std::unique_ptr<IGameEvent> accept(std::unique_ptr<IVisitor> ptr_visitor) override;
 };
\ No newline at end of file
diff --git a/source/events.cpp b/source/events.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8997233bba68341f5450adae61a0b41745a3ef67
--- /dev/null
+++ b/source/events.cpp
@@ -0,0 +1 @@
+#include "events.hpp"
diff --git a/source/events.hpp b/source/events.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e2f34b3b62e7a7c6cb720f2e79fc42b4e3112c28
--- /dev/null
+++ b/source/events.hpp
@@ -0,0 +1,23 @@
+#pragma once
+#include "gamecontext.hpp"
+
+class IGameEvent {
+public:
+    virtual ~IGameEvent() = default;
+    virtual void handle(std::unique_ptr<GameContext> context) = 0;
+};
+
+class DeleteStaticEntity: public IGameEvent {
+public:
+    ~DeleteStaticEntity() override = default;
+    void handle(std::unique_ptr<GameContext> context) override;
+    DeleteStaticEntity(std::shared_ptr<IStaticEntity> ptr_entity);
+private:
+    std::shared_ptr<IStaticEntity> ptr_entity;
+};
+
+class LostGame: public IGameEvent {
+    public:
+    ~LostGame() override = default;
+    void handle(std::unique_ptr<GameContext> context) override;
+};
\ No newline at end of file
diff --git a/source/gamecontext.hpp b/source/gamecontext.hpp
index 914ef8875240e9ff617df30215e845cc5fc37011..5870ee3bd74e6d2f188f9364b3592af501557047 100644
--- a/source/gamecontext.hpp
+++ b/source/gamecontext.hpp
@@ -1,5 +1,6 @@
 #pragma once
 #include "entities.hpp"
+#include <stack>
 
 class GameContext {
 public:
@@ -9,4 +10,15 @@ public:
     std::vector<std::unique_ptr<IStaticEntity>> static_objects;
     std::vector<std::unique_ptr<IDynamicEntity>> dynamic_objects;
     enum State { INGAME, WIN, LOST } state = INGAME;
+};
+
+class ContextManager {
+public:
+    void reset(GameContext&& context);
+    GameContext& get_current_context();
+    void save_current_context();
+    void restore_previous_context();
+private:
+    GameContext m_initial_context;
+    std::stack<GameContext> m_context_stack;
 };
\ No newline at end of file
diff --git a/source/gamestate.hpp b/source/gamestate.hpp
index 6f70f09beec2219624baeca92e2cd7deaa104fb4..051654879b80a2c4ed3d5af992ab90b3e8766bbd 100644
--- a/source/gamestate.hpp
+++ b/source/gamestate.hpp
@@ -1 +1,20 @@
 #pragma once
+#include "gamecontext.hpp"
+#include "maze.hpp"
+#include "states.hpp"
+
+class GameState : public IState, public IWindowKeeper {
+public:
+    ~GameState() override = default;
+    GameState(IStateManager& state_manager,
+        sf::VideoMode video_mode, std::string window_title);
+    bool do_step() override;
+    void set_maze(Maze&& maze);
+    void set_context(GameContext&& context);
+private:
+    void event_handling() override;
+    void update() override;
+    void render() override;
+    Maze m_maze;
+    ContextManager m_context_manager;
+};
diff --git a/source/selectstate.hpp b/source/selectstate.hpp
index 90c1f4388fb71c359b9a37d75e39990994859f62..7ec3f012ec88eb3e7336eeb524129d8fc0687107 100644
--- a/source/selectstate.hpp
+++ b/source/selectstate.hpp
@@ -1,6 +1,7 @@
 #pragma once
 #include "states.hpp"
-#include "drawable.hpp"
+// #include "drawable.hpp"
+#include "buttonmenu.hpp"
 
 class SelectState: public IState, public IWindowKeeper {
 public:
diff --git a/source/states.hpp b/source/states.hpp
index 9cbb1d43ab7322be1e7d2afd3a6ff105716cdce2..ed4f0cd8bafeba38e0034bbc5088714753e2fae5 100644
--- a/source/states.hpp
+++ b/source/states.hpp
@@ -18,12 +18,6 @@ protected:
     IStateManager& m_state_manager;
 };
 
-class GameState : public IState, public IWindowKeeper {
-    public:
-    GameState(IStateManager& state_manager,
-        sf::VideoMode video_mode, std::string window_title);
-};
-
 class ExitState: public IState {
 public:
     ~ExitState() override = default;
diff --git a/source/visitor.cpp b/source/visitor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0bcb6661fcd36cd193a2423c942cabc4a317c853
--- /dev/null
+++ b/source/visitor.cpp
@@ -0,0 +1 @@
+#include "visitor.hpp"
diff --git a/source/visitor.hpp b/source/visitor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..67b50c1b2a3587a0d738f1cf939b06418007b6b6
--- /dev/null
+++ b/source/visitor.hpp
@@ -0,0 +1,19 @@
+#pragma once
+#include <memory>
+
+class IGameEvent;
+class Food;
+class Enemy;
+
+class IVisitor {
+public:
+    virtual ~IVisitor() = default;
+    virtual std::unique_ptr<IGameEvent> visit(std::unique_ptr<Food> ptr_food) = 0;
+    virtual std::unique_ptr<IGameEvent> visit(std::unique_ptr<Enemy> ptr_enemy) = 0;
+};
+
+class IVisitable {
+    public:
+    virtual ~IVisitable() = default;
+    virtual std::unique_ptr<IGameEvent> accept(std::unique_ptr<IVisitor> ptr_visitor) = 0;
+};
\ No newline at end of file