1 /*
2  * Copyright (C) 2009-2020 by the Widelands Development Team
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (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, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  *
18  */
19 
20 #include "ai/ai_help_structs.h"
21 
22 #include <algorithm>
23 
24 #include "base/macros.h"
25 #include "base/time_string.h"
26 #include "logic/ai_dna_handler.h"
27 #include "logic/map.h"
28 #include "logic/player.h"
29 
30 namespace Widelands {
31 
32 constexpr int kNoAiTrainingMutation = 200;
33 constexpr int kUpperDefaultMutationLimit = 150;
34 constexpr int kLowerDefaultMutationLimit = 75;
35 
36 // CheckStepRoadAI
CheckStepRoadAI(Player * const pl,uint8_t const mc,bool const oe)37 CheckStepRoadAI::CheckStepRoadAI(Player* const pl, uint8_t const mc, bool const oe)
38    : player(pl), movecaps(mc), open_end(oe) {
39 }
40 
allowed(const Map & map,FCoords start,FCoords end,int32_t,CheckStep::StepId const id) const41 bool CheckStepRoadAI::allowed(
42    const Map& map, FCoords start, FCoords end, int32_t, CheckStep::StepId const id) const {
43 	const uint8_t endcaps = player->get_buildcaps(end);
44 
45 	// we should not cross fields with road or flags (or any other immovable)
46 	if ((map.get_immovable(start)) && !(id == CheckStep::stepFirst)) {
47 		return false;
48 	}
49 
50 	// Calculate cost and passability
51 	if (!(endcaps & movecaps)) {
52 		return false;
53 	}
54 
55 	// Check for blocking immovables
56 	if (BaseImmovable const* const imm = map.get_immovable(end)) {
57 		if (imm->get_size() >= BaseImmovable::SMALL) {
58 			if (id != CheckStep::stepLast && !open_end) {
59 				return false;
60 			}
61 			if (dynamic_cast<Flag const*>(imm)) {
62 				return true;
63 			}
64 			if (!dynamic_cast<Road const*>(imm) || !(endcaps & BUILDCAPS_FLAG)) {
65 				return false;
66 			}
67 		}
68 	}
69 	return true;
70 }
71 
reachable_dest(const Map & map,const FCoords & dest) const72 bool CheckStepRoadAI::reachable_dest(const Map& map, const FCoords& dest) const {
73 	NodeCaps const caps = dest.field->nodecaps();
74 
75 	if (!(caps & movecaps)) {
76 		if (!((movecaps & MOVECAPS_SWIM) && (caps & MOVECAPS_WALK))) {
77 			return false;
78 		}
79 		if (!map.can_reach_by_water(dest)) {
80 			return false;
81 		}
82 	}
83 
84 	return true;
85 }
86 
87 // CheckStepOwnTerritory
CheckStepOwnTerritory(Player * const pl,uint8_t const mc,bool const oe)88 CheckStepOwnTerritory::CheckStepOwnTerritory(Player* const pl, uint8_t const mc, bool const oe)
89    : player(pl), movecaps(mc), open_end(oe) {
90 }
91 
92 // Defines when movement is allowed:
93 // 1. startfield is walkable (or it is the first step)
94 // And endfield either:
95 // 2a. is walkable
96 // 2b. has our PlayerImmovable (building or flag)
allowed(const Map & map,FCoords start,FCoords end,int32_t,CheckStep::StepId const id) const97 bool CheckStepOwnTerritory::allowed(
98    const Map& map, FCoords start, FCoords end, int32_t, CheckStep::StepId const id) const {
99 	const uint8_t endcaps = player->get_buildcaps(end);
100 	const uint8_t startcaps = player->get_buildcaps(start);
101 
102 	// We should not cross fields with road or flags (or any other immovable)
103 	// Or rather we can step on it, but not go on from such field
104 	if ((map.get_immovable(start)) && !(id == CheckStep::stepFirst)) {
105 		return false;
106 	}
107 
108 	// Start field must be walkable
109 	if (!(startcaps & movecaps)) {
110 		return false;
111 	}
112 
113 	// Endfield can not be water
114 	if (endcaps & MOVECAPS_SWIM) {
115 		return false;
116 	}
117 
118 	return true;
119 }
120 
121 // We accept either walkable territory or field with own immovable
reachable_dest(const Map & map,const FCoords & dest) const122 bool CheckStepOwnTerritory::reachable_dest(const Map& map, const FCoords& dest) const {
123 	const uint8_t endcaps = player->get_buildcaps(dest);
124 	if (BaseImmovable const* const imm = map.get_immovable(dest)) {
125 		if (imm->descr().type() >= MapObjectType::FLAG) {
126 			return true;
127 		} else {
128 			return false;
129 		}
130 	}
131 	if (endcaps & MOVECAPS_WALK) {
132 		return true;
133 	}
134 	return false;
135 }
136 
137 // We are looking for fields we can walk on
138 // and owned by hostile player.
FindNodeEnemy(Player * p,Game & g)139 FindNodeEnemy::FindNodeEnemy(Player* p, Game& g) : player(p), game(g) {
140 }
141 
accept(const EditorGameBase &,const FCoords & fc) const142 bool FindNodeEnemy::accept(const EditorGameBase&, const FCoords& fc) const {
143 	return (fc.field->nodecaps() & MOVECAPS_WALK) && fc.field->get_owned_by() != 0 &&
144 	       player->is_hostile(*game.get_player(fc.field->get_owned_by()));
145 }
146 
147 // We are looking for buildings owned by hostile player
148 // (sometimes there is a enemy's teritorry without buildings, and
149 // this confuses the AI)
FindNodeEnemiesBuilding(Player * p,Game & g)150 FindNodeEnemiesBuilding::FindNodeEnemiesBuilding(Player* p, Game& g) : player(p), game(g) {
151 }
152 
accept(const EditorGameBase &,const FCoords & fc) const153 bool FindNodeEnemiesBuilding::accept(const EditorGameBase&, const FCoords& fc) const {
154 	return (fc.field->get_immovable()) && fc.field->get_owned_by() != 0 &&
155 	       player->is_hostile(*game.get_player(fc.field->get_owned_by()));
156 }
157 
158 // When looking for unowned terrain to acquire, we are actually
159 // only interested in fields we can walk on.
160 // Fields should either be completely unowned or owned by an opposing player
FindEnemyNodeWalkable(Player * p,Game & g)161 FindEnemyNodeWalkable::FindEnemyNodeWalkable(Player* p, Game& g) : player(p), game(g) {
162 }
163 
accept(const EditorGameBase &,const FCoords & fc) const164 bool FindEnemyNodeWalkable::accept(const EditorGameBase&, const FCoords& fc) const {
165 	return ((fc.field->nodecaps() & MOVECAPS_WALK) && (fc.field->get_owned_by() > 0) &&
166 	        player->is_hostile(*game.get_player(fc.field->get_owned_by())));
167 }
168 
169 // Sometimes we need to know how many nodes our allies owns
FindNodeAllyOwned(Player * p,Game & g,PlayerNumber n)170 FindNodeAllyOwned::FindNodeAllyOwned(Player* p, Game& g, PlayerNumber n)
171    : player(p), game(g), player_number(n) {
172 }
173 
accept(const EditorGameBase &,const FCoords & fc) const174 bool FindNodeAllyOwned::accept(const EditorGameBase&, const FCoords& fc) const {
175 	return (fc.field->nodecaps() & MOVECAPS_WALK) && (fc.field->get_owned_by() != 0) &&
176 	       (fc.field->get_owned_by() != player_number) &&
177 	       !player->is_hostile(*game.get_player(fc.field->get_owned_by()));
178 }
179 
180 // When looking for unowned terrain to acquire, we must
181 // pay speciall attention to fields where mines can be built.
182 // Fields should be completely unowned
FindNodeUnownedMineable(Player * p,Game & g,int32_t t)183 FindNodeUnownedMineable::FindNodeUnownedMineable(Player* p, Game& g, int32_t t)
184    : player(p), game(g), ore_type(t) {
185 }
186 
accept(const EditorGameBase &,const FCoords & fc) const187 bool FindNodeUnownedMineable::accept(const EditorGameBase&, const FCoords& fc) const {
188 	if (ore_type == INVALID_INDEX) {
189 		return (fc.field->nodecaps() & BUILDCAPS_MINE) && (fc.field->get_owned_by() == neutral());
190 	}
191 	return (fc.field->nodecaps() & BUILDCAPS_MINE) && (fc.field->get_owned_by() == neutral()) &&
192 	       fc.field->get_resources() == ore_type;
193 }
194 
FindNodeUnownedBuildable(Player * p,Game & g)195 FindNodeUnownedBuildable::FindNodeUnownedBuildable(Player* p, Game& g) : player(p), game(g) {
196 }
197 
accept(const EditorGameBase &,const FCoords & fc) const198 bool FindNodeUnownedBuildable::accept(const EditorGameBase&, const FCoords& fc) const {
199 	return ((fc.field->nodecaps() & BUILDCAPS_SIZEMASK) ||
200 	        (fc.field->nodecaps() & BUILDCAPS_MINE)) &&
201 	       (fc.field->get_owned_by() == neutral());
202 }
203 
204 // Unowned but walkable fields nearby
FindNodeUnownedWalkable(Player * p,Game & g)205 FindNodeUnownedWalkable::FindNodeUnownedWalkable(Player* p, Game& g) : player(p), game(g) {
206 }
207 
accept(const EditorGameBase &,const FCoords & fc) const208 bool FindNodeUnownedWalkable::accept(const EditorGameBase&, const FCoords& fc) const {
209 	return (fc.field->nodecaps() & MOVECAPS_WALK) && (fc.field->get_owned_by() == neutral());
210 }
211 
212 // Looking only for mines-capable fields nearby
213 // of specific type
FindNodeMineable(Game & g,DescriptionIndex r)214 FindNodeMineable::FindNodeMineable(Game& g, DescriptionIndex r) : game(g), res(r) {
215 }
216 
accept(const EditorGameBase &,const FCoords & fc) const217 bool FindNodeMineable::accept(const EditorGameBase&, const FCoords& fc) const {
218 
219 	return (fc.field->nodecaps() & BUILDCAPS_MINE) && (fc.field->get_resources() == res);
220 }
221 
222 // Fishers and fishbreeders must be built near water
FindNodeWater(const World & world)223 FindNodeWater::FindNodeWater(const World& world) : world_(world) {
224 }
225 
accept(const EditorGameBase & egbase,const FCoords & coord) const226 bool FindNodeWater::accept(const EditorGameBase& egbase, const FCoords& coord) const {
227 	return (world_.terrain_descr(coord.field->terrain_d()).get_is() &
228 	        TerrainDescription::Is::kWater) ||
229 	       (world_.terrain_descr(egbase.map().get_neighbour(coord, WALK_W).field->terrain_r())
230 	           .get_is() &
231 	        TerrainDescription::Is::kWater) ||
232 	       (world_.terrain_descr(egbase.map().get_neighbour(coord, WALK_NW).field->terrain_r())
233 	           .get_is() &
234 	        TerrainDescription::Is::kWater);
235 }
236 
accept(const EditorGameBase &,const FCoords & coord) const237 bool FindNodeOpenWater::accept(const EditorGameBase&, const FCoords& coord) const {
238 	return !(coord.field->nodecaps() & MOVECAPS_WALK) && (coord.field->nodecaps() & MOVECAPS_SWIM);
239 }
240 
241 // FindNodeWithFlagOrRoad
accept(const EditorGameBase &,FCoords fc) const242 bool FindNodeWithFlagOrRoad::accept(const EditorGameBase&, FCoords fc) const {
243 	if (upcast(PlayerImmovable const, pimm, fc.field->get_immovable())) {
244 		return (dynamic_cast<Flag const*>(pimm) ||
245 		        (dynamic_cast<Road const*>(pimm) && (fc.field->nodecaps() & BUILDCAPS_FLAG)));
246 	}
247 	return false;
248 }
249 
NearFlag(const Flag * f,int32_t const c)250 NearFlag::NearFlag(const Flag* f, int32_t const c) : flag(f), current_road_distance(c) {
251 	to_be_checked = true;
252 }
253 
NearFlag()254 NearFlag::NearFlag() {
255 	flag = nullptr;
256 	current_road_distance = 0;
257 	to_be_checked = true;
258 }
259 
EventTimeQueue()260 EventTimeQueue::EventTimeQueue() {
261 }
262 
push(const uint32_t production_time,const uint32_t additional_id)263 void EventTimeQueue::push(const uint32_t production_time, const uint32_t additional_id) {
264 	queue.push_front(std::make_pair(production_time, additional_id));
265 }
266 
267 // Return count of entries in log (deque), if id is provided, it counts corresponding
268 // members. id here can be index of building, f.e. it count how many soldiers were
269 // trained in particular type of training site
count(const uint32_t current_time,const uint32_t additional_id)270 uint32_t EventTimeQueue::count(const uint32_t current_time, const uint32_t additional_id) {
271 	strip_old(current_time);
272 	if (additional_id == std::numeric_limits<uint32_t>::max()) {
273 		return queue.size();
274 	} else {
275 		uint32_t cnt = 0;
276 		for (auto item : queue) {
277 			if (item.second == additional_id) {
278 				++cnt;
279 			}
280 		}
281 		return cnt;
282 	}
283 }
284 
strip_old(const uint32_t current_time)285 void EventTimeQueue::strip_old(const uint32_t current_time) {
286 	while (!queue.empty() && queue.back().first < current_time - duration_) {
287 		queue.pop_back();
288 	}
289 }
290 
BuildableField(const Widelands::FCoords & fc)291 BuildableField::BuildableField(const Widelands::FCoords& fc)
292    : coords(fc),
293      field_info_expiration(20000),
294      preferred(false),
295      enemy_nearby(0),
296      enemy_accessible_(false),
297      enemy_wh_nearby(false),
298      unowned_land_nearby(0),
299      enemy_owned_land_nearby(0U),
300      unowned_buildable_spots_nearby(0U),
301      unowned_portspace_vicinity_nearby(0U),
302      nearest_buildable_spot_nearby(0U),
303      near_border(false),
304      unowned_mines_spots_nearby(0),
305      unowned_iron_mines_nearby(false),
306      trees_nearby(0),
307      bushes_nearby(0),
308      // explanation of starting values
309      // this is done to save some work for AI (CPU utilization)
310      // base rules are:
311      // count of rocks can only decrease, so  amount of rocks
312      // is recalculated only when previous count is positive
313      // count of water fields are stable, so if the current count is
314      // non-negative, water is not recalculated
315      rocks_nearby(1),
316      water_nearby(-1),
317      open_water_nearby(-1),
318      distant_water(0),
319      fish_nearby(-1),
320      critters_nearby(-1),
321      ground_water(1),
322      space_consumers_nearby(0),
323      rangers_nearby(0),
324      area_military_capacity(0),
325      military_loneliness(1000),
326      military_in_constr_nearby(0),
327      own_military_presence(0),
328      enemy_military_presence(0),
329      enemy_military_sites(0),
330      ally_military_presence(0),
331      military_stationed(0),
332      unconnected_nearby(false),
333      military_unstationed(0),
334      own_non_military_nearby(0),
335      defense_msite_allowed(false),
336      is_portspace(Widelands::ExtendedBool::kUnset),
337      port_nearby(false),
338      portspace_nearby(Widelands::ExtendedBool::kUnset),
339      max_buildcap_nearby(0),
340      last_resources_check_time(0),
341      military_score_(0),
342      inland(false),
343      local_soldier_capacity(0),
344      is_militarysite(false) {
345 }
346 
MineableField(const Widelands::FCoords & fc)347 MineableField::MineableField(const Widelands::FCoords& fc)
348    : coords(fc),
349      field_info_expiration(20000),
350      preferred(false),
351      mines_nearby(0),
352      same_mine_fields_nearby(0) {
353 }
354 
EconomyObserver(Widelands::Economy & e)355 EconomyObserver::EconomyObserver(Widelands::Economy& e) : economy(e) {
356 	dismantle_grace_time = std::numeric_limits<uint32_t>::max();
357 	fields_block_last_time = 0;
358 }
359 
total_count() const360 int32_t BuildingObserver::total_count() const {
361 	return cnt_built + cnt_under_construction;
362 }
363 
is(BuildingAttribute attribute) const364 bool BuildingObserver::is(BuildingAttribute attribute) const {
365 	return is_what.count(attribute) == 1;
366 }
367 
set_is(const BuildingAttribute attribute)368 void BuildingObserver::set_is(const BuildingAttribute attribute) {
369 	is_what.insert(attribute);
370 }
371 
unset_is(const BuildingAttribute attribute)372 void BuildingObserver::unset_is(const BuildingAttribute attribute) {
373 	is_what.erase(attribute);
374 	assert(!is(attribute));
375 }
376 
has_collected_map_resource() const377 bool BuildingObserver::has_collected_map_resource() const {
378 	return collected_map_resource != INVALID_INDEX;
379 }
set_collected_map_resource(const Widelands::TribeDescr & tribe,const std::string & ware_name)380 void BuildingObserver::set_collected_map_resource(const Widelands::TribeDescr& tribe,
381                                                   const std::string& ware_name) {
382 	if (!ware_name.empty()) {
383 		collected_map_resource = tribe.safe_ware_index(ware_name);
384 	} else {
385 		collected_map_resource = Widelands::INVALID_INDEX;
386 	}
387 }
get_collected_map_resource() const388 DescriptionIndex BuildingObserver::get_collected_map_resource() const {
389 	if (has_collected_map_resource()) {
390 		return collected_map_resource;
391 	} else {
392 		throw wexception("Building '%s' needs to define the AI hint \"collects_ware_from_map\"",
393 		                 desc->name().c_str());
394 	}
395 }
396 
aimode_limit_status() const397 Widelands::AiModeBuildings BuildingObserver::aimode_limit_status() const {
398 	if (total_count() > cnt_limit_by_aimode) {
399 		return Widelands::AiModeBuildings::kLimitExceeded;
400 	} else if (total_count() == cnt_limit_by_aimode) {
401 		return Widelands::AiModeBuildings::kOnLimit;
402 	} else {
403 		return Widelands::AiModeBuildings::kAnotherAllowed;
404 	}
405 }
buildable(Widelands::Player & p)406 bool BuildingObserver::buildable(Widelands::Player& p) {
407 	return is(BuildingAttribute::kBuildable) && p.is_building_type_allowed(id);
408 }
409 
410 // as all mines have 3 levels, AI does not know total count of mines per mined material
411 // so this observer will be used for this
MineTypesObserver()412 MineTypesObserver::MineTypesObserver()
413    : in_construction(0), finished(0), is_critical(false), unoccupied(0) {
414 }
415 
416 // Reset counter for all field types
zero()417 void MineFieldsObserver::zero() {
418 	for (auto& material : stat) {
419 		material.second = 0;
420 	}
421 }
422 
423 // Increase counter by one for specific ore/minefield type
add(const Widelands::DescriptionIndex idx)424 void MineFieldsObserver::add(const Widelands::DescriptionIndex idx) {
425 	++stat[idx];
426 }
427 
428 // Add ore into critical_ores
add_critical_ore(const Widelands::DescriptionIndex idx)429 void MineFieldsObserver::add_critical_ore(const Widelands::DescriptionIndex idx) {
430 	critical_ores.insert(idx);
431 }
432 
433 // Does the player has at least one mineable field with positive amount for each critical ore?
has_critical_ore_fields()434 bool MineFieldsObserver::has_critical_ore_fields() {
435 	for (auto ore : critical_ores) {
436 		if (get(ore) == 0) {
437 			return false;
438 		}
439 	}
440 	return true;
441 }
442 
443 // Returns count of fields with desired ore
get(const Widelands::DescriptionIndex idx)444 uint16_t MineFieldsObserver::get(const Widelands::DescriptionIndex idx) {
445 	if (stat.count(idx) == 0) {
446 		return 0;
447 	}
448 	return stat[idx];
449 }
450 
451 // Count of types of mineable fields, up to 4 currently
count_types()452 uint8_t MineFieldsObserver::count_types() {
453 	uint16_t count = 0;
454 	for (auto material : stat) {
455 		if (material.second > 0) {
456 			++count;
457 		}
458 	}
459 	return count;
460 }
461 
ExpansionType()462 ExpansionType::ExpansionType() {
463 	type = ExpansionMode::kResources;
464 }
465 
set_expantion_type(const ExpansionMode etype)466 void ExpansionType::set_expantion_type(const ExpansionMode etype) {
467 	type = etype;
468 }
469 
470 // Initialization of neuron. Neuron is defined by curve (type) and weight [-kWeightRange,
471 // kWeightRange]
472 // third argument is just id
Neuron(int8_t w,uint8_t f,uint16_t i)473 Neuron::Neuron(int8_t w, uint8_t f, uint16_t i) : weight(w), type(f), id(i) {
474 	assert(type < neuron_curves.size());
475 	assert(weight >= -kNeuronWeightLimit && weight <= kNeuronWeightLimit);
476 	recalculate();
477 }
478 
479 // Weight, or rather value in range [-kWeightRange, kWeightRange]. Automatically adjusts the weight
480 // to the range in case of
481 // overflow.
set_weight(int8_t w)482 void Neuron::set_weight(int8_t w) {
483 	weight = Neuron::clip_weight_to_range(w);
484 }
485 
486 // Neuron stores calculated values in an array of size 21.
487 // This has to be recalculated when the weight or curve type change
recalculate()488 void Neuron::recalculate() {
489 	assert(neuron_curves.size() > type);
490 	for (uint8_t i = 0; i <= kNeuronMaxPosition; ++i) {
491 		results[i] = weight * neuron_curves[type][i] / kNeuronWeightLimit;
492 	}
493 }
494 
495 // The Following two functions return Neuron values on position
get_result(const size_t pos)496 int8_t Neuron::get_result(const size_t pos) {
497 	assert(pos <= kNeuronMaxPosition);
498 	return results[pos];
499 }
500 
501 // get value corresponding to input in range 0-20, if you are out of range
502 // the input will be cropped
get_result_safe(int32_t pos,const bool absolute)503 int8_t Neuron::get_result_safe(int32_t pos, const bool absolute) {
504 	// pos has to be normalized into range 0 - 20(kNeuronMaxPosition)
505 	pos = std::max(0, std::min(static_cast<int>(kNeuronMaxPosition), pos));
506 
507 	assert(pos <= static_cast<int32_t>(kNeuronMaxPosition));
508 	assert(pos >= 0);
509 	assert(results[pos] >= -kNeuronWeightLimit && results[pos] <= kNeuronWeightLimit);
510 
511 	if (absolute) {
512 		return std::abs(results[pos]);
513 	}
514 	return results[pos];
515 }
516 
517 // Setting the type of curve
set_type(uint8_t new_type)518 void Neuron::set_type(uint8_t new_type) {
519 	assert(new_type < neuron_curves.size());
520 	type = new_type;
521 }
522 
523 // FNeuron is basically a single uint32_t integer, and the AI can query every bit of that uint32_t
FNeuron(uint32_t c,uint16_t i)524 FNeuron::FNeuron(uint32_t c, uint16_t i) : core(c), id(i) {
525 }
526 
527 // Returning a result depending on combinations of 5 bools
528 // Bools are completely anonymous, but can present any yes/no inputs, e.g. imagine the AI that is
529 // to figure out if it should attack from a militarysite. The inputs can be:
530 // bool1 - are we stronger than the enemy?
531 // bool2 - do we have a basic economy established?
532 // bool3 - do we have local predominance?
533 // bool4 - has our strength grown during the last 60 minutes?
534 // bool5 - are there mines in the vicinity?
535 // These five bools can create 32 combinations = yes/no answers.
536 // In fact this can be perceived as a complicated if..then structure, but one that can
537 // adjust automatically as a part of training.
538 // Or rather it is a 5-dimensional table with 2 columns in every dimension :)
539 // In fact this concept if very demanding for training so we don't use it much
get_result(const bool bool1,const bool bool2,const bool bool3,const bool bool4,const bool bool5)540 bool FNeuron::get_result(
541    const bool bool1, const bool bool2, const bool bool3, const bool bool4, const bool bool5) {
542 	return core.test(bool1 * 16 + bool2 * 8 + bool3 * 4 + bool4 * 2 + bool5);
543 }
544 
545 // Returning bool on a position
get_position(const uint8_t pos)546 bool FNeuron::get_position(const uint8_t pos) {
547 	assert(pos < kFNeuronBitSize);
548 	return core.test(pos);
549 }
550 
551 // Returning numerical value of FNeuron. Used for saving and priting into log
get_int()552 uint32_t FNeuron::get_int() {
553 	return core.to_ulong();
554 }
555 
556 // This is basically a mutation of FNeuron
flip_bit(const uint8_t pos)557 void FNeuron::flip_bit(const uint8_t pos) {
558 	assert(pos < kFNeuronBitSize);
559 	core.flip(pos);
560 }
561 
562 // Shifting the value in range -kWeightRange to kWeightRange, if zero_align is true, it is now
563 // allowed to shift
564 // from negative to positive and vice versa, 0 must be used.
shift_weight_value(const int8_t old_value,const bool aggressive)565 int8_t ManagementData::shift_weight_value(const int8_t old_value, const bool aggressive) {
566 
567 	int16_t halfVArRange = 50;
568 	if (aggressive) {
569 		halfVArRange = 200;
570 	}
571 
572 	const int16_t upper_limit = std::min<int16_t>(old_value + halfVArRange, kNeuronWeightLimit);
573 	const int16_t bottom_limit = std::max<int16_t>(old_value - halfVArRange, -kNeuronWeightLimit);
574 	int16_t new_value = bottom_limit + std::rand() % (upper_limit - bottom_limit + 1);
575 
576 	if (!aggressive && ((old_value > 0 && new_value < 0) || (old_value < 0 && new_value > 0))) {
577 		new_value = 0;
578 	}
579 
580 	new_value = Neuron::clip_weight_to_range(new_value);
581 	return static_cast<int8_t>(new_value);
582 }
583 
584 // Used to score performance of AI
585 // Should be disabled for "production"
review(const uint32_t gametime,PlayerNumber pn,const uint32_t land,const uint32_t max_e_land,const uint32_t old_land,const uint16_t attackers,const int16_t trained_soldiers,const uint16_t strength,const uint32_t existing_ps,const uint32_t first_iron_mine_time)586 void ManagementData::review(const uint32_t gametime,
587                             PlayerNumber pn,
588                             const uint32_t land,
589                             const uint32_t max_e_land,
590                             const uint32_t old_land,
591                             const uint16_t attackers,
592                             const int16_t trained_soldiers,
593                             const uint16_t strength,
594                             const uint32_t existing_ps,
595                             const uint32_t first_iron_mine_time) {
596 
597 	// bonuses (1000 or nothing)
598 	const uint16_t territory_bonus = (land > old_land || land > max_e_land) ? 1000 : 0;
599 	const uint16_t iron_mine_bonus = (first_iron_mine_time < 2 * 60 * 60 * 1000) ? 1000 : 0;
600 	const uint16_t attack_bonus = (attackers > 0) ? 1000 : 0;
601 	const uint16_t training_bonus = (trained_soldiers > 0) ? 1000 : 0;
602 
603 	// scores (numbers dependant on performance)
604 	const uint16_t land_score = land / kCurrentLandDivider;
605 	const uint16_t strength_score = std::min<uint16_t>(strength, 100) * kStrengthMultiplier;
606 	const uint16_t attack_score = std::min<uint16_t>(attackers, 40) * 50;
607 	const uint32_t ps_sites_score = kPSitesRatioMultiplier * std::pow(existing_ps, 3) / 1000 / 1000;
608 
609 	score = territory_bonus + iron_mine_bonus + attack_bonus + training_bonus + land_score +
610 	        strength_score + ps_sites_score + attack_score;
611 
612 	log(" %2d %s: reviewing AI mngm. data, sc: %5d Pr.p: %d (Bonuses:Te:%s I:%s A:%s Tr:%s, "
613 	    "Scores:Land:%5d Str:%4d PS:%4d, Att:%4d\n",
614 	    pn, gamestring_with_leading_zeros(gametime), score, primary_parent,
615 	    (territory_bonus) ? "Y" : "N", (iron_mine_bonus) ? "Y" : "N", (attack_bonus) ? "Y" : "N",
616 	    (training_bonus) ? "Y" : "N", land_score, strength_score, ps_sites_score, attack_score);
617 
618 	if (score < -10000 || score > 30000) {
619 		log("%2d %s: reviewing AI mngm. data, score too extreme: %4d\n", pn,
620 		    gamestring_with_leading_zeros(gametime), score);
621 	}
622 	assert(score > -10000 && score < 100000);
623 }
624 
625 // Here we generate new AI DNA (no mutation yet) and push them into persistent data
626 // this can cause inconsistency between local and persistent
new_dna_for_persistent(const uint8_t pn,const Widelands::AiType type)627 void ManagementData::new_dna_for_persistent(const uint8_t pn, const Widelands::AiType type) {
628 
629 	ai_type = type;
630 
631 	log("%2d: DNA initialization... \n", pn);
632 
633 	primary_parent = std::rand() % 4;
634 	const uint8_t parent2 = std::rand() % 4;
635 
636 	std::vector<int16_t> AI_military_numbers_P1(
637 	   Widelands::Player::AiPersistentState::kMagicNumbersSize);
638 	std::vector<int8_t> input_weights_P1(Widelands::Player::AiPersistentState::kNeuronPoolSize);
639 	std::vector<int8_t> input_func_P1(Widelands::Player::AiPersistentState::kNeuronPoolSize);
640 	std::vector<uint32_t> f_neurons_P1(Widelands::Player::AiPersistentState::kFNeuronPoolSize);
641 	ai_dna_handler.fetch_dna(
642 	   AI_military_numbers_P1, input_weights_P1, input_func_P1, f_neurons_P1, primary_parent + 1);
643 
644 	std::vector<int16_t> AI_military_numbers_P2(
645 	   Widelands::Player::AiPersistentState::kMagicNumbersSize);
646 	std::vector<int8_t> input_weights_P2(Widelands::Player::AiPersistentState::kNeuronPoolSize);
647 	std::vector<int8_t> input_func_P2(Widelands::Player::AiPersistentState::kNeuronPoolSize);
648 	std::vector<uint32_t> f_neurons_P2(Widelands::Player::AiPersistentState::kFNeuronPoolSize);
649 	ai_dna_handler.fetch_dna(
650 	   AI_military_numbers_P2, input_weights_P2, input_func_P2, f_neurons_P2, parent2 + 1);
651 
652 	log("    ... Primary parent: %d, secondary parent: %d\n", primary_parent, parent2);
653 
654 	// First setting of military numbers, they go directly to persistent data
655 	for (uint16_t i = 0; i < Widelands::Player::AiPersistentState::kMagicNumbersSize; ++i) {
656 		// Child inherits DNA with probability 1/kSecondParentProbability from main parent
657 		DnaParent dna_donor = ((std::rand() % kSecondParentProbability) > 0) ? DnaParent::kPrimary :
658 		                                                                       DnaParent::kSecondary;
659 		if (i == kMutationRatePosition) {  // Overwriting
660 			dna_donor = DnaParent::kPrimary;
661 		}
662 
663 		switch (dna_donor) {
664 		case DnaParent::kPrimary:
665 			set_military_number_at(i, AI_military_numbers_P1[i]);
666 			break;
667 		case DnaParent::kSecondary:
668 			set_military_number_at(i, AI_military_numbers_P2[i]);
669 			break;
670 		}
671 	}
672 
673 	persistent_data->neuron_weights.clear();
674 	persistent_data->neuron_functs.clear();
675 	persistent_data->f_neurons.clear();
676 
677 	for (uint16_t i = 0; i < Widelands::Player::AiPersistentState::kNeuronPoolSize; ++i) {
678 		const DnaParent dna_donor = ((std::rand() % kSecondParentProbability) > 0) ?
679 		                               DnaParent::kPrimary :
680 		                               DnaParent::kSecondary;
681 
682 		switch (dna_donor) {
683 		case DnaParent::kPrimary:
684 			persistent_data->neuron_weights.push_back(input_weights_P1[i]);
685 			persistent_data->neuron_functs.push_back(input_func_P1[i]);
686 			break;
687 		case DnaParent::kSecondary:
688 			persistent_data->neuron_weights.push_back(input_weights_P2[i]);
689 			persistent_data->neuron_functs.push_back(input_func_P2[i]);
690 			break;
691 		}
692 	}
693 
694 	for (uint16_t i = 0; i < Widelands::Player::AiPersistentState::kFNeuronPoolSize; ++i) {
695 		const DnaParent dna_donor = ((std::rand() % kSecondParentProbability) > 0) ?
696 		                               DnaParent::kPrimary :
697 		                               DnaParent::kSecondary;
698 		switch (dna_donor) {
699 		case DnaParent::kPrimary:
700 			persistent_data->f_neurons.push_back(f_neurons_P1[i]);
701 			break;
702 		case DnaParent::kSecondary:
703 			persistent_data->f_neurons.push_back(f_neurons_P2[i]);
704 			break;
705 		}
706 	}
707 
708 	assert(persistent_data->magic_numbers.size() ==
709 	       Widelands::Player::AiPersistentState::kMagicNumbersSize);
710 }
711 // Decides if mutation takes place and how intensive it will be
do_mutate(const uint8_t is_preferred,const int16_t mutation_probability)712 MutatingIntensity ManagementData::do_mutate(const uint8_t is_preferred,
713                                             const int16_t mutation_probability) {
714 	if (is_preferred > 0) {
715 		return MutatingIntensity::kAgressive;
716 	}
717 	if (std::rand() % mutation_probability == 0) {
718 		return MutatingIntensity::kNormal;
719 	}
720 	return MutatingIntensity::kNo;
721 }
722 
723 // Mutating, but all done on persistent data
mutate(const uint8_t pn)724 void ManagementData::mutate(const uint8_t pn) {
725 
726 	// Below numbers are used to dictate intensity of mutation
727 	// Probability that a value will be mutated = 1 / probability
728 	// (lesser number means higher probability and higher mutation)
729 	int16_t probability =
730 	   shift_weight_value(get_military_number_at(kMutationRatePosition), false) + 101;
731 	// Some of mutation will be agressive - over full range of values, the number below
732 	// say how many (aproximately) they will be
733 	uint16_t preferred_numbers_count = 0;
734 	// This is used to store status whether wild card was or was not used
735 	bool wild_card = false;
736 
737 	// When doing training mutation probability is to be bigger than not in training mode
738 	if (ai_training_mode_) {
739 		if (probability > kUpperDefaultMutationLimit) {
740 			probability = kUpperDefaultMutationLimit;
741 		}
742 		if (probability < kLowerDefaultMutationLimit) {
743 			probability = kLowerDefaultMutationLimit;
744 		}
745 		preferred_numbers_count = 1;
746 	} else {
747 		probability = kNoAiTrainingMutation;
748 	}
749 
750 	set_military_number_at(kMutationRatePosition, probability - 101);
751 	// decreasing probability (or rather increasing probability of mutation) if weaker player
752 	if (ai_type == Widelands::AiType::kWeak) {
753 		probability /= 15;
754 		preferred_numbers_count = 25;
755 	} else if (ai_type == Widelands::AiType::kVeryWeak) {
756 		probability /= 40;
757 		preferred_numbers_count = 50;
758 	}
759 
760 	// Wildcard for ai trainingmode
761 	if (ai_training_mode_ && std::rand() % 8 == 0 && ai_type == Widelands::AiType::kNormal) {
762 		probability /= 3;
763 		preferred_numbers_count = 5;
764 		wild_card = true;
765 	}
766 
767 	assert(probability > 0 && probability <= 201);
768 
769 	log("%2d: mutating DNA with probability 1 / %3d, preffered numbers target %d%s:\n", pn,
770 	    probability, preferred_numbers_count, (wild_card) ? ", wild card" : "");
771 
772 	if (probability < 201) {
773 
774 		// Modifying pool of Military numbers
775 		{
776 			// Preferred numbers are ones that will be mutated agressively in full range
777 			// [-kWeightRange, kWeightRange]
778 			std::set<int32_t> preferred_numbers;
779 			for (int i = 0; i < preferred_numbers_count; i++) {
780 				preferred_numbers.insert(std::rand() % pref_number_probability);
781 			}
782 
783 			for (uint16_t i = 0; i < Widelands::Player::AiPersistentState::kMagicNumbersSize; ++i) {
784 				if (i == kMutationRatePosition) {  // mutated above
785 					continue;
786 				}
787 
788 				const MutatingIntensity mutating_intensity =
789 				   do_mutate(preferred_numbers.count(i) > 0, probability);
790 
791 				if (mutating_intensity != MutatingIntensity::kNo) {
792 					const int16_t old_value = get_military_number_at(i);
793 					const int16_t new_value = shift_weight_value(
794 					   get_military_number_at(i), mutating_intensity == MutatingIntensity::kAgressive);
795 					set_military_number_at(i, new_value);
796 					log("      Magic number %3d: value changed: %4d -> %4d  %s\n", i, old_value,
797 					    new_value,
798 					    (mutating_intensity == MutatingIntensity::kAgressive) ? "aggressive" : "");
799 				}
800 			}
801 		}
802 
803 		// Modifying pool of neurons
804 		{
805 			// Neurons to be mutated more agressively
806 			std::set<int32_t> preferred_neurons;
807 			for (int i = 0; i < preferred_numbers_count; i++) {
808 				preferred_neurons.insert(std::rand() % pref_number_probability);
809 			}
810 			for (auto& item : neuron_pool) {
811 
812 				const MutatingIntensity mutating_intensity =
813 				   do_mutate(preferred_neurons.count(item.get_id()) > 0, probability);
814 
815 				if (mutating_intensity != MutatingIntensity::kNo) {
816 					const int16_t old_value = item.get_weight();
817 					if (std::rand() % 4 == 0) {
818 						assert(!neuron_curves.empty());
819 						item.set_type(std::rand() % neuron_curves.size());
820 						persistent_data->neuron_functs[item.get_id()] = item.get_type();
821 					} else {
822 						int16_t new_value = shift_weight_value(
823 						   item.get_weight(), mutating_intensity == MutatingIntensity::kAgressive);
824 						item.set_weight(new_value);
825 						persistent_data->neuron_weights[item.get_id()] = item.get_weight();
826 					}
827 					log("      Neuron %2d: weight: %4d -> %4d, new curve: %d   %s\n", item.get_id(),
828 					    old_value, item.get_weight(), item.get_type(),
829 					    (mutating_intensity == MutatingIntensity::kAgressive) ? "aggressive" : "");
830 
831 					item.recalculate();
832 				}
833 			}
834 		}
835 
836 		// Modifying pool of f-neurons
837 		{
838 			// FNeurons to be mutated more agressively
839 			std::set<int32_t> preferred_f_neurons;
840 			// preferred_numbers_count is multiplied by 3 because FNeuron store more than
841 			// one value
842 			for (int i = 0; i < 3 * preferred_numbers_count; i++) {
843 				preferred_f_neurons.insert(std::rand() % pref_number_probability);
844 			}
845 
846 			for (auto& item : f_neuron_pool) {
847 				uint8_t changed_bits = 0;
848 				// is this a preferred neuron
849 				if (preferred_f_neurons.count(item.get_id()) > 0) {
850 					for (uint8_t i = 0; i < kFNeuronBitSize; ++i) {
851 						if (std::rand() % 5 == 0) {
852 							item.flip_bit(i);
853 							++changed_bits;
854 						}
855 					}
856 				} else {  // normal mutation
857 					for (uint8_t i = 0; i < kFNeuronBitSize; ++i) {
858 						if (std::rand() % (probability * 3) == 0) {
859 							item.flip_bit(i);
860 							++changed_bits;
861 						}
862 					}
863 				}
864 
865 				if (changed_bits) {
866 					persistent_data->f_neurons[item.get_id()] = item.get_int();
867 					log("      F-Neuron %2d: new value: %13ul, changed bits: %2d   %s\n", item.get_id(),
868 					    item.get_int(), changed_bits,
869 					    (preferred_f_neurons.count(item.get_id()) > 0) ? "aggressive" : "");
870 				}
871 			}
872 		}
873 	}
874 
875 	test_consistency();
876 }
877 
878 // Now we copy persistent to local
copy_persistent_to_local()879 void ManagementData::copy_persistent_to_local() {
880 
881 	assert(persistent_data->neuron_weights.size() ==
882 	       Widelands::Player::AiPersistentState::kNeuronPoolSize);
883 	assert(persistent_data->neuron_functs.size() ==
884 	       Widelands::Player::AiPersistentState::kNeuronPoolSize);
885 	neuron_pool.clear();
886 	for (uint32_t i = 0; i < Widelands::Player::AiPersistentState::kNeuronPoolSize; ++i) {
887 		neuron_pool.push_back(
888 		   Neuron(persistent_data->neuron_weights[i], persistent_data->neuron_functs[i], i));
889 	}
890 
891 	assert(persistent_data->f_neurons.size() ==
892 	       Widelands::Player::AiPersistentState::kFNeuronPoolSize);
893 	f_neuron_pool.clear();
894 	for (uint32_t i = 0; i < Widelands::Player::AiPersistentState::kFNeuronPoolSize; ++i) {
895 		f_neuron_pool.push_back(FNeuron(persistent_data->f_neurons[i], i));
896 	}
897 
898 	assert(persistent_data->magic_numbers.size() ==
899 	       Widelands::Player::AiPersistentState::kMagicNumbersSize);
900 
901 	test_consistency();
902 	log("    ... DNA initialized\n");
903 }
904 
test_consistency(bool itemized)905 void ManagementData::test_consistency(bool itemized) {
906 	assert(persistent_data->magic_numbers.size() ==
907 	       Widelands::Player::AiPersistentState::kMagicNumbersSize);
908 	assert(persistent_data->neuron_weights.size() ==
909 	       Widelands::Player::AiPersistentState::kNeuronPoolSize);
910 	assert(persistent_data->neuron_functs.size() ==
911 	       Widelands::Player::AiPersistentState::kNeuronPoolSize);
912 	assert(neuron_pool.size() == Widelands::Player::AiPersistentState::kNeuronPoolSize);
913 	assert(f_neuron_pool.size() == Widelands::Player::AiPersistentState::kFNeuronPoolSize);
914 
915 	if (itemized) {
916 		// comparing contents of neuron and fneuron pools
917 		for (uint16_t i = 0; i < Widelands::Player::AiPersistentState::kNeuronPoolSize; ++i) {
918 			assert(persistent_data->neuron_weights[i] == neuron_pool[i].get_weight());
919 			assert(persistent_data->neuron_functs[i] == neuron_pool[i].get_type());
920 			assert(neuron_pool[i].get_id() == i);
921 		}
922 		for (uint16_t i = 0; i < Widelands::Player::AiPersistentState::kFNeuronPoolSize; ++i) {
923 			assert(persistent_data->f_neurons[i] == f_neuron_pool[i].get_int());
924 			assert(f_neuron_pool[i].get_id() == i);
925 		}
926 	}
927 
928 	return;
929 }
930 
dump_data(const PlayerNumber pn)931 void ManagementData::dump_data(const PlayerNumber pn) {
932 	ai_dna_handler.dump_output(persistent_data, pn);
933 }
934 
935 // Querying military number at a possition
get_military_number_at(uint8_t pos)936 int16_t ManagementData::get_military_number_at(uint8_t pos) {
937 	assert(pos < Widelands::Player::AiPersistentState::kMagicNumbersSize);
938 	return persistent_data->magic_numbers.at(pos);
939 }
940 
941 // Setting military number (persistent numbers are used also for local use)
set_military_number_at(const uint8_t pos,int16_t value)942 void ManagementData::set_military_number_at(const uint8_t pos, int16_t value) {
943 	assert(pos < Widelands::Player::AiPersistentState::kMagicNumbersSize);
944 	assert(persistent_data->magic_numbers.size() ==
945 	       Widelands::Player::AiPersistentState::kMagicNumbersSize);
946 	persistent_data->magic_numbers.at(pos) = Neuron::clip_weight_to_range(value);
947 }
948 
total_count() const949 uint16_t MineTypesObserver::total_count() const {
950 	return in_construction + finished;
951 }
952 
953 // this is used to count militarysites by their size
MilitarySiteSizeObserver()954 MilitarySiteSizeObserver::MilitarySiteSizeObserver() : in_construction(0), finished(0) {
955 }
956 
957 // this represents a scheduler task
SchedulerTask(const uint32_t time,const Widelands::SchedulerTaskId t,const uint8_t p,const char * d)958 SchedulerTask::SchedulerTask(const uint32_t time,
959                              const Widelands::SchedulerTaskId t,
960                              const uint8_t p,
961                              const char* d)
962    : due_time(time), id(t), priority(p), descr(d) {
963 }
964 
operator <(const SchedulerTask & other) const965 bool SchedulerTask::operator<(const SchedulerTask& other) const {
966 	return priority > other.priority;
967 }
968 
969 // List of blocked fields with block time, with some accompanying functions
add(Widelands::Coords coords,uint32_t till)970 void BlockedFields::add(Widelands::Coords coords, uint32_t till) {
971 	const uint32_t hash = coords.hash();
972 	if (blocked_fields_.count(hash) == 0) {
973 		blocked_fields_.insert(std::pair<uint32_t, uint32_t>(hash, till));
974 	} else if (blocked_fields_[hash] < till) {
975 		blocked_fields_[hash] = till;
976 	}
977 	// The third possibility is that a field has been already blocked for longer time than 'till'
978 }
979 
count()980 uint32_t BlockedFields::count() {
981 	return blocked_fields_.size();
982 }
983 
remove_expired(uint32_t gametime)984 void BlockedFields::remove_expired(uint32_t gametime) {
985 	std::vector<uint32_t> fields_to_remove;
986 	for (auto field : blocked_fields_) {
987 		if (field.second < gametime) {
988 			fields_to_remove.push_back(field.first);
989 		}
990 	}
991 	while (!fields_to_remove.empty()) {
992 		blocked_fields_.erase(fields_to_remove.back());
993 		fields_to_remove.pop_back();
994 	}
995 }
996 
is_blocked(Coords coords)997 bool BlockedFields::is_blocked(Coords coords) {
998 	return (blocked_fields_.count(coords.hash()) != 0);
999 }
1000 
PlayersStrengths()1001 PlayersStrengths::PlayersStrengths() : update_time(0) {
1002 }
1003 
1004 // Constructor to be used
PlayerStat(Widelands::TeamNumber tc,uint32_t pp,uint32_t op,uint32_t o60p,uint32_t cs,uint32_t land,uint32_t oland,uint32_t o60l)1005 PlayersStrengths::PlayerStat::PlayerStat(Widelands::TeamNumber tc,
1006                                          uint32_t pp,
1007                                          uint32_t op,
1008                                          uint32_t o60p,
1009                                          uint32_t cs,
1010                                          uint32_t land,
1011                                          uint32_t oland,
1012                                          uint32_t o60l)
1013    : team_number(tc),
1014      players_power(pp),
1015      old_players_power(op),
1016      old60_players_power(o60p),
1017      players_casualities(cs),
1018      players_land(land),
1019      old_players_land(oland),
1020      old60_players_land(o60l) {
1021 	last_time_seen = kNever;
1022 }
1023 
1024 // Inserting/updating data
1025 // We keep information for
1026 // - player strength / power
1027 // - player casualties
1028 // - player land
1029 // We store actual values, but for some of them we store also
1030 // - old = 15 mins ago
1031 // - old60 = 60 mins ago
1032 // e.g. players_power / old_players_power / old60_players_power
1033 // we recieve also player and team numbers to figure out if we are enemies, or in the team
add(Widelands::PlayerNumber pn,Widelands::PlayerNumber opn,Widelands::TeamNumber mytn,Widelands::TeamNumber pltn,uint32_t pp,uint32_t op,uint32_t o60p,uint32_t cs,uint32_t land,uint32_t oland,uint32_t o60l)1034 void PlayersStrengths::add(Widelands::PlayerNumber pn,
1035                            Widelands::PlayerNumber opn,
1036                            Widelands::TeamNumber mytn,
1037                            Widelands::TeamNumber pltn,
1038                            uint32_t pp,
1039                            uint32_t op,
1040                            uint32_t o60p,
1041                            uint32_t cs,
1042                            uint32_t land,
1043                            uint32_t oland,
1044                            uint32_t o60l) {
1045 	if (all_stats.count(opn) == 0) {
1046 		this_player_number = pn;
1047 		this_player_team = mytn;
1048 		all_stats.insert(std::make_pair(opn, PlayerStat(pltn, pp, op, o60p, cs, land, oland, o60l)));
1049 	} else {
1050 		all_stats[opn].players_power = pp;
1051 		all_stats[opn].old_players_power = op;
1052 		all_stats[opn].old60_players_power = o60p;
1053 		all_stats[opn].players_casualities = cs;
1054 		all_stats[opn].players_land = land;
1055 		all_stats[opn].old_players_land = oland;
1056 		all_stats[opn].old60_players_land = oland;
1057 		assert(this_player_number == pn);
1058 		if (this_player_team != mytn) {
1059 			log("%2d: Team changed %d -> %d\n", pn, this_player_team, mytn);
1060 			this_player_team = mytn;
1061 		}
1062 		if (all_stats[opn].team_number != pltn) {
1063 			log("%2d: Team changed for player %d: %d -> %d\n", pn, opn, all_stats[opn].team_number,
1064 			    pltn);
1065 			all_stats[opn].team_number = pltn;
1066 		}
1067 	}
1068 }
1069 
1070 // Very tiny possibility that player that has a statistics info here
1071 // does not exist anymore
remove_stat(const Widelands::PlayerNumber pn)1072 void PlayersStrengths::remove_stat(const Widelands::PlayerNumber pn) {
1073 	if (all_stats.count(pn) > 0) {
1074 		log("%d: AI: Erasing statistics for player %d\n", this_player_number, pn);
1075 		all_stats.erase(pn);
1076 	}
1077 }
1078 
1079 // After statistics for team members are updated, this calculation is needed
recalculate_team_power()1080 void PlayersStrengths::recalculate_team_power() {
1081 	team_powers.clear();
1082 	for (auto& item : all_stats) {
1083 		if (item.second.team_number > 0) {  // is a member of a team
1084 			if (team_powers.count(item.second.team_number) > 0) {
1085 				team_powers[item.second.team_number] += item.second.players_power;
1086 			} else {
1087 				team_powers[item.second.team_number] = item.second.players_power;
1088 			}
1089 		}
1090 	}
1091 }
1092 
1093 // This just goes over information about all enemies and where they were seen the last time
any_enemy_seen_lately(const uint32_t gametime)1094 bool PlayersStrengths::any_enemy_seen_lately(const uint32_t gametime) {
1095 	for (auto& item : all_stats) {
1096 		if (get_is_enemy(item.first) && player_seen_lately(item.first, gametime)) {
1097 			return true;
1098 		}
1099 	}
1100 	return false;
1101 }
1102 
1103 // Returns count of nearby enemies
enemies_seen_lately_count(const uint32_t gametime)1104 uint8_t PlayersStrengths::enemies_seen_lately_count(const uint32_t gametime) {
1105 	uint8_t count = 0;
1106 	for (auto& item : all_stats) {
1107 		if (get_is_enemy(item.first) && player_seen_lately(item.first, gametime)) {
1108 			++count;
1109 		}
1110 	}
1111 	return count;
1112 }
1113 
1114 // When we see enemy, we use this to store the time
set_last_time_seen(const uint32_t seentime,Widelands::PlayerNumber pn)1115 void PlayersStrengths::set_last_time_seen(const uint32_t seentime, Widelands::PlayerNumber pn) {
1116 	if (all_stats.count(pn) == 0) {
1117 		return;
1118 	}
1119 	all_stats[pn].last_time_seen = seentime;
1120 }
1121 
get_is_enemy(Widelands::PlayerNumber other_player_number)1122 bool PlayersStrengths::get_is_enemy(Widelands::PlayerNumber other_player_number) {
1123 	// So this is me
1124 	if (other_player_number == this_player_number) {
1125 		return false;
1126 	}
1127 	// If we do not belong to any team, all others are our enemies
1128 	if (this_player_team == 0) {
1129 		return true;
1130 	}
1131 	if (all_stats.count(other_player_number) == 0) {
1132 		// Should happen only rarely so we print a warning here
1133 		log("%d: WARNING: player has no statistics yet for player %d\n", this_player_number,
1134 		    other_player_number);
1135 		return false;
1136 	}
1137 	// finally we compare my team number of the other player team number
1138 	return all_stats[other_player_number].team_number != this_player_team;
1139 }
1140 
1141 // Was the player seen less then 2 minutes ago
player_seen_lately(Widelands::PlayerNumber pn,const uint32_t gametime)1142 bool PlayersStrengths::player_seen_lately(Widelands::PlayerNumber pn, const uint32_t gametime) {
1143 	if (all_stats.count(pn) == 0) {
1144 		// Should happen only rarely so we print a warning here
1145 		log("%d: WARNING: player has no statistics yet\n", this_player_number);
1146 		return false;
1147 	}
1148 	if (all_stats[pn].last_time_seen == kNever) {
1149 		return false;
1150 	}
1151 	if (all_stats[pn].last_time_seen + (2U * 60U * 1000U) > gametime) {
1152 		return true;
1153 	}
1154 	return false;
1155 }
1156 
1157 // This is the strength of a player
get_player_power(Widelands::PlayerNumber pn)1158 uint32_t PlayersStrengths::get_player_power(Widelands::PlayerNumber pn) {
1159 	if (all_stats.count(pn) > 0) {
1160 		return all_stats[pn].players_power;
1161 	}
1162 	return 0;
1163 }
1164 
1165 // This is the land size owned by player
get_player_land(Widelands::PlayerNumber pn)1166 uint32_t PlayersStrengths::get_player_land(Widelands::PlayerNumber pn) {
1167 	if (all_stats.count(pn) > 0) {
1168 		return all_stats[pn].players_land;
1169 	}
1170 	return 0;
1171 }
1172 
1173 // Calculates the strength of the enemies seen within the last 2 minutes
get_visible_enemies_power(const uint32_t gametime)1174 uint32_t PlayersStrengths::get_visible_enemies_power(const uint32_t gametime) {
1175 	uint32_t pw = 0;
1176 	for (auto& item : all_stats) {
1177 		if (get_is_enemy(item.first) && player_seen_lately(item.first, gametime)) {
1178 			pw += item.second.players_power;
1179 		}
1180 	}
1181 	return pw;
1182 }
1183 
get_enemies_average_power()1184 uint32_t PlayersStrengths::get_enemies_average_power() {
1185 	uint32_t sum = 0;
1186 	uint8_t count = 0;
1187 	for (auto& item : all_stats) {
1188 		if (get_is_enemy(item.first)) {
1189 			sum += item.second.players_power;
1190 			++count;
1191 		}
1192 	}
1193 	if (count > 0) {
1194 		return sum / count;
1195 	}
1196 	return 0;
1197 }
1198 
get_enemies_average_land()1199 uint32_t PlayersStrengths::get_enemies_average_land() {
1200 	uint32_t sum = 0;
1201 	uint8_t count = 0;
1202 	for (auto& item : all_stats) {
1203 		if (get_is_enemy(item.first)) {
1204 			sum += item.second.players_land;
1205 			++count;
1206 		}
1207 	}
1208 	if (count > 0) {
1209 		return sum / count;
1210 	}
1211 	return 0;
1212 }
1213 
1214 // Strength of stronger player
get_enemies_max_power()1215 uint32_t PlayersStrengths::get_enemies_max_power() {
1216 	uint32_t power = 0;
1217 	for (auto& item : all_stats) {
1218 		if (get_is_enemy(item.first)) {
1219 			power = std::max<uint32_t>(power, item.second.players_power);
1220 		}
1221 	}
1222 	return power;
1223 }
1224 
get_enemies_max_land()1225 uint32_t PlayersStrengths::get_enemies_max_land() {
1226 	uint32_t land = 0;
1227 	for (auto& item : all_stats) {
1228 		if (get_is_enemy(item.first)) {
1229 			land = std::max<uint32_t>(land, item.second.players_land);
1230 		}
1231 	}
1232 	return land;
1233 }
1234 
get_old_player_power(Widelands::PlayerNumber pn)1235 uint32_t PlayersStrengths::get_old_player_power(Widelands::PlayerNumber pn) {
1236 	if (all_stats.count(pn) > 0) {
1237 		return all_stats[pn].old_players_power;
1238 	}
1239 	return 0;
1240 }
1241 
get_old60_player_power(Widelands::PlayerNumber pn)1242 uint32_t PlayersStrengths::get_old60_player_power(Widelands::PlayerNumber pn) {
1243 	if (all_stats.count(pn) > 0) {
1244 		return all_stats[pn].old60_players_power;
1245 	}
1246 	return 0;
1247 }
1248 
get_old_player_land(Widelands::PlayerNumber pn)1249 uint32_t PlayersStrengths::get_old_player_land(Widelands::PlayerNumber pn) {
1250 	if (all_stats.count(pn) == 0) {
1251 		log(" %d: Players statistics are still empty\n", pn);
1252 		return 0;
1253 	}
1254 	return all_stats[pn].old_players_land;
1255 }
1256 
get_old60_player_land(Widelands::PlayerNumber pn)1257 uint32_t PlayersStrengths::get_old60_player_land(Widelands::PlayerNumber pn) {
1258 	if (all_stats.count(pn) == 0) {
1259 		log(" %d: Players statistics are still empty\n", pn);
1260 		return 0;
1261 	}
1262 	return all_stats[pn].old60_players_land;
1263 }
1264 
get_old_visible_enemies_power(const uint32_t gametime)1265 uint32_t PlayersStrengths::get_old_visible_enemies_power(const uint32_t gametime) {
1266 	uint32_t pw = 0;
1267 	for (auto& item : all_stats) {
1268 		if (get_is_enemy(item.first) && player_seen_lately(item.first, gametime)) {
1269 			pw += item.second.old_players_power;
1270 		}
1271 	}
1272 	return pw;
1273 }
1274 
1275 // This is strength of player plus third of strength of other members of his team
get_modified_player_power(Widelands::PlayerNumber pn)1276 uint32_t PlayersStrengths::get_modified_player_power(Widelands::PlayerNumber pn) {
1277 	uint32_t result = 0;
1278 	Widelands::TeamNumber team = 0;
1279 	if (all_stats.count(pn) > 0) {
1280 		result = all_stats[pn].players_power;
1281 		team = all_stats[pn].team_number;
1282 	}
1283 	if (team > 0 && team_powers.count(team) > 0) {
1284 		result = result + (team_powers[team] - result) / 3;
1285 	}
1286 	return result;
1287 }
1288 
1289 // Are the player in the same team
players_in_same_team(Widelands::PlayerNumber pl1,Widelands::PlayerNumber pl2)1290 bool PlayersStrengths::players_in_same_team(Widelands::PlayerNumber pl1,
1291                                             Widelands::PlayerNumber pl2) {
1292 	assert(all_stats.count(pl1) > 0);
1293 	assert(all_stats.count(pl2) > 0);
1294 	if (pl1 == pl2) {
1295 		return false;
1296 	} else if (all_stats[pl1].team_number > 0 &&
1297 	           all_stats[pl1].team_number == all_stats[pl2].team_number) {
1298 		// team number 0 = no team
1299 		return true;
1300 	} else {
1301 		return false;
1302 	}
1303 }
1304 
strong_enough(Widelands::PlayerNumber pl)1305 bool PlayersStrengths::strong_enough(Widelands::PlayerNumber pl) {
1306 	if (all_stats.count(pl) == 0) {
1307 		return false;
1308 	}
1309 	uint32_t my_strength = all_stats[pl].players_power;
1310 	uint32_t strongest_opponent_strength = 0;
1311 	for (auto item : all_stats) {
1312 		if (!players_in_same_team(item.first, pl) && pl != item.first) {
1313 			if (get_modified_player_power(item.first) > strongest_opponent_strength) {
1314 				strongest_opponent_strength = get_modified_player_power(item.first);
1315 			}
1316 		}
1317 	}
1318 	return my_strength > strongest_opponent_strength + 50;
1319 }
1320 
1321 // Update_time is used to prevent too frequent updates of statistics
set_update_time(const uint32_t gametime)1322 void PlayersStrengths::set_update_time(const uint32_t gametime) {
1323 	update_time = gametime;
1324 }
1325 
get_update_time()1326 uint32_t PlayersStrengths::get_update_time() {
1327 	return update_time;
1328 }
1329 
FlagInfo(const uint32_t gametime,const uint16_t dist,const uint32_t wh)1330 FlagWarehouseDistances::FlagInfo::FlagInfo(const uint32_t gametime,
1331                                            const uint16_t dist,
1332                                            const uint32_t wh) {
1333 	expiry_time = gametime + kFlagDistanceExpirationPeriod;
1334 	soft_expiry_time = gametime + kFlagDistanceExpirationPeriod / 2;
1335 	distance = dist;
1336 	nearest_warehouse = wh;
1337 	new_road_prohibited_till = 0;
1338 }
FlagInfo()1339 FlagWarehouseDistances::FlagInfo::FlagInfo() {
1340 	expiry_time = 0;
1341 	distance = 1000;
1342 	new_road_prohibited_till = 0;
1343 }
1344 
1345 // We are updating the distance info, but not all the time.
1346 // Always if after soft expiration period, but
1347 // if below expiration period only when the new value is lower than current one
1348 // In both cases new expiration times are calculated
update(const uint32_t gametime,const uint16_t new_distance,const uint32_t nearest_wh)1349 bool FlagWarehouseDistances::FlagInfo::update(const uint32_t gametime,
1350                                               const uint16_t new_distance,
1351                                               const uint32_t nearest_wh) {
1352 	const uint32_t new_expiry_time = gametime + kFlagDistanceExpirationPeriod;
1353 
1354 	if (gametime > soft_expiry_time) {
1355 		distance = new_distance;
1356 		expiry_time = new_expiry_time;
1357 		soft_expiry_time = gametime + kFlagDistanceExpirationPeriod / 2;
1358 		nearest_warehouse = nearest_wh;
1359 		return true;
1360 	} else if (new_distance < distance ||
1361 	           (new_distance == distance && expiry_time < new_expiry_time)) {
1362 		distance = new_distance;
1363 		expiry_time = new_expiry_time;
1364 		nearest_warehouse = nearest_wh;
1365 		return true;
1366 	}
1367 	return false;
1368 }
1369 
get(const uint32_t gametime,uint32_t * nw) const1370 uint16_t FlagWarehouseDistances::FlagInfo::get(const uint32_t gametime, uint32_t* nw) const {
1371 	*nw = nearest_warehouse;
1372 	if (gametime <= expiry_time) {
1373 		return distance;
1374 	}
1375 	return kFarButReachable;
1376 }
1377 
set_road_built(const uint32_t gametime)1378 void FlagWarehouseDistances::FlagInfo::set_road_built(const uint32_t gametime) {
1379 	// Prohibiting for next 60 seconds
1380 	new_road_prohibited_till = gametime + 60 * 1000;
1381 }
1382 
is_road_prohibited(const uint32_t gametime) const1383 bool FlagWarehouseDistances::FlagInfo::is_road_prohibited(const uint32_t gametime) const {
1384 	return new_road_prohibited_till > gametime;
1385 }
1386 
set_distance(const uint32_t flag_coords,const uint16_t distance,uint32_t const gametime,uint32_t const nearest_warehouse)1387 bool FlagWarehouseDistances::set_distance(const uint32_t flag_coords,
1388                                           const uint16_t distance,
1389                                           uint32_t const gametime,
1390                                           uint32_t const nearest_warehouse) {
1391 	if (flags_map.count(flag_coords) == 0) {
1392 		flags_map[flag_coords] =
1393 		   FlagWarehouseDistances::FlagInfo(gametime, distance, nearest_warehouse);
1394 		return true;
1395 	}
1396 	return flags_map[flag_coords].update(gametime, distance, nearest_warehouse);
1397 }
1398 
count() const1399 uint16_t FlagWarehouseDistances::count() const {
1400 	return flags_map.size();
1401 }
1402 
1403 int16_t
get_distance(const uint32_t flag_coords,uint32_t gametime,uint32_t * nw)1404 FlagWarehouseDistances::get_distance(const uint32_t flag_coords, uint32_t gametime, uint32_t* nw) {
1405 	if (flags_map.count(flag_coords) == 0) {
1406 		*nw = 0;
1407 		return kFarButReachable;  // this is to discourage to build second road from brand new flag...
1408 	} else {
1409 		return flags_map[flag_coords].get(gametime, nw);
1410 	}
1411 }
1412 
set_road_built(const uint32_t coords_hash,const uint32_t gametime)1413 void FlagWarehouseDistances::set_road_built(const uint32_t coords_hash, const uint32_t gametime) {
1414 	if (flags_map.count(coords_hash) == 1) {
1415 		flags_map[coords_hash].set_road_built(gametime);
1416 	}
1417 }
1418 
is_road_prohibited(const uint32_t coords_hash,const uint32_t gametime)1419 bool FlagWarehouseDistances::is_road_prohibited(const uint32_t coords_hash,
1420                                                 const uint32_t gametime) {
1421 	if (flags_map.count(coords_hash) == 1) {
1422 		return flags_map[coords_hash].is_road_prohibited(gametime);
1423 	}
1424 	return false;
1425 }
1426 
remove_old_flag(uint32_t gametime)1427 bool FlagWarehouseDistances::remove_old_flag(uint32_t gametime) {
1428 	for (std::map<uint32_t, FlagWarehouseDistances::FlagInfo>::iterator it = flags_map.begin();
1429 	     it != flags_map.end(); it++) {
1430 		if (it->second.expiry_time + kOldFlagRemoveTime < gametime) {
1431 			it = flags_map.erase(it);
1432 			return true;
1433 		}
1434 	}
1435 	return false;
1436 }
1437 
1438 // Returns pointer to winning road, provided that the treshold is exceeded
get_winner(const int16_t threshold)1439 FlagCandidates::Candidate* FlagCandidates::get_winner(const int16_t threshold) {
1440 	if (flags_.empty()) {
1441 		return nullptr;
1442 	}
1443 	sort();
1444 	if (flags_[0].score() < threshold) {
1445 		return nullptr;
1446 	}
1447 	if (!flags_[0].is_buildable()) {
1448 		return nullptr;
1449 	}
1450 	return &flags_[0];
1451 }
1452 
Candidate(const uint32_t c_hash,bool different_eco,uint16_t start_f_dist,uint16_t act_dist_to_wh,uint16_t air_dst)1453 FlagCandidates::Candidate::Candidate(const uint32_t c_hash,
1454                                      bool different_eco,
1455                                      uint16_t start_f_dist,
1456                                      uint16_t act_dist_to_wh,
1457                                      uint16_t air_dst) {
1458 	coords_hash = c_hash;
1459 	different_economy = different_eco;
1460 	start_flag_dist_to_wh = start_f_dist;
1461 	flag_to_flag_road_distance = 0;
1462 	possible_road_distance = kFarButReachable;
1463 	cand_flag_distance_to_wh = act_dist_to_wh;
1464 	air_distance = air_dst;
1465 }
1466 
score() const1467 int16_t FlagCandidates::Candidate::score() const {
1468 	return different_economy * 2000 + (start_flag_dist_to_wh - cand_flag_distance_to_wh) +
1469 	       (flag_to_flag_road_distance - 2 * possible_road_distance);
1470 }
1471 
set_road_possible(const uint32_t coords_hash,const uint16_t steps)1472 bool FlagCandidates::set_road_possible(const uint32_t coords_hash, const uint16_t steps) {
1473 	for (auto& item : flags_) {
1474 		if (item.coords_hash == coords_hash) {
1475 			item.possible_road_distance = steps;
1476 			return true;
1477 		}
1478 	}
1479 	return false;
1480 }
1481 
set_cur_road_distance(const uint32_t coords,uint16_t dist)1482 bool FlagCandidates::set_cur_road_distance(const uint32_t coords, uint16_t dist) {
1483 	for (auto& item : flags_) {
1484 		if (item.coords_hash == coords) {
1485 			item.flag_to_flag_road_distance = dist;
1486 			return true;
1487 		}
1488 	}
1489 	return false;
1490 }
sort()1491 void FlagCandidates::sort() {
1492 	std::sort(flags_.begin(), flags_.end());
1493 }
1494 
sort_by_air_distance()1495 void FlagCandidates::sort_by_air_distance() {
1496 	std::sort(flags_.begin(), flags_.end(),
1497 	          [](const FlagCandidates::Candidate& lf, const FlagCandidates::Candidate& rf) {
1498 		          return lf.air_distance < rf.air_distance;
1499 		       });
1500 }
1501 
add_flag(const uint32_t coords,const bool different_economy,const uint16_t act_dist_to_wh,const uint16_t air_distance)1502 void FlagCandidates::add_flag(const uint32_t coords,
1503                               const bool different_economy,
1504                               const uint16_t act_dist_to_wh,
1505                               const uint16_t air_distance) {
1506 	flags_.push_back(
1507 	   Candidate(coords, different_economy, start_flag_dist_to_wh, act_dist_to_wh, air_distance));
1508 }
1509 
has_candidate(const uint32_t coords_hash) const1510 bool FlagCandidates::has_candidate(const uint32_t coords_hash) const {
1511 	for (auto& item : flags_) {
1512 		if (item.coords_hash == coords_hash) {
1513 			return true;
1514 		}
1515 	}
1516 	return false;
1517 }
1518 
1519 }  // namespace Widelands
1520