Skip to content

Commit

Permalink
Merge pull request #115 from AsPJT/feature-fast-marriage
Browse files Browse the repository at this point in the history
婚姻処理の高速化
  • Loading branch information
AsPJT authored Dec 14, 2024
2 parents 4b3b2ab + b24cba3 commit 99d4d27
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 189 deletions.
2 changes: 1 addition & 1 deletion Library/PAX_MAHOROBA/LocationPoint.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1011,7 +1011,7 @@ namespace paxs {
static_cast<int>((old_lli.coordinate.x - (map_view_center_x - map_view_width / 2)) / map_view_width * double(paxg::Window::width())),
static_cast<int>(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));
}
}
}
Expand Down
278 changes: 123 additions & 155 deletions Library/PAX_SAPIENTICA/Simulation/Settlement.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,80 +339,54 @@ namespace paxs {

/// @brief Get the agents.
/// @brief エージェントを取得
const std::vector<Agent> cgetAgents() const noexcept { return agents; }
const std::vector<Agent>& cgetAgents() const noexcept { return agents; }

bool marriagePair(
Marriage3& male_settlement_pair,
std::discrete_distribution<std::size_t>& csl_dist,
const std::vector<std::vector<Settlement>*>& close_settlements_list
) {
// 近隣の集落を探し、エージェントIDと集落IDのペアを作成
std::size_t i = csl_dist(*gen);
if (close_settlements_list.size() <= i) return false; // 配列外
const std::vector<Settlement>& 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<int> dist(0, close_settlements_size - 1);
const std::size_t j = static_cast<std::size_t>(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<Agent>& 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<std::uint_least32_t>(k),
static_cast<std::uint_least32_t>(j),
static_cast<std::uint_least32_t>(i),
close_agent[k].cgetFarming()
};
return true; // 成功
}
}
return false; // 失敗
}

/// @brief Marriage.
/// @brief 婚姻
void marriage(
const std::vector<std::vector<Settlement>*>& close_settlements_list,

// std::vector のメモリ確保・解放のコストを削減するために再利用する
/* この入力値は使わない */ std::vector<std::size_t>& marriageable_female_index,
/* この入力値は使わない */ std::vector<std::pair<std::uint_least32_t, std::uint_least32_t>>& male_settlement_pair,
/* この入力値は使わない */ std::vector<Settlement*>& close_settlements,
std::function<void(const paxs::SettlementAgent, const std::uint_least32_t, const Vector2)> add_agent,
std::function<void(const std::uint64_t, const std::uint_least32_t, const Vector2)> delete_agent,
std::vector<GridType4>& 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) {
Expand All @@ -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<std::size_t> close_settlements_list_probabilities;
std::discrete_distribution<std::size_t> 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<std::size_t>(
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_);
}
}
}
Expand All @@ -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* の経路探索
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit 99d4d27

Please sign in to comment.