Skip to content

Commit

Permalink
Merge pull request #116 from AsPJT/feature-sim-life-tsv
Browse files Browse the repository at this point in the history
生活に関するTSVデータ入力の追加
AsPJT authored Jan 8, 2025
2 parents 99d4d27 + dba962c commit 3b90696
Showing 16 changed files with 528 additions and 203 deletions.
102 changes: 102 additions & 0 deletions Data/Simulations/Sample/Childbearing.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
age hunter_gatherer agricultural
0 0 0
1 0 0
2 0 0
3 0 0
4 0 0
5 0 0
6 0 0
7 0 0
8 0 0
9 0 0
10 0 0
11 0 0
12 0 0
13 0 0
14 0.009022766008 0.003007588669
15 0.03746975846 0.01248991949
16 0.06757353696 0.02252451232
17 0.09958830351 0.03319610117
18 0.1337557159 0.04458523864
19 0.1703668704 0.05678895681
20 0.2098868832 0.0699622944
21 0.2526807842 0.08422692807
22 0.2995459554 0.09984865178
23 0.3511586331 0.1170528777
24 0.4089346823 0.1363115608
25 0.4743033249 0.1581011083
26 0.5500259893 0.1833419964
27 0.6405890518 0.2135296839
28 0.7534403528 0.2511467843
29 0.9056625696 0.3018875232
30 1.151236496 0.3837454986
31 1.312976024 0.4376586748
32 1.087701736 0.3625672452
33 0.8668848806 0.2889616269
34 0.7222288212 0.2407429404
35 0.6129487486 0.2043162495
36 0.5239938899 0.17466463
37 0.4484244189 0.1494748063
38 0.3825212912 0.1275070971
39 0.3241175852 0.1080391951
40 0.2715395289 0.09051317629
41 0.2237504232 0.07458347441
42 0.1799112904 0.05997043012
43 0.139559001 0.046519667
44 0.1021741604 0.03405805348
45 0.06747719441 0.02249239814
46 0.03521142259 0.01173714086
47 0.005148315889 0.001716105296
48 0 0
49 0 0
50 0 0
51 0 0
52 0 0
53 0 0
54 0 0
55 0 0
56 0 0
57 0 0
58 0 0
59 0 0
60 0 0
61 0 0
62 0 0
63 0 0
64 0 0
65 0 0
66 0 0
67 0 0
68 0 0
69 0 0
70 0 0
71 0 0
72 0 0
73 0 0
74 0 0
75 0 0
76 0 0
77 0 0
78 0 0
79 0 0
80 0 0
81 0 0
82 0 0
83 0 0
84 0 0
85 0 0
86 0 0
87 0 0
88 0 0
89 0 0
90 0 0
91 0 0
92 0 0
93 0 0
94 0 0
95 0 0
96 0 0
97 0 0
98 0 0
99 0 0
100 0 0
2 changes: 1 addition & 1 deletion Data/Simulations/Sample/LifeSpan.tsv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
age jomon_male_ndx jomon_female_ndx yayoi_male_ndx yayoi_female_ndx
age hunter_gatherer_male_ndx hunter_gatherer_female_ndx agricultural_male_ndx agricultural_female_ndx
0 30 30 25 25
1 2.437705134 2.422221692 2.562626297 2.573569918
2 1.78945613 1.783878323 1.911296549 1.911594501
2 changes: 1 addition & 1 deletion Data/Simulations/Sample/LifeSpanCMR45.tsv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
age jomon_male_ndx jomon_female_ndx yayoi_male_ndx yayoi_female_ndx
age hunter_gatherer_male_ndx hunter_gatherer_female_ndx agricultural_male_ndx agricultural_female_ndx
0 25 25 25 25
1 2.611221016 2.595138407 2.562626297 2.573569918
2 1.916840495 1.911217517 1.911296549 1.911594501
2 changes: 1 addition & 1 deletion Data/Simulations/Sample/LifeSpanCMR48.tsv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
age jomon_male_ndx jomon_female_ndx yayoi_male_ndx yayoi_female_ndx
age hunter_gatherer_male_ndx hunter_gatherer_female_ndx agricultural_male_ndx agricultural_female_ndx
0 28 28 28 28
1 2.506849758 2.491395267 2.460002869 2.470659332
2 1.840221274 1.834818437 1.83476416 1.835158896
2 changes: 1 addition & 1 deletion Data/Simulations/Sample/LifeSpanCMR50.tsv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
age jomon_male_ndx jomon_female_ndx yayoi_male_ndx yayoi_female_ndx
age hunter_gatherer_male_ndx hunter_gatherer_female_ndx agricultural_male_ndx agricultural_female_ndx
0 30 30 30 30
1 2.437705134 2.422221692 2.391664237 2.402062336
2 1.78945613 1.783878323 1.783796459 1.784209906
102 changes: 102 additions & 0 deletions Data/Simulations/Sample/LifeSpanImazu2022.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
age hunter_gatherer_male_ndx hunter_gatherer_female_ndx agricultural_male_ndx agricultural_female_ndx
0 30 30 25 25
1 1.771 1.771 1.8975 1.8975
2 1.7466624 1.7466624 1.871424 1.871424
3 1.71524431 1.71524431 1.837761761 1.837761761
4 1.690421135 1.690421135 1.811165502 1.811165502
5 1.658916478 1.658916478 1.777410512 1.777410512
6 1.633712301 1.633712301 1.750406037 1.750406037
7 1.602212362 1.602212362 1.716656103 1.716656103
8 1.57672762 1.57672762 1.689351022 1.689351022
9 1.550979833 1.550979833 1.661764107 1.661764107
10 1.524999223 1.524999223 1.633927739 1.633927739
11 1.493462569 1.493462569 1.600138467 1.600138467
12 1.467405662 1.467405662 1.572220352 1.572220352
13 1.441195299 1.441195299 1.54413782 1.54413782
14 1.419772057 1.419772057 1.521184347 1.521184347
15 1.393052832 1.393052832 1.492556605 1.492556605
16 1.36626996 1.36626996 1.463860671 1.463860671
17 1.339449386 1.339449386 1.435124342 1.435124342
18 1.316977201 1.316977201 1.411047001 1.411047001
19 1.289891951 1.289891951 1.38202709 1.38202709
20 1.266950905 1.266950905 1.357447399 1.357447399
21 1.239722531 1.239722531 1.328274141 1.328274141
22 1.216441178 1.216441178 1.303329834 1.303329834
23 1.19291305 1.19291305 1.278121125 1.278121125
24 1.16917408 1.16917408 1.252686514 1.252686514
25 1.145259418 1.145259418 1.227063662 1.227063662
26 1.121203384 1.121203384 1.20128934 1.20128934
27 1.100304423 1.100304423 1.178897596 1.178897596
28 1.075844035 1.075844035 1.152690038 1.152690038
29 1.054394671 1.054394671 1.129708576 1.129708576
30 1.029680391 1.029680391 1.103228991 1.103228991
31 1.007836457 1.007836457 1.079824775 1.079824775
32 0.9857492243 0.9857492243 1.056159883 1.056159883
33 0.9634603391 0.9634603391 1.032278935 1.032278935
34 0.9435536647 0.9435536647 1.010950355 1.010950355
35 0.9207924036 0.9207924036 0.9865632896 0.9865632896
36 0.9003116296 0.9003116296 0.9646196031 0.9646196031
37 0.8795205587 0.8795205587 0.9423434558 0.9423434558
38 0.8584682821 0.8584682821 0.9197874451 0.9197874451
39 0.8372026719 0.8372026719 0.8970028627 0.8970028627
40 0.8157702835 0.8157702835 0.8740395894 0.8740395894
41 0.7961439756 0.7961439756 0.8530114025 0.8530114025
42 0.7761998943 0.7761998943 0.8316427439 0.8316427439
43 0.7577632988 0.7577632988 0.8118892487 0.8118892487
44 0.7371939202 0.7371939202 0.7898506288 0.7898506288
45 0.7180938189 0.7180938189 0.7693862345 0.7693862345
46 0.6986756219 0.6986756219 0.7485810234 0.7485810234
47 0.6804790723 0.6804790723 0.7290847203 0.7290847203
48 0.6618783248 0.6618783248 0.709155348 0.709155348
49 0.6429418307 0.6429418307 0.6888662472 0.6888662472
50 0.6250169353 0.6250169353 0.6696610021 0.6696610021
51 0.5920795674 0.5920795674 0.6343709651 0.6343709651
52 0.5737360652 0.5737360652 0.6147172128 0.6147172128
53 0.5563530215 0.5563530215 0.5960925231 0.5960925231
54 0.5387177324 0.5387177324 0.5771975705 0.5771975705
55 0.52089559 0.52089559 0.5581024178 0.5581024178
56 0.5038895892 0.5038895892 0.5398817027 0.5398817027
57 0.4866678464 0.4866678464 0.5214298354 0.5214298354
58 0.4701395564 0.4701395564 0.5037209533 0.5037209533
59 0.4533870291 0.4533870291 0.4857718168 0.4857718168
60 0.4372315232 0.4372315232 0.4684623463 0.4684623463
61 0.4215666557 0.4215666557 0.4516785597 0.4516785597
62 0.4056362299 0.4056362299 0.4346102463 0.4346102463
63 0.3901449027 0.3901449027 0.4180123958 0.4180123958
64 0.3744312101 0.3744312101 0.4011762965 0.4011762965
65 0.3596710384 0.3596710384 0.3853618269 0.3853618269
66 0.3446355883 0.3446355883 0.3692524161 0.3692524161
67 0.3298945658 0.3298945658 0.3534584633 0.3534584633
68 0.3154047788 0.3154047788 0.3379336916 0.3379336916
69 0.3007220449 0.3007220449 0.322202191 0.322202191
70 0.2867049467 0.2867049467 0.3071838715 0.3071838715
71 0.2728618191 0.2728618191 0.292351949 0.292351949
72 0.2591852806 0.2591852806 0.2776985149 0.2776985149
73 0.2459741716 0.2459741716 0.2635437553 0.2635437553
74 0.2325874383 0.2325874383 0.2492008267 0.2492008267
75 0.2196415337 0.2196415337 0.2353302147 0.2353302147
76 0.2068364323 0.2068364323 0.2216104631 0.2216104631
77 0.1942018643 0.1942018643 0.208073426 0.208073426
78 0.1819597288 0.1819597288 0.1949568523 0.1949568523
79 0.1697304101 0.1697304101 0.1818540108 0.1818540108
80 0.1577802281 0.1577802281 0.1690502444 0.1690502444
81 0.1461461046 0.1461461046 0.1565851121 0.1565851121
82 0.1346176886 0.1346176886 0.1442332378 0.1442332378
83 0.1233386512 0.1233386512 0.1321485548 0.1321485548
84 0.1122326762 0.1122326762 0.1202492959 0.1202492959
85 0.1013988072 0.1013988072 0.1086415791 0.1086415791
86 0.09079050542 0.09079050542 0.09727554152 0.09727554152
87 0.08038026482 0.08038026482 0.0861217123 0.0861217123
88 0.07075874712 0.07075874712 0.07581294334 0.07581294334
89 0.06228892509 0.06228892509 0.06673813402 0.06673813402
90 0.05483294076 0.05483294076 0.05874957938 0.05874957938
91 0.04826943775 0.04826943775 0.05171725473 0.05171725473
92 0.04249158605 0.04249158605 0.04552669934 0.04552669934
93 0.0374053432 0.0374053432 0.04007715343 0.04007715343
94 0.03292792362 0.03292792362 0.03527991816 0.03527991816
95 0.02898645116 0.02898645116 0.03105691196 0.03105691196
96 0.02551677296 0.02551677296 0.0273393996 0.0273393996
97 0.02246241523 0.02246241523 0.02406687346 0.02406687346
98 0.01977366413 0.01977366413 0.02118606871 0.02118606871
99 0.01740675653 0.01740675653 0.01865009629 0.01865009629
100 0.1280130976 0.1280130976 0.1371568902 0.1371568902
102 changes: 102 additions & 0 deletions Data/Simulations/Sample/Marriage.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
age hunter_gatherer agricultural
0 0 0
1 0 0
2 0 0
3 0 0
4 0 0
5 0 0
6 0 0
7 0 0
8 0 0
9 0 0
10 0 0
11 0 0
12 0 0
13 0 0
14 0.00002406070935 0.00002406070935
15 0.0000999193559 0.0000999193559
16 0.0001801960986 0.0001801960986
17 0.0002655688094 0.0002655688094
18 0.0003566819091 0.0003566819091
19 0.0004543116545 0.0004543116545
20 0.0005596983552 0.0005596983552
21 0.0006738154246 0.0006738154246
22 0.0007987892143 0.0007987892143
23 0.0009364230216 0.0009364230216
24 0.001090492486 0.001090492486
25 0.001264808867 0.001264808867
26 0.001466735971 0.001466735971
27 0.001708237472 0.001708237472
28 0.002009174274 0.002009174274
29 0.002415100186 0.002415100186
30 0.003069963989 0.003069963989
31 0.003501269398 0.003501269398
32 0.002900537962 0.002900537962
33 0.002311693015 0.002311693015
34 0.001925943523 0.001925943523
35 0.001634529996 0.001634529996
36 0.00139731704 0.00139731704
37 0.00119579845 0.00119579845
38 0.001020056776 0.001020056776
39 0.0008643135606 0.0008643135606
40 0.0007241054103 0.0007241054103
41 0.0005966677953 0.0005966677953
42 0.000479763441 0.000479763441
43 0.000372157336 0.000372157336
44 0.0002724644279 0.0002724644279
45 0.0001799391851 0.0001799391851
46 0.0000938971269 0.0000938971269
47 0.00001372884237 0.00001372884237
48 0 0
49 0 0
50 0 0
51 0 0
52 0 0
53 0 0
54 0 0
55 0 0
56 0 0
57 0 0
58 0 0
59 0 0
60 0 0
61 0 0
62 0 0
63 0 0
64 0 0
65 0 0
66 0 0
67 0 0
68 0 0
69 0 0
70 0 0
71 0 0
72 0 0
73 0 0
74 0 0
75 0 0
76 0 0
77 0 0
78 0 0
79 0 0
80 0 0
81 0 0
82 0 0
83 0 0
84 0 0
85 0 0
86 0 0
87 0 0
88 0 0
89 0 0
90 0 0
91 0 0
92 0 0
93 0 0
94 0 0
95 0 0
96 0 0
97 0 0
98 0 0
99 0 0
100 0 0
10 changes: 2 additions & 8 deletions Data/Simulations/Settings.tsv
Original file line number Diff line number Diff line change
@@ -19,29 +19,23 @@ female_marriageable_age_min 13 女性の最小婚姻可能年齢(歳)
male_marriageable_age_min 17 男性の最小婚姻可能年齢(歳)
female_marriageable_age_max 60 女性の最大婚姻可能年齢(歳)
male_marriageable_age_max 70 男性の最大婚姻可能年齢(歳)
marriageable_age_constant 8.5 婚姻可能年齢定数
marriageable_age_threshold 0.98 婚姻可能年齢閾値
marriageable_age_all_weight 101.8 婚姻可能年齢重み
#Childbirth -------------------- #出産 --------------------------------------------------
birth_interval 10 妊娠から出産までのStep間隔(step)
hunter_gatherer_stillbirth_rate 0 狩猟採集死産率 (no effect)
agricultural_stillbirth_rate 0 水田稲作死産率 (no effect)
child_agriculture_priority 0.5 片親が農耕文化を持ち、もう一方の片親が農耕文化を持たない時の農耕文化継承の優先度
childbearing_age_min 15 出産の最小可能年齢(歳)
childbearing_age_max 50 出産の最大可能年齢(歳)
childbearing_age_constant 8.5 出産可能年齢定数
childbearing_age_threshold 16 出産可能年齢閾値
childbearing_age_all_weight 101.8 出産可能年齢重み
#Movement -------------------- #移動 --------------------------------------------------
max_hunter_gatherer_settlement_population 25 狩猟採集集落の最大人数(人)
max_agricultural_settlement_population 80 水田稲作集落の最大人数(人)
min_move_distance 1 最小移動距離(cell)
max_move_distance 1600 最大移動距離(cell)
move_probability 0.01 移動確率
move_probability 0.0021 移動確率
ocean_cost 1.1 海上の通行コスト
coast_cost 0.7 海岸の通行コスト
land_cost 2.5 傾斜度0度の陸上の通行コスト
move_redo 10 移動再試行回数
move_method astar 移動の手法(astar/random)
move_astar_loop 8 A*を行うルート数
move_astar_distance 64 A*を行うルート間隔
move_astar_distance 128 A*を行うルート間隔
77 changes: 1 addition & 76 deletions Library/PAX_SAPIENTICA/Simulation/JapanProvinces.hpp
Original file line number Diff line number Diff line change
@@ -44,19 +44,6 @@ namespace paxs {
std::discrete_distribution<> dist{};
};

