From d30421fa7d7f6ad87d3f90cc0ab491742f0d2548 Mon Sep 17 00:00:00 2001 From: hop311 Date: Mon, 6 Nov 2023 20:39:40 +0000 Subject: History loading generalisation --- src/openvic-simulation/history/HistoryMap.hpp | 110 ++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/openvic-simulation/history/HistoryMap.hpp (limited to 'src/openvic-simulation/history/HistoryMap.hpp') diff --git a/src/openvic-simulation/history/HistoryMap.hpp b/src/openvic-simulation/history/HistoryMap.hpp new file mode 100644 index 0000000..64d886d --- /dev/null +++ b/src/openvic-simulation/history/HistoryMap.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include +#include + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/types/Date.hpp" + +namespace OpenVic { + + struct HistoryEntry { + private: + Date PROPERTY(date); + + protected: + HistoryEntry(Date new_date); + }; + + struct GameManager; + + /* Helper functions to avoid cyclic dependency issues */ + Date _get_start_date(GameManager const& game_manager); + Date _get_end_date(GameManager const& game_manager); + + template _Entry, typename... Args> + struct HistoryMap { + using entry_type = _Entry; + + private: + std::map> PROPERTY(entries); + + bool _try_load_history_entry(GameManager const& game_manager, Args... args, Date date, ast::NodeCPtr root) { + typename decltype(entries)::iterator it = entries.find(date); + if (it == entries.end()) { + const std::pair result = entries.emplace(date, _make_entry(date)); + if (result.second) { + it = result.first; + } else { + Logger::error("Failed to create history entry at date ", date); + return false; + } + } + return _load_history_entry(game_manager, args..., *it->second, root); + } + + protected: + HistoryMap() = default; + + virtual std::unique_ptr _make_entry(Date date) const = 0; + + virtual bool _load_history_entry( + GameManager const& game_manager, Args... args, entry_type& entry, ast::NodeCPtr root + ) = 0; + + bool _load_history_file(GameManager const& game_manager, Args... args, ast::NodeCPtr root) { + return _try_load_history_entry(game_manager, args..., _get_start_date(game_manager), root); + } + + bool _load_history_sub_entry_callback( + GameManager const& game_manager, Args... args, Date date, ast::NodeCPtr root, std::string_view key, + ast::NodeCPtr value, NodeTools::key_value_callback_t default_callback = NodeTools::key_value_invalid_callback + ) { + /* Date blocks (loaded into the corresponding HistoryEntry) */ + bool is_date = false; + const Date sub_date { Date::from_string(key, &is_date, true) }; + if (is_date) { + if (sub_date <= date) { + Logger::error("History entry ", sub_date, " defined before parent entry date ", date); + return false; + } + const Date end_date = _get_end_date(game_manager); + if (sub_date > end_date) { + Logger::error("History entry ", sub_date, " defined after end date ", end_date); + return false; + } + if (_try_load_history_entry(game_manager, args..., sub_date, value)) { + return true; + } else { + Logger::error("Failed to load history entry at date ", sub_date); + return false; + } + } + + return default_callback(key, value); + } + + public: + /* Returns history entry at specific date, if date doesn't have an entry returns nullptr. */ + entry_type const* get_entry(Date date) const { + typename decltype(entries)::const_iterator it = entries.find(date); + if (it != entries.end()) { + return it->second.get(); + } + return nullptr; + } + /* Returns history entries up to date as an ordered list of entries. */ + std::vector get_entries(Date end) const { + std::vector ret; + for (typename decltype(entries)::value_type const& entry : entries) { + if (entry.first <= end) { + ret.push_back(entry.second.get()); + } + } + std::sort(ret.begin(), ret.end(), [](entry_type const* lhs, entry_type const* rhs) -> bool { + return lhs->get_date() < rhs->get_date(); + }); + return ret; + } + }; +} -- cgit v1.2.3-56-ga3b1