Symphony Of Empires
emigration.cpp
Go to the documentation of this file.
1 // Symphony of Empires
2 // Copyright (C) 2021, Symphony of Empires contributors
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <https://www.gnu.org/licenses/>.
16 //
17 // ----------------------------------------------------------------------------
18 // Name:
19 // server/emigration.cpp
20 //
21 // Abstract:
22 // Does some important stuff.
23 // ----------------------------------------------------------------------------
24 
25 #include <algorithm>
26 #include <cstdio>
27 #include <cstddef>
28 #include <tbb/blocked_range.h>
29 #include <tbb/concurrent_vector.h>
30 #include <tbb/parallel_for.h>
31 #include <tbb/combinable.h>
32 
33 #include "eng3d/disc_dist.hpp"
34 
35 #include "server/emigration.hpp"
36 #include "world.hpp"
37 
38 // "Fuzzers" for emigration chances
39 static DiscreteDistribution<float> rng_multipliers({ 0.01f, 0.05f, 0.25f, 0.3f, 0.5f, 1.f }, { 1.f, 0.5f, 0.25f, 0.01f, 0.2f, 0.1f });
40 
41 static inline void conlonial_migration(World& world);
42 static inline void internal_migration(World& world);
43 static inline void external_migration(World& world);
44 
45 void do_emigration(World& world) {
46  external_migration(world);
47  internal_migration(world);
48  conlonial_migration(world);
49 }
50 
51 static inline void conlonial_migration(World&) {
52 
53 }
54 
55 static inline void internal_migration(World&) {
56 
57 }
58 
59 // Basic
60 static inline float nation_attraction(Nation& nation, Language& language) {
61  const auto attraction = nation.language_acceptance[language];
62  return attraction + rng_multipliers.get_item();
63 }
64 
65 static inline float province_attraction(const Province& province) {
66  auto rand_attractive = province.base_attractive + rng_multipliers.get_item();
67  rand_attractive /= g_world.terrain_types[province.terrain_type_id].penalty;
68  rand_attractive *= 1.f - province.average_militancy(); // from 0 to 1
69  return rand_attractive;
70 }
71 
72 static inline void external_migration(World& world) {
73  std::vector<DiscreteDistribution<Province*>> province_distributions;
74  province_distributions.reserve(world.provinces.size());
75  for(auto& nation : world.nations) {
76  std::vector<float> attractions;
77  std::vector<Province*> viable_provinces;
78  for(const auto province_id : nation.controlled_provinces) {
79  auto& province = world.provinces[province_id];
80  if(world.terrain_types[province.terrain_type_id].is_water_body)
81  continue;
82  auto attraction = province_attraction(province);
83  if(attraction <= 0.f)
84  continue;
85  attractions.push_back(attraction);
86  viable_provinces.push_back(&province);
87  }
88  if(viable_provinces.empty()) {
89  attractions.push_back(1.f);
90  viable_provinces.push_back(nullptr);
91  }
92  province_distributions.emplace_back(viable_provinces, attractions);
93  }
94  assert(!province_distributions.empty());
95 
96  std::vector<DiscreteDistribution<Nation*>> nation_distributions;
97  nation_distributions.reserve(world.nations.size());
98  for(auto& language : world.languages) {
99  std::vector<float> attractions;
100  std::vector<Nation*> viable_nations;
101  for(auto& nation : world.nations) {
102  auto attraction = nation_attraction(nation, language);
103  if(attraction <= 0.f)
104  continue;
105  attractions.push_back(attraction);
106  viable_nations.push_back(&nation);
107  }
108  if(viable_nations.empty()) {
109  attractions.push_back(1.f);
110  viable_nations.push_back(nullptr);
111  }
112  nation_distributions.emplace_back(viable_nations, attractions);
113  }
114  assert(!nation_distributions.empty());
115 
116  struct EmigrationData {
117  ProvinceId origin_id;
118  ProvinceId target_id;
119  float size = 0.f;
120  Pop emigred;
121  };
123  tbb::parallel_for(tbb::blocked_range(world.provinces.begin(), world.provinces.end()), [&emigration, &nation_distributions, &province_distributions, &world](const auto& provinces_range) {
124  for(auto& province : provinces_range) {
125  if(world.terrain_types[province.terrain_type_id].is_water_body)
126  continue;
127 
128  const auto language_id = std::distance(province.languages.begin(), std::max_element(province.languages.begin(), province.languages.end()));
129  // Randomness factor to emulate a pseudo-imperfect economy
130  for(auto& pop : province.pops) {
131  if(pop.size <= 1.f)
132  continue;
133 
134  // Depending on how much not our life needs are being met is how many we
135  // want to get out of here
136  // And literacy determines "best" spot, for example a low literacy will
137  // choose a slightly less desirable location
138  const auto emigration_desire = glm::max(pop.militancy * -pop.life_needs_met, 1.f);
139  auto emigrants = glm::min(pop.size * emigration_desire * rng_multipliers.get_item(), pop.size);
140  if(emigrants > 0.f) {
141  auto& nation_distribution = nation_distributions[language_id];
142  const auto* random_nation = nation_distribution.get_item();
143  if(random_nation == nullptr)
144  continue;
145 
146  auto& province_distribution = province_distributions[random_nation->get_id()];
147  auto* choosen_province = province_distribution.get_item();
148  if(choosen_province == nullptr || world.terrain_types[choosen_province->terrain_type_id].is_water_body)
149  continue;
150 
151  emigration.local().push_back(EmigrationData{
152  province.get_id(),
153  choosen_province->get_id(),
154  emigrants,
155  pop
156  });
157 
158  pop.size -= emigrants;
159  assert(!(pop.size < 0.f));
160  }
161  }
162  }
163  });
164 
165  // Emigrate pop to another province
166  emigration.combine_each([&world](const auto& list) {
167  for(const auto& e : list) {
168  auto& target = world.provinces[e.target_id];
169  const auto it = std::find(target.pops.begin(), target.pops.end(), e.emigred);
170  assert(it != target.pops.end());
171  it->size += e.size;
172  //it->budget += e.emigred.budget;
173  }
174  });
175 }
Uses the Aiias method to generate a lookup table of with different probabilties.
Definition: disc_dist.hpp:39
std::vector< ProvinceId > controlled_provinces
Definition: nation.hpp:149
std::vector< float > language_acceptance
Definition: nation.hpp:145
Definition: indpobj.hpp:264
A single province, which is used to simulate economy in a "bulk-tiles" way instead of doing economica...
Definition: province.hpp:48
float base_attractive
Definition: province.hpp:99
float average_militancy() const
Definition: province.hpp:58
TerrainTypeId terrain_type_id
Definition: province.hpp:105
Definition: world.hpp:114
void do_emigration(World &world)
Definition: emigration.cpp:45
void parallel_for(T range, F &&func)
void combine_each(F &&func)
Definition: combinable.hpp:43
World g_world
Definition: world.cpp:59