/// @brief
/// @brief 生命表
struct lifeSpan {
std::vector<double> weight_farming_female{}; // 農耕民の女性の重み
std::vector<double> weight_farming_male{}; // 農耕民の男性の重み
std::vector<double> weight_hunter_gatherer_female{}; // 狩猟採集民の女性の重み
std::vector<double> weight_hunter_gatherer_male{}; // 狩猟採集民の男性の重み
std::discrete_distribution<> dist_farming_female{};
std::discrete_distribution<> dist_farming_male{};
std::discrete_distribution<> dist_hunter_gatherer_female{};
std::discrete_distribution<> dist_hunter_gatherer_male{};
};

/// @brief A struct that represents a prefecture in Japan.
/// @brief 日本の地区を表す構造体
struct District {
@@ -117,6 +104,7 @@ namespace paxs {
sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("mtdna"))
) {
PAXS_WARNING("Failed to read Japan MtDNA_List TSV file: " + path + " at line " + std::to_string(i));
++i;
continue;
}
mtdna_list.emplace_back(sub_menu_v[menu[MurMur3::calcHash("mtdna")]]);
@@ -125,56 +113,6 @@ namespace paxs {

}

void inputLifeSpan(const std::string& japan_provinces_path) noexcept {

const std::string path = japan_provinces_path + "/LifeSpan.tsv";

paxs::InputFile life_span_tsv(AppConfig::getInstance()->getRootPath() + path);
if (life_span_tsv.fail()) {
PAXS_WARNING("Failed to read LifeSpan TSV file: " + path);
return;
}
// 1 行目を読み込む
if (!(life_span_tsv.getLine())) {
return; // 何もない場合
}
// BOM を削除
life_span_tsv.deleteBOM();
// 1 行目を分割する
std::unordered_map<std::uint_least32_t, std::size_t> menu = life_span_tsv.splitHashMapMurMur3('\t');
std::size_t i = 1;

// 1 行ずつ読み込み(区切りはタブ)
while (life_span_tsv.getLine()) {
std::vector<std::string> sub_menu_v = life_span_tsv.split('\t');
if (
sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("jomon_male_ndx")) ||
sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("jomon_female_ndx")) ||
sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("yayoi_male_ndx")) ||
sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("yayoi_female_ndx"))
) {
PAXS_WARNING("Failed to read Japan LifeSpan TSV file: " + path + " at line " + std::to_string(i));

life_span.weight_farming_female.emplace_back(0.0);
life_span.weight_farming_male.emplace_back(0.0);
life_span.weight_hunter_gatherer_female.emplace_back(0.0);
life_span.weight_hunter_gatherer_male.emplace_back(0.0);
continue;
}
life_span.weight_farming_female.emplace_back(std::stod(sub_menu_v[menu[MurMur3::calcHash("yayoi_female_ndx")]]));
life_span.weight_farming_male.emplace_back(std::stod(sub_menu_v[menu[MurMur3::calcHash("yayoi_male_ndx")]]));
life_span.weight_hunter_gatherer_female.emplace_back(std::stod(sub_menu_v[menu[MurMur3::calcHash("jomon_female_ndx")]]));
life_span.weight_hunter_gatherer_male.emplace_back(std::stod(sub_menu_v[menu[MurMur3::calcHash("jomon_male_ndx")]]));
;
// 確率分布を生成
life_span.dist_farming_female = std::discrete_distribution<>(life_span.weight_farming_female.begin(), life_span.weight_farming_female.end());
life_span.dist_farming_male = std::discrete_distribution<>(life_span.weight_farming_male.begin(), life_span.weight_farming_male.end());
life_span.dist_hunter_gatherer_female = std::discrete_distribution<>(life_span.weight_hunter_gatherer_female.begin(), life_span.weight_hunter_gatherer_female.end());
life_span.dist_hunter_gatherer_male = std::discrete_distribution<>(life_span.weight_hunter_gatherer_male.begin(), life_span.weight_hunter_gatherer_male.end());
++i;
}
}

