Merge lp:~widelands-dev/widelands/ai-post-b19-2 into lp:widelands

Proposed by GunChleoc
Status: Merged
Merged at revision: 8397
Proposed branch: lp:~widelands-dev/widelands/ai-post-b19-2
Merge into: lp:widelands
Prerequisite: lp:~widelands-dev/widelands/ai_trainingsites_proportion
Diff against target: 9667 lines (+5075/-1677)
65 files modified
data/tribes/atlanteans.lua (+5/-0)
data/tribes/barbarians.lua (+5/-0)
data/tribes/buildings/productionsites/atlanteans/bakery/init.lua (+1/-1)
data/tribes/buildings/productionsites/atlanteans/barracks/init.lua (+0/-1)
data/tribes/buildings/productionsites/atlanteans/blackroot_farm/init.lua (+0/-1)
data/tribes/buildings/productionsites/atlanteans/crystalmine/init.lua (+1/-0)
data/tribes/buildings/productionsites/atlanteans/farm/init.lua (+1/-1)
data/tribes/buildings/productionsites/atlanteans/fishers_house/init.lua (+1/-0)
data/tribes/buildings/productionsites/atlanteans/mill/init.lua (+1/-0)
data/tribes/buildings/productionsites/atlanteans/sawmill/init.lua (+1/-1)
data/tribes/buildings/productionsites/atlanteans/smokery/init.lua (+1/-1)
data/tribes/buildings/productionsites/atlanteans/spiderfarm/init.lua (+1/-1)
data/tribes/buildings/productionsites/atlanteans/toolsmithy/init.lua (+0/-1)
data/tribes/buildings/productionsites/atlanteans/weaving_mill/init.lua (+2/-2)
data/tribes/buildings/productionsites/barbarians/barracks/init.lua (+1/-1)
data/tribes/buildings/productionsites/barbarians/farm/init.lua (+1/-2)
data/tribes/buildings/productionsites/barbarians/gamekeepers_hut/init.lua (+2/-1)
data/tribes/buildings/productionsites/barbarians/hunters_hut/init.lua (+2/-1)
data/tribes/buildings/productionsites/barbarians/lime_kiln/init.lua (+2/-2)
data/tribes/buildings/productionsites/barbarians/lumberjacks_hut/init.lua (+1/-2)
data/tribes/buildings/productionsites/barbarians/metal_workshop/init.lua (+1/-1)
data/tribes/buildings/productionsites/barbarians/micro_brewery/init.lua (+0/-1)
data/tribes/buildings/productionsites/barbarians/rangers_hut/init.lua (+1/-1)
data/tribes/buildings/productionsites/barbarians/reed_yard/init.lua (+1/-1)
data/tribes/buildings/productionsites/barbarians/smelting_works/init.lua (+1/-0)
data/tribes/buildings/productionsites/barbarians/tavern/init.lua (+1/-1)
data/tribes/buildings/productionsites/barbarians/well/init.lua (+1/-2)
data/tribes/buildings/productionsites/barbarians/wood_hardener/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/armorsmithy/init.lua (+0/-1)
data/tribes/buildings/productionsites/empire/bakery/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/barracks/init.lua (+0/-1)
data/tribes/buildings/productionsites/empire/brewery/init.lua (+0/-1)
data/tribes/buildings/productionsites/empire/farm/init.lua (+2/-2)
data/tribes/buildings/productionsites/empire/inn/init.lua (+0/-1)
data/tribes/buildings/productionsites/empire/marblemine/init.lua (+2/-1)
data/tribes/buildings/productionsites/empire/piggery/init.lua (+0/-1)
data/tribes/buildings/productionsites/empire/sawmill/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/sheepfarm/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/stonemasons_house/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/tavern/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/toolsmithy/init.lua (+0/-1)
data/tribes/buildings/productionsites/empire/vineyard/init.lua (+1/-1)
data/tribes/buildings/productionsites/empire/weaponsmithy/init.lua (+2/-1)
data/tribes/buildings/productionsites/empire/winery/init.lua (+2/-2)
data/tribes/buildings/trainingsites/atlanteans/dungeon/init.lua (+0/-1)
data/tribes/buildings/trainingsites/atlanteans/labyrinth/init.lua (+0/-1)
data/tribes/buildings/trainingsites/barbarians/battlearena/init.lua (+0/-1)
data/tribes/buildings/trainingsites/barbarians/trainingcamp/init.lua (+0/-1)
data/tribes/buildings/trainingsites/empire/arena/init.lua (+0/-1)
data/tribes/buildings/trainingsites/empire/colosseum/init.lua (+0/-1)
data/tribes/buildings/trainingsites/empire/trainingcamp/init.lua (+0/-1)
data/tribes/empire.lua (+5/-0)
src/ai/ai_help_structs.cc (+1107/-42)
src/ai/ai_help_structs.h (+291/-48)
src/ai/ai_hints.cc (+2/-10)
src/ai/ai_hints.h (+6/-8)
src/ai/defaultai.cc (+2515/-1067)
src/ai/defaultai.h (+75/-39)
src/ai/defaultai_seafaring.cc (+6/-6)
src/ai/defaultai_warfare.cc (+821/-366)
src/game_io/game_player_ai_persistent_packet.cc (+102/-23)
src/logic/map_objects/tribes/tribe_descr.cc (+38/-0)
src/logic/map_objects/tribes/tribe_descr.h (+13/-1)
src/logic/player.h (+16/-15)
src/wui/interactive_base.cc (+28/-0)
To merge this branch: bzr merge lp:~widelands-dev/widelands/ai-post-b19-2
Reviewer Review Type Date Requested Status
GunChleoc Approve
Review via email: mp+325796@code.launchpad.net

Description of the change

Same proposal as Tibor made, but with prerequisite branch set. Thanks to Launchpad for not letting me add it/hiding the option in the UI.

I would like to propose this branch for merge. The changes are really big, the genetic algorithm itself and many other changes that was done along the way.
The problem is that this is kind of one-way road. Once the genetic algorithm will be in trunk, it will be difficult to come back. Or rather it will be possible but a lot of logic will need to be re-created/tested manually.

Follow up action will be to prepare saving and loading AI data to and from separate text/lua file, so that training AI will not require changes in ai_help_structs.cc and recompilation.

We can do review/code fix in multiple steps...

To post a comment you must log in.
Revision history for this message
GunChleoc (gunchleoc) wrote :

@Tibor I recreated this to have a smaller diff - the trainingsites proportions branch needs to go in first.

https://code.launchpad.net/~widelands-dev/widelands/ai_trainingsites_proportion/+merge/324607

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2308. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/243585362.
Appveyor build 2142. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_ai_post_b19_2-2142.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2330. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/244936136.
Appveyor build 2158. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_ai_post_b19_2-2158.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2332. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/245083207.
Appveyor build 2160. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_ai_post_b19_2-2160.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2335. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/245269883.
Appveyor build 2163. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_ai_post_b19_2-2163.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Code review done.

1. Please go over my changes with a fine-tooth comb to make sure that I haven't accidentally introduced any logic changes.

2. Was the test suite already broken, or did I break it with my changes?

3. We need to keep savegame compatibility until after Bzuild 20, so savegames will remain viable for bug fixing. I have added some code so that old savegames will load, but the games will segfault, because something still needs initializing. Since you know the code better than I, can you have a look?

4. We have a bunch of hard coded ware and building names that I'd like to get rid of. Turn anything that's too complicated to solve right now into a TODO comment, so that we can fix it in a follow-up branch.

5. I found a bunch of commented out code lines, I left them in with a NOCOM comment so that you can decide if you still want them someplace.

6. I think we should have a command line switch for the training mode, so we won't have to recompile to switch modes. Let me know if you want me to take care of it.

Note that I will be unable to contribute code for a few weeks starting from Thursday next week, so if you want me to do any more coding in this branch, please let me know ASAP.

Revision history for this message
TiborB (tiborb95) wrote :

Gun,

thanks for review!

Savegame compatibility is quite tricky. I will look at this but I believe that proper strategy is just to ignore previous AI data (from older packet) and initialize AI data from scratch.

I also dont like addressing buildings by name, and when we discuss AI changes on forum, most players gives advices specific for buildings&tribes. I am trying to avoid this as much as possible.

I can introduce bunch of new ai_hints like "is_bakery", though opposite would be better (per each tribe):
bakery:"barbarian_bakery"
woodcutter:"barbarian_lumberjack"

For now AI expects on some spaces in code that there is exactly one building of the type. But entries in "ai_hints" are OK, and AI will make sure the building is identified, and just one.

I will start with save/load stuff....

Revision history for this message
GunChleoc (gunchleoc) wrote :

> Savegame compatibility is quite tricky. I will look at this but I believe that proper strategy is just to ignore previous AI data (from older packet) and initialize AI data from scratch.

I agree - we have to run the loading to eat uo the packets, but
initializing the AI data from scratch will be the way to go.

> I also dont like addressing buildings by name, and when we discuss AI changes on forum, most players gives advices specific for buildings&tribes. I am trying to avoid this as much as possible.
>
> I can introduce bunch of new ai_hints like "is_bakery", though opposite would be better (per each tribe):
> bakery:"barbarian_bakery"
> woodcutter:"barbarian_lumberjack"

You mean like we have for the port & barracks? I could implement that
pretty easily.

Revision history for this message
TiborB (tiborb95) wrote :

>> I can introduce bunch of new ai_hints like "is_bakery", though opposite would be better (per each tribe):
>> bakery:"barbarian_bakery"
>> woodcutter:"barbarian_lumberjack"

?You mean like we have for the port & barracks? I could implement that

Do you mean through "ai_hints"? I have to fix also "is_basic" - as you suggested, so I can to it at once. I want to be sure it is properly "plugged" to default_ai.cc

Revision history for this message
GunChleoc (gunchleoc) wrote :

>>> I can introduce bunch of new ai_hints like "is_bakery", though opposite would be better (per each tribe):
>>> bakery:"barbarian_bakery"
>>> woodcutter:"barbarian_lumberjack"
>
> ?You mean like we have for the port & barracks? I could implement that
>
> Do you mean through "ai_hints"? I have to fix also "is_basic" - as you suggested, so I can to it at once. I want to be sure it is properly "plugged" to default_ai.cc

No, I thought that you meant this:

https://bazaar.launchpad.net/~widelands-dev/widelands/trunk/view/head:/data/tribes/atlanteans.lua#L353

and this:

https://bazaar.launchpad.net/~widelands-dev/widelands/trunk/view/head:/src/logic/map_objects/tribes/tribe_descr.h#L106

AI hints will certainly work though.

Revision history for this message
TiborB (tiborb95) wrote :

I did not know about this code, it seems to be exactly what is needed. I will use this. I will implement it myself, I want to make myself familiar with this stuff.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Sounds good to me. Holler if you need help.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2339. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/245680347.
Appveyor build 2167. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_ai_post_b19_2-2167.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I ran the test suite on r8148 (the last revision before I started changing stuff) and it went though for the release build, but had 3 failures on the debug build.

Revision history for this message
TiborB (tiborb95) wrote :

@Gun,
I found that the game crashes on line:

