diff --git a/Library/PAX_SAPIENTICA/Simulation/JapanProvinces.hpp b/Library/PAX_SAPIENTICA/Simulation/JapanProvinces.hpp index 6ecef4fb0..7abf89cb9 100644 --- a/Library/PAX_SAPIENTICA/Simulation/JapanProvinces.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/JapanProvinces.hpp @@ -70,6 +70,11 @@ namespace paxs { double immigrant_f64 = 0; double increased_immigration = 0; std::uint_least32_t mtdna_region_hash = 0; + + std::uint_least32_t direction_min_distance = 100; + // std::array direction_weight{}; + std::vector direction_weight{}; + std::discrete_distribution<> direction_dist{}; }; /// @brief A class that represents a prefecture in Japan. @@ -257,22 +262,34 @@ namespace paxs { sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("max_pop_placed_per_cell")) || sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("init_pop")) || sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("immigrant")) || - sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("mtdna_region")) + sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("mtdna_region")) || + sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("direction_min_distance")) || + sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("directions")) ) { PAXS_WARNING("Failed to read Japan region TSV file: " + district_tsv_path + " at line " + std::to_string(i)); continue; } District district; - district.id = static_cast(std::stoi(sub_menu_v[menu[MurMur3::calcHash("id")]])); + district.id = static_cast(std::stoul(sub_menu_v[menu[MurMur3::calcHash("id")]])); district.name = sub_menu_v[menu[MurMur3::calcHash("name")]]; - district.region_id = static_cast(std::stoi(sub_menu_v[menu[MurMur3::calcHash("region")]])); - district.settlement_population_min_ad200 = std::stoi(sub_menu_v[menu[MurMur3::calcHash("min_pop_placed_per_cell")]]); - district.settlement_population_max_ad200 = std::stoi(sub_menu_v[menu[MurMur3::calcHash("max_pop_placed_per_cell")]]); - district.init_pop = std::stoi(sub_menu_v[menu[MurMur3::calcHash("init_pop")]]); - district.immigrant = std::stoi(sub_menu_v[menu[MurMur3::calcHash("immigrant")]]); + district.region_id = static_cast(std::stoul(sub_menu_v[menu[MurMur3::calcHash("region")]])); + district.settlement_population_min_ad200 = static_cast(std::stoul(sub_menu_v[menu[MurMur3::calcHash("min_pop_placed_per_cell")]])); + district.settlement_population_max_ad200 = static_cast(std::stoul(sub_menu_v[menu[MurMur3::calcHash("max_pop_placed_per_cell")]])); + district.init_pop = static_cast(std::stoul(sub_menu_v[menu[MurMur3::calcHash("init_pop")]])); + district.immigrant = static_cast(std::stoul(sub_menu_v[menu[MurMur3::calcHash("immigrant")]])); district.immigrant_f64 = static_cast(district.immigrant); district.increased_immigration = std::stod(sub_menu_v[menu[MurMur3::calcHash("increased_immigration")]]); + district.direction_min_distance = static_cast(std::stoul(sub_menu_v[menu[MurMur3::calcHash("direction_min_distance")]])); + + std::vector direction_split = paxs::StringExtensions::split(sub_menu_v[menu[MurMur3::calcHash("directions")]], '/'); + for (std::size_t di = 0; di < direction_split.size() && di < 8; ++di) { + district.direction_weight.emplace_back(std::stod(direction_split[di])); + } + if(district.direction_weight.size() == 0) district.direction_weight.emplace_back(0.0); + // 方向の確率分布を生成 + district.direction_dist = std::discrete_distribution<>(district.direction_weight.begin(), district.direction_weight.end()); + const std::string& mtdna_region_str = sub_menu_v[menu[MurMur3::calcHash("mtdna_region")]]; district.mtdna_region_hash = MurMur3::calcHash(mtdna_region_str.size(), mtdna_region_str.c_str()); district_list.emplace_back(district); diff --git a/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp b/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp index 58f31d993..32904756b 100644 --- a/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp @@ -318,7 +318,7 @@ namespace paxs { /// @brief Move. /// @brief 移動 /// @return 集落グリッドを移動したかどうか - std::tuple move(std::mt19937& engine) noexcept { + std::tuple move(std::mt19937& engine, District& district_) noexcept { Vector2 current_key; Vector2 target_key; @@ -327,17 +327,23 @@ namespace paxs { // 座標を移動 // 移動距離0~max_move_distance - std::uniform_int_distribution<> move_dist(SimulationConstants::getInstance()->min_move_distance, SimulationConstants::getInstance()->max_move_distance); - std::uniform_real_distribution theta_dist(0.0f, static_cast(2.0 * M_PI)); Vector2 current_position = position; Vector2 target_position = current_position; + int loop_count = 0; // 無限ループ回避用のループ上限値 // 小さい領域のシミュレーションでは無限ループする可能性がある while (target_position == current_position || !environment->isLive(target_position)) { - float theta = theta_dist(engine); - int distance = move_dist(engine); + // 無限ループに陥った場合は無視 + if(loop_count >= 100) return { 0, Vector2(), Vector2() }; + int distance = SimulationConstants::getInstance()->move_dist(engine); + + // 移動距離が偏りのある方向を指定する距離以上か判定し、方向を格納する + double theta = (distance >= static_cast(district_.direction_min_distance)) ? + SimulationConstants::getInstance()->theta_dist_array[district_.direction_dist(engine)](engine) : + SimulationConstants::getInstance()->theta_dist(engine); target_position = current_position + Vector2(static_cast(std::cos(theta) * distance), static_cast(std::sin(theta) * distance)); + ++loop_count; } current_key = current_position / SimulationConstants::getInstance()->grid_length; diff --git a/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp b/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp index bc7c39f62..933fb26b9 100644 --- a/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp @@ -102,16 +102,22 @@ namespace paxs { } void moveSettlement(const std::uint_least32_t id, const Vector2 current_key, const Vector2 target_key) noexcept { - auto it = settlement_grids.find(target_key.to(SettlementGridsType{})); + const auto current_index = current_key.to(SettlementGridsType{}); + const auto target_index = target_key.to(SettlementGridsType{}); + + // ターゲットの地域が登録されているか? + auto it = settlement_grids.find(target_index); if (it != settlement_grids.end()) { - it->second.moveSettlementToThis(settlement_grids[current_key.to(SettlementGridsType{})].getSettlement(id)); + // 登録されている場合はそのターゲット地域へ移動 + it->second.moveSettlementToThis(settlement_grids[current_index].getSettlement(id)); } else { + // 登録されていない場合は新しく地域を作成 SettlementGrid settlement_grid = SettlementGrid(target_key * SimulationConstants::getInstance()->grid_length, environment, gen); - settlement_grid.moveSettlementToThis(settlement_grids[current_key.to(SettlementGridsType{})].getSettlement(id)); - settlement_grids[target_key.to(SettlementGridsType{})] = settlement_grid; + settlement_grid.moveSettlementToThis(settlement_grids[current_index].getSettlement(id)); + settlement_grids[target_index] = settlement_grid; } - settlement_grids[current_key.to(SettlementGridsType{})].deleteSettlement(id); + settlement_grids[current_index].deleteSettlement(id); } /// @brief Execute the simulation for the one step. @@ -174,7 +180,7 @@ namespace paxs { continue; } - auto [target_id, current_key, target_key] = settlements[i].move(gen); + auto [target_id, current_key, target_key] = settlements[i].move(gen, japan_provinces->getDistrict(environment->template getData(SimulationConstants::getInstance()->district_key, settlements[i].getPosition()))); if (target_id != 0) { move_list.emplace_back(target_id, current_key, target_key); diff --git a/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp b/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp index 6c34f0ca0..d4bb3e138 100644 --- a/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp @@ -16,6 +16,7 @@ ##########################################################################################*/ +#include #include #include #include @@ -38,6 +39,10 @@ namespace paxs { constexpr std::uint_least8_t female_value = 0; // 女性 constexpr std::uint_least8_t male_value = 1; // 男性 + constexpr double pi_per_4 = M_PI / 4.0; // π/4 + constexpr double pi_per_8 = M_PI / 8.0; // π/8 + // constexpr double pi_per_4m8 = pi_per_4 - pi_per_8; // π/4ーπ/8 + // std::sqrt(2 * M_PI) constexpr double sqrt_2_x_pi = static_cast(2.506628275); @@ -140,6 +145,20 @@ namespace paxs { std::uniform_real_distribution random_dist = std::uniform_real_distribution( 0.0, 1.0 ); // 乱数分布 std::uniform_int_distribution step_per_year_dist = std::uniform_int_distribution( 0, 11 ); // 乱数分布 + std::uniform_int_distribution move_dist = std::uniform_int_distribution(min_move_distance, max_move_distance); + std::uniform_real_distribution theta_dist = std::uniform_real_distribution(0.0, static_cast(2.0 * M_PI)); + + std::array, 8> theta_dist_array = { + std::uniform_real_distribution(-pi_per_8, pi_per_8), + std::uniform_real_distribution(pi_per_8, pi_per_8 + pi_per_4), + std::uniform_real_distribution(pi_per_8 + pi_per_4, pi_per_8 + 2* pi_per_4), + std::uniform_real_distribution(pi_per_8 + 2 * pi_per_4, pi_per_8 + 3 * pi_per_4), + std::uniform_real_distribution(pi_per_8 + 3 * pi_per_4, pi_per_8 + 4 * pi_per_4), + std::uniform_real_distribution(pi_per_8 + 4 * pi_per_4, pi_per_8 + 5 * pi_per_4), + std::uniform_real_distribution(pi_per_8 + 5 * pi_per_4, pi_per_8 + 6 * pi_per_4), + std::uniform_real_distribution(pi_per_8 + 7 * pi_per_4, pi_per_8 + 8 * pi_per_4) + }; + private: template void stoiFunc(KeyValueTSV& key_value_tsv_, const std::uint_least32_t key_, Func_&& func_) { @@ -213,6 +232,7 @@ namespace paxs { stoiFunc(kvt, MurMur3::calcHash("max_hunter_gatherer_settlement_population"), [&](const std::string& str_) {max_hunter_gatherer_settlement_weight = 1.0 / static_cast(std::stoul(str_)); }); stoiFunc(kvt, MurMur3::calcHash("min_move_distance"), [&](const std::string& str_) {min_move_distance = static_cast(std::stoul(str_)); }); stoiFunc(kvt, MurMur3::calcHash("max_move_distance"), [&](const std::string& str_) {max_move_distance = static_cast(std::stoul(str_)); }); + move_dist = std::uniform_int_distribution(min_move_distance, max_move_distance); stoiFunc(kvt, MurMur3::calcHash("move_probability"), [&](const std::string& str_) {move_probability = std::stod(str_); }); stoiFunc(kvt, MurMur3::calcHash("child_agriculture_priority"), [&](const std::string& str_) {child_agriculture_priority = std::stod(str_); });