void inputMtDNA_Region(const std::string& japan_provinces_path) noexcept {

const std::string path = japan_provinces_path + "/mtDNA.tsv";
@@ -314,8 +252,6 @@ namespace paxs {
inputMtDNA_List(japan_provinces_path);
inputMtDNA_Region(japan_provinces_path);
inputDistrict(japan_provinces_path);
inputLifeSpan(japan_provinces_path);

}

/// @brief
@@ -434,15 +370,6 @@ namespace paxs {
return mtdna_list.size();
}

AgeType getLifeSpan(const bool is_farming, const bool is_female, std::mt19937& gen) noexcept {
return static_cast<AgeType>((is_farming)?
((is_female) ? life_span.dist_farming_female(gen):
life_span.dist_farming_male(gen)) :
((is_female) ? life_span.dist_hunter_gatherer_female(gen) :
life_span.dist_hunter_gatherer_male(gen)));
}


/// @brief 日本の地区のIDから人口を取得する
/// @param id 日本の地区のID
/// @return 人口
@@ -487,8 +414,6 @@ namespace paxs {
std::unordered_map<std::uint_least32_t, mtDNA_Region> mtdna_region_list; // mtDNA 地方区分
//std::vector<std::uint_least32_t> mtdna_region_hash_list; // mtDNA ハッシュ計算用
std::vector<std::string> mtdna_list; // mtDNA
lifeSpan life_span;

};

}
72 changes: 1 addition & 71 deletions Library/PAX_SAPIENTICA/Simulation/KanakumaLifeSpan.hpp
Original file line number Diff line number Diff line change
@@ -26,83 +26,13 @@ namespace paxs {
/// @brief Lifespan data of the Kanakuma site.
/// @brief 金隈遺跡の寿命データ
class KanakumaLifeSpan {
private:

//// 金隈遺跡の寿命データ

//std::uniform_int_distribution<> life_person_num{ 1, 136 }; // 金隈遺跡の寿命データによる人間の数
//std::uniform_int_distribution<> life_child_num{ 1, 35 }; // 金隈遺跡の寿命データによる子供の数

//std::uniform_int_distribution<> life_male_adult_num{ 1, 38 }; // 金隈遺跡の寿命データによる男性の数
//std::uniform_int_distribution<> life_female_adult_num{ 1, 44 }; // 金隈遺跡の寿命データによる女性の数

//std::uniform_int_distribution<> life_infant_exp_dist{ 1, 1 * SimulationConstants::getInstance()->steps_per_year }; // 乳児の寿命の乱数分布
//std::uniform_int_distribution<> life_toddler_exp_dist{ 1 * SimulationConstants::getInstance()->steps_per_year + 1, 6 * SimulationConstants::getInstance()->steps_per_year }; // 幼児の寿命の乱数分布
//std::uniform_int_distribution<> life_child_exp_dist{ 6 * SimulationConstants::getInstance()->steps_per_year + 1, 12 * SimulationConstants::getInstance()->steps_per_year }; // 小児の寿命の乱数分布
//std::uniform_int_distribution<> life_young_exp_dist{ 12 * SimulationConstants::getInstance()->steps_per_year + 1, 20 * SimulationConstants::getInstance()->steps_per_year }; // 若年の寿命の乱数分布
//std::uniform_int_distribution<> life_adult_exp_dist{ 20 * SimulationConstants::getInstance()->steps_per_year + 1, 40 * SimulationConstants::getInstance()->steps_per_year }; // 成年の寿命の乱数分布
//std::uniform_int_distribution<> life_mature_exp_dist{ 40 * SimulationConstants::getInstance()->steps_per_year + 1, 60 * SimulationConstants::getInstance()->steps_per_year }; // 熟年の寿命の乱数分布
//std::uniform_int_distribution<> life_older_exp_dist{ 60 * SimulationConstants::getInstance()->steps_per_year + 1, 80 * SimulationConstants::getInstance()->steps_per_year }; // 老年の寿命の乱数分布

public:

paxs::JapanProvinces* japan_provinces;
KanakumaLifeSpan() = default;

///// @brief 英語未翻訳
///// @brief 寿命を決定する
//AgeType setAdultLifeSpan(const bool is_male_, std::mt19937& gen) {
// // もし大人だったら
// if (!is_male_) { // 女性の場合
// const AgeType adult_type = static_cast<AgeType>(life_male_adult_num(gen));

// if (adult_type <= 14) { // 成年
// return static_cast<AgeType>(life_adult_exp_dist(gen));
// }
// else if (adult_type <= (14 + 26)) { // 熟年
// return static_cast<AgeType>(life_mature_exp_dist(gen));
// }
// // 老年
// return static_cast<AgeType>(life_older_exp_dist(gen));
// }
// else { // 男性の場合
// const AgeType adult_type = static_cast<AgeType>(life_female_adult_num(gen));

// if (adult_type <= 19) { // 成年
// return static_cast<AgeType>(life_adult_exp_dist(gen));
// }
// else if (adult_type <= (19 + 19)) { // 熟年
// return static_cast<AgeType>(life_mature_exp_dist(gen));
// }
// }
// // 老年
// return static_cast<AgeType>(life_older_exp_dist(gen));
//}

/// @brief 英語未翻訳
/// @brief 寿命を決定する
AgeType setLifeSpan(const bool is_farming_, const bool is_male_, std::mt19937& gen) {

return static_cast<AgeType>(japan_provinces->getLifeSpan(is_farming_, !is_male_, gen) * SimulationConstants::getInstance()->steps_per_year
return static_cast<AgeType>(SimulationConstants::getInstance()->getLifeSpan(is_farming_, !is_male_, gen) * SimulationConstants::getInstance()->steps_per_year
+ SimulationConstants::getInstance()->step_per_year_dist(gen));

//if (life_person_num(gen) <= 37) { // もし子供だったら
// const AgeType child_type = static_cast<AgeType>(life_child_num(gen));

// if (child_type <= 6) { // 乳児
// return static_cast<AgeType>(life_infant_exp_dist(gen));
// }
// else if (child_type <= (6 + 22)) { // 幼児
// return static_cast<AgeType>(life_toddler_exp_dist(gen));
// }
// else if (child_type <= (6 + 22 + 5)) { // 小児
// return static_cast<AgeType>(life_child_exp_dist(gen));
// }
// // 若年
// return static_cast<AgeType>(life_young_exp_dist(gen));
//}
//// もし大人だったら
//return setAdultLifeSpan(is_male_, gen);
}

};
30 changes: 10 additions & 20 deletions Library/PAX_SAPIENTICA/Simulation/Settlement.hpp
Original file line number Diff line number Diff line change
@@ -413,7 +413,7 @@ namespace paxs {
// 妊娠していたら婚姻しない(婚姻可能と定義すると再婚者のデータで上書きされ子供への継承が不自然になる)
if (female.getBirthIntervalCount() > 0) continue;
// 婚姻するか乱数で決定
if (!isMarried(female.getAge())) continue;
if (!isMarried(female.getAgeSizeT(), female.cgetFarming() > 0)) continue;

// 集落グリッドを重み付け
if (close_settlements_list_probabilities.size() == 0) {
@@ -842,7 +842,7 @@ namespace paxs {
}
}
// 出産可能かどうか
else if (agent.isAbleToGiveBirth() && isAbleToGiveBirth(agent.getAge())) {
else if (agent.isAbleToGiveBirth() && isAbleToGiveBirth(agent.getAgeSizeT(), agent.cgetFarming() > 0)) {
agent.setBirthIntervalCount(SimulationConstants::getInstance()->birth_interval);
}

@@ -895,30 +895,20 @@ namespace paxs {

/// @brief Is the agent married?
/// @brief 確率で結婚するかどうかを返す
bool isMarried(const double age) noexcept {
bool isMarried(const std::size_t age, const bool is_agricultural) noexcept {
// 婚姻可能年齢の上限値以上だったら結婚しない
if (age >= SimulationConstants::getInstance()->female_marriageable_age_max) return false;
auto x = [](double age) { return (age - SimulationConstants::getInstance()->female_marriageable_age_min_f64) / SimulationConstants::getInstance()->marriageable_age_constant; };
auto weight = [=](double age) {
return std::exp(-std::pow(std::log(x(age)), 2.0) / settlement::sigma_p_2_x_2) / (x(age) * settlement::sigma_x_sqrt_2_x_pi);
};

const double threshold = static_cast<double>(weight(age)) * (SimulationConstants::getInstance()->marriageable_age_threshold / SimulationConstants::getInstance()->marriageable_age_all_weight); // (0.98 / 101.8);

const double threshold = SimulationConstants::getInstance()->getMarriageProbability(age, is_agricultural);
if (threshold == 0.0) return false;
if (threshold >= 1.0) return true;
return SimulationConstants::getInstance()->random_dist(*gen) < threshold;
}

/// @brief Is able to give birth?
/// @brief 確率で出産するかどうかを返す
bool isAbleToGiveBirth(const double age) noexcept {
//return true;// 毎回出産
auto x = [](double age) { return (age - SimulationConstants::getInstance()->pregnant_age_min_f64) / SimulationConstants::getInstance()->childbearing_age_constant; };
auto weight = [=](double age) {
return std::exp(-std::pow(std::log(x(age)), 2.0) / settlement::sigma_p_2_x_2) / (x(age) * settlement::sigma_x_sqrt_2_x_pi);
};

const double threshold = static_cast<double>(weight(age)) * (SimulationConstants::getInstance()->childbearing_age_threshold / SimulationConstants::getInstance()->childbearing_age_all_weight); // (16 / 101.8);

bool isAbleToGiveBirth(const std::size_t age, const bool is_agricultural) noexcept {
const double threshold = SimulationConstants::getInstance()->getChildbearingProbability(age, is_agricultural);
if (threshold == 0.0) return false;
if (threshold >= 1.0) return true;
return SimulationConstants::getInstance()->random_dist(*gen) < threshold;
}
};
1 change: 1 addition & 0 deletions Library/PAX_SAPIENTICA/Simulation/SettlementAgent.hpp
Original file line number Diff line number Diff line change
@@ -59,6 +59,7 @@ namespace paxs {
/// @brief Get the agent's age.
/// @brief エージェントの年齢を取得する
float getAge() const noexcept { return age / static_cast<float>(SimulationConstants::getInstance()->steps_per_year); }
std::size_t getAgeSizeT() const noexcept { return static_cast<std::size_t>(age) / static_cast<std::size_t>(SimulationConstants::getInstance()->steps_per_year); }

constexpr AgeType getAgeInt() const noexcept { return age; }

6 changes: 3 additions & 3 deletions Library/PAX_SAPIENTICA/Simulation/SettlementGrid.hpp
Original file line number Diff line number Diff line change
@@ -177,10 +177,10 @@ namespace paxs {
/// @brief 集落を分割する
void divideSettlements() noexcept {
// 人口が最大人口を超えている集落を複数探し、分割する
for (auto& settlement : settlements) {
if (settlement.getPopulationWeight() >= 1.0) {
for (std::size_t i = 0; i < settlements.size(); ++i) {
if (settlements[i].getPopulationWeight() >= 1.0) {
// 分割
Settlement divided_settlement = settlement.divide();
Settlement divided_settlement = settlements[i].divide();
moveSettlementToThis(divided_settlement);
}
}
2 changes: 0 additions & 2 deletions Library/PAX_SAPIENTICA/Simulation/SettlementSimulator.hpp
Original file line number Diff line number Diff line change
@@ -56,7 +56,6 @@ namespace paxs {
explicit SettlementSimulator(const std::string& map_list_path, const std::string& japan_provinces_path, const unsigned seed = 0) noexcept :
environment(std::make_unique<Environment>(map_list_path)), gen(seed) {
japan_provinces = std::make_unique<paxs::JapanProvinces>(japan_provinces_path);
kanakuma_life_span.japan_provinces = japan_provinces.get();
}
/// @brief 環境を設定
void setEnvironment(const std::string& map_list_path, const std::string& japan_provinces_path, /*const int z,*/ const unsigned seed = 0) noexcept {
@@ -67,7 +66,6 @@ namespace paxs {

japan_provinces.reset();
japan_provinces = std::make_unique<paxs::JapanProvinces>(japan_provinces_path);
kanakuma_life_span.japan_provinces = japan_provinces.get();
}

/// @brief
207 changes: 191 additions & 16 deletions Library/PAX_SAPIENTICA/Simulation/SimulationConst.hpp
Original file line number Diff line number Diff line change
@@ -69,6 +69,33 @@ namespace paxs {
std::uint_least8_t farming{};
};

/// @brief
/// @brief 生命表
struct lifeSpan {
std::vector<double> weight_farming_female{}; // 農耕民の女性の重み
std::vector<double> weight_farming_male{}; // 農耕民の男性の重み
std::vector<double> weight_hunter_gatherer_female{}; // 狩猟採集民の女性の重み
std::vector<double> weight_hunter_gatherer_male{}; // 狩猟採集民の男性の重み
std::discrete_distribution<> dist_farming_female{};
std::discrete_distribution<> dist_farming_male{};
std::discrete_distribution<> dist_hunter_gatherer_female{};
std::discrete_distribution<> dist_hunter_gatherer_male{};
};

/// @brief
/// @brief 妊娠確率
struct MarriageProbability {
std::vector<double> agricultural{}; // 農耕民の確率
std::vector<double> hunter_gatherer{}; // 狩猟採集民の確率
};
/// @brief
/// @brief 出産確率
struct ChildbearingProbability {
std::vector<double> agricultural{}; // 農耕民の確率
std::vector<double> hunter_gatherer{}; // 狩猟採集民の確率
};


struct SimulationConstants {
// インスタンスを取得
static SimulationConstants* getInstance() {
@@ -107,14 +134,6 @@ namespace paxs {
std::uint_least8_t birth_interval = 10;
// 妊娠可能
double pregnant_age_min_f64 = childbearing_age_min_f64 - static_cast<double>(birth_interval) / static_cast<double>(steps_per_year);
// 婚姻可能年齢定数
double marriageable_age_constant = 8.5;
double marriageable_age_threshold = 0.98;
double marriageable_age_all_weight = 101.8;
// 出産可能年齢定数
double childbearing_age_constant = 8.5;
double childbearing_age_threshold = 16.0;
double childbearing_age_all_weight = 101.8;

// 結婚時に近くの集落からエージェントを探す際の探索範囲
std::uint_least32_t marriage_search_range = 60;
@@ -134,7 +153,7 @@ namespace paxs {
std::uint_least8_t immigration_district_id = 73;

// 青銅量
std::uint_least64_t bronze = 100; // 渡来人が持ってくる青銅量
std::uint_least64_t bronze = 500; // 渡来人が持ってくる青銅量

// 初期化時の寿命までの最低年数
AgeType init_lifespan_grace_period = 180;
@@ -174,6 +193,10 @@ namespace paxs {
double ocean_cost = 0.5; // 海上の通行コスト
double land_cost = 0.5; // 傾斜度0度の陸上の通行コスト

lifeSpan life_span;
MarriageProbability marriage_probability;
ChildbearingProbability childbearing_probability;

std::uniform_real_distribution<float> random_dist_f32 = std::uniform_real_distribution<float>( 0.0f, 1.0f ); // 乱数分布
std::uniform_real_distribution<double> random_dist = std::uniform_real_distribution<double>( 0.0, 1.0 ); // 乱数分布
std::uniform_int_distribution<int> step_per_year_dist = std::uniform_int_distribution<int>( 0, 11 ); // 乱数分布
@@ -212,6 +235,161 @@ namespace paxs {
return sr.getZ(area);
}

// 項目の ID を返す
std::size_t getMenuIndex(const std::unordered_map<std::uint_least32_t, std::size_t>& menu, const std::uint_least32_t& str_) const {
return (menu.find(str_) != menu.end()) ? menu.at(str_) : SIZE_MAX;
}

AgeType getLifeSpan(const bool is_farming, const bool is_female, std::mt19937& gen) noexcept {
return static_cast<AgeType>((is_farming) ?
((is_female) ? life_span.dist_farming_female(gen) :
life_span.dist_farming_male(gen)) :
((is_female) ? life_span.dist_hunter_gatherer_female(gen) :
life_span.dist_hunter_gatherer_male(gen)));
}

void inputLifeSpan() noexcept {
std::string path = "";
AppConfig::getInstance()->calcDataSettings(MurMur3::calcHash("SimulationProvincesPath"),
[&](const std::string& path_) {path = path_; });
path += "/LifeSpan.tsv";

paxs::InputFile life_span_tsv(path);
if (life_span_tsv.fail()) {
PAXS_WARNING("Failed to read LifeSpan TSV file: " + path);
return;
}
// 1 行目を読み込む
if (!(life_span_tsv.getLine())) {
return; // 何もない場合
}
// BOM を削除
life_span_tsv.deleteBOM();
// 1 行目を分割する
std::unordered_map<std::uint_least32_t, std::size_t> menu = life_span_tsv.splitHashMapMurMur3('\t');
std::size_t i = 1;
// 1 行ずつ読み込み(区切りはタブ)
while (life_span_tsv.getLine()) {
std::vector<std::string> sub_menu_v = life_span_tsv.split('\t');
if (
sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("hunter_gatherer_male_ndx")) ||
sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("hunter_gatherer_female_ndx")) ||
sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("agricultural_male_ndx")) ||
sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("agricultural_female_ndx"))
) {
PAXS_WARNING("Failed to read Japan LifeSpan TSV file: " + path + " at line " + std::to_string(i));

life_span.weight_farming_female.emplace_back(0.0);
life_span.weight_farming_male.emplace_back(0.0);
life_span.weight_hunter_gatherer_female.emplace_back(0.0);
life_span.weight_hunter_gatherer_male.emplace_back(0.0);
++i;
continue;
}
life_span.weight_farming_female.emplace_back(std::stod(sub_menu_v[menu[MurMur3::calcHash("agricultural_female_ndx")]]));
life_span.weight_farming_male.emplace_back(std::stod(sub_menu_v[menu[MurMur3::calcHash("agricultural_male_ndx")]]));
life_span.weight_hunter_gatherer_female.emplace_back(std::stod(sub_menu_v[menu[MurMur3::calcHash("hunter_gatherer_female_ndx")]]));
life_span.weight_hunter_gatherer_male.emplace_back(std::stod(sub_menu_v[menu[MurMur3::calcHash("hunter_gatherer_male_ndx")]]));
// 確率分布を生成
life_span.dist_farming_female = std::discrete_distribution<>(life_span.weight_farming_female.begin(), life_span.weight_farming_female.end());
life_span.dist_farming_male = std::discrete_distribution<>(life_span.weight_farming_male.begin(), life_span.weight_farming_male.end());
life_span.dist_hunter_gatherer_female = std::discrete_distribution<>(life_span.weight_hunter_gatherer_female.begin(), life_span.weight_hunter_gatherer_female.end());
life_span.dist_hunter_gatherer_male = std::discrete_distribution<>(life_span.weight_hunter_gatherer_male.begin(), life_span.weight_hunter_gatherer_male.end());
++i;
}
}

// 婚姻確率を返す
double getMarriageProbability(const std::size_t age_, const bool is_agricultural) const noexcept {
return (is_agricultural) ? marriage_probability.agricultural[age_] :
marriage_probability.hunter_gatherer[age_];
}
// 出産確率を返す
double getChildbearingProbability(const std::size_t age_, const bool is_agricultural) const noexcept {
return (is_agricultural) ? childbearing_probability.agricultural[age_] :
childbearing_probability.hunter_gatherer[age_];
}

void inputMarriage() noexcept {
std::string path = "";
AppConfig::getInstance()->calcDataSettings(MurMur3::calcHash("SimulationProvincesPath"),
[&](const std::string& path_) {path = path_; });
path += "/Marriage.tsv";
paxs::InputFile probability_tsv(path);
if (probability_tsv.fail()) {
PAXS_WARNING("Failed to read Marriage TSV file: " + path);
return;
}
// 1 行目を読み込む
if (!(probability_tsv.getLine())) {
return; // 何もない場合
}
// BOM を削除
probability_tsv.deleteBOM();
// 1 行目を分割する
std::unordered_map<std::uint_least32_t, std::size_t> menu = probability_tsv.splitHashMapMurMur3('\t');
marriage_probability.agricultural.clear();
marriage_probability.hunter_gatherer.clear();
std::size_t i = 1;
// 1 行ずつ読み込み(区切りはタブ)
while (probability_tsv.getLine()) {
std::vector<std::string> sub_menu_v = probability_tsv.split('\t');
if (
sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("hunter_gatherer")) ||
sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("agricultural"))
) {
PAXS_WARNING("Failed to read Japan Marriage TSV file: " + path + " at line " + std::to_string(i));
marriage_probability.agricultural.emplace_back(0.0);
marriage_probability.hunter_gatherer.emplace_back(0.0);
++i;
continue;
}
marriage_probability.agricultural.emplace_back(std::stod(sub_menu_v[menu[MurMur3::calcHash("agricultural")]]));
marriage_probability.hunter_gatherer.emplace_back(std::stod(sub_menu_v[menu[MurMur3::calcHash("hunter_gatherer")]]));
++i;
}
}
void inputChildbearing() noexcept {
std::string path = "";
AppConfig::getInstance()->calcDataSettings(MurMur3::calcHash("SimulationProvincesPath"),
[&](const std::string& path_) {path = path_; });
if (path.size() == 0) return;
path += "/Childbearing.tsv";
paxs::InputFile probability_tsv(path);
if (probability_tsv.fail()) {
PAXS_WARNING("Failed to read Childbearing TSV file: " + path);
return;
}
// 1 行目を読み込む
if (!(probability_tsv.getLine())) {
return; // 何もない場合
}
// BOM を削除
probability_tsv.deleteBOM();
// 1 行目を分割する
std::unordered_map<std::uint_least32_t, std::size_t> menu = probability_tsv.splitHashMapMurMur3('\t');
childbearing_probability.agricultural.clear();
childbearing_probability.hunter_gatherer.clear();
std::size_t i = 1;
// 1 行ずつ読み込み(区切りはタブ)
while (probability_tsv.getLine()) {
std::vector<std::string> sub_menu_v = probability_tsv.split('\t');
if (
sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("hunter_gatherer")) ||
sub_menu_v.size() <= getMenuIndex(menu, MurMur3::calcHash("agricultural"))
) {
PAXS_WARNING("Failed to read Japan Childbearing TSV file: " + path + " at line " + std::to_string(i));
childbearing_probability.agricultural.emplace_back(0.0);
childbearing_probability.hunter_gatherer.emplace_back(0.0);
++i;
continue;
}
childbearing_probability.agricultural.emplace_back(std::stod(sub_menu_v[menu[MurMur3::calcHash("agricultural")]]));
childbearing_probability.hunter_gatherer.emplace_back(std::stod(sub_menu_v[menu[MurMur3::calcHash("hunter_gatherer")]]));
++i;
}
}

void init() {
std::string str = "";
AppConfig::getInstance()->calcDataSettings(MurMur3::calcHash("SimulationConstants"),
@@ -249,13 +427,6 @@ namespace paxs {
stoiFunc(kvt, MurMur3::calcHash("marriage_search_range"), [&](const std::string& str_) {marriage_search_range = static_cast<std::uint_least32_t>(std::stoul(str_)); });
marriage_search_range_pow2 = marriage_search_range * marriage_search_range;

stoiFunc(kvt, MurMur3::calcHash("marriageable_age_constant"), [&](const std::string& str_) {marriageable_age_constant = std::stod(str_); });
stoiFunc(kvt, MurMur3::calcHash("marriageable_age_threshold"), [&](const std::string& str_) {marriageable_age_threshold = std::stod(str_); });
stoiFunc(kvt, MurMur3::calcHash("marriageable_age_all_weight"), [&](const std::string& str_) {marriageable_age_all_weight = std::stod(str_); });
stoiFunc(kvt, MurMur3::calcHash("childbearing_age_constant"), [&](const std::string& str_) {childbearing_age_constant = std::stod(str_); });
stoiFunc(kvt, MurMur3::calcHash("childbearing_age_threshold"), [&](const std::string& str_) {childbearing_age_threshold = std::stod(str_); });
stoiFunc(kvt, MurMur3::calcHash("childbearing_age_all_weight"), [&](const std::string& str_) {childbearing_age_all_weight = std::stod(str_); });

stoiFunc(kvt, MurMur3::calcHash("cell_group_length"), [&](const std::string& str_) {cell_group_length = static_cast<std::uint_least32_t>(std::stoul(str_)); });
stoiFunc(kvt, MurMur3::calcHash("immigration_start_steps"), [&](const std::string& str_) {immigration_start_steps = static_cast<std::uint_least64_t>(std::stoul(str_)); });
stoiFunc(kvt, MurMur3::calcHash("immigration_end_steps"), [&](const std::string& str_) {immigration_end_steps = static_cast<std::uint_least64_t>(std::stoul(str_)); });
@@ -282,6 +453,10 @@ namespace paxs {
stoiFunc(kvt, MurMur3::calcHash("land_cost"), [&](const std::string& str_) {land_cost = std::stod(str_); });

pregnant_age_min_f64 = childbearing_age_min_f64 - static_cast<double>(birth_interval) / static_cast<double>(steps_per_year);

inputMarriage(); // 婚姻確率
inputChildbearing(); // 出産確率
inputLifeSpan(); // 生命表
}

SimulationConstants() {
12 changes: 9 additions & 3 deletions Library/PAX_SAPIENTICA/Version.hpp
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@

// 正式バージョン数値
#ifndef PAX_SAPIENTICA_LIBRARY_VERSION
#define PAX_SAPIENTICA_LIBRARY_VERSION (20241030L)
#define PAX_SAPIENTICA_LIBRARY_VERSION (20241214L)
#endif

// PAX_SAPIENTICA 主要バージョン ( 0 がテスト版 , 1 から正式リリース版 ) [vX.a.a]
@@ -34,12 +34,12 @@

// PAX_SAPIENTICA 微修正バージョン [v6.0.a.a.X]
#ifndef PAX_SAPIENTICA_LIBRARY_PATCHLEVEL
#define PAX_SAPIENTICA_LIBRARY_PATCHLEVEL (10)
#define PAX_SAPIENTICA_LIBRARY_PATCHLEVEL (16)
#endif

// バージョン名 ( https://github.com/AsPJT/PAX_SAPIENTICA )
#ifndef PAX_SAPIENTICA_LIBRARY_VERSION_NAME
#define PAX_SAPIENTICA_LIBRARY_VERSION_NAME "1.2.10 KIZAMIME TOTTAIMON"
#define PAX_SAPIENTICA_LIBRARY_VERSION_NAME "1.2.16 KIZAMIME TOTTAIMON"
#endif

//char name[
@@ -51,6 +51,12 @@
更新履歴
バージョン | 更新日 | 概要
6.0.1.0.X | 2024/0X/XX | コピペ用
6.0.1.2.16 | 2024/12/14 | 生活に関するTSVデータ入力の追加 #116
6.0.1.2.15 | 2024/12/13 | 婚姻処理の高速化 #115
6.0.1.2.14 | 2024/12/10 | シミュレーションの人間・設定初期化ボタン・青銅器変数を追加 #114
6.0.1.2.13 | 2024/11/30 | 婚姻処理の高速化 #113
6.0.1.2.12 | 2024/11/17 | シミュレーションに婚姻移動表示とグリッド表示機能を追加 #112
6.0.1.2.11 | 2024/10/31 | シミュレーション変数の命名変更 #111
6.0.1.2.10 | 2024/10/30 | シミュレーション結果の追加 #110
6.0.1.2.9 | 2024/10/26 | 渡来人及び地区別の初期時の狩猟採集、農耕、SNP、言語の値の指定機能 #109
6.0.1.2.8 | 2024/10/26 | シミュレーションの選択項目のテキストを表示 #109

0 comments on commit 3b90696

Please sign in to comment.