#ifndef STATE_MANAGER_HPP_ #define STATE_MANAGER_HPP_ #include "graph_distances.hpp" #include "threaded_physics.hpp" #include "puzzle.hpp" #include #include #include class state_manager { private: threaded_physics& physics; std::string preset_file; size_t current_preset = 0; std::vector preset_states = {puzzle(4, 5, 0, 0, true, false)}; std::vector preset_comments = {"# Empty"}; // State storage (store states twice for bidirectional lookup). // Everything else should only store indices to state_pool. std::vector state_pool; // Indices are equal to mass_springs mass indices boost::unordered_flat_map state_indices; // Maps states to indices std::vector> links; // Indices are equal to mass_springs springs indices graph_distances node_target_distances; // Buffered and reused if the graph doesn't change boost::unordered_flat_set winning_indices; // Indices of all states where the board is solved std::vector winning_path; // Ordered list of node indices leading to the nearest solved state boost::unordered_flat_set path_indices; // For faster lookup if a vertex is part of the path in renderer std::vector move_history; // Moves between the starting state and the current state boost::unordered_flat_map visit_counts; // How often each state was visited size_t starting_state_index = 0; size_t current_state_index = 0; size_t previous_state_index = 0; int total_moves = 0; bool edited = false; public: state_manager(threaded_physics& _physics, const std::string& _preset_file) : physics(_physics) { parse_preset_file(_preset_file); load_preset(0); } state_manager(const state_manager& copy) = delete; auto operator=(const state_manager& copy) -> state_manager& = delete; state_manager(state_manager&& move) = delete; auto operator=(state_manager&& move) -> state_manager& = delete; private: /** * Inserts a board state into the state_manager and the physics system. * States should only be inserted using this function to keep both systems in sync. * The function checks for duplicates before insertion. * * @param state State to insert * @return Index of insertion (or existing index if duplicate) */ auto synced_try_insert_state(const puzzle& state) -> size_t; /** * Inserts a state link into the state_manager and the physics system. * Links should only be inserted using this function to keep both systems in sync. * The function does not check for duplicates before insertion. * * @param first_index Index of the first linked state * @param second_index Index of the second linked state */ auto synced_insert_link(size_t first_index, size_t second_index) -> void; /** * Inserts an entire statespace into the state_manager and the physics system. * If inserting many states and links in bulk, this function should always be used * to not stress the physics command mutex. * The function does not check for duplicates before insertion. * * @param states List of states to insert * @param _links List of links to insert */ auto synced_insert_statespace(const std::vector& states, const std::vector>& _links) -> void; /** * Clears all states and links (and related) from the state_manager and the physics system. * Note that this leaves any dangling indices (e.g., current_state_index) in an invalid state. */ auto synced_clear_statespace() -> void; public: // Presets auto parse_preset_file(const std::string& _preset_file) -> bool; auto append_preset_file(const std::string& preset_name) -> bool; auto load_preset(size_t preset) -> void; auto load_previous_preset() -> void; auto load_next_preset() -> void; // Update current_state auto update_current_state(const puzzle& p) -> void; auto edit_starting_state(const puzzle& p) -> void; auto goto_starting_state() -> void; auto goto_optimal_next_state() -> void; auto goto_previous_state() -> void; auto goto_most_distant_state() -> void; auto goto_closest_target_state() -> void; // Update graph auto populate_graph() -> void; auto clear_graph_and_add_current(const puzzle& p) -> void; auto clear_graph_and_add_current() -> void; auto populate_winning_indices() -> void; auto populate_node_target_distances() -> void; auto populate_winning_path() -> void; // Index mapping [[nodiscard]] auto get_index(const puzzle& state) const -> size_t; [[nodiscard]] auto get_current_index() const -> size_t; [[nodiscard]] auto get_starting_index() const -> size_t; [[nodiscard]] auto get_state(size_t index) const -> const puzzle&; [[nodiscard]] auto get_current_state() const -> const puzzle&; [[nodiscard]] auto get_starting_state() const -> const puzzle&; // Access [[nodiscard]] auto get_state_count() const -> size_t; [[nodiscard]] auto get_target_count() const -> size_t; [[nodiscard]] auto get_link_count() const -> size_t; [[nodiscard]] auto get_path_length() const -> size_t; [[nodiscard]] auto get_links() const -> const std::vector>&; [[nodiscard]] auto get_winning_indices() const -> const boost::unordered_flat_set&; [[nodiscard]] auto get_visit_counts() const -> const boost::unordered_flat_map&; [[nodiscard]] auto get_winning_path() const -> const std::vector&; [[nodiscard]] auto get_path_indices() const -> const boost::unordered_flat_set&; [[nodiscard]] auto get_current_visits() const -> int; [[nodiscard]] auto get_current_preset() const -> size_t; [[nodiscard]] auto get_preset_count() const -> size_t; [[nodiscard]] auto get_current_preset_comment() const -> const std::string&; [[nodiscard]] auto has_history() const -> bool; [[nodiscard]] auto has_distances() const -> bool; [[nodiscard]] auto get_total_moves() const -> size_t; [[nodiscard]] auto was_edited() const -> bool; }; #endif