if (field.coords.field->get_immovable()->descr().type() == Widelands::MapObjectType::MILITARYSITE) {

(gdb) bt
#0 0x0000000000ce6f5a in Widelands::MapObject::descr (this=0x0)
    at /var/widelands2/BZR/ai-post-b19-2/src/logic/map_objects/map_object.h:229
#1 0x000000000111c015 in DefaultAI::update_buildable_field (this=0x9d9cc60, field=...)
    at /var/widelands2/BZR/ai-post-b19-2/src/ai/defaultai.cc:1243
#2 0x000000000111bc9e in DefaultAI::update_all_not_buildable_fields (this=0x9d9cc60)
    at /var/widelands2/BZR/ai-post-b19-2/src/ai/defaultai.cc:1204

Do you know what had you changed? It worked for sure before...

Revision history for this message
TiborB (tiborb95) wrote :

> I ran the test suite on r8148 (the last revision before I started changing stuff) and it went though for the release build, but had 3 failures on the debug build.

The log says it is failed assert in AI code, there is player statistics (land, enemy size) missing - I can work this around easily, so do not bother yourself about it..

Revision history for this message
TiborB (tiborb95) wrote :

re:
if (field.coords.field->get_immovable()->descr().type() == Widelands::MapObjectType::MILITARYSITE) {
crash

it crashes also with brand new game, so it is not related to save/load

Revision history for this message
GunChleoc (gunchleoc) wrote :

That's a change I made because it's more efficient than an upcast (I recenty learned that dynamic_cast is expensive). This of course assumes that there is an immovable on the field.

So, either test if the immovable is nullptr first, or change back to upcast.

Revision history for this message
TiborB (tiborb95) wrote :

OK, makes sense. I did not know you had changed that line. I will add a test there if there is a building there.

Revision history for this message
SirVer (sirver) wrote :

drive by: I like this stack overflow for information about dynamic_cast<> cost: https://stackoverflow.com/questions/4050901/performance-of-dynamic-cast

tl;dr: dynamic_cast<> is not crazy expensive, but if you can do without, great. It also is usually bad design if you require dynamic_cast<> - but unfortunately, we have plenty of bad design in Widelands, so you have to use it sometimes, since you cannot design around our errors of the past.

Revision history for this message
GunChleoc (gunchleoc) wrote :

BTW comments like

  //Should happen only rarely so we print a warning here

will break codecheck, always use a blank space:

  // Should happen only rarely so we print a warning here

Regarding your question in the code, size_t is an unsigned type http://www.cplusplus.com/reference/cstddef/size_t/?kw=size_t

Revision history for this message
TiborB (tiborb95) wrote :

I presume that clang-format will take care of that, will not? Once all changes are done I will run clang-format and retest and let you know.

in 8163 I introduced new special wares, investigate diff for this revision if you are interested. I did not introduce the helper function, I think we can manage without it, especially now, when I shortened the code. But if you insist I can.

Revision history for this message
TiborB (tiborb95) wrote :

Gun,
I did some changes to critical materials stuff, I made some mistakes in 8163 it seems.

Also there are some notes for you, look for "@Gun" in the diff

The biggest issue that remains is that command line switch, I have to investigate this a bit

Revision history for this message
GunChleoc (gunchleoc) wrote :

I have answered the notes. I have also renamed "stones" to "granite". This was a leftover from my terminology cleanup for Build19 that was never implemented in the AI variables and functions - "stones" it the mountain map resource, "granite" is the ware now. The immovables littered around the map are "rocks".

I think you can get rid of one of the new definitions. If a building has refinedlog() listed in its outputs, it is automatically a logrefiner().

Also, why do we need this assertion?

    assert(count_buildings_with_attribute(BuildingAttribute::kIronMine) <= 3);

If somebody mods a tribe with even more mine upgrades, or many different types of iron ore producers, the assertion won't hold.

Revision history for this message
TiborB (tiborb95) wrote :

As for mines<=3 question, you are right, if somebody creates tribe with more mines he will have to modify the assertion - should not be a big deal, I even inserted a comment there. But for now I want to be 100% sure that it does what it is supposed to do..

Revision history for this message
GunChleoc (gunchleoc) wrote :

Sounds good to me.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I ave been thinking about the command line parameter, we should do that in a follow-up branch and just set kAITrainingMode to false for now. This will make it easier to review.

You can also use kAITrainingMode in the interactive base for switching the game speed code on and off, this way that code can stay in and be always valid.

Revision history for this message
TiborB (tiborb95) wrote :

OK, we will leave the command line switch for the next time. I will go over the code again and let you know when it will be ready....

Revision history for this message
TiborB (tiborb95) wrote :

OK, next try please

Just 2 points
- diff here on web page is truncated
- there is still one NOCOM and response to Gun in that cut off part of the diff...

Revision history for this message
GunChleoc (gunchleoc) wrote :

I did a few small cleanups, this is ready to go now. Thanks for taking on this huge job!

@bunnybot merge

review: Approve
Revision history for this message
TiborB (tiborb95) wrote :

I am bit afraid of reaction of players, the change of behavior is really significant

Revision history for this message
GunChleoc (gunchleoc) wrote :

The forum people are generally very friendly and supportive, so try not to worry about it too much. The new AI seems to perform better than the old one already. Of course, there will be bugs or things not working as expected, but that's just how big changes work in software development. Well just have to fix them then as they come up.

I expect that this will produce a lot less new bugs and crashes than I myself have caused since Build 19, and I'm glad that somebody is doing AI stuff, because I don't know the first thing about AI development ;)

Revision history for this message
GunChleoc (gunchleoc) wrote :

We still have codecheck warnings. Travis is having problems today, so best wait until tomorrow and fix them then.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2392. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/247861477.
Appveyor build 2220. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_ai_post_b19_2-2220.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Refusing to merge, since Travis is not green. Use @bunnybot merge force for merging anyways.

Travis build 2392. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/247861477.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2403. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/248478925.
Appveyor build 2231. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_ai_post_b19_2-2231.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2406. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/248928377.
Appveyor build 2234. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_ai_post_b19_2-2234.

Revision history for this message
TiborB (tiborb95) wrote :

CI seems to be OK now. Can it be merged?

Revision history for this message
GunChleoc (gunchleoc) wrote :

Yes, let's merge this :)

@bunnybot merge

BTW: I have all my editors set to automatically kill trailing whitespaces. This helps keeping the diffs small and avoids that particular codecheck fail.

Revision history for this message
TiborB (tiborb95) wrote :

Oh, I did not know such feature exists. My editor (geany) has it so I will enable it...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/tribes/atlanteans.lua'
2--- data/tribes/atlanteans.lua 2017-04-30 10:30:02 +0000
3+++ data/tribes/atlanteans.lua 2017-07-03 17:39:38 +0000
4@@ -352,4 +352,9 @@
5 headquarters = "atlanteans_headquarters",
6 port = "atlanteans_port",
7 barracks = "atlanteans_barracks",
8+ bakery = "atlanteans_bakery",
9+ ironore = "iron_ore",
10+ rawlog = "log",
11+ refinedlog = "planks",
12+ granite = "granite",
13 }
14
15=== modified file 'data/tribes/barbarians.lua'
16--- data/tribes/barbarians.lua 2017-04-30 10:30:02 +0000
17+++ data/tribes/barbarians.lua 2017-07-03 17:39:38 +0000
18@@ -285,4 +285,9 @@
19 headquarters = "barbarians_headquarters",
20 port = "barbarians_port",
21 barracks = "barbarians_barracks",
22+ bakery = "barbarians_bakery",
23+ ironore = "iron_ore",
24+ rawlog = "log",
25+ refinedlog = "blackwood",
26+ granite = "granite",
27 }
28
29=== modified file 'data/tribes/buildings/productionsites/atlanteans/bakery/init.lua'
30--- data/tribes/buildings/productionsites/atlanteans/bakery/init.lua 2017-02-10 09:40:17 +0000
31+++ data/tribes/buildings/productionsites/atlanteans/bakery/init.lua 2017-07-03 17:39:38 +0000
32@@ -32,7 +32,7 @@
33 },
34
35 aihints = {
36- forced_after = 1200,
37+ basic_amount = 1,
38 prohibited_till = 900
39 },
40
41
42=== modified file 'data/tribes/buildings/productionsites/atlanteans/barracks/init.lua'
43--- data/tribes/buildings/productionsites/atlanteans/barracks/init.lua 2017-02-10 09:40:17 +0000
44+++ data/tribes/buildings/productionsites/atlanteans/barracks/init.lua 2017-07-03 17:39:38 +0000
45@@ -34,7 +34,6 @@
46 },
47
48 aihints = {
49- forced_after = 1000,
50 very_weak_ai_limit = 1,
51 weak_ai_limit = 3
52 },
53
54=== modified file 'data/tribes/buildings/productionsites/atlanteans/blackroot_farm/init.lua'
55--- data/tribes/buildings/productionsites/atlanteans/blackroot_farm/init.lua 2015-12-11 16:54:00 +0000
56+++ data/tribes/buildings/productionsites/atlanteans/blackroot_farm/init.lua 2017-07-03 17:39:38 +0000
57@@ -29,7 +29,6 @@
58
59 aihints = {
60 prohibited_till = 600,
61- forced_after = 800,
62 space_consumer = true
63 },
64
65
66=== modified file 'data/tribes/buildings/productionsites/atlanteans/crystalmine/init.lua'
67--- data/tribes/buildings/productionsites/atlanteans/crystalmine/init.lua 2017-02-18 23:07:56 +0000
68+++ data/tribes/buildings/productionsites/atlanteans/crystalmine/init.lua 2017-07-03 17:39:38 +0000
69@@ -36,6 +36,7 @@
70
71 aihints = {
72 mines = "stones",
73+ basic_amount = 1,
74 prohibited_till = 600
75 },
76
77
78=== modified file 'data/tribes/buildings/productionsites/atlanteans/farm/init.lua'
79--- data/tribes/buildings/productionsites/atlanteans/farm/init.lua 2016-01-28 05:24:34 +0000
80+++ data/tribes/buildings/productionsites/atlanteans/farm/init.lua 2017-07-03 17:39:38 +0000
81@@ -30,9 +30,9 @@
82
83 aihints = {
84 space_consumer = true,
85+ basic_amount = 1,
86 -- Farm needs spidercloth to be built and spidercloth needs corn for production
87 -- -> farm should be built ASAP!
88- forced_after = 400,
89 prohibited_till = 200
90 },
91
92
93=== modified file 'data/tribes/buildings/productionsites/atlanteans/fishers_house/init.lua'
94--- data/tribes/buildings/productionsites/atlanteans/fishers_house/init.lua 2015-12-11 16:54:00 +0000
95+++ data/tribes/buildings/productionsites/atlanteans/fishers_house/init.lua 2017-07-03 17:39:38 +0000
96@@ -26,6 +26,7 @@
97
98 aihints = {
99 needs_water = true,
100+ basic_amount = 1,
101 prohibited_till = 600
102 },
103
104
105=== modified file 'data/tribes/buildings/productionsites/atlanteans/mill/init.lua'
106--- data/tribes/buildings/productionsites/atlanteans/mill/init.lua 2016-10-30 19:15:45 +0000
107+++ data/tribes/buildings/productionsites/atlanteans/mill/init.lua 2017-07-03 17:39:38 +0000
108@@ -33,6 +33,7 @@
109 },
110
111 aihints = {
112+ basic_amount = 1,
113 prohibited_till = 600
114 },
115
116
117=== modified file 'data/tribes/buildings/productionsites/atlanteans/sawmill/init.lua'
118--- data/tribes/buildings/productionsites/atlanteans/sawmill/init.lua 2016-12-11 09:29:28 +0000
119+++ data/tribes/buildings/productionsites/atlanteans/sawmill/init.lua 2017-07-03 17:39:38 +0000
120@@ -31,7 +31,7 @@
121 },
122
123 aihints = {
124- forced_after = 250,
125+ basic_amount = 2,
126 prohibited_till = 250,
127 very_weak_ai_limit = 1,
128 weak_ai_limit = 2
129
130=== modified file 'data/tribes/buildings/productionsites/atlanteans/smokery/init.lua'
131--- data/tribes/buildings/productionsites/atlanteans/smokery/init.lua 2016-10-26 19:21:32 +0000
132+++ data/tribes/buildings/productionsites/atlanteans/smokery/init.lua 2017-07-03 17:39:38 +0000
133@@ -32,7 +32,7 @@
134 },
135
136 aihints = {
137- forced_after = 800,
138+ basic_amount = 1,
139 prohibited_till = 180,
140 very_weak_ai_limit = 1,
141 weak_ai_limit = 2
142
143=== modified file 'data/tribes/buildings/productionsites/atlanteans/spiderfarm/init.lua'
144--- data/tribes/buildings/productionsites/atlanteans/spiderfarm/init.lua 2016-09-03 14:59:10 +0000
145+++ data/tribes/buildings/productionsites/atlanteans/spiderfarm/init.lua 2017-07-03 17:39:38 +0000
146@@ -31,7 +31,7 @@
147 },
148
149 aihints = {
150- forced_after = 450,
151+ basic_amount = 1,
152 prohibited_till = 350
153 },
154
155
156=== modified file 'data/tribes/buildings/productionsites/atlanteans/toolsmithy/init.lua'
157--- data/tribes/buildings/productionsites/atlanteans/toolsmithy/init.lua 2016-10-30 19:15:45 +0000
158+++ data/tribes/buildings/productionsites/atlanteans/toolsmithy/init.lua 2017-07-03 17:39:38 +0000
159@@ -32,7 +32,6 @@
160 },
161
162 aihints = {
163- forced_after = 500,
164 prohibited_till = 450
165 },
166
167
168=== modified file 'data/tribes/buildings/productionsites/atlanteans/weaving_mill/init.lua'
169--- data/tribes/buildings/productionsites/atlanteans/weaving_mill/init.lua 2016-12-14 09:09:34 +0000
170+++ data/tribes/buildings/productionsites/atlanteans/weaving_mill/init.lua 2017-07-03 17:39:38 +0000
171@@ -32,8 +32,8 @@
172 },
173
174 aihints = {
175- forced_after = 600,
176- prohibited_till = 450,
177+ prohibited_till = 400,
178+ basic_amount = 1,
179 very_weak_ai_limit = 1,
180 weak_ai_limit = 2
181 },
182
183=== modified file 'data/tribes/buildings/productionsites/barbarians/barracks/init.lua'
184--- data/tribes/buildings/productionsites/barbarians/barracks/init.lua 2017-02-10 09:40:17 +0000
185+++ data/tribes/buildings/productionsites/barbarians/barracks/init.lua 2017-07-03 17:39:38 +0000
186@@ -33,7 +33,7 @@
187 },
188
189 aihints = {
190- forced_after = 1000,
191+ basic_amount = 1,
192 very_weak_ai_limit = 1,
193 weak_ai_limit = 3
194 },
195
196=== modified file 'data/tribes/buildings/productionsites/barbarians/farm/init.lua'
197--- data/tribes/buildings/productionsites/barbarians/farm/init.lua 2016-01-28 05:24:34 +0000
198+++ data/tribes/buildings/productionsites/barbarians/farm/init.lua 2017-07-03 17:39:38 +0000
199@@ -40,8 +40,7 @@
200 },
201
202 aihints = {
203- space_consumer = true,
204- forced_after = 600
205+ space_consumer = true
206 },
207
208 working_positions = {
209
210=== modified file 'data/tribes/buildings/productionsites/barbarians/gamekeepers_hut/init.lua'
211--- data/tribes/buildings/productionsites/barbarians/gamekeepers_hut/init.lua 2015-12-11 16:54:00 +0000
212+++ data/tribes/buildings/productionsites/barbarians/gamekeepers_hut/init.lua 2017-07-03 17:39:38 +0000
213@@ -35,7 +35,8 @@
214
215 aihints = {
216 renews_map_resource = "meat",
217- prohibited_till = 900
218+ prohibited_till = 900,
219+ basic_amount = 1
220 },
221
222 working_positions = {
223
224=== modified file 'data/tribes/buildings/productionsites/barbarians/hunters_hut/init.lua'
225--- data/tribes/buildings/productionsites/barbarians/hunters_hut/init.lua 2015-12-11 16:54:00 +0000
226+++ data/tribes/buildings/productionsites/barbarians/hunters_hut/init.lua 2017-07-03 17:39:38 +0000
227@@ -34,7 +34,8 @@
228 },
229
230 aihints = {
231- prohibited_till = 500
232+ prohibited_till = 500,
233+ basic_amount = 1
234 },
235
236 working_positions = {
237
238=== modified file 'data/tribes/buildings/productionsites/barbarians/lime_kiln/init.lua'
239--- data/tribes/buildings/productionsites/barbarians/lime_kiln/init.lua 2016-09-03 14:59:10 +0000
240+++ data/tribes/buildings/productionsites/barbarians/lime_kiln/init.lua 2017-07-03 17:39:38 +0000
241@@ -31,9 +31,9 @@
242 },
243
244 aihints = {
245- forced_after = 600,
246 very_weak_ai_limit = 1,
247- weak_ai_limit = 2
248+ weak_ai_limit = 2,
249+ basic_amount = 1
250 },
251
252 working_positions = {
253
254=== modified file 'data/tribes/buildings/productionsites/barbarians/lumberjacks_hut/init.lua'
255--- data/tribes/buildings/productionsites/barbarians/lumberjacks_hut/init.lua 2015-12-11 16:54:00 +0000
256+++ data/tribes/buildings/productionsites/barbarians/lumberjacks_hut/init.lua 2017-07-03 17:39:38 +0000
257@@ -32,8 +32,7 @@
258 },
259
260 aihints = {
261- forced_after = 180,
262- prohibited_till = 180,
263+ basic_amount = 1,
264 logproducer = true
265 },
266
267
268=== modified file 'data/tribes/buildings/productionsites/barbarians/metal_workshop/init.lua'
269--- data/tribes/buildings/productionsites/barbarians/metal_workshop/init.lua 2016-09-03 14:59:10 +0000
270+++ data/tribes/buildings/productionsites/barbarians/metal_workshop/init.lua 2017-07-03 17:39:38 +0000
271@@ -44,7 +44,7 @@
272 },
273
274 aihints = {
275- forced_after = 400,
276+ basic_amount = 2,
277 prohibited_till = 400
278 },
279
280
281=== modified file 'data/tribes/buildings/productionsites/barbarians/micro_brewery/init.lua'
282--- data/tribes/buildings/productionsites/barbarians/micro_brewery/init.lua 2016-09-03 14:59:10 +0000
283+++ data/tribes/buildings/productionsites/barbarians/micro_brewery/init.lua 2017-07-03 17:39:38 +0000
284@@ -34,7 +34,6 @@
285 },
286
287 aihints = {
288- forced_after = 500
289 },
290
291 working_positions = {
292
293=== modified file 'data/tribes/buildings/productionsites/barbarians/rangers_hut/init.lua'
294--- data/tribes/buildings/productionsites/barbarians/rangers_hut/init.lua 2015-12-11 16:54:00 +0000
295+++ data/tribes/buildings/productionsites/barbarians/rangers_hut/init.lua 2017-07-03 17:39:38 +0000
296@@ -34,7 +34,7 @@
297 aihints = {
298 renews_map_resource = "log",
299 space_consumer = true,
300- prohibited_till = 200
301+ basic_amount = 1
302 },
303
304 working_positions = {
305
306=== modified file 'data/tribes/buildings/productionsites/barbarians/reed_yard/init.lua'
307--- data/tribes/buildings/productionsites/barbarians/reed_yard/init.lua 2015-12-11 16:54:00 +0000
308+++ data/tribes/buildings/productionsites/barbarians/reed_yard/init.lua 2017-07-03 17:39:38 +0000
309@@ -27,7 +27,7 @@
310
311 aihints = {
312 space_consumer = true,
313- forced_after = 250,
314+ basic_amount = 1,
315 prohibited_till = 250
316 },
317
318
319=== modified file 'data/tribes/buildings/productionsites/barbarians/smelting_works/init.lua'
320--- data/tribes/buildings/productionsites/barbarians/smelting_works/init.lua 2016-10-24 10:07:57 +0000
321+++ data/tribes/buildings/productionsites/barbarians/smelting_works/init.lua 2017-07-03 17:39:38 +0000
322@@ -35,6 +35,7 @@
323
324 aihints = {
325 prohibited_till = 400,
326+ basic_amount = 1,
327 very_weak_ai_limit = 1,
328 weak_ai_limit = 2
329 },
330
331=== modified file 'data/tribes/buildings/productionsites/barbarians/tavern/init.lua'
332--- data/tribes/buildings/productionsites/barbarians/tavern/init.lua 2016-10-24 10:10:06 +0000
333+++ data/tribes/buildings/productionsites/barbarians/tavern/init.lua 2017-07-03 17:39:38 +0000
334@@ -38,7 +38,7 @@
335 },
336
337 aihints = {
338- forced_after = 900
339+ basic_amount = 1
340 },
341
342 working_positions = {
343
344=== modified file 'data/tribes/buildings/productionsites/barbarians/well/init.lua'
345--- data/tribes/buildings/productionsites/barbarians/well/init.lua 2016-10-05 05:29:11 +0000
346+++ data/tribes/buildings/productionsites/barbarians/well/init.lua 2017-07-03 17:39:38 +0000
347@@ -33,8 +33,7 @@
348
349 aihints = {
350 mines_water = true,
351- prohibited_till = 800,
352- forced_after = 800
353+ basic_amount = 1
354 },
355
356 working_positions = {
357
358=== modified file 'data/tribes/buildings/productionsites/barbarians/wood_hardener/init.lua'
359--- data/tribes/buildings/productionsites/barbarians/wood_hardener/init.lua 2016-09-03 14:59:10 +0000
360+++ data/tribes/buildings/productionsites/barbarians/wood_hardener/init.lua 2017-07-03 17:39:38 +0000
361@@ -38,7 +38,7 @@
362 },
363
364 aihints = {
365- forced_after = 250,
366+ basic_amount = 1,
367 prohibited_till = 250,
368 very_weak_ai_limit = 1,
369 weak_ai_limit = 2
370
371=== modified file 'data/tribes/buildings/productionsites/empire/armorsmithy/init.lua'
372--- data/tribes/buildings/productionsites/empire/armorsmithy/init.lua 2016-10-30 19:06:01 +0000
373+++ data/tribes/buildings/productionsites/empire/armorsmithy/init.lua 2017-07-03 17:39:38 +0000
374@@ -43,7 +43,6 @@
375
376 aihints = {
377 prohibited_till = 700,
378- forced_after = 900
379 },
380
381 working_positions = {
382
383=== modified file 'data/tribes/buildings/productionsites/empire/bakery/init.lua'
384--- data/tribes/buildings/productionsites/empire/bakery/init.lua 2017-02-10 09:40:17 +0000
385+++ data/tribes/buildings/productionsites/empire/bakery/init.lua 2017-07-03 17:39:38 +0000
386@@ -37,7 +37,7 @@
387
388 aihints = {
389 prohibited_till = 600,
390- forced_after = 700
391+ basic_amount = 1
392 },
393
394 working_positions = {
395
396=== modified file 'data/tribes/buildings/productionsites/empire/barracks/init.lua'
397--- data/tribes/buildings/productionsites/empire/barracks/init.lua 2017-02-10 09:40:17 +0000
398+++ data/tribes/buildings/productionsites/empire/barracks/init.lua 2017-07-03 17:39:38 +0000
399@@ -35,7 +35,6 @@
400 },
401
402 aihints = {
403- forced_after = 1000,
404 very_weak_ai_limit = 1,
405 weak_ai_limit = 3
406 },
407
408=== modified file 'data/tribes/buildings/productionsites/empire/brewery/init.lua'
409--- data/tribes/buildings/productionsites/empire/brewery/init.lua 2016-09-03 14:59:10 +0000
410+++ data/tribes/buildings/productionsites/empire/brewery/init.lua 2017-07-03 17:39:38 +0000
411@@ -31,7 +31,6 @@
412 },
413
414 aihints = {
415- forced_after = 900,
416 prohibited_till = 600,
417 very_weak_ai_limit = 1,
418 weak_ai_limit = 2
419
420=== modified file 'data/tribes/buildings/productionsites/empire/farm/init.lua'
421--- data/tribes/buildings/productionsites/empire/farm/init.lua 2016-01-28 05:24:34 +0000
422+++ data/tribes/buildings/productionsites/empire/farm/init.lua 2017-07-03 17:39:38 +0000
423@@ -30,8 +30,8 @@
424 },
425
426 aihints = {
427- space_consumer = true,
428- forced_after = 900
429+ basic_amount = 1,
430+ space_consumer = true
431 },
432
433 working_positions = {
434
435=== modified file 'data/tribes/buildings/productionsites/empire/inn/init.lua'
436--- data/tribes/buildings/productionsites/empire/inn/init.lua 2016-10-26 19:21:32 +0000
437+++ data/tribes/buildings/productionsites/empire/inn/init.lua 2017-07-03 17:39:38 +0000
438@@ -31,7 +31,6 @@
439 },
440
441 aihints = {
442- forced_after = 900,
443 prohibited_till = 600
444 },
445
446
447=== modified file 'data/tribes/buildings/productionsites/empire/marblemine/init.lua'
448--- data/tribes/buildings/productionsites/empire/marblemine/init.lua 2016-09-03 14:59:10 +0000
449+++ data/tribes/buildings/productionsites/empire/marblemine/init.lua 2017-07-03 17:39:38 +0000
450@@ -38,7 +38,8 @@
451 aihints = {
452 mines = "stones",
453 mines_percent = 50,
454- prohibited_till = 450
455+ prohibited_till = 450,
456+ basic_amount = 1
457 },
458
459 working_positions = {
460
461=== modified file 'data/tribes/buildings/productionsites/empire/piggery/init.lua'
462--- data/tribes/buildings/productionsites/empire/piggery/init.lua 2016-09-03 14:59:10 +0000
463+++ data/tribes/buildings/productionsites/empire/piggery/init.lua 2017-07-03 17:39:38 +0000
464@@ -33,7 +33,6 @@
465 },
466
467 aihints = {
468- forced_after = 1800
469 },
470
471 working_positions = {
472
473=== modified file 'data/tribes/buildings/productionsites/empire/sawmill/init.lua'
474--- data/tribes/buildings/productionsites/empire/sawmill/init.lua 2016-09-03 14:59:10 +0000
475+++ data/tribes/buildings/productionsites/empire/sawmill/init.lua 2017-07-03 17:39:38 +0000
476@@ -31,7 +31,7 @@
477 },
478
479 aihints = {
480- forced_after = 250,
481+ basic_amount = 2,
482 prohibited_till = 250,
483 very_weak_ai_limit = 1,
484 weak_ai_limit = 2
485
486=== modified file 'data/tribes/buildings/productionsites/empire/sheepfarm/init.lua'
487--- data/tribes/buildings/productionsites/empire/sheepfarm/init.lua 2016-09-03 14:59:10 +0000
488+++ data/tribes/buildings/productionsites/empire/sheepfarm/init.lua 2017-07-03 17:39:38 +0000
489@@ -31,7 +31,7 @@
490 },
491
492 aihints = {
493- prohibited_till = 600
494+ prohibited_till = 300
495 },
496
497 working_positions = {
498
499=== modified file 'data/tribes/buildings/productionsites/empire/stonemasons_house/init.lua'
500--- data/tribes/buildings/productionsites/empire/stonemasons_house/init.lua 2016-09-03 14:59:10 +0000
501+++ data/tribes/buildings/productionsites/empire/stonemasons_house/init.lua 2017-07-03 17:39:38 +0000
502@@ -32,7 +32,7 @@
503 },
504
505 aihints = {
506- forced_after = 400,
507+ basic_amount = 1,
508 prohibited_till = 400,
509 very_weak_ai_limit = 1,
510 weak_ai_limit = 2
511
512=== modified file 'data/tribes/buildings/productionsites/empire/tavern/init.lua'
513--- data/tribes/buildings/productionsites/empire/tavern/init.lua 2016-10-26 19:21:32 +0000
514+++ data/tribes/buildings/productionsites/empire/tavern/init.lua 2017-07-03 17:39:38 +0000
515@@ -33,7 +33,7 @@
516 },
517
518 aihints = {
519- forced_after = 900,
520+ basic_amount = 1,
521 prohibited_till = 300
522 },
523
524
525=== modified file 'data/tribes/buildings/productionsites/empire/toolsmithy/init.lua'
526--- data/tribes/buildings/productionsites/empire/toolsmithy/init.lua 2016-09-03 14:59:10 +0000
527+++ data/tribes/buildings/productionsites/empire/toolsmithy/init.lua 2017-07-03 17:39:38 +0000
528@@ -32,7 +32,6 @@
529 },
530
531 aihints = {
532- forced_after = 400,
533 prohibited_till = 400
534 },
535
536
537=== modified file 'data/tribes/buildings/productionsites/empire/vineyard/init.lua'
538--- data/tribes/buildings/productionsites/empire/vineyard/init.lua 2015-12-11 16:54:00 +0000
539+++ data/tribes/buildings/productionsites/empire/vineyard/init.lua 2017-07-03 17:39:38 +0000
540@@ -29,7 +29,7 @@
541
542 aihints = {
543 space_consumer = true,
544- forced_after = 300
545+ basic_amount = 1
546 },
547
548 working_positions = {
549
550=== modified file 'data/tribes/buildings/productionsites/empire/weaponsmithy/init.lua'
551--- data/tribes/buildings/productionsites/empire/weaponsmithy/init.lua 2016-10-30 19:06:01 +0000
552+++ data/tribes/buildings/productionsites/empire/weaponsmithy/init.lua 2017-07-03 17:39:38 +0000
553@@ -39,7 +39,8 @@
554 },
555
556 aihints = {
557- prohibited_till = 900
558+ prohibited_till = 900,
559+ forced_after = 3600
560 },
561
562 working_positions = {
563
564=== modified file 'data/tribes/buildings/productionsites/empire/winery/init.lua'
565--- data/tribes/buildings/productionsites/empire/winery/init.lua 2016-09-03 14:59:10 +0000
566+++ data/tribes/buildings/productionsites/empire/winery/init.lua 2017-07-03 17:39:38 +0000
567@@ -33,10 +33,10 @@
568 },
569
570 aihints = {
571- forced_after = 600,
572 prohibited_till = 600,
573 very_weak_ai_limit = 1,
574- weak_ai_limit = 2
575+ weak_ai_limit = 2,
576+ basic_amount = 1
577 },
578
579 working_positions = {
580
581=== modified file 'data/tribes/buildings/trainingsites/atlanteans/dungeon/init.lua'
582--- data/tribes/buildings/trainingsites/atlanteans/dungeon/init.lua 2017-02-10 09:40:17 +0000
583+++ data/tribes/buildings/trainingsites/atlanteans/dungeon/init.lua 2017-07-03 17:39:38 +0000
584@@ -112,7 +112,6 @@
585 },
586
587 aihints = {
588- trainingsite_type = "advanced",
589 prohibited_till = 1500,
590 very_weak_ai_limit = 0,
591 weak_ai_limit = 1
592
593=== modified file 'data/tribes/buildings/trainingsites/atlanteans/labyrinth/init.lua'
594--- data/tribes/buildings/trainingsites/atlanteans/labyrinth/init.lua 2017-02-10 09:40:17 +0000
595+++ data/tribes/buildings/trainingsites/atlanteans/labyrinth/init.lua 2017-07-03 17:39:38 +0000
596@@ -36,7 +36,6 @@
597 aihints = {
598 prohibited_till = 900,
599 forced_after = 1500,
600- trainingsite_type = "basic",
601 very_weak_ai_limit = 1,
602 weak_ai_limit = 2
603 },
604
605=== modified file 'data/tribes/buildings/trainingsites/barbarians/battlearena/init.lua'
606--- data/tribes/buildings/trainingsites/barbarians/battlearena/init.lua 2017-02-10 09:40:17 +0000
607+++ data/tribes/buildings/trainingsites/barbarians/battlearena/init.lua 2017-07-03 17:39:38 +0000
608@@ -48,7 +48,6 @@
609 aihints = {
610 prohibited_till = 900,
611 forced_after = 1500,
612- trainingsite_type = "basic",
613 very_weak_ai_limit = 1,
614 weak_ai_limit = 3
615 },
616
617=== modified file 'data/tribes/buildings/trainingsites/barbarians/trainingcamp/init.lua'
618--- data/tribes/buildings/trainingsites/barbarians/trainingcamp/init.lua 2017-02-10 09:40:17 +0000
619+++ data/tribes/buildings/trainingsites/barbarians/trainingcamp/init.lua 2017-07-03 17:39:38 +0000
620@@ -41,7 +41,6 @@
621
622 aihints = {
623 prohibited_till = 1500,
624- trainingsite_type = "advanced",
625 very_weak_ai_limit = 0,
626 weak_ai_limit = 1
627 },
628
629=== modified file 'data/tribes/buildings/trainingsites/empire/arena/init.lua'
630--- data/tribes/buildings/trainingsites/empire/arena/init.lua 2017-03-23 07:36:36 +0000
631+++ data/tribes/buildings/trainingsites/empire/arena/init.lua 2017-07-03 17:39:38 +0000
632@@ -37,7 +37,6 @@
633 },
634
635 aihints = {
636- trainingsite_type = "basic",
637 trainingsites_max_percent = 20,
638 prohibited_till = 900,
639 very_weak_ai_limit = 1,
640
641=== modified file 'data/tribes/buildings/trainingsites/empire/colosseum/init.lua'
642--- data/tribes/buildings/trainingsites/empire/colosseum/init.lua 2017-02-10 09:40:17 +0000
643+++ data/tribes/buildings/trainingsites/empire/colosseum/init.lua 2017-07-03 17:39:38 +0000
644@@ -34,7 +34,6 @@
645
646 aihints = {
647 prohibited_till = 1200,
648- trainingsite_type = "basic",
649 forced_after = 1800,
650 very_weak_ai_limit = 1,
651 weak_ai_limit = 2
652
653=== modified file 'data/tribes/buildings/trainingsites/empire/trainingcamp/init.lua'
654--- data/tribes/buildings/trainingsites/empire/trainingcamp/init.lua 2017-02-10 09:40:17 +0000
655+++ data/tribes/buildings/trainingsites/empire/trainingcamp/init.lua 2017-07-03 17:39:38 +0000
656@@ -35,7 +35,6 @@
657
658 aihints = {
659 prohibited_till = 1500,
660- trainingsite_type = "advanced",
661 very_weak_ai_limit = 0,
662 weak_ai_limit = 1
663 },
664
665=== modified file 'data/tribes/empire.lua'
666--- data/tribes/empire.lua 2017-04-30 10:30:02 +0000
667+++ data/tribes/empire.lua 2017-07-03 17:39:38 +0000
668@@ -326,4 +326,9 @@
669 headquarters = "empire_headquarters",
670 port = "empire_port",
671 barracks = "empire_barracks",
672+ bakery = "empire_bakery",
673+ ironore = "iron_ore",
674+ rawlog = "log",
675+ refinedlog = "planks",
676+ granite = "granite",
677 }
678
679=== modified file 'src/ai/ai_help_structs.cc'
680--- src/ai/ai_help_structs.cc 2017-01-25 18:55:59 +0000
681+++ src/ai/ai_help_structs.cc 2017-07-03 17:39:38 +0000
682@@ -20,6 +20,7 @@
683 #include "ai/ai_help_structs.h"
684
685 #include "base/macros.h"
686+#include "base/time_string.h"
687 #include "logic/map.h"
688 #include "logic/player.h"
689
690@@ -27,6 +28,8 @@
691 constexpr int kRoadNotFound = -1000;
692 constexpr int kShortcutWithinSameEconomy = 1000;
693 constexpr int kRoadToDifferentEconomy = 10000;
694+constexpr int kUpperDefaultMutationLimit = 200;
695+constexpr int kLowerDefaultMutationLimit = 150;
696
697 namespace Widelands {
698
699@@ -102,15 +105,12 @@
700 // When looking for unowned terrain to acquire, we are actually
701 // only interested in fields we can walk on.
702 // Fields should either be completely unowned or owned by an opposing player
703-FindNodeUnowned::FindNodeUnowned(Player* p, Game& g, bool oe)
704- : player(p), game(g), only_enemies(oe) {
705+FindEnemyNodeWalkable::FindEnemyNodeWalkable(Player* p, Game& g) : player(p), game(g) {
706 }
707
708-bool FindNodeUnowned::accept(const Map&, const FCoords& fc) const {
709- return (fc.field->nodecaps() & MOVECAPS_WALK) &&
710- ((fc.field->get_owned_by() == 0) ||
711- player->is_hostile(*game.get_player(fc.field->get_owned_by()))) &&
712- (!only_enemies || (fc.field->get_owned_by() != 0));
713+bool FindEnemyNodeWalkable::accept(const Map&, const FCoords& fc) const {
714+ return ((fc.field->nodecaps() & MOVECAPS_WALK) && (fc.field->get_owned_by() > 0) &&
715+ player->is_hostile(*game.get_player(fc.field->get_owned_by())));
716 }
717
718 // Sometimes we need to know how many nodes our allies owns
719@@ -127,11 +127,23 @@
720 // When looking for unowned terrain to acquire, we must
721 // pay speciall attention to fields where mines can be built.
722 // Fields should be completely unowned
723-FindNodeUnownedMineable::FindNodeUnownedMineable(Player* p, Game& g) : player(p), game(g) {
724+FindNodeUnownedMineable::FindNodeUnownedMineable(Player* p, Game& g, int32_t t)
725+ : player(p), game(g), ore_type(t) {
726 }
727
728 bool FindNodeUnownedMineable::accept(const Map&, const FCoords& fc) const {
729- return (fc.field->nodecaps() & BUILDCAPS_MINE) && (fc.field->get_owned_by() == 0);
730+ if (ore_type == INVALID_INDEX) {
731+ return (fc.field->nodecaps() & BUILDCAPS_MINE) && (fc.field->get_owned_by() == neutral());
732+ }
733+ return (fc.field->nodecaps() & BUILDCAPS_MINE) && (fc.field->get_owned_by() == neutral()) &&
734+ fc.field->get_resources() == ore_type;
735+}
736+
737+FindNodeUnownedBuildable::FindNodeUnownedBuildable(Player* p, Game& g) : player(p), game(g) {
738+}
739+
740+bool FindNodeUnownedBuildable::accept(const Map&, const FCoords& fc) const {
741+ return (fc.field->nodecaps() & BUILDCAPS_SIZEMASK) && (fc.field->get_owned_by() == neutral());
742 }
743
744 // Unowned but walkable fields nearby
745@@ -139,7 +151,7 @@
746 }
747
748 bool FindNodeUnownedWalkable::accept(const Map&, const FCoords& fc) const {
749- return (fc.field->nodecaps() & MOVECAPS_WALK) && (fc.field->get_owned_by() == 0);
750+ return (fc.field->nodecaps() & MOVECAPS_WALK) && (fc.field->get_owned_by() == neutral());
751 }
752
753 // Looking only for mines-capable fields nearby
754@@ -181,14 +193,35 @@
755 : flag(&f), cost(c), distance(d) {
756 }
757
758+EventTimeQueue::EventTimeQueue() {
759+}
760+
761+void EventTimeQueue::push(const uint32_t production_time) {
762+ queue.push(production_time);
763+}
764+
765+uint32_t EventTimeQueue::count(const uint32_t current_time) {
766+ strip_old(current_time);
767+ return queue.size();
768+}
769+
770+void EventTimeQueue::strip_old(const uint32_t current_time) {
771+ while (!queue.empty() && queue.front() < current_time - duration_) {
772+ queue.pop();
773+ }
774+}
775+
776 BuildableField::BuildableField(const Widelands::FCoords& fc)
777 : coords(fc),
778 field_info_expiration(20000),
779 preferred(false),
780 enemy_nearby(0),
781+ enemy_accessible_(false),
782+ enemy_wh_nearby(false),
783 unowned_land_nearby(0),
784 near_border(false),
785 unowned_mines_spots_nearby(0),
786+ unowned_iron_mines_nearby(false),
787 trees_nearby(0),
788 // explanation of starting values
789 // this is done to save some work for AI (CPU utilization)
790@@ -209,15 +242,22 @@
791 area_military_capacity(0),
792 military_loneliness(1000),
793 military_in_constr_nearby(0),
794- area_military_presence(0),
795+ own_military_presence(0),
796+ enemy_military_presence(0),
797+ ally_military_presence(0),
798 military_stationed(0),
799 unconnected_nearby(false),
800 military_unstationed(0),
801- is_portspace(false),
802+ own_non_military_nearby(0),
803+ is_portspace(Widelands::ExtendedBool::kUnset),
804 port_nearby(false),
805 portspace_nearby(Widelands::ExtendedBool::kUnset),
806 max_buildcap_nearby(0),
807- last_resources_check_time(0) {
808+ last_resources_check_time(0),
809+ military_score_(0),
810+ inland(false),
811+ local_soldier_capacity(0),
812+ is_militarysite(false) {
813 }
814
815 int32_t BuildableField::own_military_sites_nearby_() {
816@@ -240,6 +280,19 @@
817 return cnt_built + cnt_under_construction;
818 }
819
820+bool BuildingObserver::is(BuildingAttribute attribute) const {
821+ return is_what.count(attribute) == 1;
822+}
823+
824+void BuildingObserver::set_is(const BuildingAttribute attribute) {
825+ is_what.insert(attribute);
826+}
827+
828+void BuildingObserver::unset_is(const BuildingAttribute attribute) {
829+ is_what.erase(attribute);
830+ assert(!is(attribute));
831+}
832+
833 Widelands::AiModeBuildings BuildingObserver::aimode_limit_status() {
834 if (total_count() > cnt_limit_by_aimode) {
835 return Widelands::AiModeBuildings::kLimitExceeded;
836@@ -250,7 +303,7 @@
837 }
838 }
839 bool BuildingObserver::buildable(Widelands::Player& p) {
840- return is_buildable && p.is_building_type_allowed(id);
841+ return is(BuildingAttribute::kBuildable) && p.is_building_type_allowed(id);
842 }
843
844 // Computer player does not get notification messages about enemy militarysites
845@@ -264,16 +317,769 @@
846 attack_soldiers_strength(0),
847 defenders_strength(0),
848 stationed_soldiers(0),
849- last_time_attackable(std::numeric_limits<uint32_t>::max()),
850+ last_time_seen(0),
851 last_tested(0),
852 score(0),
853 mines_nearby(Widelands::ExtendedBool::kUnset),
854- no_attack_counter(0) {
855+ last_time_attacked(0),
856+ attack_counter(0) {
857 }
858
859 // as all mines have 3 levels, AI does not know total count of mines per mined material
860 // so this observer will be used for this
861-MineTypesObserver::MineTypesObserver() : in_construction(0), finished(0) {
862+MineTypesObserver::MineTypesObserver()
863+ : in_construction(0), finished(0), is_critical(false), unoccupied(0) {
864+}
865+
866+ExpansionType::ExpansionType() {
867+ type = ExpansionMode::kResources;
868+}
869+
870+void ExpansionType::set_expantion_type(const ExpansionMode etype) {
871+ type = etype;
872+}
873+
874+ManagementData::ManagementData() {
875+ score = 1;
876+ primary_parent = 255;
877+ next_neuron_id = 0;
878+ performance_change = 0;
879+}
880+
881+// Initialization of neuron. Neuron is defined by curve (type) and weight [-kWeightRange, kWeightRange]
882+// third argument is just id
883+Neuron::Neuron(int8_t w, uint8_t f, uint16_t i) : weight(w), type(f), id(i) {
884+ assert(type < neuron_curves.size());
885+ assert(weight >= -kNeuronWeightLimit && weight <= kNeuronWeightLimit);
886+ recalculate();
887+}
888+
889+// Weight, or rather value in range [-kWeightRange, kWeightRange]. Automatically adjusts the weight to the range in case of
890+// overflow.
891+void Neuron::set_weight(int8_t w) {
892+ weight = Neuron::clip_weight_to_range(w);
893+}
894+
895+// Neuron stores calculated values in an array of size 21.
896+// This has to be recalculated when the weight or curve type change
897+void Neuron::recalculate() {
898+ assert(neuron_curves.size() > type);
899+ for (uint8_t i = 0; i <= kNeuronMaxPosition; i += 1) {
900+ results[i] = weight * neuron_curves[type][i] / kNeuronWeightLimit;
901+ }
902+}
903+
904+// The Following two functions return Neuron values on position
905+int8_t Neuron::get_result(const size_t pos) {
906+ assert(pos <= kNeuronMaxPosition);
907+ return results[pos];
908+}
909+
910+// get value corresponding to input in range 0-20, if you are out of range
911+// the input will be cropped
912+int8_t Neuron::get_result_safe(int32_t pos, const bool absolute) {
913+ // pos has to be normalized into range 0 - 20(kNeuronMaxPosition)
914+ pos = std::max(0, std::min(static_cast<int>(kNeuronMaxPosition), pos));
915+
916+ assert(pos <= static_cast<int32_t>(kNeuronMaxPosition));
917+ assert(pos >= 0);
918+ assert(results[pos] >= -kNeuronWeightLimit && results[pos] <= kNeuronWeightLimit);
919+
920+ if (absolute) {
921+ return std::abs(results[pos]);
922+ }
923+ return results[pos];
924+}
925+
926+// Setting the type of curve
927+void Neuron::set_type(uint8_t new_type) {
928+ assert(new_type < neuron_curves.size());
929+ type = new_type;
930+}
931+
932+// FNeuron is basically a single uint32_t integer, and the AI can query every bit of that uint32_t
933+FNeuron::FNeuron(uint32_t c, uint16_t i) {
934+ core = c;
935+ id = i;
936+}
937+
938+// Returning a result depending on combinations of 5 bools
939+// Bools are completely anonymous, but can present any yes/no inputs, e.g. imagine the AI that is
940+// to figure out if it should attack from a militarysite. The inputs can be:
941+// bool1 - are we stronger than the enemy?
942+// bool2 - do we have a basic economy established?
943+// bool3 - do we have local predominance?
944+// bool4 - has our strength grown during the last 60 minutes?
945+// bool5 - are there mines in the vicinity?
946+// These five bools can create 32 combinations = yes/no answers.
947+// In fact this can be perceived as a complicated if..then structure, but one that can
948+// adjust automatically as a part of training.
949+// Or rather it is a 5-dimensional table with 2 columns in every dimension :)
950+// In fact this concept if very demanding for training so we don't use it much
951+bool FNeuron::get_result(
952+ const bool bool1, const bool bool2, const bool bool3, const bool bool4, const bool bool5) {
953+ return core.test(bool1 * 16 + bool2 * 8 + bool3 * 4 + bool4 * 2 + bool5);
954+}
955+
956+// Returning bool on a position
957+bool FNeuron::get_position(const uint8_t pos) {
958+ assert(pos < kFNeuronBitSize);
959+ return core.test(pos);
960+}
961+
962+// Returning numerical value of FNeuron. Used for saving and priting into log
963+uint32_t FNeuron::get_int() {
964+ return core.to_ulong();
965+}
966+
967+// This is basically a mutation of FNeuron
968+void FNeuron::flip_bit(const uint8_t pos) {
969+ assert(pos < kFNeuronBitSize);
970+ core.flip(pos);
971+}
972+
973+// Shifting the value in range -kWeightRange to kWeightRange, if zero_align is true, it is now allowed to shift
974+// from negative to positive and vice versa, 0 must be used.
975+int8_t ManagementData::shift_weight_value(const int8_t old_value, const bool aggressive) {
976+
977+ int16_t halfVArRange = 50;
978+ if (aggressive) {
979+ halfVArRange = 200;
980+ }
981+
982+ const int16_t upper_limit = std::min<int16_t>(old_value + halfVArRange, kNeuronWeightLimit);
983+ const int16_t bottom_limit = std::max<int16_t>(old_value - halfVArRange, -kNeuronWeightLimit);
984+ int16_t new_value = bottom_limit + std::rand() % (upper_limit - bottom_limit + 1);
985+
986+ if (!aggressive && ((old_value > 0 && new_value < 0) || (old_value < 0 && new_value > 0))) {
987+ new_value = 0;
988+ }
989+
990+ new_value = Neuron::clip_weight_to_range(new_value);
991+ return static_cast<int8_t>(new_value);
992+}
993+
994+// Used to score performance of AI
995+// Should be disabled for "production"
996+void ManagementData::review(const uint32_t gametime,
997+ PlayerNumber pn,
998+ const uint32_t land,
999+ const uint32_t max_e_land,
1000+ const uint32_t old_land,
1001+ const uint16_t attackers,
1002+ const int16_t trained_soldiers,
1003+ const int16_t latest_attackers,
1004+ const uint16_t conq_ws) {
1005+
1006+ const int16_t main_bonus =
1007+ ((static_cast<int32_t>(land - old_land) > 0 && land > max_e_land * 5 / 6 && attackers > 0 &&
1008+ trained_soldiers > 0 && latest_attackers > 0) ?
1009+ kBonus :
1010+ 0);
1011+
1012+ const int16_t land_delta_bonus = static_cast<int16_t>(land - old_land) * kLandDeltaMultiplier;
1013+
1014+ score = land / kCurrentLandDivider + land_delta_bonus + main_bonus +
1015+ attackers * kAttackersMultiplier + ((attackers > 0) ? kAttackBonus : -kAttackBonus) +
1016+ trained_soldiers * kTrainedSoldiersScore + +kConqueredWhBonus + conq_ws;
1017+
1018+ log(" %2d %s: reviewing AI mngm. data, sc: %5d Pr.p: %d (l: %4d / %4d / %4d, "
1019+ "at:%4d(%3d), ts:%4d(%2d), ConqWH:%2d)\n",
1020+ pn, gamestring_with_leading_zeros(gametime), score, primary_parent,
1021+ land / kCurrentLandDivider, main_bonus, land_delta_bonus,
1022+ attackers * kAttackersMultiplier, latest_attackers,
1023+ trained_soldiers * kTrainedSoldiersScore, trained_soldiers, conq_ws);
1024+
1025+ if (score < -10000 || score > 30000) {
1026+ log(
1027+ "%2d %s: reviewing AI mngm. data, score too extreme: %4d\n",
1028+ pn, gamestring_with_leading_zeros(gametime), score);
1029+ }
1030+ assert(score > -10000 && score < 100000);
1031+}
1032+
1033+// Here we generate new AI DNA (no mutation yet) and push them into persistent data
1034+// this can cause inconsistency between local and persistent
1035+void ManagementData::new_dna_for_persistent(const uint8_t pn, const Widelands::AiType type) {
1036+
1037+ ai_type = type;
1038+
1039+ log ("%2d: DNA initialization... \n", pn);
1040+
1041+ // AutoSCore_AIDNA_1
1042+ const std::vector<int16_t> AI_initial_military_numbers_A =
1043+ { 18, -42, 21, 59, -7, -78, -22, 81, 48, 0, // AutoContent_01_AIDNA_1
1044+ -82, 45, -96, 16, 70, -30, 33, 79, -78, -42, // AutoContent_02_AIDNA_1
1045+ 79, -16, 34, 46, -22, 0, 45, -29, 53, 51, // AutoContent_03_AIDNA_1
1046+ 24, 47, -27, 80, -86, 46, -63, -47, 20, -63, // AutoContent_04_AIDNA_1
1047+ 78, 51, -11, -77, 20, 38, 6, 37, -64, -41, // AutoContent_05_AIDNA_1
1048+ -3, -55, 62, 0, 64, -92, 4, -89, 71, -18, // AutoContent_06_AIDNA_1
1049+ -87, 56, 17, -34, -69, 24, -57, 84, 40, -51, // AutoContent_07_AIDNA_1
1050+ 0, 44, 0, -2, -11, -4, -96, -35, -29, -12, // AutoContent_08_AIDNA_1
1051+ 71, -98, -25, 50, 97, 74, 0, 65, -60, 23, // AutoContent_09_AIDNA_1
1052+ 38, 53, 74, 0, -43, 27, 32, 37, -24, -65, // AutoContent_10_AIDNA_1
1053+ 16, -42, 19, -94, -28, 83, -55, -63, 16, -41, // AutoContent_11_AIDNA_1
1054+ 28, -3, 0, -87, 32, 5, 4, 6, -20, 62, // AutoContent_12_AIDNA_1
1055+ 85, 0, 58, 48, -80, -20, -49, 71, 60, 8, // AutoContent_13_AIDNA_1
1056+ -52, 59, 100, -74, 0, -36, -9, 80, 41, -67, // AutoContent_14_AIDNA_1
1057+ 0, 15, -96, -51, -21, 11, -27, -30, 76, -47 // AutoContent_15_AIDNA_1
1058+ }
1059+ ;
1060+
1061+ assert(kMagicNumbersSize == AI_initial_military_numbers_A.size());
1062+
1063+ const std::vector<int8_t> input_weights_A =
1064+ // 0 1 2 3 4 5 6 7 8 9
1065+ { 48, -85, -26, 47, -93, -22, -91, 84, 24, -12, // AutoContent_16_AIDNA_1
1066+ 98, -59, -89, 76, 81, 95, -91, -90, -56, -15, // AutoContent_17_AIDNA_1
1067+ -65, -18, 6, -65, 41, 38, 47, -31, 79, 23, // AutoContent_18_AIDNA_1
1068+ 16, 25, -59, 0, -38, -85, -60, -42, 0, -70, // AutoContent_19_AIDNA_1
1069+ -78, -86, -87, -55, 92, -63, -21, -76, 4, 87, // AutoContent_20_AIDNA_1
1070+ 58, -40, 71, -90, -72, 0, -47, -94, -15, 66, // AutoContent_21_AIDNA_1
1071+ -32, 9, 67, -47, -44, -76, -53, 57, -31, -47, // AutoContent_22_AIDNA_1
1072+ 0, -28, -16, 48, 41, -45, 36, -8, 51, -49 // AutoContent_23_AIDNA_1
1073+ }
1074+ ;
1075+ const std::vector<int8_t> input_func_A =
1076+ { 1, 0, 1, 2, 2, 1, 0, 0, 0, 0, // AutoContent_24_AIDNA_1
1077+ 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, // AutoContent_25_AIDNA_1
1078+ 2, 2, 0, 2, 2, 1, 0, 1, 0, 2, // AutoContent_26_AIDNA_1
1079+ 1, 2, 2, 1, 2, 1, 0, 1, 2, 1, // AutoContent_27_AIDNA_1
1080+ 2, 1, 2, 0, 0, 1, 2, 0, 0, 0, // AutoContent_28_AIDNA_1
1081+ 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, // AutoContent_29_AIDNA_1
1082+ 2, 0, 1, 2, 0, 2, 2, 2, 1, 0, // AutoContent_30_AIDNA_1
1083+ 2, 1, 2, 2, 1, 1, 0, 2, 2, 1 // AutoContent_31_AIDNA_1
1084+ }
1085+ ;
1086+ assert(kNeuronPoolSize == input_func_A.size());
1087+ assert(kNeuronPoolSize == input_weights_A.size());
1088+
1089+ const std::vector<uint32_t> f_neurons_A =
1090+ { 471130763, 1322756799, 1148682382, 2713953719, 194460207, 18113635, 2947490886, 3275944172, 3438582086, 856208494, // AutoContent_32_AIDNA_1
1091+ 1326156684, 986431571, 3465514749, 1962749574, 1523585333, 1376482111, 1223335901, 2962231598, 657710612, 578259960, // AutoContent_33_AIDNA_1
1092+ 1271222963, 2915927856, 3396846486, 1743568169, 2679432920, 410834609, 134904175, 2968201710, 2132567223, 2248461478, // AutoContent_34_AIDNA_1
1093+ 161963959, 3295327519, 670058472, 2013696856, 3608400883, 496651103, 733137541, 2952748738, 3307293853, 3886490843, // AutoContent_35_AIDNA_1
1094+ 3233788172, 715230539, 2583635732, 4271028953, 1217674278, 4043323645, 1857109651, 2181897047, 2825979187, 3081298269, // AutoContent_36_AIDNA_1
1095+ 1277901018, 1255642150, 2384261818, 2866704864, 755617465, 835768208, 1394358417, 4012239945, 2601238115, 3933467106 // AutoContent_37_AIDNA_1
1096+ };
1097+ assert(kFNeuronPoolSize == f_neurons_A.size());
1098+
1099+
1100+ // AutoSCore_AIDNA_2
1101+ const std::vector<int16_t> AI_initial_military_numbers_B =
1102+ { 18, -42, 21, 59, -7, -78, -22, 81, 48, 0, // AutoContent_01_AIDNA_2
1103+ -55, 45, -96, 16, 70, -30, 33, 79, -78, -42, // AutoContent_02_AIDNA_2
1104+ 79, -16, 34, 46, -22, 0, 45, -33, 53, 51, // AutoContent_03_AIDNA_2
1105+ 49, 47, -27, 80, -86, 46, -63, -47, 20, -63, // AutoContent_04_AIDNA_2
1106+ 78, 51, 23, -77, 20, 38, 6, 37, -64, -41, // AutoContent_05_AIDNA_2
1107+ -3, -55, 62, 0, 64, -92, 4, -89, 71, -18, // AutoContent_06_AIDNA_2
1108+ -87, 56, 17, -34, -69, 24, -57, 84, 40, -51, // AutoContent_07_AIDNA_2
1109+ 0, 44, 0, -2, -11, -4, -96, -35, -29, -12, // AutoContent_08_AIDNA_2
1110+ 71, -98, -25, 50, 97, 74, 0, 65, -60, 23, // AutoContent_09_AIDNA_2
1111+ 38, 53, 74, 0, -15, 27, 32, 37, -24, -65, // AutoContent_10_AIDNA_2
1112+ 16, -42, 19, -94, -28, 83, -55, -63, 16, -41, // AutoContent_11_AIDNA_2
1113+ 28, -3, 0, -87, 32, 5, 4, 6, -20, 62, // AutoContent_12_AIDNA_2
1114+ 85, 0, 58, 48, -80, -20, -49, 71, 60, 8, // AutoContent_13_AIDNA_2
1115+ -52, 59, 100, -74, 0, -36, -9, 80, 41, -67, // AutoContent_14_AIDNA_2
1116+ 0, 15, -96, -51, -21, 11, -27, -30, 76, -47 // AutoContent_15_AIDNA_2
1117+ }
1118+ ;
1119+ assert(kMagicNumbersSize == AI_initial_military_numbers_B.size());
1120+
1121+ const std::vector<int8_t> input_weights_B =
1122+ { 48, -85, -26, 47, -93, -22, -91, 84, 50, -12, // AutoContent_16_AIDNA_2
1123+ 98, -59, -89, 76, 81, 95, -91, -90, -56, -15, // AutoContent_17_AIDNA_2
1124+ -65, -18, 6, -65, 41, 38, 47, -31, 79, 23, // AutoContent_18_AIDNA_2
1125+ 16, 25, -59, 0, -38, -85, -60, -42, 0, -70, // AutoContent_19_AIDNA_2
1126+ -78, -86, -87, -55, 92, -63, -21, -76, 4, 87, // AutoContent_20_AIDNA_2
1127+ 58, -40, 71, -90, -72, 0, -47, -94, -15, 66, // AutoContent_21_AIDNA_2
1128+ -32, 9, 67, -47, -44, -76, -53, 57, -31, -47, // AutoContent_22_AIDNA_2
1129+ 0, -28, -16, 48, 41, -45, 36, -8, 51, -49 // AutoContent_23_AIDNA_2
1130+}
1131+ ;
1132+
1133+ const std::vector<int8_t> input_func_B =
1134+ { 1, 0, 1, 2, 2, 1, 0, 0, 0, 0, // AutoContent_24_AIDNA_2
1135+ 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, // AutoContent_25_AIDNA_2
1136+ 2, 2, 0, 2, 2, 1, 0, 1, 0, 2, // AutoContent_26_AIDNA_2
1137+ 1, 2, 2, 1, 2, 1, 0, 1, 2, 1, // AutoContent_27_AIDNA_2
1138+ 2, 1, 2, 0, 0, 1, 2, 0, 0, 0, // AutoContent_28_AIDNA_2
1139+ 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, // AutoContent_29_AIDNA_2
1140+ 2, 0, 1, 2, 0, 2, 2, 2, 1, 0, // AutoContent_30_AIDNA_2
1141+ 2, 1, 2, 2, 1, 1, 0, 2, 2, 1 // AutoContent_31_AIDNA_2
1142+}
1143+ ;
1144+ assert(kNeuronPoolSize == input_func_B.size());
1145+ assert(kNeuronPoolSize == input_weights_B.size());
1146+
1147+
1148+ const std::vector<uint32_t> f_neurons_B =
1149+ { 471130763, 1322756799, 1148682382, 2713953719, 194460207, 18113635, 2947490886, 3275944172, 3438582022, 855676014, // AutoContent_32_AIDNA_2
1150+ 1326156684, 986431571, 3465514749, 1962749574, 1523650869, 1376482111, 2347409353, 2962227502, 657710612, 578259960, // AutoContent_33_AIDNA_2
1151+ 1271222963, 2915927856, 3396846486, 1743568169, 2679432920, 410834609, 134904175, 2968201710, 2132567223, 2248461478, // AutoContent_34_AIDNA_2
1152+ 161963959, 3295327519, 670058472, 2013696856, 4145271795, 496651103, 733211269, 2952748738, 1159810205, 3886490843, // AutoContent_35_AIDNA_2
1153+ 3225153820, 732007755, 2583635732, 4271028825, 1217674278, 4043323645, 1857109651, 2249005911, 2825979187, 3081298269, // AutoContent_36_AIDNA_2
1154+ 1277901018, 1255642150, 2384261818, 2866704864, 755617465, 835768208, 1394096273, 4012239945, 2609626723, 3932418530 // AutoContent_37_AIDNA_2
1155+ };
1156+ assert(kFNeuronPoolSize == f_neurons_B.size());
1157+
1158+
1159+ // AutoSCore_AIDNA_3
1160+ const std::vector<int16_t> AI_initial_military_numbers_C =
1161+ { 18, -42, 63, 82, -7, -78, -22, 81, 48, 0, // AutoContent_01_AIDNA_3
1162+ -55, 55, -96, 16, 70, -30, 33, 79, -78, -42, // AutoContent_02_AIDNA_3
1163+ 79, -16, 34, 46, 0, 0, 0, -29, 53, 51, // AutoContent_03_AIDNA_3
1164+ 24, 47, -27, 42, -86, 46, -63, -47, 20, -63, // AutoContent_04_AIDNA_3
1165+ 78, 31, 98, -73, 20, 38, 44, 37, -64, -41, // AutoContent_05_AIDNA_3
1166+ -3, -55, 67, 0, 64, -92, 4, -89, 71, -60, // AutoContent_06_AIDNA_3
1167+ -87, 56, 17, -34, -69, 24, -57, 50, 40, -51, // AutoContent_07_AIDNA_3
1168+ -34, 44, 0, -2, -11, -4, -96, -35, -29, -12, // AutoContent_08_AIDNA_3
1169+ 68, -98, -25, 50, 97, 74, 0, 65, -60, 23, // AutoContent_09_AIDNA_3
1170+ 38, 53, 74, 0, -15, 27, 32, 61, -24, -65, // AutoContent_10_AIDNA_3
1171+ 16, -42, 19, -94, -65, 83, -55, -63, 16, -41, // AutoContent_11_AIDNA_3
1172+ 74, -3, 0, -87, 32, 5, 4, 6, -70, 62, // AutoContent_12_AIDNA_3
1173+ 85, 0, 58, 48, -80, -20, -49, 71, 60, 8, // AutoContent_13_AIDNA_3
1174+ -52, 30, 69, -74, 0, -36, -57, 80, 64, -67, // AutoContent_14_AIDNA_3
1175+ 0, 15, -96, -51, -21, 0, -33, -30, 76, -47 // AutoContent_15_AIDNA_3
1176+ }
1177+
1178+ ;
1179+
1180+ assert(kMagicNumbersSize == AI_initial_military_numbers_C.size());
1181+
1182+ const std::vector<int8_t> input_weights_C =
1183+ { 48, -85, -26, 47, -65, -19, -91, 84, 92, -12, // AutoContent_16_AIDNA_3
1184+ 78, -59, -89, 76, 81, 95, -91, -90, -56, -15, // AutoContent_17_AIDNA_3
1185+ -65, -18, 6, -65, 17, 38, 47, -45, 79, 23, // AutoContent_18_AIDNA_3
1186+ 16, 25, -59, 36, -38, -85, -60, -42, 0, -70, // AutoContent_19_AIDNA_3
1187+ -78, -86, -87, -55, 92, -63, -21, -76, 4, 87, // AutoContent_20_AIDNA_3
1188+ 58, -40, 71, -90, -72, 0, -47, -94, -15, 95, // AutoContent_21_AIDNA_3
1189+ -32, 9, 67, -47, -44, -76, -53, 57, -31, -47, // AutoContent_22_AIDNA_3
1190+ 0, -28, -16, 48, 41, -45, 36, -8, 63, -49 // AutoContent_23_AIDNA_3
1191+ }
1192+ ;
1193+ const std::vector<int8_t> input_func_C =
1194+ { 1, 0, 1, 2, 2, 1, 0, 0, 0, 0, // AutoContent_24_AIDNA_3
1195+ 2, 1, 2, 2, 1, 1, 2, 2, 2, 1, // AutoContent_25_AIDNA_3
1196+ 2, 2, 0, 2, 2, 1, 0, 1, 0, 2, // AutoContent_26_AIDNA_3
1197+ 1, 2, 2, 1, 2, 1, 0, 1, 2, 1, // AutoContent_27_AIDNA_3
1198+ 2, 1, 2, 0, 0, 1, 2, 0, 0, 0, // AutoContent_28_AIDNA_3
1199+ 1, 0, 0, 1, 0, 2, 0, 0, 1, 1, // AutoContent_29_AIDNA_3
1200+ 2, 0, 1, 2, 0, 2, 2, 2, 1, 0, // AutoContent_30_AIDNA_3
1201+ 2, 1, 2, 2, 1, 1, 0, 2, 2, 1 // AutoContent_31_AIDNA_3
1202+ }
1203+ ;
1204+ assert(kNeuronPoolSize == input_func_C.size());
1205+ assert(kNeuronPoolSize == input_weights_C.size());
1206+
1207+ const std::vector<uint32_t> f_neurons_C =
1208+ { 202828425, 1322625717, 1148682382, 2244191679, 194456367, 26493027, 2947491014, 3410163948, 3438582086, 856208494, // AutoContent_32_AIDNA_3
1209+ 1259048860, 986431571, 3457662708, 1954360966, 1523650869, 1376351039, 1223335901, 2997887274, 657657361, 645368312, // AutoContent_33_AIDNA_3
1210+ 1271218867, 2915927856, 3933717470, 2011479337, 2645864080, 408737457, 2282387823, 2968205806, 1597793527, 2248461494, // AutoContent_34_AIDNA_3
1211+ 161963959, 1147843615, 670058474, 2013712728, 4145271795, 496651102, 724812677, 2952748738, 1629834973, 3819381979, // AutoContent_35_AIDNA_3
1212+ 3233788172, 732007755, 2583635734, 4271028825, 1217670182, 4043323645, 1857109659, 2249005911, 2834367795, 3072909661, // AutoContent_36_AIDNA_3
1213+ 1278949528, 1792513063, 2384261690, 2732487136, 755615385, 835767184, 1393834641, 4012239945, 2601238115, 2859725290 // AutoContent_37_AIDNA_3
1214+ };
1215+ assert(kFNeuronPoolSize == f_neurons_C.size());
1216+
1217+
1218+ // AutoSCore_AIDNA_4
1219+ const std::vector<int16_t> AI_initial_military_numbers_D =
1220+ { 18, -42, 21, 59, -7, -78, -22, 81, 48, 0, // AutoContent_01_AIDNA_4
1221+ -55, 45, -96, 16, 70, -30, 33, 79, -78, -42, // AutoContent_02_AIDNA_4
1222+ 79, -16, 34, 46, -22, 0, 45, -29, 53, 51, // AutoContent_03_AIDNA_4
1223+ 24, 47, -27, 80, -86, 46, -63, -47, 20, -63, // AutoContent_04_AIDNA_4
1224+ 78, 51, -25, -77, 20, 38, 6, 37, -64, -41, // AutoContent_05_AIDNA_4
1225+ -3, -55, 62, 0, 64, -92, 4, -89, 71, -18, // AutoContent_06_AIDNA_4
1226+ -87, 56, 17, -34, -69, 24, -57, 84, 40, -51, // AutoContent_07_AIDNA_4
1227+ 0, 44, 0, -2, -11, -4, -96, -35, -29, -12, // AutoContent_08_AIDNA_4
1228+ 71, -98, -25, 50, 97, 74, 0, 65, -60, 23, // AutoContent_09_AIDNA_4
1229+ 38, 53, 74, 0, -15, 27, 32, 37, -24, -65, // AutoContent_10_AIDNA_4
1230+ 16, -61, 19, -94, -28, 83, -55, -63, 16, -41, // AutoContent_11_AIDNA_4
1231+ 28, -3, 0, -87, 32, 5, 4, 6, -20, 62, // AutoContent_12_AIDNA_4
1232+ 85, 0, 58, 48, -80, -20, -49, 71, 60, 8, // AutoContent_13_AIDNA_4
1233+ -52, 59, 100, -74, 0, -36, -9, 80, 41, -67, // AutoContent_14_AIDNA_4
1234+ 0, 15, -96, -51, -21, 11, -27, -30, 76, -47 // AutoContent_15_AIDNA_4
1235+ }
1236+ ;
1237+ assert(kMagicNumbersSize == AI_initial_military_numbers_D.size());
1238+
1239+ const std::vector<int8_t> input_weights_D =
1240+ { 48, -85, -26, 47, -93, -22, -91, 84, 50, -12, // AutoContent_16_AIDNA_4
1241+ 98, -59, -89, 76, 81, 95, -91, -90, -56, -15, // AutoContent_17_AIDNA_4
1242+ -65, -18, 6, -65, 41, 38, 47, -31, 79, 23, // AutoContent_18_AIDNA_4
1243+ 16, 25, -59, 0, -38, -85, -60, -42, 0, -70, // AutoContent_19_AIDNA_4
1244+ -78, -86, -87, -55, 92, -63, -21, -76, 4, 87, // AutoContent_20_AIDNA_4
1245+ 58, -40, 71, -90, -72, 0, -47, -94, -15, 66, // AutoContent_21_AIDNA_4
1246+ -32, 9, 67, -47, -44, -76, -53, 57, -31, -47, // AutoContent_22_AIDNA_4
1247+ 0, -28, -16, 48, 41, -45, 36, -8, 63, -49 // AutoContent_23_AIDNA_4
1248+ }
1249+ ;
1250+
1251+ const std::vector<int8_t> input_func_D =
1252+ { 1, 0, 1, 2, 2, 1, 0, 0, 0, 0, // AutoContent_24_AIDNA_4
1253+ 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, // AutoContent_25_AIDNA_4
1254+ 2, 2, 0, 2, 2, 1, 0, 1, 0, 2, // AutoContent_26_AIDNA_4
1255+ 1, 2, 2, 1, 2, 1, 0, 1, 2, 1, // AutoContent_27_AIDNA_4
1256+ 2, 1, 2, 0, 0, 1, 2, 0, 0, 0, // AutoContent_28_AIDNA_4
1257+ 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, // AutoContent_29_AIDNA_4
1258+ 2, 0, 1, 2, 0, 2, 2, 2, 1, 0, // AutoContent_30_AIDNA_4
1259+ 2, 1, 2, 2, 1, 1, 0, 2, 2, 1 // AutoContent_31_AIDNA_4
1260+ }
1261+ ;
1262+ assert(kNeuronPoolSize == input_func_D.size());
1263+ assert(kNeuronPoolSize == input_weights_D.size());
1264+
1265+ const std::vector<uint32_t> f_neurons_D =
1266+ { 471130763, 1322756799, 1148682382, 2713953719, 194460175, 18113635, 2947490886, 3275944172, 3438582214, 856208494, // AutoContent_32_AIDNA_4
1267+ 1326156684, 449560659, 3465512701, 1962749574, 1523650869, 1376482111, 2347409353, 2962227502, 657710614, 578259960, // AutoContent_33_AIDNA_4
1268+ 1271222963, 2915927856, 3396846486, 1743568169, 2679432920, 410834611, 134904175, 2968201710, 2132567223, 2248461478, // AutoContent_34_AIDNA_4
1269+ 161963959, 3295327519, 670058472, 2013696856, 3608400883, 496651103, 733137541, 2952748738, 1159810205, 3886490843, // AutoContent_35_AIDNA_4
1270+ 3225153820, 732007755, 2583635732, 4271028825, 1217674278, 4043323645, 1857109651, 2248481623, 3899721011, 3081298269, // AutoContent_36_AIDNA_4
1271+ 1277901016, 1255642150, 2384261818, 2866704864, 755617465, 835768208, 1394358417, 4012239945, 2601238115, 3933467106 // AutoContent_37_AIDNA_4
1272+ };
1273+ assert(kFNeuronPoolSize == f_neurons_D.size());
1274+
1275+ primary_parent = std::rand() % 4;
1276+ const uint8_t parent2 = std::rand() % 4;
1277+
1278+ log (" ... Primary parent: %d, secondary parent: %d\n", primary_parent, parent2);
1279+
1280+ // First setting of military numbers, they go directly to persistent data
1281+ for (uint16_t i = 0; i < kMagicNumbersSize; i += 1) {
1282+ // Child inherits DNA with probability 5:1 from main parent
1283+ uint8_t dna_donor = (std::rand() % kSecondParentProbability > 0) ? primary_parent : parent2;
1284+ if (i == kMutationRatePosition) { // Overwriting
1285+ dna_donor = primary_parent;
1286+ }
1287+
1288+ switch (dna_donor) {
1289+ case 0 :
1290+ set_military_number_at(i, AI_initial_military_numbers_A[i]);
1291+ break;
1292+ case 1 :
1293+ set_military_number_at(i, AI_initial_military_numbers_B[i]);
1294+ break;
1295+ case 2 :
1296+ set_military_number_at(i, AI_initial_military_numbers_C[i]);
1297+ break;
1298+ case 3 :
1299+ set_military_number_at(i, AI_initial_military_numbers_D[i]);
1300+ break;
1301+ default:
1302+ log ("parent %d?\n", dna_donor);
1303+ NEVER_HERE();
1304+ }
1305+ }
1306+
1307+ pd->neuron_weights.clear();
1308+ pd->neuron_functs.clear();
1309+ pd->f_neurons.clear();
1310+
1311+ for (uint16_t i = 0; i < kNeuronPoolSize; i += 1) {
1312+ const uint8_t dna_donor = (std::rand() % kSecondParentProbability > 0) ? primary_parent : parent2;
1313+
1314+ switch (dna_donor) {
1315+ case 0 :
1316+ pd->neuron_weights.push_back(input_weights_A[i]);
1317+ pd->neuron_functs.push_back(input_func_A[i]);
1318+ break;
1319+ case 1 :
1320+ pd->neuron_weights.push_back(input_weights_B[i]);
1321+ pd->neuron_functs.push_back(input_func_B[i]);
1322+ break;
1323+ case 2 :
1324+ pd->neuron_weights.push_back(input_weights_C[i]);
1325+ pd->neuron_functs.push_back(input_func_C[i]);
1326+ break;
1327+ case 3 :
1328+ pd->neuron_weights.push_back(input_weights_D[i]);
1329+ pd->neuron_functs.push_back(input_func_D[i]);
1330+ break;
1331+ default:
1332+ log ("parent %d?\n", dna_donor);
1333+ NEVER_HERE();
1334+ }
1335+ }
1336+
1337+
1338+ for (uint16_t i = 0; i < kFNeuronPoolSize; i += 1) {
1339+ const uint8_t dna_donor = (std::rand() % kSecondParentProbability > 0) ? primary_parent : parent2;
1340+ switch (dna_donor) {
1341+ case 0 :
1342+ pd->f_neurons.push_back(f_neurons_A[i]);
1343+ break;
1344+ case 1 :
1345+ pd->f_neurons.push_back(f_neurons_B[i]);
1346+ break;
1347+ case 2 :
1348+ pd->f_neurons.push_back(f_neurons_C[i]);
1349+ break;
1350+ case 3 :
1351+ pd->f_neurons.push_back(f_neurons_D[i]);
1352+ break;
1353+ default:
1354+ log ("parent %d?\n", dna_donor);
1355+ NEVER_HERE();
1356+ }
1357+ }
1358+
1359+ pd->magic_numbers_size = kMagicNumbersSize;
1360+ pd->neuron_pool_size = kNeuronPoolSize;
1361+ pd->f_neuron_pool_size = kFNeuronPoolSize;
1362+
1363+}
1364+// Decides if mutation takes place and how intensive it will be
1365+MutatingIntensity ManagementData::do_mutate(const uint8_t is_preferred, const int16_t mutation_probability) {
1366+ if (is_preferred > 0) {
1367+ return MutatingIntensity::kAgressive;
1368+ }
1369+ if (std::rand() % mutation_probability == 0) {
1370+ return MutatingIntensity::kNormal;
1371+ }
1372+ return MutatingIntensity::kNo;
1373+}
1374+
1375+// Mutating, but all done on persistent data
1376+void ManagementData::mutate(const uint8_t pn) {
1377+
1378+ int16_t probability =
1379+ shift_weight_value(get_military_number_at(kMutationRatePosition), false) + 101;
1380+ if (probability > kUpperDefaultMutationLimit) {
1381+ probability = kUpperDefaultMutationLimit;
1382+ }
1383+ if (probability < kLowerDefaultMutationLimit) {
1384+ probability = kLowerDefaultMutationLimit;
1385+ }
1386+
1387+ set_military_number_at(kMutationRatePosition, probability - 101);
1388+
1389+ // decreasing probability (or rather increasing probability of mutation) if weaker player
1390+ if (ai_type == Widelands::AiType::kWeak) {
1391+ probability /= 2;
1392+ log("%2d: Weak mode, increasing mutation probability to 1 / %d\n", pn, probability);
1393+ } else if (ai_type == Widelands::AiType::kVeryWeak) {
1394+ probability /= 4;
1395+ log("%2d: Very weak mode, increasing mutation probability to 1 / %d\n", pn, probability);
1396+ }
1397+
1398+ assert(probability > 0 && probability <= 201);
1399+
1400+ log("%2d: mutating DNA with probability 1 / %3d:\n", pn, probability);
1401+
1402+ if (probability < 201) {
1403+
1404+ // Modifying pool of Military numbers
1405+ {
1406+ // Preferred numbers are ones that will be mutated agressively in full range [-kWeightRange, kWeightRange]
1407+ std::set<int32_t> preferred_numbers = {};
1408+
1409+ for (uint16_t i = 0; i < kMagicNumbersSize; i += 1) {
1410+ if (i == kMutationRatePosition) { // mutated above
1411+ continue;
1412+ }
1413+
1414+ const MutatingIntensity mutating_intensity = do_mutate(preferred_numbers.count(i) > 0, probability);
1415+
1416+ if (mutating_intensity != MutatingIntensity::kNo) {
1417+ const int16_t old_value = get_military_number_at(i);
1418+ const int16_t new_value = shift_weight_value(get_military_number_at(i), mutating_intensity == MutatingIntensity::kAgressive);
1419+ set_military_number_at(i, new_value);
1420+ log(" Magic number %3d: value changed: %4d -> %4d %s\n", i, old_value,
1421+ new_value, (mutating_intensity == MutatingIntensity::kAgressive) ? "aggressive" : "");
1422+ }
1423+ }
1424+ }
1425+
1426+ // Modifying pool of neurons
1427+ {
1428+ // Neurons to be mutated more agressively
1429+ std::set<int32_t> preferred_neurons = {};
1430+ for (auto& item : neuron_pool) {
1431+
1432+ const MutatingIntensity mutating_intensity = do_mutate(preferred_neurons.count(item.get_id()) > 0, probability);
1433+
1434+ if (mutating_intensity != MutatingIntensity::kNo) {
1435+ const int16_t old_value = item.get_weight();
1436+ if (std::rand() % 4 == 0) {
1437+ assert(!neuron_curves.empty());
1438+ item.set_type(std::rand() % neuron_curves.size());
1439+ pd->neuron_functs[item.get_id()] = item.get_type();
1440+ } else {
1441+ int16_t new_value = shift_weight_value(item.get_weight(), mutating_intensity == MutatingIntensity::kAgressive);
1442+ item.set_weight(new_value);
1443+ pd->neuron_weights[item.get_id()] = item.get_weight();
1444+ }
1445+ log(" Neuron %2d: weight: %4d -> %4d, new curve: %d %s\n", item.get_id(),
1446+ old_value, item.get_weight(), item.get_type(),
1447+ (mutating_intensity == MutatingIntensity::kAgressive) ? "aggressive" : "");
1448+
1449+ item.recalculate();
1450+ }
1451+ }
1452+ }
1453+
1454+ // Modifying pool of f-neurons
1455+ {
1456+ // FNeurons to be mutated more agressively
1457+ std::set<int32_t> preferred_f_neurons = {};
1458+ for (auto& item : f_neuron_pool) {
1459+ uint8_t changed_bits = 0;
1460+ // is this a preferred neuron
1461+ if (preferred_f_neurons.count(item.get_id()) > 0) {
1462+ for (uint8_t i = 0; i < kFNeuronBitSize; i += 1) {
1463+ if (std::rand() % 5 == 0) {
1464+ item.flip_bit(i);
1465+ changed_bits += 1;
1466+ }
1467+ }
1468+ } else { // normal mutation
1469+ for (uint8_t i = 0; i < kFNeuronBitSize; i += 1) {
1470+ if (std::rand() % (probability * 3) == 0) {
1471+ item.flip_bit(i);
1472+ changed_bits += 1;
1473+ }
1474+ }
1475+ }
1476+
1477+ if (changed_bits) {
1478+ pd->f_neurons[item.get_id()] = item.get_int();
1479+ log(" F-Neuron %2d: new value: %13ul, changed bits: %2d %s\n",
1480+ item.get_id(), item.get_int(), changed_bits,
1481+ (preferred_f_neurons.count(item.get_id()) > 0) ? "aggressive" : "");
1482+ }
1483+ }
1484+ }
1485+ }
1486+
1487+ test_consistency();
1488+}
1489+
1490+// Now we copy persistent to local
1491+void ManagementData::copy_persistent_to_local() {
1492+
1493+ assert(pd->neuron_weights.size() == kNeuronPoolSize);
1494+ assert(pd->neuron_functs.size() == kNeuronPoolSize);
1495+ neuron_pool.clear();
1496+ for (uint32_t i = 0; i < kNeuronPoolSize; i = i + 1) {
1497+ neuron_pool.push_back(Neuron(pd->neuron_weights[i], pd->neuron_functs[i], i));
1498+ }
1499+
1500+ assert(pd->f_neurons.size() == kFNeuronPoolSize);
1501+ f_neuron_pool.clear();
1502+ for (uint32_t i = 0; i < kFNeuronPoolSize; i = i + 1) {
1503+ f_neuron_pool.push_back(FNeuron(pd->f_neurons[i], i));
1504+ }
1505+
1506+ pd->magic_numbers_size = kMagicNumbersSize;
1507+ pd->neuron_pool_size = kNeuronPoolSize;
1508+ pd->f_neuron_pool_size = kFNeuronPoolSize;
1509+
1510+ test_consistency();
1511+ log(" ... DNA initialized\n");
1512+}
1513+
1514+void ManagementData::test_consistency(bool itemized) {
1515+
1516+ assert(pd->neuron_weights.size() == pd->neuron_pool_size);
1517+ assert(pd->neuron_functs.size() == pd->neuron_pool_size);
1518+ assert(neuron_pool.size() == pd->neuron_pool_size);
1519+ assert(neuron_pool.size() == kNeuronPoolSize);
1520+
1521+ assert(pd->magic_numbers_size == kMagicNumbersSize);
1522+ assert(pd->magic_numbers.size() == kMagicNumbersSize);
1523+
1524+ assert(pd->f_neurons.size() == pd->f_neuron_pool_size);
1525+ assert(f_neuron_pool.size() == pd->f_neuron_pool_size);
1526+ assert(f_neuron_pool.size() == kFNeuronPoolSize);
1527+
1528+ if (itemized) {
1529+ // comparing contents of neuron and fneuron pools
1530+ for (uint16_t i = 0; i < kNeuronPoolSize; i += 1) {
1531+ assert(pd->neuron_weights[i] == neuron_pool[i].get_weight());
1532+ assert(pd->neuron_functs[i] == neuron_pool[i].get_type());
1533+ assert(neuron_pool[i].get_id() == i);
1534+ }
1535+ for (uint16_t i = 0; i < kFNeuronPoolSize; i += 1) {
1536+ assert(pd->f_neurons[i] == f_neuron_pool[i].get_int());
1537+ assert(f_neuron_pool[i].get_id() == i);
1538+ }
1539+ }
1540+
1541+ return;
1542+}
1543+
1544+// Print DNA data to console, used for training
1545+// TODO(tiborb): Once we will have AI dumped into files, this should be removed
1546+// Also, used only for training
1547+void ManagementData::dump_data() {
1548+ // dumping new numbers
1549+ log(" actual military_numbers (%lu):\n {", pd->magic_numbers.size());
1550+ uint16_t itemcounter = 1;
1551+ uint16_t line_counter = 1;
1552+ for (const auto& item : pd->magic_numbers) {
1553+ log(" %3d%s", item, (&item != &pd->magic_numbers.back()) ? ", " : " ");
1554+ if (itemcounter % 10 == 0) {
1555+ log(" // AutoContent_%02d\n ", line_counter);
1556+ line_counter += 1;
1557+ }
1558+ ++itemcounter;
1559+ }
1560+ log("}\n");
1561+
1562+ log(" actual neuron setup:\n ");
1563+ log("{ ");
1564+ itemcounter = 1;
1565+ for (auto& item : neuron_pool) {
1566+ log(" %3d%s", item.get_weight(), (&item != &neuron_pool.back()) ? ", " : " ");
1567+ if (itemcounter % 10 == 0) {
1568+ log(" // AutoContent_%02d\n ", line_counter);
1569+ line_counter += 1;
1570+ }
1571+ ++itemcounter;
1572+ }
1573+ log("}\n { ");
1574+ itemcounter = 1;
1575+ for (auto& item : neuron_pool) {
1576+ log(" %3d%s", item.get_type(), (&item != &neuron_pool.back()) ? ", " : " ");
1577+ if (itemcounter % 10 == 0) {
1578+ log(" // AutoContent_%02d\n ", line_counter);
1579+ line_counter += 1;
1580+ }
1581+ ++itemcounter;
1582+ }
1583+ log("}\n");
1584+
1585+ log(" actual f-neuron setup:\n ");
1586+ log("{ ");
1587+ itemcounter = 1;
1588+ for (auto& item : f_neuron_pool) {
1589+ log(" %8u%s", item.get_int(), (&item != &f_neuron_pool.back()) ? ", " : " ");
1590+ if (itemcounter % 10 == 0) {
1591+ log(" // AutoContent_%02d\n ", line_counter);
1592+ line_counter += 1;
1593+ }
1594+ ++itemcounter;
1595+ }
1596+ log("}\n");
1597+}
1598+
1599+// Querying military number at a possition
1600+int16_t ManagementData::get_military_number_at(uint8_t pos) {
1601+ assert(pos < kMagicNumbersSize);
1602+ return pd->magic_numbers[pos];
1603+}
1604+
1605+// Setting military number (persistent numbers are used also for local use)
1606+void ManagementData::set_military_number_at(const uint8_t pos, int16_t value) {
1607+ assert(pos < kMagicNumbersSize);
1608+
1609+ while (pos >= pd->magic_numbers.size()) {
1610+ pd->magic_numbers.push_back(0);
1611+ }
1612+
1613+ value = Neuron::clip_weight_to_range(value);
1614+ pd->magic_numbers[pos] = value;
1615 }
1616
1617 uint16_t MineTypesObserver::total_count() const {
1618@@ -292,7 +1098,7 @@
1619 : due_time(time), id(t), priority(p), descr(d) {
1620 }
1621
1622-bool SchedulerTask::operator<(SchedulerTask other) const {
1623+bool SchedulerTask::operator<(const SchedulerTask& other) const {
1624 return priority > other.priority;
1625 }
1626
1627@@ -450,9 +1256,7 @@
1628 }
1629 }
1630
1631-bool FlagsForRoads::get_winner(uint32_t* winner_hash, uint32_t pos) {
1632- assert(pos == 1 || pos == 2);
1633- uint32_t counter = 1;
1634+bool FlagsForRoads::get_winner(uint32_t* winner_hash) {
1635 // If AI can ask for 2nd position, but there is only one viable candidate
1636 // we return the first one of course
1637 bool has_winner = false;
1638@@ -466,37 +1270,98 @@
1639 *winner_hash = candidate_flag.coords_hash;
1640 has_winner = true;
1641
1642- if (counter == pos) {
1643+ if (std::rand() % 3 > 0) {
1644+ // with probability of 2/3 we accept this flag
1645 return true;
1646- } else if (counter < pos) {
1647- counter += 1;
1648- } else {
1649- break;
1650 }
1651 }
1652+
1653 if (has_winner) {
1654 return true;
1655 }
1656 return false;
1657 }
1658
1659-// This is an struct that stores strength of players, info on teams and provides some outputs from
1660-// these data
1661-PlayersStrengths::PlayerStat::PlayerStat() : team_number(0), players_power(0) {
1662-}
1663-PlayersStrengths::PlayerStat::PlayerStat(Widelands::TeamNumber tc, uint32_t pp)
1664- : team_number(tc), players_power(pp) {
1665+PlayersStrengths::PlayersStrengths() : update_time(0) {
1666+}
1667+
1668+// Default constructor
1669+PlayersStrengths::PlayerStat::PlayerStat()
1670+ : team_number(0),
1671+ is_enemy(false),
1672+ players_power(0),
1673+ old_players_power(0),
1674+ old60_players_power(0),
1675+ players_casualities(0) {
1676+}
1677+
1678+// Constructor to be used
1679+PlayersStrengths::PlayerStat::PlayerStat(Widelands::TeamNumber tc,
1680+ bool e,
1681+ uint32_t pp,
1682+ uint32_t op,
1683+ uint32_t o60p,
1684+ uint32_t cs,
1685+ uint32_t land,
1686+ uint32_t oland,
1687+ uint32_t o60l)
1688+ : team_number(tc),
1689+ is_enemy(e),
1690+ players_power(pp),
1691+ old_players_power(op),
1692+ old60_players_power(o60p),
1693+ players_casualities(cs),
1694+ players_land(land),
1695+ old_players_land(oland),
1696+ old60_players_land(o60l) {
1697+ last_time_seen = kNever;
1698 }
1699
1700 // Inserting/updating data
1701-void PlayersStrengths::add(Widelands::PlayerNumber pn, Widelands::TeamNumber tn, uint32_t pp) {
1702- if (all_stats.count(pn) == 0) {
1703- all_stats.insert(std::pair<Widelands::PlayerNumber, PlayerStat>(pn, PlayerStat(tn, pp)));
1704+// We keep information for
1705+// - player strength / power
1706+// - player casualties
1707+// - player land
1708+// We store actual values, but for some of them we store also
1709+// - old = 15 mins ago
1710+// - old60 = 60 mins ago
1711+// e.g. players_power / old_players_power / old60_players_power
1712+// we recieve also player and team numbers to figure out if we are enemies, or in the team
1713+void PlayersStrengths::add(Widelands::PlayerNumber pn,
1714+ Widelands::PlayerNumber opn,
1715+ Widelands::TeamNumber mytn,
1716+ Widelands::TeamNumber pltn,
1717+ uint32_t pp,
1718+ uint32_t op,
1719+ uint32_t o60p,
1720+ uint32_t cs,
1721+ uint32_t land,
1722+ uint32_t oland,
1723+ uint32_t o60l) {
1724+ if (all_stats.count(opn) == 0) {
1725+ bool enemy = false;
1726+ if (pn == opn) {
1727+ ;
1728+ } else if (pltn == 0 || mytn == 0) {
1729+ enemy = true;
1730+ } else if (pltn != mytn) {
1731+ enemy = true;
1732+ }
1733+ this_player_number = pn;
1734+ all_stats.insert(std::make_pair(
1735+ opn, PlayerStat(pltn, enemy, pp, op, o60p, cs, land, oland, o60l)));
1736 } else {
1737- all_stats[pn].players_power = pp;
1738+ all_stats[opn].players_power = pp;
1739+ all_stats[opn].old_players_power = op;
1740+ all_stats[opn].old60_players_power = o60p;
1741+ all_stats[opn].players_casualities = cs;
1742+ all_stats[opn].players_land = land;
1743+ all_stats[opn].old_players_land = oland;
1744+ all_stats[opn].old60_players_land = oland;
1745 }
1746 }
1747
1748+// After statistics for team members are updated, this calculation is needed
1749 void PlayersStrengths::recalculate_team_power() {
1750 team_powers.clear();
1751 for (auto& item : all_stats) {
1752@@ -510,6 +1375,186 @@
1753 }
1754 }
1755
1756+// This just goes over information about all enemies and where they were seen the last time
1757+bool PlayersStrengths::any_enemy_seen_lately(const uint32_t gametime) {
1758+ for (auto& item : all_stats) {
1759+ if (item.second.is_enemy && player_seen_lately(item.first, gametime)) {
1760+ return true;
1761+ }
1762+ }
1763+ return false;
1764+}
1765+
1766+// Returns count of nearby enemies
1767+uint8_t PlayersStrengths::enemies_seen_lately_count(const uint32_t gametime) {
1768+ uint8_t count = 0;
1769+ for (auto& item : all_stats) {
1770+ if (item.second.is_enemy && player_seen_lately(item.first, gametime)) {
1771+ count += 1;
1772+ }
1773+ }
1774+ return count;
1775+}
1776+
1777+// When we see enemy, we use this to store the time
1778+void PlayersStrengths::set_last_time_seen(const uint32_t seentime, Widelands::PlayerNumber pn) {
1779+ if (all_stats.count(pn) == 0) {
1780+ return;
1781+ }
1782+ all_stats[pn].last_time_seen = seentime;
1783+}
1784+
1785+bool PlayersStrengths::get_is_enemy(Widelands::PlayerNumber pn) {
1786+ if (all_stats.count(pn) == 0) {
1787+ // Should happen only rarely so we print a warning here
1788+ log("%d: WARNING: player has no statistics yet\n", this_player_number);
1789+ return false;
1790+ }
1791+ return all_stats[pn].is_enemy;
1792+}
1793+
1794+// Was the player seen less then 2 minutes ago
1795+bool PlayersStrengths::player_seen_lately(Widelands::PlayerNumber pn, const uint32_t gametime) {
1796+ if (all_stats.count(pn) == 0) {
1797+ // Should happen only rarely so we print a warning here
1798+ log("%d: WARNING: player has no statistics yet\n", this_player_number);
1799+ return false;
1800+ }
1801+ if (all_stats[pn].last_time_seen == kNever) {
1802+ return false;
1803+ }
1804+ if (all_stats[pn].last_time_seen + (2U * 60U * 1000U) > gametime) {
1805+ return true;
1806+ }
1807+ return false;
1808+}
1809+
1810+// This is the strength of a player
1811+uint32_t PlayersStrengths::get_player_power(Widelands::PlayerNumber pn) {
1812+ if (all_stats.count(pn) > 0) {
1813+ return all_stats[pn].players_power;
1814+ };
1815+ return 0;
1816+}
1817+
1818+// This is the land size owned by player
1819+uint32_t PlayersStrengths::get_player_land(Widelands::PlayerNumber pn) {
1820+ if (all_stats.count(pn) > 0) {
1821+ return all_stats[pn].players_land;
1822+ };
1823+ return 0;
1824+}
1825+
1826+// Calculates the strength of the enemies seen within the last 2 minutes
1827+uint32_t PlayersStrengths::get_visible_enemies_power(const uint32_t gametime) {
1828+ uint32_t pw = 0;
1829+ for (auto& item : all_stats) {
1830+ if (get_is_enemy(item.first) && player_seen_lately(item.first, gametime)) {
1831+ pw += item.second.players_power;
1832+ }
1833+ }
1834+ return pw;
1835+}
1836+
1837+uint32_t PlayersStrengths::get_enemies_average_power() {
1838+ uint32_t sum = 0;
1839+ uint8_t count = 0;
1840+ for (auto& item : all_stats) {
1841+ if (get_is_enemy(item.first)) {
1842+ sum += item.second.players_power;
1843+ ++count;
1844+ }
1845+ }
1846+ if (count > 0) {
1847+ return sum / count;
1848+ }
1849+ return 0;
1850+}
1851+
1852+uint32_t PlayersStrengths::get_enemies_average_land() {
1853+ uint32_t sum = 0;
1854+ uint8_t count = 0;
1855+ for (auto& item : all_stats) {
1856+ if (get_is_enemy(item.first)) {
1857+ sum += item.second.players_land;
1858+ ++count;
1859+ }
1860+ }
1861+ if (count > 0) {
1862+ return sum / count;
1863+ }
1864+ return 0;
1865+}
1866+
1867+// Strength of stronger player
1868+uint32_t PlayersStrengths::get_enemies_max_power() {
1869+ uint32_t power = 0;
1870+ for (auto& item : all_stats) {
1871+ if (get_is_enemy(item.first)) {
1872+ power = std::max<uint32_t>(power, item.second.players_power);
1873+ }
1874+ }
1875+ return power;
1876+}
1877+
1878+uint32_t PlayersStrengths::get_enemies_max_land() {
1879+ uint32_t land = 0;
1880+ for (auto& item : all_stats) {
1881+ if (get_is_enemy(item.first)) {
1882+ land = std::max<uint32_t>(land, item.second.players_land);
1883+ }
1884+ }
1885+ return land;
1886+}
1887+
1888+uint32_t PlayersStrengths::get_old_player_power(Widelands::PlayerNumber pn) {
1889+ if (all_stats.count(pn) > 0) {
1890+ return all_stats[pn].old_players_power;
1891+ }
1892+ return 0;
1893+}
1894+
1895+uint32_t PlayersStrengths::get_old60_player_power(Widelands::PlayerNumber pn) {
1896+ if (all_stats.count(pn) > 0) {
1897+ return all_stats[pn].old60_players_power;
1898+ }
1899+ return 0;
1900+}
1901+
1902+uint32_t PlayersStrengths::get_old_player_land(Widelands::PlayerNumber pn) {
1903+ if (all_stats.count(pn) == 0) {
1904+ log(" %d: Players statistics are still empty\n", pn);
1905+ return 0;
1906+ }
1907+ return all_stats[pn].old_players_land;
1908+}
1909+
1910+uint32_t PlayersStrengths::get_old60_player_land(Widelands::PlayerNumber pn) {
1911+ if (all_stats.count(pn) == 0) {
1912+ log(" %d: Players statistics are still empty\n", pn);
1913+ return 0;
1914+ }
1915+ return all_stats[pn].old60_players_land;
1916+}
1917+
1918+uint32_t PlayersStrengths::get_old_visible_enemies_power(const uint32_t gametime) {
1919+ uint32_t pw = 0;
1920+ for (auto& item : all_stats) {
1921+ if (get_is_enemy(item.first) && player_seen_lately(item.first, gametime)) {
1922+ pw += item.second.old_players_power;
1923+ }
1924+ }
1925+ return pw;
1926+}
1927+
1928+// This is casualities of player
1929+uint32_t PlayersStrengths::get_player_casualities(Widelands::PlayerNumber pn) {
1930+ if (all_stats.count(pn) > 0) {
1931+ return all_stats[pn].players_casualities;
1932+ }
1933+ return 0;
1934+}
1935+
1936 // This is strength of player plus third of strength of other members of his team
1937 uint32_t PlayersStrengths::get_modified_player_power(Widelands::PlayerNumber pn) {
1938 uint32_t result = 0;
1939@@ -517,19 +1562,24 @@
1940 if (all_stats.count(pn) > 0) {
1941 result = all_stats[pn].players_power;
1942 team = all_stats[pn].team_number;
1943- };
1944+ }
1945 if (team > 0 && team_powers.count(team) > 0) {
1946 result = result + (team_powers[team] - result) / 3;
1947- };
1948+ }
1949 return result;
1950 }
1951
1952+// Are the player in the same team
1953 bool PlayersStrengths::players_in_same_team(Widelands::PlayerNumber pl1,
1954 Widelands::PlayerNumber pl2) {
1955- if (all_stats.count(pl1) > 0 && all_stats.count(pl2) > 0 && pl1 != pl2) {
1956+ assert(all_stats.count(pl1) > 0);
1957+ assert(all_stats.count(pl2) > 0);
1958+ if (pl1 == pl2) {
1959+ return false;
1960+ } else if (all_stats[pl1].team_number > 0 &&
1961+ all_stats[pl1].team_number == all_stats[pl2].team_number) {
1962 // team number 0 = no team
1963- return all_stats[pl1].team_number > 0 &&
1964- all_stats[pl1].team_number == all_stats[pl2].team_number;
1965+ return true;
1966 } else {
1967 return false;
1968 }
1969@@ -551,4 +1601,19 @@
1970 return my_strength > strongest_opponent_strength + 50;
1971 }
1972
1973-} // namespace WIdelands
1974+// Update_time is used to prevent too frequent updates of statistics
1975+void PlayersStrengths::set_update_time(const uint32_t gametime) {
1976+ update_time = gametime;
1977+}
1978+
1979+uint32_t PlayersStrengths::get_update_time() {
1980+ return update_time;
1981+}
1982+
1983+ProductionSiteObserver::ProductionSiteObserver()
1984+ : no_resources_since(kNever),
1985+ upgrade_pending(false),
1986+ dismantle_pending_since(kNever) {
1987+}
1988+
1989+} // namespace Widelands
1990
1991=== modified file 'src/ai/ai_help_structs.h'
1992--- src/ai/ai_help_structs.h 2017-06-24 08:47:46 +0000
1993+++ src/ai/ai_help_structs.h 2017-07-03 17:39:38 +0000
1994@@ -21,6 +21,7 @@
1995 #define WL_AI_AI_HELP_STRUCTS_H
1996
1997 #include <list>
1998+#include <queue>
1999 #include <unordered_set>
2000
2001 #include "ai/ai_hints.h"
2002@@ -43,6 +44,8 @@
2003
2004 enum class ExtendedBool : uint8_t { kUnset, kTrue, kFalse };
2005
2006+enum class MutatingIntensity : uint8_t { kNo, kNormal, kAgressive };
2007+
2008 enum class BuildingNecessity : uint8_t {
2009 kForced,
2010 kNeeded,
2011@@ -54,6 +57,33 @@
2012 kForbidden
2013 };
2014
2015+// A building type can have no, one or multiple of these attributes
2016+enum class BuildingAttribute : uint8_t {
2017+ kBakery,
2018+ kRanger,
2019+ kBuildable,
2020+ kLumberjack,
2021+ kPort,
2022+ kNeedsRocks,
2023+ kWell,
2024+ kNeedsCoast,
2025+ kHunter,
2026+ kFisher,
2027+ kShipyard,
2028+ kBarracks,
2029+ kSpaceConsumer,
2030+ kRecruitment,
2031+ kBuildingMatProducer,
2032+ kUpgradeSubstitutes,
2033+ kUpgradeExtends,
2034+ kLogRefiner,
2035+ kIronMine
2036+};
2037+
2038+enum class AiType : uint8_t { kVeryWeak, kWeak, kNormal };
2039+
2040+enum class ExpansionMode : uint8_t { kResources = 0, kSpace = 1, kEconomy = 2, kBoth = 3 };
2041+
2042 enum class AiModeBuildings : uint8_t { kAnotherAllowed, kOnLimit, kLimitExceeded };
2043
2044 enum class SchedulerTaskId : uint8_t {
2045@@ -74,9 +104,28 @@
2046 kCheckTrainingsites,
2047 kCountMilitaryVacant,
2048 kCheckEnemySites,
2049+ kManagementUpdate,
2050+ kUpdateStats,
2051 kUnset
2052 };
2053
2054+// This is a simplification of a curve, to avoid repeated calculation
2055+const std::vector<std::vector<int8_t>> neuron_curves = {
2056+ {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100},
2057+ {0, 0, 1, 2, 4, 6, 9, 12, 16, 20, 25, 30, 36, 42, 49, 56, 64, 72, 81, 90, 100},
2058+ {0, 17, 25, 32, 38, 44, 49, 53, 58, 62, 66, 70, 74, 78, 81, 84, 88, 91, 94, 97, 100},
2059+};
2060+
2061+constexpr int kMagicNumbersSize = 150;
2062+constexpr int kNeuronPoolSize = 80;
2063+constexpr int kFNeuronPoolSize = 60;
2064+constexpr int kFNeuronBitSize = 32;
2065+constexpr int kMutationRatePosition = 42;
2066+// TODO(tiborb): this should be replaced by command line switch
2067+constexpr bool kAITrainingMode = false;
2068+
2069+constexpr uint32_t kNever = std::numeric_limits<uint32_t>::max();
2070+
2071 struct CheckStepRoadAI {
2072 CheckStepRoadAI(Player* const pl, uint8_t const mc, bool const oe);
2073
2074@@ -112,17 +161,14 @@
2075 Game& game;
2076 };
2077
2078-// When looking for unowned terrain to acquire, we are actually
2079-// only interested in fields we can walk on.
2080-// Fields should either be completely unowned or owned by an opposing player_
2081-struct FindNodeUnowned {
2082- FindNodeUnowned(Player* p, Game& g, bool oe = false);
2083+// We need to count walkable fields owned by enemy
2084+struct FindEnemyNodeWalkable {
2085+ FindEnemyNodeWalkable(Player* p, Game& g);
2086
2087 bool accept(const Map&, const FCoords& fc) const;
2088
2089 Player* player;
2090 Game& game;
2091- bool only_enemies;
2092 };
2093
2094 // Sometimes we need to know how many nodes our allies owns
2095@@ -140,7 +186,19 @@
2096 // pay speciall attention to fields where mines can be built.
2097 // Fields should be completely unowned
2098 struct FindNodeUnownedMineable {
2099- FindNodeUnownedMineable(Player* p, Game& g);
2100+ FindNodeUnownedMineable(Player* p, Game& g, int32_t t = INVALID_INDEX);
2101+
2102+ bool accept(const Map&, const FCoords& fc) const;
2103+
2104+ Player* player;
2105+ Game& game;
2106+ int32_t ore_type;
2107+};
2108+
2109+// When looking for unowned terrain to acquire, we must
2110+// consider if any buildings can be built on unowned land.
2111+struct FindNodeUnownedBuildable {
2112+ FindNodeUnownedBuildable(Player* p, Game& g);
2113
2114 bool accept(const Map&, const FCoords& fc) const;
2115
2116@@ -216,6 +274,18 @@
2117 int32_t distance;
2118 };
2119
2120+struct EventTimeQueue {
2121+ EventTimeQueue();
2122+
2123+ void push(uint32_t);
2124+ uint32_t count(uint32_t);
2125+ void strip_old(uint32_t);
2126+
2127+private:
2128+ uint32_t duration_ = 20 * 60 * 1000;
2129+ std::queue<uint32_t> queue;
2130+};
2131+
2132 struct WalkableSpot {
2133 Coords coords;
2134 bool has_flag;
2135@@ -238,11 +308,17 @@
2136
2137 bool preferred;
2138 bool enemy_nearby;
2139+ bool enemy_accessible_;
2140
2141- uint8_t unowned_land_nearby;
2142+ bool enemy_wh_nearby;
2143+ uint16_t unowned_land_nearby;
2144+ uint16_t enemy_owned_land_nearby;
2145+ uint16_t unowned_buildable_spots_nearby;
2146+ uint16_t nearest_buildable_spot_nearby;
2147 // to identify that field is too close to border and no production building should be built there
2148 bool near_border;
2149- uint8_t unowned_mines_spots_nearby;
2150+ uint16_t unowned_mines_spots_nearby;
2151+ uint16_t unowned_iron_mines_nearby;
2152 uint8_t trees_nearby;
2153 uint8_t rocks_nearby;
2154 int16_t water_nearby;
2155@@ -264,22 +340,29 @@
2156 // are construction sites that will change this once they are built
2157 int16_t military_in_constr_nearby;
2158 // actual count of soldiers in nearby buldings
2159- int16_t area_military_presence;
2160+ int16_t own_military_presence;
2161+ int16_t enemy_military_presence;
2162+ int16_t enemy_military_sites; // Including unfinished
2163+ int16_t ally_military_presence;
2164 // stationed (manned) military buildings nearby
2165 int16_t military_stationed;
2166- // stationed (manned) military buildings nearby
2167 // unconnected buildings nearby
2168 bool unconnected_nearby;
2169 int16_t military_unstationed;
2170- bool is_portspace;
2171+ int16_t own_non_military_nearby;
2172+ bool defense_msite_allowed;
2173+ Widelands::ExtendedBool is_portspace;
2174 bool port_nearby; // to increase priority if a port is nearby,
2175 // especially for new colonies
2176 Widelands::ExtendedBool portspace_nearby; // prefer military buildings closer to the portspace
2177 int32_t max_buildcap_nearby;
2178- // it is not necessary to check resources (stones, fish...) too frequently as they do not change
2179- // fast
2180- // this stores time of last check
2181+ // It is not necessary to check resources (stones, fish...) too frequently as they do not change
2182+ // fast. This stores the time of the last check.
2183 uint32_t last_resources_check_time;
2184+ int32_t military_score_;
2185+ bool inland;
2186+ uint16_t local_soldier_capacity;
2187+ bool is_militarysite;
2188
2189 std::vector<uint8_t> consumers_nearby;
2190 std::vector<uint8_t> producers_nearby;
2191@@ -322,33 +405,27 @@
2192 Widelands::AiModeBuildings aimode_limit_status();
2193 bool buildable(Widelands::Player& p);
2194
2195+ // Convenience functions for is_what
2196+ bool is(BuildingAttribute) const;
2197+ void set_is(BuildingAttribute);
2198+ void unset_is(BuildingAttribute);
2199+
2200 char const* name;
2201 Widelands::DescriptionIndex id;
2202 Widelands::BuildingDescr const* desc;
2203
2204 Type type;
2205
2206- bool plants_trees;
2207- bool recruitment; // is "producing" workers?
2208 Widelands::BuildingNecessity new_building;
2209 uint32_t new_building_overdue;
2210 int32_t primary_priority;
2211- bool is_buildable;
2212- bool need_trees; // lumberjack = true
2213- bool need_rocks; // quarry = true
2214- bool mines_water; // wells
2215- bool need_water; // fisher, fish_breeder = true
2216- bool is_hunter; // need to identify hunters
2217- bool is_fisher; // need to identify fishers
2218- bool is_port;
2219- bool is_shipyard;
2220- bool space_consumer; // farm, vineyard... = true
2221+
2222 bool expansion_type; // military building used that can be used to control area
2223 bool fighting_type; // military building built near enemies
2224 bool mountain_conqueror; // military building built near mountains
2225 uint32_t prohibited_till; // do not build before (ms)
2226 uint32_t forced_after; // do not wait until ware is needed
2227- TrainingSiteType trainingsite_type;
2228+ uint8_t max_trainingsites_proportion;
2229
2230 uint16_t unconnected_count; // to any warehouse (count of such buildings)
2231
2232@@ -358,16 +435,9 @@
2233
2234 std::vector<Widelands::DescriptionIndex> inputs;
2235 std::vector<Widelands::DescriptionIndex> outputs;
2236+ std::vector<Widelands::DescriptionIndex> positions;
2237 std::vector<Widelands::DescriptionIndex> critical_building_material;
2238
2239- bool produces_building_material;
2240-
2241- // an enhancement to this building:
2242- // produces all wares as current building, and perhaps more
2243- bool upgrade_substitutes;
2244- // produces some additional wares
2245- bool upgrade_extends;
2246-
2247 // It seems that fish and meat are subsitutes (for trainingsites), so
2248 // when testing if a trainingsite is supplied enough
2249 // we count the wares together
2250@@ -388,7 +458,7 @@
2251 int32_t cnt_upgrade_pending; // number of buildings that are to be upgraded
2252
2253 // used to track amount of wares produced by building
2254- uint32_t stocklevel;
2255+ uint32_t stocklevel_count;
2256 uint32_t stocklevel_time; // time when stocklevel_ was last time recalculated
2257 uint32_t last_dismantle_time;
2258 uint32_t construction_decision_time;
2259@@ -397,25 +467,27 @@
2260 uint32_t unoccupied_count;
2261
2262 bool build_material_shortage;
2263+
2264+private:
2265+ std::set<BuildingAttribute> is_what;
2266 };
2267
2268 struct ProductionSiteObserver {
2269+ ProductionSiteObserver();
2270 Widelands::ProductionSite* site;
2271 uint32_t built_time;
2272 uint32_t unoccupied_till;
2273- uint8_t stats_zero;
2274 uint32_t no_resources_since;
2275 bool upgrade_pending;
2276+ uint32_t dismantle_pending_since;
2277 BuildingObserver* bo;
2278 };
2279
2280 struct MilitarySiteObserver {
2281 Widelands::MilitarySite* site;
2282 BuildingObserver* bo;
2283- uint8_t checks;
2284- // when considering attack most military sites are inside territory and should be skipped during
2285- // evaluation
2286- bool enemies_nearby;
2287+ uint16_t understaffed;
2288+ uint32_t last_change; // to prevent switching the occupancy policy too fast
2289 uint32_t built_time;
2290 };
2291
2292@@ -457,13 +529,15 @@
2293
2294 bool is_warehouse;
2295 int32_t attack_soldiers_strength;
2296+ int32_t attack_soldiers_competency;
2297 int32_t defenders_strength;
2298 uint8_t stationed_soldiers;
2299- uint32_t last_time_attackable;
2300+ uint32_t last_time_seen;
2301 uint32_t last_tested;
2302 int16_t score;
2303 Widelands::ExtendedBool mines_nearby;
2304- int16_t no_attack_counter;
2305+ uint32_t last_time_attacked;
2306+ uint32_t attack_counter;
2307 };
2308
2309 // as all mines have 3 levels, AI does not know total count of mines per mined material
2310@@ -475,6 +549,122 @@
2311
2312 uint16_t in_construction;
2313 uint16_t finished;
2314+ bool is_critical;
2315+ uint16_t unoccupied;
2316+};
2317+
2318+constexpr int kNeuronWeightLimit = 100;
2319+constexpr size_t kNeuronMaxPosition = 20;
2320+constexpr size_t kSecondParentProbability = 50;
2321+
2322+// A bunch of parameters used for trainig AI (for calculation of fitness function result)
2323+constexpr int16_t kCurrentLandDivider = 2;
2324+constexpr int16_t kLandDeltaMultiplier = 1;
2325+constexpr int16_t kBonus = 1000;
2326+constexpr int16_t kAttackersMultiplier = 1;
2327+constexpr int16_t kAttackBonus = 100;
2328+constexpr int16_t kTrainedSoldiersScore = 250;
2329+constexpr int16_t kConqueredWhBonus = 500;
2330+
2331+struct Neuron {
2332+ static int clip_weight_to_range(int w) {
2333+ assert(w < 125);
2334+ assert(w > -125);
2335+ return std::max(-kNeuronWeightLimit, std::min(kNeuronWeightLimit, w));
2336+ }
2337+
2338+ Neuron(int8_t, uint8_t, uint16_t);
2339+ void recalculate();
2340+ void set_weight(int8_t w);
2341+ int8_t get_weight() {
2342+ return weight;
2343+ }
2344+ int8_t get_result(size_t);
2345+ int8_t get_result_safe(int32_t, bool = false);
2346+ void set_type(uint8_t);
2347+ uint8_t get_type() {
2348+ return type;
2349+ }
2350+ uint16_t get_id() {
2351+ return id;
2352+ }
2353+
2354+private:
2355+ int8_t results[21]; // kPositions + 1
2356+ int8_t weight;
2357+ uint8_t type;
2358+ uint16_t id;
2359+};
2360+
2361+struct FNeuron {
2362+ FNeuron(uint32_t, uint16_t);
2363+
2364+ void flip_bit(uint8_t);
2365+ bool get_result(bool, bool, bool, bool bool4 = true, bool bool5 = true);
2366+ bool get_position(uint8_t);
2367+ uint32_t get_int();
2368+ uint16_t get_id() {
2369+ return id;
2370+ };
2371+
2372+private:
2373+ std::bitset<kFNeuronBitSize> core;
2374+ uint16_t id;
2375+};
2376+
2377+struct ExpansionType {
2378+ ExpansionType();
2379+
2380+ void set_expantion_type(ExpansionMode);
2381+ ExpansionMode get_expansion_type() {
2382+ return type;
2383+ };
2384+
2385+private:
2386+ ExpansionMode type;
2387+};
2388+
2389+// This is to keep all data related to AI magic numbers
2390+struct ManagementData {
2391+ ManagementData();
2392+
2393+ std::vector<Neuron> neuron_pool;
2394+ std::vector<FNeuron> f_neuron_pool;
2395+ Widelands::Player::AiPersistentState* pd;
2396+
2397+ void mutate(PlayerNumber = 0);
2398+ void new_dna_for_persistent(uint8_t, Widelands::AiType);
2399+ void copy_persistent_to_local();
2400+ void review(
2401+ uint32_t, PlayerNumber, uint32_t, uint32_t, uint32_t, uint16_t, int16_t, int16_t, uint16_t);
2402+ void dump_data();
2403+ uint16_t new_neuron_id() {
2404+ ++next_neuron_id;
2405+ return next_neuron_id - 1;
2406+ };
2407+ void reset_neuron_id() {
2408+ next_neuron_id = 0;
2409+ }
2410+ uint16_t new_bi_neuron_id() {
2411+ ++next_bi_neuron_id;
2412+ return next_bi_neuron_id - 1;
2413+ };
2414+ void reset_bi_neuron_id() {
2415+ next_bi_neuron_id = 0;
2416+ }
2417+ int16_t get_military_number_at(uint8_t);
2418+ void set_military_number_at(uint8_t, int16_t);
2419+ MutatingIntensity do_mutate(uint8_t, int16_t);
2420+ int8_t shift_weight_value(int8_t, bool = true);
2421+ void test_consistency(bool = false);
2422+
2423+private:
2424+ int32_t score;
2425+ uint8_t primary_parent;
2426+ uint16_t next_neuron_id;
2427+ uint16_t next_bi_neuron_id;
2428+ uint16_t performance_change;
2429+ Widelands::AiType ai_type;
2430 };
2431
2432 // this is used to count militarysites by their size
2433@@ -492,7 +682,7 @@
2434 const uint8_t p,
2435 const char* d);
2436
2437- bool operator<(SchedulerTask other) const;
2438+ bool operator<(const SchedulerTask& other) const;
2439
2440 uint32_t due_time;
2441 Widelands::SchedulerTaskId id;
2442@@ -560,34 +750,87 @@
2443 // Updating walking distance over existing roads
2444 void set_road_distance(Widelands::Coords coords, int32_t distance);
2445 // Finally we query the flag that we will build a road to
2446- bool get_winner(uint32_t* winner_hash, uint32_t pos);
2447+ bool get_winner(uint32_t* winner_hash);
2448 };
2449
2450 // This is a struct that stores strength of players, info on teams and provides some outputs from
2451 // these data
2452 struct PlayersStrengths {
2453+private:
2454 struct PlayerStat {
2455 PlayerStat();
2456- PlayerStat(Widelands::TeamNumber tc, uint32_t pp);
2457+ PlayerStat(Widelands::TeamNumber tc,
2458+ bool en,
2459+ uint32_t pp,
2460+ uint32_t op,
2461+ uint32_t o60p,
2462+ uint32_t cs,
2463+ uint32_t land,
2464+ uint32_t oland,
2465+ uint32_t o60l);
2466
2467 Widelands::TeamNumber team_number;
2468+ bool is_enemy;
2469 uint32_t players_power;
2470+ uint32_t old_players_power;
2471+ uint32_t old60_players_power;
2472+ uint32_t players_casualities;
2473+ uint32_t last_time_seen;
2474+ uint32_t players_land;
2475+ uint32_t old_players_land;
2476+ uint32_t old60_players_land;
2477 };
2478
2479+public:
2480+ PlayersStrengths();
2481 // Inserting/updating data
2482- void add(Widelands::PlayerNumber pn, Widelands::TeamNumber tn, uint32_t pp);
2483+ void add(Widelands::PlayerNumber pn,
2484+ Widelands::PlayerNumber opn,
2485+ Widelands::TeamNumber mytn,
2486+ Widelands::TeamNumber pltn,
2487+ uint32_t pp,
2488+ uint32_t op,
2489+ uint32_t o60p,
2490+ uint32_t cs,
2491+ uint32_t land,
2492+ uint32_t oland,
2493+ uint32_t o60l);
2494 void recalculate_team_power();
2495
2496 // This is strength of player plus third of strength of other members of his team
2497 uint32_t get_modified_player_power(Widelands::PlayerNumber pn);
2498+ uint32_t get_player_power(Widelands::PlayerNumber pn);
2499+ uint32_t get_old_player_power(Widelands::PlayerNumber pn);
2500+ uint32_t get_old60_player_power(Widelands::PlayerNumber pn);
2501+ uint32_t get_player_land(Widelands::PlayerNumber pn);
2502+ uint32_t get_old_player_land(Widelands::PlayerNumber pn);
2503+ uint32_t get_old60_player_land(Widelands::PlayerNumber pn);
2504+ uint32_t get_visible_enemies_power(uint32_t);
2505+ uint32_t get_enemies_average_power();
2506+ uint32_t get_enemies_average_land();
2507+ uint32_t get_enemies_max_power();
2508+ uint32_t get_enemies_max_land();
2509+ uint32_t get_old_visible_enemies_power(uint32_t);
2510+ uint32_t get_player_casualities(Widelands::PlayerNumber pn);
2511 bool players_in_same_team(Widelands::PlayerNumber pl1, Widelands::PlayerNumber pl2);
2512 bool strong_enough(Widelands::PlayerNumber pl);
2513+ void set_last_time_seen(uint32_t, Widelands::PlayerNumber);
2514+ bool player_seen_lately(Widelands::PlayerNumber, uint32_t);
2515+ bool get_is_enemy(Widelands::PlayerNumber);
2516+ uint8_t enemies_seen_lately_count(uint32_t);
2517+ bool any_enemy_seen_lately(uint32_t);
2518+ PlayerNumber this_player_number;
2519+ void set_update_time(uint32_t);
2520+ uint32_t get_update_time();
2521
2522+private:
2523 // This is the core part of this struct
2524 std::map<Widelands::PlayerNumber, PlayerStat> all_stats;
2525
2526 // Number of team, sum of players' strength
2527 std::map<Widelands::TeamNumber, uint32_t> team_powers;
2528+
2529+ uint32_t update_time;
2530 };
2531 } // namespace Widelands
2532
2533
2534=== modified file 'src/ai/ai_hints.cc'
2535--- src/ai/ai_hints.cc 2017-05-25 07:16:05 +0000
2536+++ src/ai/ai_hints.cc 2017-07-03 17:39:38 +0000
2537@@ -38,27 +38,19 @@
2538 table->has_key("mountain_conqueror") ? table->get_bool("mountain_conqueror") : false),
2539 shipyard_(table->has_key("shipyard") ? table->get_bool("shipyard") : false),
2540 prohibited_till_(table->has_key("prohibited_till") ? table->get_int("prohibited_till") : 0),
2541+ basic_amount_(table->has_key("basic_amount") ? table->get_int("basic_amount") : 0),
2542 // 10 days default
2543 forced_after_(table->has_key("forced_after") ? table->get_int("forced_after") : 864000),
2544 mines_percent_(table->has_key("mines_percent") ? table->get_int("mines_percent") : 100),
2545 very_weak_ai_limit_(
2546 table->has_key("very_weak_ai_limit") ? table->get_int("very_weak_ai_limit") : -1),
2547 weak_ai_limit_(table->has_key("weak_ai_limit") ? table->get_int("weak_ai_limit") : -1),
2548- trainingsite_type_(TrainingSiteType::kNoTS),
2549 trainingsites_max_percent_(table->has_key("trainingsites_max_percent") ?
2550 table->get_int("trainingsites_max_percent") :
2551 0) {
2552-
2553- if (table->has_key("trainingsite_type")) {
2554- if (table->get_string("trainingsite_type") == "basic") {
2555- trainingsite_type_ = TrainingSiteType::kBasic;
2556- } else if (table->get_string("trainingsite_type") == "advanced") {
2557- trainingsite_type_ = TrainingSiteType::kAdvanced;
2558- }
2559- }
2560 }
2561
2562-void BuildingHints::set_trainingsites_max_percent(uint8_t percent) {
2563+void BuildingHints::set_trainingsites_max_percent(int percent) {
2564 trainingsites_max_percent_ = percent;
2565 }
2566
2567
2568=== modified file 'src/ai/ai_hints.h'
2569--- src/ai/ai_hints.h 2017-06-24 08:47:46 +0000
2570+++ src/ai/ai_hints.h 2017-07-03 17:39:38 +0000
2571@@ -28,8 +28,6 @@
2572 #include "base/macros.h"
2573 #include "scripting/lua_table.h"
2574
2575-enum class TrainingSiteType : uint8_t { kNoTS = 0, kBasic = 1, kAdvanced = 2 };
2576-
2577 /// This struct is used to read out the data given in [aihints] section of a
2578 /// buildings conf file. It is used to tell the computer player about the
2579 /// special properties of a building.
2580@@ -95,6 +93,10 @@
2581 return prohibited_till_;
2582 }
2583
2584+ uint32_t basic_amount() const {
2585+ return basic_amount_;
2586+ }
2587+
2588 uint32_t get_forced_after() const {
2589 return forced_after_;
2590 }
2591@@ -111,11 +113,7 @@
2592 return weak_ai_limit_;
2593 }
2594
2595- TrainingSiteType get_trainingsite_type() const {
2596- return trainingsite_type_;
2597- }
2598-
2599- void set_trainingsites_max_percent(uint8_t percent);
2600+ void set_trainingsites_max_percent(int percent);
2601
2602 uint8_t trainingsites_max_percent() const;
2603
2604@@ -133,11 +131,11 @@
2605 bool mountain_conqueror_;
2606 bool shipyard_;
2607 int32_t prohibited_till_;
2608+ uint32_t basic_amount_;
2609 int32_t forced_after_;
2610 int8_t mines_percent_;
2611 int16_t very_weak_ai_limit_;
2612 int16_t weak_ai_limit_;
2613- TrainingSiteType trainingsite_type_;
2614 int trainingsites_max_percent_;
2615
2616 DISALLOW_COPY_AND_ASSIGN(BuildingHints);
2617
2618=== modified file 'src/ai/defaultai.cc'
2619--- src/ai/defaultai.cc 2017-06-24 20:22:19 +0000
2620+++ src/ai/defaultai.cc 2017-07-03 17:39:38 +0000
2621@@ -62,13 +62,16 @@
2622 constexpr int kMinBFCheckInterval = 5 * 1000;
2623 constexpr int kMinMFCheckInterval = 19 * 1000;
2624 constexpr int kMarineDecisionInterval = 20 * 1000;
2625+constexpr int kRemainingBasicBuildingsResetTime = 1 * 60 * 1000;
2626
2627 // following two are used for roads management, for creating shortcuts and dismantling dispensable
2628 // roads
2629 constexpr int32_t kSpotsTooLittle = 15;
2630 constexpr int32_t kSpotsEnough = 25;
2631
2632-// this is intended for map developers, by default should be off
2633+constexpr uint16_t kTargetQuantCap = 30;
2634+
2635+// this is intended for map developers & testers, should be off by default
2636 constexpr bool kPrintStats = false;
2637
2638 // for scheduler
2639@@ -81,37 +84,29 @@
2640 DefaultAI::VeryWeakImpl DefaultAI::very_weak_impl;
2641
2642 /// Constructor of DefaultAI
2643-DefaultAI::DefaultAI(Game& ggame, PlayerNumber const pid, DefaultAI::Type const t)
2644+DefaultAI::DefaultAI(Game& ggame, PlayerNumber const pid, Widelands::AiType const t)
2645 : ComputerPlayer(ggame, pid),
2646 type_(t),
2647 player_(nullptr),
2648 tribe_(nullptr),
2649+ attackers_count_(0),
2650 next_ai_think_(0),
2651 scheduler_delay_counter_(0),
2652 wood_policy_(WoodPolicy::kAllowRangers),
2653- num_prod_constructionsites(0),
2654+ numof_psites_in_constr(0),
2655 num_ports(0),
2656 numof_warehouses_(0),
2657+ numof_warehouses_in_const_(0),
2658 military_last_dismantle_(0),
2659 military_last_build_(0),
2660 time_of_last_construction_(0),
2661 next_mine_construction_due_(0),
2662- ts_basic_count_(0),
2663- ts_basic_const_count_(0),
2664- ts_advanced_count_(0),
2665- ts_advanced_const_count_(0),
2666+ fishers_count_(0),
2667+ bakeries_count_(),
2668+ ts_finished_count_(0),
2669+ ts_in_const_count_(0),
2670 ts_without_trainers_(0),
2671 inhibit_road_building_(0),
2672- last_road_dismantled_(0),
2673- enemy_last_seen_(kNever),
2674- vacant_mil_positions_(0),
2675- last_attack_time_(0),
2676- enemysites_check_delay_(60),
2677- spots_(0),
2678- new_buildings_stop_(false),
2679- resource_necessity_territory_(100),
2680- resource_necessity_mines_(100),
2681- resource_necessity_water_(0),
2682 resource_necessity_water_needed_(false),
2683 highest_nonmil_prio_(0),
2684 seafaring_economy(false),
2685@@ -249,6 +244,8 @@
2686
2687 const int32_t delay_time = gametime - taskPool.front().due_time;
2688
2689+ Map& map = game().map();
2690+
2691 // Here we decide how many jobs will be run now (none - 5)
2692 // in case no job is due now, it can be zero
2693 uint32_t jobs_to_run_count = (delay_time < 0) ? 0 : 1;
2694@@ -400,7 +397,7 @@
2695 // checking 3 mines if possible
2696 {
2697 int32_t mines_to_check = (mines_.size() < 5) ? mines_.size() : 5;
2698- for (int j = 0; j < mines_to_check; j += 1) {
2699+ for (int j = 0; j < mines_to_check; ++j) {
2700 // every run of check_mines_() checks one mine
2701 if (check_mines_(gametime)) {
2702 // if significant change takes place do not go on
2703@@ -411,9 +408,11 @@
2704 break;
2705 case SchedulerTaskId::kCheckMilitarysites:
2706 // just to be sure the value is reset
2707- // 4 seconds is really fine
2708- set_taskpool_task_time(gametime + 4 * 1000, SchedulerTaskId::kCheckMilitarysites);
2709- check_militarysites(gametime);
2710+ if (check_militarysites(gametime)) {
2711+ set_taskpool_task_time(gametime + 15 * 1000, SchedulerTaskId::kCheckMilitarysites);
2712+ } else {
2713+ set_taskpool_task_time(gametime + 4 * 1000, SchedulerTaskId::kCheckMilitarysites);
2714+ }
2715 break;
2716 case SchedulerTaskId::kCheckTrainingsites:
2717 set_taskpool_task_time(
2718@@ -422,7 +421,7 @@
2719 break;
2720 case SchedulerTaskId::kCountMilitaryVacant:
2721 count_military_vacant_positions();
2722- set_taskpool_task_time(gametime + 45 * 1000, SchedulerTaskId::kCountMilitaryVacant);
2723+ set_taskpool_task_time(gametime + 25 * 1000, SchedulerTaskId::kCountMilitaryVacant);
2724 break;
2725 case SchedulerTaskId::kWareReview:
2726 if (check_economies()) { // economies must be consistent
2727@@ -435,13 +434,38 @@
2728 if (check_economies()) { // economies must be consistent
2729 return;
2730 }
2731- set_taskpool_task_time(gametime + 30 * 60 * 1000, SchedulerTaskId::kPrintStats);
2732- print_stats();
2733+ set_taskpool_task_time(gametime + 10 * 60 * 1000, SchedulerTaskId::kPrintStats);
2734+ print_stats(gametime);
2735 break;
2736 case SchedulerTaskId::kCheckEnemySites:
2737 check_enemy_sites(gametime);
2738 set_taskpool_task_time(gametime + 19 * 1000, SchedulerTaskId::kCheckEnemySites);
2739 break;
2740+ case SchedulerTaskId::kManagementUpdate:
2741+ // This task is used for training the AI, so it should be usually disabled
2742+ { // statistics for spotted warehouses
2743+ uint16_t conquered_wh = 0;
2744+ for (auto coords : enemy_warehouses) {
2745+ if (get_land_owner(map, coords) == player_number()) {
2746+ ++conquered_wh;
2747+ }
2748+ };
2749+ if (!enemy_warehouses.empty())
2750+ log("Conquered warehouses: %d / %lu\n", conquered_wh, enemy_warehouses.size());
2751+ management_data.review(gametime, player_number(),
2752+ player_statistics.get_player_land(player_number()),
2753+ player_statistics.get_enemies_max_land(),
2754+ player_statistics.get_old60_player_land(player_number()),
2755+ attackers_count_, soldier_trained_log.count(gametime),
2756+ soldier_attacks_log.count(gametime), conquered_wh);
2757+ set_taskpool_task_time(
2758+ gametime + kManagementUpdateInterval, SchedulerTaskId::kManagementUpdate);
2759+ }
2760+ break;
2761+ case SchedulerTaskId::kUpdateStats:
2762+ update_player_stat(gametime);
2763+ set_taskpool_task_time(gametime + kStatUpdateInterval, SchedulerTaskId::kUpdateStats);
2764+ break;
2765 case SchedulerTaskId::kUnset:
2766 NEVER_HERE();
2767 }
2768@@ -474,7 +498,92 @@
2769
2770 const DescriptionIndex& nr_buildings = game().tribes().nrbuildings();
2771
2772- // Collect information about the different buildings that our tribe can have
2773+ // The data struct below is owned by Player object, the purpose is to have them saved therein
2774+ persistent_data = player_->get_mutable_ai_persistent_state();
2775+ management_data.pd = player_->get_mutable_ai_persistent_state();
2776+ const bool create_basic_buildings_list = (gametime < kRemainingBasicBuildingsResetTime);
2777+
2778+ if (persistent_data->initialized == kFalse) {
2779+ // As all data are initialized without given values, they must be populated with reasonable
2780+ // values first
2781+ persistent_data->colony_scan_area = kColonyScanStartArea;
2782+ persistent_data->trees_around_cutters = 0;
2783+ persistent_data->initialized = kTrue;
2784+ persistent_data->last_attacked_player = std::numeric_limits<int16_t>::max();
2785+ persistent_data->expedition_start_time = kNoExpedition;
2786+ persistent_data->ships_utilization = 200;
2787+ persistent_data->no_more_expeditions = kFalse;
2788+ persistent_data->target_military_score = 100;
2789+ persistent_data->least_military_score = 0;
2790+ persistent_data->ai_productionsites_ratio = std::rand() % 5 + 7;
2791+ persistent_data->ai_personality_mil_upper_limit = 100;
2792+
2793+ // all zeroes
2794+ assert(persistent_data->neuron_weights.empty());
2795+ assert(persistent_data->neuron_functs.empty());
2796+ assert(persistent_data->magic_numbers_size == 0);
2797+ assert(persistent_data->neuron_pool_size == 0);
2798+ assert(persistent_data->magic_numbers.empty());
2799+
2800+ // AI's DNA population
2801+ management_data.new_dna_for_persistent(player_number(), type_);
2802+ management_data.copy_persistent_to_local();
2803+ management_data.mutate(player_number());
2804+ if (kAITrainingMode) {
2805+ management_data.dump_data();
2806+ }
2807+
2808+ management_data.test_consistency(true);
2809+ assert(management_data.get_military_number_at(42) ==
2810+ management_data.get_military_number_at(kMutationRatePosition));
2811+
2812+ } else if (persistent_data->initialized == kTrue) {
2813+ // Doing some consistency checks
2814+ check_range<uint32_t>(
2815+ persistent_data->expedition_start_time, gametime, "expedition_start_time");
2816+ check_range<uint16_t>(persistent_data->ships_utilization, 0, 10000, "ships_utilization_");
2817+
2818+ // for backward consistency
2819+ if (persistent_data->ai_personality_mil_upper_limit <
2820+ persistent_data->target_military_score) {
2821+ persistent_data->ai_personality_mil_upper_limit = persistent_data->target_military_score;
2822+ }
2823+ if (persistent_data->least_military_score > persistent_data->target_military_score) {
2824+ persistent_data->least_military_score = persistent_data->target_military_score;
2825+ }
2826+
2827+ if (kAITrainingMode) {
2828+ log("%2d: reinitializing dna (kAITrainingMode set true)", player_number());
2829+ management_data.new_dna_for_persistent(player_number(), type_);
2830+ management_data.copy_persistent_to_local();
2831+ management_data.mutate(player_number());
2832+
2833+ } else {
2834+ management_data.copy_persistent_to_local();
2835+ }
2836+
2837+ management_data.test_consistency(true);
2838+ if (kAITrainingMode) {
2839+ management_data.dump_data();
2840+ }
2841+
2842+ log(" %2d: %lu basic buildings in savegame file. %s\n", player_number(),
2843+ persistent_data->remaining_basic_buildings.size(),
2844+ (create_basic_buildings_list) ?
2845+ "New list will be recreated though (kAITrainingMode is true)" :
2846+ "");
2847+
2848+ } else {
2849+ throw wexception("Corrupted AI data");
2850+ }
2851+
2852+ // Even if we have basic buildings from savefile, we ignore them and recreate them based
2853+ // on lua conf files
2854+ if (create_basic_buildings_list) {
2855+ persistent_data->remaining_basic_buildings.clear();
2856+ persistent_data->remaining_buildings_size = 0;
2857+ }
2858+
2859 for (DescriptionIndex building_index = 0; building_index < nr_buildings; ++building_index) {
2860 const BuildingDescr& bld = *tribe_->get_building_descr(building_index);
2861 if (!tribe_->has_building(building_index) && bld.type() != MapObjectType::MILITARYSITE) {
2862@@ -494,7 +603,7 @@
2863 bo.cnt_target = 1; // default for everything
2864 bo.cnt_limit_by_aimode = std::numeric_limits<int32_t>::max();
2865 bo.cnt_upgrade_pending = 0;
2866- bo.stocklevel = 0;
2867+ bo.stocklevel_count = 0;
2868 bo.stocklevel_time = 0;
2869 bo.last_dismantle_time = 0;
2870 // this is set to negative number, otherwise the AI would wait 25 sec
2871@@ -508,23 +617,41 @@
2872 bo.unconnected_count = 0;
2873 bo.new_building_overdue = 0;
2874 bo.primary_priority = 0;
2875- bo.is_buildable = bld.is_buildable();
2876- bo.need_trees = bh.is_logproducer();
2877- bo.need_rocks = bh.is_graniteproducer();
2878- bo.need_water = bh.get_needs_water();
2879- bo.mines_water = bh.mines_water();
2880- bo.recruitment = bh.for_recruitment();
2881- bo.space_consumer = bh.is_space_consumer();
2882+ if (bld.is_buildable()) {
2883+ bo.set_is(BuildingAttribute::kBuildable);
2884+ }
2885+ if (bh.is_logproducer()) {
2886+ bo.set_is(BuildingAttribute::kLumberjack);
2887+ }
2888+ if (bh.is_graniteproducer()) {
2889+ bo.set_is(BuildingAttribute::kNeedsRocks);
2890+ }
2891+ if (create_basic_buildings_list &&
2892+ bh.basic_amount() > 0) { // This is the very begining of the game
2893+ persistent_data->remaining_basic_buildings[bo.id] = bh.basic_amount();
2894+ ++persistent_data->remaining_buildings_size;
2895+ }
2896+ if (bh.get_needs_water()) {
2897+ bo.set_is(BuildingAttribute::kNeedsCoast);
2898+ }
2899+ if (bh.mines_water()) {
2900+ bo.set_is(BuildingAttribute::kWell);
2901+ }
2902+ if (bh.is_space_consumer()) {
2903+ bo.set_is(BuildingAttribute::kSpaceConsumer);
2904+ }
2905+ if (bh.for_recruitment()) {
2906+ bo.set_is(BuildingAttribute::kRecruitment);
2907+ }
2908 bo.expansion_type = bh.is_expansion_type();
2909 bo.fighting_type = bh.is_fighting_type();
2910 bo.mountain_conqueror = bh.is_mountain_conqueror();
2911 bo.prohibited_till = bh.get_prohibited_till() * 1000; // value in conf is in seconds
2912 bo.forced_after = bh.get_forced_after() * 1000; // value in conf is in seconds
2913- bo.is_port = bld.get_isport();
2914- bo.trainingsite_type = TrainingSiteType::kNoTS;
2915- bo.upgrade_substitutes = false;
2916- bo.upgrade_extends = false;
2917- bo.produces_building_material = false;
2918+ if (bld.get_isport()) {
2919+ bo.set_is(BuildingAttribute::kPort);
2920+ }
2921+ bo.max_trainingsites_proportion = 100;
2922 bo.max_preciousness = 0;
2923 bo.max_needed_preciousness = 0;
2924
2925@@ -534,18 +661,16 @@
2926
2927 // I just presume cut wood is named "log" in the game
2928 if (tribe_->safe_ware_index("log") == bo.production_hint) {
2929- bo.plants_trees = true;
2930- } else {
2931- bo.plants_trees = false;
2932+ bo.set_is(BuildingAttribute::kRanger);
2933 }
2934
2935 // Is total count of this building limited by AI mode?
2936- if (type_ == DefaultAI::Type::kVeryWeak && bh.get_very_weak_ai_limit() >= 0) {
2937+ if (type_ == Widelands::AiType::kVeryWeak && bh.get_very_weak_ai_limit() >= 0) {
2938 bo.cnt_limit_by_aimode = bh.get_very_weak_ai_limit();
2939 log(" %d: AI 'very weak' mode: applying limit %d building(s) for %s\n", player_number(),
2940 bo.cnt_limit_by_aimode, bo.name);
2941 }
2942- if (type_ == DefaultAI::Type::kWeak && bh.get_weak_ai_limit() >= 0) {
2943+ if (type_ == Widelands::AiType::kWeak && bh.get_weak_ai_limit() >= 0) {
2944 bo.cnt_limit_by_aimode = bh.get_weak_ai_limit();
2945 log(" %d: AI 'weak' mode: applying limit %d building(s) for %s\n", player_number(),
2946 bo.cnt_limit_by_aimode, bo.name);
2947@@ -562,6 +687,11 @@
2948 for (const DescriptionIndex& temp_output : prod.output_ware_types()) {
2949 bo.outputs.push_back(temp_output);
2950 }
2951+ for (const auto temp_position : prod.working_positions()) {
2952+ bo.positions.push_back(temp_position.first);
2953+ }
2954+
2955+ iron_ore_id = tribe_->ironore();
2956
2957 if (bo.type == BuildingObserver::Type::kMine) {
2958 // get the resource needed by the mine
2959@@ -575,23 +705,36 @@
2960 if (mines_per_type.count(bo.mines) == 0) {
2961 mines_per_type[bo.mines] = MineTypesObserver();
2962 }
2963+ // Identify iron mines based on output
2964+ if (bo.outputs[0] == iron_ore_id) {
2965+ bo.set_is(BuildingAttribute::kIronMine);
2966+ mines_per_type[bo.mines].is_critical = true;
2967+ }
2968 }
2969
2970 // here we identify hunters
2971 if (bo.outputs.size() == 1 && tribe_->safe_ware_index("meat") == bo.outputs.at(0)) {
2972- bo.is_hunter = true;
2973- } else {
2974- bo.is_hunter = false;
2975+ bo.set_is(BuildingAttribute::kHunter);
2976 }
2977
2978 // and fishers
2979 if (bo.outputs.size() == 1 && tribe_->safe_ware_index("fish") == bo.outputs.at(0)) {
2980- bo.is_fisher = true;
2981- } else {
2982- bo.is_fisher = false;
2983+ bo.set_is(BuildingAttribute::kFisher);
2984 }
2985
2986- bo.is_shipyard = bh.is_shipyard();
2987+ if (bh.is_shipyard()) {
2988+ bo.set_is(BuildingAttribute::kShipyard);
2989+ }
2990+ if (building_index == tribe_->barracks()) {
2991+ bo.set_is(BuildingAttribute::kBarracks);
2992+ }
2993+ if (building_index == tribe_->bakery()) {
2994+ bo.set_is(BuildingAttribute::kBakery);
2995+ }
2996+ // Identify refined log producer
2997+ if (bo.outputs.size() == 1 && bo.outputs[0] == tribe_->refinedlog()) {
2998+ bo.set_is(BuildingAttribute::kLogRefiner);
2999+ }
3000
3001 // now we find out if the upgrade of the building is a full substitution
3002 // (produces all wares as current one)
3003@@ -607,10 +750,10 @@
3004 }
3005 // now testing outputs of current building
3006 // and comparing
3007- bo.upgrade_substitutes = true;
3008+ bo.set_is(BuildingAttribute::kUpgradeSubstitutes);
3009 for (DescriptionIndex ware : bo.outputs) {
3010 if (enh_outputs.count(ware) == 0) {
3011- bo.upgrade_substitutes = false;
3012+ bo.unset_is(BuildingAttribute::kUpgradeSubstitutes);
3013 break;
3014 }
3015 }
3016@@ -620,40 +763,50 @@
3017 for (const DescriptionIndex& ware : bo.outputs) {
3018 cur_outputs.insert(ware);
3019 }
3020- bo.upgrade_extends = false;
3021+ // Does upgraded building produce any different outputs?
3022 for (DescriptionIndex ware : enh_outputs) {
3023 if (cur_outputs.count(ware) == 0) {
3024- bo.upgrade_extends = true;
3025+ bo.set_is(BuildingAttribute::kUpgradeExtends);
3026 break;
3027 }
3028 }
3029 }
3030
3031 // now we identify producers of critical build materials
3032- // hardwood now
3033 for (DescriptionIndex ware : bo.outputs) {
3034- // iterating over wares subsitutes
3035- if (tribe_->ware_index("wood") == ware || tribe_->ware_index("blackwood") == ware ||
3036- tribe_->ware_index("marble") == ware || tribe_->ware_index("planks") == ware) {
3037- bo.produces_building_material = true;
3038- }
3039- }
3040+ // building material except for trivial material
3041+ if (tribe_->is_construction_material(ware) &&
3042+ !(ware == tribe_->rawlog() || ware == tribe_->granite())) {
3043+ bo.set_is(BuildingAttribute::kBuildingMatProducer);
3044+ if (bo.type == BuildingObserver::Type::kMine) {
3045+ has_critical_mines = true;
3046+ mines_per_type[bo.mines].is_critical = true;
3047+ }
3048+ }
3049+ }
3050+
3051+ for (const auto& temp_buildcosts : prod.buildcost()) {
3052+ // building material except for trivial material
3053+ if (tribe_->is_construction_material(temp_buildcosts.first) &&
3054+ !(temp_buildcosts.first == tribe_->rawlog() ||
3055+ temp_buildcosts.first == tribe_->granite())) {
3056+ bo.critical_building_material.push_back(temp_buildcosts.first);
3057+ }
3058+ }
3059+
3060 continue;
3061 }
3062
3063 // now for every military building, we fill critical_building_material vector
3064 // with critical construction wares
3065- // non critical are excluded (see below)
3066 if (bld.type() == MapObjectType::MILITARYSITE) {
3067 bo.type = BuildingObserver::Type::kMilitarysite;
3068 const MilitarySiteDescr& milit = dynamic_cast<const MilitarySiteDescr&>(bld);
3069 for (const auto& temp_buildcosts : milit.buildcost()) {
3070- // bellow are non-critical wares (well, various types of wood)
3071- if (tribe_->ware_index("log") == temp_buildcosts.first ||
3072- tribe_->ware_index("blackwood") == temp_buildcosts.first ||
3073- tribe_->ware_index("planks") == temp_buildcosts.first)
3074+ // Below are non-critical wares (well, various types of wood)
3075+ if (temp_buildcosts.first == tribe_->rawlog() ||
3076+ temp_buildcosts.first == tribe_->refinedlog())
3077 continue;
3078-
3079 bo.critical_building_material.push_back(temp_buildcosts.first);
3080 }
3081 continue;
3082@@ -666,6 +819,8 @@
3083
3084 if (bld.type() == MapObjectType::TRAININGSITE) {
3085 bo.type = BuildingObserver::Type::kTrainingsite;
3086+ bo.max_trainingsites_proportion = bh.trainingsites_max_percent();
3087+ assert(bo.max_trainingsites_proportion <= 100);
3088 const TrainingSiteDescr& train = dynamic_cast<const TrainingSiteDescr&>(bld);
3089 for (const auto& temp_input : train.input_wares()) {
3090 bo.inputs.push_back(temp_input.first);
3091@@ -678,19 +833,17 @@
3092 bo.substitute_inputs.insert(temp_input.first);
3093 }
3094
3095+ // Creating vector with critical material, to be used to discourage
3096+ // building of new sites if ware is lacking
3097 for (const auto& temp_buildcosts : train.buildcost()) {
3098- // critical wares for trainingsites
3099- if (tribe_->ware_index("spidercloth") == temp_buildcosts.first ||
3100- tribe_->ware_index("gold") == temp_buildcosts.first ||
3101- tribe_->ware_index("grout") == temp_buildcosts.first) {
3102+ // building material except for trivial material
3103+ if (!(temp_buildcosts.first == tribe_->rawlog() ||
3104+ temp_buildcosts.first == tribe_->refinedlog() ||
3105+ temp_buildcosts.first == tribe_->granite())) {
3106 bo.critical_building_material.push_back(temp_buildcosts.first);
3107 }
3108 }
3109 }
3110- bo.trainingsite_type = bh.get_trainingsite_type();
3111- // it would behave badly if no type was set
3112- // make sure all TS have its type set properly in conf files
3113- assert(bo.trainingsite_type != TrainingSiteType::kNoTS);
3114 continue;
3115 }
3116
3117@@ -700,6 +853,16 @@
3118 }
3119 }
3120
3121+ // We must verify that some buildings has been identified
3122+ // Also note that the AI assumes that some buildings are unique, if you want to
3123+ // create e.g. two barracks or bakeries, the impact on the AI must be considered
3124+ assert(count_buildings_with_attribute(BuildingAttribute::kBarracks) == 1);
3125+ assert(count_buildings_with_attribute(BuildingAttribute::kBakery) == 1);
3126+ assert(count_buildings_with_attribute(BuildingAttribute::kLogRefiner) == 1);
3127+ assert(count_buildings_with_attribute(BuildingAttribute::kIronMine) >= 1);
3128+ // If there will be a tribe with more than 3 mines of the same type, just increase the number
3129+ assert(count_buildings_with_attribute(BuildingAttribute::kIronMine) <= 3);
3130+
3131 // atlanteans they consider water as a resource
3132 // (together with mines, rocks and wood)
3133 if (tribe_->name() == "atlanteans") {
3134@@ -743,13 +906,21 @@
3135 "check unbuildable fields"));
3136 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 15 * 60 * 1000),
3137 SchedulerTaskId::kWareReview, 9, "wares review"));
3138- taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 30 * 60 * 1000),
3139+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 10 * 60 * 1000),
3140 SchedulerTaskId::kPrintStats, 9, "print statistics"));
3141 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 1 * 60 * 1000),
3142 SchedulerTaskId::kCountMilitaryVacant, 2,
3143 "count military vacant"));
3144 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 10 * 60 * 1000),
3145 SchedulerTaskId::kCheckEnemySites, 6, "check enemy sites"));
3146+ if (kAITrainingMode) {
3147+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 10 * 1000),
3148+ SchedulerTaskId::kManagementUpdate, 8, "reviewing"));
3149+ }
3150+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 9 * 1000),
3151+ SchedulerTaskId::kUpdateStats, 6, "update player stats"));
3152+ taskPool.push_back(SchedulerTask(
3153+ std::max<uint32_t>(gametime, 10 * 1000), SchedulerTaskId::kUpdateStats, 15, "review"));
3154
3155 Map& map = game().map();
3156
3157@@ -821,7 +992,8 @@
3158
3159 // blocking space consumers vicinity (when reloading a game)
3160 for (const ProductionSiteObserver& ps_obs : productionsites) {
3161- if (ps_obs.bo->space_consumer && !ps_obs.bo->plants_trees) {
3162+ if (ps_obs.bo->is(BuildingAttribute::kSpaceConsumer) &&
3163+ !ps_obs.bo->is(BuildingAttribute::kRanger)) {
3164 MapRegion<Area<FCoords>> mr(
3165 map, Area<FCoords>(map.get_fcoords(ps_obs.site->get_position()), 4));
3166 do {
3167@@ -830,45 +1002,19 @@
3168 }
3169 }
3170
3171- // The data struct below is owned by Player object, the purpose is to have them saved therein
3172- persistent_data = player_->get_mutable_ai_persistent_state();
3173-
3174- if (persistent_data->initialized == kFalse) {
3175- // As all data are initialized without given values, they must be populated with reasonable
3176- // values first
3177- persistent_data->colony_scan_area = kColonyScanStartArea;
3178- persistent_data->trees_around_cutters = 0;
3179- persistent_data->initialized = kTrue;
3180- persistent_data->last_attacked_player = std::numeric_limits<int16_t>::max();
3181- persistent_data->expedition_start_time = kNoExpedition;
3182- persistent_data->ships_utilization = 200;
3183- persistent_data->no_more_expeditions = kFalse;
3184- persistent_data->target_military_score = 0;
3185- persistent_data->least_military_score = 100;
3186- persistent_data->ai_personality_military_loneliness = std::rand() % 5 * 30 - 60;
3187- persistent_data->ai_personality_attack_margin = std::max(std::rand() % 20 - 5, 0);
3188- persistent_data->ai_productionsites_ratio = std::rand() % 5 + 7;
3189- persistent_data->ai_personality_wood_difference = std::rand() % 40 - 20;
3190- persistent_data->ai_personality_early_militarysites = std::rand() % 20 + 20;
3191- persistent_data->last_soldier_trained = kNever;
3192- } else if (persistent_data->initialized == kTrue) {
3193- // Doing some consistency checks
3194- check_range<uint32_t>(
3195- persistent_data->expedition_start_time, gametime, "expedition_start_time");
3196- check_range<uint16_t>(persistent_data->ships_utilization, 0, 10000, "ships_utilization_");
3197- check_range<int16_t>(persistent_data->ai_personality_military_loneliness, -60, 60,
3198- "ai_personality_military_loneliness");
3199- check_range<int32_t>(
3200- persistent_data->ai_personality_attack_margin, 15, "ai_personality_attack_margin");
3201- check_range<uint32_t>(
3202- persistent_data->ai_productionsites_ratio, 5, 15, "ai_productionsites_ratio");
3203- check_range<int32_t>(persistent_data->ai_personality_wood_difference, -20, 19,
3204- "ai_personality_wood_difference");
3205- check_range<uint32_t>(persistent_data->ai_personality_early_militarysites, 20, 40,
3206- "ai_personality_early_militarysites");
3207- } else {
3208- throw wexception("Corrupted AI data");
3209+ // printing identified basic buildings if we are in the basic economy mode
3210+ basic_economy_established = persistent_data->remaining_basic_buildings.empty();
3211+ if (!basic_economy_established) {
3212+ log("%2d: Initializing in the basic economy mode, required buildings:\n", player_number());
3213+ for (auto bb : persistent_data->remaining_basic_buildings) {
3214+ log(" %3d / %-25s- target %d\n", bb.first, get_building_observer(bb.first).name,
3215+ bb.second);
3216+ }
3217 }
3218+ assert(persistent_data->remaining_basic_buildings.size() ==
3219+ persistent_data->remaining_buildings_size);
3220+
3221+ update_player_stat(gametime);
3222
3223 // Initialise the max duration of a single ship's expedition
3224 const uint32_t map_area = uint32_t(map.get_height()) * map.get_width();
3225@@ -893,6 +1039,18 @@
3226 // Current gametime is better then 'kNoExpedition'
3227 persistent_data->expedition_start_time = gametime;
3228 }
3229+
3230+ // just to be sure we have iron mines identified
3231+ assert(iron_ore_id != INVALID_INDEX);
3232+
3233+ productionsites_ratio_ = management_data.get_military_number_at(86) / 10 + 12;
3234+
3235+ // Just to be initialized
3236+ soldier_status_ = SoldiersStatus::kEnough;
3237+ vacant_mil_positions_average_ = 0;
3238+ spots_avail.resize(4);
3239+ trees_nearby_treshold_ = 3 + std::abs(management_data.get_military_number_at(121)) / 2;
3240+ last_road_dismantled_ = 0;
3241 }
3242
3243 /**
3244@@ -1022,27 +1180,66 @@
3245 }
3246
3247 /// Updates one buildable field
3248-void DefaultAI::update_buildable_field(BuildableField& field, uint16_t range, bool military) {
3249+void DefaultAI::update_buildable_field(BuildableField& field) {
3250 // look if there is any unowned land nearby
3251 Map& map = game().map();
3252 const uint32_t gametime = game().get_gametime();
3253 FindNodeUnownedWalkable find_unowned_walkable(player_, game());
3254+ FindEnemyNodeWalkable find_enemy_owned_walkable(player_, game());
3255+ FindNodeUnownedBuildable find_unowned_buildable(player_, game());
3256 FindNodeUnownedMineable find_unowned_mines_pots(player_, game());
3257+ FindNodeUnownedMineable find_unowned_iron_mines(player_, game(), iron_ore_id);
3258+ FindNodeAllyOwned find_ally(player_, game(), player_number());
3259 PlayerNumber const pn = player_->player_number();
3260 const World& world = game().world();
3261- field.unowned_land_nearby =
3262- map.find_fields(Area<FCoords>(field.coords, range), nullptr, find_unowned_walkable);
3263- FindNodeAllyOwned find_ally(player_, game(), player_number());
3264- const int32_t AllyOwnedFields =
3265- map.find_fields(Area<FCoords>(field.coords, 3), nullptr, find_ally);
3266-
3267- field.near_border = false;
3268- if (AllyOwnedFields > 0) {
3269+
3270+ constexpr uint16_t kProductionArea = 6;
3271+ constexpr uint16_t kBuildableSpotsCheckArea = 10;
3272+ constexpr uint16_t kEnemyCheckArea = 16;
3273+ const uint16_t ms_enemy_check_area =
3274+ kEnemyCheckArea + std::abs(management_data.get_military_number_at(75)) / 10;
3275+ constexpr uint16_t kDistantResourcesArea = 20;
3276+
3277+ uint16_t actual_enemy_check_area = kEnemyCheckArea;
3278+ field.is_militarysite = false;
3279+ if (field.coords.field->get_immovable()) {
3280+ if (field.coords.field->get_immovable()->descr().type() ==
3281+ Widelands::MapObjectType::MILITARYSITE) {
3282+ field.is_militarysite = true;
3283+ actual_enemy_check_area = ms_enemy_check_area;
3284+ }
3285+ }
3286+
3287+ field.unowned_land_nearby = map.find_fields(
3288+ Area<FCoords>(field.coords, actual_enemy_check_area), nullptr, find_unowned_walkable);
3289+
3290+ field.enemy_owned_land_nearby = map.find_fields(
3291+ Area<FCoords>(field.coords, actual_enemy_check_area), nullptr, find_enemy_owned_walkable);
3292+
3293+ field.nearest_buildable_spot_nearby = std::numeric_limits<uint16_t>::max();
3294+ if (field.unowned_land_nearby > 0) {
3295+ std::vector<Coords> found_buildable_fields;
3296+
3297+ field.unowned_buildable_spots_nearby =
3298+ map.find_fields(Area<FCoords>(field.coords, kBuildableSpotsCheckArea),
3299+ &found_buildable_fields, find_unowned_buildable);
3300+ // Now iterate over fields to get nearest one
3301+ for (auto& coords : found_buildable_fields) {
3302+ const uint32_t cur_distance = map.calc_distance(coords, field.coords);
3303+ if (cur_distance < field.nearest_buildable_spot_nearby) {
3304+ field.nearest_buildable_spot_nearby = cur_distance;
3305+ }
3306+ }
3307+ } else {
3308+ field.unowned_buildable_spots_nearby = 0;
3309+ }
3310+
3311+ // Is this near the border? Get rid of fields owned by ally
3312+ if (map.find_fields(Area<FCoords>(field.coords, 3), nullptr, find_ally) ||
3313+ map.find_fields(Area<FCoords>(field.coords, 3), nullptr, find_unowned_walkable)) {
3314 field.near_border = true;
3315- } else if (field.unowned_land_nearby > 0) {
3316- if (map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned_walkable) > 0) {
3317- field.near_border = true;
3318- }
3319+ } else {
3320+ field.near_border = false;
3321 }
3322
3323 // are we going to count resources now?
3324@@ -1054,26 +1251,39 @@
3325 field.last_resources_check_time = gametime;
3326 }
3327
3328- // to save some CPU
3329- if (mines_.size() > 8 && !resource_count_now) {
3330- field.unowned_mines_spots_nearby = 0;
3331- } else {
3332- uint32_t close_mines =
3333- map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned_mines_pots);
3334+ // testing mines
3335+ if (resource_count_now) {
3336+ uint32_t close_mines = map.find_fields(
3337+ Area<FCoords>(field.coords, kProductionArea), nullptr, find_unowned_mines_pots);
3338 uint32_t distant_mines =
3339- map.find_fields(Area<FCoords>(field.coords, (range + 6 < 14) ? 14 : range + 6), nullptr,
3340+ map.find_fields(Area<FCoords>(field.coords, kDistantResourcesArea), nullptr,
3341+
3342 find_unowned_mines_pots);
3343 distant_mines = distant_mines - close_mines;
3344 field.unowned_mines_spots_nearby = 4 * close_mines + distant_mines / 2;
3345 if (distant_mines > 0) {
3346 field.unowned_mines_spots_nearby += 15;
3347 }
3348+ if (field.unowned_mines_spots_nearby > 0 &&
3349+ // for performance considerations we count iron nodes only if we have less than 2 iron
3350+ // mines now...
3351+ (mines_per_type[iron_ore_id].in_construction + mines_per_type[iron_ore_id].finished) <=
3352+ 1) {
3353+ // counting iron mines, if we have less than two iron mines
3354+ field.unowned_iron_mines_nearby = map.find_fields(
3355+ Area<FCoords>(field.coords, kDistantResourcesArea), nullptr, find_unowned_iron_mines);
3356+ } else {
3357+ field.unowned_iron_mines_nearby = 0;
3358+ }
3359 }
3360
3361 // identifying portspace fields
3362- if (!field.is_portspace) { // if we know it, no need to do it once more
3363+ if (field.is_portspace ==
3364+ Widelands::ExtendedBool::kUnset) { // if we know it, no need to do it once more
3365 if (player_->get_buildcaps(field.coords) & BUILDCAPS_PORT) {
3366- field.is_portspace = true;
3367+ field.is_portspace = ExtendedBool::kTrue;
3368+ } else {
3369+ field.is_portspace = ExtendedBool::kFalse;
3370 }
3371 }
3372
3373@@ -1090,15 +1300,20 @@
3374 }
3375
3376 // testing if a port is nearby, such field will get a priority boost
3377- uint16_t nearest_distance = std::numeric_limits<uint16_t>::max();
3378- for (const WarehouseSiteObserver& wh_obs : warehousesites) {
3379- const uint16_t actual_distance = map.calc_distance(field.coords, wh_obs.site->get_position());
3380- nearest_distance = std::min(nearest_distance, actual_distance);
3381- }
3382- if (nearest_distance < 15) {
3383- field.port_nearby = true;
3384- } else {
3385- field.port_nearby = false;
3386+ if (resource_count_now) { // misusing a bit
3387+ uint16_t nearest_distance = std::numeric_limits<uint16_t>::max();
3388+ for (const WarehouseSiteObserver& wh_obs : warehousesites) {
3389+ if (wh_obs.bo->is(BuildingAttribute::kPort)) {
3390+ const uint16_t actual_distance =
3391+ map.calc_distance(field.coords, wh_obs.site->get_position());
3392+ nearest_distance = std::min(nearest_distance, actual_distance);
3393+ }
3394+ }
3395+ if (nearest_distance < 15) {
3396+ field.port_nearby = true;
3397+ } else {
3398+ field.port_nearby = false;
3399+ }
3400 }
3401
3402 // testing fields in radius 1 to find biggest buildcaps.
3403@@ -1115,119 +1330,63 @@
3404
3405 assert((player_->get_buildcaps(field.coords) & BUILDCAPS_SIZEMASK) <= field.max_buildcap_nearby);
3406
3407- // collect information about resources in the area
3408- std::vector<ImmovableFound> immovables;
3409- // Search in a radius of range
3410- map.find_immovables(Area<FCoords>(field.coords, range), &immovables);
3411-
3412- // Is this a general update or just for military consideration
3413- // (second is used in check_militarysites)
3414- if (!military) {
3415- int32_t const tree_attr = MapObjectDescr::get_attribute_id("tree");
3416- field.preferred = false;
3417- field.enemy_nearby = false;
3418- field.area_military_capacity = 0;
3419- field.military_loneliness = 1000; // instead of floats(v-
3420- field.area_military_presence = 0;
3421- field.military_stationed = 0;
3422- field.unconnected_nearby = false;
3423- field.trees_nearby = 0;
3424- field.space_consumers_nearby = 0;
3425- field.rangers_nearby = 0;
3426- field.producers_nearby.clear();
3427- field.producers_nearby.resize(wares.size());
3428- field.consumers_nearby.clear();
3429- field.consumers_nearby.resize(wares.size());
3430- field.supporters_nearby.clear();
3431- field.supporters_nearby.resize(wares.size());
3432- std::vector<Coords> resource_list;
3433- std::vector<Bob*> critters_list;
3434-
3435- if (field.water_nearby == kUncalculated) {
3436- assert(field.open_water_nearby == kUncalculated);
3437-
3438- FindNodeWater find_water(game().world());
3439- field.water_nearby = map.find_fields(Area<FCoords>(field.coords, 5), nullptr, find_water);
3440-
3441- if (field.water_nearby > 0) {
3442- FindNodeOpenWater find_open_water(game().world());
3443- field.open_water_nearby =
3444- map.find_fields(Area<FCoords>(field.coords, 5), nullptr, find_open_water);
3445- }
3446-
3447- if (resource_necessity_water_needed_) { // for atlanteans
3448- field.distant_water =
3449- map.find_fields(Area<FCoords>(field.coords, 14), nullptr, find_water) -
3450- field.water_nearby;
3451- assert(field.open_water_nearby <= field.water_nearby);
3452- }
3453- }
3454-
3455- // counting fields with fish
3456- if (field.water_nearby > 0 && (field.fish_nearby == kUncalculated || resource_count_now)) {
3457- map.find_fields(Area<FCoords>(field.coords, 6), &resource_list,
3458- FindNodeResource(world.get_resource("fish")));
3459- field.fish_nearby = resource_list.size();
3460- }
3461-
3462- // counting fields with critters (game)
3463- // not doing this always, this does not change fast
3464- if (resource_count_now) {
3465- map.find_bobs(Area<FCoords>(field.coords, 6), &critters_list, FindBobCritter());
3466- field.critters_nearby = critters_list.size();
3467- }
3468-
3469- FCoords fse;
3470- map.get_neighbour(field.coords, WALK_SE, &fse);
3471-
3472- if (BaseImmovable const* const imm = fse.field->get_immovable()) {
3473- if (dynamic_cast<Flag const*>(imm) ||
3474- (dynamic_cast<Road const*>(imm) && (fse.field->nodecaps() & BUILDCAPS_FLAG))) {
3475- field.preferred = true;
3476- }
3477- }
3478-
3479- for (uint32_t i = 0; i < immovables.size(); ++i) {
3480- const BaseImmovable& base_immovable = *immovables.at(i).object;
3481-
3482- if (upcast(PlayerImmovable const, player_immovable, &base_immovable)) {
3483-
3484- // TODO(unknown): Only continue; if this is an opposing site
3485- // allied sites should be counted for military influence
3486- if (player_immovable->owner().player_number() != pn) {
3487- if (player_->is_hostile(player_immovable->owner())) {
3488- field.enemy_nearby = true;
3489- }
3490-
3491- continue;
3492- }
3493- // here we identify the buiding (including expected building if constructionsite)
3494- // and calculate some statistics about nearby buildings
3495- if (upcast(ProductionSite const, productionsite, player_immovable)) {
3496- BuildingObserver& bo = get_building_observer(productionsite->descr().name().c_str());
3497- consider_productionsite_influence(field, immovables.at(i).coords, bo);
3498- }
3499- if (upcast(ConstructionSite const, constructionsite, player_immovable)) {
3500- const BuildingDescr& target_descr = constructionsite->building();
3501- BuildingObserver& bo = get_building_observer(target_descr.name().c_str());
3502- consider_productionsite_influence(field, immovables.at(i).coords, bo);
3503- }
3504- }
3505-
3506- if (immovables.at(i).object->has_attribute(tree_attr)) {
3507- ++field.trees_nearby;
3508- }
3509- }
3510+ // Testing surface water (once only)
3511+ // TODO(GunChleoc): We can change the terrain by scripting, so we should work with notifications
3512+ // here.
3513+ // Let's leave this as it is for now for performance reasons - terrain change of water is
3514+ // currently only
3515+ // used in the Atlantean scenario.
3516+ if (field.water_nearby == kUncalculated) {
3517+ assert(field.open_water_nearby == kUncalculated);
3518+
3519+ FindNodeWater find_water(game().world());
3520+ field.water_nearby =
3521+ map.find_fields(Area<FCoords>(field.coords, kProductionArea), nullptr, find_water);
3522+
3523+ if (field.water_nearby > 0) {
3524+ FindNodeOpenWater find_open_water(game().world());
3525+ field.open_water_nearby =
3526+ map.find_fields(Area<FCoords>(field.coords, kProductionArea), nullptr, find_open_water);
3527+ }
3528+
3529+ if (resource_necessity_water_needed_) { // for atlanteans
3530+ field.distant_water = map.find_fields(Area<FCoords>(field.coords, kDistantResourcesArea),
3531+ nullptr, find_water) -
3532+ field.water_nearby;
3533+ assert(field.open_water_nearby <= field.water_nearby);
3534+ }
3535+ }
3536+
3537+ FCoords fse;
3538+ map.get_neighbour(field.coords, WALK_SE, &fse);
3539+ field.preferred = false;
3540+ if (BaseImmovable const* const imm = fse.field->get_immovable()) {
3541+ if (dynamic_cast<Flag const*>(imm) ||
3542+ (dynamic_cast<Road const*>(imm) && (fse.field->nodecaps() & BUILDCAPS_FLAG))) {
3543+ field.preferred = true;
3544+ }
3545+ }
3546+
3547+ // counting fields with fish
3548+ if (field.water_nearby > 0 && (field.fish_nearby == kUncalculated || resource_count_now)) {
3549+ field.fish_nearby = map.find_fields(Area<FCoords>(field.coords, kProductionArea), nullptr,
3550+ FindNodeResource(world.get_resource("fish")));
3551+ }
3552+
3553+ // Counting resources that do not change fast
3554+ if (resource_count_now) {
3555+ // Counting fields with critters (game)
3556+ field.critters_nearby =
3557+ map.find_bobs(Area<FCoords>(field.coords, kProductionArea), nullptr, FindBobCritter());
3558
3559 // Rocks are not renewable, we will count them only if previous state is nonzero
3560- if (field.rocks_nearby > 0 && resource_count_now) {
3561-
3562- field.rocks_nearby =
3563- map.find_immovables(Area<FCoords>(map.get_fcoords(field.coords), 6), nullptr,
3564- FindImmovableAttribute(MapObjectDescr::get_attribute_id("rocks")));
3565-
3566- // adding 10 if rocks found
3567- field.rocks_nearby = (field.rocks_nearby > 0) ? field.rocks_nearby + 10 : 0;
3568+ if (field.rocks_nearby > 0) {
3569+ field.rocks_nearby = map.find_immovables(
3570+ Area<FCoords>(map.get_fcoords(field.coords), kProductionArea), nullptr,
3571+ FindImmovableAttribute(MapObjectDescr::get_attribute_id("rocks")));
3572+
3573+ // adding 5 if rocks found
3574+ field.rocks_nearby = (field.rocks_nearby > 0) ? field.rocks_nearby + 2 : 0;
3575 }
3576
3577 // ground water is not renewable and its amount can only fall, we will count them only if
3578@@ -1235,47 +1394,136 @@
3579 if (field.ground_water > 0) {
3580 field.ground_water = field.coords.field->get_resources_amount();
3581 }
3582- }
3583-
3584- // The following is done always (regardless of military or not)
3585-
3586- // We get immovables with higher radius
3587- immovables.clear();
3588- map.find_immovables(Area<FCoords>(field.coords, (range < 11) ? 11 : range), &immovables);
3589+
3590+ // Counting trees nearby
3591+ int32_t const tree_attr = MapObjectDescr::get_attribute_id("tree");
3592+ field.trees_nearby =
3593+ map.find_immovables(Area<FCoords>(map.get_fcoords(field.coords), kProductionArea), nullptr,
3594+ FindImmovableAttribute(tree_attr));
3595+ }
3596+
3597+ // resetting some values
3598+ field.enemy_nearby =
3599+ (field.enemy_owned_land_nearby > std::abs(management_data.get_military_number_at(41) / 4)) ?
3600+ true :
3601+ false;
3602+ if (field.enemy_owned_land_nearby == 0) {
3603+ assert(!field.enemy_nearby);
3604+ }
3605+
3606+ // resetting a bunch of values for the field
3607+ field.ally_military_presence = 0;
3608+ field.area_military_capacity = 0;
3609+ field.consumers_nearby.clear();
3610+ field.consumers_nearby.resize(wares.size());
3611+ field.enemy_military_presence = 0;
3612+ field.enemy_military_sites = 0;
3613+ field.enemy_wh_nearby = false;
3614+ field.military_in_constr_nearby = 0;
3615+ field.military_loneliness = 1000;
3616 field.military_stationed = 0;
3617 field.military_unstationed = 0;
3618- field.military_in_constr_nearby = 0;
3619- field.area_military_capacity = 0;
3620- field.military_loneliness = 1000;
3621- field.area_military_presence = 0;
3622+ field.own_military_presence = 0;
3623+ field.own_non_military_nearby = 0;
3624+ field.producers_nearby.clear();
3625+ field.producers_nearby.resize(wares.size());
3626+ field.rangers_nearby = 0;
3627+ field.space_consumers_nearby = 0;
3628+ field.supporters_nearby.clear();
3629+ field.supporters_nearby.resize(wares.size());
3630 field.unconnected_nearby = false;
3631
3632+ // collect information about productionsites nearby
3633+ std::vector<ImmovableFound> immovables;
3634+ // Search in a radius of range
3635+ map.find_immovables(Area<FCoords>(field.coords, kProductionArea + 2), &immovables);
3636+
3637+ // function seems to return duplicates, so we will use serial numbers to filter them out
3638+ std::set<uint32_t> unique_serials;
3639+
3640+ for (uint32_t i = 0; i < immovables.size(); ++i) {
3641+ const BaseImmovable& base_immovable = *immovables.at(i).object;
3642+ if (!unique_serials.insert(base_immovable.serial()).second) {
3643+ continue; // serial was not inserted in the set, so this is a duplicate
3644+ }
3645+
3646+ if (upcast(PlayerImmovable const, player_immovable, &base_immovable)) {
3647+
3648+ // TODO(unknown): Only continue if this is an opposing site
3649+ // allied sites should be counted for military influence
3650+ if (player_immovable->owner().player_number() != pn) {
3651+ continue;
3652+ }
3653+ // here we identify the buiding (including expected building if constructionsite)
3654+ // and calculate some statistics about nearby buildings
3655+ if (player_immovable->descr().type() == MapObjectType::PRODUCTIONSITE) {
3656+ BuildingObserver& bo = get_building_observer(player_immovable->descr().name().c_str());
3657+ consider_productionsite_influence(field, immovables.at(i).coords, bo);
3658+ } else if (upcast(ConstructionSite const, constructionsite, player_immovable)) {
3659+ const BuildingDescr& target_descr = constructionsite->building();
3660+ BuildingObserver& bo = get_building_observer(target_descr.name().c_str());
3661+ consider_productionsite_influence(field, immovables.at(i).coords, bo);
3662+ }
3663+ }
3664+ }
3665+
3666+ // Now testing military aspects
3667+ immovables.clear();
3668+ map.find_immovables(Area<FCoords>(field.coords, actual_enemy_check_area), &immovables);
3669+
3670 // We are interested in unconnected immovables, but we must be also close to connected ones
3671 bool any_connected_imm = false;
3672 bool any_unconnected_imm = false;
3673+ unique_serials.clear();
3674
3675 for (uint32_t i = 0; i < immovables.size(); ++i) {
3676-
3677 const BaseImmovable& base_immovable = *immovables.at(i).object;
3678
3679- // testing if it is enemy-owned field
3680- // TODO(unknown): count such fields...
3681- if (upcast(PlayerImmovable const, player_immovable, &base_immovable)) {
3682-
3683- // TODO(unknown): Only continue; if this is an opposing site
3684- // allied sites should be counted for military influence
3685- if (player_immovable->owner().player_number() != pn) {
3686- if (player_->is_hostile(player_immovable->owner())) {
3687- field.enemy_nearby = true;
3688- }
3689- continue;
3690- }
3691+ if (!unique_serials.insert(base_immovable.serial()).second) {
3692+ continue; // serial was not inserted in the set, so this is duplicate
3693 }
3694
3695- // if we are here, immovable is ours
3696+ // testing if immovable is owned by someone else and collecting some statistics
3697 if (upcast(Building const, building, &base_immovable)) {
3698
3699- // connected to warehouse
3700+ const PlayerNumber bpn = building->owner().player_number();
3701+ if (player_statistics.get_is_enemy(bpn)) { // owned by enemy
3702+ assert(!player_statistics.players_in_same_team(bpn, pn));
3703+ field.enemy_nearby = true;
3704+ if (upcast(MilitarySite const, militarysite, building)) {
3705+ field.enemy_military_presence +=
3706+ militarysite->soldier_control()->stationed_soldiers().size();
3707+ ++field.enemy_military_sites;
3708+ }
3709+ if (upcast(ConstructionSite const, constructionsite, building)) {
3710+ const BuildingDescr& target_descr = constructionsite->building();
3711+ if (target_descr.type() == MapObjectType::MILITARYSITE) {
3712+ ++field.enemy_military_sites;
3713+ }
3714+ }
3715+
3716+ // Warehouses are counted here too as they can host soldiers as well
3717+ if (upcast(Warehouse const, warehouse, building)) {
3718+ field.enemy_military_presence +=
3719+ warehouse->soldier_control()->stationed_soldiers().size();
3720+ ++field.enemy_military_sites;
3721+ field.enemy_wh_nearby = true;
3722+ enemy_warehouses.insert(building->get_position().hash());
3723+ }
3724+ continue;
3725+ } else if (bpn != pn) { // it is an ally
3726+ assert(!player_statistics.get_is_enemy(bpn));
3727+ if (upcast(MilitarySite const, militarysite, building)) {
3728+ field.ally_military_presence +=
3729+ militarysite->soldier_control()->stationed_soldiers().size();
3730+ }
3731+ continue;
3732+ }
3733+
3734+ // if we are here, the immovable is ours
3735+ assert(building->owner().player_number() == pn);
3736+
3737+ // connected to a warehouse
3738 bool connected = !building->get_economy()->warehouses().empty();
3739 if (connected) {
3740 any_connected_imm = true;
3741@@ -1297,7 +1545,7 @@
3742 }
3743 }
3744 } else if (!connected) {
3745- // we dont care about unconnected constructionsites
3746+ // we don't care about unconnected constructionsites
3747 any_unconnected_imm = true;
3748 }
3749
3750@@ -1308,7 +1556,7 @@
3751 if (radius > dist) {
3752 field.area_military_capacity +=
3753 militarysite->soldier_control()->max_soldier_capacity();
3754- field.area_military_presence +=
3755+ field.own_military_presence +=
3756 militarysite->soldier_control()->stationed_soldiers().size();
3757
3758 if (militarysite->soldier_control()->stationed_soldiers().empty()) {
3759@@ -1321,12 +1569,241 @@
3760 field.military_loneliness *= static_cast<double_t>(dist) / radius;
3761 }
3762 }
3763+ } else {
3764+ ++field.own_non_military_nearby;
3765 }
3766 }
3767 }
3768+
3769+ assert(field.military_loneliness <= 1000);
3770+
3771 if (any_unconnected_imm && any_connected_imm && field.military_in_constr_nearby == 0) {
3772 field.unconnected_nearby = true;
3773 }
3774+
3775+ // if there is a militarysite on the field, we try to walk to enemy
3776+ field.enemy_accessible_ = false;
3777+ field.local_soldier_capacity = 0;
3778+ if (field.is_militarysite) {
3779+ if (upcast(MilitarySite, ms, field.coords.field->get_immovable())) {
3780+ if (field.enemy_nearby) {
3781+ uint32_t unused1 = 0;
3782+ uint16_t unused2 = 0;
3783+ field.enemy_accessible_ = other_player_accessible(
3784+ actual_enemy_check_area + 3, &unused1, &unused2, field.coords, WalkSearch::kEnemy);
3785+ }
3786+ field.local_soldier_capacity = ms->soldier_control()->max_soldier_capacity();
3787+ field.is_militarysite = true;
3788+ } else {
3789+ NEVER_HERE();
3790+ }
3791+ }
3792+
3793+ // Calculating field score
3794+ field.military_score_ = 0;
3795+ field.inland = false;
3796+
3797+ if (!(field.enemy_nearby || field.near_border)) {
3798+ field.inland = true;
3799+ }
3800+
3801+ const uint8_t score_parts_size = 55;
3802+ int32_t score_parts[score_parts_size] = {0};
3803+ if (field.enemy_owned_land_nearby) {
3804+ score_parts[0] = 3 *
3805+ management_data.neuron_pool[73].get_result_safe(
3806+ field.enemy_owned_land_nearby / 5, kAbsValue);
3807+ score_parts[1] =
3808+ 3 *
3809+ management_data.neuron_pool[76].get_result_safe(field.enemy_owned_land_nearby, kAbsValue);
3810+ score_parts[2] = 3 *
3811+ management_data.neuron_pool[54].get_result_safe(
3812+ field.enemy_military_presence * 2, kAbsValue);
3813+ score_parts[3] = 3 *
3814+ management_data.neuron_pool[61].get_result_safe(
3815+ field.enemy_military_presence / 3, kAbsValue);
3816+ score_parts[4] =
3817+ (!field.enemy_accessible_) ? (-100 + management_data.get_military_number_at(55)) : 0;
3818+ score_parts[5] =
3819+ 2 *
3820+ management_data.neuron_pool[50].get_result_safe(field.enemy_owned_land_nearby, kAbsValue);
3821+
3822+ score_parts[6] =
3823+ field.enemy_military_sites * std::abs(management_data.get_military_number_at(67) / 2);
3824+ score_parts[7] =
3825+ 2 *
3826+ management_data.neuron_pool[34].get_result_safe(field.enemy_military_sites * 2, kAbsValue);
3827+ score_parts[8] = management_data.neuron_pool[56].get_result_safe(
3828+ field.enemy_military_presence * 2, kAbsValue);
3829+
3830+ score_parts[9] = management_data.neuron_pool[65].get_result_safe(
3831+ (field.unowned_land_nearby + field.enemy_owned_land_nearby) / 2, kAbsValue);
3832+ score_parts[10] = (field.enemy_accessible_) ? management_data.get_military_number_at(80) : 0;
3833+
3834+ score_parts[11] =
3835+ -3 *
3836+ management_data.neuron_pool[8].get_result_safe(
3837+ (field.military_in_constr_nearby + field.military_unstationed) * 3, kAbsValue);
3838+ score_parts[12] =
3839+ -3 *
3840+ management_data.neuron_pool[74].get_result_safe(
3841+ (field.military_in_constr_nearby + field.military_unstationed) * 5, kAbsValue);
3842+ score_parts[13] = ((field.military_in_constr_nearby + field.military_unstationed) > 0) ?
3843+ -std::abs(management_data.get_military_number_at(32)) :
3844+ 0;
3845+ score_parts[14] = -1 * (field.military_in_constr_nearby + field.military_unstationed) *
3846+ std::abs(management_data.get_military_number_at(12));
3847+
3848+ score_parts[15] =
3849+ -2 * management_data.neuron_pool[75].get_result_safe(field.own_military_presence);
3850+ score_parts[16] = -5 * std::min<int16_t>(field.area_military_capacity, 20);
3851+ score_parts[17] = 3 * management_data.get_military_number_at(28);
3852+ score_parts[18] =
3853+ (field.enemy_nearby) ? 3 * std::abs(management_data.get_military_number_at(68)) : 0;
3854+ score_parts[19] =
3855+ (field.enemy_wh_nearby) ? 3 * std::abs(management_data.get_military_number_at(132)) : 0;
3856+ score_parts[58] = (field.enemy_wh_nearby) ?
3857+ std::abs(management_data.get_military_number_at(135)) :
3858+ -std::abs(management_data.get_military_number_at(135));
3859+
3860+ } else { // for expansion or inner land
3861+
3862+ score_parts[20] = management_data.neuron_pool[22].get_result_safe(
3863+ (field.unowned_mines_spots_nearby + 2) / 3, kAbsValue);
3864+ score_parts[21] = (field.unowned_mines_spots_nearby > 0) ?
3865+ std::abs(management_data.get_military_number_at(58)) :
3866+ 0;
3867+ if (expansion_type.get_expansion_type() == ExpansionMode::kResources) {
3868+ score_parts[23] = 2 *
3869+ management_data.neuron_pool[78].get_result_safe(
3870+ (field.unowned_mines_spots_nearby + 2) / 3, kAbsValue);
3871+ }
3872+
3873+ score_parts[24] =
3874+ (field.unowned_land_nearby) ?
3875+ management_data.neuron_pool[25].get_result_safe(field.water_nearby / 2, kAbsValue) :
3876+ 0;
3877+ score_parts[25] =
3878+ (field.unowned_land_nearby) ?
3879+ management_data.neuron_pool[27].get_result_safe(field.trees_nearby / 2, kAbsValue) :
3880+ 0;
3881+
3882+ if (resource_necessity_water_needed_) {
3883+ score_parts[26] =
3884+ (field.unowned_land_nearby) ?
3885+ management_data.neuron_pool[15].get_result_safe(field.water_nearby, kAbsValue) :
3886+ 0;
3887+ score_parts[27] =
3888+ resource_necessity_water_needed_ *
3889+ management_data.neuron_pool[17].get_result_safe(field.distant_water, kAbsValue) / 100;
3890+ }
3891+ score_parts[28] =
3892+ (field.unowned_land_nearby) ?
3893+ management_data.neuron_pool[33].get_result_safe(field.water_nearby, kAbsValue) :
3894+ 0;
3895+ score_parts[29] =
3896+ management_data.neuron_pool[10].get_result_safe(field.military_loneliness / 50, kAbsValue);
3897+
3898+ score_parts[30] =
3899+ -10 *
3900+ management_data.neuron_pool[8].get_result_safe(
3901+ 3 * (field.military_in_constr_nearby + field.military_unstationed), kAbsValue);
3902+ score_parts[31] =
3903+ -10 *
3904+ management_data.neuron_pool[31].get_result_safe(
3905+ 3 * (field.military_in_constr_nearby + field.military_unstationed), kAbsValue);
3906+ score_parts[32] = -4 * field.military_in_constr_nearby *
3907+ std::abs(management_data.get_military_number_at(82));
3908+ score_parts[33] = (field.military_in_constr_nearby > 0) ?
3909+ -5 * management_data.get_military_number_at(85) :
3910+ 0;
3911+
3912+ score_parts[34] = -1 *
3913+ management_data.neuron_pool[4].get_result_safe(
3914+ (field.area_military_capacity + 4) / 5, kAbsValue);
3915+ score_parts[35] = 3 * management_data.get_military_number_at(133);
3916+
3917+ if (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) {
3918+ score_parts[36] = -100 - 4 * std::abs(management_data.get_military_number_at(139));
3919+ } else if (expansion_type.get_expansion_type() == ExpansionMode::kResources ||
3920+ expansion_type.get_expansion_type() == ExpansionMode::kSpace) {
3921+ score_parts[37] =
3922+ +100 + 4 * std::abs(management_data.get_military_number_at(139)); // The same as above
3923+ }
3924+ if (msites_in_constr() > 0 && field.max_buildcap_nearby == BUILDCAPS_BIG &&
3925+ spots_avail.at(BUILDCAPS_BIG) <= 2) {
3926+ score_parts[57] = -10 * std::abs(management_data.get_military_number_at(54));
3927+ }
3928+ }
3929+
3930+ // common inputs
3931+ if (field.unowned_iron_mines_nearby > 0 && ((mines_per_type[iron_ore_id].in_construction +
3932+ mines_per_type[iron_ore_id].finished) == 0)) {
3933+ score_parts[40] = field.unowned_iron_mines_nearby *
3934+ std::abs(management_data.get_military_number_at(92)) / 50;
3935+ }
3936+ if (field.unowned_iron_mines_nearby && ((mines_per_type[iron_ore_id].in_construction +
3937+ mines_per_type[iron_ore_id].finished) <= 1)) {
3938+ score_parts[41] = 3 * std::abs(management_data.get_military_number_at(93));
3939+ }
3940+
3941+ score_parts[42] =
3942+ (field.unowned_land_nearby) ?
3943+ management_data.neuron_pool[18].get_result_safe(field.own_non_military_nearby, kAbsValue) :
3944+ 0;
3945+
3946+ score_parts[43] = 2 *
3947+ management_data.neuron_pool[11].get_result_safe(
3948+ field.unowned_buildable_spots_nearby, kAbsValue);
3949+ score_parts[44] =
3950+ management_data.neuron_pool[12].get_result_safe(field.unowned_mines_spots_nearby, kAbsValue);
3951+ score_parts[45] =
3952+ (field.unowned_land_nearby) ?
3953+ field.military_loneliness * std::abs(management_data.get_military_number_at(53)) / 800 :
3954+ 0;
3955+
3956+ score_parts[46] =
3957+ -1 * management_data.neuron_pool[55].get_result_safe(field.ally_military_presence, kAbsValue);
3958+ score_parts[47] =
3959+ -1 *
3960+ management_data.neuron_pool[53].get_result_safe(2 * field.ally_military_presence, kAbsValue);
3961+ score_parts[48] = -2 *
3962+ management_data.neuron_pool[4].get_result_safe(
3963+ (field.area_military_capacity + 4) / 5, kAbsValue);
3964+ score_parts[49] = ((field.military_in_constr_nearby + field.military_unstationed) > 0) ?
3965+ -std::abs(management_data.get_military_number_at(81)) :
3966+ 0;
3967+ score_parts[55] = (field.military_loneliness < 10) ?
3968+ 2 * std::abs(management_data.get_military_number_at(141)) :
3969+ 0;
3970+ score_parts[56] =
3971+ (any_unconnected_imm) ? 2 * std::abs(management_data.get_military_number_at(23)) : 0;
3972+
3973+ for (uint16_t i = 0; i < score_parts_size; i++) {
3974+ field.military_score_ += score_parts[i];
3975+ }
3976+
3977+ if (kAITrainingMode) {
3978+ if (field.military_score_ < -5000 || field.military_score_ > 2000) {
3979+ log("Warning field.military_score_ %5d, compounds: ", field.military_score_);
3980+ for (uint16_t i = 0; i < score_parts_size; i++) {
3981+ log("%d, ", score_parts[i]);
3982+ }
3983+ log("\n");
3984+ }
3985+ }
3986+
3987+ // is new site allowed at all here?
3988+ field.defense_msite_allowed = false;
3989+ int16_t multiplicator = 10;
3990+ if (soldier_status_ == SoldiersStatus::kBadShortage) {
3991+ multiplicator = 4;
3992+ } else if (soldier_status_ == SoldiersStatus::kShortage) {
3993+ multiplicator = 7;
3994+ }
3995+ if (field.area_military_capacity < field.enemy_military_presence * multiplicator / 10) {
3996+ field.defense_msite_allowed = true;
3997+ }
3998 }
3999
4000 /// Updates one mineable field
4001@@ -1378,7 +1855,6 @@
4002
4003 /// Updates the production and MINE sites statistics needed for construction decision.
4004 void DefaultAI::update_productionsite_stats() {
4005- uint16_t fishers_count = 0; // used for atlanteans only
4006
4007 // Reset statistics for all buildings
4008 for (uint32_t i = 0; i < buildings_.size(); ++i) {
4009@@ -1400,11 +1876,6 @@
4010 productionsites.front().bo->current_stats +=
4011 productionsites.front().site->get_crude_statistics();
4012
4013- // counting fishers
4014- if (productionsites.front().bo->is_fisher) {
4015- fishers_count += 1;
4016- }
4017-
4018 // Check whether this building is completely occupied
4019 if (!productionsites.front().site->can_start_working()) {
4020 productionsites.front().bo->unoccupied_count += 1;
4021@@ -1418,16 +1889,6 @@
4022 productionsites.pop_front();
4023 }
4024
4025- if (resource_necessity_water_needed_) {
4026- if (fishers_count == 0) {
4027- resource_necessity_water_ = 100;
4028- } else if (fishers_count == 1) {
4029- resource_necessity_water_ = 50;
4030- } else {
4031- resource_necessity_water_ = 10;
4032- }
4033- }
4034-
4035 // for mines_ also
4036 // Check all available mines
4037 for (uint32_t i = 0; i < mines_.size(); ++i) {
4038@@ -1467,29 +1928,23 @@
4039 // is built.
4040 // * Buildings are split into categories
4041 // * The logic is complex but approximately:
4042-// - buildings producing building material are preferred
4043-// - buildings identified as basic are preferred
4044+// - some buildings belong to "basic economy" - these are preferred
4045+// - some small huts are exempt from basic economy logic
4046 // - first bulding of a type is preferred
4047-// - buildings identified as 'direct food supplier' are built after 15 min.
4048-// from game start
4049-// - if a building is upgradeable, second building is also preferred
4050-// (there should be no upgrade when there are not two buildings of the same type)
4051 // - algorithm is trying to take into account actual utlization of buildings
4052 // (the one shown in GUI/game is not reliable, it calculates own statistics)
4053-// * military buildings have own strategy, split into two situations:
4054-// - there is no enemy
4055-// - there is an enemy
4056+// * military buildings use genetic algorithm logic to score fields
4057 // Currently more military buildings are built than needed
4058-// and "optimization" (dismantling not needed buildings) is done afterwards
4059+// so there are always some vacant positions
4060 bool DefaultAI::construct_building(uint32_t gametime) {
4061 if (buildable_fields.empty()) {
4062 return false;
4063 }
4064+
4065 // Just used for easy checking whether a mine or something else was built.
4066 bool mine = false;
4067 uint32_t consumers_nearby_count = 0;
4068- std::vector<int32_t> spots_avail;
4069- spots_avail.resize(4);
4070+
4071 Map& map = game().map();
4072
4073 for (int32_t i = 0; i < 4; ++i)
4074@@ -1503,9 +1958,6 @@
4075 spots_ += spots_avail.at(BUILDCAPS_MEDIUM);
4076 spots_ += spots_avail.at(BUILDCAPS_BIG);
4077
4078- // here we possible stop building of new buildings
4079- new_buildings_stop_ = false;
4080-
4081 // helper variable - we need some proportion of free spots vs productionsites
4082 // the proportion depends on size of economy
4083 // this proportion defines how dense the buildings will be
4084@@ -1522,8 +1974,22 @@
4085 }
4086 const bool has_enough_space = (spots_ > needed_spots);
4087
4088- // This is a replacement for simple count of mines
4089- const int32_t virtual_mines = mines_.size() + mineable_fields.size() / 25;
4090+ // Do we have basic economy established? Informing that we just left the basic economy mode.
4091+ if (!basic_economy_established && persistent_data->remaining_basic_buildings.empty()) {
4092+ log("%2d: Player has achieved the basic economy at %s\n", player_number(),
4093+ gamestring_with_leading_zeros(gametime));
4094+ basic_economy_established = true;
4095+ assert(persistent_data->remaining_buildings_size == 0);
4096+ }
4097+
4098+ if (!basic_economy_established && player_statistics.any_enemy_seen_lately(gametime) &&
4099+ management_data.f_neuron_pool[17].get_position(0)) {
4100+ log("%2d: Player has not all buildings for basic economy yet (%lu missing), but enemy is "
4101+ "nearby, so quitting the mode at %s\n",
4102+ player_number(), persistent_data->remaining_basic_buildings.size(),
4103+ gamestring_with_leading_zeros(gametime));
4104+ basic_economy_established = true;
4105+ }
4106
4107 // *_military_scores are used as minimal score for a new military building
4108 // to be built. As AI does not traverse all building fields at once, these thresholds
4109@@ -1534,102 +2000,210 @@
4110 // score) and quickly falling down until it reaches the least_military_score
4111 // this one (=target_military_score) is actually used to decide if building&field is allowed
4112 // candidate
4113- // least_military_score is allowed to get bellow 100 only if there is no military site in
4114- // construction
4115- // right now in order to (try to) avoid expansion lockup
4116-
4117- // Bools below are helpers to improve readability of code
4118-
4119- // It is bit complicated balance building militarysites and productionsites so this is small hack
4120- // to help
4121- // it
4122- bool needs_boost_economy = false;
4123- if (highest_nonmil_prio_ > 10 && has_enough_space && virtual_mines >= 5) {
4124- needs_boost_economy = true;
4125- }
4126+
4127+ const PlayerNumber pn = player_number();
4128+
4129+ // Genetic algorithm is used here
4130+ bool inputs[2 * kFNeuronBitSize] = {0};
4131+ inputs[0] = (pow(msites_in_constr(), 2) > militarysites.size() + 2);
4132+ inputs[1] = !(pow(msites_in_constr(), 2) > militarysites.size() + 2);
4133+ inputs[2] =
4134+ (highest_nonmil_prio_ > 18 + std::abs(management_data.get_military_number_at(29) / 10));
4135+ inputs[3] =
4136+ !(highest_nonmil_prio_ > 18 + std::abs(management_data.get_military_number_at(29) / 10));
4137+ inputs[4] = (highest_nonmil_prio_ > 18 + std::abs(management_data.get_military_number_at(48)));
4138+ inputs[5] = !(highest_nonmil_prio_ > 18 + std::abs(management_data.get_military_number_at(49)));
4139+ inputs[6] = ((numof_psites_in_constr + mines_in_constr()) >
4140+ (productionsites.size() + mines_built()) / productionsites_ratio_);
4141+ inputs[7] = !((numof_psites_in_constr + mines_in_constr()) >
4142+ (productionsites.size() + mines_built()) / productionsites_ratio_);
4143+
4144+ inputs[8] = (has_enough_space);
4145+ inputs[9] = !(has_enough_space);
4146+ inputs[10] = (has_enough_space);
4147+ inputs[11] = !(has_enough_space);
4148+
4149+ inputs[12] = (gametime > 45 * 60 * 1000);
4150+ inputs[13] = !(gametime > 45 * 60 * 1000);
4151+
4152+ inputs[14] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy);
4153+ inputs[15] = !(expansion_type.get_expansion_type() == ExpansionMode::kEconomy);
4154+ inputs[16] = (expansion_type.get_expansion_type() == ExpansionMode::kSpace);
4155+ inputs[17] = !(expansion_type.get_expansion_type() == ExpansionMode::kSpace);
4156+
4157+ inputs[18] = (player_statistics.any_enemy_seen_lately(gametime));
4158+ inputs[19] = !(player_statistics.any_enemy_seen_lately(gametime));
4159+ inputs[20] = (player_statistics.get_player_power(pn) >
4160+ player_statistics.get_old60_player_power(pn) +
4161+ std::abs(management_data.get_military_number_at(130)) / 10);
4162+ inputs[21] = !(player_statistics.get_player_power(pn) >
4163+ player_statistics.get_old60_player_power(pn) +
4164+ std::abs(management_data.get_military_number_at(131)) / 10);
4165+ inputs[22] =
4166+ (player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn));
4167+ inputs[23] =
4168+ !(player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn));
4169+ inputs[24] = (highest_nonmil_prio_ > 18 + management_data.get_military_number_at(65) / 10);
4170+ inputs[25] = !(highest_nonmil_prio_ > 18 + management_data.get_military_number_at(65) / 10);
4171+ inputs[26] = (player_statistics.get_modified_player_power(pn) >
4172+ player_statistics.get_visible_enemies_power(pn));
4173+ inputs[27] = (player_statistics.get_modified_player_power(pn) <=
4174+ player_statistics.get_visible_enemies_power(pn));
4175+ inputs[28] =
4176+ (player_statistics.get_player_power(pn) > player_statistics.get_enemies_average_power());
4177+ inputs[29] =
4178+ !(player_statistics.get_player_power(pn) > player_statistics.get_enemies_average_power());
4179+ inputs[30] =
4180+ (player_statistics.get_player_power(pn) > player_statistics.get_enemies_max_power());
4181+ inputs[31] =
4182+ !(player_statistics.get_player_power(pn) > player_statistics.get_enemies_max_power());
4183+
4184+ inputs[32] = (persistent_data->least_military_score <
4185+ persistent_data->ai_personality_mil_upper_limit *
4186+ std::abs(management_data.get_military_number_at(69)) / 100);
4187+ inputs[33] = !(persistent_data->least_military_score <
4188+ persistent_data->ai_personality_mil_upper_limit *
4189+ std::abs(management_data.get_military_number_at(69)) / 100);
4190+ inputs[34] = player_statistics.strong_enough(pn);
4191+ inputs[35] = !player_statistics.strong_enough(pn);
4192+
4193+ inputs[36] = (player_statistics.get_player_land(pn) < 500);
4194+ inputs[37] = (player_statistics.get_player_land(pn) < 700);
4195+ inputs[38] = (player_statistics.get_player_land(pn) < 900);
4196+ inputs[39] = (player_statistics.get_player_land(pn) < 1100);
4197+ inputs[40] = (player_statistics.get_player_land(pn) > 500);
4198+ inputs[41] = (player_statistics.get_player_land(pn) > 700);
4199+ inputs[42] = (player_statistics.get_player_land(pn) > 900);
4200+ inputs[43] = (player_statistics.get_player_land(pn) > 1100);
4201+ inputs[44] = (player_statistics.get_player_power(pn) >
4202+ player_statistics.get_old60_player_power(pn) +
4203+ std::abs(management_data.get_military_number_at(130)) / 10);
4204+ inputs[45] = !(player_statistics.get_player_power(pn) >
4205+ player_statistics.get_old60_player_power(pn) +
4206+ std::abs(management_data.get_military_number_at(131)) / 10);
4207+ inputs[46] =
4208+ (player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn));
4209+ inputs[47] =
4210+ !(player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn));
4211+ inputs[48] = (bakeries_count_ == 0);
4212+ inputs[49] = (bakeries_count_ <= 1);
4213+ inputs[50] = (bakeries_count_ <= 1);
4214+ inputs[51] = (numof_psites_in_constr > 8);
4215+ inputs[52] = (numof_psites_in_constr < 8);
4216+
4217+ int16_t needs_boost_economy_score = management_data.get_military_number_at(61) / 5;
4218+ int16_t increase_score_limit_score = 0;
4219+
4220+ for (uint8_t i = 0; i < kFNeuronBitSize; ++i) {
4221+ if (management_data.f_neuron_pool[51].get_position(i)) {
4222+ needs_boost_economy_score += (inputs[i]) ? 1 : -1;
4223+ }
4224+ if (management_data.f_neuron_pool[52].get_position(i)) {
4225+ increase_score_limit_score += (inputs[i]) ? 1 : -1;
4226+ }
4227+ if (management_data.f_neuron_pool[21].get_position(i)) {
4228+ needs_boost_economy_score += (inputs[kFNeuronBitSize + i]) ? 1 : -1;
4229+ }
4230+ if (management_data.f_neuron_pool[22].get_position(i)) {
4231+ increase_score_limit_score += (inputs[kFNeuronBitSize + i]) ? 1 : -1;
4232+ }
4233+ }
4234+
4235+ // Finding expansion policy
4236+ // Do we need basic resources?
4237+ // This is a replacement for simple count of mines
4238+ const int32_t virtual_mines = mines_.size() + mineable_fields.size() / 15;
4239+ const bool needs_fishers = resource_necessity_water_needed_ && fishers_count_ < 1;
4240+
4241+ if (virtual_mines < 4 || mines_per_type[iron_ore_id].total_count() < 1 || needs_fishers) {
4242+ expansion_type.set_expantion_type(ExpansionMode::kResources);
4243+ } else {
4244+ // now we must decide if we go after spots or economy boost
4245+ if (needs_boost_economy_score >= 3) {
4246+ expansion_type.set_expantion_type(ExpansionMode::kEconomy);
4247+ } else if (needs_boost_economy_score >= -2) {
4248+ expansion_type.set_expantion_type(ExpansionMode::kBoth);
4249+ } else {
4250+ expansion_type.set_expantion_type(ExpansionMode::kSpace);
4251+ }
4252+ }
4253+
4254+ const bool increase_least_score_limit =
4255+ (increase_score_limit_score > management_data.get_military_number_at(45) / 5);
4256+
4257+ uint16_t concurent_ms_in_constr_no_enemy = 1;
4258+ uint16_t concurent_ms_in_constr_enemy_nearby = 2;
4259
4260 // resetting highest_nonmil_prio_ so it can be recalculated anew
4261 highest_nonmil_prio_ = 0;
4262
4263- const bool too_many_ms_constructionsites =
4264- (pow(msites_in_constr(), 2) > militarysites.size() + 2);
4265- const bool too_many_vacant_mil =
4266- (vacant_mil_positions_ * 3 > static_cast<int32_t>(militarysites.size()));
4267- const int32_t kUpperLimit = 325;
4268- const int32_t kBottomLimit = 40; // to prevent too dense militarysites
4269- // modifying least_military_score, down if more military sites are needed and vice versa
4270- if (too_many_ms_constructionsites || too_many_vacant_mil || needs_boost_economy) {
4271+ if (increase_least_score_limit) {
4272 if (persistent_data->least_military_score <
4273- kUpperLimit) { // No sense in letting it grow too high
4274+ persistent_data
4275+ ->ai_personality_mil_upper_limit) { // No sense in letting it grow too high
4276 persistent_data->least_military_score += 20;
4277+ if (persistent_data->least_military_score > persistent_data->target_military_score) {
4278+ persistent_data->target_military_score = persistent_data->least_military_score;
4279+ }
4280+ if (persistent_data->target_military_score >
4281+ persistent_data->ai_personality_mil_upper_limit) {
4282+ persistent_data->ai_personality_mil_upper_limit =
4283+ persistent_data->target_military_score;
4284+ }
4285 }
4286 } else {
4287+
4288+ uint16_t divider = 1; // this is to slow down decrementing the least military score
4289+ switch (expansion_type.get_expansion_type()) {
4290+ case ExpansionMode::kEconomy:
4291+ divider = 3;
4292+ break;
4293+ case ExpansionMode::kBoth:
4294+ divider = 2;
4295+ break;
4296+ default:
4297+ divider = 1;
4298+ }
4299+
4300 // least_military_score is decreased, but depending on the size of territory
4301 switch (static_cast<uint32_t>(log10(buildable_fields.size()))) {
4302 case 0:
4303- persistent_data->least_military_score -= 10;
4304+ persistent_data->least_military_score -= 10 / divider;
4305 break;
4306 case 1:
4307- persistent_data->least_military_score -= 8;
4308+ persistent_data->least_military_score -= 8 / divider;
4309 break;
4310 case 2:
4311- persistent_data->least_military_score -= 5;
4312+ persistent_data->least_military_score -= 5 / divider;
4313 break;
4314 case 3:
4315- persistent_data->least_military_score -= 3;
4316+ persistent_data->least_military_score -= 3 / divider;
4317 break;
4318 default:
4319- persistent_data->least_military_score -= 2;
4320- }
4321- // do not get bellow kBottomLimit if there is at least one ms in construction
4322- if ((msites_in_constr() > 0 || too_many_vacant_mil) &&
4323- persistent_data->least_military_score < kBottomLimit) {
4324- persistent_data->least_military_score = kBottomLimit;
4325+ persistent_data->least_military_score -= 2 / divider;
4326 }
4327 if (persistent_data->least_military_score < 0) {
4328 persistent_data->least_military_score = 0;
4329 }
4330 }
4331
4332- // This is effective score, falling down very quickly
4333- if (persistent_data->target_military_score > kUpperLimit + 150) {
4334- persistent_data->target_military_score = 8 * persistent_data->target_military_score / 10;
4335- } else {
4336- persistent_data->target_military_score = 9 * persistent_data->target_military_score / 10;
4337- }
4338+ assert(persistent_data->least_military_score <= persistent_data->target_military_score);
4339+ assert(persistent_data->target_military_score <=
4340+ persistent_data->ai_personality_mil_upper_limit);
4341+ persistent_data->target_military_score = 9 * persistent_data->target_military_score / 10;
4342 if (persistent_data->target_military_score < persistent_data->least_military_score) {
4343 persistent_data->target_military_score = persistent_data->least_military_score;
4344 }
4345-
4346- // there are many reasons why to stop building production buildings
4347- // (note there are numerous exceptions)
4348- // 1. to not have too many constructionsites
4349- if ((num_prod_constructionsites + mines_in_constr()) >
4350- (productionsites.size() + mines_built()) / persistent_data->ai_productionsites_ratio + 2) {
4351- new_buildings_stop_ = true;
4352- }
4353- // 2. to not exhaust all free spots
4354- if (!has_enough_space) {
4355- new_buildings_stop_ = true;
4356- }
4357- // 3. too keep some proportions production sites vs military sites
4358- if ((num_prod_constructionsites + productionsites.size()) >
4359- (msites_in_constr() + militarysites.size()) * 5) {
4360- new_buildings_stop_ = true;
4361- }
4362- // 4. if we do not have 2 mines at least
4363- if (mines_.size() < 2) {
4364- new_buildings_stop_ = true;
4365- }
4366+ assert(persistent_data->target_military_score >= persistent_data->least_military_score);
4367
4368 // we must calculate wood policy
4369 const DescriptionIndex wood_index = tribe_->safe_ware_index("log");
4370 // stocked wood is to be in some propotion to productionsites and
4371 // constructionsites (this proportion is bit artifical, or we can say
4372 // it is proportion to the size of economy). Plus some positive 'margin'
4373- const int32_t stocked_wood_margin = get_warehoused_stock(wood_index) -
4374- productionsites.size() * 2 - num_prod_constructionsites +
4375- persistent_data->ai_personality_wood_difference;
4376+ const int32_t stocked_wood_margin = calculate_stocklevel(wood_index) -
4377+ productionsites.size() * 2 - numof_psites_in_constr +
4378+ management_data.get_military_number_at(87) / 5;
4379 if (gametime < 15 * 60 * 1000) {
4380 wood_policy_ = WoodPolicy::kAllowRangers;
4381 } else if (stocked_wood_margin > 80) {
4382@@ -1640,34 +2214,6 @@
4383 wood_policy_ = WoodPolicy::kAllowRangers;
4384 }
4385
4386- // we must consider need for mines
4387- // set necessity for mines
4388- // we use 'virtual mines', because also mine spots can be changed
4389- // to mines when AI decides so
4390-
4391- resource_necessity_mines_ = 100 * (15 - virtual_mines) / 15;
4392- resource_necessity_mines_ = (resource_necessity_mines_ > 100) ? 100 : resource_necessity_mines_;
4393- resource_necessity_mines_ = (resource_necessity_mines_ < 20) ? 10 : resource_necessity_mines_;
4394-
4395- // here we calculate how badly we need to expand, result is number (0-100)
4396- // like a percent
4397- if (spots_ == 0) {
4398- resource_necessity_territory_ = 100;
4399- } else {
4400- resource_necessity_territory_ = 100 * 3 * (productionsites.size() + 5) / spots_;
4401- resource_necessity_territory_ =
4402- (resource_necessity_territory_ > 100) ? 100 : resource_necessity_territory_;
4403- resource_necessity_territory_ =
4404- (resource_necessity_territory_ < 10) ? 10 : resource_necessity_territory_;
4405- // alse we need at lest 4 big spots
4406- if (spots_avail.at(BUILDCAPS_BIG) < 2) {
4407- resource_necessity_territory_ = 100;
4408- }
4409- if (spots_avail.at(BUILDCAPS_MEDIUM) < 4) {
4410- resource_necessity_territory_ = 100;
4411- }
4412- }
4413-
4414 BuildingObserver* best_building = nullptr;
4415 int32_t proposed_priority = 0;
4416 Coords proposed_coords;
4417@@ -1685,7 +2231,8 @@
4418
4419 // not doing this for non-military buildins
4420 if (!(bo.type == BuildingObserver::Type::kMilitarysite ||
4421- bo.type == BuildingObserver::Type::kTrainingsite))
4422+ bo.type == BuildingObserver::Type::kTrainingsite ||
4423+ bo.type == BuildingObserver::Type::kProductionsite))
4424 continue;
4425
4426 // and neither for small military buildings
4427@@ -1698,13 +2245,17 @@
4428 // checking we have enough critical material on stock
4429 for (uint32_t m = 0; m < bo.critical_building_material.size(); ++m) {
4430 DescriptionIndex wt(static_cast<size_t>(bo.critical_building_material.at(m)));
4431- uint32_t treshold = 3;
4432+ uint32_t treshold = 7;
4433 // generally trainingsites are more important
4434 if (bo.type == BuildingObserver::Type::kTrainingsite) {
4435+ treshold = 4;
4436+ }
4437+
4438+ if (bo.type == BuildingObserver::Type::kProductionsite) {
4439 treshold = 2;
4440 }
4441
4442- if (get_warehoused_stock(wt) < treshold) {
4443+ if (calculate_stocklevel(wt) <= treshold) {
4444 bo.build_material_shortage = true;
4445 break;
4446 }
4447@@ -1722,6 +2273,11 @@
4448
4449 bo.new_building = check_building_necessity(bo, PerfEvaluation::kForConstruction, gametime);
4450
4451+ if (bo.is(BuildingAttribute::kShipyard)) {
4452+ assert(bo.new_building == BuildingNecessity::kAllowed ||
4453+ bo.new_building == BuildingNecessity::kForbidden);
4454+ }
4455+
4456 if (bo.new_building == BuildingNecessity::kAllowed) {
4457 bo.new_building_overdue = 0;
4458 }
4459@@ -1732,7 +2288,7 @@
4460 bo.new_building == BuildingNecessity::kForced ||
4461 bo.new_building == BuildingNecessity::kAllowed ||
4462 bo.new_building == BuildingNecessity::kNeededPending) &&
4463- !bo.outputs.empty()) {
4464+ (!bo.outputs.empty() || bo.is(BuildingAttribute::kBarracks))) {
4465 if (bo.max_needed_preciousness <= 0) {
4466 throw wexception("AI: Max presciousness must not be <= 0 for building: %s",
4467 bo.desc->name().c_str());
4468@@ -1741,6 +2297,7 @@
4469 bo.max_needed_preciousness = 0;
4470 } else {
4471 // For other situations we make sure max_needed_preciousness is zero
4472+
4473 assert(bo.max_needed_preciousness == 0);
4474 }
4475
4476@@ -1763,42 +2320,31 @@
4477 // c) all other cases: primary_priority = 0;
4478 if (bo.max_needed_preciousness > 0) {
4479 if (bo.new_building == BuildingNecessity::kAllowed) {
4480- bo.primary_priority = bo.max_needed_preciousness;
4481+ bo.primary_priority += bo.max_needed_preciousness;
4482 } else {
4483- bo.primary_priority = bo.max_needed_preciousness +
4484- bo.max_needed_preciousness * bo.new_building_overdue / 100 +
4485- bo.new_building_overdue / 20;
4486+ bo.primary_priority += bo.primary_priority * bo.new_building_overdue *
4487+ std::abs(management_data.get_military_number_at(120)) / 500;
4488+ bo.primary_priority += bo.max_needed_preciousness +
4489+ bo.max_needed_preciousness * bo.new_building_overdue *
4490+ std::abs(management_data.get_military_number_at(70)) /
4491+ 1000 +
4492+ bo.new_building_overdue *
4493+ std::abs(management_data.get_military_number_at(71)) / 50;
4494+ if (bo.new_building == BuildingNecessity::kForced) {
4495+ bo.primary_priority += bo.new_building_overdue *
4496+ std::abs(management_data.get_military_number_at(119)) / 50;
4497+ }
4498 }
4499 } else {
4500 bo.primary_priority = 0;
4501 }
4502
4503- // Generally we don't start another building if there is some of the same type in
4504- // construction
4505- // Some types of building allow two buildings in construction though, but not more
4506- // Below checks are to guarantee that there is no logical error in previous steps, or
4507- // inconsistency in AI data
4508- if (bo.new_building == BuildingNecessity::kNeeded ||
4509- bo.new_building == BuildingNecessity::kForced ||
4510- bo.new_building == BuildingNecessity::kAllowed ||
4511- bo.new_building == BuildingNecessity::kNeededPending) {
4512- if (bo.plants_trees || bo.need_trees || bo.max_needed_preciousness >= 10) {
4513- if (bo.cnt_under_construction + bo.unoccupied_count > 1) {
4514- throw wexception("AI inconsistency: %s: total_count %d > 1, unoccupied: %d",
4515- bo.name, bo.total_count(), bo.unoccupied_count);
4516- }
4517- } else {
4518- if (bo.cnt_under_construction + bo.unoccupied_count > 0) {
4519- throw wexception("AI inconsistency: %s: total_count %d > 0, unoccupied: %d",
4520- bo.name, bo.total_count(), bo.unoccupied_count);
4521- }
4522- }
4523- }
4524-
4525 } else if (bo.type == BuildingObserver::Type::kMilitarysite) {
4526- bo.new_building = check_building_necessity(bo.desc->get_size(), gametime);
4527+ bo.new_building = check_building_necessity(bo, gametime);
4528 } else if (bo.type == BuildingObserver::Type::kTrainingsite) {
4529 bo.new_building = check_building_necessity(bo, PerfEvaluation::kForConstruction, gametime);
4530+ } else if (bo.type == BuildingObserver::Type::kWarehouse) {
4531+ bo.new_building = check_warehouse_necessity(bo, gametime);
4532 } else if (bo.aimode_limit_status() != AiModeBuildings::kAnotherAllowed) {
4533 bo.new_building = BuildingNecessity::kNotNeeded;
4534 } else {
4535@@ -1848,13 +2394,13 @@
4536 }
4537
4538 // testing for reserved ports
4539- if (!bo.is_port) {
4540+ if (!bo.is(BuildingAttribute::kPort)) {
4541 if (port_reserved_coords.count(bf->coords.hash()) > 0) {
4542 continue;
4543 }
4544 }
4545
4546- if (time(nullptr) % 3 == 0 && bo.total_count() > 0) {
4547+ if (std::rand() % 3 == 0 && bo.total_count() > 0) {
4548 continue;
4549 } // add randomnes and ease AI
4550
4551@@ -1864,7 +2410,8 @@
4552
4553 // here we do an exemption for lumberjacks, mainly in early stages of game
4554 // sometimes the first one is not built and AI waits too long for second attempt
4555- if (gametime - bo.construction_decision_time < kBuildingMinInterval && !bo.need_trees) {
4556+ if (gametime - bo.construction_decision_time < kBuildingMinInterval &&
4557+ !bo.is(BuildingAttribute::kLumberjack)) {
4558 continue;
4559 }
4560
4561@@ -1877,8 +2424,11 @@
4562
4563 if (bo.type == BuildingObserver::Type::kProductionsite) {
4564
4565+ prio = management_data.neuron_pool[44].get_result_safe(bf->military_score_ / 20) / 5;
4566+ assert(prio >= -20 && prio <= 20);
4567+
4568 // this can be only a well (as by now)
4569- if (bo.mines_water) {
4570+ if (bo.is(BuildingAttribute::kWell)) {
4571
4572 if (bo.new_building == BuildingNecessity::kForced) {
4573 assert(bo.total_count() - bo.unconnected_count == 0);
4574@@ -1900,33 +2450,41 @@
4575 prio += 200;
4576 }
4577
4578- prio += bf->ground_water - 2;
4579+ prio += -10 + 2 * bf->ground_water;
4580
4581- } else if (bo.need_trees) { // LUMBERJACS
4582+ } else if (bo.is(BuildingAttribute::kLumberjack)) {
4583
4584 prio = bo.primary_priority;
4585
4586- prio += -20 + 200 / (bo.total_count() + 1);
4587-
4588 if (bo.new_building == BuildingNecessity::kForced) {
4589- prio *= 2;
4590- } else if (bf->trees_nearby < 2 && bf->supporters_nearby.at(bo.outputs.at(0) == 0)) {
4591+ prio += 5 * std::abs(management_data.get_military_number_at(17));
4592+ }
4593+
4594+ if (bf->trees_nearby < trees_nearby_treshold_ &&
4595+ bo.new_building == BuildingNecessity::kAllowed) {
4596 continue;
4597 }
4598
4599+ prio += std::abs(management_data.get_military_number_at(26)) *
4600+ (bf->trees_nearby - trees_nearby_treshold_) / 10;
4601+
4602 // consider cutters and rangers nearby
4603- prio -= bf->producers_nearby.at(bo.outputs.at(0)) * 20;
4604- prio += bf->supporters_nearby.at(bo.outputs.at(0)) * 5;
4605-
4606- prio += 2 * bf->trees_nearby;
4607-
4608- } else if (bo.need_rocks) {
4609+ prio += 2 * bf->supporters_nearby.at(bo.outputs.at(0)) *
4610+ std::abs(management_data.get_military_number_at(25));
4611+ prio -= bf->producers_nearby.at(bo.outputs.at(0)) *
4612+ std::abs(management_data.get_military_number_at(36)) * 3;
4613+
4614+ } else if (bo.is(BuildingAttribute::kNeedsRocks)) {
4615
4616 // Quarries are generally to be built everywhere where rocks are
4617 // no matter the need for granite, as rocks are considered an obstacle
4618 // to expansion
4619 prio = 2 * bf->rocks_nearby;
4620
4621+ if (bf->rocks_nearby > 0 && bf->near_border) {
4622+ prio += management_data.get_military_number_at(27) / 2;
4623+ }
4624+
4625 // value is initialized with 1 but minimal value that can be
4626 // calculated is 11
4627 if (prio <= 1) {
4628@@ -1937,19 +2495,14 @@
4629 prio += 150;
4630 }
4631
4632- if (bo.stocklevel_time < game().get_gametime() - 5 * 1000) {
4633- bo.stocklevel = get_stocklevel(static_cast<size_t>(bo.production_hint));
4634- bo.stocklevel_time = game().get_gametime();
4635- }
4636-
4637- if (bo.stocklevel == 0) {
4638+ if (get_stocklevel(bo, gametime) == 0) {
4639 prio *= 2;
4640 }
4641
4642 // to prevent to many quaries on one spot
4643 prio = prio - 50 * bf->producers_nearby.at(bo.outputs.at(0));
4644
4645- } else if (bo.is_hunter) {
4646+ } else if (bo.is(BuildingAttribute::kHunter)) {
4647
4648 if (bf->critters_nearby < 5) {
4649 continue;
4650@@ -1967,26 +2520,31 @@
4651 prio +=
4652 (bf->critters_nearby * 3) - 8 - 5 * bf->producers_nearby.at(bo.outputs.at(0));
4653
4654- } else if (bo.is_fisher) { // fisher
4655+ } else if (bo.is(BuildingAttribute::kFisher)) { // fisher
4656
4657 if (bf->water_nearby < 2 || bf->fish_nearby < 2) {
4658 continue;
4659 }
4660
4661 if (bo.new_building == BuildingNecessity::kForced) {
4662- prio += 20;
4663+ prio += 200;
4664 }
4665
4666 // Overdue priority here
4667 prio += bo.primary_priority;
4668
4669 prio -= bf->producers_nearby.at(bo.outputs.at(0)) * 20;
4670- prio += bf->supporters_nearby.at(bo.outputs.at(0)) * 10;
4671+ prio += bf->supporters_nearby.at(bo.outputs.at(0)) * 20;
4672
4673- prio += -5 + bf->fish_nearby;
4674+ prio +=
4675+ -5 +
4676+ bf->fish_nearby * (1 + std::abs(management_data.get_military_number_at(63) / 15));
4677+ if (resource_necessity_water_needed_) {
4678+ prio *= 3;
4679+ }
4680
4681 } else if (bo.production_hint >= 0) {
4682- if (bo.plants_trees) {
4683+ if (bo.is(BuildingAttribute::kRanger)) {
4684 assert(bo.cnt_target > 0);
4685 } else {
4686 bo.cnt_target =
4687@@ -1996,39 +2554,41 @@
4688 // They have no own primary priority
4689 assert(bo.primary_priority == 0);
4690
4691- if (bo.plants_trees) { // RANGERS
4692+ if (bo.is(BuildingAttribute::kRanger)) {
4693
4694 assert(bo.new_building == BuildingNecessity::kNeeded);
4695
4696- // if there are too many trees nearby
4697- if (bf->trees_nearby > 25 && bo.total_count() >= 1) {
4698- continue;
4699- }
4700-
4701- // for small starting spots - to prevent crowding by rangers and trees
4702- if (spots_ < (4 * bo.total_count()) && bo.total_count() > 0) {
4703- continue;
4704- }
4705+ prio = 0;
4706
4707 if (bo.total_count() == 0) {
4708- prio = 200;
4709+ prio += 200;
4710 } else {
4711- prio = 50 / bo.total_count();
4712+ prio += std::abs(management_data.get_military_number_at(66)) *
4713+ (bo.cnt_target - bo.total_count());
4714 }
4715-
4716- // considering producers
4717- prio += std::min<uint8_t>(bf->producers_nearby.at(bo.production_hint), 4) * 5 -
4718- new_buildings_stop_ * 15 - bf->space_consumers_nearby * 5 -
4719- bf->rocks_nearby / 3 + bf->trees_nearby / 2 +
4720- std::min<uint8_t>(bf->supporters_nearby.at(bo.production_hint), 4) * 3;
4721+ prio -= bf->water_nearby / 5;
4722+
4723+ prio += management_data.neuron_pool[67].get_result_safe(
4724+ bf->producers_nearby.at(bo.production_hint) * 5, kAbsValue) /
4725+ 2;
4726+
4727+ prio +=
4728+ management_data.neuron_pool[49].get_result_safe(bf->trees_nearby, kAbsValue) /
4729+ 5;
4730+
4731+ prio += bf->producers_nearby.at(bo.production_hint) * 5 -
4732+ (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) * 15 -
4733+ bf->space_consumers_nearby * 5 - bf->rocks_nearby / 3 +
4734+ bf->supporters_nearby.at(bo.production_hint) * 3;
4735
4736 } else { // FISH BREEDERS and GAME KEEPERS
4737
4738 // especially for fish breeders
4739- if (bo.need_water && (bf->water_nearby < 6 || bf->fish_nearby < 6)) {
4740+ if (bo.is(BuildingAttribute::kNeedsCoast) &&
4741+ (bf->water_nearby < 6 || bf->fish_nearby < 6)) {
4742 continue;
4743 }
4744- if (bo.need_water) {
4745+ if (bo.is(BuildingAttribute::kNeedsCoast)) {
4746 prio += (-6 + bf->water_nearby) / 3;
4747 prio += (-6 + bf->fish_nearby) / 3;
4748 }
4749@@ -2037,18 +2597,13 @@
4750 continue;
4751 }
4752
4753- if (bo.stocklevel_time < game().get_gametime() - 5 * 1000) {
4754- bo.stocklevel =
4755- get_stocklevel_by_hint(static_cast<size_t>(bo.production_hint));
4756- bo.stocklevel_time = game().get_gametime();
4757- }
4758- if (bo.stocklevel > 50) {
4759+ if (get_stocklevel(bo, gametime) > 50) {
4760 continue;
4761 }
4762
4763 if (bo.total_count() == 0) {
4764 prio += 100;
4765- } else if (!bo.need_water) {
4766+ } else if (!bo.is(BuildingAttribute::kNeedsCoast)) {
4767 prio += 10 / bo.total_count();
4768 }
4769
4770@@ -2060,21 +2615,18 @@
4771 }
4772 }
4773
4774- } else if (bo.recruitment && !new_buildings_stop_) {
4775- // this will depend on number of mines_ and productionsites
4776- if (static_cast<int32_t>((productionsites.size() + mines_.size()) / 30) >
4777- bo.total_count() &&
4778- (bo.cnt_under_construction + bo.unoccupied_count) == 0 &&
4779- // but only if current buildings are utilized enough
4780- (bo.total_count() == 0 || bo.current_stats > 60)) {
4781- prio = 10;
4782- }
4783+ } else if (bo.is(BuildingAttribute::kRecruitment)) {
4784+ prio = bo.primary_priority;
4785+ prio -= bf->unowned_land_nearby * 2;
4786+ prio -= (bf->enemy_nearby) * 100;
4787+ prio -= (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) * 100;
4788 } else { // finally normal productionsites
4789 assert(bo.production_hint < 0);
4790
4791 if (bo.new_building == BuildingNecessity::kForced) {
4792 prio += 150;
4793- } else if (bo.is_shipyard) {
4794+ assert(!bo.is(BuildingAttribute::kShipyard));
4795+ } else if (bo.is(BuildingAttribute::kShipyard)) {
4796 assert(bo.new_building == BuildingNecessity::kAllowed);
4797 if (!seafaring_economy) {
4798 continue;
4799@@ -2089,33 +2641,43 @@
4800 // we check separatelly buildings with no inputs and some inputs
4801 if (bo.inputs.empty()) {
4802
4803- if (bo.space_consumer) {
4804+ assert(!bo.is(BuildingAttribute::kShipyard));
4805+
4806+ if (bo.is(BuildingAttribute::kSpaceConsumer)) {
4807 // we dont like trees nearby
4808 prio += 1 - bf->trees_nearby / 15;
4809 // we attempt to cluster space consumers together
4810 prio += bf->space_consumers_nearby * 2;
4811 // and be far from rangers
4812- prio += 1 - bf->rangers_nearby * 3;
4813+ prio += 1 -
4814+ bf->rangers_nearby *
4815+ std::abs(management_data.get_military_number_at(102)) / 5;
4816 } else {
4817 // leave some free space between them
4818- prio -= bf->producers_nearby.at(bo.outputs.at(0)) * 5;
4819- }
4820-
4821- if (bo.space_consumer && !bf->water_nearby) { // not close to water
4822- prio += 1;
4823- }
4824-
4825- if (bo.space_consumer &&
4826- !bf->unowned_mines_spots_nearby) { // not close to mountains
4827- prio += 1;
4828+ prio -= bf->producers_nearby.at(bo.outputs.at(0)) *
4829+ std::abs(management_data.get_military_number_at(108)) / 5;
4830+ }
4831+
4832+ if (bo.is(BuildingAttribute::kSpaceConsumer) &&
4833+ bf->water_nearby) { // not close to water
4834+ prio -= std::abs(management_data.get_military_number_at(103)) / 5;
4835+ }
4836+
4837+ if (bo.is(BuildingAttribute::kSpaceConsumer) &&
4838+ bf->unowned_mines_spots_nearby) { // not close to mountains
4839+ prio -= std::abs(management_data.get_military_number_at(104)) / 5;
4840 }
4841 }
4842
4843- else if (bo.is_shipyard) {
4844+ else if (bo.is(BuildingAttribute::kShipyard)) {
4845 // for now AI builds only one shipyard
4846- if (bf->open_water_nearby > 1 && (bo.total_count() - bo.unconnected_count) == 0 &&
4847- seafaring_economy) {
4848- prio += productionsites.size() * 5 + bf->open_water_nearby;
4849+ assert(bo.total_count() == 0);
4850+ if (bf->open_water_nearby > 3 && seafaring_economy) {
4851+ prio += productionsites.size() * 5 +
4852+ bf->open_water_nearby *
4853+ std::abs(management_data.get_military_number_at(109)) / 10;
4854+ } else {
4855+ continue;
4856 }
4857 }
4858
4859@@ -2125,11 +2687,11 @@
4860
4861 // bonus for big buildings if shortage of big fields
4862 if (spots_avail.at(BUILDCAPS_BIG) <= 5 && bo.desc->get_size() == 3) {
4863- prio += 10;
4864+ prio += std::abs(management_data.get_military_number_at(105)) / 5;
4865 }
4866
4867 if (spots_avail.at(BUILDCAPS_MEDIUM) <= 5 && bo.desc->get_size() == 2) {
4868- prio += 5;
4869+ prio += std::abs(management_data.get_military_number_at(106)) / 5;
4870 }
4871
4872 // +1 if any consumers_ are nearby
4873@@ -2139,152 +2701,69 @@
4874 consumers_nearby_count += bf->consumers_nearby.at(bo.outputs.at(k));
4875
4876 if (consumers_nearby_count > 0) {
4877- prio += 1;
4878+ prio += std::abs(management_data.get_military_number_at(107)) / 3;
4879 }
4880 }
4881
4882 // Consider border with exemption of some huts
4883- if (!(bo.need_trees || bo.need_water || bo.is_fisher)) {
4884+ if (!(bo.is(BuildingAttribute::kLumberjack) || bo.is(BuildingAttribute::kNeedsCoast) ||
4885+ bo.is(BuildingAttribute::kFisher))) {
4886 prio = recalc_with_border_range(*bf, prio);
4887- } else if (bf->near_border && (bo.need_trees || bo.need_water)) {
4888+ } else if (bf->near_border && (bo.is(BuildingAttribute::kLumberjack) ||
4889+ bo.is(BuildingAttribute::kNeedsCoast))) {
4890 prio /= 2;
4891 }
4892
4893 } // production sites done
4894 else if (bo.type == BuildingObserver::Type::kMilitarysite) {
4895
4896- if (!(bf->unowned_land_nearby || bf->enemy_nearby)) {
4897- continue;
4898- }
4899-
4900- if (military_last_build_ > gametime - 15 * 1000) {
4901- continue;
4902- }
4903-
4904- // This is another restriction of military building - but general
4905- if (bf->enemy_nearby && bo.fighting_type) {
4906- ;
4907- } // it is ok, go on
4908- else if (bf->unowned_mines_spots_nearby > 2 &&
4909- (bo.mountain_conqueror || bo.expansion_type)) {
4910- ;
4911- } // it is ok, go on
4912- else if (bo.expansion_type) {
4913- if (bo.desc->get_size() == 2 && gametime % 2 >= 1) {
4914- continue;
4915- }
4916- if (bo.desc->get_size() == 3 && gametime % 4 >= 1) {
4917- continue;
4918- };
4919- } else {
4920- continue;
4921- } // the building is not suitable for situation
4922-
4923- // score here is a compound of various input values
4924- // usually resources in vicinity, but when enemy is nearby
4925- // additional bonus is added
4926- if (bf->enemy_nearby) {
4927- prio += bf->military_loneliness / 3;
4928- prio += (20 - bf->area_military_capacity) * 10;
4929- prio -= bo.build_material_shortage * 50;
4930- prio -= (bf->military_in_constr_nearby + bf->military_unstationed) * 50;
4931- } else {
4932- if (bf->near_border) {
4933- prio += 50;
4934- prio -= bo.build_material_shortage * 150;
4935- } else {
4936- prio -= bo.build_material_shortage * 500; // prohibitive
4937- }
4938- prio -= (bf->military_in_constr_nearby + bf->military_unstationed) * 150;
4939- prio += (5 - bf->own_military_sites_nearby_()) * 15;
4940- }
4941- prio += bf->unconnected_nearby * 50;
4942- prio += bf->unowned_land_nearby * resource_necessity_territory_ / 100;
4943- prio += bf->unowned_mines_spots_nearby * resource_necessity_mines_ / 100;
4944- prio +=
4945- ((bf->unowned_mines_spots_nearby > 0) ? 35 : 0) * resource_necessity_mines_ / 100;
4946- prio += bf->rocks_nearby / 2;
4947- prio += bf->water_nearby;
4948- prio += bf->distant_water * resource_necessity_water_needed_ / 100;
4949- prio += bf->military_loneliness / 10;
4950- prio += bf->trees_nearby / 3;
4951- if (bf->portspace_nearby == ExtendedBool::kTrue) {
4952- if (num_ports == 0) {
4953- prio += 100;
4954- } else {
4955- prio += 25;
4956- }
4957- }
4958- // sometimes expansion is stalled and this is to help boost it
4959- if (msites_in_constr() == 0 && vacant_mil_positions_ <= 2) {
4960- prio += 10;
4961- if (bf->enemy_nearby) {
4962- prio += 20;
4963- }
4964- }
4965-
4966- // additional score for bigger buildings
4967- int32_t prio_for_size = bo.desc->get_size() - 1;
4968- if (bf->enemy_nearby) {
4969- prio_for_size *= 30;
4970- } else {
4971- prio_for_size *= 5;
4972- }
4973- prio += prio_for_size;
4974+ assert(prio == 0);
4975+ prio = bo.primary_priority;
4976+
4977+ // Two possibilities why to construct militarysite here
4978+ if (!bf->defense_msite_allowed &&
4979+ bf->nearest_buildable_spot_nearby < bo.desc->get_conquers() &&
4980+ (bf->military_in_constr_nearby + bf->military_unstationed) <
4981+ concurent_ms_in_constr_no_enemy) {
4982+ // it will conquer new buildable spots for buildings or mines
4983+ ;
4984+ } else if (bf->defense_msite_allowed &&
4985+ (bf->military_in_constr_nearby + bf->military_unstationed) <
4986+ concurent_ms_in_constr_enemy_nearby) {
4987+ // we need it to increase capacity on the field
4988+ if (bo.fighting_type) {
4989+ prio += 5;
4990+ }
4991+ } else {
4992+ continue;
4993+ }
4994+ if (bf->unowned_mines_spots_nearby > 2 && bo.mountain_conqueror) {
4995+ prio += 5;
4996+ }
4997+ prio += std::abs(management_data.get_military_number_at(35)) / 5 *
4998+ (static_cast<int16_t>(bo.desc->get_conquers()) -
4999+ static_cast<int16_t>(bf->nearest_buildable_spot_nearby));
5000+
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to status/vote changes: