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 944622b31..1ab57269e 100644 --- a/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/Settlement.hpp @@ -339,80 +339,54 @@ namespace paxs { /// @brief Get the agents. /// @brief エージェントを取得 - const std::vector cgetAgents() const noexcept { return agents; } + 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& close_settlements, - std::function add_agent, - std::function delete_agent, 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; - } - // 近隣の集落を探す - 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])); - } - } - } - // 自分の集落を含めて、近くに集落がない - if (close_settlements.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 (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()); - } - } - } - 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()); - 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) { @@ -424,78 +398,74 @@ 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) { - std::uint_least32_t male_id = male_settlement_pair[i/*元婚姻ペア(夫)*/].first; - std::uint_least32_t male_settlement_id = male_settlement_pair[i/*元婚姻ペア(夫)*/].second; - - 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; - } - 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(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_); - - 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/*元婚姻ペア(妻)*/]]; + // 各集落の選定重み + std::vector close_settlements_list_probabilities; + std::discrete_distribution csl_dist; - 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()); - - male_settlement_position /= SimulationConstants::getInstance()->cell_group_length; - add_agent(female_, male_settlement_id, male_settlement_position); + // 結婚の条件を満たすエージェントを取得 + for (std::size_t i = 0; i < agents.size(); ++i) { + Agent& female = agents[i]; + // 結婚可能かどうか + if (female.isFemale() && female.isAbleToMarriage()) { + // 妊娠していたら婚姻しない(婚姻可能と定義すると再婚者のデータで上書きされ子供への継承が不自然になる) + if (female.getBirthIntervalCount() > 0) continue; + // 婚姻するか乱数で決定 + if (!isMarried(female.getAge())) continue; - marriage_pos_list.emplace_back(GridType4{ position.x, position.y, close_settlements[j]->position.x, close_settlements[j]->position.y }); + // 集落グリッドを重み付け + if (close_settlements_list_probabilities.size() == 0) { + for (const auto& close_settlements : close_settlements_list) { + close_settlements_list_probabilities.emplace_back(close_settlements->size()); } - is_found = true; - break; + csl_dist = std::discrete_distribution( + close_settlements_list_probabilities.begin(), + close_settlements_list_probabilities.end() + ); } - } - - 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; + Marriage3 male_settlement_pair{}; + bool pair_result = false; // ペアが見つかったか? + 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 + ); + } + // ペアが見つからなかったので婚姻を諦める + 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; + 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()); + marriage_pos_list.emplace_back(GridType4{ pair_settlement.position.x, pair_settlement.position.y, position.x, position.y, is_matrilocality }); + 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()); + marriage_pos_list.emplace_back(GridType4{ position.x, position.y, pair_settlement.position.x, pair_settlement.position.y, is_matrilocality }); + pair_settlement.getAgents().emplace_back(female_copy); } - auto& id_ = marriageable_female_index[i/*元婚姻ペア(妻)*/]; - agents.erase(agents.begin() + id_); } } } @@ -506,12 +476,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* の経路探索 @@ -663,6 +649,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 { @@ -903,28 +893,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 a332b6547..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 エージェントが死んでいるかどうかを返す @@ -163,6 +164,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/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); diff --git a/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp b/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp index 64860c563..c3d68902c 100644 --- a/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp @@ -297,34 +297,12 @@ 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のペアを作成 - 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) { @@ -354,8 +332,7 @@ namespace paxs { for (auto& settlement : settlements) { settlement.marriage( close_settlements_list, - marriageable_female_index, male_settlement_pair, marriage_settlements, - add_agent, delete_agent, marriage_pos_list + marriage_pos_list ); } } @@ -365,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(); } @@ -385,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 78bb502b4..dd5e24571 100644 --- a/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp +++ b/Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp @@ -61,6 +61,12 @@ namespace paxs { // 始点と終点を管理(婚姻の前後の位置情報を保持する用) struct GridType4 { GridType sx{}, sy{}, ex{}, ey{}; + bool is_matrilocality = false; + }; + // 始点と終点を管理(婚姻の前後の位置情報を保持する用) + struct Marriage3 { + std::uint_least32_t first{}, second{}, third{}; + std::uint_least8_t farming{}; }; struct SimulationConstants {