Symphony Of Empires
ai.hpp
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/ai.hpp
20 //
21 // Abstract:
22 // Does some important stuff.
23 // ----------------------------------------------------------------------------
24 
25 #pragma once
26 
27 #include <vector>
28 #include "eng3d/entity.hpp"
29 #include "objects.hpp"
30 #include "nation.hpp"
31 #include "province.hpp"
32 #include "world.hpp"
33 
34 extern std::vector<ProvinceId> g_water_provinces;
35 
36 struct AIManager {
37  float war_weight = 1.f; // Base weight for war
38  float unit_battle_weight = 1.f; // Attraction of units into entering on pre-existing battles
39  float unit_exist_weight = 1.f; // Weight of an unit by just existing
40  float coastal_weight = 1.f; // Importance given to coastal provinces
41  float reconquer_weight = 1.f; // Weight to reconquer lost **home**lan
42  float erratic = 1.f; // How erratic we behave when doing military strategies
43  float strength_threshold = 0.5f; // How much our enemy has to be in terms of strength
44  // for us to revaluate our diplomatic stances
45  float override_threshold = 1.f; // Threshold for overriding orders of units
46  float conqueror_weight = 1.f; // How much this nation is going to conquer others for no reason
47  std::vector<float> nations_risk_factor;
48  std::vector<float> potential_risk;
49  std::vector<ProvinceId> eval_provinces;
51  size_t gains = 0;
52  size_t losses = 0;
53  float military_strength = 0.f;
54 
56  potential_risk.resize(g_world.provinces.size(), 1.f);
57  }
58 
59  float get_rand() const {
60  return glm::max<float>(rand() % 100, 1.f) / 100.f;
61  }
62 
64  void recalc_weights() {
65  war_weight = 1.f + 1.f * this->get_rand();
66  unit_battle_weight = 1.f + 1.f * this->get_rand();
67  unit_exist_weight = 1.f + 1.f * this->get_rand();
68  coastal_weight = 1.f + 1.f * this->get_rand();
69  reconquer_weight = 1.f + 1.f * this->get_rand();
70  erratic = 1.f + 1.f * this->get_rand();
71  strength_threshold = 1.f * this->get_rand();
72  override_threshold = 1.f * this->get_rand();
73  conqueror_weight = 1.f * this->get_rand();
74  }
75 
77  void calc_weights(const Nation& nation) {
78  auto new_controlled_cnt = nation.controlled_provinces.size();
79  if(last_constrolled_cnt < new_controlled_cnt)
80  gains += new_controlled_cnt - last_constrolled_cnt;
81  else if(last_constrolled_cnt > new_controlled_cnt)
82  losses += last_constrolled_cnt - new_controlled_cnt;
83 
84  if(losses >= gains) {
85  losses -= gains;
86  gains = 0;
88  }
89  last_constrolled_cnt = new_controlled_cnt;
90  }
91 
92  void collect_eval_provinces(const World& world, const Nation& nation) {
93  // Provinces that can be evaluated in our AI
95  eval_provinces.reserve(world.provinces.size());
96  eval_provinces.insert(eval_provinces.end(), nation.controlled_provinces.begin(), nation.controlled_provinces.end()); // Add our own nation's province ids
97  for(const auto& other : world.nations) {
98  if(&other != &nation) {
99  const auto& relation = world.get_relation(nation, other);
100  if(relation.has_landpass())
101  eval_provinces.insert(eval_provinces.end(), other.controlled_provinces.begin(), other.controlled_provinces.end());
102  }
103  }
104  }
105 
106  float get_nation_risk(const World& world, const Nation& nation, const Nation& other) {
107  const auto& relation = world.get_relation(nation, other);
108  float factor = relation.has_war ? war_weight : -relation.relation;
109  return factor;
110  }
111 
112  void calc_nation_risk(const World& world, const Nation& nation) {
113  nations_risk_factor.resize(world.nations.size());
114  for(const auto& other : world.nations)
115  if(&other != &nation)
116  nations_risk_factor[other] = get_nation_risk(world, nation, other);
117  nations_risk_factor[nation] = -1.f;
118  }
119 
120  void calc_province_risk(const World& world, const Nation& nation) {
121  std::fill(potential_risk.begin(), potential_risk.end(), 1.f);
122  // Calculate potential risk for every province
123  for(const auto province_id : eval_provinces) {
124  const auto& province = world.provinces[province_id];
125  auto draw_in_force = 1.f;
126  // The "cooling" value which basically makes us ignore some provinces with lots of defenses
127  // so we don't rack up deathstacks on a border with some micronation
128  const auto& unit_ids = world.unit_manager.get_province_units(province_id);
129  for(const auto unit_id : unit_ids) {
130  const auto& unit = world.unit_manager.units[unit_id];
131  const auto& unit_owner = world.nations[unit.owner_id];
132  const auto unit_weight = unit.on_battle ? unit_battle_weight : unit_exist_weight;
133  draw_in_force += unit.get_strength() * unit_weight * nations_risk_factor[unit_owner];
134  }
135 
136  if(province.is_coastal)
137  draw_in_force *= coastal_weight;
138  if(!world.terrain_types[province.terrain_type_id].is_water_body) {
139  // Try to recover our own lost provinces
140  if(province.owner_id == nation && province.controller_id != nation)
141  draw_in_force *= reconquer_weight;
142  draw_in_force += nations_risk_factor[province.controller_id];
143  }
144  potential_risk[province_id] += draw_in_force;
145  }
146 
147  // Spread out the heat
148  for(const auto province_id : eval_provinces) {
149  const auto& province = world.provinces[province_id];
150  for(const auto neighbour_id : province.neighbour_ids)
151  potential_risk[neighbour_id] += potential_risk[province_id] / province.neighbour_ids.size();
152  }
153  }
154 
155  const Province& get_highest_priority_province(const World& world, const Province& start, const Unit& unit) {
156  // See which province has the most potential_risk so we cover it from potential threats
157  const auto* highest_risk = &start;
158  for(const auto neighbour_id : start.neighbour_ids) {
159  const auto& neighbour = world.provinces[neighbour_id];
160  // If going to water, must be a naval/amphibious/airplane unit
161  //if(!world.unit_types[unit.type_id].is_naval && world.terrain_types[neighbour.terrain_type_id].is_water_body) continue;
162 
163  if(potential_risk[neighbour_id] >= potential_risk[highest_risk->get_id()]) {
164  if(neighbour.controller_id != unit.owner_id) {
165  const auto& relation = world.get_relation(neighbour.controller_id, unit.owner_id);
166  if(relation.has_landpass())
167  highest_risk = &neighbour;
168  } else {
169  highest_risk = &neighbour;
170  }
171  }
172  }
173  return *highest_risk;
174  }
175 };
176 
177 extern std::vector<AIManager> ai_man;
std::vector< AIManager > ai_man
Definition: ai.cpp:55
std::vector< ProvinceId > g_water_provinces
Definition: ai.cpp:54
std::vector< ProvinceId > controlled_provinces
Definition: nation.hpp:149
A single province, which is used to simulate economy in a "bulk-tiles" way instead of doing economica...
Definition: province.hpp:48
std::vector< ProvinceId > neighbour_ids
Definition: province.hpp:124
Roughly a batallion, consisting of approximately 500 soldiers each.
Definition: unit.hpp:80
NationId owner_id
Definition: unit.hpp:128
Eng3D::Freelist< Unit > units
Definition: unit.hpp:173
std::vector< UnitId > get_province_units(ProvinceId province_id) const
Definition: unit.hpp:165
Definition: world.hpp:114
UnitManager unit_manager
Definition: world.hpp:145
Nation::Relation & get_relation(NationId a, NationId b)
Definition: world.hpp:192
Definition: ai.hpp:36
AIManager()
Definition: ai.hpp:55
float war_weight
Definition: ai.hpp:37
float military_strength
Definition: ai.hpp:53
float conqueror_weight
Definition: ai.hpp:46
void recalc_weights()
Reshuffle weights of the AI.
Definition: ai.hpp:64
void calc_nation_risk(const World &world, const Nation &nation)
Definition: ai.hpp:112
float unit_battle_weight
Definition: ai.hpp:38
float erratic
Definition: ai.hpp:42
void calc_province_risk(const World &world, const Nation &nation)
Definition: ai.hpp:120
size_t last_constrolled_cnt
Definition: ai.hpp:50
float get_rand() const
Definition: ai.hpp:59
float unit_exist_weight
Definition: ai.hpp:39
std::vector< float > potential_risk
Definition: ai.hpp:48
void calc_weights(const Nation &nation)
Recalculate weights iff losing territory.
Definition: ai.hpp:77
float override_threshold
Definition: ai.hpp:45
float reconquer_weight
Definition: ai.hpp:41
float get_nation_risk(const World &world, const Nation &nation, const Nation &other)
Definition: ai.hpp:106
const Province & get_highest_priority_province(const World &world, const Province &start, const Unit &unit)
Definition: ai.hpp:155
size_t gains
Definition: ai.hpp:51
size_t losses
Definition: ai.hpp:52
void collect_eval_provinces(const World &world, const Nation &nation)
Definition: ai.hpp:92
std::vector< float > nations_risk_factor
Definition: ai.hpp:47
float strength_threshold
Definition: ai.hpp:43
std::vector< ProvinceId > eval_provinces
Definition: ai.hpp:49
float coastal_weight
Definition: ai.hpp:40
World g_world
Definition: world.cpp:59