From 5b7d2d19f506e7c7c4d3632fc74de18ef655ac54 Mon Sep 17 00:00:00 2001 From: wanotaitei Date: Wed, 11 Dec 2024 18:27:22 +0900 Subject: [PATCH 1/4] upd: fast marriage --- .../PAX_SAPIENTICA/Simulation/Settlement.hpp | 115 ++++++++---------- .../Simulation/SettlementAgent.hpp | 4 + .../Simulation/SettlementSimulator.hpp | 22 +--- 3 files changed, 55 insertions(+), 86 deletions(-) diff --git a/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp b/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp index 944622b31..d08583688 100644 --- a/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp @@ -339,7 +339,7 @@ namespace paxs { /// @brief Get the agents. /// @brief エージェントを取得 - const std::vector cgetAgents() const noexcept { return agents; } + const std::vector& cgetAgents() const noexcept { return agents; } /// @brief Marriage. /// @brief 婚姻 @@ -350,8 +350,6 @@ namespace paxs { /* この入力値は使わない */ std::vector& marriageable_female_index, /* この入力値は使わない */ std::vector>& male_settlement_pair, /* この入力値は使わない */ std::vector& close_settlements, - std::function add_agent, - std::function delete_agent, std::vector& marriage_pos_list ) noexcept { // 結婚の条件を満たすエージェントを取得 @@ -392,21 +390,17 @@ namespace paxs { } // エージェントIDと集落IDのペアを作成 male_settlement_pair.clear(); - for (auto& close_settlement : close_settlements) { - for (auto& agent : close_settlement->cgetAgents()) { - if (agent.isAbleToMarriage() && agent.isMale()) { - male_settlement_pair.emplace_back(agent.getId(), close_settlement->getId()); + for (std::size_t cs = 0; cs < close_settlements.size(); ++cs) { + const std::vector& close_agent = close_settlements[cs]->cgetAgents(); + const std::size_t ca_size = close_agent.size(); + for (std::size_t ca = 0; ca < ca_size; ++ca) { + if (close_agent[ca].isAbleToMarriage() && close_agent[ca].isMale()) { + male_settlement_pair.emplace_back(ca, cs); } } } - std::shuffle(male_settlement_pair.begin(), male_settlement_pair.end(), *gen); - // 女性と男性の組み合わせをランダムに選択 - //RandomSelector selector(gen); - - // first: 女性のインデックス, second: 男性のインデックス - //selector.select(marriageable_agents_index_pair, - // marriageable_female_index.size(), male_settlement_pair.size()); + std::shuffle(male_settlement_pair.begin(), male_settlement_pair.end(), *gen); const std::size_t num_elements = (std::min)(marriageable_female_index.size(), male_settlement_pair.size()); // 居住婚 @@ -430,73 +424,64 @@ namespace paxs { // シミュレーションの設定で母方に移住するか父方に移住するかを決める for (std::size_t i = 0; i < num_elements; ++i) { - std::uint_least32_t male_id = male_settlement_pair[i/*元婚姻ペア(夫)*/].first; - std::uint_least32_t male_settlement_id = male_settlement_pair[i/*元婚姻ペア(夫)*/].second; + const std::uint_least32_t pair_agent_index = male_settlement_pair[i/*元婚姻ペア(夫)*/].first; + const std::uint_least32_t pair_settlement_index = male_settlement_pair[i/*元婚姻ペア(夫)*/].second; + Settlement& pair_settlement = *(close_settlements[pair_settlement_index]); + Agent& pair_agent = pair_settlement.getAgents()[pair_agent_index]; bool is_found = false; - Vector2 male_settlement_position; - for (std::size_t j = 0; j < close_settlements.size(); ++j) { - if (close_settlements[j]->getId() == male_settlement_id) { + if (i/*元婚姻ペア(妻)*/ >= marriageable_female_index.size()) { + PAXS_ERROR("The FIRST of index_pair is larger than the size of marriageable_female_index."); + continue; + } + if (marriageable_female_index[i/*元婚姻ペア(妻)*/] >= agents.size()) { + PAXS_ERROR("marriageable_female_index is larger than the size of AGENTS."); + continue; + } + Vector2 male_settlement_position = pair_settlement.getPosition(); + // 母方の場合 + if (is_matrilocality) { + Agent male_ = pair_agent; + Agent& female_ = agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]]; - if (i/*元婚姻ペア(妻)*/ >= marriageable_female_index.size()) { - PAXS_ERROR("The FIRST of index_pair is larger than the size of marriageable_female_index."); - continue; - } - if (marriageable_female_index[i/*元婚姻ペア(妻)*/] >= agents.size()) { - PAXS_ERROR("marriageable_female_index is larger than the size of AGENTS."); - continue; - } - male_settlement_position = close_settlements[j]->getPosition(); - // 母方の場合 - if (is_matrilocality) { - Agent male_ = close_settlements[j]->getAgentCopy(male_id); - Agent& female_ = agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]]; + female_.marry(pair_agent.getId(), male_.cgetGenome(), male_.cgetFarming(), male_.cgetHunterGatherer(), male_.cgetLanguage()); + const HumanIndexType female_id = female_.getId(); - female_.marry(male_id, male_.cgetGenome(), male_.cgetFarming(), male_.cgetHunterGatherer(), male_.cgetLanguage()); - const HumanIndexType female_id = female_.getId(); + male_.marry(female_id, female_.cgetGenome(), female_.cgetFarming(), female_.cgetHunterGatherer(), female_.cgetLanguage()); + agents.emplace_back(male_); - male_.marry(female_id, female_.cgetGenome(), female_.cgetFarming(), female_.cgetHunterGatherer(), female_.cgetLanguage()); - agents.emplace_back(male_); + marriage_pos_list.emplace_back(GridType4{ pair_settlement.position.x, pair_settlement.position.y, position.x, position.y }); - marriage_pos_list.emplace_back(GridType4{ close_settlements[j]->position.x, close_settlements[j]->position.y, position.x, position.y }); - } - // 父方の場合 - else { - Agent& male_ = close_settlements[j]->getAgent(male_id); - Agent female_ = agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]]; + pair_agent.setAge((std::numeric_limits::max)()); + pair_agent.setLifeSpan(0); + pair_agent.setPartnerId(0); + } + // 父方の場合 + else { + Agent& male_ = pair_agent; + Agent female_ = agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]]; - female_.marry(male_id, male_.cgetGenome(), male_.cgetFarming(), male_.cgetHunterGatherer(), male_.cgetLanguage()); - const HumanIndexType female_id = female_.getId(); + female_.marry(pair_agent.getId(), male_.cgetGenome(), male_.cgetFarming(), male_.cgetHunterGatherer(), male_.cgetLanguage()); + const HumanIndexType female_id = female_.getId(); - male_.marry(female_id, female_.cgetGenome(), female_.cgetFarming(), female_.cgetHunterGatherer(), female_.cgetLanguage()); + male_.marry(female_id, female_.cgetGenome(), female_.cgetFarming(), female_.cgetHunterGatherer(), female_.cgetLanguage()); - male_settlement_position /= SimulationConstants::getInstance()->cell_group_length; - add_agent(female_, male_settlement_id, male_settlement_position); + male_settlement_position /= SimulationConstants::getInstance()->cell_group_length; + pair_settlement.getAgents().emplace_back(female_); - marriage_pos_list.emplace_back(GridType4{ position.x, position.y, close_settlements[j]->position.x, close_settlements[j]->position.y }); - } - is_found = true; - break; - } + marriage_pos_list.emplace_back(GridType4{ position.x, position.y, pair_settlement.position.x, pair_settlement.position.y }); + + agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]].setAge((std::numeric_limits::max)()); + agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]].setLifeSpan(0); + agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]].setPartnerId(0); } + is_found = true; + break; if (!is_found) { PAXS_ERROR("Settlement not found."); continue; } - - if (is_matrilocality) { - male_settlement_position /= SimulationConstants::getInstance()->cell_group_length; - delete_agent(male_id, male_settlement_id, male_settlement_position); - } - else { - for (std::size_t j = i + 1; j < num_elements; ++j) { - if (marriageable_female_index[j/*元婚姻ペア(妻)*/] < marriageable_female_index[i/*元婚姻ペア(妻)*/]) continue; - marriageable_female_index[j/*元婚姻ペア(妻)*/] -= 1; - } - auto& id_ = marriageable_female_index[i/*元婚姻ペア(妻)*/]; - agents.erase(agents.begin() + id_); - } } } diff --git a/Library/PAX_SAPIENTICA/Simulation/SettlementAgent.hpp b/Library/PAX_SAPIENTICA/Simulation/SettlementAgent.hpp index a332b6547..c08917fb2 100644 --- a/Library/PAX_SAPIENTICA/Simulation/SettlementAgent.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/SettlementAgent.hpp @@ -163,6 +163,10 @@ namespace paxs { void setBirthIntervalCount(const std::uint_least8_t count) noexcept { birth_interval_count = count; } std::uint_least8_t decrementBirthIntervalCount() noexcept { return --birth_interval_count; } + void setAge(const AgeType age_) { age = age_; } + void setLifeSpan(const AgeType life_span_) { life_span = life_span_; } + void setPartnerId(const HumanIndexType partner_id_) { partner_id = partner_id_; } + protected: bool is_married = false; // 結婚しているかどうか std::uint_least8_t birth_interval_count = 0; // 出産の間隔のカウント diff --git a/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp b/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp index 64860c563..ef3729c87 100644 --- a/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp @@ -297,26 +297,6 @@ namespace paxs { m_start_time = std::chrono::system_clock::now(); // 婚姻計測開始 marriage_pos_list.clear(); - auto&& delete_agent = [this](const std::uint_least32_t agent_id, const std::uint_least32_t settlement_id, const Vector2 key) { - auto it = settlement_grids.find(key.to(SettlementGridsType{})); - if (it != settlement_grids.end()) { - it->second.deleteAgent(agent_id, settlement_id); - } - else { - PAXS_ERROR("Settlement grid not found. Key: " + std::to_string(key.x) + ", " + std::to_string(key.y)); - } - }; - - auto&& add_agent = [this](const paxs::SettlementAgent agent_, const std::uint_least32_t settlement_id, const Vector2 key) { - auto it = settlement_grids.find(key.to(SettlementGridsType{})); - if (it != settlement_grids.end()) { - it->second.addAgent(agent_, settlement_id); - } - else { - PAXS_ERROR("Settlement grid not found. Key: " + std::to_string(key.x) + ", " + std::to_string(key.y)); - } - }; - // 結婚の条件を満たすエージェントを取得 std::vector marriageable_female_index; // エージェントIDと集落IDのペアを作成 @@ -355,7 +335,7 @@ namespace paxs { settlement.marriage( close_settlements_list, marriageable_female_index, male_settlement_pair, marriage_settlements, - add_agent, delete_agent, marriage_pos_list + marriage_pos_list ); } } From 5400bdc9f23fd398d6c44b5a79bb299b775e89b8 Mon Sep 17 00:00:00 2001 From: wanotaitei Date: Wed, 11 Dec 2024 21:52:32 +0900 Subject: [PATCH 2/4] upd: Ultra-fast marriage processing --- .../PAX_SAPIENTICA/Simulation/Settlement.hpp | 100 +++++++++--------- .../Simulation/SettlementSimulator.hpp | 10 +- .../Simulation/SimulationConst.hpp | 5 + 3 files changed, 60 insertions(+), 55 deletions(-) diff --git a/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp b/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp index d08583688..1720e42ea 100644 --- a/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp @@ -348,8 +348,7 @@ namespace paxs { // std::vector のメモリ確保・解放のコストを削減するために再利用する /* この入力値は使わない */ std::vector& marriageable_female_index, - /* この入力値は使わない */ std::vector>& male_settlement_pair, - /* この入力値は使わない */ std::vector& close_settlements, + /* この入力値は使わない */ std::vector& male_settlement_pair, std::vector& marriage_pos_list ) noexcept { // 結婚の条件を満たすエージェントを取得 @@ -368,39 +367,58 @@ namespace paxs { if (marriageable_female_index.empty()) { return; } - // 近隣の集落を探す - close_settlements.clear(); - for (const auto& const_close_settlements : close_settlements_list) { - for (std::size_t i = 0; i < const_close_settlements->size(); ++i) { - if ((*const_close_settlements)[i].getPosition().distance_pow2(position) <= SimulationConstants::getInstance()->marriage_search_range_pow2) { - close_settlements.emplace_back(&((*const_close_settlements)[i])); + // 農耕度が高い順にソート + std::sort(marriageable_female_index.begin(), marriageable_female_index.end(), + [&](const std::size_t a, const std::size_t b) { + return agents[a].cgetFarming() > agents[b].cgetFarming(); + }); + + // 近隣の集落を探し、エージェントIDと集落IDのペアを作成 + male_settlement_pair.clear(); + const double const_marriage_search_range_pow2 = SimulationConstants::getInstance()->marriage_search_range_pow2; + for (std::size_t i = 0; i < close_settlements_list.size(); ++i) { + if (marriageable_female_index.size() <= male_settlement_pair.size()) break; + + const std::size_t close_settlements_size = close_settlements_list[i]->size(); + if (close_settlements_size == 0) continue; + std::uniform_int_distribution dist(0, close_settlements_size - 1); + const std::size_t cs_start = static_cast(dist(*gen)); + + for (std::size_t acs = 0, css = cs_start; acs < close_settlements_size; ++acs, ++css) { + if (marriageable_female_index.size() <= male_settlement_pair.size()) break; + if (css >= close_settlements_size) css -= close_settlements_size; + + if ((*close_settlements_list[i])[css].getPosition().distance_pow2(position) <= const_marriage_search_range_pow2) { + const std::vector& close_agent = (*close_settlements_list[i])[css].cgetAgents(); + const std::size_t close_agent_size = close_agent.size(); + for (std::size_t ca = 0; ca < close_agent_size; ++ca) { + if (close_agent[ca].isMale() && close_agent[ca].isAbleToMarriage()) { + male_settlement_pair.emplace_back( + Marriage3{ + static_cast(ca), + static_cast(css), + static_cast(i), + close_agent[ca].cgetFarming() + }); + break; // 1集落1人まで + } + } } } } - // 自分の集落を含めて、近くに集落がない - if (close_settlements.empty()) { + // 自分の集落を含めて、近くに婚約者がない + if (male_settlement_pair.empty()) { PAXS_ERROR("Settlement not found."); return; } - // idで自分を探す - auto it = std::find_if(close_settlements.begin(), close_settlements.end(), [this](const Settlement* const settlement) { return settlement->getId() == id; }); - if (it == close_settlements.end()) { - PAXS_ERROR("Settlement not found."); - return; - } - // エージェントIDと集落IDのペアを作成 - male_settlement_pair.clear(); - for (std::size_t cs = 0; cs < close_settlements.size(); ++cs) { - const std::vector& close_agent = close_settlements[cs]->cgetAgents(); - const std::size_t ca_size = close_agent.size(); - for (std::size_t ca = 0; ca < ca_size; ++ca) { - if (close_agent[ca].isAbleToMarriage() && close_agent[ca].isMale()) { - male_settlement_pair.emplace_back(ca, cs); - } - } - } // 女性と男性の組み合わせをランダムに選択 std::shuffle(male_settlement_pair.begin(), male_settlement_pair.end(), *gen); + // 農耕度が高い順にソート + std::sort(male_settlement_pair.begin(), male_settlement_pair.end(), + [&](const Marriage3 a, const Marriage3 b) { + return a.farming > b.farming; + }); + const std::size_t num_elements = (std::min)(marriageable_female_index.size(), male_settlement_pair.size()); // 居住婚 @@ -424,25 +442,16 @@ namespace paxs { // シミュレーションの設定で母方に移住するか父方に移住するかを決める for (std::size_t i = 0; i < num_elements; ++i) { - const std::uint_least32_t pair_agent_index = male_settlement_pair[i/*元婚姻ペア(夫)*/].first; - const std::uint_least32_t pair_settlement_index = male_settlement_pair[i/*元婚姻ペア(夫)*/].second; - Settlement& pair_settlement = *(close_settlements[pair_settlement_index]); + const std::uint_least32_t pair_agent_index = male_settlement_pair[i].first; + const std::uint_least32_t pair_settlement_index = male_settlement_pair[i].second; + Settlement& pair_settlement = (*(close_settlements_list[male_settlement_pair[i].third]))[pair_settlement_index]; Agent& pair_agent = pair_settlement.getAgents()[pair_agent_index]; - bool is_found = false; - if (i/*元婚姻ペア(妻)*/ >= marriageable_female_index.size()) { - PAXS_ERROR("The FIRST of index_pair is larger than the size of marriageable_female_index."); - continue; - } - if (marriageable_female_index[i/*元婚姻ペア(妻)*/] >= agents.size()) { - PAXS_ERROR("marriageable_female_index is larger than the size of AGENTS."); - continue; - } Vector2 male_settlement_position = pair_settlement.getPosition(); // 母方の場合 if (is_matrilocality) { Agent male_ = pair_agent; - Agent& female_ = agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]]; + Agent& female_ = agents[marriageable_female_index[i]]; female_.marry(pair_agent.getId(), male_.cgetGenome(), male_.cgetFarming(), male_.cgetHunterGatherer(), male_.cgetLanguage()); const HumanIndexType female_id = female_.getId(); @@ -452,14 +461,13 @@ namespace paxs { marriage_pos_list.emplace_back(GridType4{ pair_settlement.position.x, pair_settlement.position.y, position.x, position.y }); - pair_agent.setAge((std::numeric_limits::max)()); pair_agent.setLifeSpan(0); pair_agent.setPartnerId(0); } // 父方の場合 else { Agent& male_ = pair_agent; - Agent female_ = agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]]; + Agent female_ = agents[marriageable_female_index[i]]; female_.marry(pair_agent.getId(), male_.cgetGenome(), male_.cgetFarming(), male_.cgetHunterGatherer(), male_.cgetLanguage()); const HumanIndexType female_id = female_.getId(); @@ -471,17 +479,9 @@ namespace paxs { marriage_pos_list.emplace_back(GridType4{ position.x, position.y, pair_settlement.position.x, pair_settlement.position.y }); - agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]].setAge((std::numeric_limits::max)()); agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]].setLifeSpan(0); agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]].setPartnerId(0); } - is_found = true; - break; - - if (!is_found) { - PAXS_ERROR("Settlement not found."); - continue; - } } } diff --git a/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp b/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp index ef3729c87..d89dfab72 100644 --- a/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp @@ -300,11 +300,9 @@ namespace paxs { // 結婚の条件を満たすエージェントを取得 std::vector marriageable_female_index; // エージェントIDと集落IDのペアを作成 - std::vector> male_settlement_pair; - // 婚姻半径内の集落 - std::vector marriage_settlements; + std::vector male_settlement_pair; - // 近隣8グリッドの集落を取得 + // 近隣9グリッドの集落を取得 std::vector*> close_settlements_list; for (auto& settlement_grid : settlement_grids) { @@ -323,6 +321,8 @@ namespace paxs { } } } + // 近隣9グリッドのシャッフル + std::shuffle(close_settlements_list.begin(), close_settlements_list.end(), gen); for (auto& close_settlements : close_settlements_list) { // 青銅交換 if (close_settlements->size() >= 2) { @@ -334,7 +334,7 @@ namespace paxs { for (auto& settlement : settlements) { settlement.marriage( close_settlements_list, - marriageable_female_index, male_settlement_pair, marriage_settlements, + marriageable_female_index, male_settlement_pair, marriage_pos_list ); } diff --git a/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp b/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp index 78bb502b4..97f4e4114 100644 --- a/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp @@ -62,6 +62,11 @@ namespace paxs { struct GridType4 { GridType sx{}, sy{}, ex{}, ey{}; }; + // 始点と終点を管理(婚姻の前後の位置情報を保持する用) + struct Marriage3 { + std::uint_least32_t first{}, second{}, third{}; + std::uint_least8_t farming{}; + }; struct SimulationConstants { // インスタンスを取得 From 33cfb36b0112c7bff0fd34273bc628675f77c111 Mon Sep 17 00:00:00 2001 From: wanotaitei Date: Thu, 12 Dec 2024 23:25:43 +0900 Subject: [PATCH 3/4] add: marriagePair --- Library/PAX_MAHOROBA/LocationPoint.hpp | 2 +- .../PAX_SAPIENTICA/Simulation/Settlement.hpp | 266 +++++++++--------- .../Simulation/SettlementAgent.hpp | 1 + .../Simulation/SettlementSimulator.hpp | 14 +- .../Simulation/SimulationConst.hpp | 1 + 5 files changed, 133 insertions(+), 151 deletions(-) diff --git a/Library/PAX_MAHOROBA/LocationPoint.hpp b/Library/PAX_MAHOROBA/LocationPoint.hpp index 286e4348d..bfe6e26e6 100644 --- a/Library/PAX_MAHOROBA/LocationPoint.hpp +++ b/Library/PAX_MAHOROBA/LocationPoint.hpp @@ -1011,7 +1011,7 @@ namespace paxs { static_cast((old_lli.coordinate.x - (map_view_center_x - map_view_width / 2)) / map_view_width * double(paxg::Window::width())), static_cast(double(paxg::Window::height()) - ((old_lli.coordinate.y - (map_view_center_y - map_view_height / 2)) / map_view_height * double(paxg::Window::height()))) }; - s3d::Line{ draw_old_pos.x(), draw_old_pos.y(), draw_pos.x(), draw_pos.y() }.drawArrow(2, s3d::Vec2{ 8, 16 }, s3d::Color(221, 67, 98)); + s3d::Line{ draw_old_pos.x(), draw_old_pos.y(), draw_pos.x(), draw_pos.y() }.drawArrow(2, s3d::Vec2{ 8, 16 }, (marriage_pos.is_matrilocality) ? s3d::Color(221, 67, 98) : s3d::Color(87, 66, 221)); } } } diff --git a/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp b/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp index 1720e42ea..13bfc17e6 100644 --- a/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp @@ -341,90 +341,52 @@ namespace paxs { /// @brief エージェントを取得 const std::vector& cgetAgents() const noexcept { return agents; } + bool marriagePair( + Marriage3& male_settlement_pair, + std::discrete_distribution& csl_dist, + const std::vector*>& close_settlements_list + ) { + // 近隣の集落を探し、エージェントIDと集落IDのペアを作成 + std::size_t i = csl_dist(*gen); + if (close_settlements_list.size() <= i) return false; // 配列外 + const std::vector& close_settlements = (*close_settlements_list[i]); + + const std::size_t close_settlements_size = close_settlements.size(); + if (close_settlements_size == 0) return false; // 集落が無かったので失敗 + + std::uniform_int_distribution dist(0, close_settlements_size - 1); + const std::size_t j = static_cast(dist(*gen)); + const Settlement& close_settlement = close_settlements[j]; + if (close_settlement.getPosition().distance_pow2(position) > SimulationConstants::getInstance()->marriage_search_range_pow2) return false; // 婚姻距離内に集落が無いため失敗 + + const std::vector& close_agent = close_settlement.cgetAgents(); + for (std::size_t k = 0; k < close_agent.size(); ++k) { + if (close_agent[k].isMale() && close_agent[k].getLifeSpan() != 0 && close_agent[k].isAbleToMarriage()) { + // 婚姻可能なエージェントを発見 + male_settlement_pair = Marriage3{ + static_cast(k), + static_cast(j), + static_cast(i), + close_agent[k].cgetFarming() + }; + return true; // 成功 + } + } + return false; // 失敗 + } + /// @brief Marriage. /// @brief 婚姻 void marriage( const std::vector*>& close_settlements_list, - - // std::vector のメモリ確保・解放のコストを削減するために再利用する - /* この入力値は使わない */ std::vector& marriageable_female_index, - /* この入力値は使わない */ std::vector& male_settlement_pair, std::vector& marriage_pos_list ) noexcept { - // 結婚の条件を満たすエージェントを取得 - marriageable_female_index.clear(); - for (std::size_t i = 0; i < agents.size(); ++i) { - // 結婚可能かどうか - if (agents[i].isFemale() && agents[i].isAbleToMarriage()) { - // 妊娠していたら婚姻しない(婚姻可能と定義すると再婚者のデータで上書きされ子供への継承が不自然になる) - if (agents[i].getBirthIntervalCount() > 0) continue; - // 婚姻するか乱数で決定 - if (!isMarried(agents[i].getAge())) continue; - marriageable_female_index.emplace_back(i); - } - } - // 結婚の条件を満たすエージェントがいない - if (marriageable_female_index.empty()) { - return; - } - // 農耕度が高い順にソート - std::sort(marriageable_female_index.begin(), marriageable_female_index.end(), - [&](const std::size_t a, const std::size_t b) { - return agents[a].cgetFarming() > agents[b].cgetFarming(); - }); - - // 近隣の集落を探し、エージェントIDと集落IDのペアを作成 - male_settlement_pair.clear(); - const double const_marriage_search_range_pow2 = SimulationConstants::getInstance()->marriage_search_range_pow2; - for (std::size_t i = 0; i < close_settlements_list.size(); ++i) { - if (marriageable_female_index.size() <= male_settlement_pair.size()) break; - - const std::size_t close_settlements_size = close_settlements_list[i]->size(); - if (close_settlements_size == 0) continue; - std::uniform_int_distribution dist(0, close_settlements_size - 1); - const std::size_t cs_start = static_cast(dist(*gen)); - - for (std::size_t acs = 0, css = cs_start; acs < close_settlements_size; ++acs, ++css) { - if (marriageable_female_index.size() <= male_settlement_pair.size()) break; - if (css >= close_settlements_size) css -= close_settlements_size; - - if ((*close_settlements_list[i])[css].getPosition().distance_pow2(position) <= const_marriage_search_range_pow2) { - const std::vector& close_agent = (*close_settlements_list[i])[css].cgetAgents(); - const std::size_t close_agent_size = close_agent.size(); - for (std::size_t ca = 0; ca < close_agent_size; ++ca) { - if (close_agent[ca].isMale() && close_agent[ca].isAbleToMarriage()) { - male_settlement_pair.emplace_back( - Marriage3{ - static_cast(ca), - static_cast(css), - static_cast(i), - close_agent[ca].cgetFarming() - }); - break; // 1集落1人まで - } - } - } - } - } - // 自分の集落を含めて、近くに婚約者がない - if (male_settlement_pair.empty()) { - PAXS_ERROR("Settlement not found."); - return; - } - // 女性と男性の組み合わせをランダムに選択 - std::shuffle(male_settlement_pair.begin(), male_settlement_pair.end(), *gen); - // 農耕度が高い順にソート - std::sort(male_settlement_pair.begin(), male_settlement_pair.end(), - [&](const Marriage3 a, const Marriage3 b) { - return a.farming > b.farming; - }); - - const std::size_t num_elements = (std::min)(marriageable_female_index.size(), male_settlement_pair.size()); + if (close_settlements_list.size() == 0) return; // 居住婚 bool is_matrilocality = false; // 選択居住婚 - //bool is_select_matrilocality = false; + bool is_select_matrilocality = false; // 母方居住婚 if (SimulationConstants::getInstance()->maternal_residence_probability >= 1.0f) { @@ -436,51 +398,77 @@ namespace paxs { } // 選択居住婚 else { - //is_select_matrilocality = true; - is_matrilocality = (SimulationConstants::getInstance()->maternal_residence_probability >= SimulationConstants::getInstance()->random_dist_f32(*gen)); + is_select_matrilocality = true; } - // シミュレーションの設定で母方に移住するか父方に移住するかを決める - for (std::size_t i = 0; i < num_elements; ++i) { - const std::uint_least32_t pair_agent_index = male_settlement_pair[i].first; - const std::uint_least32_t pair_settlement_index = male_settlement_pair[i].second; - Settlement& pair_settlement = (*(close_settlements_list[male_settlement_pair[i].third]))[pair_settlement_index]; - Agent& pair_agent = pair_settlement.getAgents()[pair_agent_index]; - - Vector2 male_settlement_position = pair_settlement.getPosition(); - // 母方の場合 - if (is_matrilocality) { - Agent male_ = pair_agent; - Agent& female_ = agents[marriageable_female_index[i]]; - - female_.marry(pair_agent.getId(), male_.cgetGenome(), male_.cgetFarming(), male_.cgetHunterGatherer(), male_.cgetLanguage()); - const HumanIndexType female_id = female_.getId(); + // 各集落の選定重み + std::vector close_settlements_list_probabilities; + std::discrete_distribution csl_dist; - male_.marry(female_id, female_.cgetGenome(), female_.cgetFarming(), female_.cgetHunterGatherer(), female_.cgetLanguage()); - agents.emplace_back(male_); - - marriage_pos_list.emplace_back(GridType4{ pair_settlement.position.x, pair_settlement.position.y, position.x, position.y }); - - pair_agent.setLifeSpan(0); - pair_agent.setPartnerId(0); - } - // 父方の場合 - else { - Agent& male_ = pair_agent; - Agent female_ = agents[marriageable_female_index[i]]; - - female_.marry(pair_agent.getId(), male_.cgetGenome(), male_.cgetFarming(), male_.cgetHunterGatherer(), male_.cgetLanguage()); - const HumanIndexType female_id = female_.getId(); + // 結婚の条件を満たすエージェントを取得 + for (auto& female : agents) { + // 結婚可能かどうか + if (female.isFemale() && female.isAbleToMarriage()) { + // 妊娠していたら婚姻しない(婚姻可能と定義すると再婚者のデータで上書きされ子供への継承が不自然になる) + if (female.getBirthIntervalCount() > 0) continue; + // 婚姻するか乱数で決定 + if (!isMarried(female.getAge())) continue; - male_.marry(female_id, female_.cgetGenome(), female_.cgetFarming(), female_.cgetHunterGatherer(), female_.cgetLanguage()); + // 集落グリッドを重み付け + if (close_settlements_list_probabilities.size() == 0) { + for (const auto& close_settlements : close_settlements_list) { + close_settlements_list_probabilities.emplace_back(close_settlements->size()); + } + csl_dist = std::discrete_distribution( + close_settlements_list_probabilities.begin(), + close_settlements_list_probabilities.end() + ); + } - male_settlement_position /= SimulationConstants::getInstance()->cell_group_length; - pair_settlement.getAgents().emplace_back(female_); + Marriage3 male_settlement_pair{}; + bool pair_result = false; // ペアが見つかったか? + for (std::size_t pair_loop = 0; pair_loop < 10/*婚姻相手を見つけるループの回数*/ && !pair_result; ++pair_loop) { + pair_result = marriagePair( + male_settlement_pair, csl_dist, close_settlements_list + ); + } + // ペアが見つからなかったので婚姻を諦める + if (!pair_result) continue; + + // ペアが見つかった場合 + const std::uint_least32_t pair_agent_index = male_settlement_pair.first; + const std::uint_least32_t pair_settlement_index = male_settlement_pair.second; + Settlement& pair_settlement = (*(close_settlements_list[male_settlement_pair.third]))[pair_settlement_index]; + Agent& male = pair_settlement.getAgents()[pair_agent_index]; + // 選択居住婚の場合はどちらの住居に移動するか乱数で決定する + if (is_select_matrilocality) { + is_matrilocality = (SimulationConstants::getInstance()->maternal_residence_probability >= SimulationConstants::getInstance()->random_dist_f32(*gen)); + } + // シミュレーションの設定で母方に移住するか父方に移住するかを決める + // 母方の場合 + if (is_matrilocality) { + Agent male_copy = male; + female.marry(male_copy.getId(), male_copy.cgetGenome(), male_copy.cgetFarming(), male_copy.cgetHunterGatherer(), male_copy.cgetLanguage()); + male_copy.marry(female.getId(), female.cgetGenome(), female.cgetFarming(), female.cgetHunterGatherer(), female.cgetLanguage()); + agents.emplace_back(male_copy); + + marriage_pos_list.emplace_back(GridType4{ pair_settlement.position.x, pair_settlement.position.y, position.x, position.y, is_matrilocality }); + + male.setLifeSpan(0); + male.setPartnerId(0); + } + // 父方の場合 + else { + Agent female_copy = female; + female_copy.marry(male.getId(), male.cgetGenome(), male.cgetFarming(), male.cgetHunterGatherer(), male.cgetLanguage()); + male.marry(female_copy.getId(), female_copy.cgetGenome(), female_copy.cgetFarming(), female_copy.cgetHunterGatherer(), female_copy.cgetLanguage()); + pair_settlement.getAgents().emplace_back(female_copy); - marriage_pos_list.emplace_back(GridType4{ position.x, position.y, pair_settlement.position.x, pair_settlement.position.y }); + marriage_pos_list.emplace_back(GridType4{ position.x, position.y, pair_settlement.position.x, pair_settlement.position.y, is_matrilocality }); - agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]].setLifeSpan(0); - agents[marriageable_female_index[i/*元婚姻ペア(妻)*/]].setPartnerId(0); + female.setLifeSpan(0); + female.setPartnerId(0); + } } } } @@ -491,12 +479,28 @@ namespace paxs { birth(kanakuma_life_span); } - /// @brief On update. - /// @brief 更新 - void onUpdate() noexcept { - ageUpdate(); - death(); - is_moved = false; + /// @brief Death. + /// @brief 死亡 + void death() noexcept { + for (auto i = 0; i < agents.size();) { + // 年齢を1増やす + agents[i].incrementAge(); + // もし死んでいなかったら次のエージェントを見る + if (!(agents[i].isDead())) { + ++i; + continue; + } + + const HumanIndexType partner_id = agents[i].getPartnerId(); + if (partner_id != 0) { + auto partnerIt = std::find_if(agents.begin(), agents.end(), [partner_id](const Agent& agent) { return agent.getId() == partner_id; }); + if (partnerIt != agents.end()) { + partnerIt->divorce(); + } + } + agents[i] = agents.back(); // 同義 it = agents.erase(it); + agents.pop_back(); + } } // A* の経路探索 @@ -648,6 +652,10 @@ namespace paxs { /// @brief 移動したかどうかを取得 bool isMoved() const noexcept { return is_moved; } + void setIsMoved(const bool is_moved_) noexcept { + is_moved = is_moved_; + } + /// @brief Get the Bronze. /// @brief 青銅を取得 std::uint_least32_t getBronze() const noexcept { @@ -888,28 +896,6 @@ namespace paxs { #endif } - /// @brief Death. - /// @brief 死亡 - void death() noexcept { - for (auto i = 0; i < agents.size();) { - if (!(agents[i].isDead())) { - ++i; - continue; - } - - const HumanIndexType partner_id = agents[i].getPartnerId(); - if (partner_id != 0) { - auto partnerIt = std::find_if(agents.begin(), agents.end(), [partner_id](const Agent& agent) { return agent.getId() == partner_id; }); - if (partnerIt != agents.end()) { - partnerIt->divorce(); - } - } - - agents[i] = agents.back(); // 同義 it = agents.erase(it); - agents.pop_back(); - } - } - /// @brief Is the agent married? /// @brief 確率で結婚するかどうかを返す bool isMarried(const double age) noexcept { diff --git a/Library/PAX_SAPIENTICA/Simulation/SettlementAgent.hpp b/Library/PAX_SAPIENTICA/Simulation/SettlementAgent.hpp index c08917fb2..fa9a370a2 100644 --- a/Library/PAX_SAPIENTICA/Simulation/SettlementAgent.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/SettlementAgent.hpp @@ -50,6 +50,7 @@ namespace paxs { /// @brief Get the id. /// @brief idを取得 constexpr HumanIndexType getId() const noexcept { return id; } + constexpr AgeType getLifeSpan() const noexcept { return life_span; } /// @brief Is the agent dead? /// @brief エージェントが死んでいるかどうかを返す diff --git a/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp b/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp index d89dfab72..c3d68902c 100644 --- a/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp @@ -321,8 +321,6 @@ namespace paxs { } } } - // 近隣9グリッドのシャッフル - std::shuffle(close_settlements_list.begin(), close_settlements_list.end(), gen); for (auto& close_settlements : close_settlements_list) { // 青銅交換 if (close_settlements->size() >= 2) { @@ -334,7 +332,6 @@ namespace paxs { for (auto& settlement : settlements) { settlement.marriage( close_settlements_list, - marriageable_female_index, male_settlement_pair, marriage_pos_list ); } @@ -345,15 +342,13 @@ namespace paxs { for (auto& settlement_grid : settlement_grids) { for (auto& settlement : settlement_grid.second.getSettlements()) { - settlement.onUpdate(); + settlement.death(); + settlement.setIsMoved(false); } } - + // 集落の削除処理と集落の分割処理 for (auto& settlement_grid : settlement_grids) { settlement_grid.second.checkSettlements(); - } - - for (auto& settlement_grid : settlement_grids) { settlement_grid.second.divideSettlements(); } @@ -365,8 +360,7 @@ namespace paxs { // 渡来数を増やす japan_provinces->update(); } - - ++step_count; + ++step_count; // ステップ数を増やす end_time = std::chrono::system_clock::now(); // 計測終了 processing_time = static_cast(std::chrono::duration_cast(end_time - start_time).count() / 1000.0); } diff --git a/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp b/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp index 97f4e4114..dd5e24571 100644 --- a/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp @@ -61,6 +61,7 @@ namespace paxs { // 始点と終点を管理(婚姻の前後の位置情報を保持する用) struct GridType4 { GridType sx{}, sy{}, ex{}, ey{}; + bool is_matrilocality = false; }; // 始点と終点を管理(婚姻の前後の位置情報を保持する用) struct Marriage3 { From b24cba395c7ae06ffa1d0ac89744bf3ff0b5b8b9 Mon Sep 17 00:00:00 2001 From: wanotaitei Date: Fri, 13 Dec 2024 22:49:52 +0900 Subject: [PATCH 4/4] fix: marriage --- .../PAX_SAPIENTICA/Simulation/Settlement.hpp | 21 ++++++++----------- .../Simulation/SettlementGrid.hpp | 1 + 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp b/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp index 13bfc17e6..1ab57269e 100644 --- a/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp @@ -406,7 +406,8 @@ namespace paxs { std::discrete_distribution csl_dist; // 結婚の条件を満たすエージェントを取得 - for (auto& female : agents) { + for (std::size_t i = 0; i < agents.size(); ++i) { + Agent& female = agents[i]; // 結婚可能かどうか if (female.isFemale() && female.isAbleToMarriage()) { // 妊娠していたら婚姻しない(婚姻可能と定義すると再婚者のデータで上書きされ子供への継承が不自然になる) @@ -427,7 +428,7 @@ namespace paxs { Marriage3 male_settlement_pair{}; bool pair_result = false; // ペアが見つかったか? - for (std::size_t pair_loop = 0; pair_loop < 10/*婚姻相手を見つけるループの回数*/ && !pair_result; ++pair_loop) { + for (std::size_t pair_loop = 0; pair_loop < 50/*婚姻相手を見つけるループの回数*/ && !pair_result; ++pair_loop) { pair_result = marriagePair( male_settlement_pair, csl_dist, close_settlements_list ); @@ -448,26 +449,22 @@ namespace paxs { // 母方の場合 if (is_matrilocality) { Agent male_copy = male; + male.setLifeSpan(0); + male.setPartnerId(0); female.marry(male_copy.getId(), male_copy.cgetGenome(), male_copy.cgetFarming(), male_copy.cgetHunterGatherer(), male_copy.cgetLanguage()); male_copy.marry(female.getId(), female.cgetGenome(), female.cgetFarming(), female.cgetHunterGatherer(), female.cgetLanguage()); - agents.emplace_back(male_copy); - marriage_pos_list.emplace_back(GridType4{ pair_settlement.position.x, pair_settlement.position.y, position.x, position.y, is_matrilocality }); - - male.setLifeSpan(0); - male.setPartnerId(0); + agents.emplace_back(male_copy); } // 父方の場合 else { Agent female_copy = female; + female.setLifeSpan(0); + female.setPartnerId(0); female_copy.marry(male.getId(), male.cgetGenome(), male.cgetFarming(), male.cgetHunterGatherer(), male.cgetLanguage()); male.marry(female_copy.getId(), female_copy.cgetGenome(), female_copy.cgetFarming(), female_copy.cgetHunterGatherer(), female_copy.cgetLanguage()); - pair_settlement.getAgents().emplace_back(female_copy); - marriage_pos_list.emplace_back(GridType4{ position.x, position.y, pair_settlement.position.x, pair_settlement.position.y, is_matrilocality }); - - female.setLifeSpan(0); - female.setPartnerId(0); + pair_settlement.getAgents().emplace_back(female_copy); } } } diff --git a/Library/PAX_SAPIENTICA/Simulation/SettlementGrid.hpp b/Library/PAX_SAPIENTICA/Simulation/SettlementGrid.hpp index f5048f9e6..9d4141cef 100644 --- a/Library/PAX_SAPIENTICA/Simulation/SettlementGrid.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/SettlementGrid.hpp @@ -78,6 +78,7 @@ namespace paxs { if (black_list.size() == SimulationConstants::getInstance()->cell_group_length * SimulationConstants::getInstance()->cell_group_length) { // 居住可能な場所がない PAXS_WARNING("No place to live."); + return; // 集落を追加しない } settlement.setPosition(position);