← Back to team overview

widelands-dev team mailing list archive

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

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/ai-post-b19-2 into lp:widelands with lp:~widelands-dev/widelands/ai_trainingsites_proportion as a prerequisite.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/ai-post-b19-2/+merge/325796

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...
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/ai-post-b19-2 into lp:widelands.
=== modified file 'data/tribes/buildings/productionsites/atlanteans/bakery/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/bakery/init.lua	2017-02-10 09:40:17 +0000
+++ data/tribes/buildings/productionsites/atlanteans/bakery/init.lua	2017-06-16 05:22:22 +0000
@@ -32,7 +32,7 @@
    },
 
    aihints = {
-      forced_after = 1200,
+      is_basic = 1,
       prohibited_till = 900
    },
 

=== modified file 'data/tribes/buildings/productionsites/atlanteans/barracks/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/barracks/init.lua	2017-02-10 09:40:17 +0000
+++ data/tribes/buildings/productionsites/atlanteans/barracks/init.lua	2017-06-16 05:22:22 +0000
@@ -34,7 +34,6 @@
    },
 
    aihints = {
-      forced_after = 1000,
       very_weak_ai_limit = 1,
       weak_ai_limit = 3
    },

=== modified file 'data/tribes/buildings/productionsites/atlanteans/blackroot_farm/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/blackroot_farm/init.lua	2015-12-11 16:54:00 +0000
+++ data/tribes/buildings/productionsites/atlanteans/blackroot_farm/init.lua	2017-06-16 05:22:22 +0000
@@ -29,7 +29,6 @@
 
    aihints = {
       prohibited_till = 600,
-      forced_after = 800,
       space_consumer = true
    },
 

=== modified file 'data/tribes/buildings/productionsites/atlanteans/crystalmine/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/crystalmine/init.lua	2017-02-18 23:07:56 +0000
+++ data/tribes/buildings/productionsites/atlanteans/crystalmine/init.lua	2017-06-16 05:22:22 +0000
@@ -36,6 +36,7 @@
 
    aihints = {
       mines = "stones",
+      is_basic = 1,
       prohibited_till = 600
    },
 

=== modified file 'data/tribes/buildings/productionsites/atlanteans/farm/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/farm/init.lua	2016-01-28 05:24:34 +0000
+++ data/tribes/buildings/productionsites/atlanteans/farm/init.lua	2017-06-16 05:22:22 +0000
@@ -30,9 +30,9 @@
 
    aihints = {
       space_consumer = true,
+      is_basic = 1,
        -- Farm needs spidercloth to be built and spidercloth needs corn for production
        -- -> farm should be built ASAP!
-      forced_after = 400,
       prohibited_till = 200
    },
 

=== modified file 'data/tribes/buildings/productionsites/atlanteans/fishers_house/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/fishers_house/init.lua	2015-12-11 16:54:00 +0000
+++ data/tribes/buildings/productionsites/atlanteans/fishers_house/init.lua	2017-06-16 05:22:22 +0000
@@ -26,6 +26,7 @@
 
    aihints = {
       needs_water = true,
+      is_basic = 1,
       prohibited_till = 600
    },
 

=== modified file 'data/tribes/buildings/productionsites/atlanteans/mill/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/mill/init.lua	2016-10-30 19:15:45 +0000
+++ data/tribes/buildings/productionsites/atlanteans/mill/init.lua	2017-06-16 05:22:22 +0000
@@ -33,6 +33,7 @@
    },
 
    aihints = {
+      is_basic = 1,
       prohibited_till = 600
    },
 

=== modified file 'data/tribes/buildings/productionsites/atlanteans/sawmill/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/sawmill/init.lua	2016-12-11 09:29:28 +0000
+++ data/tribes/buildings/productionsites/atlanteans/sawmill/init.lua	2017-06-16 05:22:22 +0000
@@ -31,7 +31,7 @@
    },
 
    aihints = {
-      forced_after = 250,
+      is_basic = 2,
       prohibited_till = 250,
       very_weak_ai_limit = 1,
       weak_ai_limit = 2

=== modified file 'data/tribes/buildings/productionsites/atlanteans/smokery/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/smokery/init.lua	2016-10-26 19:21:32 +0000
+++ data/tribes/buildings/productionsites/atlanteans/smokery/init.lua	2017-06-16 05:22:22 +0000
@@ -32,7 +32,7 @@
    },
 
    aihints = {
-      forced_after = 800,
+      is_basic = 1,
       prohibited_till = 180,
       very_weak_ai_limit = 1,
       weak_ai_limit = 2

=== modified file 'data/tribes/buildings/productionsites/atlanteans/spiderfarm/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/spiderfarm/init.lua	2016-09-03 14:59:10 +0000
+++ data/tribes/buildings/productionsites/atlanteans/spiderfarm/init.lua	2017-06-16 05:22:22 +0000
@@ -31,7 +31,7 @@
    },
 
    aihints = {
-      forced_after = 450,
+      is_basic = 1,
       prohibited_till = 350
    },
 

=== modified file 'data/tribes/buildings/productionsites/atlanteans/toolsmithy/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/toolsmithy/init.lua	2016-10-30 19:15:45 +0000
+++ data/tribes/buildings/productionsites/atlanteans/toolsmithy/init.lua	2017-06-16 05:22:22 +0000
@@ -32,7 +32,6 @@
    },
 
    aihints = {
-      forced_after = 500,
       prohibited_till = 450
    },
 

=== modified file 'data/tribes/buildings/productionsites/atlanteans/weaving_mill/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/weaving_mill/init.lua	2016-12-14 09:09:34 +0000
+++ data/tribes/buildings/productionsites/atlanteans/weaving_mill/init.lua	2017-06-16 05:22:22 +0000
@@ -32,8 +32,8 @@
    },
 
    aihints = {
-      forced_after = 600,
-      prohibited_till = 450,
+      prohibited_till = 400,
+      is_basic = 1,
       very_weak_ai_limit = 1,
       weak_ai_limit = 2
    },

=== modified file 'data/tribes/buildings/productionsites/barbarians/barracks/init.lua'
--- data/tribes/buildings/productionsites/barbarians/barracks/init.lua	2017-02-10 09:40:17 +0000
+++ data/tribes/buildings/productionsites/barbarians/barracks/init.lua	2017-06-16 05:22:22 +0000
@@ -33,7 +33,7 @@
    },
 
    aihints = {
-      forced_after = 1000,
+      is_basic = 1,
       very_weak_ai_limit = 1,
       weak_ai_limit = 3
    },

=== modified file 'data/tribes/buildings/productionsites/barbarians/farm/init.lua'
--- data/tribes/buildings/productionsites/barbarians/farm/init.lua	2016-01-28 05:24:34 +0000
+++ data/tribes/buildings/productionsites/barbarians/farm/init.lua	2017-06-16 05:22:22 +0000
@@ -40,8 +40,7 @@
    },
 
    aihints = {
-      space_consumer = true,
-      forced_after = 600
+      space_consumer = true
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/barbarians/gamekeepers_hut/init.lua'
--- data/tribes/buildings/productionsites/barbarians/gamekeepers_hut/init.lua	2015-12-11 16:54:00 +0000
+++ data/tribes/buildings/productionsites/barbarians/gamekeepers_hut/init.lua	2017-06-16 05:22:22 +0000
@@ -35,7 +35,8 @@
 
    aihints = {
       renews_map_resource = "meat",
-      prohibited_till = 900
+      prohibited_till = 900,
+      is_basic = 1
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/barbarians/hunters_hut/init.lua'
--- data/tribes/buildings/productionsites/barbarians/hunters_hut/init.lua	2015-12-11 16:54:00 +0000
+++ data/tribes/buildings/productionsites/barbarians/hunters_hut/init.lua	2017-06-16 05:22:22 +0000
@@ -34,7 +34,8 @@
    },
 
    aihints = {
-      prohibited_till = 500
+      prohibited_till = 500,
+      is_basic = 1
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/barbarians/lime_kiln/init.lua'
--- data/tribes/buildings/productionsites/barbarians/lime_kiln/init.lua	2016-09-03 14:59:10 +0000
+++ data/tribes/buildings/productionsites/barbarians/lime_kiln/init.lua	2017-06-16 05:22:22 +0000
@@ -31,9 +31,9 @@
    },
 
    aihints = {
-      forced_after = 600,
       very_weak_ai_limit = 1,
-      weak_ai_limit = 2
+      weak_ai_limit = 2,
+      is_basic = 1
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/barbarians/lumberjacks_hut/init.lua'
--- data/tribes/buildings/productionsites/barbarians/lumberjacks_hut/init.lua	2015-12-11 16:54:00 +0000
+++ data/tribes/buildings/productionsites/barbarians/lumberjacks_hut/init.lua	2017-06-16 05:22:22 +0000
@@ -32,8 +32,7 @@
    },
 
    aihints = {
-      forced_after = 180,
-      prohibited_till = 180,
+      is_basic = 1,
       logproducer = true
    },
 

=== modified file 'data/tribes/buildings/productionsites/barbarians/metal_workshop/init.lua'
--- data/tribes/buildings/productionsites/barbarians/metal_workshop/init.lua	2016-09-03 14:59:10 +0000
+++ data/tribes/buildings/productionsites/barbarians/metal_workshop/init.lua	2017-06-16 05:22:22 +0000
@@ -44,7 +44,7 @@
    },
 
    aihints = {
-      forced_after = 400,
+      is_basic = 2,
       prohibited_till = 400
    },
 

=== modified file 'data/tribes/buildings/productionsites/barbarians/micro_brewery/init.lua'
--- data/tribes/buildings/productionsites/barbarians/micro_brewery/init.lua	2016-09-03 14:59:10 +0000
+++ data/tribes/buildings/productionsites/barbarians/micro_brewery/init.lua	2017-06-16 05:22:22 +0000
@@ -34,7 +34,6 @@
    },
 
    aihints = {
-      forced_after = 500
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/barbarians/rangers_hut/init.lua'
--- data/tribes/buildings/productionsites/barbarians/rangers_hut/init.lua	2015-12-11 16:54:00 +0000
+++ data/tribes/buildings/productionsites/barbarians/rangers_hut/init.lua	2017-06-16 05:22:22 +0000
@@ -34,7 +34,7 @@
    aihints = {
       renews_map_resource = "log",
       space_consumer = true,
-      prohibited_till = 200
+      is_basic = 1
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/barbarians/reed_yard/init.lua'
--- data/tribes/buildings/productionsites/barbarians/reed_yard/init.lua	2015-12-11 16:54:00 +0000
+++ data/tribes/buildings/productionsites/barbarians/reed_yard/init.lua	2017-06-16 05:22:22 +0000
@@ -27,7 +27,7 @@
 
    aihints = {
       space_consumer = true,
-      forced_after = 250,
+      is_basic = 1,
       prohibited_till = 250
    },
 

=== modified file 'data/tribes/buildings/productionsites/barbarians/smelting_works/init.lua'
--- data/tribes/buildings/productionsites/barbarians/smelting_works/init.lua	2016-10-24 10:07:57 +0000
+++ data/tribes/buildings/productionsites/barbarians/smelting_works/init.lua	2017-06-16 05:22:22 +0000
@@ -35,6 +35,7 @@
 
    aihints = {
       prohibited_till = 400,
+      is_basic = 1,
       very_weak_ai_limit = 1,
       weak_ai_limit = 2
    },

=== modified file 'data/tribes/buildings/productionsites/barbarians/tavern/init.lua'
--- data/tribes/buildings/productionsites/barbarians/tavern/init.lua	2016-10-24 10:10:06 +0000
+++ data/tribes/buildings/productionsites/barbarians/tavern/init.lua	2017-06-16 05:22:22 +0000
@@ -38,7 +38,7 @@
    },
 
    aihints = {
-      forced_after = 900
+      is_basic = 1
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/barbarians/well/init.lua'
--- data/tribes/buildings/productionsites/barbarians/well/init.lua	2016-10-05 05:29:11 +0000
+++ data/tribes/buildings/productionsites/barbarians/well/init.lua	2017-06-16 05:22:22 +0000
@@ -33,8 +33,7 @@
 
    aihints = {
       mines_water = true,
-      prohibited_till = 800,
-      forced_after = 800
+      is_basic = 1
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/barbarians/wood_hardener/init.lua'
--- data/tribes/buildings/productionsites/barbarians/wood_hardener/init.lua	2016-09-03 14:59:10 +0000
+++ data/tribes/buildings/productionsites/barbarians/wood_hardener/init.lua	2017-06-16 05:22:22 +0000
@@ -38,7 +38,7 @@
    },
 
    aihints = {
-      forced_after = 250,
+      is_basic = 1,
       prohibited_till = 250,
       very_weak_ai_limit = 1,
       weak_ai_limit = 2

=== modified file 'data/tribes/buildings/productionsites/empire/armorsmithy/init.lua'
--- data/tribes/buildings/productionsites/empire/armorsmithy/init.lua	2016-10-30 19:06:01 +0000
+++ data/tribes/buildings/productionsites/empire/armorsmithy/init.lua	2017-06-16 05:22:22 +0000
@@ -43,7 +43,6 @@
 
    aihints = {
       prohibited_till = 700,
-      forced_after = 900
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/empire/bakery/init.lua'
--- data/tribes/buildings/productionsites/empire/bakery/init.lua	2017-02-10 09:40:17 +0000
+++ data/tribes/buildings/productionsites/empire/bakery/init.lua	2017-06-16 05:22:22 +0000
@@ -37,7 +37,7 @@
 
    aihints = {
       prohibited_till = 600,
-      forced_after = 700
+      is_basic = 1
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/empire/barracks/init.lua'
--- data/tribes/buildings/productionsites/empire/barracks/init.lua	2017-02-10 09:40:17 +0000
+++ data/tribes/buildings/productionsites/empire/barracks/init.lua	2017-06-16 05:22:22 +0000
@@ -35,7 +35,7 @@
    },
 
    aihints = {
-      forced_after = 1000,
+      --forced_after = 1000, 
       very_weak_ai_limit = 1,
       weak_ai_limit = 3
    },

=== modified file 'data/tribes/buildings/productionsites/empire/brewery/init.lua'
--- data/tribes/buildings/productionsites/empire/brewery/init.lua	2016-09-03 14:59:10 +0000
+++ data/tribes/buildings/productionsites/empire/brewery/init.lua	2017-06-16 05:22:22 +0000
@@ -31,7 +31,6 @@
    },
 
    aihints = {
-      forced_after = 900,
       prohibited_till = 600,
       very_weak_ai_limit = 1,
       weak_ai_limit = 2

=== modified file 'data/tribes/buildings/productionsites/empire/farm/init.lua'
--- data/tribes/buildings/productionsites/empire/farm/init.lua	2016-01-28 05:24:34 +0000
+++ data/tribes/buildings/productionsites/empire/farm/init.lua	2017-06-16 05:22:22 +0000
@@ -30,8 +30,8 @@
    },
 
    aihints = {
-      space_consumer = true,
-      forced_after = 900
+      is_basic = 1,
+      space_consumer = true
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/empire/inn/init.lua'
--- data/tribes/buildings/productionsites/empire/inn/init.lua	2016-10-26 19:21:32 +0000
+++ data/tribes/buildings/productionsites/empire/inn/init.lua	2017-06-16 05:22:22 +0000
@@ -31,7 +31,6 @@
    },
 
    aihints = {
-      forced_after = 900,
       prohibited_till = 600
    },
 

=== modified file 'data/tribes/buildings/productionsites/empire/marblemine/init.lua'
--- data/tribes/buildings/productionsites/empire/marblemine/init.lua	2016-09-03 14:59:10 +0000
+++ data/tribes/buildings/productionsites/empire/marblemine/init.lua	2017-06-16 05:22:22 +0000
@@ -38,7 +38,8 @@
    aihints = {
       mines = "stones",
       mines_percent = 50,
-      prohibited_till = 450
+      prohibited_till = 450,
+      is_basic = 1
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/empire/piggery/init.lua'
--- data/tribes/buildings/productionsites/empire/piggery/init.lua	2016-09-03 14:59:10 +0000
+++ data/tribes/buildings/productionsites/empire/piggery/init.lua	2017-06-16 05:22:22 +0000
@@ -33,7 +33,6 @@
    },
 
    aihints = {
-      forced_after = 1800
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/empire/sawmill/init.lua'
--- data/tribes/buildings/productionsites/empire/sawmill/init.lua	2016-09-03 14:59:10 +0000
+++ data/tribes/buildings/productionsites/empire/sawmill/init.lua	2017-06-16 05:22:22 +0000
@@ -31,7 +31,7 @@
    },
 
    aihints = {
-      forced_after = 250,
+      is_basic = 2,
       prohibited_till = 250,
       very_weak_ai_limit = 1,
       weak_ai_limit = 2

=== modified file 'data/tribes/buildings/productionsites/empire/sheepfarm/init.lua'
--- data/tribes/buildings/productionsites/empire/sheepfarm/init.lua	2016-09-03 14:59:10 +0000
+++ data/tribes/buildings/productionsites/empire/sheepfarm/init.lua	2017-06-16 05:22:22 +0000
@@ -31,7 +31,7 @@
    },
 
    aihints = {
-      prohibited_till = 600
+      prohibited_till = 300
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/empire/stonemasons_house/init.lua'
--- data/tribes/buildings/productionsites/empire/stonemasons_house/init.lua	2016-09-03 14:59:10 +0000
+++ data/tribes/buildings/productionsites/empire/stonemasons_house/init.lua	2017-06-16 05:22:22 +0000
@@ -32,7 +32,7 @@
    },
 
    aihints = {
-      forced_after = 400,
+      is_basic = 1,
       prohibited_till = 400,
       very_weak_ai_limit = 1,
       weak_ai_limit = 2

=== modified file 'data/tribes/buildings/productionsites/empire/tavern/init.lua'
--- data/tribes/buildings/productionsites/empire/tavern/init.lua	2016-10-26 19:21:32 +0000
+++ data/tribes/buildings/productionsites/empire/tavern/init.lua	2017-06-16 05:22:22 +0000
@@ -33,7 +33,7 @@
    },
 
    aihints = {
-      forced_after = 900,
+      is_basic = 1,
       prohibited_till = 300
    },
 

=== modified file 'data/tribes/buildings/productionsites/empire/toolsmithy/init.lua'
--- data/tribes/buildings/productionsites/empire/toolsmithy/init.lua	2016-09-03 14:59:10 +0000
+++ data/tribes/buildings/productionsites/empire/toolsmithy/init.lua	2017-06-16 05:22:22 +0000
@@ -32,7 +32,6 @@
    },
 
    aihints = {
-      forced_after = 400,
       prohibited_till = 400
    },
 

=== modified file 'data/tribes/buildings/productionsites/empire/vineyard/init.lua'
--- data/tribes/buildings/productionsites/empire/vineyard/init.lua	2015-12-11 16:54:00 +0000
+++ data/tribes/buildings/productionsites/empire/vineyard/init.lua	2017-06-16 05:22:22 +0000
@@ -29,7 +29,7 @@
 
    aihints = {
       space_consumer = true,
-      forced_after = 300
+      is_basic = 1
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/empire/weaponsmithy/init.lua'
--- data/tribes/buildings/productionsites/empire/weaponsmithy/init.lua	2016-10-30 19:06:01 +0000
+++ data/tribes/buildings/productionsites/empire/weaponsmithy/init.lua	2017-06-16 05:22:22 +0000
@@ -39,7 +39,8 @@
    },
 
    aihints = {
-      prohibited_till = 900
+      prohibited_till = 900,
+      forced_after = 3600
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/productionsites/empire/winery/init.lua'
--- data/tribes/buildings/productionsites/empire/winery/init.lua	2016-09-03 14:59:10 +0000
+++ data/tribes/buildings/productionsites/empire/winery/init.lua	2017-06-16 05:22:22 +0000
@@ -33,10 +33,10 @@
    },
 
    aihints = {
-      forced_after = 600,
       prohibited_till = 600,
       very_weak_ai_limit = 1,
-      weak_ai_limit = 2
+      weak_ai_limit = 2,
+      is_basic = 1
    },
 
    working_positions = {

=== modified file 'data/tribes/buildings/trainingsites/atlanteans/dungeon/init.lua'
--- data/tribes/buildings/trainingsites/atlanteans/dungeon/init.lua	2017-02-10 09:40:17 +0000
+++ data/tribes/buildings/trainingsites/atlanteans/dungeon/init.lua	2017-06-16 05:22:22 +0000
@@ -112,7 +112,6 @@
    },
 
    aihints = {
-      trainingsite_type = "advanced",
       prohibited_till = 1500,
       very_weak_ai_limit = 0,
       weak_ai_limit = 1

=== modified file 'data/tribes/buildings/trainingsites/atlanteans/labyrinth/init.lua'
--- data/tribes/buildings/trainingsites/atlanteans/labyrinth/init.lua	2017-02-10 09:40:17 +0000
+++ data/tribes/buildings/trainingsites/atlanteans/labyrinth/init.lua	2017-06-16 05:22:22 +0000
@@ -36,7 +36,6 @@
    aihints = {
       prohibited_till = 900,
       forced_after = 1500,
-      trainingsite_type = "basic",
       very_weak_ai_limit = 1,
       weak_ai_limit = 2
    },

=== modified file 'data/tribes/buildings/trainingsites/barbarians/battlearena/init.lua'
--- data/tribes/buildings/trainingsites/barbarians/battlearena/init.lua	2017-02-10 09:40:17 +0000
+++ data/tribes/buildings/trainingsites/barbarians/battlearena/init.lua	2017-06-16 05:22:22 +0000
@@ -48,7 +48,6 @@
    aihints = {
       prohibited_till = 900,
       forced_after = 1500,
-      trainingsite_type = "basic",
       very_weak_ai_limit = 1,
       weak_ai_limit = 3
    },

=== modified file 'data/tribes/buildings/trainingsites/barbarians/trainingcamp/init.lua'
--- data/tribes/buildings/trainingsites/barbarians/trainingcamp/init.lua	2017-02-10 09:40:17 +0000
+++ data/tribes/buildings/trainingsites/barbarians/trainingcamp/init.lua	2017-06-16 05:22:22 +0000
@@ -41,7 +41,6 @@
 
    aihints = {
       prohibited_till = 1500,
-      trainingsite_type = "advanced",
       very_weak_ai_limit = 0,
       weak_ai_limit = 1
    },

=== modified file 'data/tribes/buildings/trainingsites/empire/arena/init.lua'
--- data/tribes/buildings/trainingsites/empire/arena/init.lua	2017-02-10 09:40:17 +0000
+++ data/tribes/buildings/trainingsites/empire/arena/init.lua	2017-06-16 05:22:22 +0000
@@ -37,7 +37,7 @@
    },
 
    aihints = {
-      trainingsite_type = "basic",
+      trainingsites_max_percent = 20,
       prohibited_till = 900,
       very_weak_ai_limit = 1,
       weak_ai_limit = 2,

=== modified file 'data/tribes/buildings/trainingsites/empire/colosseum/init.lua'
--- data/tribes/buildings/trainingsites/empire/colosseum/init.lua	2017-02-10 09:40:17 +0000
+++ data/tribes/buildings/trainingsites/empire/colosseum/init.lua	2017-06-16 05:22:22 +0000
@@ -34,7 +34,6 @@
 
    aihints = {
       prohibited_till = 1200,
-      trainingsite_type = "basic",
       forced_after = 1800,
       very_weak_ai_limit = 1,
       weak_ai_limit = 2

=== modified file 'data/tribes/buildings/trainingsites/empire/trainingcamp/init.lua'
--- data/tribes/buildings/trainingsites/empire/trainingcamp/init.lua	2017-02-10 09:40:17 +0000
+++ data/tribes/buildings/trainingsites/empire/trainingcamp/init.lua	2017-06-16 05:22:22 +0000
@@ -35,7 +35,6 @@
 
    aihints = {
       prohibited_till = 1500,
-      trainingsite_type = "advanced",
       very_weak_ai_limit = 0,
       weak_ai_limit = 1
    },

=== modified file 'src/ai/ai_help_structs.cc'
--- src/ai/ai_help_structs.cc	2017-01-25 18:55:59 +0000
+++ src/ai/ai_help_structs.cc	2017-06-16 05:22:22 +0000
@@ -20,6 +20,7 @@
 #include "ai/ai_help_structs.h"
 
 #include "base/macros.h"
+#include "base/time_string.h"
 #include "logic/map.h"
 #include "logic/player.h"
 
@@ -27,6 +28,8 @@
 constexpr int kRoadNotFound = -1000;
 constexpr int kShortcutWithinSameEconomy = 1000;
 constexpr int kRoadToDifferentEconomy = 10000;
+constexpr int kUpperDefaultMutationLimit = 200;
+constexpr int kLowerDefaultMutationLimit = 70;
 
 namespace Widelands {
 
@@ -102,15 +105,12 @@
 // When looking for unowned terrain to acquire, we are actually
 // only interested in fields we can walk on.
 // Fields should either be completely unowned or owned by an opposing player
-FindNodeUnowned::FindNodeUnowned(Player* p, Game& g, bool oe)
-   : player(p), game(g), only_enemies(oe) {
+FindEnemyNodeWalkable::FindEnemyNodeWalkable(Player* p, Game& g) : player(p), game(g) {
 }
 
-bool FindNodeUnowned::accept(const Map&, const FCoords& fc) const {
-	return (fc.field->nodecaps() & MOVECAPS_WALK) &&
-	       ((fc.field->get_owned_by() == 0) ||
-	        player->is_hostile(*game.get_player(fc.field->get_owned_by()))) &&
-	       (!only_enemies || (fc.field->get_owned_by() != 0));
+bool FindEnemyNodeWalkable::accept(const Map&, const FCoords& fc) const {
+	return ((fc.field->nodecaps() & MOVECAPS_WALK) && (fc.field->get_owned_by() > 0) &&
+	        player->is_hostile(*game.get_player(fc.field->get_owned_by())));
 }
 
 // Sometimes we need to know how many nodes our allies owns
@@ -127,11 +127,23 @@
 // When looking for unowned terrain to acquire, we must
 // pay speciall attention to fields where mines can be built.
 // Fields should be completely unowned
-FindNodeUnownedMineable::FindNodeUnownedMineable(Player* p, Game& g) : player(p), game(g) {
+FindNodeUnownedMineable::FindNodeUnownedMineable(Player* p, Game& g, int32_t t)
+   : player(p), game(g), ore_type(t) {
 }
 
 bool FindNodeUnownedMineable::accept(const Map&, const FCoords& fc) const {
-	return (fc.field->nodecaps() & BUILDCAPS_MINE) && (fc.field->get_owned_by() == 0);
+	if (ore_type == INVALID_INDEX) {
+		return (fc.field->nodecaps() & BUILDCAPS_MINE) && (fc.field->get_owned_by() == 0);
+	}
+	return (fc.field->nodecaps() & BUILDCAPS_MINE) && (fc.field->get_owned_by() == 0) &&
+	       fc.field->get_resources() == ore_type;
+}
+
+FindNodeUnownedBuildable::FindNodeUnownedBuildable(Player* p, Game& g) : player(p), game(g) {
+}
+
+bool FindNodeUnownedBuildable::accept(const Map&, const FCoords& fc) const {
+	return (fc.field->nodecaps() & BUILDCAPS_SIZEMASK) && (fc.field->get_owned_by() == 0);
 }
 
 // Unowned but walkable fields nearby
@@ -181,14 +193,35 @@
    : flag(&f), cost(c), distance(d) {
 }
 
+EventTimeQueue::EventTimeQueue() {
+}
+
+void EventTimeQueue::push(const uint32_t production_time) {
+	queue.push(production_time);
+}
+
+uint32_t EventTimeQueue::count(const uint32_t current_time) {
+	strip_old(current_time);
+	return queue.size();
+}
+
+void EventTimeQueue::strip_old(const uint32_t current_time) {
+	while (queue.size() > 0 && queue.front() < current_time - duration_) {
+		queue.pop();
+	}
+}
+
 BuildableField::BuildableField(const Widelands::FCoords& fc)
    : coords(fc),
      field_info_expiration(20000),
      preferred(false),
      enemy_nearby(0),
+     enemy_accessible_(false),
+     enemy_wh_nearby(false),
      unowned_land_nearby(0),
      near_border(false),
      unowned_mines_spots_nearby(0),
+     unowned_iron_mines_nearby(false),
      trees_nearby(0),
      // explanation of starting values
      // this is done to save some work for AI (CPU utilization)
@@ -209,15 +242,22 @@
      area_military_capacity(0),
      military_loneliness(1000),
      military_in_constr_nearby(0),
-     area_military_presence(0),
+     own_military_presence(0),
+     enemy_military_presence(0),
+     ally_military_presence(0),
      military_stationed(0),
      unconnected_nearby(false),
      military_unstationed(0),
-     is_portspace(false),
+     own_non_military_nearby(0),
+     is_portspace(Widelands::ExtendedBool::kUnset),
      port_nearby(false),
      portspace_nearby(Widelands::ExtendedBool::kUnset),
      max_buildcap_nearby(0),
-     last_resources_check_time(0) {
+     last_resources_check_time(0),
+     military_score_(0),
+     inland(false),
+     local_soldier_capacity(0),
+     is_militarysite(false) {
 }
 
 int32_t BuildableField::own_military_sites_nearby_() {
@@ -240,6 +280,19 @@
 	return cnt_built + cnt_under_construction;
 }
 
+bool BuildingObserver::is(BuildingAttribute attribute) const {
+	return is_what.count(attribute);
+}
+
+void BuildingObserver::set_is(const BuildingAttribute attribute) {
+	is_what.insert(attribute);
+}
+
+void BuildingObserver::unset_is(const BuildingAttribute attribute) {
+	is_what.erase(attribute);
+	assert(is_what.count(attribute) == 0);
+}
+
 Widelands::AiModeBuildings BuildingObserver::aimode_limit_status() {
 	if (total_count() > cnt_limit_by_aimode) {
 		return Widelands::AiModeBuildings::kLimitExceeded;
@@ -250,7 +303,7 @@
 	}
 }
 bool BuildingObserver::buildable(Widelands::Player& p) {
-	return is_buildable && p.is_building_type_allowed(id);
+	return is_what.count(BuildingAttribute::kBuildable) && p.is_building_type_allowed(id);
 }
 
 // Computer player does not get notification messages about enemy militarysites
@@ -264,16 +317,801 @@
      attack_soldiers_strength(0),
      defenders_strength(0),
      stationed_soldiers(0),
-     last_time_attackable(std::numeric_limits<uint32_t>::max()),
+     last_time_seen(0),
      last_tested(0),
      score(0),
      mines_nearby(Widelands::ExtendedBool::kUnset),
-     no_attack_counter(0) {
+     last_time_attacked(0),
+     attack_counter(0) {
 }
 
 // as all mines have 3 levels, AI does not know total count of mines per mined material
 // so this observer will be used for this
-MineTypesObserver::MineTypesObserver() : in_construction(0), finished(0) {
+MineTypesObserver::MineTypesObserver()
+   : in_construction(0), finished(0), is_critical(false), unoccupied(0) {
+}
+
+ExpansionType::ExpansionType() {
+	type = ExpansionMode::kResources;
+}
+
+void ExpansionType::set_expantion_type(const ExpansionMode etype) {
+	type = etype;
+}
+
+ManagementData::ManagementData() {
+	score = 1;
+	primary_parent = 255;
+	next_neuron_id = 0;
+	performance_change = 0;
+}
+
+// Initialization of neuron. Neuron is defined by curve (type) and weight (-100 - 100)
+// third argument is just id
+Neuron::Neuron(int8_t w, uint8_t f, uint16_t i) : weight(w), type(f), id(i) {
+	assert(type < neuron_curves.size());
+	assert(weight >= -100 && weight <= 100);
+	lowest_pos = std::numeric_limits<uint8_t>::max();
+	highest_pos = std::numeric_limits<uint8_t>::min();
+	recalculate();
+}
+
+// weigh, or rather value in range -100 - 100
+void Neuron::set_weight(int8_t w) {
+	if (w > 100) {
+		weight = 100;
+	} else if (w < -100) {
+		weight = -100;
+	} else {
+		weight = w;
+	}
+}
+
+// Neuron stores calculated values in array of size 21
+// array has to be recalculated when weight or curve type changes
+void Neuron::recalculate() {
+	assert(neuron_curves.size() > type);
+	for (int8_t i = 0; i <= 20; i += 1) {
+		results[i] = weight * neuron_curves[type][i] / 100;
+	}
+}
+
+// Following two functions returns Neuron value on position
+int8_t Neuron::get_result(const uint8_t pos) {
+	assert(pos <= 20);
+	return results[pos];
+}
+
+// get value corresponding to input in range 0-20, if you are out of range
+// the input will be cropped
+int8_t Neuron::get_result_safe(int32_t pos, const bool absolute) {
+	if (pos > highest_pos) {
+		highest_pos = pos;
+	};
+	if (pos < lowest_pos) {
+		lowest_pos = pos;
+	};
+	if (pos < 0) {
+		pos = 0;
+	}
+	if (pos > 20) {
+		pos = 20;
+	}
+	assert(pos <= 20);
+	assert(results[pos] >= -100 && results[pos] <= 100);
+	if (absolute) {
+		return std::abs(results[pos]);
+	}
+	return results[pos];
+}
+
+// Setting the type of curve
+void Neuron::set_type(uint8_t new_type) {
+	assert(new_type < neuron_curves.size());
+	type = new_type;
+}
+
+// FNeuron is basically single uint32_t integer, and AI can quary every bit of that uint32_t
+FNeuron::FNeuron(uint32_t c, uint16_t i) {
+	core = c;
+	id = i;
+}
+
+// Returning result depending on combinations of 5 bools
+bool FNeuron::get_result(
+   const bool bool1, const bool bool2, const bool bool3, const bool bool4, const bool bool5) {
+	return core.test(bool1 * 16 + bool2 * 8 + bool3 * 4 + bool4 * 2 + bool5);
+}
+
+// Returning bool on a position
+bool FNeuron::get_position(const uint8_t pos) {
+	assert(pos < f_neuron_bit_size);
+	return core.test(pos);
+}
+
+// Returning numerical value of FNeuron. Used for saving and priting into log
+uint32_t FNeuron::get_int() {
+	return core.to_ulong();
+}
+
+// This is basically mutation of FNeuron
+void FNeuron::flip_bit(const uint8_t pos) {
+	assert(pos < f_neuron_bit_size);
+	core.flip(pos);
+}
+
+// Shifting the value in range -100 to 100, if zero_align is true, it is now allowed to shift
+// from negative to positive and vice versa, 0 must be used.
+int8_t ManagementData::shift_weight_value(const int8_t old_value, const bool aggressive) {
+
+	int16_t halfVArRange = 50;
+	if (aggressive) {
+		halfVArRange = 200;
+	}
+
+	const int16_t upper_limit = std::min<int16_t>(old_value + halfVArRange, 100);
+	const int16_t bottom_limit = std::max<int16_t>(old_value - halfVArRange, -100);
+	int16_t new_value = bottom_limit + std::rand() % (upper_limit - bottom_limit + 1);
+
+	if (!aggressive && ((old_value > 0 && new_value < 0) || (old_value < 0 && new_value > 0))) {
+		new_value = 0;
+	}
+
+	if (new_value < -100) {
+		new_value = -100;
+	}
+	if (new_value > 100) {
+		new_value = 100;
+	}
+	return static_cast<int8_t>(new_value);
+}
+
+// Used to score performance of AI
+// Should be disabled for "production"
+void ManagementData::review(const uint32_t gametime,
+                            PlayerNumber pn,
+                            const uint32_t land,
+                            const uint32_t max_e_land,
+                            const uint32_t old_land,
+                            const uint16_t attackers,
+                            const int16_t trained_soldiers,
+                            const int16_t latest_attackers,
+                            const uint16_t conq_ws) {
+	const int16_t current_land_divider = 2;
+	const int16_t land_delta_multiplier = 1;
+	const int16_t bonus = 1000;
+	const int16_t attackers_multiplicator = 1;
+	const int16_t attack_bonus = 100;
+	const int16_t trained_soldiers_score = 250;
+	const int16_t conquered_wh_bonus = 500;
+
+	const int16_t main_bonus =
+	   ((static_cast<int32_t>(land - old_land) > 0 && land > max_e_land * 5 / 6 && attackers > 0 &&
+	     trained_soldiers > 0 && latest_attackers > 0) ?
+	       bonus :
+	       0);
+
+	const int16_t land_delta_bonus = static_cast<int16_t>(land - old_land) * land_delta_multiplier;
+
+	score = land / current_land_divider + land_delta_bonus + main_bonus +
+	        attackers * attackers_multiplicator + ((attackers > 0) ? attack_bonus : -attack_bonus) +
+	        trained_soldiers * trained_soldiers_score + +conquered_wh_bonus + conq_ws;
+
+	log(" %2d %s: reviewing AI mngm. data, sc: %5d Pr.p: %d (l: %4d / %4d / %4d, "
+	       "at:%4d(%3d),ts:%4d(%2d), ConqWH:%2d)\n",
+	       pn, gamestring_with_leading_zeros(gametime), score, primary_parent,
+	       land / current_land_divider, main_bonus, land_delta_bonus,
+	       attackers * attackers_multiplicator, latest_attackers,
+	       trained_soldiers * trained_soldiers_score, trained_soldiers, conq_ws);
+
+	if (score < -10000 || score > 30000) {
+		log(
+		   "%2d %s: reviewing AI mngm. data, score too extreme: %4d\n",
+		   pn, gamestring_with_leading_zeros(gametime), score);
+	}
+	assert(score > -10000 && score < 100000);
+}
+
+// Here we generate new AI DNA (no mutation yet) and push them into persistent data
+// this can cause inconsistency between local and persistent
+
+void ManagementData::new_dna_for_persistent( const uint8_t pn, const Widelands::AiType type){
+
+	ai_type = type;
+	
+	log (" %2d ... initialize starts\n", pn);
+
+   //AutoSCore_AIDNA_1
+    const std::vector<int16_t> AI_initial_military_numbers_A =
+      {  18 ,  -42 ,   21 ,   59 ,   -7 ,  -78 ,  -22 ,   81 ,   48 ,    0 ,  //AutoContent_01_AIDNA_1
+        -82 ,   45 ,  -96 ,   16 ,   70 ,  -30 ,   33 ,   79 ,  -78 ,  -42 ,  //AutoContent_02_AIDNA_1
+         79 ,  -16 ,   34 ,   46 ,  -22 ,    0 ,   45 ,  -29 ,   53 ,   51 ,  //AutoContent_03_AIDNA_1
+         24 ,   47 ,  -27 ,   80 ,  -86 ,   46 ,  -63 ,  -47 ,   20 ,  -63 ,  //AutoContent_04_AIDNA_1
+         78 ,   51 ,  -11 ,  -77 ,   20 ,   38 ,    6 ,   37 ,  -64 ,  -41 ,  //AutoContent_05_AIDNA_1
+         -3 ,  -55 ,   62 ,    0 ,   64 ,  -92 ,    4 ,  -89 ,   71 ,  -18 ,  //AutoContent_06_AIDNA_1
+        -87 ,   56 ,   17 ,  -34 ,  -69 ,   24 ,  -57 ,   84 ,   40 ,  -51 ,  //AutoContent_07_AIDNA_1
+          0 ,   44 ,    0 ,   -2 ,  -11 ,   -4 ,  -96 ,  -35 ,  -29 ,  -12 ,  //AutoContent_08_AIDNA_1
+         71 ,  -98 ,  -25 ,   50 ,   97 ,   74 ,    0 ,   65 ,  -60 ,   23 ,  //AutoContent_09_AIDNA_1
+         38 ,   53 ,   74 ,    0 ,  -43 ,   27 ,   32 ,   37 ,  -24 ,  -65 ,  //AutoContent_10_AIDNA_1
+         16 ,  -42 ,   19 ,  -94 ,  -28 ,   83 ,  -55 ,  -63 ,   16 ,  -41 ,  //AutoContent_11_AIDNA_1
+         28 ,   -3 ,    0 ,  -87 ,   32 ,    5 ,    4 ,    6 ,  -20 ,   62 ,  //AutoContent_12_AIDNA_1
+         85 ,    0 ,   58 ,   48 ,  -80 ,  -20 ,  -49 ,   71 ,   60 ,    8 ,  //AutoContent_13_AIDNA_1
+        -52 ,   59 ,  100 ,  -74 ,    0 ,  -36 ,   -9 ,   80 ,   41 ,  -67 ,  //AutoContent_14_AIDNA_1
+          0 ,   15 ,  -96 ,  -51 ,  -21 ,   11 ,  -27 ,  -30 ,   76 ,  -47  //AutoContent_15_AIDNA_1
+       }
+		;
+	
+	assert(magic_numbers_size == AI_initial_military_numbers_A.size());
+	
+	const std::vector<int8_t> input_weights_A =
+		//0		1		2		3	4		5		6		7		8	9
+      {   48 ,  -85 ,  -26 ,   47 ,  -93 ,  -22 ,  -91 ,   84 ,   24 ,  -12 ,  //AutoContent_16_AIDNA_1
+         98 ,  -59 ,  -89 ,   76 ,   81 ,   95 ,  -91 ,  -90 ,  -56 ,  -15 ,  //AutoContent_17_AIDNA_1
+        -65 ,  -18 ,    6 ,  -65 ,   41 ,   38 ,   47 ,  -31 ,   79 ,   23 ,  //AutoContent_18_AIDNA_1
+         16 ,   25 ,  -59 ,    0 ,  -38 ,  -85 ,  -60 ,  -42 ,    0 ,  -70 ,  //AutoContent_19_AIDNA_1
+        -78 ,  -86 ,  -87 ,  -55 ,   92 ,  -63 ,  -21 ,  -76 ,    4 ,   87 ,  //AutoContent_20_AIDNA_1
+         58 ,  -40 ,   71 ,  -90 ,  -72 ,    0 ,  -47 ,  -94 ,  -15 ,   66 ,  //AutoContent_21_AIDNA_1
+        -32 ,    9 ,   67 ,  -47 ,  -44 ,  -76 ,  -53 ,   57 ,  -31 ,  -47 ,  //AutoContent_22_AIDNA_1
+          0 ,  -28 ,  -16 ,   48 ,   41 ,  -45 ,   36 ,   -8 ,   51 ,  -49  //AutoContent_23_AIDNA_1
+	}
+			;
+	const std::vector<int8_t> input_func_A =
+      {    1 ,    0 ,    1 ,    2 ,    2 ,    1 ,    0 ,    0 ,    0 ,    0 ,  //AutoContent_24_AIDNA_1
+          2 ,    1 ,    2 ,    2 ,    2 ,    1 ,    2 ,    2 ,    2 ,    1 ,  //AutoContent_25_AIDNA_1
+          2 ,    2 ,    0 ,    2 ,    2 ,    1 ,    0 ,    1 ,    0 ,    2 ,  //AutoContent_26_AIDNA_1
+          1 ,    2 ,    2 ,    1 ,    2 ,    1 ,    0 ,    1 ,    2 ,    1 ,  //AutoContent_27_AIDNA_1
+          2 ,    1 ,    2 ,    0 ,    0 ,    1 ,    2 ,    0 ,    0 ,    0 ,  //AutoContent_28_AIDNA_1
+          1 ,    0 ,    0 ,    1 ,    0 ,    1 ,    0 ,    0 ,    1 ,    1 ,  //AutoContent_29_AIDNA_1
+          2 ,    0 ,    1 ,    2 ,    0 ,    2 ,    2 ,    2 ,    1 ,    0 ,  //AutoContent_30_AIDNA_1
+          2 ,    1 ,    2 ,    2 ,    1 ,    1 ,    0 ,    2 ,    2 ,    1  //AutoContent_31_AIDNA_1
+	}
+		;
+	assert(neuron_pool_size == input_func_A.size());
+	assert(neuron_pool_size == input_weights_A.size());
+
+	const std::vector<uint32_t> f_neurons_A =
+      {  471130763 ,  1322756799 ,  1148682382 ,  2713953719 ,  194460207 ,  18113635 ,  2947490886 ,  3275944172 ,  3438582086 ,  856208494 ,  //AutoContent_32_AIDNA_1
+        1326156684 ,  986431571 ,  3465514749 ,  1962749574 ,  1523585333 ,  1376482111 ,  1223335901 ,  2962231598 ,  657710612 ,  578259960 ,  //AutoContent_33_AIDNA_1
+        1271222963 ,  2915927856 ,  3396846486 ,  1743568169 ,  2679432920 ,  410834609 ,  134904175 ,  2968201710 ,  2132567223 ,  2248461478 ,  //AutoContent_34_AIDNA_1
+        161963959 ,  3295327519 ,  670058472 ,  2013696856 ,  3608400883 ,  496651103 ,  733137541 ,  2952748738 ,  3307293853 ,  3886490843 ,  //AutoContent_35_AIDNA_1
+        3233788172 ,  715230539 ,  2583635732 ,  4271028953 ,  1217674278 ,  4043323645 ,  1857109651 ,  2181897047 ,  2825979187 ,  3081298269 ,  //AutoContent_36_AIDNA_1
+        1277901018 ,  1255642150 ,  2384261818 ,  2866704864 ,  755617465 ,  835768208 ,  1394358417 ,  4012239945 ,  2601238115 ,  3933467106  //AutoContent_37_AIDNA_1
+	 };
+	assert(f_neuron_pool_size == f_neurons_A.size());
+
+		
+    //AutoSCore_AIDNA_2
+	const std::vector<int16_t> AI_initial_military_numbers_B =
+      {  18 ,  -42 ,   21 ,   59 ,   -7 ,  -78 ,  -22 ,   81 ,   48 ,    0 ,  //AutoContent_01_AIDNA_2
+        -55 ,   45 ,  -96 ,   16 ,   70 ,  -30 ,   33 ,   79 ,  -78 ,  -42 ,  //AutoContent_02_AIDNA_2
+         79 ,  -16 ,   34 ,   46 ,  -22 ,    0 ,   45 ,  -33 ,   53 ,   51 ,  //AutoContent_03_AIDNA_2
+         49 ,   47 ,  -27 ,   80 ,  -86 ,   46 ,  -63 ,  -47 ,   20 ,  -63 ,  //AutoContent_04_AIDNA_2
+         78 ,   51 ,   23 ,  -77 ,   20 ,   38 ,    6 ,   37 ,  -64 ,  -41 ,  //AutoContent_05_AIDNA_2
+         -3 ,  -55 ,   62 ,    0 ,   64 ,  -92 ,    4 ,  -89 ,   71 ,  -18 ,  //AutoContent_06_AIDNA_2
+        -87 ,   56 ,   17 ,  -34 ,  -69 ,   24 ,  -57 ,   84 ,   40 ,  -51 ,  //AutoContent_07_AIDNA_2
+          0 ,   44 ,    0 ,   -2 ,  -11 ,   -4 ,  -96 ,  -35 ,  -29 ,  -12 ,  //AutoContent_08_AIDNA_2
+         71 ,  -98 ,  -25 ,   50 ,   97 ,   74 ,    0 ,   65 ,  -60 ,   23 ,  //AutoContent_09_AIDNA_2
+         38 ,   53 ,   74 ,    0 ,  -15 ,   27 ,   32 ,   37 ,  -24 ,  -65 ,  //AutoContent_10_AIDNA_2
+         16 ,  -42 ,   19 ,  -94 ,  -28 ,   83 ,  -55 ,  -63 ,   16 ,  -41 ,  //AutoContent_11_AIDNA_2
+         28 ,   -3 ,    0 ,  -87 ,   32 ,    5 ,    4 ,    6 ,  -20 ,   62 ,  //AutoContent_12_AIDNA_2
+         85 ,    0 ,   58 ,   48 ,  -80 ,  -20 ,  -49 ,   71 ,   60 ,    8 ,  //AutoContent_13_AIDNA_2
+        -52 ,   59 ,  100 ,  -74 ,    0 ,  -36 ,   -9 ,   80 ,   41 ,  -67 ,  //AutoContent_14_AIDNA_2
+          0 ,   15 ,  -96 ,  -51 ,  -21 ,   11 ,  -27 ,  -30 ,   76 ,  -47  //AutoContent_15_AIDNA_2
+		}
+		;
+	assert(magic_numbers_size == AI_initial_military_numbers_B.size());
+		
+	const std::vector<int8_t> input_weights_B =
+      {   48 ,  -85 ,  -26 ,   47 ,  -93 ,  -22 ,  -91 ,   84 ,   50 ,  -12 ,  //AutoContent_16_AIDNA_2
+         98 ,  -59 ,  -89 ,   76 ,   81 ,   95 ,  -91 ,  -90 ,  -56 ,  -15 ,  //AutoContent_17_AIDNA_2
+        -65 ,  -18 ,    6 ,  -65 ,   41 ,   38 ,   47 ,  -31 ,   79 ,   23 ,  //AutoContent_18_AIDNA_2
+         16 ,   25 ,  -59 ,    0 ,  -38 ,  -85 ,  -60 ,  -42 ,    0 ,  -70 ,  //AutoContent_19_AIDNA_2
+        -78 ,  -86 ,  -87 ,  -55 ,   92 ,  -63 ,  -21 ,  -76 ,    4 ,   87 ,  //AutoContent_20_AIDNA_2
+         58 ,  -40 ,   71 ,  -90 ,  -72 ,    0 ,  -47 ,  -94 ,  -15 ,   66 ,  //AutoContent_21_AIDNA_2
+        -32 ,    9 ,   67 ,  -47 ,  -44 ,  -76 ,  -53 ,   57 ,  -31 ,  -47 ,  //AutoContent_22_AIDNA_2
+          0 ,  -28 ,  -16 ,   48 ,   41 ,  -45 ,   36 ,   -8 ,   51 ,  -49  //AutoContent_23_AIDNA_2
+}
+	      ;
+	
+	const std::vector<int8_t> input_func_B = 
+      {    1 ,    0 ,    1 ,    2 ,    2 ,    1 ,    0 ,    0 ,    0 ,    0 ,  //AutoContent_24_AIDNA_2
+          2 ,    1 ,    2 ,    2 ,    2 ,    1 ,    2 ,    2 ,    2 ,    1 ,  //AutoContent_25_AIDNA_2
+          2 ,    2 ,    0 ,    2 ,    2 ,    1 ,    0 ,    1 ,    0 ,    2 ,  //AutoContent_26_AIDNA_2
+          1 ,    2 ,    2 ,    1 ,    2 ,    1 ,    0 ,    1 ,    2 ,    1 ,  //AutoContent_27_AIDNA_2
+          2 ,    1 ,    2 ,    0 ,    0 ,    1 ,    2 ,    0 ,    0 ,    0 ,  //AutoContent_28_AIDNA_2
+          1 ,    0 ,    0 ,    1 ,    0 ,    1 ,    0 ,    0 ,    1 ,    1 ,  //AutoContent_29_AIDNA_2
+          2 ,    0 ,    1 ,    2 ,    0 ,    2 ,    2 ,    2 ,    1 ,    0 ,  //AutoContent_30_AIDNA_2
+          2 ,    1 ,    2 ,    2 ,    1 ,    1 ,    0 ,    2 ,    2 ,    1  //AutoContent_31_AIDNA_2
+}
+		;
+		assert(neuron_pool_size == input_func_B.size());
+		assert(neuron_pool_size == input_weights_B.size());
+
+      
+	const std::vector<uint32_t> f_neurons_B =
+      {  471130763 ,  1322756799 ,  1148682382 ,  2713953719 ,  194460207 ,  18113635 ,  2947490886 ,  3275944172 ,  3438582022 ,  855676014 ,  //AutoContent_32_AIDNA_2
+        1326156684 ,  986431571 ,  3465514749 ,  1962749574 ,  1523650869 ,  1376482111 ,  2347409353 ,  2962227502 ,  657710612 ,  578259960 ,  //AutoContent_33_AIDNA_2
+        1271222963 ,  2915927856 ,  3396846486 ,  1743568169 ,  2679432920 ,  410834609 ,  134904175 ,  2968201710 ,  2132567223 ,  2248461478 ,  //AutoContent_34_AIDNA_2
+        161963959 ,  3295327519 ,  670058472 ,  2013696856 ,  4145271795 ,  496651103 ,  733211269 ,  2952748738 ,  1159810205 ,  3886490843 ,  //AutoContent_35_AIDNA_2
+        3225153820 ,  732007755 ,  2583635732 ,  4271028825 ,  1217674278 ,  4043323645 ,  1857109651 ,  2249005911 ,  2825979187 ,  3081298269 ,  //AutoContent_36_AIDNA_2
+        1277901018 ,  1255642150 ,  2384261818 ,  2866704864 ,  755617465 ,  835768208 ,  1394096273 ,  4012239945 ,  2609626723 ,  3932418530  //AutoContent_37_AIDNA_2
+	 };
+	assert(f_neuron_pool_size == f_neurons_B.size());
+
+
+    //AutoSCore_AIDNA_3
+	const std::vector<int16_t> AI_initial_military_numbers_C =
+      {  18 ,  -42 ,   63 ,   82 ,   -7 ,  -78 ,  -22 ,   81 ,   48 ,    0 ,  //AutoContent_01_AIDNA_3
+        -55 ,   55 ,  -96 ,   16 ,   70 ,  -30 ,   33 ,   79 ,  -78 ,  -42 ,  //AutoContent_02_AIDNA_3
+         79 ,  -16 ,   34 ,   46 ,    0 ,    0 ,    0 ,  -29 ,   53 ,   51 ,  //AutoContent_03_AIDNA_3
+         24 ,   47 ,  -27 ,   42 ,  -86 ,   46 ,  -63 ,  -47 ,   20 ,  -63 ,  //AutoContent_04_AIDNA_3
+         78 ,   31 ,   98 ,  -73 ,   20 ,   38 ,   44 ,   37 ,  -64 ,  -41 ,  //AutoContent_05_AIDNA_3
+         -3 ,  -55 ,   67 ,    0 ,   64 ,  -92 ,    4 ,  -89 ,   71 ,  -60 ,  //AutoContent_06_AIDNA_3
+        -87 ,   56 ,   17 ,  -34 ,  -69 ,   24 ,  -57 ,   50 ,   40 ,  -51 ,  //AutoContent_07_AIDNA_3
+        -34 ,   44 ,    0 ,   -2 ,  -11 ,   -4 ,  -96 ,  -35 ,  -29 ,  -12 ,  //AutoContent_08_AIDNA_3
+         68 ,  -98 ,  -25 ,   50 ,   97 ,   74 ,    0 ,   65 ,  -60 ,   23 ,  //AutoContent_09_AIDNA_3
+         38 ,   53 ,   74 ,    0 ,  -15 ,   27 ,   32 ,   61 ,  -24 ,  -65 ,  //AutoContent_10_AIDNA_3
+         16 ,  -42 ,   19 ,  -94 ,  -65 ,   83 ,  -55 ,  -63 ,   16 ,  -41 ,  //AutoContent_11_AIDNA_3
+         74 ,   -3 ,    0 ,  -87 ,   32 ,    5 ,    4 ,    6 ,  -70 ,   62 ,  //AutoContent_12_AIDNA_3
+         85 ,    0 ,   58 ,   48 ,  -80 ,  -20 ,  -49 ,   71 ,   60 ,    8 ,  //AutoContent_13_AIDNA_3
+        -52 ,   30 ,   69 ,  -74 ,    0 ,  -36 ,  -57 ,   80 ,   64 ,  -67 ,  //AutoContent_14_AIDNA_3
+          0 ,   15 ,  -96 ,  -51 ,  -21 ,    0 ,  -33 ,  -30 ,   76 ,  -47  //AutoContent_15_AIDNA_3
+       }
+
+		;
+	
+		assert(magic_numbers_size == AI_initial_military_numbers_C.size());
+	
+	const std::vector<int8_t> input_weights_C =
+      {   48 ,  -85 ,  -26 ,   47 ,  -65 ,  -19 ,  -91 ,   84 ,   92 ,  -12 ,  //AutoContent_16_AIDNA_3
+         78 ,  -59 ,  -89 ,   76 ,   81 ,   95 ,  -91 ,  -90 ,  -56 ,  -15 ,  //AutoContent_17_AIDNA_3
+        -65 ,  -18 ,    6 ,  -65 ,   17 ,   38 ,   47 ,  -45 ,   79 ,   23 ,  //AutoContent_18_AIDNA_3
+         16 ,   25 ,  -59 ,   36 ,  -38 ,  -85 ,  -60 ,  -42 ,    0 ,  -70 ,  //AutoContent_19_AIDNA_3
+        -78 ,  -86 ,  -87 ,  -55 ,   92 ,  -63 ,  -21 ,  -76 ,    4 ,   87 ,  //AutoContent_20_AIDNA_3
+         58 ,  -40 ,   71 ,  -90 ,  -72 ,    0 ,  -47 ,  -94 ,  -15 ,   95 ,  //AutoContent_21_AIDNA_3
+        -32 ,    9 ,   67 ,  -47 ,  -44 ,  -76 ,  -53 ,   57 ,  -31 ,  -47 ,  //AutoContent_22_AIDNA_3
+          0 ,  -28 ,  -16 ,   48 ,   41 ,  -45 ,   36 ,   -8 ,   63 ,  -49  //AutoContent_23_AIDNA_3
+       }
+			;
+	const std::vector<int8_t> input_func_C =
+      {    1 ,    0 ,    1 ,    2 ,    2 ,    1 ,    0 ,    0 ,    0 ,    0 ,  //AutoContent_24_AIDNA_3
+          2 ,    1 ,    2 ,    2 ,    1 ,    1 ,    2 ,    2 ,    2 ,    1 ,  //AutoContent_25_AIDNA_3
+          2 ,    2 ,    0 ,    2 ,    2 ,    1 ,    0 ,    1 ,    0 ,    2 ,  //AutoContent_26_AIDNA_3
+          1 ,    2 ,    2 ,    1 ,    2 ,    1 ,    0 ,    1 ,    2 ,    1 ,  //AutoContent_27_AIDNA_3
+          2 ,    1 ,    2 ,    0 ,    0 ,    1 ,    2 ,    0 ,    0 ,    0 ,  //AutoContent_28_AIDNA_3
+          1 ,    0 ,    0 ,    1 ,    0 ,    2 ,    0 ,    0 ,    1 ,    1 ,  //AutoContent_29_AIDNA_3
+          2 ,    0 ,    1 ,    2 ,    0 ,    2 ,    2 ,    2 ,    1 ,    0 ,  //AutoContent_30_AIDNA_3
+          2 ,    1 ,    2 ,    2 ,    1 ,    1 ,    0 ,    2 ,    2 ,    1  //AutoContent_31_AIDNA_3
+       }
+			;
+	assert(neuron_pool_size == input_func_C.size());
+	assert(neuron_pool_size == input_weights_C.size());
+	
+	const std::vector<uint32_t> f_neurons_C =
+      {  202828425 ,  1322625717 ,  1148682382 ,  2244191679 ,  194456367 ,  26493027 ,  2947491014 ,  3410163948 ,  3438582086 ,  856208494 ,  //AutoContent_32_AIDNA_3
+        1259048860 ,  986431571 ,  3457662708 ,  1954360966 ,  1523650869 ,  1376351039 ,  1223335901 ,  2997887274 ,  657657361 ,  645368312 ,  //AutoContent_33_AIDNA_3
+        1271218867 ,  2915927856 ,  3933717470 ,  2011479337 ,  2645864080 ,  408737457 ,  2282387823 ,  2968205806 ,  1597793527 ,  2248461494 ,  //AutoContent_34_AIDNA_3
+        161963959 ,  1147843615 ,  670058474 ,  2013712728 ,  4145271795 ,  496651102 ,  724812677 ,  2952748738 ,  1629834973 ,  3819381979 ,  //AutoContent_35_AIDNA_3
+        3233788172 ,  732007755 ,  2583635734 ,  4271028825 ,  1217670182 ,  4043323645 ,  1857109659 ,  2249005911 ,  2834367795 ,  3072909661 ,  //AutoContent_36_AIDNA_3
+        1278949528 ,  1792513063 ,  2384261690 ,  2732487136 ,  755615385 ,  835767184 ,  1393834641 ,  4012239945 ,  2601238115 ,  2859725290  //AutoContent_37_AIDNA_3
+	 };
+	assert(f_neuron_pool_size == f_neurons_C.size());
+
+		
+    //AutoSCore_AIDNA_4
+	const std::vector<int16_t> AI_initial_military_numbers_D =
+      {  18 ,  -42 ,   21 ,   59 ,   -7 ,  -78 ,  -22 ,   81 ,   48 ,    0 ,  //AutoContent_01_AIDNA_4
+        -55 ,   45 ,  -96 ,   16 ,   70 ,  -30 ,   33 ,   79 ,  -78 ,  -42 ,  //AutoContent_02_AIDNA_4
+         79 ,  -16 ,   34 ,   46 ,  -22 ,    0 ,   45 ,  -29 ,   53 ,   51 ,  //AutoContent_03_AIDNA_4
+         24 ,   47 ,  -27 ,   80 ,  -86 ,   46 ,  -63 ,  -47 ,   20 ,  -63 ,  //AutoContent_04_AIDNA_4
+         78 ,   51 ,  -25 ,  -77 ,   20 ,   38 ,    6 ,   37 ,  -64 ,  -41 ,  //AutoContent_05_AIDNA_4
+         -3 ,  -55 ,   62 ,    0 ,   64 ,  -92 ,    4 ,  -89 ,   71 ,  -18 ,  //AutoContent_06_AIDNA_4
+        -87 ,   56 ,   17 ,  -34 ,  -69 ,   24 ,  -57 ,   84 ,   40 ,  -51 ,  //AutoContent_07_AIDNA_4
+          0 ,   44 ,    0 ,   -2 ,  -11 ,   -4 ,  -96 ,  -35 ,  -29 ,  -12 ,  //AutoContent_08_AIDNA_4
+         71 ,  -98 ,  -25 ,   50 ,   97 ,   74 ,    0 ,   65 ,  -60 ,   23 ,  //AutoContent_09_AIDNA_4
+         38 ,   53 ,   74 ,    0 ,  -15 ,   27 ,   32 ,   37 ,  -24 ,  -65 ,  //AutoContent_10_AIDNA_4
+         16 ,  -61 ,   19 ,  -94 ,  -28 ,   83 ,  -55 ,  -63 ,   16 ,  -41 ,  //AutoContent_11_AIDNA_4
+         28 ,   -3 ,    0 ,  -87 ,   32 ,    5 ,    4 ,    6 ,  -20 ,   62 ,  //AutoContent_12_AIDNA_4
+         85 ,    0 ,   58 ,   48 ,  -80 ,  -20 ,  -49 ,   71 ,   60 ,    8 ,  //AutoContent_13_AIDNA_4
+        -52 ,   59 ,  100 ,  -74 ,    0 ,  -36 ,   -9 ,   80 ,   41 ,  -67 ,  //AutoContent_14_AIDNA_4
+          0 ,   15 ,  -96 ,  -51 ,  -21 ,   11 ,  -27 ,  -30 ,   76 ,  -47  //AutoContent_15_AIDNA_4
+	}
+		;
+	assert(magic_numbers_size == AI_initial_military_numbers_D.size());
+		
+	const std::vector<int8_t> input_weights_D =
+      {   48 ,  -85 ,  -26 ,   47 ,  -93 ,  -22 ,  -91 ,   84 ,   50 ,  -12 ,  //AutoContent_16_AIDNA_4
+         98 ,  -59 ,  -89 ,   76 ,   81 ,   95 ,  -91 ,  -90 ,  -56 ,  -15 ,  //AutoContent_17_AIDNA_4
+        -65 ,  -18 ,    6 ,  -65 ,   41 ,   38 ,   47 ,  -31 ,   79 ,   23 ,  //AutoContent_18_AIDNA_4
+         16 ,   25 ,  -59 ,    0 ,  -38 ,  -85 ,  -60 ,  -42 ,    0 ,  -70 ,  //AutoContent_19_AIDNA_4
+        -78 ,  -86 ,  -87 ,  -55 ,   92 ,  -63 ,  -21 ,  -76 ,    4 ,   87 ,  //AutoContent_20_AIDNA_4
+         58 ,  -40 ,   71 ,  -90 ,  -72 ,    0 ,  -47 ,  -94 ,  -15 ,   66 ,  //AutoContent_21_AIDNA_4
+        -32 ,    9 ,   67 ,  -47 ,  -44 ,  -76 ,  -53 ,   57 ,  -31 ,  -47 ,  //AutoContent_22_AIDNA_4
+          0 ,  -28 ,  -16 ,   48 ,   41 ,  -45 ,   36 ,   -8 ,   63 ,  -49  //AutoContent_23_AIDNA_4
+	}
+	      ;
+	
+	const std::vector<int8_t> input_func_D = 
+      {    1 ,    0 ,    1 ,    2 ,    2 ,    1 ,    0 ,    0 ,    0 ,    0 ,  //AutoContent_24_AIDNA_4
+          2 ,    1 ,    2 ,    2 ,    2 ,    1 ,    2 ,    2 ,    2 ,    1 ,  //AutoContent_25_AIDNA_4
+          2 ,    2 ,    0 ,    2 ,    2 ,    1 ,    0 ,    1 ,    0 ,    2 ,  //AutoContent_26_AIDNA_4
+          1 ,    2 ,    2 ,    1 ,    2 ,    1 ,    0 ,    1 ,    2 ,    1 ,  //AutoContent_27_AIDNA_4
+          2 ,    1 ,    2 ,    0 ,    0 ,    1 ,    2 ,    0 ,    0 ,    0 ,  //AutoContent_28_AIDNA_4
+          1 ,    0 ,    0 ,    1 ,    0 ,    1 ,    0 ,    0 ,    1 ,    1 ,  //AutoContent_29_AIDNA_4
+          2 ,    0 ,    1 ,    2 ,    0 ,    2 ,    2 ,    2 ,    1 ,    0 ,  //AutoContent_30_AIDNA_4
+          2 ,    1 ,    2 ,    2 ,    1 ,    1 ,    0 ,    2 ,    2 ,    1  //AutoContent_31_AIDNA_4
+	}
+		;
+	assert(neuron_pool_size == input_func_D.size());
+	assert(neuron_pool_size == input_weights_D.size());
+
+	const std::vector<uint32_t> f_neurons_D =
+      {  471130763 ,  1322756799 ,  1148682382 ,  2713953719 ,  194460175 ,  18113635 ,  2947490886 ,  3275944172 ,  3438582214 ,  856208494 ,  //AutoContent_32_AIDNA_4
+        1326156684 ,  449560659 ,  3465512701 ,  1962749574 ,  1523650869 ,  1376482111 ,  2347409353 ,  2962227502 ,  657710614 ,  578259960 ,  //AutoContent_33_AIDNA_4
+        1271222963 ,  2915927856 ,  3396846486 ,  1743568169 ,  2679432920 ,  410834611 ,  134904175 ,  2968201710 ,  2132567223 ,  2248461478 ,  //AutoContent_34_AIDNA_4
+        161963959 ,  3295327519 ,  670058472 ,  2013696856 ,  3608400883 ,  496651103 ,  733137541 ,  2952748738 ,  1159810205 ,  3886490843 ,  //AutoContent_35_AIDNA_4
+        3225153820 ,  732007755 ,  2583635732 ,  4271028825 ,  1217674278 ,  4043323645 ,  1857109651 ,  2248481623 ,  3899721011 ,  3081298269 ,  //AutoContent_36_AIDNA_4
+        1277901016 ,  1255642150 ,  2384261818 ,  2866704864 ,  755617465 ,  835768208 ,  1394358417 ,  4012239945 ,  2601238115 ,  3933467106  //AutoContent_37_AIDNA_4
+	 };
+	assert(f_neuron_pool_size == f_neurons_D.size());
+	
+	primary_parent = std::rand() % 4;
+	const uint8_t parent2 = std::rand() % 4;
+	
+	log (" ... DNA initialization (primary parent: %d, secondary parent: %d)\n", primary_parent, parent2);
+	
+	// First setting military numbers, they goes dirrectly to persistent data
+	for (uint16_t i = 0; i < magic_numbers_size; i += 1){
+		// Child inherites DNA with probability 5:1 from main parent
+		uint8_t dna_donor = (std::rand() % 50 > 0) ? primary_parent : parent2;
+		if (i == MutationRatePosition) { //overwritting
+			dna_donor = primary_parent;
+		}
+		
+		switch ( dna_donor ) {
+			case 0 : 
+				set_military_number_at(i,AI_initial_military_numbers_A[i]);
+				break;
+			case 1 : 
+				set_military_number_at(i,AI_initial_military_numbers_B[i]);
+				break;
+			case 2 : 
+				set_military_number_at(i,AI_initial_military_numbers_C[i]);
+				break;
+			case 3 : 
+				set_military_number_at(i,AI_initial_military_numbers_D[i]);
+				break;
+			default:
+				log ("parent %d?\n", dna_donor);
+				NEVER_HERE();
+			}
+		}
+
+	pd->neuron_weights.clear();
+	pd->neuron_functs.clear();
+	pd->f_neurons.clear();	
+
+	for (uint16_t i = 0; i <neuron_pool_size; i += 1){
+		const uint8_t dna_donor = (std::rand() % 20 > 0) ? primary_parent : parent2;
+		
+		switch ( dna_donor ) {
+			case 0 : 
+				pd->neuron_weights.push_back(input_weights_A[i]);
+				pd->neuron_functs.push_back(input_func_A[i]);
+				break;
+			case 1 : 
+				pd->neuron_weights.push_back(input_weights_B[i]);
+				pd->neuron_functs.push_back(input_func_B[i]);
+				break;
+			case 2 : 
+				pd->neuron_weights.push_back(input_weights_C[i]);
+				pd->neuron_functs.push_back(input_func_C[i]);
+				break;
+			case 3 : 
+				pd->neuron_weights.push_back(input_weights_D[i]);
+				pd->neuron_functs.push_back(input_func_D[i]);
+				break;
+			default:
+				log ("parent %d?\n", dna_donor);
+				NEVER_HERE();
+		}
+	}
+
+
+	for (uint16_t i = 0; i <f_neuron_pool_size; i += 1){
+		const uint8_t dna_donor = (std::rand() % 20 > 0) ? primary_parent : parent2;
+		switch ( dna_donor ) {
+			case 0 : 
+				pd->f_neurons.push_back(f_neurons_A[i]);
+				//f_neuron_pool.push_back(FNeuron(f_neurons_A[i], i));				
+				break;
+			case 1 : 
+				pd->f_neurons.push_back(f_neurons_B[i]);
+				//f_neuron_pool.push_back(FNeuron(f_neurons_B[i], i));	
+				break;
+			case 2 : 
+				pd->f_neurons.push_back(f_neurons_C[i]);
+				//f_neuron_pool.push_back(FNeuron(f_neurons_C[i], i));					
+				break;
+			case 3 : 
+				pd->f_neurons.push_back(f_neurons_D[i]);
+				//f_neuron_pool.push_back(FNeuron(f_neurons_D[i], i));	
+				break;
+			default:
+				log ("parent %d?\n", dna_donor);
+				NEVER_HERE();
+		}
+	}
+	
+	pd->magic_numbers_size = magic_numbers_size;
+	pd->neuron_pool_size = neuron_pool_size;
+	pd->f_neuron_pool_size = f_neuron_pool_size;
+
+	//test_consistency(); might not be consistent yet
+	
+}
+
+// Mutating, but all done on persistent data
+void ManagementData::mutate(const uint8_t pn) {
+
+	const int8_t old_probability = get_military_number_at(MutationRatePosition);
+
+	int16_t probability =
+	   shift_weight_value(get_military_number_at(MutationRatePosition), false) + 101;
+	if (probability > kUpperDefaultMutationLimit) {
+		probability = kUpperDefaultMutationLimit;
+	}
+	if (probability < kLowerDefaultMutationLimit) {
+		probability = kLowerDefaultMutationLimit;
+	}
+
+	set_military_number_at(MutationRatePosition, probability - 101);
+
+	// decreasing probability (or rather increasing probability of mutation) if weaker player
+	if (ai_type == Widelands::AiType::kWeak) {
+		probability /= 2;
+		log("%2d: Weak mode, increasing mutation probability to 1 / %d\n", pn, probability);
+	} else if (ai_type == Widelands::AiType::kVeryWeak) {
+		probability /= 4;
+		log("%2d: Very weak mode, increasing mutation probability to 1 / %d\n", pn, probability);
+	}
+
+	assert(probability > 0 && probability <= 201);
+
+	log(" %2d ... mutating, final prob.: 1 / %3d (old: 1 / %3d)\n", pn, probability,
+	       old_probability + 101);
+
+	if (probability < 201) {
+
+		// Modifying pool of Military numbers
+		{
+			// Preferred numbers are ones that will be mutated agressively in full range -100 - +100
+			std::set<int32_t> preferred_numbers = {};
+
+			for (uint16_t i = 0; i < magic_numbers_size; i += 1) {
+				if (i == MutationRatePosition) {  // mutated above
+					continue;
+				}
+				bool mutating = false;
+				bool aggressive = false;
+				if (preferred_numbers.count(i) > 0) {
+					mutating = true;
+					aggressive = true;
+				} else if (std::rand() % probability == 0) {
+					mutating = true;
+				}
+				if (mutating) {
+					const int16_t old_value = get_military_number_at(i);
+					const int16_t new_value = shift_weight_value(get_military_number_at(i), aggressive);
+					set_military_number_at(i, new_value);
+					log("      Magic number %3d: value changed: %4d -> %4d  %s\n", i, old_value,
+					       new_value, (aggressive) ? "aggressive" : "");
+				}
+			}
+		}
+
+		// Modifying pool of neurons
+		{
+			//Neurons to be mutated more agressively
+			std::set<int32_t> preferred_neurons = {};
+			for (auto& item : neuron_pool) {
+
+				bool mutating = false;
+				bool aggressive = false;
+				if (preferred_neurons.count(item.get_id()) > 0) {
+					mutating = true;
+					aggressive = true;
+				} else if (std::rand() % probability == 0) {
+					mutating = true;
+				}
+				if (mutating) {
+					const int16_t old_value = item.get_weight();
+					if (std::rand() % 4 == 0) {
+						assert(neuron_curves.size() > 0);
+						item.set_type(std::rand() % neuron_curves.size());
+						pd->neuron_functs[item.get_id()] = item.get_type();
+					} else {
+						int16_t new_value = shift_weight_value(item.get_weight(), aggressive);
+						item.set_weight(new_value);
+						pd->neuron_weights[item.get_id()] = item.get_weight();
+					}
+					log("      Neuron %2d: weight: %4d -> %4d, new curve: %d   %s\n", item.get_id(),
+					       old_value, item.get_weight(), item.get_type(),
+					       (aggressive) ? "aggressive" : "");
+
+					item.recalculate();
+				}
+			}
+		}
+
+		// Modifying pool of f-neurons
+		{
+			//FNeurons to be mutated more agressively
+			std::set<int32_t> preferred_f_neurons = {};
+			for (auto& item : f_neuron_pool) {
+				uint8_t changed_bits = 0;
+				// is this preferred neuron
+				if (preferred_f_neurons.count(item.get_id()) > 0) {
+					for (uint8_t i = 0; i < f_neuron_bit_size; i += 1) {
+						if (std::rand() % 5 == 0) {
+							item.flip_bit(i);
+							changed_bits += 1;
+						}
+					}
+				} else {  // normal mutation
+					for (uint8_t i = 0; i < f_neuron_bit_size; i += 1) {
+						if (std::rand() % (probability * 3) == 0) {
+							item.flip_bit(i);
+							changed_bits += 1;
+						}
+					}
+				}
+
+				if (changed_bits) {
+					pd->f_neurons[item.get_id()] = item.get_int();
+					log("      F-Neuron %2d: new value: %13ul, changed bits: %2d   %s\n",
+					       item.get_id(), item.get_int(), changed_bits,
+					       (preferred_f_neurons.count(item.get_id()) > 0) ? "aggressive" : "");
+				}
+			}
+		}
+	}
+
+	test_consistency();
+}
+
+// Now we copy persistent to loacl
+void ManagementData::copy_persistent_to_local(const uint8_t pn) {
+
+	assert(pd->neuron_weights.size() == neuron_pool_size);
+	assert(pd->neuron_functs.size() == neuron_pool_size);
+	neuron_pool.clear();
+	for (uint32_t i = 0; i < neuron_pool_size; i = i + 1) {
+		neuron_pool.push_back(Neuron(pd->neuron_weights[i], pd->neuron_functs[i], i));
+	}
+
+	assert(pd->f_neurons.size() == f_neuron_pool_size);
+	f_neuron_pool.clear();
+	for (uint32_t i = 0; i < f_neuron_pool_size; i = i + 1) {
+		// pd->f_neurons.push_back(f_neuron_pool[i].get_int());
+		f_neuron_pool.push_back(FNeuron(pd->f_neurons[i], i));
+	}
+
+	pd->magic_numbers_size = magic_numbers_size;
+	pd->neuron_pool_size = neuron_pool_size;
+	pd->f_neuron_pool_size = f_neuron_pool_size;
+
+	test_consistency();
+	log(" %d: DNA initialized\n", pn);
+}
+
+bool ManagementData::test_consistency(bool itemized) {
+
+	assert(pd->neuron_weights.size() == pd->neuron_pool_size);
+	assert(pd->neuron_functs.size() == pd->neuron_pool_size);
+	assert(neuron_pool.size() == pd->neuron_pool_size);
+	assert(neuron_pool.size() == neuron_pool_size);
+
+	assert(pd->magic_numbers_size == magic_numbers_size);
+	assert(pd->magic_numbers.size() == magic_numbers_size);
+
+	assert(pd->f_neurons.size() == pd->f_neuron_pool_size);
+	assert(f_neuron_pool.size() == pd->f_neuron_pool_size);
+	assert(f_neuron_pool.size() == f_neuron_pool_size);
+
+	if (itemized) {
+		// comparing content of neuron and fneuron pools
+		for (uint16_t i = 0; i < neuron_pool_size; i += 1) {
+			assert(pd->neuron_weights[i] == neuron_pool[i].get_weight());
+			assert(pd->neuron_functs[i] == neuron_pool[i].get_type());
+			assert(neuron_pool[i].get_id() == i);
+		}
+		for (uint16_t i = 0; i < f_neuron_pool_size; i += 1) {
+			assert(pd->f_neurons[i] == f_neuron_pool[i].get_int());
+			assert(f_neuron_pool[i].get_id() == i);
+		}
+	}
+
+	// There is no 'return false', because asserts above would take care of this
+	return true;
+}
+
+// Print DNA data to console, used for training
+// Once we will have AI dumped into files, this should be removed
+// Also, used only for training
+void ManagementData::dump_data() {
+	// dumping new numbers
+	log("     actual military_numbers (%lu):\n      {", pd->magic_numbers.size());
+	uint16_t itemcounter = 1;
+	uint16_t line_counter = 1;
+	for (const auto& item : pd->magic_numbers) {
+		log(" %3d %s", item, (&item != &pd->magic_numbers.back()) ? ", " : "");
+		if (itemcounter % 10 == 0) {
+			log(" //AutoContent_%02d\n       ", line_counter);
+			line_counter += 1;
+		}
+		++itemcounter;
+	}
+	log("}\n");
+
+	log("     actual neuron setup:\n      ");
+	log("{ ");
+	itemcounter = 1;
+	for (auto& item : neuron_pool) {
+		log(" %3d %s", item.get_weight(), (&item != &neuron_pool.back()) ? ", " : "");
+		if (itemcounter % 10 == 0) {
+			log(" //AutoContent_%02d\n       ", line_counter);
+			line_counter += 1;
+		}
+		++itemcounter;
+	}
+	log("}\n      { ");
+	itemcounter = 1;
+	for (auto& item : neuron_pool) {
+		log(" %3d %s", item.get_type(), (&item != &neuron_pool.back()) ? ", " : "");
+		if (itemcounter % 10 == 0) {
+			log(" //AutoContent_%02d\n       ", line_counter);
+			line_counter += 1;
+		}
+		++itemcounter;
+	}
+	log("}\n");
+
+	log("     actual f-neuron setup:\n      ");
+	log("{ ");
+	itemcounter = 1;
+	for (auto& item : f_neuron_pool) {
+		log(" %8u %s", item.get_int(), (&item != &f_neuron_pool.back()) ? ", " : "");
+		if (itemcounter % 10 == 0) {
+			log(" //AutoContent_%02d\n       ", line_counter);
+			line_counter += 1;
+		}
+		++itemcounter;
+	}
+	log("}\n");
+}
+
+// querrying military number at a possition
+int16_t ManagementData::get_military_number_at(uint8_t pos) {
+	assert(pos < magic_numbers_size);
+	return pd->magic_numbers[pos];
+}
+
+// Setting military number (persistent are used also for local use)
+void ManagementData::set_military_number_at(const uint8_t pos, int16_t value) {
+	assert(pos < magic_numbers_size);
+
+	while (pos >= pd->magic_numbers.size()) {
+		pd->magic_numbers.push_back(0);
+	}
+
+	if (value < -100) {
+		value = -100;
+	}
+	if (value > 100) {
+		value = 100;
+	}
+	pd->magic_numbers[pos] = value;
 }
 
 uint16_t MineTypesObserver::total_count() const {
@@ -450,9 +1288,7 @@
 	}
 }
 
-bool FlagsForRoads::get_winner(uint32_t* winner_hash, uint32_t pos) {
-	assert(pos == 1 || pos == 2);
-	uint32_t counter = 1;
+bool FlagsForRoads::get_winner(uint32_t* winner_hash) {
 	// If AI can ask for 2nd position, but there is only one viable candidate
 	// we return the first one of course
 	bool has_winner = false;
@@ -466,37 +1302,89 @@
 		*winner_hash = candidate_flag.coords_hash;
 		has_winner = true;
 
-		if (counter == pos) {
+		if (std::rand() % 3 > 0) {
+			// with probability of 2/3 we accept this flag
 			return true;
-		} else if (counter < pos) {
-			counter += 1;
-		} else {
-			break;
 		}
 	}
+
 	if (has_winner) {
 		return true;
 	}
 	return false;
 }
 
-// This is an struct that stores strength of players, info on teams and provides some outputs from
-// these data
-PlayersStrengths::PlayerStat::PlayerStat() : team_number(0), players_power(0) {
-}
-PlayersStrengths::PlayerStat::PlayerStat(Widelands::TeamNumber tc, uint32_t pp)
-   : team_number(tc), players_power(pp) {
+PlayersStrengths::PlayersStrengths() : update_time(0) {
+}
+
+// Default constructor
+PlayersStrengths::PlayerStat::PlayerStat()
+   : team_number(0),
+     is_enemy(false),
+     players_power(0),
+     old_players_power(0),
+     old60_players_power(0),
+     players_casualities(0) {
+}
+
+// Constructor to be used
+PlayersStrengths::PlayerStat::PlayerStat(Widelands::TeamNumber tc,
+                                         bool e,
+                                         uint32_t pp,
+                                         uint32_t op,
+                                         uint32_t o60p,
+                                         uint32_t cs,
+                                         uint32_t land,
+                                         uint32_t oland,
+                                         uint32_t o60l)
+   : team_number(tc),
+     is_enemy(e),
+     players_power(pp),
+     old_players_power(op),
+     old60_players_power(o60p),
+     players_casualities(cs),
+     players_land(land),
+     old_players_land(oland),
+     old60_players_land(o60l) {
+	last_time_seen = kNever;
 }
 
 // Inserting/updating data
-void PlayersStrengths::add(Widelands::PlayerNumber pn, Widelands::TeamNumber tn, uint32_t pp) {
-	if (all_stats.count(pn) == 0) {
-		all_stats.insert(std::pair<Widelands::PlayerNumber, PlayerStat>(pn, PlayerStat(tn, pp)));
+void PlayersStrengths::add(Widelands::PlayerNumber pn,
+                           Widelands::PlayerNumber opn,
+                           Widelands::TeamNumber mytn,
+                           Widelands::TeamNumber pltn,
+                           uint32_t pp,
+                           uint32_t op,
+                           uint32_t o60p,
+                           uint32_t cs,
+                           uint32_t land,
+                           uint32_t oland,
+                           uint32_t o60l) {
+	if (all_stats.count(opn) == 0) {
+		bool enemy = false;
+		if (pn == opn) {
+			;
+		} else if (pltn == 0 || mytn == 0) {
+			enemy = true;
+		} else if (pltn != mytn) {
+			enemy = true;
+		}
+		this_player_number = pn;
+		all_stats.insert(std::pair<Widelands::PlayerNumber, PlayerStat>(
+		   opn, PlayerStat(pltn, enemy, pp, op, o60p, cs, land, oland, o60l)));
 	} else {
-		all_stats[pn].players_power = pp;
+		all_stats[opn].players_power = pp;
+		all_stats[opn].old_players_power = op;
+		all_stats[opn].old60_players_power = o60p;
+		all_stats[opn].players_casualities = cs;
+		all_stats[opn].players_land = land;
+		all_stats[opn].old_players_land = oland;
+		all_stats[opn].old60_players_land = oland;
 	}
 }
 
+// After statistics for team members are updated, this calculation is needed
 void PlayersStrengths::recalculate_team_power() {
 	team_powers.clear();
 	for (auto& item : all_stats) {
@@ -510,6 +1398,190 @@
 	}
 }
 
+// This just goes over information about all enemies and where they were seen last time
+bool PlayersStrengths::any_enemy_seen_lately(const uint32_t gametime) {
+	for (auto& item : all_stats) {
+		if (item.second.is_enemy && player_seen_lately(item.first, gametime)) {
+			return true;
+		}
+	}
+	return false;
+}
+
+// Returns count of nearby enemies
+uint8_t PlayersStrengths::enemies_seen_lately_count(const uint32_t gametime) {
+	uint8_t count = 0;
+	for (auto& item : all_stats) {
+		if (item.second.is_enemy && player_seen_lately(item.first, gametime)) {
+			count += 1;
+		}
+	}
+	return count;
+}
+
+// uint32_t PlayersStrengths::enemy_last_seen(){
+// uint32_t time = 0;
+// for (auto& item : all_stats) {
+// if (item.second.last_time_seen == kNever){
+// continue;
+//}
+// if (item.second.is_enemy && item.second.last_time_seen > time) {
+// time = item.second.last_time_seen;
+//}
+//}
+// if (time == 0) {return kNever;}
+// return time;
+//}
+
+// When we see enemy, we use this to store the time
+void PlayersStrengths::set_last_time_seen(const uint32_t seentime, Widelands::PlayerNumber pn) {
+	assert(all_stats.count(pn) > 0);
+	all_stats[pn].last_time_seen = seentime;
+}
+
+bool PlayersStrengths::get_is_enemy(Widelands::PlayerNumber pn) {
+	assert(all_stats.count(pn) > 0);
+	return all_stats[pn].is_enemy;
+}
+
+// Was the player seen less then 2 minutes ago
+bool PlayersStrengths::player_seen_lately(Widelands::PlayerNumber pn, const uint32_t gametime) {
+	assert(all_stats.count(pn) > 0);
+	if (all_stats[pn].last_time_seen == kNever) {
+		return false;
+	}
+	if (all_stats[pn].last_time_seen + static_cast<uint32_t>(2 * 60 * 1000) > gametime) {
+		return true;
+	}
+	return false;
+}
+
+// This is strength of player
+uint32_t PlayersStrengths::get_player_power(Widelands::PlayerNumber pn) {
+	if (all_stats.count(pn) > 0) {
+		return all_stats[pn].players_power;
+	};
+	return 0;
+}
+
+// This is land size owned by player
+uint32_t PlayersStrengths::get_player_land(Widelands::PlayerNumber pn) {
+	if (all_stats.count(pn) > 0) {
+		return all_stats[pn].players_land;
+	};
+	return 0;
+}
+
+// Calculates strenght of enemies seen within last 2 minutes
+uint32_t PlayersStrengths::get_visible_enemies_power(const uint32_t gametime) {
+	uint32_t pw = 0;
+	for (auto& item : all_stats) {
+		if (get_is_enemy(item.first) && player_seen_lately(item.first, gametime)) {
+			pw += item.second.players_power;
+		}
+	}
+	return pw;
+}
+
+uint32_t PlayersStrengths::get_enemies_average_power() {
+	uint32_t sum = 0;
+	uint8_t count = 0;
+	for (auto& item : all_stats) {
+		if (get_is_enemy(item.first)) {
+			sum += item.second.players_power;
+			count += 1;
+		}
+	}
+	if (count > 0) {
+		return sum / count;
+	}
+	return 0;
+}
+
+uint32_t PlayersStrengths::get_enemies_average_land() {
+	uint32_t sum = 0;
+	uint8_t count = 0;
+	for (auto& item : all_stats) {
+		if (get_is_enemy(item.first)) {
+			sum += item.second.players_land;
+			count += 1;
+		}
+	}
+	if (count > 0) {
+		return sum / count;
+	}
+	return 0;
+}
+
+// Strength of stronger player
+uint32_t PlayersStrengths::get_enemies_max_power() {
+	uint32_t power = 0;
+	for (auto& item : all_stats) {
+		if (get_is_enemy(item.first)) {
+			power = std::max<uint32_t>(power, item.second.players_power);
+		}
+	}
+	return power;
+}
+
+uint32_t PlayersStrengths::get_enemies_max_land() {
+	uint32_t land = 0;
+	for (auto& item : all_stats) {
+		if (get_is_enemy(item.first)) {
+			land = std::max<uint32_t>(land, item.second.players_land);
+		}
+	}
+	return land;
+}
+
+uint32_t PlayersStrengths::get_old_player_power(Widelands::PlayerNumber pn) {
+	if (all_stats.count(pn) > 0) {
+		return all_stats[pn].old_players_power;
+	};
+	return 0;
+}
+
+uint32_t PlayersStrengths::get_old60_player_power(Widelands::PlayerNumber pn) {
+	if (all_stats.count(pn) > 0) {
+		return all_stats[pn].old60_players_power;
+	};
+	return 0;
+}
+
+uint32_t PlayersStrengths::get_old_player_land(Widelands::PlayerNumber pn) {
+	if (all_stats.count(pn) == 0) {
+		log(" %d: Players statistics are still empty\n", pn);
+		return 0;
+	}
+	return all_stats[pn].old_players_land;
+}
+
+uint32_t PlayersStrengths::get_old60_player_land(Widelands::PlayerNumber pn) {
+	if (all_stats.count(pn) == 0) {
+		log(" %d: Players statistics are still empty\n", pn);
+		return 0;
+	}
+	return all_stats[pn].old60_players_land;
+}
+
+uint32_t PlayersStrengths::get_old_visible_enemies_power(const uint32_t gametime) {
+	uint32_t pw = 0;
+	for (auto& item : all_stats) {
+		if (get_is_enemy(item.first) && player_seen_lately(item.first, gametime)) {
+			pw += item.second.old_players_power;
+		}
+	}
+	return pw;
+}
+
+// This is casualities of player
+uint32_t PlayersStrengths::get_player_casualities(Widelands::PlayerNumber pn) {
+	if (all_stats.count(pn) > 0) {
+		return all_stats[pn].players_casualities;
+	};
+	return 0;
+}
+
 // This is strength of player plus third of strength of other members of his team
 uint32_t PlayersStrengths::get_modified_player_power(Widelands::PlayerNumber pn) {
 	uint32_t result = 0;
@@ -524,12 +1596,17 @@
 	return result;
 }
 
+// Are the player in the same team
 bool PlayersStrengths::players_in_same_team(Widelands::PlayerNumber pl1,
                                             Widelands::PlayerNumber pl2) {
-	if (all_stats.count(pl1) > 0 && all_stats.count(pl2) > 0 && pl1 != pl2) {
+	assert(all_stats.count(pl1) > 0);
+	assert(all_stats.count(pl2) > 0);
+	if (pl1 == pl2) {
+		return false;
+	} else if (all_stats[pl1].team_number > 0 &&
+	           all_stats[pl1].team_number == all_stats[pl2].team_number) {
 		// team number 0 = no team
-		return all_stats[pl1].team_number > 0 &&
-		       all_stats[pl1].team_number == all_stats[pl2].team_number;
+		return true;
 	} else {
 		return false;
 	}
@@ -551,4 +1628,20 @@
 	return my_strength > strongest_opponent_strength + 50;
 }
 
+// Update_time is used to prevent too frequent updates of statistics
+void PlayersStrengths::set_update_time(const uint32_t gametime) {
+	update_time = gametime;
+}
+
+uint32_t PlayersStrengths::get_update_time() {
+	return update_time;
+}
+
+ProductionSiteObserver::ProductionSiteObserver()
+   : stats_zero(0),
+     no_resources_since(kNever),
+     upgrade_pending(false),
+     dismantle_pending_since(kNever) {
+}
+
 }  // namespace WIdelands

=== modified file 'src/ai/ai_help_structs.h'
--- src/ai/ai_help_structs.h	2017-01-25 18:55:59 +0000
+++ src/ai/ai_help_structs.h	2017-06-16 05:22:22 +0000
@@ -21,6 +21,7 @@
 #define WL_AI_AI_HELP_STRUCTS_H
 
 #include <list>
+#include <queue>
 #include <unordered_set>
 
 #include "ai/ai_hints.h"
@@ -54,6 +55,33 @@
 	kForbidden
 };
 
+// A building type can have no, one or multiple of these attributes
+enum class BuildingAttribute : uint8_t {
+	kBakery,
+	kRanger,
+	kBuildable,
+	kLumberjack,
+	kPort,
+	kNeedsRocks,
+	kWell,
+	kNeedsCoast,
+	kHunter,
+	kFisher,
+	kShipyard,
+	kBarracks,
+	kSpaceConsumer,
+	kRecruitment,
+	kBuildingMatProducer,
+	kUpgradeSubstitutes,
+	kUpgradeExtends,
+	kSaw,
+	kIronMine
+};
+
+enum class AiType : uint8_t { kVeryWeak, kWeak, kNormal };
+
+enum class ExpansionMode : uint8_t { kResources = 0, kSpace = 1, kEconomy = 2, kBoth = 3 };
+
 enum class AiModeBuildings : uint8_t { kAnotherAllowed, kOnLimit, kLimitExceeded };
 
 enum class SchedulerTaskId : uint8_t {
@@ -74,9 +102,29 @@
 	kCheckTrainingsites,
 	kCountMilitaryVacant,
 	kCheckEnemySites,
+	kManagementUpdate,
+	kUpdateStats,
 	kUnset
 };
 
+// This is simplification of a curve, to avoid repeated calculation
+const std::vector<std::vector<int8_t>> neuron_curves = {
+   {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100},
+   {0, 0, 1, 2, 4, 6, 9, 12, 16, 20, 25, 30, 36, 42, 49, 56, 64, 72, 81, 90, 100},
+   {0, 17, 25, 32, 38, 44, 49, 53, 58, 62, 66, 70, 74, 78, 81, 84, 88, 91, 94, 97, 100},
+};
+
+constexpr int magic_numbers_size = 150;
+constexpr int neuron_pool_size = 80;
+constexpr int f_neuron_pool_size = 60;
+constexpr int f_neuron_bit_size = 32;
+constexpr int MutationRatePosition = 42;
+// set this to false for trunk NOCOM
+// this is used only for training of AI
+constexpr bool kAITrainingMode = true;
+
+constexpr uint32_t kNever = std::numeric_limits<uint32_t>::max();
+
 struct CheckStepRoadAI {
 	CheckStepRoadAI(Player* const pl, uint8_t const mc, bool const oe);
 
@@ -112,17 +160,14 @@
 	Game& game;
 };
 
-// When looking for unowned terrain to acquire, we are actually
-// only interested in fields we can walk on.
-// Fields should either be completely unowned or owned by an opposing player_
-struct FindNodeUnowned {
-	FindNodeUnowned(Player* p, Game& g, bool oe = false);
+// We need to count walkable fields owned by enemy
+struct FindEnemyNodeWalkable {
+	FindEnemyNodeWalkable(Player* p, Game& g);
 
 	bool accept(const Map&, const FCoords& fc) const;
 
 	Player* player;
 	Game& game;
-	bool only_enemies;
 };
 
 // Sometimes we need to know how many nodes our allies owns
@@ -140,7 +185,19 @@
 // pay speciall attention to fields where mines can be built.
 // Fields should be completely unowned
 struct FindNodeUnownedMineable {
-	FindNodeUnownedMineable(Player* p, Game& g);
+	FindNodeUnownedMineable(Player* p, Game& g, const int32_t t = INVALID_INDEX);
+
+	bool accept(const Map&, const FCoords& fc) const;
+
+	Player* player;
+	Game& game;
+	int32_t ore_type;
+};
+
+// When looking for unowned terrain to acquire, we must
+// consider if any buildings can be built on unowned land.
+struct FindNodeUnownedBuildable {
+	FindNodeUnownedBuildable(Player* p, Game& g);
 
 	bool accept(const Map&, const FCoords& fc) const;
 
@@ -216,6 +273,18 @@
 	int32_t distance;
 };
 
+struct EventTimeQueue {
+	EventTimeQueue();
+
+	void push(uint32_t);
+	uint32_t count(uint32_t);
+	void strip_old(uint32_t);
+
+private:
+	uint32_t duration_ = 20 * 60 * 1000;
+	std::queue<uint32_t> queue;
+};
+
 struct WalkableSpot {
 	Coords coords;
 	bool has_flag;
@@ -238,11 +307,17 @@
 
 	bool preferred;
 	bool enemy_nearby;
+	bool enemy_accessible_;
 
-	uint8_t unowned_land_nearby;
+	bool enemy_wh_nearby;
+	uint16_t unowned_land_nearby;
+	uint16_t enemy_owned_land_nearby;
+	uint16_t unowned_buildable_spots_nearby;
+	uint16_t nearest_buildable_spot_nearby;
 	// to identify that field is too close to border and no production building should be built there
 	bool near_border;
-	uint8_t unowned_mines_spots_nearby;
+	uint16_t unowned_mines_spots_nearby;
+	uint16_t unowned_iron_mines_nearby;
 	uint8_t trees_nearby;
 	uint8_t rocks_nearby;
 	int16_t water_nearby;
@@ -264,22 +339,29 @@
 	// are construction sites that will change this once they are built
 	int16_t military_in_constr_nearby;
 	// actual count of soldiers in nearby buldings
-	int16_t area_military_presence;
+	int16_t own_military_presence;
+	int16_t enemy_military_presence;
+	int16_t enemy_military_sites;  // Including unfinished
+	int16_t ally_military_presence;
 	// stationed (manned) military buildings nearby
 	int16_t military_stationed;
-	// stationed (manned) military buildings nearby
 	// unconnected buildings nearby
 	bool unconnected_nearby;
 	int16_t military_unstationed;
-	bool is_portspace;
+	int16_t own_non_military_nearby;
+	bool defense_msite_allowed;
+	Widelands::ExtendedBool is_portspace;
 	bool port_nearby;  // to increase priority if a port is nearby,
 	// especially for new colonies
 	Widelands::ExtendedBool portspace_nearby;  // prefer military buildings closer to the portspace
 	int32_t max_buildcap_nearby;
 	// it is not necessary to check resources (stones, fish...) too frequently as they do not change
-	// fast
-	// this stores time of last check
+	// fast this stores time of last check
 	uint32_t last_resources_check_time;
+	int32_t military_score_;
+	bool inland;
+	uint16_t local_soldier_capacity;
+	bool is_militarysite;
 
 	std::vector<uint8_t> consumers_nearby;
 	std::vector<uint8_t> producers_nearby;
@@ -328,27 +410,20 @@
 
 	Type type;
 
-	bool plants_trees;
-	bool recruitment;  // is "producing" workers?
 	Widelands::BuildingNecessity new_building;
 	uint32_t new_building_overdue;
 	int32_t primary_priority;
-	bool is_buildable;
-	bool need_trees;   // lumberjack = true
-	bool need_rocks;   // quarry = true
-	bool mines_water;  // wells
-	bool need_water;   // fisher, fish_breeder = true
-	bool is_hunter;    // need to identify hunters
-	bool is_fisher;    // need to identify fishers
-	bool is_port;
-	bool is_shipyard;
-	bool space_consumer;       // farm, vineyard... = true
+	std::set<BuildingAttribute> is_what;
+	// this is a "shortcut" to above
+	bool is(BuildingAttribute) const;
+	void set_is(BuildingAttribute);
+	void unset_is(BuildingAttribute);
 	bool expansion_type;       // military building used that can be used to control area
 	bool fighting_type;        // military building built near enemies
 	bool mountain_conqueror;   // military building built near mountains
 	uint32_t prohibited_till;  // do not build before (ms)
 	uint32_t forced_after;     // do not wait until ware is needed
-	TrainingSiteType trainingsite_type;
+	uint8_t max_ts_proportion;
 
 	uint16_t unconnected_count;  // to any warehouse (count of such buildings)
 
@@ -358,16 +433,9 @@
 
 	std::vector<Widelands::DescriptionIndex> inputs;
 	std::vector<Widelands::DescriptionIndex> outputs;
+	std::vector<Widelands::DescriptionIndex> positions;
 	std::vector<Widelands::DescriptionIndex> critical_building_material;
 
-	bool produces_building_material;
-
-	// an enhancement to this building:
-	// produces all wares as current building, and perhaps more
-	bool upgrade_substitutes;
-	// produces some additional wares
-	bool upgrade_extends;
-
 	// It seems that fish and meat are subsitutes (for trainingsites), so
 	// when testing if a trainingsite is supplied enough
 	// we count the wares together
@@ -388,7 +456,7 @@
 	int32_t cnt_upgrade_pending;  // number of buildings that are to be upgraded
 
 	// used to track amount of wares produced by building
-	uint32_t stocklevel;
+	uint32_t stocklevel_count;
 	uint32_t stocklevel_time;  // time when stocklevel_ was last time recalculated
 	uint32_t last_dismantle_time;
 	uint32_t construction_decision_time;
@@ -400,22 +468,22 @@
 };
 
 struct ProductionSiteObserver {
+	ProductionSiteObserver();
 	Widelands::ProductionSite* site;
 	uint32_t built_time;
 	uint32_t unoccupied_till;
 	uint8_t stats_zero;
 	uint32_t no_resources_since;
 	bool upgrade_pending;
+	uint32_t dismantle_pending_since;
 	BuildingObserver* bo;
 };
 
 struct MilitarySiteObserver {
 	Widelands::MilitarySite* site;
 	BuildingObserver* bo;
-	uint8_t checks;
-	// when considering attack most military sites are inside territory and should be skipped during
-	// evaluation
-	bool enemies_nearby;
+	uint16_t understaffed;
+	uint32_t last_change;  // to prevent too fast switching ocupancy policy
 	uint32_t built_time;
 };
 
@@ -457,13 +525,15 @@
 
 	bool is_warehouse;
 	int32_t attack_soldiers_strength;
+	int32_t attack_soldiers_competency;
 	int32_t defenders_strength;
 	uint8_t stationed_soldiers;
-	uint32_t last_time_attackable;
+	uint32_t last_time_seen;
 	uint32_t last_tested;
 	int16_t score;
 	Widelands::ExtendedBool mines_nearby;
-	int16_t no_attack_counter;
+	uint32_t last_time_attacked;
+	uint32_t attack_counter;
 };
 
 // as all mines have 3 levels, AI does not know total count of mines per mined material
@@ -475,6 +545,112 @@
 
 	uint16_t in_construction;
 	uint16_t finished;
+	bool is_critical;
+	uint16_t unoccupied;
+};
+
+struct Neuron {
+	Neuron(int8_t, uint8_t, uint16_t);
+	void recalculate();
+	void set_weight(int8_t w);
+	int8_t get_weight() {
+		return weight;
+	};
+	int8_t get_result(uint8_t);
+	int8_t get_result_safe(int32_t, bool = false);
+	void set_type(uint8_t);
+	uint8_t get_type() {
+		return type;
+	};
+	uint16_t get_id() {
+		return id;
+	};
+	int32_t get_lowest_pos() {
+		return lowest_pos;
+	};
+	int32_t get_highest_pos() {
+		return highest_pos;
+	};
+
+private:
+	int8_t results[21];
+	int8_t weight;
+	uint8_t type;
+	uint16_t id;
+	int32_t lowest_pos;
+	int32_t highest_pos;
+};
+
+struct FNeuron {
+	FNeuron(uint32_t, uint16_t);
+
+	void flip_bit(uint8_t);
+	// void set(uint8_t);
+	bool get_result(bool, bool, bool, bool bool4 = true, bool bool5 = true);
+	bool get_position(uint8_t);
+	uint32_t get_int();
+	uint16_t get_id() {
+		return id;
+	};
+
+private:
+	std::bitset<f_neuron_bit_size> core;
+	uint16_t id;
+};
+
+struct ExpansionType {
+	ExpansionType();
+
+	void set_expantion_type(ExpansionMode);
+	ExpansionMode get_expansion_type() {
+		return type;
+	};
+
+private:
+	ExpansionMode type;
+};
+
+// This is to keep all data related to AI magic numbers
+struct ManagementData {
+	ManagementData();
+
+	std::vector<Neuron> neuron_pool;
+	std::vector<FNeuron> f_neuron_pool;
+	Widelands::Player::AiPersistentState* pd;
+
+	void mutate(PlayerNumber = 0);
+	void new_dna_for_persistent(uint8_t, Widelands::AiType);
+	void copy_persistent_to_local(uint8_t);
+	void review(
+	   uint32_t, PlayerNumber, uint32_t, uint32_t, uint32_t, uint16_t, int16_t, int16_t, uint16_t);
+	void dump_data();
+	// void initialize(uint8_t, Widelands::AiType, bool reinitializing = false);
+	uint16_t new_neuron_id() {
+		next_neuron_id += 1;
+		return next_neuron_id - 1;
+	};
+	void reset_neuron_id() {
+		next_neuron_id = 0;
+	}
+	uint16_t new_bi_neuron_id() {
+		next_bi_neuron_id += 1;
+		return next_bi_neuron_id - 1;
+	};
+	void reset_bi_neuron_id() {
+		next_bi_neuron_id = 0;
+	}
+	int16_t get_military_number_at(uint8_t);
+	void set_military_number_at(uint8_t, int16_t);
+	int8_t shift_weight_value(int8_t, bool = true);
+	bool test_consistency(bool = false);
+
+private:
+	int32_t score;
+	uint8_t primary_parent;
+	uint16_t next_neuron_id;
+	uint16_t next_bi_neuron_id;
+	uint16_t performance_change;
+	Widelands::AiType ai_type;
 };
 
 // this is used to count militarysites by their size
@@ -560,34 +736,87 @@
 	// Updating walking distance over existing roads
 	void set_road_distance(Widelands::Coords coords, int32_t distance);
 	// Finally we query the flag that we will build a road to
-	bool get_winner(uint32_t* winner_hash, uint32_t pos);
+	bool get_winner(uint32_t* winner_hash);
 };
 
 // This is a struct that stores strength of players, info on teams and provides some outputs from
 // these data
 struct PlayersStrengths {
+private:
 	struct PlayerStat {
 		PlayerStat();
-		PlayerStat(Widelands::TeamNumber tc, uint32_t pp);
+		PlayerStat(Widelands::TeamNumber tc,
+		           bool en,
+		           uint32_t pp,
+		           uint32_t op,
+		           uint32_t o60p,
+		           uint32_t cs,
+		           uint32_t land,
+		           uint32_t oland,
+		           uint32_t o60l);
 
 		Widelands::TeamNumber team_number;
+		bool is_enemy;
 		uint32_t players_power;
+		uint32_t old_players_power;
+		uint32_t old60_players_power;
+		uint32_t players_casualities;
+		uint32_t last_time_seen;
+		uint32_t players_land;
+		uint32_t old_players_land;
+		uint32_t old60_players_land;
 	};
 
+public:
+	PlayersStrengths();
 	// Inserting/updating data
-	void add(Widelands::PlayerNumber pn, Widelands::TeamNumber tn, uint32_t pp);
+	void add(Widelands::PlayerNumber pn,
+	         Widelands::PlayerNumber opn,
+	         Widelands::TeamNumber mytn,
+	         Widelands::TeamNumber pltn,
+	         uint32_t pp,
+	         uint32_t op,
+	         uint32_t o60p,
+	         uint32_t cs,
+	         uint32_t land,
+	         uint32_t oland,
+	         uint32_t o60l);
 	void recalculate_team_power();
 
 	// This is strength of player plus third of strength of other members of his team
 	uint32_t get_modified_player_power(Widelands::PlayerNumber pn);
+	uint32_t get_player_power(Widelands::PlayerNumber pn);
+	uint32_t get_old_player_power(Widelands::PlayerNumber pn);
+	uint32_t get_old60_player_power(Widelands::PlayerNumber pn);
+	uint32_t get_player_land(Widelands::PlayerNumber pn);
+	uint32_t get_old_player_land(Widelands::PlayerNumber pn);
+	uint32_t get_old60_player_land(Widelands::PlayerNumber pn);
+	uint32_t get_visible_enemies_power(uint32_t);
+	uint32_t get_enemies_average_power();
+	uint32_t get_enemies_average_land();
+	uint32_t get_enemies_max_power();
+	uint32_t get_enemies_max_land();
+	uint32_t get_old_visible_enemies_power(uint32_t);
+	uint32_t get_player_casualities(Widelands::PlayerNumber pn);
 	bool players_in_same_team(Widelands::PlayerNumber pl1, Widelands::PlayerNumber pl2);
 	bool strong_enough(Widelands::PlayerNumber pl);
+	void set_last_time_seen(uint32_t, Widelands::PlayerNumber);
+	bool player_seen_lately(Widelands::PlayerNumber, uint32_t);
+	bool get_is_enemy(Widelands::PlayerNumber);
+	uint8_t enemies_seen_lately_count(uint32_t);
+	bool any_enemy_seen_lately(uint32_t);
+	PlayerNumber this_player_number;
+	void set_update_time(uint32_t);
+	uint32_t get_update_time();
 
+private:
 	// This is the core part of this struct
 	std::map<Widelands::PlayerNumber, PlayerStat> all_stats;
 
 	// Number of team, sum of players' strength
 	std::map<Widelands::TeamNumber, uint32_t> team_powers;
+
+	uint32_t update_time;
 };
 }  // namespace Widelands
 

=== modified file 'src/ai/ai_hints.cc'
--- src/ai/ai_hints.cc	2017-01-25 18:55:59 +0000
+++ src/ai/ai_hints.cc	2017-06-16 05:22:22 +0000
@@ -38,19 +38,22 @@
         table->has_key("mountain_conqueror") ? table->get_bool("mountain_conqueror") : false),
      shipyard_(table->has_key("shipyard") ? table->get_bool("shipyard") : false),
      prohibited_till_(table->has_key("prohibited_till") ? table->get_int("prohibited_till") : 0),
+     is_basic_(table->has_key("is_basic") ? table->get_int("is_basic") : 0),
      // 10 days default
      forced_after_(table->has_key("forced_after") ? table->get_int("forced_after") : 864000),
      mines_percent_(table->has_key("mines_percent") ? table->get_int("mines_percent") : 100),
      very_weak_ai_limit_(
         table->has_key("very_weak_ai_limit") ? table->get_int("very_weak_ai_limit") : -1),
      weak_ai_limit_(table->has_key("weak_ai_limit") ? table->get_int("weak_ai_limit") : -1),
-     trainingsite_type_(TrainingSiteType::kNoTS) {
-
-	if (table->has_key("trainingsite_type")) {
-		if (table->get_string("trainingsite_type") == "basic") {
-			trainingsite_type_ = TrainingSiteType::kBasic;
-		} else if (table->get_string("trainingsite_type") == "advanced") {
-			trainingsite_type_ = TrainingSiteType::kAdvanced;
-		}
-	}
+     trainingsites_max_percent_(table->has_key("trainingsites_max_percent") ?
+                                   table->get_int("trainingsites_max_percent") :
+                                   0) {
+}
+
+void BuildingHints::set_trainingsites_max_percent(int percent) {
+	trainingsites_max_percent_ = percent;
+}
+
+uint8_t BuildingHints::trainingsites_max_percent() const {
+	return trainingsites_max_percent_;
 }

=== modified file 'src/ai/ai_hints.h'
--- src/ai/ai_hints.h	2017-01-25 18:55:59 +0000
+++ src/ai/ai_hints.h	2017-06-16 05:22:22 +0000
@@ -28,8 +28,6 @@
 #include "base/macros.h"
 #include "scripting/lua_table.h"
 
-enum class TrainingSiteType : uint8_t { kNoTS = 0, kBasic = 1, kAdvanced = 2 };
-
 /// This struct is used to read out the data given in [aihints] section of a
 /// buildings conf file. It is used to tell the computer player about the
 /// special properties of a building.
@@ -95,6 +93,10 @@
 		return prohibited_till_;
 	}
 
+	uint32_t get_is_basic() const {
+		return is_basic_;
+	}
+
 	uint32_t get_forced_after() const {
 		return forced_after_;
 	}
@@ -111,9 +113,9 @@
 		return weak_ai_limit_;
 	}
 
-	TrainingSiteType get_trainingsite_type() const {
-		return trainingsite_type_;
-	}
+	void set_trainingsites_max_percent(int percent);
+
+	uint8_t trainingsites_max_percent() const;
 
 private:
 	std::string renews_map_resource_;
@@ -129,11 +131,12 @@
 	bool mountain_conqueror_;
 	bool shipyard_;
 	int32_t prohibited_till_;
+	uint32_t is_basic_;
 	int32_t forced_after_;
 	int8_t mines_percent_;
 	int16_t very_weak_ai_limit_;
 	int16_t weak_ai_limit_;
-	TrainingSiteType trainingsite_type_;
+	int trainingsites_max_percent_;
 
 	DISALLOW_COPY_AND_ASSIGN(BuildingHints);
 };

=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc	2017-01-28 14:53:28 +0000
+++ src/ai/defaultai.cc	2017-06-16 05:22:22 +0000
@@ -62,13 +62,16 @@
 constexpr int kMinBFCheckInterval = 5 * 1000;
 constexpr int kMinMFCheckInterval = 19 * 1000;
 constexpr int kMarineDecisionInterval = 20 * 1000;
+constexpr int kRemainingBasicBuildingsResetTime = 1 * 60 * 1000;
 
 // following two are used for roads management, for creating shortcuts and dismantling dispensable
 // roads
 constexpr int32_t kSpotsTooLittle = 15;
 constexpr int32_t kSpotsEnough = 25;
 
-// this is intended for map developers, by default should be off
+constexpr uint16_t kTargetQuantCap = 30;
+
+// this is intended for map developers&testers, by default should be off
 constexpr bool kPrintStats = false;
 
 // for scheduler
@@ -81,37 +84,29 @@
 DefaultAI::VeryWeakImpl DefaultAI::very_weak_impl;
 
 /// Constructor of DefaultAI
-DefaultAI::DefaultAI(Game& ggame, PlayerNumber const pid, DefaultAI::Type const t)
+DefaultAI::DefaultAI(Game& ggame, PlayerNumber const pid, Widelands::AiType const t)
    : ComputerPlayer(ggame, pid),
      type_(t),
      player_(nullptr),
      tribe_(nullptr),
+     attackers_count_(0),
      next_ai_think_(0),
      scheduler_delay_counter_(0),
      wood_policy_(WoodPolicy::kAllowRangers),
-     num_prod_constructionsites(0),
+     numof_psites_in_constr(0),
      num_ports(0),
      numof_warehouses_(0),
+     numof_warehouses_in_const_(0),
      military_last_dismantle_(0),
      military_last_build_(0),
      time_of_last_construction_(0),
      next_mine_construction_due_(0),
-     ts_basic_count_(0),
-     ts_basic_const_count_(0),
-     ts_advanced_count_(0),
-     ts_advanced_const_count_(0),
+     fishers_count_(0),
+     bakeries_count_(),
+     ts_finished_count_(0),
+     ts_in_const_count_(0),
      ts_without_trainers_(0),
      inhibit_road_building_(0),
-     last_road_dismantled_(0),
-     enemy_last_seen_(kNever),
-     vacant_mil_positions_(0),
-     last_attack_time_(0),
-     enemysites_check_delay_(60),
-     spots_(0),
-     new_buildings_stop_(false),
-     resource_necessity_territory_(100),
-     resource_necessity_mines_(100),
-     resource_necessity_water_(0),
      resource_necessity_water_needed_(false),
      highest_nonmil_prio_(0),
      seafaring_economy(false),
@@ -249,6 +244,8 @@
 
 	const int32_t delay_time = gametime - taskPool.front().due_time;
 
+	Map& map = game().map();
+
 	// Here we decide how many jobs will be run now (none - 5)
 	// in case no job is due now, it can be zero
 	uint32_t jobs_to_run_count = (delay_time < 0) ? 0 : 1;
@@ -411,9 +408,11 @@
 			break;
 		case SchedulerTaskId::kCheckMilitarysites:
 			// just to be sure the value is reset
-			// 4 seconds is really fine
-			set_taskpool_task_time(gametime + 4 * 1000, SchedulerTaskId::kCheckMilitarysites);
-			check_militarysites(gametime);
+			if (check_militarysites(gametime)) {
+				set_taskpool_task_time(gametime + 15 * 1000, SchedulerTaskId::kCheckMilitarysites);
+			} else {
+				set_taskpool_task_time(gametime + 4 * 1000, SchedulerTaskId::kCheckMilitarysites);
+			}
 			break;
 		case SchedulerTaskId::kCheckTrainingsites:
 			set_taskpool_task_time(
@@ -422,7 +421,7 @@
 			break;
 		case SchedulerTaskId::kCountMilitaryVacant:
 			count_military_vacant_positions();
-			set_taskpool_task_time(gametime + 45 * 1000, SchedulerTaskId::kCountMilitaryVacant);
+			set_taskpool_task_time(gametime + 25 * 1000, SchedulerTaskId::kCountMilitaryVacant);
 			break;
 		case SchedulerTaskId::kWareReview:
 			if (check_economies()) {  // economies must be consistent
@@ -435,13 +434,38 @@
 			if (check_economies()) {  // economies must be consistent
 				return;
 			}
-			set_taskpool_task_time(gametime + 30 * 60 * 1000, SchedulerTaskId::kPrintStats);
-			print_stats();
+			set_taskpool_task_time(gametime + 10 * 60 * 1000, SchedulerTaskId::kPrintStats);
+			print_stats(gametime);
 			break;
 		case SchedulerTaskId::kCheckEnemySites:
 			check_enemy_sites(gametime);
 			set_taskpool_task_time(gametime + 19 * 1000, SchedulerTaskId::kCheckEnemySites);
 			break;
+		case SchedulerTaskId::kManagementUpdate:
+			// This task is used for training AI, so should be disabled usually
+			{  // statistics for spotted warehouses
+				uint16_t conquered_wh = 0;
+				for (auto coords : enemy_warehouses) {
+					if (get_land_owner(map, coords) == player_number()) {
+						conquered_wh += 1;
+					}
+				};
+				if (!enemy_warehouses.empty())
+					log("Conquered warehouses: %d / %lu\n", conquered_wh, enemy_warehouses.size());
+				management_data.review(gametime, player_number(),
+				                       player_statistics.get_player_land(player_number()),
+				                       player_statistics.get_enemies_max_land(),
+				                       player_statistics.get_old60_player_land(player_number()),
+				                       attackers_count_, soldier_trained_log.count(gametime),
+				                       soldier_attacks_log.count(gametime), conquered_wh);
+				set_taskpool_task_time(
+				   gametime + kManagementUpdateInterval, SchedulerTaskId::kManagementUpdate);
+			}
+			break;
+		case SchedulerTaskId::kUpdateStats:
+			update_player_stat(gametime);
+			set_taskpool_task_time(gametime + kStatUpdateInterval, SchedulerTaskId::kUpdateStats);
+			break;
 		case SchedulerTaskId::kUnset:
 			NEVER_HERE();
 		}
@@ -475,6 +499,96 @@
 	const DescriptionIndex& nr_buildings = game().tribes().nrbuildings();
 
 	// Collect information about the different buildings that our tribe can have
+	// parsing based on name is not optimal though
+	// the logic below will quarantee that one and only one such building will be
+	// identified
+	bool barracks_identified = false;
+	bool bakery_identified = false;
+	bool sawmill_identified = false;
+
+	// The data struct below is owned by Player object, the purpose is to have them saved therein
+	persistent_data = player_->get_mutable_ai_persistent_state();
+	management_data.pd = player_->get_mutable_ai_persistent_state();
+	const bool create_basic_buildings_list = (gametime < kRemainingBasicBuildingsResetTime);
+
+	if (persistent_data->initialized == kFalse) {
+		// As all data are initialized without given values, they must be populated with reasonable
+		// values first
+		persistent_data->colony_scan_area = kColonyScanStartArea;
+		persistent_data->trees_around_cutters = 0;
+		persistent_data->initialized = kTrue;
+		persistent_data->last_attacked_player = std::numeric_limits<int16_t>::max();
+		persistent_data->expedition_start_time = kNoExpedition;
+		persistent_data->ships_utilization = 200;
+		persistent_data->no_more_expeditions = kFalse;
+		persistent_data->target_military_score = 100;
+		persistent_data->least_military_score = 0;
+		persistent_data->ai_productionsites_ratio = std::rand() % 5 + 7;
+		persistent_data->ai_personality_wood_difference = std::rand() % 40 - 20;
+		persistent_data->ai_personality_mil_upper_limit = 100;
+
+		// all zeroes
+		assert(persistent_data->neuron_weights.size() == 0);
+		assert(persistent_data->neuron_functs.size() == 0);
+		assert(persistent_data->magic_numbers_size == 0);
+		assert(persistent_data->neuron_pool_size == 0);
+		assert(persistent_data->magic_numbers.size() == 0);
+
+		// AI's DNA population
+		management_data.new_dna_for_persistent(player_number(), type_);
+		management_data.copy_persistent_to_local(player_number());
+		management_data.mutate(player_number());
+		management_data.dump_data();
+
+		management_data.test_consistency(true);
+		assert(management_data.get_military_number_at(42) ==
+		       management_data.get_military_number_at(MutationRatePosition));
+
+	} else if (persistent_data->initialized == kTrue) {
+		// Doing some consistency checks
+		check_range<uint32_t>(
+		   persistent_data->expedition_start_time, gametime, "expedition_start_time");
+		check_range<uint16_t>(persistent_data->ships_utilization, 0, 10000, "ships_utilization_");
+
+		// for backward consistency
+		if (persistent_data->ai_personality_mil_upper_limit <
+		    persistent_data->target_military_score) {
+			persistent_data->ai_personality_mil_upper_limit = persistent_data->target_military_score;
+		}
+		if (persistent_data->least_military_score > persistent_data->target_military_score) {
+			persistent_data->least_military_score = persistent_data->target_military_score;
+		}
+
+		if (kAITrainingMode) {
+			log("%2d: reinitializing dna (kAITrainingMode set true)", player_number());
+			management_data.new_dna_for_persistent(player_number(), type_);
+			management_data.copy_persistent_to_local(player_number());
+			management_data.mutate(player_number());
+
+		} else {
+			management_data.copy_persistent_to_local(player_number());
+		}
+
+		management_data.test_consistency(true);
+		management_data.dump_data();
+
+		log(" %2d: %lu basic buildings in savegame file. %s\n", player_number(),
+		    persistent_data->remaining_basic_buildings.size(),
+		    (create_basic_buildings_list) ?
+		       "New list will be recreated though (kAITrainingMode is true)" :
+		       "");
+
+	} else {
+		throw wexception("Corrupted AI data");
+	}
+
+	// Even if we have basic building from savefile, we ignore them and recreate based
+	// on lua conf file
+	if (create_basic_buildings_list) {
+		persistent_data->remaining_basic_buildings.clear();
+		persistent_data->remaining_buildings_size = 0;
+	}
+
 	for (DescriptionIndex building_index = 0; building_index < nr_buildings; ++building_index) {
 		const BuildingDescr& bld = *tribe_->get_building_descr(building_index);
 		if (!tribe_->has_building(building_index) && bld.type() != MapObjectType::MILITARYSITE) {
@@ -494,7 +608,7 @@
 		bo.cnt_target = 1;  // default for everything
 		bo.cnt_limit_by_aimode = std::numeric_limits<int32_t>::max();
 		bo.cnt_upgrade_pending = 0;
-		bo.stocklevel = 0;
+		bo.stocklevel_count = 0;
 		bo.stocklevel_time = 0;
 		bo.last_dismantle_time = 0;
 		// this is set to negative number, otherwise the AI would wait 25 sec
@@ -508,23 +622,40 @@
 		bo.unconnected_count = 0;
 		bo.new_building_overdue = 0;
 		bo.primary_priority = 0;
-		bo.is_buildable = bld.is_buildable();
-		bo.need_trees = bh.is_logproducer();
-		bo.need_rocks = bh.is_graniteproducer();
-		bo.need_water = bh.get_needs_water();
-		bo.mines_water = bh.mines_water();
-		bo.recruitment = bh.for_recruitment();
-		bo.space_consumer = bh.is_space_consumer();
+		if (bld.is_buildable()) {
+			bo.is_what.insert(BuildingAttribute::kBuildable);
+		}
+		if (bh.is_logproducer()) {
+			bo.is_what.insert(BuildingAttribute::kLumberjack);
+		}
+		if (bh.is_graniteproducer()) {
+			bo.set_is(BuildingAttribute::kNeedsRocks);
+		}
+		if (create_basic_buildings_list && bh.get_is_basic() > 0) {  // This is very begining of game
+			persistent_data->remaining_basic_buildings[bo.id] = bh.get_is_basic();
+			persistent_data->remaining_buildings_size += 1;
+		}
+		if (bh.get_needs_water()) {
+			bo.set_is(BuildingAttribute::kNeedsCoast);
+		}
+		if (bh.mines_water()) {
+			bo.set_is(BuildingAttribute::kWell);
+		}
+		if (bh.is_space_consumer()) {
+			bo.set_is(BuildingAttribute::kSpaceConsumer);
+		}
+		if (bh.for_recruitment()) {
+			bo.set_is(BuildingAttribute::kRecruitment);
+		}
 		bo.expansion_type = bh.is_expansion_type();
 		bo.fighting_type = bh.is_fighting_type();
 		bo.mountain_conqueror = bh.is_mountain_conqueror();
 		bo.prohibited_till = bh.get_prohibited_till() * 1000;  // value in conf is in seconds
 		bo.forced_after = bh.get_forced_after() * 1000;        // value in conf is in seconds
-		bo.is_port = bld.get_isport();
-		bo.trainingsite_type = TrainingSiteType::kNoTS;
-		bo.upgrade_substitutes = false;
-		bo.upgrade_extends = false;
-		bo.produces_building_material = false;
+		if (bld.get_isport()) {
+			bo.is_what.insert(BuildingAttribute::kPort);
+		}
+		bo.max_ts_proportion = 100;
 		bo.max_preciousness = 0;
 		bo.max_needed_preciousness = 0;
 
@@ -534,18 +665,16 @@
 
 		// I just presume cut wood is named "log" in the game
 		if (tribe_->safe_ware_index("log") == bo.production_hint) {
-			bo.plants_trees = true;
-		} else {
-			bo.plants_trees = false;
+			bo.set_is(BuildingAttribute::kRanger);
 		}
 
 		// Is total count of this building limited by AI mode?
-		if (type_ == DefaultAI::Type::kVeryWeak && bh.get_very_weak_ai_limit() >= 0) {
+		if (type_ == Widelands::AiType::kVeryWeak && bh.get_very_weak_ai_limit() >= 0) {
 			bo.cnt_limit_by_aimode = bh.get_very_weak_ai_limit();
 			log(" %d: AI 'very weak' mode: applying limit %d building(s) for %s\n", player_number(),
 			    bo.cnt_limit_by_aimode, bo.name);
 		}
-		if (type_ == DefaultAI::Type::kWeak && bh.get_weak_ai_limit() >= 0) {
+		if (type_ == Widelands::AiType::kWeak && bh.get_weak_ai_limit() >= 0) {
 			bo.cnt_limit_by_aimode = bh.get_weak_ai_limit();
 			log(" %d: AI 'weak' mode: applying limit %d building(s) for %s\n", player_number(),
 			    bo.cnt_limit_by_aimode, bo.name);
@@ -562,6 +691,9 @@
 			for (const DescriptionIndex& temp_output : prod.output_ware_types()) {
 				bo.outputs.push_back(temp_output);
 			}
+			for (const auto temp_position : prod.working_positions()) {
+				bo.positions.push_back(temp_position.first);
+			}
 
 			if (bo.type == BuildingObserver::Type::kMine) {
 				// get the resource needed by the mine
@@ -575,23 +707,51 @@
 				if (mines_per_type.count(bo.mines) == 0) {
 					mines_per_type[bo.mines] = MineTypesObserver();
 				}
+				if (!strcmp(bh.get_mines(), "iron")) {
+					iron_ore_id = bo.mines;
+					bo.set_is(BuildingAttribute::kIronMine);
+					mines_per_type[bo.mines].is_critical = true;
+				}
 			}
 
 			// here we identify hunters
 			if (bo.outputs.size() == 1 && tribe_->safe_ware_index("meat") == bo.outputs.at(0)) {
-				bo.is_hunter = true;
-			} else {
-				bo.is_hunter = false;
+				bo.set_is(BuildingAttribute::kHunter);
 			}
 
 			// and fishers
 			if (bo.outputs.size() == 1 && tribe_->safe_ware_index("fish") == bo.outputs.at(0)) {
-				bo.is_fisher = true;
-			} else {
-				bo.is_fisher = false;
-			}
-
-			bo.is_shipyard = bh.is_shipyard();
+				bo.set_is(BuildingAttribute::kFisher);
+			}
+
+			// is it barracks - finding out by name
+			if (building_name.find("barracks") != std::string::npos) {
+				// there can be only one building type identified as barracks
+				assert(!barracks_identified);
+				bo.set_is(BuildingAttribute::kBarracks);
+				barracks_identified = true;
+			}
+
+			// is it bakery - finding out by name
+			if (building_name.find("bakery") != std::string::npos) {
+				// there can be only one building type identified as barracks
+				assert(!bakery_identified);
+				bo.is_what.insert(BuildingAttribute::kBakery);
+				bakery_identified = true;
+			}
+
+			// is it sawmill - finding out by name
+			if (building_name.find("saw") != std::string::npos ||
+			    building_name.find("hardener") != std::string::npos) {
+				// there can be only one building type identified as sawmill
+				assert(!sawmill_identified);
+				bo.is_what.insert(BuildingAttribute::kSaw);
+				sawmill_identified = true;
+			}
+
+			if (bh.is_shipyard()) {
+				bo.set_is(BuildingAttribute::kShipyard);
+			}
 
 			// now we find out if the upgrade of the building is a full substitution
 			// (produces all wares as current one)
@@ -607,10 +767,10 @@
 				}
 				// now testing outputs of current building
 				// and comparing
-				bo.upgrade_substitutes = true;
+				bo.set_is(BuildingAttribute::kUpgradeSubstitutes);
 				for (DescriptionIndex ware : bo.outputs) {
 					if (enh_outputs.count(ware) == 0) {
-						bo.upgrade_substitutes = false;
+						bo.unset_is(BuildingAttribute::kUpgradeSubstitutes);
 						break;
 					}
 				}
@@ -620,24 +780,47 @@
 				for (const DescriptionIndex& ware : bo.outputs) {
 					cur_outputs.insert(ware);
 				}
-				bo.upgrade_extends = false;
+				// bo.set_is(kUpgradeExtends)lse;
 				for (DescriptionIndex ware : enh_outputs) {
 					if (cur_outputs.count(ware) == 0) {
-						bo.upgrade_extends = true;
+						bo.set_is(BuildingAttribute::kUpgradeExtends);
 						break;
 					}
 				}
 			}
 
 			// now we identify producers of critical build materials
-			// hardwood now
 			for (DescriptionIndex ware : bo.outputs) {
 				// iterating over wares subsitutes
 				if (tribe_->ware_index("wood") == ware || tribe_->ware_index("blackwood") == ware ||
-				    tribe_->ware_index("marble") == ware || tribe_->ware_index("planks") == ware) {
-					bo.produces_building_material = true;
-				}
-			}
+				    tribe_->ware_index("marble") == ware || tribe_->ware_index("planks") == ware ||
+				    tribe_->ware_index("diamond") == ware || tribe_->ware_index("quartz") == ware ||
+				    tribe_->ware_index("marble_column") == ware ||
+				    tribe_->ware_index("cloth") == ware || tribe_->ware_index("spidercloth") == ware ||
+				    tribe_->ware_index("gold") == ware || tribe_->ware_index("grout") == ware) {
+					bo.set_is(BuildingAttribute::kBuildingMatProducer);
+					if (bo.type == BuildingObserver::Type::kMine) {
+						has_critical_mines = true;
+						mines_per_type[bo.mines].is_critical = true;
+					}
+				}
+			}
+
+			for (const auto& temp_buildcosts : prod.buildcost()) {
+				// bellow are non-critical wares (well, various types of wood)
+				if (tribe_->ware_index("blackwood") == temp_buildcosts.first ||
+				    tribe_->ware_index("planks") == temp_buildcosts.first ||
+				    tribe_->ware_index("marble") == temp_buildcosts.first ||
+				    tribe_->ware_index("marble_column") == temp_buildcosts.first ||
+				    tribe_->ware_index("quartz") == temp_buildcosts.first ||
+				    tribe_->ware_index("diamond") == temp_buildcosts.first ||
+				    tribe_->ware_index("cloth") == temp_buildcosts.first ||
+				    tribe_->ware_index("grout") == temp_buildcosts.first ||
+				    tribe_->ware_index("gold") == temp_buildcosts.first) {
+					bo.critical_building_material.push_back(temp_buildcosts.first);
+				}
+			}
+
 			continue;
 		}
 
@@ -650,7 +833,6 @@
 			for (const auto& temp_buildcosts : milit.buildcost()) {
 				// bellow are non-critical wares (well, various types of wood)
 				if (tribe_->ware_index("log") == temp_buildcosts.first ||
-				    tribe_->ware_index("blackwood") == temp_buildcosts.first ||
 				    tribe_->ware_index("planks") == temp_buildcosts.first)
 					continue;
 
@@ -666,6 +848,8 @@
 
 		if (bld.type() == MapObjectType::TRAININGSITE) {
 			bo.type = BuildingObserver::Type::kTrainingsite;
+			bo.max_ts_proportion = bh.trainingsites_max_percent();
+			assert(bo.max_ts_proportion <= 100);
 			const TrainingSiteDescr& train = dynamic_cast<const TrainingSiteDescr&>(bld);
 			for (const auto& temp_input : train.input_wares()) {
 				bo.inputs.push_back(temp_input.first);
@@ -682,15 +866,13 @@
 					// critical wares for trainingsites
 					if (tribe_->ware_index("spidercloth") == temp_buildcosts.first ||
 					    tribe_->ware_index("gold") == temp_buildcosts.first ||
-					    tribe_->ware_index("grout") == temp_buildcosts.first) {
+					    tribe_->ware_index("grout") == temp_buildcosts.first ||
+					    tribe_->ware_index("marble_column") == temp_buildcosts.first ||
+					    tribe_->ware_index("cloth") == temp_buildcosts.first) {
 						bo.critical_building_material.push_back(temp_buildcosts.first);
 					}
 				}
 			}
-			bo.trainingsite_type = bh.get_trainingsite_type();
-			// it would behave badly if no type was set
-			// make sure all TS have its type set properly in conf files
-			assert(bo.trainingsite_type != TrainingSiteType::kNoTS);
 			continue;
 		}
 
@@ -700,6 +882,10 @@
 		}
 	}
 
+	assert(barracks_identified);  // Barracks were identified
+	assert(bakery_identified);    // Bakery was identified
+	assert(sawmill_identified);   // Sawmill was identified
+
 	// atlanteans they consider water as a resource
 	// (together with mines, rocks and wood)
 	if (tribe_->name() == "atlanteans") {
@@ -743,13 +929,21 @@
 	                                 "check unbuildable fields"));
 	taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 15 * 60 * 1000),
 	                                 SchedulerTaskId::kWareReview, 9, "wares review"));
-	taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 30 * 60 * 1000),
+	taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 10 * 60 * 1000),
 	                                 SchedulerTaskId::kPrintStats, 9, "print statistics"));
 	taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 1 * 60 * 1000),
 	                                 SchedulerTaskId::kCountMilitaryVacant, 2,
 	                                 "count military vacant"));
 	taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 10 * 60 * 1000),
 	                                 SchedulerTaskId::kCheckEnemySites, 6, "check enemy sites"));
+	taskPool.push_back(SchedulerTask(
+	   std::max<uint32_t>(gametime, 10 * 1000), SchedulerTaskId::kManagementUpdate, 8, "reviewing"));
+	taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 9 * 1000),
+	                                 SchedulerTaskId::kUpdateStats, 6, "update player stats"));
+	if (kAITrainingMode) {
+		taskPool.push_back(SchedulerTask(
+		   std::max<uint32_t>(gametime, 10 * 1000), SchedulerTaskId::kUpdateStats, 15, "review"));
+	}
 
 	Map& map = game().map();
 
@@ -821,7 +1015,8 @@
 
 	// blocking space consumers vicinity (when reloading a game)
 	for (const ProductionSiteObserver& ps_obs : productionsites) {
-		if (ps_obs.bo->space_consumer && !ps_obs.bo->plants_trees) {
+		if (ps_obs.bo->is(BuildingAttribute::kSpaceConsumer) &&
+		    !ps_obs.bo->is(BuildingAttribute::kRanger)) {
 			MapRegion<Area<FCoords>> mr(
 			   map, Area<FCoords>(map.get_fcoords(ps_obs.site->get_position()), 4));
 			do {
@@ -830,45 +1025,19 @@
 		}
 	}
 
-	// The data struct below is owned by Player object, the purpose is to have them saved therein
-	persistent_data = player_->get_mutable_ai_persistent_state();
-
-	if (persistent_data->initialized == kFalse) {
-		// As all data are initialized without given values, they must be populated with reasonable
-		// values first
-		persistent_data->colony_scan_area = kColonyScanStartArea;
-		persistent_data->trees_around_cutters = 0;
-		persistent_data->initialized = kTrue;
-		persistent_data->last_attacked_player = std::numeric_limits<int16_t>::max();
-		persistent_data->expedition_start_time = kNoExpedition;
-		persistent_data->ships_utilization = 200;
-		persistent_data->no_more_expeditions = kFalse;
-		persistent_data->target_military_score = 0;
-		persistent_data->least_military_score = 100;
-		persistent_data->ai_personality_military_loneliness = std::rand() % 5 * 30 - 60;
-		persistent_data->ai_personality_attack_margin = std::max(std::rand() % 20 - 5, 0);
-		persistent_data->ai_productionsites_ratio = std::rand() % 5 + 7;
-		persistent_data->ai_personality_wood_difference = std::rand() % 40 - 20;
-		persistent_data->ai_personality_early_militarysites = std::rand() % 20 + 20;
-		persistent_data->last_soldier_trained = kNever;
-	} else if (persistent_data->initialized == kTrue) {
-		// Doing some consistency checks
-		check_range<uint32_t>(
-		   persistent_data->expedition_start_time, gametime, "expedition_start_time");
-		check_range<uint16_t>(persistent_data->ships_utilization, 0, 10000, "ships_utilization_");
-		check_range<int16_t>(persistent_data->ai_personality_military_loneliness, -60, 60,
-		                     "ai_personality_military_loneliness");
-		check_range<int32_t>(
-		   persistent_data->ai_personality_attack_margin, 15, "ai_personality_attack_margin");
-		check_range<uint32_t>(
-		   persistent_data->ai_productionsites_ratio, 5, 15, "ai_productionsites_ratio");
-		check_range<int32_t>(persistent_data->ai_personality_wood_difference, -20, 19,
-		                     "ai_personality_wood_difference");
-		check_range<uint32_t>(persistent_data->ai_personality_early_militarysites, 20, 40,
-		                      "ai_personality_early_militarysites");
-	} else {
-		throw wexception("Corrupted AI data");
+	// printing identified basic buildings if we are in the basic economy mode
+	basic_economy_established = persistent_data->remaining_basic_buildings.empty();
+	if (!basic_economy_established) {
+		log("%2d: Initializing in the basic economy mode, required buildings:\n", player_number());
+		for (auto bb : persistent_data->remaining_basic_buildings) {
+			log("   %3d / %-25s,  target %d\n", bb.first, get_building_observer(bb.first).name,
+			    bb.second);
+		}
 	}
+	assert(persistent_data->remaining_basic_buildings.size() ==
+	       persistent_data->remaining_buildings_size);
+
+	update_player_stat(gametime);
 
 	// Initialise the max duration of a single ship's expedition
 	const uint32_t map_area = uint32_t(map.get_height()) * map.get_width();
@@ -893,6 +1062,21 @@
 		// Current gametime is better then 'kNoExpedition'
 		persistent_data->expedition_start_time = gametime;
 	}
+
+	// just to be sure we have iron mines identified
+	assert(iron_ore_id >= 0);
+
+	productionsites_ratio_ = management_data.get_military_number_at(86) / 10 + 12;
+
+	// Just to be initialized
+	soldier_status_ = SoldiersStatus::kEnough;
+	vacant_mil_positions_average_ = 0;
+
+	spots_avail.resize(4);
+
+	trees_nearby_treshold_ = 3 + std::abs(management_data.get_military_number_at(121)) / 2;
+
+	last_road_dismantled_ = 0;
 }
 
 /**
@@ -1022,27 +1206,65 @@
 }
 
 /// Updates one buildable field
-void DefaultAI::update_buildable_field(BuildableField& field, uint16_t range, bool military) {
+void DefaultAI::update_buildable_field(BuildableField& field) {
 	// look if there is any unowned land nearby
 	Map& map = game().map();
 	const uint32_t gametime = game().get_gametime();
 	FindNodeUnownedWalkable find_unowned_walkable(player_, game());
+	FindEnemyNodeWalkable find_enemy_owned_walkable(player_, game());
+	FindNodeUnownedBuildable find_unowned_buildable(player_, game());
 	FindNodeUnownedMineable find_unowned_mines_pots(player_, game());
+	FindNodeUnownedMineable find_unowned_iron_mines(player_, game(), iron_ore_id);
+	FindNodeAllyOwned find_ally(player_, game(), player_number());
 	PlayerNumber const pn = player_->player_number();
 	const World& world = game().world();
-	field.unowned_land_nearby =
-	   map.find_fields(Area<FCoords>(field.coords, range), nullptr, find_unowned_walkable);
-	FindNodeAllyOwned find_ally(player_, game(), player_number());
-	const int32_t AllyOwnedFields =
-	   map.find_fields(Area<FCoords>(field.coords, 3), nullptr, find_ally);
-
-	field.near_border = false;
-	if (AllyOwnedFields > 0) {
+
+	const uint16_t production_area = 6;
+	const uint16_t buildable_spots_check_area = 10;
+	const uint16_t enemy_check_area = 16;
+	const uint16_t ms_enemy_check_area =
+	   enemy_check_area + std::abs(management_data.get_military_number_at(75)) / 10;
+	;
+	const uint16_t distant_resources_area = 20;
+
+	uint16_t actual_enemy_check_area = enemy_check_area;
+	field.is_militarysite = false;
+	if (upcast(MilitarySite, ms, field.coords.field->get_immovable())) {
+		field.is_militarysite = true;
+		actual_enemy_check_area = ms_enemy_check_area;
+	}
+
+	field.unowned_land_nearby = map.find_fields(
+	   Area<FCoords>(field.coords, actual_enemy_check_area), nullptr, find_unowned_walkable);
+
+	field.enemy_owned_land_nearby = map.find_fields(
+	   Area<FCoords>(field.coords, actual_enemy_check_area), nullptr, find_enemy_owned_walkable);
+
+	field.nearest_buildable_spot_nearby = std::numeric_limits<uint16_t>::max();
+	if (field.unowned_land_nearby > 0) {
+		std::vector<Coords> found_buildable_fields;
+
+		field.unowned_buildable_spots_nearby =
+		   map.find_fields(Area<FCoords>(field.coords, buildable_spots_check_area),
+		                   &found_buildable_fields, find_unowned_buildable);
+		// Now iterate over fields to get nearest one
+		for (auto& coords : found_buildable_fields) {
+			const uint32_t cur_distance = map.calc_distance(coords, field.coords);
+			if (cur_distance < field.nearest_buildable_spot_nearby) {
+				field.nearest_buildable_spot_nearby = cur_distance;
+			}
+		}
+
+	} else {
+		field.unowned_buildable_spots_nearby = 0;
+	}
+
+	// Is this near border  // get rid of allyownedfields
+	if (map.find_fields(Area<FCoords>(field.coords, 3), nullptr, find_ally) ||
+	    map.find_fields(Area<FCoords>(field.coords, 3), nullptr, find_unowned_walkable)) {
 		field.near_border = true;
-	} else if (field.unowned_land_nearby > 0) {
-		if (map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned_walkable) > 0) {
-			field.near_border = true;
-		}
+	} else {
+		field.near_border = false;
 	}
 
 	// are we going to count resources now?
@@ -1054,26 +1276,39 @@
 		field.last_resources_check_time = gametime;
 	}
 
-	// to save some CPU
-	if (mines_.size() > 8 && !resource_count_now) {
-		field.unowned_mines_spots_nearby = 0;
-	} else {
-		uint32_t close_mines =
-		   map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned_mines_pots);
+	// testing mines
+	if (resource_count_now) {
+		uint32_t close_mines = map.find_fields(
+		   Area<FCoords>(field.coords, production_area), nullptr, find_unowned_mines_pots);
 		uint32_t distant_mines =
-		   map.find_fields(Area<FCoords>(field.coords, (range + 6 < 14) ? 14 : range + 6), nullptr,
+		   map.find_fields(Area<FCoords>(field.coords, distant_resources_area), nullptr,
+
 		                   find_unowned_mines_pots);
 		distant_mines = distant_mines - close_mines;
 		field.unowned_mines_spots_nearby = 4 * close_mines + distant_mines / 2;
 		if (distant_mines > 0) {
 			field.unowned_mines_spots_nearby += 15;
 		}
+		if (field.unowned_mines_spots_nearby > 0 &&
+		    // for performance considerations we count iron nodes only if we have less than 2 iron
+		    // mines now...
+		    (mines_per_type[iron_ore_id].in_construction + mines_per_type[iron_ore_id].finished) <=
+		       1) {
+			// counting iron mines, if we have less than two iron mines
+			field.unowned_iron_mines_nearby = map.find_fields(
+			   Area<FCoords>(field.coords, distant_resources_area), nullptr, find_unowned_iron_mines);
+		} else {
+			field.unowned_iron_mines_nearby = 0;
+		}
 	}
 
 	// identifying portspace fields
-	if (!field.is_portspace) {  // if we know it, no need to do it once more
+	if (field.is_portspace ==
+	    Widelands::ExtendedBool::kUnset) {  // if we know it, no need to do it once more
 		if (player_->get_buildcaps(field.coords) & BUILDCAPS_PORT) {
-			field.is_portspace = true;
+			field.is_portspace = ExtendedBool::kTrue;
+		} else {
+			field.is_portspace = ExtendedBool::kFalse;
 		}
 	}
 
@@ -1090,15 +1325,20 @@
 	}
 
 	// testing if a port is nearby, such field will get a priority boost
-	uint16_t nearest_distance = std::numeric_limits<uint16_t>::max();
-	for (const WarehouseSiteObserver& wh_obs : warehousesites) {
-		const uint16_t actual_distance = map.calc_distance(field.coords, wh_obs.site->get_position());
-		nearest_distance = std::min(nearest_distance, actual_distance);
-	}
-	if (nearest_distance < 15) {
-		field.port_nearby = true;
-	} else {
-		field.port_nearby = false;
+	if (resource_count_now) {  // misusing a bit
+		uint16_t nearest_distance = std::numeric_limits<uint16_t>::max();
+		for (const WarehouseSiteObserver& wh_obs : warehousesites) {
+			if (wh_obs.bo->is_what.count(BuildingAttribute::kPort)) {
+				const uint16_t actual_distance =
+				   map.calc_distance(field.coords, wh_obs.site->get_position());
+				nearest_distance = std::min(nearest_distance, actual_distance);
+			}
+		}
+		if (nearest_distance < 15) {
+			field.port_nearby = true;
+		} else {
+			field.port_nearby = false;
+		}
 	}
 
 	// testing fields in radius 1 to find biggest buildcaps.
@@ -1115,158 +1355,194 @@
 
 	assert((player_->get_buildcaps(field.coords) & BUILDCAPS_SIZEMASK) <= field.max_buildcap_nearby);
 
-	// collect information about resources in the area
-	std::vector<ImmovableFound> immovables;
-	// Search in a radius of range
-	map.find_immovables(Area<FCoords>(field.coords, range), &immovables);
-
-	// Is this a general update or just for military consideration
-	// (second is used in check_militarysites)
-	if (!military) {
+	// Testing surface water (once only)
+	if (field.water_nearby == kUncalculated) {
+		assert(field.open_water_nearby == kUncalculated);
+
+		FindNodeWater find_water(game().world());
+		field.water_nearby =
+		   map.find_fields(Area<FCoords>(field.coords, production_area), nullptr, find_water);
+
+		if (field.water_nearby > 0) {
+			FindNodeOpenWater find_open_water(game().world());
+			field.open_water_nearby =
+			   map.find_fields(Area<FCoords>(field.coords, production_area), nullptr, find_open_water);
+		}
+
+		if (resource_necessity_water_needed_) {  // for atlanteans
+			field.distant_water = map.find_fields(Area<FCoords>(field.coords, distant_resources_area),
+			                                      nullptr, find_water) -
+			                      field.water_nearby;
+			assert(field.open_water_nearby <= field.water_nearby);
+		}
+	}
+
+	// counting fields with fish
+	if (field.water_nearby > 0 && (field.fish_nearby == kUncalculated || resource_count_now)) {
+		field.fish_nearby = map.find_fields(Area<FCoords>(field.coords, production_area), nullptr,
+		                                    FindNodeResource(world.get_resource("fish")));
+		;
+	}
+
+	// counting fields with critters (game)
+	// not doing this always, this does not change fast
+	if (resource_count_now) {
+
+		field.critters_nearby =
+		   map.find_bobs(Area<FCoords>(field.coords, production_area), nullptr, FindBobCritter());
+		;
+	}
+
+	FCoords fse;
+	map.get_neighbour(field.coords, WALK_SE, &fse);
+	field.preferred = false;
+	if (BaseImmovable const* const imm = fse.field->get_immovable()) {
+		if (dynamic_cast<Flag const*>(imm) ||
+		    (dynamic_cast<Road const*>(imm) && (fse.field->nodecaps() & BUILDCAPS_FLAG))) {
+			field.preferred = true;
+		}
+	}
+
+	// Rocks are not renewable, we will count them only if previous state is nonzero
+	if (field.rocks_nearby > 0 && resource_count_now) {
+
+		field.rocks_nearby =
+		   map.find_immovables(Area<FCoords>(map.get_fcoords(field.coords), production_area), nullptr,
+		                       FindImmovableAttribute(MapObjectDescr::get_attribute_id("rocks")));
+
+		// adding 5 if rocks found
+		field.rocks_nearby = (field.rocks_nearby > 0) ? field.rocks_nearby + 2 : 0;
+	}
+
+	// ground water is not renewable and its amount can only fall, we will count them only if
+	// previous state is nonzero
+	if (field.ground_water > 0 && resource_count_now) {
+		field.ground_water = field.coords.field->get_resources_amount();
+	}
+
+	// Counting trees nearby
+	if (resource_count_now) {
 		int32_t const tree_attr = MapObjectDescr::get_attribute_id("tree");
-		field.preferred = false;
-		field.enemy_nearby = false;
-		field.area_military_capacity = 0;
-		field.military_loneliness = 1000;  // instead of floats(v-
-		field.area_military_presence = 0;
-		field.military_stationed = 0;
-		field.unconnected_nearby = false;
-		field.trees_nearby = 0;
-		field.space_consumers_nearby = 0;
-		field.rangers_nearby = 0;
-		field.producers_nearby.clear();
-		field.producers_nearby.resize(wares.size());
-		field.consumers_nearby.clear();
-		field.consumers_nearby.resize(wares.size());
-		field.supporters_nearby.clear();
-		field.supporters_nearby.resize(wares.size());
-		std::vector<Coords> resource_list;
-		std::vector<Bob*> critters_list;
-
-		if (field.water_nearby == kUncalculated) {
-			assert(field.open_water_nearby == kUncalculated);
-
-			FindNodeWater find_water(game().world());
-			field.water_nearby = map.find_fields(Area<FCoords>(field.coords, 5), nullptr, find_water);
-
-			if (field.water_nearby > 0) {
-				FindNodeOpenWater find_open_water(game().world());
-				field.open_water_nearby =
-				   map.find_fields(Area<FCoords>(field.coords, 5), nullptr, find_open_water);
-			}
-
-			if (resource_necessity_water_needed_) {  // for atlanteans
-				field.distant_water =
-				   map.find_fields(Area<FCoords>(field.coords, 14), nullptr, find_water) -
-				   field.water_nearby;
-				assert(field.open_water_nearby <= field.water_nearby);
-			}
-		}
-
-		// counting fields with fish
-		if (field.water_nearby > 0 && (field.fish_nearby == kUncalculated || resource_count_now)) {
-			map.find_fields(Area<FCoords>(field.coords, 6), &resource_list,
-			                FindNodeResource(world.get_resource("fish")));
-			field.fish_nearby = resource_list.size();
-		}
-
-		// counting fields with critters (game)
-		// not doing this always, this does not change fast
-		if (resource_count_now) {
-			map.find_bobs(Area<FCoords>(field.coords, 6), &critters_list, FindBobCritter());
-			field.critters_nearby = critters_list.size();
-		}
-
-		FCoords fse;
-		map.get_neighbour(field.coords, WALK_SE, &fse);
-
-		if (BaseImmovable const* const imm = fse.field->get_immovable()) {
-			if (dynamic_cast<Flag const*>(imm) ||
-			    (dynamic_cast<Road const*>(imm) && (fse.field->nodecaps() & BUILDCAPS_FLAG))) {
-				field.preferred = true;
-			}
-		}
-
-		for (uint32_t i = 0; i < immovables.size(); ++i) {
-			const BaseImmovable& base_immovable = *immovables.at(i).object;
-
-			if (upcast(PlayerImmovable const, player_immovable, &base_immovable)) {
-
-				// TODO(unknown): Only continue; if this is an opposing site
-				// allied sites should be counted for military influence
-				if (player_immovable->owner().player_number() != pn) {
-					if (player_->is_hostile(player_immovable->owner())) {
-						field.enemy_nearby = true;
-					}
-
-					continue;
-				}
-				// here we identify the buiding (including expected building if constructionsite)
-				// and calculate some statistics about nearby buildings
-				if (upcast(ProductionSite const, productionsite, player_immovable)) {
-					BuildingObserver& bo = get_building_observer(productionsite->descr().name().c_str());
-					consider_productionsite_influence(field, immovables.at(i).coords, bo);
-				}
-				if (upcast(ConstructionSite const, constructionsite, player_immovable)) {
-					const BuildingDescr& target_descr = constructionsite->building();
-					BuildingObserver& bo = get_building_observer(target_descr.name().c_str());
-					consider_productionsite_influence(field, immovables.at(i).coords, bo);
-				}
-			}
-
-			if (immovables.at(i).object->has_attribute(tree_attr)) {
-				++field.trees_nearby;
-			}
-		}
-
-		// Rocks are not renewable, we will count them only if previous state is nonzero
-		if (field.rocks_nearby > 0 && resource_count_now) {
-
-			field.rocks_nearby =
-			   map.find_immovables(Area<FCoords>(map.get_fcoords(field.coords), 6), nullptr,
-			                       FindImmovableAttribute(MapObjectDescr::get_attribute_id("rocks")));
-
-			// adding 10 if rocks found
-			field.rocks_nearby = (field.rocks_nearby > 0) ? field.rocks_nearby + 10 : 0;
-		}
-
-		// ground water is not renewable and its amount can only fall, we will count them only if
-		// previous state is nonzero
-		if (field.ground_water > 0) {
-			field.ground_water = field.coords.field->get_resources_amount();
-		}
-	}
-
-	// The following is done always (regardless of military or not)
-
-	// We get immovables with higher radius
-	immovables.clear();
-	map.find_immovables(Area<FCoords>(field.coords, (range < 11) ? 11 : range), &immovables);
+		field.trees_nearby =
+		   map.find_immovables(Area<FCoords>(map.get_fcoords(field.coords), production_area), nullptr,
+		                       FindImmovableAttribute(tree_attr));
+	}
+
+	// resetting some values
+	field.enemy_nearby =
+	   (field.enemy_owned_land_nearby > std::abs(management_data.get_military_number_at(41) / 4)) ?
+	      true :
+	      false;
+	if (field.enemy_owned_land_nearby == 0) {
+		assert(field.enemy_nearby == false);
+	}
+
+	// resetting bunch of values for the field
+	field.ally_military_presence = 0;
+	field.area_military_capacity = 0;
+	field.consumers_nearby.clear();
+	field.consumers_nearby.resize(wares.size());
+	field.enemy_military_presence = 0;
+	field.enemy_military_sites = 0;
+	field.enemy_wh_nearby = false;
+	field.military_in_constr_nearby = 0;
+	field.military_loneliness = 1000;
 	field.military_stationed = 0;
 	field.military_unstationed = 0;
-	field.military_in_constr_nearby = 0;
-	field.area_military_capacity = 0;
-	field.military_loneliness = 1000;
-	field.area_military_presence = 0;
+	field.own_military_presence = 0;
+	field.own_non_military_nearby = 0;
+	field.producers_nearby.clear();
+	field.producers_nearby.resize(wares.size());
+	field.rangers_nearby = 0;
+	field.space_consumers_nearby = 0;
+	field.supporters_nearby.clear();
+	field.supporters_nearby.resize(wares.size());
 	field.unconnected_nearby = false;
 
-	// We are interested in unconnected immovables, but we must be also close to connected ones
-	bool any_connected_imm = false;
-	bool any_unconnected_imm = false;
+	// collect information about productionsites nearby
+	std::vector<ImmovableFound> immovables;
+	// Search in a radius of range
+	map.find_immovables(Area<FCoords>(field.coords, production_area + 2), &immovables);
+
+	// functions seems to return duplicates, so we will use serial numbers to filter them out
+	std::set<uint32_t> unique_serials;
 
 	for (uint32_t i = 0; i < immovables.size(); ++i) {
-
 		const BaseImmovable& base_immovable = *immovables.at(i).object;
+		if (!unique_serials.insert(base_immovable.serial()).second) {
+			continue;  // serial was not inserted in the set, so this is duplicate
+		}
 
-		// testing if it is enemy-owned field
-		// TODO(unknown): count such fields...
 		if (upcast(PlayerImmovable const, player_immovable, &base_immovable)) {
 
 			// TODO(unknown): Only continue; if this is an opposing site
 			// allied sites should be counted for military influence
 			if (player_immovable->owner().player_number() != pn) {
-				if (player_->is_hostile(player_immovable->owner())) {
-					field.enemy_nearby = true;
+				continue;
+			}
+			// here we identify the buiding (including expected building if constructionsite)
+			// and calculate some statistics about nearby buildings
+			if (upcast(ProductionSite const, productionsite, player_immovable)) {
+				BuildingObserver& bo = get_building_observer(productionsite->descr().name().c_str());
+				consider_productionsite_influence(field, immovables.at(i).coords, bo);
+			}
+			if (upcast(ConstructionSite const, constructionsite, player_immovable)) {
+				const BuildingDescr& target_descr = constructionsite->building();
+				BuildingObserver& bo = get_building_observer(target_descr.name().c_str());
+				consider_productionsite_influence(field, immovables.at(i).coords, bo);
+			}
+		}
+	}
+
+	// Now testing military aspects
+	immovables.clear();
+	map.find_immovables(Area<FCoords>(field.coords, actual_enemy_check_area), &immovables);
+
+	// We are interested in unconnected immovables, but we must be also close to connected ones
+	bool any_connected_imm = false;
+	bool any_unconnected_imm = false;
+	unique_serials.clear();
+
+	for (uint32_t i = 0; i < immovables.size(); ++i) {
+
+		const BaseImmovable& base_immovable = *immovables.at(i).object;
+
+		if (!unique_serials.insert(base_immovable.serial()).second) {
+			continue;  // serial was not inserted in the set, so this is duplicate
+		}
+
+		// testing if immovable is owned by someone else and collecting some statistics
+		if (upcast(Building const, building, &base_immovable)) {
+
+			const PlayerNumber bpn = building->owner().player_number();
+			if (bpn == pn) {
+				;
+			} else if (!player_statistics.players_in_same_team(bpn, pn)) {  // it is enemy
+				assert(player_statistics.get_is_enemy(bpn));
+				field.enemy_nearby = true;
+				if (upcast(MilitarySite const, militarysite, building)) {
+					field.enemy_military_presence += militarysite->stationed_soldiers().size();
+					field.enemy_military_sites += 1;
+				}
+				if (upcast(ConstructionSite const, constructionsite, building)) {
+					const BuildingDescr& target_descr = constructionsite->building();
+					if (target_descr.type() == MapObjectType::MILITARYSITE) {
+						field.enemy_military_sites += 1;
+					}
+				}
+
+				// Warehouses are counted here too as they can host soldiers as well
+				if (upcast(Warehouse const, warehouse, building)) {
+					field.enemy_military_presence += warehouse->stationed_soldiers().size();
+					field.enemy_military_sites += 1;
+					field.enemy_wh_nearby = true;
+					enemy_warehouses.insert(building->get_position().hash());
+				}
+				continue;
+			} else {  // it is ally
+				assert(!player_statistics.get_is_enemy(bpn));
+				if (upcast(MilitarySite const, militarysite, building)) {
+					field.ally_military_presence += militarysite->stationed_soldiers().size();
 				}
 				continue;
 			}
@@ -1275,6 +1551,8 @@
 		// if we are here, immovable is ours
 		if (upcast(Building const, building, &base_immovable)) {
 
+			assert(building->owner().player_number() == pn);
+
 			// connected to warehouse
 			bool connected = !building->get_economy()->warehouses().empty();
 			if (connected) {
@@ -1308,7 +1586,7 @@
 				if (radius > dist) {
 
 					field.area_military_capacity += militarysite->max_soldier_capacity();
-					field.area_military_presence += militarysite->stationed_soldiers().size();
+					field.own_military_presence += militarysite->stationed_soldiers().size();
 
 					if (militarysite->stationed_soldiers().empty()) {
 						field.military_unstationed += 1;
@@ -1320,12 +1598,241 @@
 						field.military_loneliness *= static_cast<double_t>(dist) / radius;
 					}
 				}
+			} else {
+				field.own_non_military_nearby += 1;
 			}
 		}
 	}
+
+	assert(field.military_loneliness <= 1000);
+
 	if (any_unconnected_imm && any_connected_imm && field.military_in_constr_nearby == 0) {
 		field.unconnected_nearby = true;
 	}
+
+	// if there is a militarysite on field, we try to walk to enemy
+	field.enemy_accessible_ = false;
+	field.local_soldier_capacity = 0;
+	if (field.is_militarysite) {
+		if (upcast(MilitarySite, ms, field.coords.field->get_immovable())) {
+			if (field.enemy_nearby) {
+				uint32_t unused1 = 0;
+				uint16_t unused2 = 0;
+				field.enemy_accessible_ = other_player_accessible(
+				   actual_enemy_check_area + 3, &unused1, &unused2, field.coords, WalkSearch::kEnemy);
+			}
+			field.local_soldier_capacity = ms->max_soldier_capacity();
+			field.is_militarysite = true;
+		} else {
+			assert(false);
+		}
+	}
+
+	// Calculating field score
+	field.military_score_ = 0;
+	field.inland = false;
+
+	if (!(field.enemy_nearby || field.near_border)) {
+		field.inland = true;
+	}
+
+	const uint8_t score_parts_size = 55;
+	int32_t score_parts[score_parts_size] = {0};
+	if (field.enemy_owned_land_nearby) {
+		score_parts[0] = 3 *
+		                 management_data.neuron_pool[73].get_result_safe(
+		                    field.enemy_owned_land_nearby / 5, kAbsValue);
+		score_parts[1] =
+		   3 *
+		   management_data.neuron_pool[76].get_result_safe(field.enemy_owned_land_nearby, kAbsValue);
+		score_parts[2] = 3 *
+		                 management_data.neuron_pool[54].get_result_safe(
+		                    field.enemy_military_presence * 2, kAbsValue);
+		score_parts[3] = 3 *
+		                 management_data.neuron_pool[61].get_result_safe(
+		                    field.enemy_military_presence / 3, kAbsValue);
+		score_parts[4] =
+		   (!field.enemy_accessible_) ? (-100 + management_data.get_military_number_at(55)) : 0;
+		score_parts[5] =
+		   2 *
+		   management_data.neuron_pool[50].get_result_safe(field.enemy_owned_land_nearby, kAbsValue);
+
+		score_parts[6] =
+		   field.enemy_military_sites * std::abs(management_data.get_military_number_at(67) / 2);
+		score_parts[7] =
+		   2 *
+		   management_data.neuron_pool[34].get_result_safe(field.enemy_military_sites * 2, kAbsValue);
+		score_parts[8] = management_data.neuron_pool[56].get_result_safe(
+		   field.enemy_military_presence * 2, kAbsValue);
+
+		score_parts[9] = management_data.neuron_pool[65].get_result_safe(
+		   (field.unowned_land_nearby + field.enemy_owned_land_nearby) / 2, kAbsValue);
+		score_parts[10] = (field.enemy_accessible_) ? management_data.get_military_number_at(80) : 0;
+
+		score_parts[11] =
+		   -3 *
+		   management_data.neuron_pool[8].get_result_safe(
+		      (field.military_in_constr_nearby + field.military_unstationed) * 3, kAbsValue);
+		score_parts[12] =
+		   -3 *
+		   management_data.neuron_pool[74].get_result_safe(
+		      (field.military_in_constr_nearby + field.military_unstationed) * 5, kAbsValue);
+		score_parts[13] = ((field.military_in_constr_nearby + field.military_unstationed) > 0) ?
+		                     -std::abs(management_data.get_military_number_at(32)) :
+		                     0;
+		score_parts[14] = -1 * (field.military_in_constr_nearby + field.military_unstationed) *
+		                  std::abs(management_data.get_military_number_at(12));
+
+		score_parts[15] =
+		   -2 * management_data.neuron_pool[75].get_result_safe(field.own_military_presence);
+		score_parts[16] = -5 * std::min<int16_t>(field.area_military_capacity, 20);
+		score_parts[17] = 3 * management_data.get_military_number_at(28);
+		score_parts[18] =
+		   (field.enemy_nearby) ? std::abs(management_data.get_military_number_at(68)) * 3 : 0;
+		score_parts[19] =
+		   (field.enemy_wh_nearby) ? std::abs(management_data.get_military_number_at(132)) * 3 : 0;
+		score_parts[58] = (field.enemy_wh_nearby) ?
+		                     std::abs(management_data.get_military_number_at(135)) :
+		                     -std::abs(management_data.get_military_number_at(135));
+
+	} else {  // for expansion or inner land
+
+		score_parts[20] = management_data.neuron_pool[22].get_result_safe(
+		   (field.unowned_mines_spots_nearby + 2) / 3, kAbsValue);
+		score_parts[21] = (field.unowned_mines_spots_nearby > 0) ?
+		                     std::abs(management_data.get_military_number_at(58)) :
+		                     0;
+		if (expansion_type.get_expansion_type() == ExpansionMode::kResources) {
+			score_parts[23] = 2 *
+			                  management_data.neuron_pool[78].get_result_safe(
+			                     (field.unowned_mines_spots_nearby + 2) / 3, kAbsValue);
+		}
+
+		score_parts[24] =
+		   (field.unowned_land_nearby) ?
+		      management_data.neuron_pool[25].get_result_safe(field.water_nearby / 2, kAbsValue) :
+		      0;
+		score_parts[25] =
+		   (field.unowned_land_nearby) ?
+		      management_data.neuron_pool[27].get_result_safe(field.trees_nearby / 2, kAbsValue) :
+		      0;
+
+		if (resource_necessity_water_needed_) {
+			score_parts[26] =
+			   (field.unowned_land_nearby) ?
+			      management_data.neuron_pool[15].get_result_safe(field.water_nearby, kAbsValue) :
+			      0;
+			score_parts[27] =
+			   resource_necessity_water_needed_ *
+			   management_data.neuron_pool[17].get_result_safe(field.distant_water, kAbsValue) / 100;
+		}
+		score_parts[28] =
+		   (field.unowned_land_nearby) ?
+		      management_data.neuron_pool[33].get_result_safe(field.water_nearby, kAbsValue) :
+		      0;
+		score_parts[29] =
+		   management_data.neuron_pool[10].get_result_safe(field.military_loneliness / 50, kAbsValue);
+
+		score_parts[30] =
+		   -10 *
+		   management_data.neuron_pool[8].get_result_safe(
+		      (field.military_in_constr_nearby + field.military_unstationed) * 3, kAbsValue);
+		score_parts[31] =
+		   -10 *
+		   management_data.neuron_pool[31].get_result_safe(
+		      (field.military_in_constr_nearby + field.military_unstationed) * 3, kAbsValue);
+		score_parts[32] = -4 * field.military_in_constr_nearby *
+		                  std::abs(management_data.get_military_number_at(82));
+		score_parts[33] = (field.military_in_constr_nearby > 0) ?
+		                     -5 * management_data.get_military_number_at(85) :
+		                     0;
+
+		score_parts[34] = -1 *
+		                  management_data.neuron_pool[4].get_result_safe(
+		                     (field.area_military_capacity + 4) / 5, kAbsValue);
+		score_parts[35] = 3 * management_data.get_military_number_at(133);
+
+		if (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) {
+			score_parts[36] = -100 - 4 * std::abs(management_data.get_military_number_at(139));
+		} else if (expansion_type.get_expansion_type() == ExpansionMode::kResources ||
+		           expansion_type.get_expansion_type() == ExpansionMode::kSpace) {
+			score_parts[37] =
+			   +100 + 4 * std::abs(management_data.get_military_number_at(139));  // The same as above
+		}
+		if (msites_in_constr() > 0 && field.max_buildcap_nearby == BUILDCAPS_BIG &&
+		    spots_avail.at(BUILDCAPS_BIG) <= 2) {
+			score_parts[57] = -10 * std::abs(management_data.get_military_number_at(54));
+		}
+	}
+
+	// common inputs
+	if (field.unowned_iron_mines_nearby > 0 && ((mines_per_type[iron_ore_id].in_construction +
+	                                             mines_per_type[iron_ore_id].finished) == 0)) {
+		score_parts[40] = field.unowned_iron_mines_nearby *
+		                  std::abs(management_data.get_military_number_at(92)) / 50;
+	}
+	if (field.unowned_iron_mines_nearby && ((mines_per_type[iron_ore_id].in_construction +
+	                                         mines_per_type[iron_ore_id].finished) <= 1)) {
+		score_parts[41] = 3 * std::abs(management_data.get_military_number_at(93));
+	}
+
+	score_parts[42] =
+	   (field.unowned_land_nearby) ?
+	      management_data.neuron_pool[18].get_result_safe(field.own_non_military_nearby, kAbsValue) :
+	      0;
+
+	score_parts[43] = 2 *
+	                  management_data.neuron_pool[11].get_result_safe(
+	                     field.unowned_buildable_spots_nearby, kAbsValue);
+	score_parts[44] =
+	   management_data.neuron_pool[12].get_result_safe(field.unowned_mines_spots_nearby, kAbsValue);
+	score_parts[45] =
+	   (field.unowned_land_nearby) ?
+	      field.military_loneliness * std::abs(management_data.get_military_number_at(53)) / 800 :
+	      0;
+
+	score_parts[46] =
+	   -1 * management_data.neuron_pool[55].get_result_safe(field.ally_military_presence, kAbsValue);
+	score_parts[47] =
+	   -1 *
+	   management_data.neuron_pool[53].get_result_safe(field.ally_military_presence * 2, kAbsValue);
+	score_parts[48] = -2 *
+	                  management_data.neuron_pool[4].get_result_safe(
+	                     (field.area_military_capacity + 4) / 5, kAbsValue);
+	score_parts[49] = ((field.military_in_constr_nearby + field.military_unstationed) > 0) ?
+	                     -std::abs(management_data.get_military_number_at(81)) :
+	                     0;
+	score_parts[55] = (field.military_loneliness < 10) ?
+	                     std::abs(management_data.get_military_number_at(141)) * 2 :
+	                     0;
+	score_parts[56] =
+	   (any_unconnected_imm) ? std::abs(management_data.get_military_number_at(23)) * 2 : 0;
+
+	for (uint16_t i = 0; i < score_parts_size; i++) {
+		field.military_score_ += score_parts[i];
+	}
+
+	if (kAITrainingMode) {
+		if (field.military_score_ < -5000 || field.military_score_ > 2000) {
+			log("Warning field.military_score_ %5d, compounds: ", field.military_score_);
+			for (uint16_t i = 0; i < score_parts_size; i++) {
+				log("%d, ", score_parts[i]);
+			}
+			log("\n");
+		}
+	}
+
+	// is new site allowed at all here?
+	field.defense_msite_allowed = false;
+	int16_t multiplicator = 10;
+	if (soldier_status_ == SoldiersStatus::kBadShortage) {
+		multiplicator = 4;
+	} else if (soldier_status_ == SoldiersStatus::kShortage) {
+		multiplicator = 7;
+	}
+	if (field.area_military_capacity < field.enemy_military_presence * multiplicator / 10) {
+		field.defense_msite_allowed = true;
+	}
 }
 
 /// Updates one mineable field
@@ -1377,7 +1884,6 @@
 
 /// Updates the production and MINE sites statistics needed for construction decision.
 void DefaultAI::update_productionsite_stats() {
-	uint16_t fishers_count = 0;  // used for atlanteans only
 
 	// Reset statistics for all buildings
 	for (uint32_t i = 0; i < buildings_.size(); ++i) {
@@ -1399,11 +1905,6 @@
 			productionsites.front().bo->current_stats +=
 			   productionsites.front().site->get_crude_statistics();
 
-			// counting fishers
-			if (productionsites.front().bo->is_fisher) {
-				fishers_count += 1;
-			}
-
 			// Check whether this building is completely occupied
 			if (!productionsites.front().site->can_start_working()) {
 				productionsites.front().bo->unoccupied_count += 1;
@@ -1417,16 +1918,6 @@
 		productionsites.pop_front();
 	}
 
-	if (resource_necessity_water_needed_) {
-		if (fishers_count == 0) {
-			resource_necessity_water_ = 100;
-		} else if (fishers_count == 1) {
-			resource_necessity_water_ = 50;
-		} else {
-			resource_necessity_water_ = 10;
-		}
-	}
-
 	// for mines_ also
 	// Check all available mines
 	for (uint32_t i = 0; i < mines_.size(); ++i) {
@@ -1466,29 +1957,23 @@
 //   is built.
 // * Buildings are split into categories
 // * The logic is complex but approximately:
-// - buildings producing building material are preferred
-// - buildings identified as basic are preferred
+// - some buildings belong to "basic economy" - these are preferred
+// - some small huts are exempt from basic economy logic
 // - first bulding of a type is preferred
-// - buildings identified as 'direct food supplier' are built after 15 min.
-//   from game start
-// - if a building is upgradeable, second building is also preferred
-//   (there should be no upgrade when there are not two buildings of the same type)
 // - algorithm is trying to take into account actual utlization of buildings
 //   (the one shown in GUI/game is not reliable, it calculates own statistics)
-// * military buildings have own strategy, split into two situations:
-// - there is no enemy
-// - there is an enemy
+// * military buildings use genetic algorithm logic to score fields
 //   Currently more military buildings are built than needed
-//   and "optimization" (dismantling not needed buildings) is done afterwards
+//   so there are always some vacant positions
 bool DefaultAI::construct_building(uint32_t gametime) {
 	if (buildable_fields.empty()) {
 		return false;
 	}
+
 	// Just used for easy checking whether a mine or something else was built.
 	bool mine = false;
 	uint32_t consumers_nearby_count = 0;
-	std::vector<int32_t> spots_avail;
-	spots_avail.resize(4);
+
 	Map& map = game().map();
 
 	for (int32_t i = 0; i < 4; ++i)
@@ -1502,9 +1987,6 @@
 	spots_ += spots_avail.at(BUILDCAPS_MEDIUM);
 	spots_ += spots_avail.at(BUILDCAPS_BIG);
 
-	// here we possible stop building of new buildings
-	new_buildings_stop_ = false;
-
 	// helper variable - we need some proportion of free spots vs productionsites
 	// the proportion depends on size of economy
 	// this proportion defines how dense the buildings will be
@@ -1521,8 +2003,22 @@
 	}
 	const bool has_enough_space = (spots_ > needed_spots);
 
-	// This is a replacement for simple count of mines
-	const int32_t virtual_mines = mines_.size() + mineable_fields.size() / 25;
+	// Do we have basic economy established, informing that we just left the basic economy mode
+	if (!basic_economy_established && persistent_data->remaining_basic_buildings.empty()) {
+		log("%2d: Player has achieved the basic economy at %s\n", player_number(),
+		    gamestring_with_leading_zeros(gametime));
+		basic_economy_established = true;
+		assert(persistent_data->remaining_buildings_size == 0);
+	}
+
+	if (!basic_economy_established && player_statistics.any_enemy_seen_lately(gametime) &&
+	    management_data.f_neuron_pool[17].get_position(0)) {
+		log("%2d: Player has not all buildings for basic economy yet (%lu missing), but enemy is "
+		    "nearby, so quitting the mode at %s\n",
+		    player_number(), persistent_data->remaining_basic_buildings.size(),
+		    gamestring_with_leading_zeros(gametime));
+		basic_economy_established = true;
+	}
 
 	// *_military_scores are used as minimal score for a new military building
 	// to be built. As AI does not traverse all building fields at once, these thresholds
@@ -1533,102 +2029,210 @@
 	// score) and quickly falling down until it reaches the least_military_score
 	// this one (=target_military_score) is actually used to decide if building&field is allowed
 	// candidate
-	// least_military_score is allowed to get bellow 100 only if there is no military site in
-	// construction
-	// right now in order to (try to) avoid expansion lockup
-
-	// Bools below are helpers to improve readability of code
-
-	// It is bit complicated balance building militarysites and productionsites so this is small hack
-	// to help
-	// it
-	bool needs_boost_economy = false;
-	if (highest_nonmil_prio_ > 10 && has_enough_space && virtual_mines >= 5) {
-		needs_boost_economy = true;
-	}
+
+	const PlayerNumber pn = player_number();
+
+	// Genetic algorithm is used here
+	bool inputs[2 * f_neuron_bit_size] = {0};
+	inputs[0] = (pow(msites_in_constr(), 2) > militarysites.size() + 2);
+	inputs[1] = !(pow(msites_in_constr(), 2) > militarysites.size() + 2);
+	inputs[2] =
+	   (highest_nonmil_prio_ > 18 + std::abs(management_data.get_military_number_at(29) / 10));
+	inputs[3] =
+	   !(highest_nonmil_prio_ > 18 + std::abs(management_data.get_military_number_at(29) / 10));
+	inputs[4] = (highest_nonmil_prio_ > 18 + std::abs(management_data.get_military_number_at(48)));
+	inputs[5] = !(highest_nonmil_prio_ > 18 + std::abs(management_data.get_military_number_at(49)));
+	inputs[6] = ((numof_psites_in_constr + mines_in_constr()) >
+	             (productionsites.size() + mines_built()) / productionsites_ratio_);
+	inputs[7] = !((numof_psites_in_constr + mines_in_constr()) >
+	              (productionsites.size() + mines_built()) / productionsites_ratio_);
+
+	inputs[8] = (has_enough_space);
+	inputs[9] = !(has_enough_space);
+	inputs[10] = (has_enough_space);
+	inputs[11] = !(has_enough_space);
+
+	inputs[12] = (gametime > 45 * 60 * 1000);
+	inputs[13] = !(gametime > 45 * 60 * 1000);
+
+	inputs[14] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy);
+	inputs[15] = !(expansion_type.get_expansion_type() == ExpansionMode::kEconomy);
+	inputs[16] = (expansion_type.get_expansion_type() == ExpansionMode::kSpace);
+	inputs[17] = !(expansion_type.get_expansion_type() == ExpansionMode::kSpace);
+
+	inputs[18] = (player_statistics.any_enemy_seen_lately(gametime));
+	inputs[19] = !(player_statistics.any_enemy_seen_lately(gametime));
+	inputs[20] = (player_statistics.get_player_power(pn) >
+	              player_statistics.get_old60_player_power(pn) +
+	                 std::abs(management_data.get_military_number_at(130)) / 10);
+	inputs[21] = !(player_statistics.get_player_power(pn) >
+	               player_statistics.get_old60_player_power(pn) +
+	                  std::abs(management_data.get_military_number_at(131)) / 10);
+	inputs[22] =
+	   (player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn));
+	inputs[23] =
+	   !(player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn));
+	inputs[24] = (highest_nonmil_prio_ > 18 + management_data.get_military_number_at(65) / 10);
+	inputs[25] = !(highest_nonmil_prio_ > 18 + management_data.get_military_number_at(65) / 10);
+	inputs[26] = (player_statistics.get_modified_player_power(pn) >
+	              player_statistics.get_visible_enemies_power(pn));
+	inputs[27] = (player_statistics.get_modified_player_power(pn) <=
+	              player_statistics.get_visible_enemies_power(pn));
+	inputs[28] =
+	   (player_statistics.get_player_power(pn) > player_statistics.get_enemies_average_power());
+	inputs[29] =
+	   !(player_statistics.get_player_power(pn) > player_statistics.get_enemies_average_power());
+	inputs[30] =
+	   (player_statistics.get_player_power(pn) > player_statistics.get_enemies_max_power());
+	inputs[31] =
+	   !(player_statistics.get_player_power(pn) > player_statistics.get_enemies_max_power());
+
+	inputs[32] = (persistent_data->least_military_score <
+	              persistent_data->ai_personality_mil_upper_limit *
+	                 std::abs(management_data.get_military_number_at(69)) / 100);
+	inputs[33] = !(persistent_data->least_military_score <
+	               persistent_data->ai_personality_mil_upper_limit *
+	                  std::abs(management_data.get_military_number_at(69)) / 100);
+	inputs[34] = player_statistics.strong_enough(pn);
+	inputs[35] = !player_statistics.strong_enough(pn);
+
+	inputs[36] = (player_statistics.get_player_land(pn) < 500);
+	inputs[37] = (player_statistics.get_player_land(pn) < 700);
+	inputs[38] = (player_statistics.get_player_land(pn) < 900);
+	inputs[39] = (player_statistics.get_player_land(pn) < 1100);
+	inputs[40] = (player_statistics.get_player_land(pn) > 500);
+	inputs[41] = (player_statistics.get_player_land(pn) > 700);
+	inputs[42] = (player_statistics.get_player_land(pn) > 900);
+	inputs[43] = (player_statistics.get_player_land(pn) > 1100);
+	inputs[44] = (player_statistics.get_player_power(pn) >
+	              player_statistics.get_old60_player_power(pn) +
+	                 std::abs(management_data.get_military_number_at(130)) / 10);
+	inputs[45] = !(player_statistics.get_player_power(pn) >
+	               player_statistics.get_old60_player_power(pn) +
+	                  std::abs(management_data.get_military_number_at(131)) / 10);
+	inputs[46] =
+	   (player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn));
+	inputs[47] =
+	   !(player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn));
+	inputs[48] = (bakeries_count_ == 0);
+	inputs[49] = (bakeries_count_ <= 1);
+	inputs[50] = (bakeries_count_ <= 1);
+	inputs[51] = (numof_psites_in_constr > 8);
+	inputs[52] = (numof_psites_in_constr < 8);
+
+	int16_t needs_boost_economy_score = management_data.get_military_number_at(61) / 5;
+	int16_t increase_score_limit_score = 0;
+
+	for (uint8_t i = 0; i < f_neuron_bit_size; i += 1) {
+		if (management_data.f_neuron_pool[51].get_position(i)) {
+			needs_boost_economy_score += (inputs[i]) ? 1 : -1;
+		}
+		if (management_data.f_neuron_pool[52].get_position(i)) {
+			increase_score_limit_score += (inputs[i]) ? 1 : -1;
+		}
+		if (management_data.f_neuron_pool[21].get_position(i)) {
+			needs_boost_economy_score += (inputs[f_neuron_bit_size + i]) ? 1 : -1;
+		}
+		if (management_data.f_neuron_pool[22].get_position(i)) {
+			increase_score_limit_score += (inputs[f_neuron_bit_size + i]) ? 1 : -1;
+		}
+	}
+
+	// Finding expansion policy
+	// Do we need basic resources?
+	// This is a replacement for simple count of mines
+	const int32_t virtual_mines = mines_.size() + mineable_fields.size() / 15;
+	const bool needs_fishers = resource_necessity_water_needed_ && fishers_count_ < 1;
+
+	if (virtual_mines < 4 || mines_per_type[iron_ore_id].total_count() < 1 || needs_fishers) {
+		expansion_type.set_expantion_type(ExpansionMode::kResources);
+	} else {
+		// now we must decide if we go after spots or economy boost
+		if (needs_boost_economy_score >= 3) {
+			expansion_type.set_expantion_type(ExpansionMode::kEconomy);
+		} else if (needs_boost_economy_score >= -2) {
+			expansion_type.set_expantion_type(ExpansionMode::kBoth);
+		} else {
+			expansion_type.set_expantion_type(ExpansionMode::kSpace);
+		}
+	}
+
+	const bool increase_least_score_limit =
+	   (increase_score_limit_score > management_data.get_military_number_at(45) / 5);
+
+	uint16_t concurent_ms_in_constr_no_enemy = 1;
+	uint16_t concurent_ms_in_constr_enemy_nearby = 2;
 
 	// resetting highest_nonmil_prio_ so it can be recalculated anew
 	highest_nonmil_prio_ = 0;
 
-	const bool too_many_ms_constructionsites =
-	   (pow(msites_in_constr(), 2) > militarysites.size() + 2);
-	const bool too_many_vacant_mil =
-	   (vacant_mil_positions_ * 3 > static_cast<int32_t>(militarysites.size()));
-	const int32_t kUpperLimit = 325;
-	const int32_t kBottomLimit = 40;  // to prevent too dense militarysites
-	// modifying least_military_score, down if more military sites are needed and vice versa
-	if (too_many_ms_constructionsites || too_many_vacant_mil || needs_boost_economy) {
+	if (increase_least_score_limit) {
 		if (persistent_data->least_military_score <
-		    kUpperLimit) {  // No sense in letting it grow too high
+		    persistent_data
+		       ->ai_personality_mil_upper_limit) {  // No sense in letting it grow too high
 			persistent_data->least_military_score += 20;
+			if (persistent_data->least_military_score > persistent_data->target_military_score) {
+				persistent_data->target_military_score = persistent_data->least_military_score;
+			}
+			if (persistent_data->target_military_score >
+			    persistent_data->ai_personality_mil_upper_limit) {
+				persistent_data->ai_personality_mil_upper_limit =
+				   persistent_data->target_military_score;
+			}
 		}
 	} else {
+
+		uint16_t divider = 1;  // this is to slow down least military score decreasion
+		switch (expansion_type.get_expansion_type()) {
+		case ExpansionMode::kEconomy:
+			divider = 3;
+			break;
+		case ExpansionMode::kBoth:
+			divider = 2;
+			break;
+		default:
+			divider = 1;
+		}
+
 		// least_military_score is decreased, but depending on the size of territory
 		switch (static_cast<uint32_t>(log10(buildable_fields.size()))) {
 		case 0:
-			persistent_data->least_military_score -= 10;
+			persistent_data->least_military_score -= 10 / divider;
 			break;
 		case 1:
-			persistent_data->least_military_score -= 8;
+			persistent_data->least_military_score -= 8 / divider;
 			break;
 		case 2:
-			persistent_data->least_military_score -= 5;
+			persistent_data->least_military_score -= 5 / divider;
 			break;
 		case 3:
-			persistent_data->least_military_score -= 3;
+			persistent_data->least_military_score -= 3 / divider;
 			break;
 		default:
-			persistent_data->least_military_score -= 2;
-		}
-		// do not get bellow kBottomLimit if there is at least one ms in construction
-		if ((msites_in_constr() > 0 || too_many_vacant_mil) &&
-		    persistent_data->least_military_score < kBottomLimit) {
-			persistent_data->least_military_score = kBottomLimit;
+			persistent_data->least_military_score -= 2 / divider;
 		}
 		if (persistent_data->least_military_score < 0) {
 			persistent_data->least_military_score = 0;
 		}
 	}
 
-	// This is effective score, falling down very quickly
-	if (persistent_data->target_military_score > kUpperLimit + 150) {
-		persistent_data->target_military_score = 8 * persistent_data->target_military_score / 10;
-	} else {
-		persistent_data->target_military_score = 9 * persistent_data->target_military_score / 10;
-	}
+	assert(persistent_data->least_military_score <= persistent_data->target_military_score);
+	assert(persistent_data->target_military_score <=
+	       persistent_data->ai_personality_mil_upper_limit);
+	persistent_data->target_military_score = 9 * persistent_data->target_military_score / 10;
 	if (persistent_data->target_military_score < persistent_data->least_military_score) {
 		persistent_data->target_military_score = persistent_data->least_military_score;
 	}
-
-	// there are many reasons why to stop building production buildings
-	// (note there are numerous exceptions)
-	// 1. to not have too many constructionsites
-	if ((num_prod_constructionsites + mines_in_constr()) >
-	    (productionsites.size() + mines_built()) / persistent_data->ai_productionsites_ratio + 2) {
-		new_buildings_stop_ = true;
-	}
-	// 2. to not exhaust all free spots
-	if (!has_enough_space) {
-		new_buildings_stop_ = true;
-	}
-	// 3. too keep some proportions production sites vs military sites
-	if ((num_prod_constructionsites + productionsites.size()) >
-	    (msites_in_constr() + militarysites.size()) * 5) {
-		new_buildings_stop_ = true;
-	}
-	// 4. if we do not have 2 mines at least
-	if (mines_.size() < 2) {
-		new_buildings_stop_ = true;
-	}
+	assert(persistent_data->target_military_score >= persistent_data->least_military_score);
 
 	// we must calculate wood policy
 	const DescriptionIndex wood_index = tribe_->safe_ware_index("log");
 	// stocked wood is to be in some propotion to productionsites and
 	// constructionsites (this proportion is bit artifical, or we can say
 	// it is proportion to the size of economy). Plus some positive 'margin'
-	const int32_t stocked_wood_margin = get_warehoused_stock(wood_index) -
-	                                    productionsites.size() * 2 - num_prod_constructionsites +
-	                                    persistent_data->ai_personality_wood_difference;
+	const int32_t stocked_wood_margin = calculate_stocklevel(wood_index) -
+	                                    productionsites.size() * 2 - numof_psites_in_constr +
+	                                    management_data.get_military_number_at(87) / 5;
 	if (gametime < 15 * 60 * 1000) {
 		wood_policy_ = WoodPolicy::kAllowRangers;
 	} else if (stocked_wood_margin > 80) {
@@ -1639,34 +2243,6 @@
 		wood_policy_ = WoodPolicy::kAllowRangers;
 	}
 
-	// we must consider need for mines
-	// set necessity for mines
-	// we use 'virtual mines', because also mine spots can be changed
-	// to mines when AI decides so
-
-	resource_necessity_mines_ = 100 * (15 - virtual_mines) / 15;
-	resource_necessity_mines_ = (resource_necessity_mines_ > 100) ? 100 : resource_necessity_mines_;
-	resource_necessity_mines_ = (resource_necessity_mines_ < 20) ? 10 : resource_necessity_mines_;
-
-	// here we calculate how badly we need to expand, result is number (0-100)
-	// like a percent
-	if (spots_ == 0) {
-		resource_necessity_territory_ = 100;
-	} else {
-		resource_necessity_territory_ = 100 * 3 * (productionsites.size() + 5) / spots_;
-		resource_necessity_territory_ =
-		   (resource_necessity_territory_ > 100) ? 100 : resource_necessity_territory_;
-		resource_necessity_territory_ =
-		   (resource_necessity_territory_ < 10) ? 10 : resource_necessity_territory_;
-		// alse we need at lest 4 big spots
-		if (spots_avail.at(BUILDCAPS_BIG) < 2) {
-			resource_necessity_territory_ = 100;
-		}
-		if (spots_avail.at(BUILDCAPS_MEDIUM) < 4) {
-			resource_necessity_territory_ = 100;
-		}
-	}
-
 	BuildingObserver* best_building = nullptr;
 	int32_t proposed_priority = 0;
 	Coords proposed_coords;
@@ -1684,7 +2260,8 @@
 
 		// not doing this for non-military buildins
 		if (!(bo.type == BuildingObserver::Type::kMilitarysite ||
-		      bo.type == BuildingObserver::Type::kTrainingsite))
+		      bo.type == BuildingObserver::Type::kTrainingsite ||
+		      bo.type == BuildingObserver::Type::kProductionsite))
 			continue;
 
 		// and neither for small military buildings
@@ -1697,13 +2274,17 @@
 		// checking we have enough critical material on stock
 		for (uint32_t m = 0; m < bo.critical_building_material.size(); ++m) {
 			DescriptionIndex wt(static_cast<size_t>(bo.critical_building_material.at(m)));
-			uint32_t treshold = 3;
+			uint32_t treshold = 7;
 			// generally trainingsites are more important
 			if (bo.type == BuildingObserver::Type::kTrainingsite) {
+				treshold = 4;
+			}
+
+			if (bo.type == BuildingObserver::Type::kProductionsite) {
 				treshold = 2;
 			}
 
-			if (get_warehoused_stock(wt) < treshold) {
+			if (calculate_stocklevel(wt) <= treshold) {
 				bo.build_material_shortage = true;
 				break;
 			}
@@ -1721,6 +2302,11 @@
 
 			bo.new_building = check_building_necessity(bo, PerfEvaluation::kForConstruction, gametime);
 
+			if (bo.is(BuildingAttribute::kShipyard)) {
+				assert(bo.new_building == BuildingNecessity::kAllowed ||
+				       bo.new_building == BuildingNecessity::kForbidden);
+			}
+
 			if (bo.new_building == BuildingNecessity::kAllowed) {
 				bo.new_building_overdue = 0;
 			}
@@ -1731,7 +2317,7 @@
 			     bo.new_building == BuildingNecessity::kForced ||
 			     bo.new_building == BuildingNecessity::kAllowed ||
 			     bo.new_building == BuildingNecessity::kNeededPending) &&
-			    !bo.outputs.empty()) {
+			    (!bo.outputs.empty() || bo.is(BuildingAttribute::kBarracks))) {
 				if (bo.max_needed_preciousness <= 0) {
 					throw wexception("AI: Max presciousness must not be <= 0 for building: %s",
 					                 bo.desc->name().c_str());
@@ -1740,6 +2326,7 @@
 				bo.max_needed_preciousness = 0;
 			} else {
 				// For other situations we make sure max_needed_preciousness is zero
+
 				assert(bo.max_needed_preciousness == 0);
 			}
 
@@ -1762,42 +2349,31 @@
 			// c) all other cases: primary_priority = 0;
 			if (bo.max_needed_preciousness > 0) {
 				if (bo.new_building == BuildingNecessity::kAllowed) {
-					bo.primary_priority = bo.max_needed_preciousness;
+					bo.primary_priority += bo.max_needed_preciousness;
 				} else {
-					bo.primary_priority = bo.max_needed_preciousness +
-					                      bo.max_needed_preciousness * bo.new_building_overdue / 100 +
-					                      bo.new_building_overdue / 20;
+					bo.primary_priority += bo.primary_priority * bo.new_building_overdue *
+					                       std::abs(management_data.get_military_number_at(120)) / 500;
+					bo.primary_priority += bo.max_needed_preciousness +
+					                       bo.max_needed_preciousness * bo.new_building_overdue *
+					                          std::abs(management_data.get_military_number_at(70)) /
+					                          1000 +
+					                       bo.new_building_overdue *
+					                          std::abs(management_data.get_military_number_at(71)) / 50;
+					if (bo.new_building == BuildingNecessity::kForced) {
+						bo.primary_priority += bo.new_building_overdue *
+						                       std::abs(management_data.get_military_number_at(119)) / 50;
+					}
 				}
 			} else {
 				bo.primary_priority = 0;
 			}
 
-			// Generally we don't start another building if there is some of the same type in
-			// construction
-			// Some types of building allow two buildings in construction though, but not more
-			// Below checks are to guarantee that there is no logical error in previous steps, or
-			// inconsistency in AI data
-			if (bo.new_building == BuildingNecessity::kNeeded ||
-			    bo.new_building == BuildingNecessity::kForced ||
-			    bo.new_building == BuildingNecessity::kAllowed ||
-			    bo.new_building == BuildingNecessity::kNeededPending) {
-				if (bo.plants_trees || bo.need_trees || bo.max_needed_preciousness >= 10) {
-					if (bo.cnt_under_construction + bo.unoccupied_count > 1) {
-						throw wexception("AI inconsistency:  %s: total_count %d > 1, unoccupied: %d",
-						                 bo.name, bo.total_count(), bo.unoccupied_count);
-					}
-				} else {
-					if (bo.cnt_under_construction + bo.unoccupied_count > 0) {
-						throw wexception("AI inconsistency:  %s: total_count %d > 0, unoccupied: %d",
-						                 bo.name, bo.total_count(), bo.unoccupied_count);
-					}
-				}
-			}
-
 		} else if (bo.type == BuildingObserver::Type::kMilitarysite) {
-			bo.new_building = check_building_necessity(bo.desc->get_size(), gametime);
+			bo.new_building = check_building_necessity(bo, gametime);
 		} else if (bo.type == BuildingObserver::Type::kTrainingsite) {
 			bo.new_building = check_building_necessity(bo, PerfEvaluation::kForConstruction, gametime);
+		} else if (bo.type == BuildingObserver::Type::kWarehouse) {
+			bo.new_building = check_warehouse_necessity(bo, gametime);
 		} else if (bo.aimode_limit_status() != AiModeBuildings::kAnotherAllowed) {
 			bo.new_building = BuildingNecessity::kNotNeeded;
 		} else {
@@ -1847,13 +2423,13 @@
 			}
 
 			// testing for reserved ports
-			if (!bo.is_port) {
+			if (!bo.is_what.count(BuildingAttribute::kPort)) {
 				if (port_reserved_coords.count(bf->coords.hash()) > 0) {
 					continue;
 				}
 			}
 
-			if (time(nullptr) % 3 == 0 && bo.total_count() > 0) {
+			if (std::rand() % 3 == 0 && bo.total_count() > 0) {
 				continue;
 			}  // add randomnes and ease AI
 
@@ -1863,7 +2439,8 @@
 
 			// here we do an exemption for lumberjacks, mainly in early stages of game
 			// sometimes the first one is not built and AI waits too long for second attempt
-			if (gametime - bo.construction_decision_time < kBuildingMinInterval && !bo.need_trees) {
+			if (gametime - bo.construction_decision_time < kBuildingMinInterval &&
+			    !bo.is(BuildingAttribute::kLumberjack)) {
 				continue;
 			}
 
@@ -1876,8 +2453,11 @@
 
 			if (bo.type == BuildingObserver::Type::kProductionsite) {
 
+				prio = management_data.neuron_pool[44].get_result_safe(bf->military_score_ / 20) / 5;
+				assert(prio >= -20 && prio <= 20);
+
 				// this can be only a well (as by now)
-				if (bo.mines_water) {
+				if (bo.is(BuildingAttribute::kWell)) {
 
 					if (bo.new_building == BuildingNecessity::kForced) {
 						assert(bo.total_count() - bo.unconnected_count == 0);
@@ -1899,33 +2479,41 @@
 						prio += 200;
 					}
 
-					prio += bf->ground_water - 2;
+					prio += -10 + 2 * bf->ground_water;
 
-				} else if (bo.need_trees) {  // LUMBERJACS
+				} else if (bo.is(BuildingAttribute::kLumberjack)) {
 
 					prio = bo.primary_priority;
 
-					prio += -20 + 200 / (bo.total_count() + 1);
-
 					if (bo.new_building == BuildingNecessity::kForced) {
-						prio *= 2;
-					} else if (bf->trees_nearby < 2 && bf->supporters_nearby.at(bo.outputs.at(0) == 0)) {
+						prio += 5 * std::abs(management_data.get_military_number_at(17));
+					}
+
+					if (bf->trees_nearby < trees_nearby_treshold_ &&
+					    bo.new_building == BuildingNecessity::kAllowed) {
 						continue;
 					}
 
+					prio += std::abs(management_data.get_military_number_at(26)) *
+					        (bf->trees_nearby - trees_nearby_treshold_) / 10;
+
 					// consider cutters and rangers nearby
-					prio -= bf->producers_nearby.at(bo.outputs.at(0)) * 20;
-					prio += bf->supporters_nearby.at(bo.outputs.at(0)) * 5;
-
-					prio += 2 * bf->trees_nearby;
-
-				} else if (bo.need_rocks) {
+					prio += 2 * bf->supporters_nearby.at(bo.outputs.at(0)) *
+					        std::abs(management_data.get_military_number_at(25));
+					prio -= bf->producers_nearby.at(bo.outputs.at(0)) *
+					        std::abs(management_data.get_military_number_at(36)) * 3;
+
+				} else if (bo.is(BuildingAttribute::kNeedsRocks)) {
 
 					// Quarries are generally to be built everywhere where rocks are
 					// no matter the need for granite, as rocks are considered an obstacle
 					// to expansion
 					prio = 2 * bf->rocks_nearby;
 
+					if (bf->rocks_nearby > 0 && bf->near_border) {
+						prio += management_data.get_military_number_at(27) / 2;
+					}
+
 					// value is initialized with 1 but minimal value that can be
 					// calculated is 11
 					if (prio <= 1) {
@@ -1936,19 +2524,14 @@
 						prio += 150;
 					}
 
-					if (bo.stocklevel_time < game().get_gametime() - 5 * 1000) {
-						bo.stocklevel = get_stocklevel(static_cast<size_t>(bo.production_hint));
-						bo.stocklevel_time = game().get_gametime();
-					}
-
-					if (bo.stocklevel == 0) {
+					if (get_stocklevel(bo, gametime) == 0) {
 						prio *= 2;
 					}
 
 					// to prevent to many quaries on one spot
 					prio = prio - 50 * bf->producers_nearby.at(bo.outputs.at(0));
 
-				} else if (bo.is_hunter) {
+				} else if (bo.is(BuildingAttribute::kHunter)) {
 
 					if (bf->critters_nearby < 5) {
 						continue;
@@ -1966,26 +2549,31 @@
 					prio +=
 					   (bf->critters_nearby * 3) - 8 - 5 * bf->producers_nearby.at(bo.outputs.at(0));
 
-				} else if (bo.is_fisher) {  // fisher
+				} else if (bo.is(BuildingAttribute::kFisher)) {  // fisher
 
 					if (bf->water_nearby < 2 || bf->fish_nearby < 2) {
 						continue;
 					}
 
 					if (bo.new_building == BuildingNecessity::kForced) {
-						prio += 20;
+						prio += 200;
 					}
 
 					// Overdue priority here
 					prio += bo.primary_priority;
 
 					prio -= bf->producers_nearby.at(bo.outputs.at(0)) * 20;
-					prio += bf->supporters_nearby.at(bo.outputs.at(0)) * 10;
+					prio += bf->supporters_nearby.at(bo.outputs.at(0)) * 20;
 
-					prio += -5 + bf->fish_nearby;
+					prio +=
+					   -5 +
+					   bf->fish_nearby * (1 + std::abs(management_data.get_military_number_at(63) / 15));
+					if (resource_necessity_water_needed_) {
+						prio *= 3;
+					}
 
 				} else if (bo.production_hint >= 0) {
-					if (bo.plants_trees) {
+					if (bo.is(BuildingAttribute::kRanger)) {
 						assert(bo.cnt_target > 0);
 					} else {
 						bo.cnt_target =
@@ -1995,39 +2583,41 @@
 					// They have no own primary priority
 					assert(bo.primary_priority == 0);
 
-					if (bo.plants_trees) {  // RANGERS
+					if (bo.is_what.count(BuildingAttribute::kRanger)) {  // RANGERS
 
 						assert(bo.new_building == BuildingNecessity::kNeeded);
 
-						// if there are too many trees nearby
-						if (bf->trees_nearby > 25 && bo.total_count() >= 1) {
-							continue;
-						}
-
-						// for small starting spots - to prevent crowding by rangers and trees
-						if (spots_ < (4 * bo.total_count()) && bo.total_count() > 0) {
-							continue;
-						}
+						prio = 0;
 
 						if (bo.total_count() == 0) {
-							prio = 200;
+							prio += 200;
 						} else {
-							prio = 50 / bo.total_count();
+							prio += std::abs(management_data.get_military_number_at(66)) *
+							        (bo.cnt_target - bo.total_count());
 						}
-
-						// considering producers
-						prio += std::min<uint8_t>(bf->producers_nearby.at(bo.production_hint), 4) * 5 -
-						        new_buildings_stop_ * 15 - bf->space_consumers_nearby * 5 -
-						        bf->rocks_nearby / 3 + bf->trees_nearby / 2 +
-						        std::min<uint8_t>(bf->supporters_nearby.at(bo.production_hint), 4) * 3;
+						prio -= bf->water_nearby / 5;
+
+						prio += management_data.neuron_pool[67].get_result_safe(
+						           bf->producers_nearby.at(bo.production_hint) * 5, kAbsValue) /
+						        2;
+
+						prio +=
+						   management_data.neuron_pool[49].get_result_safe(bf->trees_nearby, kAbsValue) /
+						   5;
+
+						prio += bf->producers_nearby.at(bo.production_hint) * 5 -
+						        (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) * 15 -
+						        bf->space_consumers_nearby * 5 - bf->rocks_nearby / 3 +
+						        bf->supporters_nearby.at(bo.production_hint) * 3;
 
 					} else {  // FISH BREEDERS and GAME KEEPERS
 
 						// especially for fish breeders
-						if (bo.need_water && (bf->water_nearby < 6 || bf->fish_nearby < 6)) {
+						if (bo.is(BuildingAttribute::kNeedsCoast) &&
+						    (bf->water_nearby < 6 || bf->fish_nearby < 6)) {
 							continue;
 						}
-						if (bo.need_water) {
+						if (bo.is(BuildingAttribute::kNeedsCoast)) {
 							prio += (-6 + bf->water_nearby) / 3;
 							prio += (-6 + bf->fish_nearby) / 3;
 						}
@@ -2036,18 +2626,13 @@
 							continue;
 						}
 
-						if (bo.stocklevel_time < game().get_gametime() - 5 * 1000) {
-							bo.stocklevel =
-							   get_stocklevel_by_hint(static_cast<size_t>(bo.production_hint));
-							bo.stocklevel_time = game().get_gametime();
-						}
-						if (bo.stocklevel > 50) {
+						if (get_stocklevel(bo, gametime) > 50) {
 							continue;
 						}
 
 						if (bo.total_count() == 0) {
 							prio += 100;
-						} else if (!bo.need_water) {
+						} else if (!bo.is(BuildingAttribute::kNeedsCoast)) {
 							prio += 10 / bo.total_count();
 						}
 
@@ -2059,21 +2644,18 @@
 						}
 					}
 
-				} else if (bo.recruitment && !new_buildings_stop_) {
-					// this will depend on number of mines_ and productionsites
-					if (static_cast<int32_t>((productionsites.size() + mines_.size()) / 30) >
-					       bo.total_count() &&
-					    (bo.cnt_under_construction + bo.unoccupied_count) == 0 &&
-					    // but only if current buildings are utilized enough
-					    (bo.total_count() == 0 || bo.current_stats > 60)) {
-						prio = 10;
-					}
+				} else if (bo.is(BuildingAttribute::kRecruitment)) {
+					prio = bo.primary_priority;
+					prio -= bf->unowned_land_nearby * 2;
+					prio -= (bf->enemy_nearby) * 100;
+					prio -= (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) * 100;
 				} else {  // finally normal productionsites
 					assert(bo.production_hint < 0);
 
 					if (bo.new_building == BuildingNecessity::kForced) {
 						prio += 150;
-					} else if (bo.is_shipyard) {
+						assert(!bo.is(BuildingAttribute::kShipyard));
+					} else if (bo.is(BuildingAttribute::kShipyard)) {
 						assert(bo.new_building == BuildingNecessity::kAllowed);
 						if (!seafaring_economy) {
 							continue;
@@ -2088,33 +2670,43 @@
 					// we check separatelly buildings with no inputs and some inputs
 					if (bo.inputs.empty()) {
 
-						if (bo.space_consumer) {
+						assert(!bo.is(BuildingAttribute::kShipyard));
+
+						if (bo.is(BuildingAttribute::kSpaceConsumer)) {
 							// we dont like trees nearby
 							prio += 1 - bf->trees_nearby / 15;
 							// we attempt to cluster space consumers together
 							prio += bf->space_consumers_nearby * 2;
 							// and be far from rangers
-							prio += 1 - bf->rangers_nearby * 3;
+							prio += 1 -
+							        bf->rangers_nearby *
+							           std::abs(management_data.get_military_number_at(102)) / 5;
 						} else {
 							// leave some free space between them
-							prio -= bf->producers_nearby.at(bo.outputs.at(0)) * 5;
-						}
-
-						if (bo.space_consumer && !bf->water_nearby) {  // not close to water
-							prio += 1;
-						}
-
-						if (bo.space_consumer &&
-						    !bf->unowned_mines_spots_nearby) {  // not close to mountains
-							prio += 1;
+							prio -= bf->producers_nearby.at(bo.outputs.at(0)) *
+							        std::abs(management_data.get_military_number_at(108)) / 5;
+						}
+
+						if (bo.is(BuildingAttribute::kSpaceConsumer) &&
+						    bf->water_nearby) {  // not close to water
+							prio -= std::abs(management_data.get_military_number_at(103)) / 5;
+						}
+
+						if (bo.is(BuildingAttribute::kSpaceConsumer) &&
+						    bf->unowned_mines_spots_nearby) {  // not close to mountains
+							prio -= std::abs(management_data.get_military_number_at(104)) / 5;
 						}
 					}
 
-					else if (bo.is_shipyard) {
+					else if (bo.is(BuildingAttribute::kShipyard)) {
 						// for now AI builds only one shipyard
-						if (bf->open_water_nearby > 1 && (bo.total_count() - bo.unconnected_count) == 0 &&
-						    seafaring_economy) {
-							prio += productionsites.size() * 5 + bf->open_water_nearby;
+						assert(bo.total_count() == 0);
+						if (bf->open_water_nearby > 3 && seafaring_economy) {
+							prio += productionsites.size() * 5 +
+							        bf->open_water_nearby *
+							           std::abs(management_data.get_military_number_at(109)) / 10;
+						} else {
+							continue;
 						}
 					}
 
@@ -2124,11 +2716,11 @@
 
 					// bonus for big buildings if shortage of big fields
 					if (spots_avail.at(BUILDCAPS_BIG) <= 5 && bo.desc->get_size() == 3) {
-						prio += 10;
+						prio += std::abs(management_data.get_military_number_at(105)) / 5;
 					}
 
 					if (spots_avail.at(BUILDCAPS_MEDIUM) <= 5 && bo.desc->get_size() == 2) {
-						prio += 5;
+						prio += std::abs(management_data.get_military_number_at(106)) / 5;
 					}
 
 					// +1 if any consumers_ are nearby
@@ -2138,152 +2730,69 @@
 						consumers_nearby_count += bf->consumers_nearby.at(bo.outputs.at(k));
 
 					if (consumers_nearby_count > 0) {
-						prio += 1;
+						prio += std::abs(management_data.get_military_number_at(107)) / 3;
 					}
 				}
 
 				// Consider border with exemption of some huts
-				if (!(bo.need_trees || bo.need_water || bo.is_fisher)) {
+				if (!(bo.is(BuildingAttribute::kLumberjack) || bo.is(BuildingAttribute::kNeedsCoast) ||
+				      bo.is(BuildingAttribute::kFisher))) {
 					prio = recalc_with_border_range(*bf, prio);
-				} else if (bf->near_border && (bo.need_trees || bo.need_water)) {
+				} else if (bf->near_border && (bo.is(BuildingAttribute::kLumberjack) ||
+				                               bo.is(BuildingAttribute::kNeedsCoast))) {
 					prio /= 2;
 				}
 
 			}  // production sites done
 			else if (bo.type == BuildingObserver::Type::kMilitarysite) {
 
-				if (!(bf->unowned_land_nearby || bf->enemy_nearby)) {
-					continue;
-				}
-
-				if (military_last_build_ > gametime - 15 * 1000) {
-					continue;
-				}
-
-				// This is another restriction of military building - but general
-				if (bf->enemy_nearby && bo.fighting_type) {
-					;
-				}  // it is ok, go on
-				else if (bf->unowned_mines_spots_nearby > 2 &&
-				         (bo.mountain_conqueror || bo.expansion_type)) {
-					;
-				}  // it is ok, go on
-				else if (bo.expansion_type) {
-					if (bo.desc->get_size() == 2 && gametime % 2 >= 1) {
-						continue;
-					}
-					if (bo.desc->get_size() == 3 && gametime % 4 >= 1) {
-						continue;
-					};
-				} else {
-					continue;
-				}  // the building is not suitable for situation
-
-				// score here is a compound of various input values
-				// usually resources in vicinity, but when enemy is nearby
-				// additional bonus is added
-				if (bf->enemy_nearby) {
-					prio += bf->military_loneliness / 3;
-					prio += (20 - bf->area_military_capacity) * 10;
-					prio -= bo.build_material_shortage * 50;
-					prio -= (bf->military_in_constr_nearby + bf->military_unstationed) * 50;
-				} else {
-					if (bf->near_border) {
-						prio += 50;
-						prio -= bo.build_material_shortage * 150;
-					} else {
-						prio -= bo.build_material_shortage * 500;  // prohibitive
-					}
-					prio -= (bf->military_in_constr_nearby + bf->military_unstationed) * 150;
-					prio += (5 - bf->own_military_sites_nearby_()) * 15;
-				}
-				prio += bf->unconnected_nearby * 50;
-				prio += bf->unowned_land_nearby * resource_necessity_territory_ / 100;
-				prio += bf->unowned_mines_spots_nearby * resource_necessity_mines_ / 100;
-				prio +=
-				   ((bf->unowned_mines_spots_nearby > 0) ? 35 : 0) * resource_necessity_mines_ / 100;
-				prio += bf->rocks_nearby / 2;
-				prio += bf->water_nearby;
-				prio += bf->distant_water * resource_necessity_water_needed_ / 100;
-				prio += bf->military_loneliness / 10;
-				prio += bf->trees_nearby / 3;
-				if (bf->portspace_nearby == ExtendedBool::kTrue) {
-					if (num_ports == 0) {
-						prio += 100;
-					} else {
-						prio += 25;
-					}
-				}
-				// sometimes expansion is stalled and this is to help boost it
-				if (msites_in_constr() == 0 && vacant_mil_positions_ <= 2) {
-					prio += 10;
-					if (bf->enemy_nearby) {
-						prio += 20;
-					}
-				}
-
-				// additional score for bigger buildings
-				int32_t prio_for_size = bo.desc->get_size() - 1;
-				if (bf->enemy_nearby) {
-					prio_for_size *= 30;
-				} else {
-					prio_for_size *= 5;
-				}
-				prio += prio_for_size;
+				assert(prio == 0);
+				prio = bo.primary_priority;
+
+				// Two possibilities why to construct militarysite here
+				if (!bf->defense_msite_allowed &&
+				    bf->nearest_buildable_spot_nearby < bo.desc->get_conquers() &&
+				    (bf->military_in_constr_nearby + bf->military_unstationed) <
+				       concurent_ms_in_constr_no_enemy) {
+					// it will conquer new buildable spots for buildings or mines
+					;
+				} else if (bf->defense_msite_allowed &&
+				           (bf->military_in_constr_nearby + bf->military_unstationed) <
+				              concurent_ms_in_constr_enemy_nearby) {
+					// we need it to increase capacity on the field
+					if (bo.fighting_type) {
+						prio += 5;
+					}
+				} else {
+					continue;
+				}
+				if (bf->unowned_mines_spots_nearby > 2 && bo.mountain_conqueror) {
+					prio += 5;
+				}
+				prio += std::abs(management_data.get_military_number_at(35)) / 5 *
+				        (static_cast<int16_t>(bo.desc->get_conquers()) -
+				         static_cast<int16_t>(bf->nearest_buildable_spot_nearby));
+
+				prio += bf->military_score_;
 
 				// if place+building is not good enough
 				if (prio <= persistent_data->target_military_score) {
 					continue;
 				}
+				if (prio > persistent_data->ai_personality_mil_upper_limit) {
+					persistent_data->ai_personality_mil_upper_limit = prio;
+				}
 			} else if (bo.type == BuildingObserver::Type::kWarehouse) {
 
 				// exclude spots on border
-				if (bf->near_border && !bo.is_port) {
-					continue;
-				}
-
-				if (!bf->is_portspace && bo.is_port) {
-					continue;
-				}
-
-				if (bo.cnt_under_construction > 0) {
-					continue;
-				}
-
-				bool warehouse_needed = false;
-
-				//  Build one warehouse for ~every 35 productionsites and mines_.
-				//  Militarysites are slightly important as well, to have a bigger
-				//  chance for a warehouses (containing waiting soldiers or wares
-				//  needed for soldier training) near the frontier.
-				prio = static_cast<int32_t>(productionsites.size() + mines_.size()) + 20 -
-				       35 * static_cast<int32_t>(numof_warehouses_);
-				if (prio > 0) {
-					warehouse_needed = true;
-				} else {
-					prio = 0;
-				}
-
-				// But we still can built a port if it is first one
-				if (bo.is_port && bo.total_count() == 0 && productionsites.size() > 5 &&
-				    !bf->enemy_nearby && bf->is_portspace && seafaring_economy) {
-					prio += productionsites.size();
-					warehouse_needed = true;
-				}
-
-				if (!warehouse_needed) {
-					continue;
-				}
-
-				// we prefer ports to a normal warehouse
-				if (bo.is_port) {
-					prio += 15;
-				}
-
-				// it is good to have more then 1 warehouse
-				if (numof_warehouses_ == 1) {
-					prio += 10;
-				}
+				if (bf->near_border && !bo.is(BuildingAttribute::kPort)) {
+					continue;
+				}
+				assert(bf->is_portspace != ExtendedBool::kUnset);
+				if (bf->is_portspace != ExtendedBool::kTrue && bo.is(BuildingAttribute::kPort)) {
+					continue;
+				}
+				prio = bo.primary_priority;
 
 				// iterating over current warehouses and testing a distance
 				// getting distance to nearest warehouse and adding it to a score
@@ -2296,7 +2805,13 @@
 				// but limit to 30
 				const uint16_t max_distance_considered = 30;
 				nearest_distance = std::min(nearest_distance, max_distance_considered);
-				prio += nearest_distance - 30;
+				if (nearest_distance < 13) {
+					continue;
+				}
+				prio +=
+				   management_data.neuron_pool[47].get_result_safe(nearest_distance / 2, kAbsValue) / 2;
+
+				prio += bf->own_non_military_nearby * 3;
 
 				// dont be close to enemies
 				if (bf->enemy_nearby) {
@@ -2304,7 +2819,8 @@
 				}
 
 				// being too close to a border is not good either
-				if (bf->unowned_land_nearby && !bo.is_port && prio > 0) {
+				if ((bf->unowned_land_nearby || bf->enemy_owned_land_nearby > 10) &&
+				    !bo.is(BuildingAttribute::kPort) && prio > 0) {
 					prio /= 2;
 					prio -= 10;
 				}
@@ -2331,7 +2847,7 @@
 					prio -= 20;
 				}
 
-				if (bf->unowned_land_nearby) {
+				if (bf->unowned_land_nearby || bf->enemy_owned_land_nearby) {
 					prio -= 15;
 				}
 			}
@@ -2347,7 +2863,7 @@
 			}
 
 			// testing also vicinity
-			if (!bo.is_port) {
+			if (!bo.is(BuildingAttribute::kPort)) {
 				if (port_reserved_coords.count(bf->coords.hash()) > 0) {
 					continue;
 				}
@@ -2361,11 +2877,11 @@
 			   (spots_avail.at(BUILDCAPS_MEDIUM) < 5 || spots_avail.at(BUILDCAPS_BIG) < 5);
 
 			if (space_stress && bo.type == BuildingObserver::Type::kMilitarysite) {
-				prio -= (bf->max_buildcap_nearby - bo.desc->get_size()) * 3;
+				prio -= (bf->max_buildcap_nearby - bo.desc->get_size()) * 10;
 			} else if (space_stress) {
-				prio -= (bf->max_buildcap_nearby - bo.desc->get_size()) * 10;
+				prio -= (bf->max_buildcap_nearby - bo.desc->get_size()) * 30;
 			} else {
-				prio -= (bf->max_buildcap_nearby - bo.desc->get_size()) * 3;
+				prio -= (bf->max_buildcap_nearby - bo.desc->get_size()) * 5;
 			}
 
 			// prefer vicinity of ports (with exemption of warehouses)
@@ -2377,6 +2893,12 @@
 				highest_nonmil_prio_ = prio;
 			}
 
+			if (bo.type == BuildingObserver::Type::kMilitarysite) {
+				if (prio <= persistent_data->target_military_score) {
+					continue;
+				}
+			}
+
 			if (prio > proposed_priority) {
 				best_building = &bo;
 				proposed_priority = prio;
@@ -2402,10 +2924,6 @@
 					continue;
 				}
 
-				if (gametime - bo.construction_decision_time < kBuildingMinInterval) {
-					continue;
-				}
-
 				assert(bo.new_building != BuildingNecessity::kAllowed);
 
 				// skip if a mine is not required
@@ -2414,23 +2932,6 @@
 					continue;
 				}
 
-				// this is penalty if there are existing mines too close
-				// it is treated as multiplier for count of near mines
-				uint32_t nearness_penalty = 0;
-				if ((mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished) ==
-				    0) {
-					nearness_penalty = 0;
-				} else {
-					nearness_penalty = 40;
-				}
-
-				// bonus score to prefer if too few mines
-				uint32_t bonus_score = 0;
-				if ((mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished) ==
-				    0) {
-					bonus_score = 2 * bo.primary_priority;
-				}
-
 				// iterating over fields
 				for (std::list<MineableField*>::iterator j = mineable_fields.begin();
 				     j != mineable_fields.end(); ++j) {
@@ -2445,6 +2946,11 @@
 						continue;
 					}
 
+					// Continue if field is blocked at the moment
+					if (blocked_fields.is_blocked(mf->coords)) {
+						continue;
+					}
+
 					int32_t prio = 0;
 					MapRegion<Area<FCoords>> mr(map, Area<FCoords>(mf->coords, 2));
 					do {
@@ -2461,10 +2967,7 @@
 					}
 
 					// applying nearnes penalty
-					prio -= mf->mines_nearby * nearness_penalty;
-
-					// applying bonus score
-					prio += bonus_score;
+					prio -= mf->mines_nearby * std::abs(management_data.get_military_number_at(126));
 
 					// applying max needed
 					prio += bo.primary_priority;
@@ -2508,7 +3011,13 @@
 	}
 
 	if (best_building->type == BuildingObserver::Type::kMilitarysite) {
+		assert(proposed_priority >= persistent_data->least_military_score);
 		persistent_data->target_military_score = proposed_priority;
+		if (persistent_data->target_military_score >
+		    persistent_data->ai_personality_mil_upper_limit) {
+			persistent_data->ai_personality_mil_upper_limit = persistent_data->target_military_score;
+		}
+		assert(proposed_priority >= persistent_data->least_military_score);
 	}
 
 	// send the command to construct a new building
@@ -2521,17 +3030,18 @@
 	// we block also nearby fields
 	// if farms and so on, for quite a long time
 	// if military sites only for short time for AI can update information on near buildable fields
-	if ((best_building->space_consumer && !best_building->plants_trees) ||
+	if ((best_building->is(BuildingAttribute::kSpaceConsumer) &&
+	     !best_building->is(BuildingAttribute::kRanger)) ||
 	    best_building->type == BuildingObserver::Type::kMilitarysite) {
 		uint32_t block_time = 0;
 		uint32_t block_area = 0;
-		if (best_building->space_consumer) {
+		if (best_building->is(BuildingAttribute::kSpaceConsumer)) {
 			if (spots_ > kSpotsEnough) {
 				block_time = 45 * 60 * 1000;
 			} else {
-				block_time = 10 * 60 * 1000;
+				block_time = 15 * 60 * 1000;
 			}
-			block_area = 3;
+			block_area = 5;
 		} else {  // militray buildings for a very short time
 			block_time = 25 * 1000;
 			block_area = 6;
@@ -2543,9 +3053,14 @@
 		} while (mr.advance(map));
 	}
 
+	if (best_building->is(BuildingAttribute::kRecruitment)) {
+		log("%2d: Building a recruitment site: %s\n", player_number(), best_building->name);
+	}
+
 	if (!(best_building->type == BuildingObserver::Type::kMilitarysite)) {
 		best_building->construction_decision_time = gametime;
-	} else {  // very ugly hack here
+	} else {
+
 		military_last_build_ = gametime;
 		best_building->construction_decision_time = gametime - kBuildingMinInterval / 2;
 	}
@@ -2601,7 +3116,7 @@
 		// Occasionaly (not more then once in 15 seconds) we test if the road can be dismantled
 		// if there is shortage of spots we do it always
 		if (last_road_dismantled_ + 15 * 1000 < gametime &&
-		    (gametime % 5 == 0 || spots_ < kSpotsTooLittle)) {
+		    (std::rand() % 5 == 0 || spots_ <= kSpotsEnough)) {
 			const Road& road = *roads.front();
 			if (dispensable_road_test(*const_cast<Road*>(&road))) {
 				game().send_player_bulldoze(*const_cast<Road*>(&road));
@@ -2665,7 +3180,7 @@
 	} else if (!has_building && flag.nr_of_roads() == 1) {
 		// This is end of road without any building, we do not initiate interconnection thus
 		return false;
-	} else if (flag.nr_of_roads() == 1 || gametime % 10 == 0) {
+	} else if (flag.nr_of_roads() == 1 || std::rand() % 10 == 0) {
 		if (spots_ > kSpotsEnough) {
 			// This is the normal situation
 			create_shortcut_road(flag, 15, 22, gametime);
@@ -2681,7 +3196,7 @@
 		}
 		// a warehouse with 3 or less roads
 	} else if (is_warehouse && flag.nr_of_roads() <= 3) {
-		create_shortcut_road(flag, 9, -5, gametime);
+		create_shortcut_road(flag, 9, -10, gametime);
 		inhibit_road_building_ = gametime + 400;
 		// and when a flag is full with wares
 	} else if (spots_ > kSpotsEnough && flag.current_wares() > 5) {
@@ -2705,8 +3220,10 @@
 
 	// We do not dismantle (even consider it) if the road is busy (some wares on flags), unless there
 	// is shortage of build spots
-	if (spots_ > kSpotsTooLittle &&
-	    roadstartflag.current_wares() + roadendflag.current_wares() > 0) {
+
+	if (spots_ > kSpotsEnough && roadstartflag.current_wares() + roadendflag.current_wares() > 0) {
+		return false;
+	} else if (roadstartflag.current_wares() + roadendflag.current_wares() > 2) {
 		return false;
 	}
 
@@ -2831,7 +3348,7 @@
 				   get_building_observer(constructionsite->building().name().c_str());
 				// first very special case - a port (in the phase of constructionsite)
 				// this might be a new colonization port
-				if (bo.is_port) {
+				if (bo.is(BuildingAttribute::kPort)) {
 					eco->dismantle_grace_time = gametime + 60 * 60 * 1000;  // one hour should be enough
 				} else {  // other constructionsites, usually new (standalone) constructionsites
 					eco->dismantle_grace_time =
@@ -2992,7 +3509,7 @@
 
 	// Well and finally building the winning road
 	uint32_t winner_hash = 0;
-	if (RoadCandidates.get_winner(&winner_hash, (gametime % 4 > 0) ? 1 : 2)) {
+	if (RoadCandidates.get_winner(&winner_hash)) {
 		const Widelands::Coords target_coords = Coords::unhash(winner_hash);
 		Path& path = *new Path();
 #ifndef NDEBUG
@@ -3072,6 +3589,8 @@
 		return false;
 	}
 
+	const PlayerNumber pn = player_number();
+
 	// Reorder and set new values; - better now because there are multiple returns in the function
 	productionsites.push_back(productionsites.front());
 	productionsites.pop_front();
@@ -3113,6 +3632,10 @@
 
 	bool considering_upgrade = enhancement != INVALID_INDEX;
 
+	if (!basic_economy_established && management_data.f_neuron_pool[17].get_position(2)) {
+		considering_upgrade = false;
+	}
+
 	// First we check for rare case when input wares are set to 0 but AI is not aware that
 	// the site is pending for upgrade - one possible cause is this is a freshly loaded game
 	if (!site.upgrade_pending) {
@@ -3133,13 +3656,9 @@
 
 	if (site.upgrade_pending) {
 		// The site is in process of emptying its input queues
-		// Counting remaining wares in the site now
-		int32_t left_wares = 0;
-		for (auto& queue : site.site->inputqueues()) {
-			left_wares += queue->get_filled();
-		}
 		// Do nothing when some wares are left, but do not wait more then 4 minutes
-		if (site.bo->construction_decision_time + 4 * 60 * 1000 > gametime && left_wares > 0) {
+		if (site.bo->construction_decision_time + 4 * 60 * 1000 > gametime &&
+		    set_inputs_to_zero(site) > 0) {
 			return false;
 		}
 		assert(site.bo->cnt_upgrade_pending == 1);
@@ -3167,7 +3686,7 @@
 	// then 10 minutes. Otherwise the site must be older then 20 minutes and
 	// gametime > 45 minutes.
 	if (considering_upgrade) {
-		if (site.bo->upgrade_extends) {
+		if (site.bo->is(BuildingAttribute::kUpgradeExtends)) {
 			if (gametime < site.built_time + 10 * 60 * 1000) {
 				considering_upgrade = false;
 			}
@@ -3219,12 +3738,7 @@
 
 		// Here we just restrict input wares to 0 and set flag 'upgrade_pending' to true
 		if (doing_upgrade) {
-
-			// reducing input queues
-			for (auto& queue : site.site->inputqueues()) {
-				game().send_player_set_input_max_fill(
-				   *site.site, queue->get_index(), queue->get_type(), 0);
-			}
+			set_inputs_to_zero(site);
 			site.bo->construction_decision_time = gametime;
 			en_bo.construction_decision_time = gametime;
 			site.upgrade_pending = true;
@@ -3233,11 +3747,80 @@
 		}
 	}
 
+	// Barracks
+	if (site.bo->is(BuildingAttribute::kBarracks)) {
+		assert(site.bo->total_count() == 1);
+		for (auto& queue : site.site->inputqueues()) {
+			if (queue->get_max_fill() > 4) {
+				game().send_player_set_input_max_fill(
+				   *site.site, queue->get_index(), queue->get_type(), 4);
+			}
+		}
+
+		// AI takes multiple inputs into account and makes decision if barracks to be stopped/started
+		int16_t tmp_score = 0;
+		int16_t inputs[f_neuron_bit_size] = {0};
+		tmp_score += (soldier_status_ == SoldiersStatus::kBadShortage) * 8;
+		tmp_score += (soldier_status_ == SoldiersStatus::kShortage) * 4;
+		tmp_score += (soldier_status_ == SoldiersStatus::kEnough) * 2;
+		tmp_score += (soldier_status_ == SoldiersStatus::kFull) * 1;
+		inputs[2] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) * -1;
+		inputs[3] = (expansion_type.get_expansion_type() == ExpansionMode::kSpace) * 1;
+		inputs[4] = -1;
+		inputs[5] = -2;
+		inputs[6] = -3;
+		inputs[14] =
+		   (player_statistics.get_player_power(pn) < player_statistics.get_old_player_power(pn)) * 1;
+		inputs[15] =
+		   (player_statistics.get_player_power(pn) < player_statistics.get_old60_player_power(pn)) *
+		   1;
+		inputs[16] =
+		   (player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn)) * 1;
+		inputs[17] =
+		   (player_statistics.get_player_power(pn) > player_statistics.get_old60_player_power(pn)) *
+		   1;
+		inputs[18] = (expansion_type.get_expansion_type() == ExpansionMode::kSpace) * -1;
+		inputs[19] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) * 1;
+		inputs[20] = 1;
+		inputs[21] = 2;
+		inputs[22] = 3;
+		inputs[23] = (ts_without_trainers_ > 0) ? -1 : 0;
+		inputs[24] = (ts_without_trainers_ > 0) ? -2 : 0;
+		inputs[25] = (ts_without_trainers_ > 0) ? -3 : 0;
+		for (uint8_t i = 0; i < f_neuron_bit_size; i += 1) {
+			if (management_data.f_neuron_pool[24].get_position(i)) {
+				tmp_score += inputs[i];
+			}
+		}
+
+		// starting the site
+		if (site.site->is_stopped() && tmp_score >= 0) {
+			game().send_player_start_stop_building(*site.site);
+			for (auto& queue : site.site->inputqueues()) {
+				game().send_player_set_input_max_fill(
+				   *site.site, queue->get_index(), queue->get_type(), 4);
+			}
+		}
+		// stopping the site
+		if (!site.site->is_stopped() && tmp_score < 0) {
+			game().send_player_start_stop_building(*site.site);
+			for (auto& queue : site.site->inputqueues()) {
+				game().send_player_set_input_max_fill(
+				   *site.site, queue->get_index(), queue->get_type(), 2);
+			}
+		}
+	}
+
 	// Lumberjack / Woodcutter handling
-	if (site.bo->need_trees) {
+	if (site.bo->is(BuildingAttribute::kLumberjack)) {
 
 		// do not dismantle immediatelly
-		if ((game().get_gametime() - site.built_time) < 4 * 60 * 1000) {
+		if ((game().get_gametime() - site.built_time) < 6 * 60 * 1000) {
+			return false;
+		}
+
+		// Do not destruct the last few lumberjacks
+		if (site.bo->cnt_built <= site.bo->cnt_target) {
 			return false;
 		}
 
@@ -3245,33 +3828,12 @@
 		   Area<FCoords>(map.get_fcoords(site.site->get_position()), radius), nullptr,
 		   FindImmovableAttribute(MapObjectDescr::get_attribute_id("tree")));
 
-		// generally, trees_around_cutters = remaining_trees + 9 *
-		// persistent_data->trees_around_cutters
-		// but keep in mind that trees_around_cutters is multiplied by 10
-		persistent_data->trees_around_cutters =
-		   (remaining_trees * 10 + 9 * persistent_data->trees_around_cutters) / 10;
-
-		// Do not destruct the last few lumberjacks
-		if (site.bo->cnt_built <= site.bo->cnt_target) {
-			return false;
-		}
-
-		if (site.site->get_statistics_percent() > 20) {
-			return false;
-		}
-
-		// do not dismantle if there are some trees remaining
-		if (remaining_trees > 5) {
-			return false;
-		}
-
-		if (site.bo->stocklevel_time < game().get_gametime() - 10 * 1000) {
-			site.bo->stocklevel = get_stocklevel(*site.bo);
-			site.bo->stocklevel_time = game().get_gametime();
-		}
-
-		// if we need wood badly
-		if (remaining_trees > 0 && site.bo->stocklevel <= 50) {
+		if (site.site->get_statistics_percent() >
+		    std::abs(management_data.get_military_number_at(117)) / 2) {
+			return false;
+		}
+
+		if (remaining_trees > trees_nearby_treshold_ / 3) {
 			return false;
 		}
 
@@ -3288,7 +3850,7 @@
 	}
 
 	// Wells handling
-	if (site.bo->mines_water) {
+	if (site.bo->is(BuildingAttribute::kWell)) {
 		if (site.unoccupied_till + 6 * 60 * 1000 < gametime &&
 		    site.site->get_statistics_percent() == 0) {
 			site.bo->last_dismantle_time = gametime;
@@ -3310,11 +3872,7 @@
 		// now we test the stocklevel and dismantle the well if we have enough water
 		// but first we make sure we do not dismantle a well too soon
 		// after dismantling previous one
-		if (site.bo->stocklevel_time < game().get_gametime() - 5 * 1000) {
-			site.bo->stocklevel = get_stocklevel(*site.bo);
-			site.bo->stocklevel_time = game().get_gametime();
-		}
-		if (site.bo->stocklevel > 250 + productionsites.size() * 5) {  // dismantle
+		if (get_stocklevel(*site.bo, gametime) > 250 + productionsites.size() * 5) {  // dismantle
 			site.bo->last_dismantle_time = game().get_gametime();
 			flags_to_be_removed.push_back(site.site->base_flag().get_position());
 			if (connected_to_wh) {
@@ -3329,7 +3887,7 @@
 	}
 
 	// Quarry handling
-	if (site.bo->need_rocks) {
+	if (site.bo->is(BuildingAttribute::kNeedsRocks)) {
 
 		if (map.find_immovables(Area<FCoords>(map.get_fcoords(site.site->get_position()), 6), nullptr,
 
@@ -3365,20 +3923,16 @@
 	}
 
 	// All other SPACE_CONSUMERS without input and above target_count
-	if (site.bo->inputs.empty()                              // does not consume anything
-	    && site.bo->production_hint == -1                    // not a renewing building (forester...)
-	    && site.unoccupied_till + 10 * 60 * 1000 < gametime  // > 10 minutes old
-	    && site.site->can_start_working()                    // building is occupied
-	    && site.bo->space_consumer && !site.bo->plants_trees) {
+	if (site.bo->inputs.empty()            // does not consume anything
+	    && site.bo->production_hint == -1  // not a renewing building (forester...)
+	    && site.bo->is(BuildingAttribute::kSpaceConsumer) &&
+	    !site.bo->is(BuildingAttribute::kRanger)) {
 
 		// if we have more buildings then target
-		if ((site.bo->cnt_built - site.bo->unconnected_count) > site.bo->cnt_target) {
-			if (site.bo->stocklevel_time < game().get_gametime() - 5 * 1000) {
-				site.bo->stocklevel = get_stocklevel(*site.bo);
-				site.bo->stocklevel_time = game().get_gametime();
-			}
+		if ((site.bo->cnt_built - site.bo->unconnected_count) > site.bo->cnt_target &&
+		    site.unoccupied_till + 10 * 60 * 1000 < gametime && site.site->can_start_working()) {
 
-			if (site.site->get_statistics_percent() < 30 && site.bo->stocklevel > 100) {
+			if (site.site->get_statistics_percent() < 30 && get_stocklevel(*site.bo, gametime) > 100) {
 				site.bo->last_dismantle_time = game().get_gametime();
 				flags_to_be_removed.push_back(site.site->base_flag().get_position());
 				if (connected_to_wh) {
@@ -3391,7 +3945,8 @@
 		}
 
 		// a building can be dismanteld if it performs too bad, if it is not the last one
-		if (site.site->get_statistics_percent() <= 10 && site.bo->cnt_built > 1) {
+		if (site.site->get_statistics_percent() <= 10 && site.bo->cnt_built > 1 &&
+		    site.unoccupied_till + 10 * 60 * 1000 < gametime && site.site->can_start_working()) {
 
 			flags_to_be_removed.push_back(site.site->base_flag().get_position());
 			if (connected_to_wh) {
@@ -3402,6 +3957,15 @@
 			return true;
 		}
 
+		// Blocking the vicinity if too low performance and ware is still needed
+		if (site.site->get_statistics_percent() <= 50 || get_stocklevel(*site.bo, gametime) < 5) {
+			MapRegion<Area<FCoords>> mr(
+			   map, Area<FCoords>(map.get_fcoords(site.site->base_flag().get_position()), 5));
+			do {
+				blocked_fields.add(mr.location(), gametime + 5 * 60 * 100);
+			} while (mr.advance(map));
+		}
+
 		return false;
 	}
 
@@ -3429,7 +3993,8 @@
 	// remaining buildings without inputs and not supporting ones (fishers only left probably and
 	// hunters)
 	if (site.bo->inputs.empty() && site.bo->production_hint < 0 && site.site->can_start_working() &&
-	    !site.bo->space_consumer && site.site->get_statistics_percent() < 10 &&
+	    !site.bo->is(BuildingAttribute::kSpaceConsumer) &&
+	    site.site->get_statistics_percent() < 10 &&
 	    ((game().get_gametime() - site.built_time) > 10 * 60 * 1000)) {
 
 		site.bo->last_dismantle_time = game().get_gametime();
@@ -3446,7 +4011,7 @@
 	// stop/start them based on stock avaiable
 	if (site.bo->production_hint >= 0) {
 
-		if (!site.bo->plants_trees) {
+		if (!site.bo->is(BuildingAttribute::kRanger)) {
 			// other supporting sites, like fish breeders, gamekeepers are not dismantled at all
 			return false;
 		}
@@ -3513,16 +4078,43 @@
 
 	const bool connected_to_wh = !site.site->get_economy()->warehouses().empty();
 
+	// First we dismantle mines that are marked as such, generally we wait till all wares all gone
+	if (site.dismantle_pending_since != kNever) {
+		assert(site.dismantle_pending_since <= gametime);
+		if (set_inputs_to_zero(site) || site.dismantle_pending_since + 5 * 60 * 1000 < gametime) {
+			flags_to_be_removed.push_back(site.site->base_flag().get_position());
+			if (connected_to_wh) {
+				game().send_player_dismantle(*site.site);
+			} else {
+				game().send_player_bulldoze(*site.site);
+			}
+
+			return true;
+		} else if (site.dismantle_pending_since + 3 * 60 * 1000 < gametime) {
+			stop_site(site);
+			return false;
+		} else {
+			return false;
+		}
+	} else if (site.site->can_start_working()) {
+		set_inputs_to_max(site);
+	} else {
+		set_inputs_to_zero(site);
+	}
+
+	// Single _critical is a critical mine if it is only of its type, so it needs special treatment
+	bool single_critical = false;
+	if ((site.bo->is(BuildingAttribute::kBuildingMatProducer) || site.bo->mines == iron_ore_id) &&
+	    mines_per_type[site.bo->mines].total_count() == 1) {
+		single_critical = true;
+	}
+
 	// first get rid of mines that are missing workers for some time (6 minutes),
 	// released worker (if any) can be usefull elsewhere !
-	if (site.built_time + 6 * 60 * 1000 < gametime && !site.site->can_start_working()) {
-		flags_to_be_removed.push_back(site.site->base_flag().get_position());
-		if (connected_to_wh) {
-			game().send_player_dismantle(*site.site);
-		} else {
-			game().send_player_bulldoze(*site.site);
-		}
-		return true;
+	if (!single_critical && site.built_time + 6 * 60 * 1000 < gametime &&
+	    !site.site->can_start_working()) {
+		initiate_dismantlement(site, gametime);
+		return false;
 	}
 
 	// to avoid problems with uint underflow, we discourage considerations below
@@ -3530,11 +4122,26 @@
 		return false;
 	}
 
+	// After 20 minutes in existence we check whether a miner is needed for a critical unoccupied
+	// mine elsewhere
+	if (site.built_time + 20 * 60 * 1000 < gametime && gametime % 5 == 0) {
+		if (!mines_per_type[site.bo->mines].is_critical && critical_mine_unoccupied(gametime)) {
+			initiate_dismantlement(site, gametime);
+			return true;
+		}
+	}
+
 	// if mine is working, doing nothing
 	if (site.no_resources_since > gametime - 5 * 60 * 1000) {
 		return false;
 	}
 
+	// Out of resources, first check whether a mines is not needed for critical mine
+	if (!mines_per_type[site.bo->mines].is_critical && critical_mine_unoccupied(gametime)) {
+		initiate_dismantlement(site, gametime);
+		return true;
+	}
+
 	// Check whether building is enhanceable. If yes consider an upgrade.
 	const DescriptionIndex enhancement = site.site->descr().enhancement();
 	bool has_upgrade = false;
@@ -3548,30 +4155,19 @@
 	// (we will not dismantle even if there are no mineable resources left for this level of mine
 	// and output is not needed)
 	bool forcing_upgrade = false;
-	const uint16_t minimal_mines_count = (site.bo->produces_building_material) ? 2 : 1;
+	const uint16_t minimal_mines_count =
+	   (site.bo->is(BuildingAttribute::kBuildingMatProducer)) ? 2 : 1;
 	if (has_upgrade && mines_per_type[site.bo->mines].total_count() <= minimal_mines_count) {
 		forcing_upgrade = true;
 	}
 
 	// dismantling a mine
 	if (!has_upgrade) {  // if no upgrade, now
-		flags_to_be_removed.push_back(site.site->base_flag().get_position());
-		if (connected_to_wh) {
-			game().send_player_dismantle(*site.site);
-		} else {
-			game().send_player_bulldoze(*site.site);
-		}
-		site.bo->construction_decision_time = gametime;
+		initiate_dismantlement(site, gametime);
 		return true;
 		// if having an upgrade, after half hour
 	} else if (site.no_resources_since < gametime - 30 * 60 * 1000 && !forcing_upgrade) {
-		flags_to_be_removed.push_back(site.site->base_flag().get_position());
-		if (connected_to_wh) {
-			game().send_player_dismantle(*site.site);
-		} else {
-			game().send_player_bulldoze(*site.site);
-		}
-		site.bo->construction_decision_time = gametime;
+		initiate_dismantlement(site, gametime);
 		return true;
 	}
 
@@ -3625,20 +4221,59 @@
 	return changed;
 }
 
-// this count ware as hints
-uint32_t DefaultAI::get_stocklevel_by_hint(size_t hintoutput) {
-	uint32_t count = 0;
-	DescriptionIndex wt(hintoutput);
-	for (EconomyObserver* observer : economies) {
-		// Don't check if the economy has no warehouse.
-		if (observer->economy.warehouses().empty()) {
-			continue;
-		}
-
-		count += observer->economy.stock_ware(wt);
-	}
-
-	return count;
+BuildingNecessity DefaultAI::check_warehouse_necessity(BuildingObserver& bo,
+                                                       const uint32_t gametime) {
+	bo.primary_priority = 0;
+
+	if (numof_warehouses_in_const_ > 0 ||
+	    bo.aimode_limit_status() != AiModeBuildings::kAnotherAllowed || !basic_economy_established) {
+		bo.new_building_overdue = 0;
+		bo.primary_priority = 0;
+		return BuildingNecessity::kForbidden;
+	}
+
+	if (bo.is_what.count(BuildingAttribute::kPort) && !seafaring_economy) {
+		bo.new_building_overdue = 0;
+		bo.primary_priority = 0;
+		return BuildingNecessity::kForbidden;
+	}
+
+	bo.primary_priority = 0;
+
+	//  Build one warehouse for ~every 35 productionsites and mines_.
+	//  Militarysites are slightly important as well, to have a bigger
+	//  chance for a warehouses (containing waiting soldiers or wares
+	//  needed for soldier training) near the frontier.
+	int32_t needed_count = static_cast<int32_t>(productionsites.size() + mines_.size()) /
+	                          (40 + management_data.get_military_number_at(21) / 10) +
+	                       1;
+	assert(needed_count >= 0 &&
+	       needed_count <= (static_cast<uint16_t>(productionsites.size() + mines_.size()) / 10) + 2);
+
+	if (player_statistics.any_enemy_seen_lately(gametime) +
+	       (productionsites.size() + mines_.size()) >
+	    10) {
+		needed_count += 1;
+	}
+
+	if (bo.is_what.count(BuildingAttribute::kPort) &&
+	    (productionsites.size() + mines_.size()) > 10) {
+		needed_count += 1;
+	}
+
+	if (needed_count <= numof_warehouses_in_const_ + numof_warehouses_) {
+		bo.new_building_overdue = 0;
+		return BuildingNecessity::kForbidden;
+	}
+
+	// So now we know the warehouse here is needed.
+	bo.primary_priority = 1 +
+	                      (needed_count - numof_warehouses_in_const_ - numof_warehouses_) *
+	                         std::abs(management_data.get_military_number_at(22));
+	bo.new_building_overdue += 1;
+	bo.primary_priority +=
+	   bo.new_building_overdue * std::abs(management_data.get_military_number_at(16)) / 40;
+	return BuildingNecessity::kAllowed;
 }
 
 // this receives an building observer and have to decide if new/one of
@@ -3650,6 +4285,26 @@
                                                       const PerfEvaluation purpose,
                                                       const uint32_t gametime) {
 
+	bo.primary_priority = 0;
+
+	BasicEconomyBuildingStatus site_needed_for_economy = BasicEconomyBuildingStatus::kNone;
+	if (gametime > 2 * 60 * 1000 && gametime < 120 * 60 * 1000 && !basic_economy_established) {
+		if (persistent_data->remaining_basic_buildings.count(bo.id)) {
+			if (static_cast<uint32_t>(bo.total_count()) >=
+			    persistent_data->remaining_basic_buildings[bo.id]) {  // exemption for sawmill
+				site_needed_for_economy = BasicEconomyBuildingStatus::kDiscouraged;
+
+			} else if (spots_ < kSpotsTooLittle && bo.type != BuildingObserver::Type::kMine) {
+				site_needed_for_economy = BasicEconomyBuildingStatus::kNeutral;
+			} else {
+				site_needed_for_economy = BasicEconomyBuildingStatus::kEncouraged;
+			}
+
+		} else if (persistent_data->remaining_basic_buildings.count(bo.id) == 0) {
+			site_needed_for_economy = BasicEconomyBuildingStatus::kNone;
+		}
+	}
+
 	// Very first we finds if AI is allowed to build such building due to its mode
 	if (purpose == PerfEvaluation::kForConstruction &&
 	    bo.aimode_limit_status() != AiModeBuildings::kAnotherAllowed) {
@@ -3659,43 +4314,58 @@
 	// First we deal with training sites, they are separate category
 	if (bo.type == BuildingObserver::Type::kTrainingsite) {
 
-		bo.primary_priority = 0;
-		if (bo.aimode_limit_status() != AiModeBuildings::kAnotherAllowed) {
-			return BuildingNecessity::kNotNeeded;
-		} else if (ts_without_trainers_ || (ts_basic_const_count_ + ts_advanced_const_count_) > 0) {
+		if (!basic_economy_established && management_data.f_neuron_pool[17].get_position(1)) {
+			return BuildingNecessity::kNotNeeded;
+		} else if (bo.aimode_limit_status() != AiModeBuildings::kAnotherAllowed) {
+			return BuildingNecessity::kNotNeeded;
+		} else if (ts_without_trainers_ > 0 || bo.cnt_under_construction > 0 ||
+		           ts_in_const_count_ > 1) {
 			return BuildingNecessity::kNotNeeded;
 		} else if (bo.prohibited_till > gametime) {
 			return BuildingNecessity::kNotNeeded;
-		} else if (bo.build_material_shortage) {
+		} else if (ts_without_trainers_ > 1) {
 			return BuildingNecessity::kNotNeeded;
 		}
 
 		// It seems we might need it after all
 		bo.primary_priority = -30;
+		if (bo.build_material_shortage) {
+			bo.primary_priority -= std::abs(management_data.get_military_number_at(72));
+		}
 
-		if (bo.forced_after < gametime && bo.total_count() == 0) {
-			bo.primary_priority += 50;
+		if (bo.forced_after > gametime && bo.total_count() == 0) {
+			bo.primary_priority += 50 + std::abs(management_data.get_military_number_at(112) / 5);
 		}
 
 		// If we are close to enemy (was seen in last 15 minutes)
-		if (enemy_last_seen_ < gametime && enemy_last_seen_ + 15 * 60 * 1000 > gametime) {
-			bo.primary_priority += 10;
+		if (player_statistics.any_enemy_seen_lately(gametime)) {
+			bo.primary_priority += std::abs(management_data.get_military_number_at(57) / 2);
 		}
 
 		// We build one trainig site per X military sites
 		// with some variations, of course
-		bo.primary_priority += static_cast<int32_t>(militarysites.size() + productionsites.size()) -
-		                       50 * (ts_basic_count_ + ts_advanced_count_);
+		int32_t target = 1 +
+		                 static_cast<int32_t>(militarysites.size() + productionsites.size()) /
+		                    (std::abs(management_data.get_military_number_at(113) / 2) + 1);
+		assert(target > 0 && target < 500);
 
-		// We prefer basic trainingsites and sites with lower count
-		if (bo.trainingsite_type == TrainingSiteType::kBasic) {
-			bo.primary_priority += 1;
+		uint16_t current_proportion = 0;
+		if (ts_finished_count_ + ts_in_const_count_ > 0) {
+			current_proportion = bo.total_count() * 100 / (ts_finished_count_ + ts_in_const_count_);
 		}
-		bo.primary_priority -= 2 * bo.total_count();
+
+		bo.primary_priority += (target - ts_finished_count_ - ts_in_const_count_) *
+		                       std::abs(management_data.get_military_number_at(114) * 2);
+		bo.primary_priority += (static_cast<int32_t>(militarysites.size() + productionsites.size()) -
+		                        target * std::abs(management_data.get_military_number_at(78) / 4)) *
+		                       3;
 
 		// Special bonus for very first site of type
 		if (bo.total_count() == 0) {
-			bo.primary_priority += 30;
+			bo.primary_priority += std::abs(management_data.get_military_number_at(56)) +
+			                       bo.max_ts_proportion - current_proportion;
+		} else if (bo.max_ts_proportion < current_proportion) {
+			bo.primary_priority -= std::abs(management_data.get_military_number_at(128) * 3);
 		}
 
 		if (bo.primary_priority > 0) {
@@ -3705,44 +4375,107 @@
 		}
 	}
 
+	if (bo.is(BuildingAttribute::kRecruitment)) {
+		if (bo.total_count() > 1) {
+			return BuildingNecessity::kForbidden;
+		}
+		if (critical_mine_unoccupied(gametime)) {
+			return BuildingNecessity::kForbidden;
+		}
+		if (!basic_economy_established) {
+			return BuildingNecessity::kForbidden;
+		}
+		const uint16_t min_roads_count = 50 + std::abs(management_data.get_military_number_at(33));
+		if (roads.size() < min_roads_count) {
+			return BuildingNecessity::kForbidden;
+		}
+		bo.primary_priority = (roads.size() - min_roads_count) *
+		                      (2 + std::abs(management_data.get_military_number_at(143)) / 5);
+		return BuildingNecessity::kNeeded;
+	}
+
 	// Let deal with productionsites now
 	// First we iterate over outputs of building, count warehoused stock
 	// and deciding if we have enough on stock (in warehouses)
 	bo.max_preciousness = 0;
 	bo.max_needed_preciousness = 0;
 
-	for (uint32_t m = 0; m < bo.outputs.size(); ++m) {
-		DescriptionIndex wt(static_cast<size_t>(bo.outputs.at(m)));
-
-		uint16_t target = tribe_->get_ware_descr(wt)->default_target_quantity(tribe_->name()) / 3;
-		// at least  1
-		target = std::max<uint16_t>(target, 1);
-
-		uint16_t preciousness = wares.at(bo.outputs.at(m)).preciousness;
-		if (preciousness < 1) {  // it seems there are wares with 0 preciousness
-			preciousness = 1;     // (no entry in conf files?). But we need positive value here
-		}
-
-		if (get_warehoused_stock(wt) < target) {
-			if (bo.max_needed_preciousness < preciousness) {
-				bo.max_needed_preciousness = preciousness;
-			}
-		}
-
-		if (bo.max_preciousness < preciousness) {
-			bo.max_preciousness = preciousness;
-		}
-	}
+	if (!bo.is(BuildingAttribute::kBarracks)) {  // barracks are now excluded from calculation
+		// preciousness is assigned below in this fuction
+		for (uint32_t m = 0; m < bo.outputs.size(); ++m) {
+			DescriptionIndex wt(static_cast<size_t>(bo.outputs.at(m)));
+
+			uint16_t target = tribe_->get_ware_descr(wt)->default_target_quantity(tribe_->name());
+			if (target == Widelands::kInvalidWare) {
+				target = kTargetQuantCap;
+			}
+			target /= 3;
+
+			// at least  1
+			target = std::max<uint16_t>(target, 1);
+
+			uint16_t preciousness = wares.at(bo.outputs.at(m)).preciousness;
+			if (preciousness < 1) {  // it seems there are wares with 0 preciousness
+				preciousness = 1;     // (no entry in conf files?). But we need positive value here
+			}
+
+			if (calculate_stocklevel(wt) < target ||
+			    site_needed_for_economy == BasicEconomyBuildingStatus::kEncouraged) {
+				if (bo.max_needed_preciousness < preciousness) {
+					bo.max_needed_preciousness = preciousness;
+				}
+				if (site_needed_for_economy == BasicEconomyBuildingStatus::kEncouraged) {
+					bo.max_needed_preciousness +=
+					   std::abs(management_data.get_military_number_at(144)) / 2;
+				}
+			}
+
+			if (bo.max_preciousness < preciousness) {
+				bo.max_preciousness = preciousness;
+			}
+		}
+	}
+
+	// Do we have enough input materials on stock?
+	bool inputs_on_stock = true;
+	if (bo.type == BuildingObserver::Type::kProductionsite ||
+	    bo.type == BuildingObserver::Type::kMine) {
+		for (auto input : bo.inputs) {
+			if (calculate_stocklevel(input) < 2) {
+				inputs_on_stock = false;
+				break;
+			}
+		}
+	}
+
+	// Do we have enough workers available in warehouses?
+	bool workers_on_stock = true;
+	if (bo.type == BuildingObserver::Type::kProductionsite ||
+	    bo.type == BuildingObserver::Type::kMine) {
+		for (auto worker : bo.positions) {
+			if (calculate_stocklevel(worker, WareWorker::kWorker) < 1) {
+				workers_on_stock = false;
+				break;
+			}
+		}
+	}
+
+	// Do we have suppliers productionsites?
+	bool supliers_exist = check_supply(bo);
 
 	if (!bo.outputs.empty()) {
 		assert(bo.max_preciousness > 0);
 	}
 
+	if (bo.is(BuildingAttribute::kShipyard)) {
+		assert(bo.max_preciousness == 0);
+	}
+
 	// This flag is to be used when buildig is forced. AI will not build another building when
 	// a substitution exists. F.e. mines or pairs like tavern-inn
 	// To skip unnecessary calculation, we calculate this only if we have 0 count of the buildings
 	bool has_substitution_building = false;
-	if (bo.total_count() == 0 && bo.upgrade_substitutes &&
+	if (bo.total_count() == 0 && bo.is(BuildingAttribute::kUpgradeSubstitutes) &&
 	    bo.type == BuildingObserver::Type::kProductionsite) {
 		const DescriptionIndex enhancement = bo.desc->enhancement();
 		BuildingObserver& en_bo =
@@ -3763,7 +4496,8 @@
 	// and after 90th minute we want second building unconditionally
 	bool needs_second_for_upgrade = false;
 	if (gametime > 30 * 60 * 1000 && bo.cnt_built == 1 && bo.cnt_under_construction == 0 &&
-	    bo.upgrade_extends && !bo.upgrade_substitutes &&
+	    bo.is(BuildingAttribute::kUpgradeExtends) &&
+	    !bo.is(BuildingAttribute::kUpgradeSubstitutes) &&
 	    bo.type == BuildingObserver::Type::kProductionsite) {
 		const DescriptionIndex enhancement = bo.desc->enhancement();
 		BuildingObserver& en_bo =
@@ -3775,165 +4509,598 @@
 		}
 	}
 
-	// This function is going to say if a building is needed. But there is a 'new_buildings_stop_'
-	// flag that should be obeyed, but sometimes can be ignored.
-	// So we can have two types of needed: kNeeded and KNeededPending
-	// below we define which one will be returned if building is 'needed'
-	BuildingNecessity needed_type = BuildingNecessity::kNeeded;
-	if (new_buildings_stop_) {
-		needed_type = BuildingNecessity::kNeededPending;
-		if (gametime < 15 * 60 * 1000) {
-			;                                     // no exemption here within first 15 minutes
-		} else if (gametime < 25 * 60 * 1000) {  // exemption after 15 minutes - 1 building allowed
-
-			if (bo.type == BuildingObserver::Type::kMine) {
-				if (mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished == 0) {
-					needed_type = BuildingNecessity::kNeeded;
-				}
-			}
-			if (bo.type == BuildingObserver::Type::kProductionsite) {
-				if (bo.produces_building_material || bo.max_needed_preciousness >= 10) {
-					if (bo.total_count() == 0) {
-						needed_type = BuildingNecessity::kNeeded;
-					}
-				}
-			}
-		} else {  // exemption after 25 minutes - 2 buildings allowed
-			if (bo.type == BuildingObserver::Type::kMine) {
-				if (mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished <= 1) {
-					needed_type = BuildingNecessity::kNeeded;
-				}
-			}
-			if (bo.type == BuildingObserver::Type::kProductionsite) {
-				if (bo.produces_building_material || bo.max_needed_preciousness >= 10) {
-					if (bo.total_count() <= 1) {
-						needed_type = BuildingNecessity::kNeeded;
-					}
-				}
-			}
-		}
-	}
-
 	// And finally the 'core' of this function
 	// First deal with construction of new sites
 	if (purpose == PerfEvaluation::kForConstruction) {
 		if (bo.forced_after < gametime && bo.total_count() == 0 && !has_substitution_building) {
-			bo.max_needed_preciousness = bo.max_preciousness;
+			if (!bo.is(BuildingAttribute::kBarracks)) {
+				bo.max_needed_preciousness = bo.max_preciousness;
+			} else {
+				// barracks has no genuine preciousness as by now
+				bo.max_needed_preciousness = 5;
+			}
 			return BuildingNecessity::kForced;
 		} else if (bo.prohibited_till > gametime) {
 			return BuildingNecessity::kForbidden;
-		} else if (bo.is_hunter || bo.is_fisher) {
+		} else if (bo.is(BuildingAttribute::kHunter) || bo.is(BuildingAttribute::kFisher) ||
+		           bo.is(BuildingAttribute::kWell)) {
+
+			bo.cnt_target = 1 + static_cast<int32_t>(mines_.size() + productionsites.size()) / 25;
 
 			if (bo.max_needed_preciousness == 0) {
 				return BuildingNecessity::kNotNeeded;
 			} else if (bo.cnt_under_construction + bo.unoccupied_count > 0) {
 				return BuildingNecessity::kForbidden;
-			} else if (bo.total_count() > 0 && new_buildings_stop_) {
+			} else if (bo.total_count() > 0 &&
+			           !(expansion_type.get_expansion_type() == ExpansionMode::kEconomy ||
+			             expansion_type.get_expansion_type() == ExpansionMode::kBoth)) {
 				return BuildingNecessity::kForbidden;
+			} else if (bo.total_count() >= bo.cnt_target) {
+				if (get_stocklevel(bo, gametime) > 1 ||
+				    bo.last_building_built + 10 * 60 * 100 > gametime) {
+					return BuildingNecessity::kForbidden;
+				} else {
+					bo.primary_priority = std::abs(management_data.get_military_number_at(137)) / 2;
+					return BuildingNecessity::kNeeded;
+				}
 			} else {
+				bo.primary_priority = (bo.cnt_target - bo.total_count()) *
+				                      std::abs(management_data.get_military_number_at(111)) / 2;
 				return BuildingNecessity::kNeeded;
 			}
-		} else if (bo.need_trees) {
+		} else if (bo.is(BuildingAttribute::kLumberjack)) {
 			if (bo.total_count() > 1 && (bo.cnt_under_construction + bo.unoccupied_count > 0)) {
 				return BuildingNecessity::kForbidden;
 			}
-			bo.cnt_target = 3 + static_cast<int32_t>(mines_.size() + productionsites.size()) / 20;
-
+			bo.cnt_target = 3;
 			// adjusting/decreasing based on cnt_limit_by_aimode
 			bo.cnt_target = limit_cnt_target(bo.cnt_target, bo.cnt_limit_by_aimode);
 
 			// for case the wood is not needed yet, to avoid inconsistency later on
 			bo.max_needed_preciousness = bo.max_preciousness;
 
+			bo.primary_priority = 0;
+
+			if (bo.total_count() < bo.cnt_target) {
+				bo.primary_priority += 10 * std::abs(management_data.get_military_number_at(34));
+			}
+			if (get_stocklevel(bo, gametime) < 10) {
+				bo.primary_priority += std::abs(management_data.get_military_number_at(118));
+			}
 			if (bo.total_count() < bo.cnt_target) {
 				return BuildingNecessity::kNeeded;
 			} else {
 				return BuildingNecessity::kAllowed;
 			}
-		} else if (bo.plants_trees) {
-
-			bo.cnt_target = 2 + static_cast<int32_t>(mines_.size() + productionsites.size()) / 40;
+		} else if (bo.is(BuildingAttribute::kRanger)) {
+
+			// making sure we have one completed lumberjack
+			if (bo.total_count() > 0 &&
+			    get_building_observer(BuildingAttribute::kLumberjack).cnt_built < 1) {
+				return BuildingNecessity::kForbidden;
+			}
+
+			// genetic algorithm to decide whether new rangers are needed
+			int16_t tmp_target = 2;
+			int16_t inputs[2 * f_neuron_bit_size] = {0};
+			inputs[0] = (persistent_data->trees_around_cutters < 10) * 2;
+			inputs[1] = (persistent_data->trees_around_cutters < 20) * 2;
+			inputs[2] = (persistent_data->trees_around_cutters < 30) * 2;
+			inputs[3] = (persistent_data->trees_around_cutters < 40) * 2;
+			inputs[4] = (persistent_data->trees_around_cutters < 50) * 1;
+			inputs[5] = (persistent_data->trees_around_cutters < 60) * 1;
+			inputs[6] = (persistent_data->trees_around_cutters < 10) * 1;
+			inputs[7] = (persistent_data->trees_around_cutters < 20) * 1;
+			inputs[8] = (persistent_data->trees_around_cutters < 100) * 1;
+			inputs[9] = (persistent_data->trees_around_cutters < 200) * 1;
+			inputs[10] = (persistent_data->trees_around_cutters < 300) * 1;
+			inputs[11] = (persistent_data->trees_around_cutters < 400) * 1;
+			inputs[12] = (wood_policy_ != WoodPolicy::kAllowRangers) * 1;
+			inputs[13] = (wood_policy_ != WoodPolicy::kAllowRangers) * 1;
+			inputs[14] = (get_stocklevel(bo, gametime) < 10) * 1;
+			inputs[15] = (get_stocklevel(bo, gametime) < 10) * 1;
+			inputs[16] = (get_stocklevel(bo, gametime) < 2) * 1;
+			if (gametime > 15 * 60) {
+				inputs[17] = (get_stocklevel(bo, gametime) > 30) * -1;
+				inputs[18] = (get_stocklevel(bo, gametime) > 20) * -1;
+				inputs[19] = (get_stocklevel(bo, gametime) > 10) * -1;
+			} else {
+				inputs[20] = 1;
+				inputs[21] = 1;
+			}
+			inputs[22] = (basic_economy_established) ? -1 : 1;
+			inputs[23] = (msites_in_constr() > 0) ? 1 : -2;
+			inputs[24] = (msites_in_constr() > 1) ? 1 : -2;
+			inputs[25] = (wood_policy_ != WoodPolicy::kAllowRangers) * 1;
+			if (gametime > 90 * 60) {
+				inputs[26] = (wood_policy_ != WoodPolicy::kAllowRangers) * 1;
+				inputs[27] = (persistent_data->trees_around_cutters < 20) * 1;
+			}
+			if (gametime > 45 * 60) {
+				inputs[28] = (wood_policy_ != WoodPolicy::kAllowRangers) * 1;
+				inputs[29] = (persistent_data->trees_around_cutters < 20) * 1;
+				inputs[30] = (get_stocklevel(bo, gametime) > 30) * -1;
+			}
+			inputs[31] = (persistent_data->trees_around_cutters < 100) * 2;
+			inputs[32] = (persistent_data->trees_around_cutters < 200) * 2;
+			inputs[33] = ((mines_per_type[iron_ore_id].in_construction +
+			               mines_per_type[iron_ore_id].finished) <= 1) *
+			             -1;
+			inputs[34] = ((mines_per_type[iron_ore_id].in_construction +
+			               mines_per_type[iron_ore_id].finished) <= 1) *
+			             -1;
+			inputs[35] = ((mines_per_type[iron_ore_id].in_construction +
+			               mines_per_type[iron_ore_id].finished) == 0) *
+			             -1;
+			inputs[36] = ((mines_per_type[iron_ore_id].in_construction +
+			               mines_per_type[iron_ore_id].finished) == 0) *
+			             -1;
+			inputs[37] = -1;
+			inputs[38] = -1;
+			inputs[39] = -1;
+			if (productionsites.size() / 3 > static_cast<uint32_t>(bo.total_count()) &&
+			    get_stocklevel(bo, gametime) < 20) {
+				inputs[40] = (persistent_data->trees_around_cutters < 40) * 1;
+				inputs[41] = (persistent_data->trees_around_cutters < 60) * 1;
+				inputs[42] = (persistent_data->trees_around_cutters < 80) * 1;
+			}
+			if (productionsites.size() / 4 > static_cast<uint32_t>(bo.total_count()) &&
+			    get_stocklevel(bo, gametime) < 20) {
+				inputs[43] = (persistent_data->trees_around_cutters < 40) * 2;
+				inputs[44] = (persistent_data->trees_around_cutters < 60) * 2;
+				inputs[45] = (persistent_data->trees_around_cutters < 80) * 2;
+			}
+
+			if (productionsites.size() / 2 > static_cast<uint32_t>(bo.total_count()) &&
+			    get_stocklevel(bo, gametime) < 10) {
+				inputs[46] = (persistent_data->trees_around_cutters < 20) * 1;
+				inputs[47] = (persistent_data->trees_around_cutters < 40) * 1;
+				inputs[48] = (persistent_data->trees_around_cutters < 60) * 1;
+				inputs[49] = (persistent_data->trees_around_cutters < 80) * 1;
+			}
+			inputs[50] = (bo.last_building_built + 1 * 60 * 100 > gametime) * -2;
+			inputs[51] = (bo.last_building_built + 2 * 60 * 100 > gametime) * -2;
+			inputs[52] = (bo.last_building_built + 4 * 60 * 100 > gametime) * -2;
+			inputs[53] = (bo.last_building_built + 6 * 60 * 100 > gametime) * -2;
+			inputs[54] = (5 * 60 * 100 > gametime) * -2;
+			inputs[55] = (6 * 60 * 100 > gametime) * -2;
+			inputs[56] = (8 * 60 * 100 > gametime) * -2;
+			inputs[57] = (10 * 60 * 100 > gametime) * -2;
+			inputs[58] = (spots_ < kSpotsEnough) ? -2 : 0;
+			inputs[59] = (spots_ < kSpotsTooLittle) ? -2 : 0;
+			inputs[60] = (spots_ < kSpotsTooLittle) ? -2 : 0;
+			inputs[61] = (spots_ < kSpotsTooLittle) ? -2 : 0;
+			inputs[62] = (basic_economy_established) ? 0 : -2;
+			inputs[63] = (spots_ < kSpotsTooLittle) ? 0 : -2;
+
+			for (uint8_t i = 0; i < f_neuron_bit_size; i += 1) {
+				if (management_data.f_neuron_pool[14].get_position(i)) {
+					assert(inputs[i] >= -2 && inputs[i] <= 2);
+					tmp_target += inputs[i];
+				}
+				if (management_data.f_neuron_pool[15].get_position(i)) {
+					tmp_target += inputs[f_neuron_bit_size + i];
+					assert(inputs[f_neuron_bit_size + i] >= -2 && inputs[f_neuron_bit_size + i] <= 2);
+				}
+			}
+
+			if (site_needed_for_economy == BasicEconomyBuildingStatus::kDiscouraged) {
+				tmp_target -= std::abs(management_data.get_military_number_at(145) / 10);
+			}
+
+			if (tmp_target < 2) {
+				tmp_target = 2;
+			}
+
+			bo.cnt_target = tmp_target;
 
 			// adjusting/decreasing based on cnt_limit_by_aimode
 			bo.cnt_target = limit_cnt_target(bo.cnt_target, bo.cnt_limit_by_aimode);
 
+			bool paralel_construction = false;
+			if (bo.total_count() + 2 < bo.cnt_target) {
+				paralel_construction = true;
+			}
+
+			assert(bo.cnt_target > 1 && bo.cnt_target < 1000);
+
 			if (wood_policy_ != WoodPolicy::kAllowRangers) {
 				return BuildingNecessity::kForbidden;
 			}
-			// 150 corresponds to 15 trees
-			if (persistent_data->trees_around_cutters < 150) {
-				bo.cnt_target *= 4;
-			}
-			if (bo.total_count() > 1 && (bo.cnt_under_construction + bo.unoccupied_count > 0)) {
-				return BuildingNecessity::kForbidden;
-			} else if (bo.total_count() > bo.cnt_target) {
-				return BuildingNecessity::kForbidden;
-			}
-			return BuildingNecessity::kNeeded;
-		} else if (bo.need_rocks && bo.cnt_under_construction + bo.unoccupied_count == 0) {
+
+			if (bo.total_count() > bo.cnt_target) {
+				return BuildingNecessity::kForbidden;
+			}
+
+			if (paralel_construction && (bo.cnt_under_construction + bo.unoccupied_count <= 1)) {
+				return BuildingNecessity::kNeeded;
+			} else if (bo.cnt_under_construction + bo.unoccupied_count == 0) {
+				return BuildingNecessity::kNeeded;
+			}
+			return BuildingNecessity::kForbidden;
+		} else if (bo.is(BuildingAttribute::kNeedsRocks) &&
+		           bo.cnt_under_construction + bo.unoccupied_count == 0) {
 			bo.max_needed_preciousness = bo.max_preciousness;  // even when rocks are not needed
 			return BuildingNecessity::kAllowed;
 		} else if (bo.production_hint >= 0 && bo.cnt_under_construction + bo.unoccupied_count == 0) {
-			return BuildingNecessity::kAllowed;
-		} else if (bo.cnt_under_construction + bo.unoccupied_count > 0 &&
-		           bo.max_needed_preciousness < 10) {
-			return BuildingNecessity::kForbidden;
-		} else if (bo.cnt_under_construction + bo.unoccupied_count > 0 && gametime < 30 * 60 * 1000) {
-			return BuildingNecessity::kForbidden;
-		} else if (bo.cnt_under_construction + bo.unoccupied_count > 1) {
-			return BuildingNecessity::kForbidden;  // for preciousness>=10 and after 30 min
+			bo.cnt_target = 1 + static_cast<int32_t>(mines_.size() + productionsites.size()) / 30;
+			if (bo.cnt_target <= bo.total_count()) {
+				return BuildingNecessity::kAllowed;
+			}
+			if (get_stocklevel(bo, gametime) == 0 &&
+			    bo.last_building_built + 10 * 60 * 100 < gametime) {
+				return BuildingNecessity::kAllowed;
+			}
+			return BuildingNecessity::kForbidden;
+
+		} else if (bo.is(BuildingAttribute::kBarracks)) {
+			if (site_needed_for_economy == BasicEconomyBuildingStatus::kDiscouraged) {
+				return BuildingNecessity::kForbidden;
+			}
+			if (gametime > 30 * 60 * 1000 && bo.total_count() == 0) {
+
+				int16_t tmp_score = 1;
+				tmp_score +=
+				   mines_per_type[iron_ore_id].in_construction + mines_per_type[iron_ore_id].finished;
+				tmp_score += (soldier_status_ == SoldiersStatus::kBadShortage) * 2;
+				tmp_score += (soldier_status_ == SoldiersStatus::kShortage) * 2;
+				tmp_score += (gametime / 60 / 1000 - 20) / 4;
+				bo.max_needed_preciousness =
+				   1 + tmp_score * std::abs(management_data.get_military_number_at(134)) / 15;
+				bo.max_preciousness = bo.max_needed_preciousness;
+				return BuildingNecessity::kNeeded;
+			} else {
+				assert(bo.max_needed_preciousness == 0);
+				return BuildingNecessity::kForbidden;
+			}
 		} else if (bo.type == BuildingObserver::Type::kMine) {
-			if ((mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished) == 0) {
+			bo.primary_priority = bo.max_needed_preciousness;
+			if ((mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished) == 0 &&
+			    site_needed_for_economy != BasicEconomyBuildingStatus::kDiscouraged) {
 				// unless a mine is prohibited, we want to have at least one of the kind
 				bo.max_needed_preciousness = bo.max_preciousness;
 				return BuildingNecessity::kNeeded;
 			} else if (((mines_per_type[bo.mines].in_construction +
 			             mines_per_type[bo.mines].finished) == 1) &&
-			           bo.produces_building_material) {
+			           bo.is(BuildingAttribute::kBuildingMatProducer) &&
+			           site_needed_for_economy != BasicEconomyBuildingStatus::kDiscouraged) {
 				bo.max_needed_preciousness = bo.max_preciousness;
+				bo.primary_priority += bo.max_needed_preciousness *
+				                       std::abs(management_data.get_military_number_at(129)) / 10;
 				return BuildingNecessity::kNeeded;
 			}
 			if (bo.max_needed_preciousness == 0) {
 				return BuildingNecessity::kNotNeeded;
 			}
-			if (bo.current_stats < 40) {
-				return BuildingNecessity::kForbidden;
-			}
-			assert(bo.last_building_built != kNever);
+			if (gametime - bo.construction_decision_time < kBuildingMinInterval) {
+				return BuildingNecessity::kForbidden;
+			}
+			if (mines_per_type[bo.mines].in_construction > 0) {
+				return BuildingNecessity::kForbidden;
+			}
+			if (mines_per_type[bo.mines].finished >= 1 && bo.current_stats < 50) {
+				return BuildingNecessity::kForbidden;
+			}
+
 			if (gametime < bo.last_building_built + 3 * 60 * 1000) {
 				return BuildingNecessity::kForbidden;
 			}
-			return needed_type;
-		}
-		if (bo.max_needed_preciousness > 0) {
-			if (bo.cnt_under_construction + bo.unoccupied_count > 0) {
-				assert(bo.cnt_under_construction + bo.unoccupied_count == 1);
-				assert(bo.max_needed_preciousness >= 10 || bo.produces_building_material);
-				assert(gametime >= 25 * 60 * 1000);
-			}
-
-			// First 'if' is special support for hardwood producers (to have 2 of them)
-			if (bo.produces_building_material && bo.total_count() <= 1 && bo.current_stats > 10) {
-				return BuildingNecessity::kNeeded;
-			} else if (bo.inputs.empty()) {
-				return needed_type;
-			} else if (bo.total_count() == 0) {
-				return needed_type;
-			} else if (!bo.outputs.empty() && bo.current_stats > 10 + 70 / bo.outputs.size()) {
-				assert(bo.last_building_built != kNever);
-				if (gametime < bo.last_building_built + 10 * 60 * 1000) {
-					// Previous building built less then 10 minutes ago
-					// Wait a bit, perhaps average utilization will drop down in the meantime
-					return BuildingNecessity::kNeededPending;
-				} else {
-					return needed_type;
-				}
-			} else if (needs_second_for_upgrade) {
-				return needed_type;
-			} else {
-				return BuildingNecessity::kForbidden;
-			}
-		} else if (bo.is_shipyard) {
+
+			int16_t inputs[f_neuron_bit_size] = {0};
+			inputs[0] = (gametime < 15 * 60 * 1000) ? -2 : 0;
+			inputs[1] = (gametime < 30 * 60 * 1000) ? -2 : 0;
+			inputs[2] = (gametime < 45 * 60 * 1000) ? -2 : 0;
+			inputs[3] =
+			   (mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished == 1) ?
+			      3 :
+			      0;
+			inputs[4] =
+			   (mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished == 1) ?
+			      2 :
+			      0;
+			inputs[5] = (bo.mines == iron_ore_id) ? 2 : 1;
+			inputs[6] = (bo.current_stats - 50) / 10;
+			inputs[7] = (gametime < 15 * 60 * 1000) ? -1 : 0;
+			inputs[8] = (gametime < 30 * 60 * 1000) ? -1 : 0;
+			inputs[9] = (gametime < 45 * 60 * 1000) ? -1 : 0;
+			inputs[10] =
+			   (mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished == 1) ?
+			      2 :
+			      0;
+			inputs[11] =
+			   (mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished == 1) ?
+			      1 :
+			      0;
+			inputs[12] = (bo.mines == iron_ore_id) ? 2 : 0;
+			inputs[13] = (bo.current_stats - 50) / 10;
+			inputs[14] = (bo.current_stats - 50) / 10;
+			inputs[15] = management_data.get_military_number_at(123) / 10;
+			inputs[16] = 0;
+			inputs[17] = (inputs_on_stock) ? 0 : -2;
+			inputs[18] = (supliers_exist) ? 0 : -3;
+			;
+			inputs[17] = (inputs_on_stock) ? 0 : -4;
+			inputs[20] =
+			   (mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished == 1) ?
+			      3 :
+			      0;
+			inputs[21] =
+			   (mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished == 1) ?
+			      2 :
+			      0;
+			inputs[22] = (bo.current_stats - 50) / 10;
+			inputs[23] = (bo.current_stats - 50) / 20;
+			inputs[24] = (supliers_exist) ? 0 : -5;
+			inputs[25] = (supliers_exist) ? 0 : -2;
+			inputs[26] = (workers_on_stock) ? 0 : -5;
+			inputs[27] = (workers_on_stock) ? 0 : -2;
+			inputs[28] = (bo.is(BuildingAttribute::kBuildingMatProducer)) ? 1 : 0;
+			inputs[29] = (bo.is(BuildingAttribute::kBuildingMatProducer)) ? 3 : 0;
+			inputs[30] = (mines_per_type[bo.mines].is_critical) ? 1 : -1;
+
+			int16_t tmp_score = management_data.get_military_number_at(83) / 5;
+
+			// Building productionsites above limit in Basic economy mode is strongly discouraged, but
+			// still possible
+			const int16_t basic_economy_score =
+			   25 + std::abs(management_data.get_military_number_at(122) * 2);
+
+			if (site_needed_for_economy == BasicEconomyBuildingStatus::kEncouraged) {
+				tmp_score += basic_economy_score;
+			}
+
+			if (site_needed_for_economy == BasicEconomyBuildingStatus::kDiscouraged) {
+				tmp_score -= basic_economy_score;
+			}
+
+			for (uint8_t i = 0; i < f_neuron_bit_size; i += 1) {
+				if (management_data.f_neuron_pool[36].get_position(i)) {
+					tmp_score += inputs[i];
+				}
+			}
+			if (tmp_score < 0) {
+				return BuildingNecessity::kNeededPending;
+			} else {
+				return BuildingNecessity::kNeeded;
+				bo.primary_priority +=
+				   tmp_score * std::abs(management_data.get_military_number_at(127) / 5);
+			}
+
+		} else if (bo.max_needed_preciousness > 0) {
+
+			int16_t inputs[4 * f_neuron_bit_size] = {0};
+			inputs[0] = (bo.total_count() <= 1) ?
+			               std::abs(management_data.get_military_number_at(110)) / 10 :
+			               0;
+			inputs[1] = -2 * bo.total_count();
+			inputs[2] =
+			   (bo.total_count() == 0) ? std::abs(management_data.get_military_number_at(0)) / 10 : 0;
+			inputs[3] = (gametime >= 25 * 60 * 1000 && bo.inputs.empty()) ?
+			               management_data.get_military_number_at(1) / 10 :
+			               0;
+			inputs[4] = (bo.max_needed_preciousness >= 10) ?
+			               std::abs(management_data.get_military_number_at(2)) / 10 :
+			               0;
+			inputs[5] = (!bo.outputs.empty() && bo.current_stats > 10 + 70 / bo.outputs.size()) ?
+			               management_data.get_military_number_at(3) / 10 :
+			               0;
+			inputs[6] = (needs_second_for_upgrade) ?
+			               std::abs(management_data.get_military_number_at(4)) / 10 :
+			               0;
+			inputs[7] = (bo.cnt_under_construction + bo.unoccupied_count) * -1 *
+			            std::abs(management_data.get_military_number_at(9)) / 5;
+			inputs[8] = (!bo.outputs.empty() && bo.current_stats > 30 + 70 / bo.outputs.size()) ?
+			               management_data.get_military_number_at(7) / 8 :
+			               0;
+			inputs[9] = (bo.is(BuildingAttribute::kBuildingMatProducer)) ?
+			               std::abs(management_data.get_military_number_at(10)) / 10 :
+			               0;
+			inputs[10] =
+			   (bo.build_material_shortage) ? -management_data.get_military_number_at(39) / 10 : 0;
+			inputs[11] = (wood_policy_ == WoodPolicy::kDismantleRangers ||
+			              wood_policy_ == WoodPolicy::kStopRangers) ?
+			                std::abs(management_data.get_military_number_at(15)) / 10 :
+			                0;
+			inputs[12] = (gametime >= 15 * 60 * 1000) ?
+			                std::abs(management_data.get_military_number_at(94)) / 10 :
+			                0;
+			inputs[13] = management_data.get_military_number_at(8) / 10;
+			inputs[14] = (persistent_data->trees_around_cutters < 20) ?
+			                -1 * std::abs(management_data.get_military_number_at(95)) / 10 :
+			                0;
+			inputs[15] = (persistent_data->trees_around_cutters > 100) ?
+			                std::abs(management_data.get_military_number_at(96)) / 10 :
+			                0;
+			inputs[16] = (player_statistics.any_enemy_seen_lately(gametime)) ?
+			                management_data.get_military_number_at(97) / 10 :
+			                0;
+			inputs[17] =
+			   (spots_ > kSpotsEnough) ? std::abs(management_data.get_military_number_at(74)) / 10 : 0;
+			inputs[18] = management_data.get_military_number_at(98) / 10;
+			inputs[19] = (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) ?
+			                -1 * std::abs(management_data.get_military_number_at(40)) / 10 :
+			                0;
+			inputs[20] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) ?
+			                std::abs(management_data.get_military_number_at(50)) / 10 :
+			                0;
+			inputs[21] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy ||
+			              expansion_type.get_expansion_type() == ExpansionMode::kBoth) ?
+			                3 :
+			                0;
+			inputs[22] =
+			   (bo.total_count() == 0 && bo.is(BuildingAttribute::kBuildingMatProducer)) ? 3 : 0;
+			if (bo.cnt_built > 0 && !bo.outputs.empty()) {
+				inputs[22] += bo.current_stats / 10;
+			}
+			inputs[23] = (!player_statistics.strong_enough(player_number())) ? 5 : 0;
+			inputs[24] = (bo.inputs.empty()) ? 6 : 0;
+			inputs[25] =
+			   (bo.total_count() == 0 && bo.is(BuildingAttribute::kBuildingMatProducer)) ? 4 : 0;
+			inputs[26] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) ? 2 : 0;
+			inputs[27] = (wood_policy_ == WoodPolicy::kDismantleRangers ||
+			              wood_policy_ == WoodPolicy::kStopRangers) ?
+			                4 :
+			                0;
+			inputs[28] = (bo.max_needed_preciousness >= 10) ? 4 : 0;
+			inputs[29] = (bo.inputs.empty() && bo.max_needed_preciousness >= 10) ? 3 : 0;
+			inputs[30] = bo.max_needed_preciousness / 2;
+			inputs[31] = ((bo.cnt_under_construction + bo.unoccupied_count) > 0) ? -5 : 0;
+			inputs[32] = bo.max_needed_preciousness / 2;
+			inputs[33] = -(bo.cnt_under_construction + bo.unoccupied_count) * 4;
+			if (bo.cnt_built > 0 && !bo.outputs.empty() && !bo.inputs.empty()) {
+				inputs[34] += bo.current_stats / 10;
+			}
+			inputs[35] = (!bo.outputs.empty() && !bo.inputs.empty() &&
+			              bo.current_stats > 10 + 70 / bo.outputs.size()) ?
+			                2 :
+			                0;
+			inputs[36] = (!bo.outputs.empty() && !bo.inputs.empty() &&
+			              bo.cnt_under_construction + bo.unoccupied_count == 0) ?
+			                bo.current_stats / 12 :
+			                0;
+			if (bo.cnt_built > 0 && !bo.inputs.empty() && !bo.outputs.empty() &&
+			    bo.current_stats < 20) {
+				inputs[37] = -5;
+			}
+			inputs[38] = (bo.cnt_under_construction + bo.unoccupied_count > 0) ? -10 : 0;
+			if (bo.cnt_built > 0 && !bo.outputs.empty() && bo.current_stats < 15) {
+				inputs[39] = -10;
+			}
+			inputs[40] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) ? 3 : 0;
+			inputs[41] = (bo.build_material_shortage) ? -3 : 0;
+			inputs[42] = (!player_statistics.strong_enough(player_number())) ? 2 : 0;
+			inputs[43] = (bo.inputs.empty()) ? 3 : 0;
+			inputs[44] = (bo.inputs.empty() && bo.max_needed_preciousness >= 10) ? 3 : 0;
+			inputs[45] = bo.max_needed_preciousness / 2;
+			inputs[46] =
+			   (!bo.outputs.empty() && bo.current_stats > 10 + 70 / bo.outputs.size()) ? 4 : 0;
+			inputs[47] = (!bo.outputs.empty() && bo.current_stats > 85) ? 4 : 0;
+			inputs[48] = (bo.max_needed_preciousness >= 10 &&
+			              (bo.cnt_under_construction + bo.unoccupied_count) == 1) ?
+			                5 :
+			                0;
+			inputs[49] = (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) ? -4 : 1;
+			inputs[50] = (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) ? -1 : 1;
+			inputs[51] = (gametime < 20 * 60 * 1000) ? -4 : 0;
+			inputs[52] = (bo.total_count() == 0) ? 4 : 0;
+			inputs[53] = (bo.total_count() == 0) ? 2 : 0;
+			inputs[54] = (spots_ < kSpotsEnough) ? -5 : 0;
+			inputs[55] = (bo.max_needed_preciousness >= 10 &&
+			              (bo.cnt_under_construction + bo.unoccupied_count) == 1) ?
+			                3 :
+			                0;
+			inputs[56] = (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) ? -8 : 1;
+			inputs[57] = (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) ? -6 : 1;
+			inputs[58] = (bo.total_count() == 0 && inputs_on_stock) ? 4 : 0;
+			inputs[59] = (bo.inputs.empty()) ? 5 : bo.current_stats / 10 - 5;
+			inputs[60] = (spots_ < kSpotsTooLittle) ? -10 : 0;
+			inputs[61] = (player_statistics.any_enemy_seen_lately(gametime)) ? 2 : 0;
+			inputs[62] = (player_statistics.any_enemy_seen_lately(gametime) &&
+			              bo.cnt_under_construction + bo.unoccupied_count == 0) ?
+			                6 :
+			                0;
+			inputs[63] = (!bo.outputs.empty() && !bo.inputs.empty()) ? bo.current_stats / 10 : 0;
+			inputs[64] = (gametime > 20 * 60 * 1000 && bo.total_count() == 0) ? 3 : 0;
+			inputs[65] = (gametime > 45 * 60 * 1000 && bo.total_count() == 0) ? 3 : 0;
+			inputs[66] = (gametime > 60 * 60 * 1000 && bo.total_count() <= 1) ? 3 : 0;
+			inputs[67] = (gametime > 50 * 60 * 1000 && bo.total_count() <= 1) ? 2 : 0;
+			inputs[68] =
+			   (bo.inputs.empty() && gametime > 50 * 60 * 1000 && bo.total_count() <= 1) ? 2 : 0;
+			inputs[69] =
+			   (!bo.inputs.empty() && gametime > 50 * 60 * 1000 && bo.total_count() <= 1) ? 2 : 0;
+			inputs[70] =
+			   (bo.inputs.empty() && gametime > 25 * 60 * 1000 && bo.total_count() <= 1) ? 2 : 0;
+			inputs[71] =
+			   (!bo.inputs.empty() && gametime > 25 * 60 * 1000 && bo.total_count() <= 1) ? 2 : 0;
+			if (bo.last_building_built != kNever) {
+				inputs[72] = (gametime < bo.last_building_built + 3 * 60 * 1000) ? -4 : 0;
+				inputs[73] = (gametime < bo.last_building_built + 5 * 60 * 1000) ? -2 : 0;
+				inputs[74] = (gametime < bo.last_building_built + 2 * 60 * 1000) ? -5 : 0;
+				inputs[75] = (gametime < bo.last_building_built + 10 * 60 * 1000) ? -2 : 0;
+				inputs[76] = (gametime < bo.last_building_built + 20 * 60 * 1000) ? -2 : 0;
+			}
+			inputs[77] = (gametime > 35 * 60 * 1000 && bo.total_count() == 0) ? 3 : 0;
+			inputs[78] = (gametime > 60 * 60 * 1000 && bo.total_count() == 0) ? 3 : 0;
+			inputs[79] = (expansion_type.get_expansion_type() == ExpansionMode::kResources ||
+			              expansion_type.get_expansion_type() == ExpansionMode::kSpace) *
+			             management_data.get_military_number_at(37) / 10;
+			inputs[80] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) *
+			             management_data.get_military_number_at(38) / 10;
+			inputs[81] = (expansion_type.get_expansion_type() == ExpansionMode::kSpace) *
+			             management_data.get_military_number_at(46) / 10;
+			inputs[82] = (inputs_on_stock) ? 0 : -2;
+			inputs[83] = (supliers_exist) ? 0 : -2;
+			inputs[84] = (inputs_on_stock) ? 0 : -4;
+			inputs[85] = (supliers_exist) ? 0 : -4;
+			inputs[86] = (inputs_on_stock) ? 0 : -8;
+			inputs[87] = (supliers_exist) ? 0 : -8;
+			inputs[88] = (workers_on_stock) ? 0 : -2;
+			inputs[89] = (workers_on_stock) ? 0 : -6;
+			inputs[90] = (bo.is(BuildingAttribute::kBuildingMatProducer)) ?
+			                std::abs(management_data.get_military_number_at(10)) / 10 :
+			                0;
+			inputs[91] = (bo.build_material_shortage) ? -2 : 0;
+			inputs[92] = (numof_psites_in_constr < 4) ? 3 : 0;
+			inputs[93] = (numof_psites_in_constr < 8) ? 3 : 0;
+			inputs[94] = (bo.inputs.empty()) ? 5 : 0;
+			inputs[95] = (bo.inputs.empty()) ? 3 : 0;
+			inputs[96] = (wood_policy_ == WoodPolicy::kAllowRangers) ? -2 : 0;
+			inputs[97] = (wood_policy_ == WoodPolicy::kAllowRangers) ? -8 : 0;
+			inputs[98] = (wood_policy_ == WoodPolicy::kAllowRangers) ? -4 : 0;
+			inputs[99] = (wood_policy_ == WoodPolicy::kAllowRangers) ? -1 : 0;
+
+			int16_t tmp_score = 0;
+			for (uint8_t i = 0; i < f_neuron_bit_size; i += 1) {
+				if (management_data.f_neuron_pool[8].get_position(i)) {
+					const int16_t partial_input = inputs[i];
+					if (kAITrainingMode && (partial_input < -10 || partial_input > 10)) {
+					}
+					tmp_score += partial_input;
+				}
+				if (management_data.f_neuron_pool[11].get_position(i)) {
+					const int16_t partial_input = inputs[i + f_neuron_bit_size];
+					tmp_score += partial_input;
+				}
+				if (management_data.f_neuron_pool[59].get_position(i)) {
+					const int16_t partial_input = inputs[i + 2 * f_neuron_bit_size];
+					tmp_score += partial_input;
+				}
+				if (management_data.f_neuron_pool[12].get_position(i)) {
+					const int16_t partial_input = inputs[i + 3 * f_neuron_bit_size];
+					tmp_score += partial_input;
+				}
+			}
+
+			const int32_t base_economy_bonus =
+			   30 + std::abs(management_data.get_military_number_at(142));
+			if (site_needed_for_economy == BasicEconomyBuildingStatus::kDiscouraged) {
+				tmp_score -= base_economy_bonus;
+			} else if (site_needed_for_economy == BasicEconomyBuildingStatus::kEncouraged) {
+				tmp_score += base_economy_bonus;
+			}
+
+			const int16_t bottom_limit = management_data.get_military_number_at(73) / 2 +
+			                             management_data.get_military_number_at(47) / 10;
+			const int16_t upper_limit =
+			   bottom_limit + std::abs(management_data.get_military_number_at(44) / 3);
+
+			if (tmp_score > upper_limit) {
+				// Productionsite is needed
+				bo.primary_priority += (tmp_score - bottom_limit) / 2;
+				return BuildingNecessity::kNeeded;
+			} else if (tmp_score > bottom_limit) {
+				// Site is needed, but not right now
+				return BuildingNecessity::kNeededPending;
+			} else {
+				// Not allowed
+				return BuildingNecessity::kForbidden;
+			}
+
+		} else if (bo.is(BuildingAttribute::kShipyard)) {
+			if (bo.total_count() > 0 ||
+			    site_needed_for_economy == BasicEconomyBuildingStatus::kDiscouraged) {
+				return BuildingNecessity::kForbidden;
+			}
 			return BuildingNecessity::kAllowed;
 		} else if (bo.max_needed_preciousness == 0) {
 			return BuildingNecessity::kNotNeeded;
@@ -3958,22 +5125,21 @@
 
 // counts produced output on stock
 // if multiple outputs, it returns lowest value
-uint32_t DefaultAI::get_stocklevel(BuildingObserver& bo) {
+uint32_t DefaultAI::calculate_stocklevel(BuildingObserver& bo, const WareWorker what) {
 	uint32_t count = std::numeric_limits<uint32_t>::max();
-
-	if (!bo.outputs.empty()) {
-		for (EconomyObserver* observer : economies) {
-			// Don't check if the economy has no warehouse.
-			if (observer->economy.warehouses().empty()) {
-				continue;
-			}
-
-			for (uint32_t m = 0; m < bo.outputs.size(); ++m) {
-				DescriptionIndex wt(static_cast<size_t>(bo.outputs.at(m)));
-				if (count > observer->economy.stock_ware(wt)) {
-					count = observer->economy.stock_ware(wt);
-				}
-			}
+	std::vector<Widelands::DescriptionIndex>* items;
+
+	if (what == WareWorker::kWare) {
+		items = &bo.outputs;
+	} else {
+		items = &bo.positions;
+	}
+
+	for (uint32_t m = 0; m < items->size(); ++m) {
+		DescriptionIndex wt(static_cast<size_t>(items->at(m)));
+		const uint32_t stock = calculate_stocklevel(wt, what);
+		if (count > stock) {
+			count = stock;
 		}
 	}
 
@@ -3982,33 +5148,38 @@
 
 // counts produced output on stock
 // if multiple outputs, it returns lowest value
-uint32_t DefaultAI::get_stocklevel(Widelands::DescriptionIndex wt) {
-	uint32_t count = 0;
-
-	for (EconomyObserver* observer : economies) {
-		// Don't check if the economy has no warehouse.
-		if (observer->economy.warehouses().empty()) {
-			continue;
-		}
-		count += observer->economy.stock_ware(wt);
-	}
-
-	return count;
-}
-
-// counts produced output in warehouses (only)
-// perhaps it will be able to replace get_stocklevel
-uint32_t DefaultAI::get_warehoused_stock(DescriptionIndex wt) {
+uint32_t DefaultAI::calculate_stocklevel(Widelands::DescriptionIndex wt, const WareWorker what) {
 	uint32_t count = 0;
 
 	for (std::list<WarehouseSiteObserver>::iterator i = warehousesites.begin();
 	     i != warehousesites.end(); ++i) {
-		count += i->site->get_wares().stock(wt);
+		if (what == WareWorker::kWare) {
+			count += i->site->get_wares().stock(wt);
+		} else {
+			count += i->site->get_workers().stock(wt);
+		}
 	}
 
 	return count;
 }
 
+// This is wrapper function to prevent too frequent recalculation of stocklevel
+// and distinquish if we count stocks for production hint or for outputs of a productionsite
+uint32_t
+DefaultAI::get_stocklevel(BuildingObserver& bo, const uint32_t gametime, const WareWorker what) {
+	if (bo.stocklevel_time < gametime - 5 * 1000) {
+		if (bo.production_hint > 0) {
+			bo.stocklevel_count = calculate_stocklevel(static_cast<size_t>(bo.production_hint), what);
+		} else if (!bo.outputs.empty()) {
+			bo.stocklevel_count = calculate_stocklevel(bo, what);
+		} else {
+			bo.stocklevel_count = 0;
+		}
+		bo.stocklevel_time = gametime;
+	}
+	return bo.stocklevel_count;
+}
+
 /**
  * This function takes care about the unowned and opposing territory and
  * recalculates the priority for non-military buildings
@@ -4032,6 +5203,7 @@
 
 	// if unowned territory nearby
 	prio -= bf.unowned_land_nearby / 4;
+	prio -= bf.enemy_owned_land_nearby / 3;
 
 	// further decrease the score if enemy nearby
 	if (bf.enemy_nearby) {
@@ -4041,8 +5213,10 @@
 	// and if close (up to 2 fields away) from border
 	if (bf.near_border) {
 		prio -= 10;
-		if (spots_ < kSpotsEnough) {
-			prio += 3 * (spots_ - kSpotsEnough);
+		if (spots_ > 0 && spots_ < kSpotsEnough) {
+			prio -= std::abs(management_data.neuron_pool[60].get_result_safe(
+			           kSpotsEnough / spots_, kAbsValue)) /
+			        4;
 		}
 	}
 
@@ -4052,7 +5226,7 @@
 void DefaultAI::consider_productionsite_influence(BuildableField& field,
                                                   Coords coords,
                                                   const BuildingObserver& bo) {
-	if (bo.space_consumer && !bo.plants_trees &&
+	if (bo.is(BuildingAttribute::kSpaceConsumer) && !bo.is(BuildingAttribute::kRanger) &&
 	    game().map().calc_distance(coords, field.coords) < 8) {
 		++field.space_consumers_nearby;
 	}
@@ -4069,7 +5243,7 @@
 		++field.supporters_nearby.at(bo.production_hint);
 	}
 
-	if (bo.plants_trees) {
+	if (bo.is(BuildingAttribute::kRanger)) {
 		++field.rangers_nearby;
 	}
 }
@@ -4100,6 +5274,52 @@
 	                 player_number(), tribe_->name().c_str(), name);
 }
 
+// \checks if the building has building observer (for debug purposes)
+bool DefaultAI::has_building_observer(char const* const name) {
+	if (tribe_ == nullptr) {
+		late_initialization();
+	}
+
+	for (BuildingObserver& bo : buildings_) {
+		if (!strcmp(bo.name, name)) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+// return observer for a first (only) building that has required attribute
+Widelands::BuildingObserver& DefaultAI::get_building_observer(BuildingAttribute attribute) {
+	if (tribe_ == nullptr) {
+		late_initialization();
+	}
+
+	for (BuildingObserver& bo : buildings_) {
+		if (bo.is(attribute)) {
+			return bo;
+		}
+	}
+
+	throw wexception(
+	   "Sorry, cannot find building with attribute %d", static_cast<int32_t>(attribute));
+}
+
+// return observer for a building with the id
+Widelands::BuildingObserver& DefaultAI::get_building_observer(const DescriptionIndex di) {
+	if (tribe_ == nullptr) {
+		late_initialization();
+	}
+
+	for (BuildingObserver& bo : buildings_) {
+		if (bo.id == di) {
+			return bo;
+		}
+	}
+
+	throw wexception("Sorry, cannot find building with id %d", static_cast<int32_t>(di));
+}
+
 // this is called whenever we gain ownership of a PlayerImmovable
 void DefaultAI::gain_immovable(PlayerImmovable& pi, const bool found_on_load) {
 	if (upcast(Building, building, &pi)) {
@@ -4192,7 +5412,8 @@
 		// sometimes we search for any owned territory (f.e. when considering
 		// a port location), but when testing (starting from) own military building
 		// we must ignore own territory, of course
-		if (f->get_owned_by() > 0) {
+		const PlayerNumber field_owner = f->get_owned_by();
+		if (field_owner > 0) {
 
 			// if field is owned by anybody
 			if (type == WalkSearch::kAnyPlayer) {
@@ -4201,21 +5422,15 @@
 			}
 
 			// if somebody but not me
-			if (type == WalkSearch::kOtherPlayers && f->get_owned_by() != pn) {
+			if (type == WalkSearch::kOtherPlayers && field_owner != pn) {
 				*tested_fields = done.size();
 				return true;
 			}
 
 			// if owned by enemy
-			if (type == WalkSearch::kEnemy && f->get_owned_by() != pn) {
-				// in case I am not member of a team
-				if (player_->team_number() == 0) {
-					*tested_fields = done.size();
-					return true;
-					// if I am in team, testing if the same team
-				} else if (player_->team_number() > 0 &&
-				           player_->team_number() !=
-				              game().get_player(f->get_owned_by())->team_number()) {
+			if (type == WalkSearch::kEnemy && field_owner != pn) {
+				// if not in the same taem => it is enemy
+				if (!player_statistics.players_in_same_team(pn, field_owner)) {
 					*tested_fields = done.size();
 					return true;
 				}
@@ -4253,7 +5468,7 @@
 		   get_building_observer(dynamic_cast<const ConstructionSite&>(b).building().name().c_str());
 		++target_bo.cnt_under_construction;
 		if (target_bo.type == BuildingObserver::Type::kProductionsite) {
-			++num_prod_constructionsites;
+			++numof_psites_in_constr;
 		}
 		if (target_bo.type == BuildingObserver::Type::kMilitarysite) {
 			msites_per_size[target_bo.desc->get_size()].in_construction += 1;
@@ -4261,13 +5476,11 @@
 		if (target_bo.type == BuildingObserver::Type::kMine) {
 			mines_per_type[target_bo.mines].in_construction += 1;
 		}
+		if (target_bo.type == BuildingObserver::Type::kWarehouse) {
+			numof_warehouses_in_const_ += 1;
+		}
 		if (target_bo.type == BuildingObserver::Type::kTrainingsite) {
-			if (target_bo.trainingsite_type == TrainingSiteType::kBasic) {
-				ts_basic_const_count_ += 1;
-			}
-			if (target_bo.trainingsite_type == TrainingSiteType::kAdvanced) {
-				ts_advanced_const_count_ += 1;
-			}
+			ts_in_const_count_ += 1;
 		}
 
 		set_taskpool_task_time(game().get_gametime(), SchedulerTaskId::kRoadCheck);
@@ -4276,6 +5489,21 @@
 		++bo.cnt_built;
 		const uint32_t gametime = game().get_gametime();
 		bo.last_building_built = gametime;
+		// erasing building from remaining_basic_buildings, but only not on saved game loading
+		if (!found_on_load && persistent_data->remaining_basic_buildings.count(bo.id) > 0) {
+			if (persistent_data->remaining_basic_buildings[bo.id] > 1) {
+				persistent_data->remaining_basic_buildings[bo.id] -= 1;
+			} else {
+				persistent_data->remaining_basic_buildings.erase(bo.id);
+				persistent_data->remaining_buildings_size -= 1;
+			}
+		}
+		// Remaining basic buildings map contain either no entry for the building, or the number is
+		// nonzero
+		assert(persistent_data->remaining_basic_buildings.count(bo.id) == 0 ||
+		       persistent_data->remaining_basic_buildings[bo.id] > 0);
+		assert(persistent_data->remaining_basic_buildings.size() ==
+		       persistent_data->remaining_buildings_size);
 
 		if (bo.type == BuildingObserver::Type::kProductionsite) {
 			productionsites.push_back(ProductionSiteObserver());
@@ -4288,14 +5516,18 @@
 				productionsites.back().built_time = gametime;
 			}
 			productionsites.back().unoccupied_till = gametime;
-			productionsites.back().stats_zero = 0;
-			productionsites.back().no_resources_since = kNever;
-			productionsites.back().upgrade_pending = false;
 			productionsites.back().bo->unoccupied_count += 1;
-			if (bo.is_shipyard) {
+			if (bo.is(BuildingAttribute::kShipyard)) {
 				marine_task_queue.push_back(kStopShipyard);
 				marine_task_queue.push_back(kReprioritize);
 			}
+			if (bo.is(BuildingAttribute::kFisher)) {
+				fishers_count_ += 1;
+			}
+
+			if (bo.is_what.count(BuildingAttribute::kBakery)) {
+				bakeries_count_ += 1;
+			}
 
 			for (uint32_t i = 0; i < bo.outputs.size(); ++i)
 				++wares.at(bo.outputs.at(i)).producers;
@@ -4307,7 +5539,10 @@
 			mines_.back().site = &dynamic_cast<ProductionSite&>(b);
 			mines_.back().bo = &bo;
 			mines_.back().built_time = gametime;
-			mines_.back().no_resources_since = kNever;
+			assert(mines_.back().no_resources_since == kNever);
+			assert(mines_.back().upgrade_pending == false);
+			assert(mines_.back().dismantle_pending_since == kNever);
+			assert(productionsites.back().stats_zero == 0);
 			mines_.back().bo->unoccupied_count += 1;
 
 			for (uint32_t i = 0; i < bo.outputs.size(); ++i)
@@ -4318,39 +5553,38 @@
 
 			mines_per_type[bo.mines].finished += 1;
 
+			if (bo.is(BuildingAttribute::kBuildingMatProducer)) {
+				buil_material_mines_count += 1;
+			}
+
+			set_inputs_to_zero(mines_.back());
+
 		} else if (bo.type == BuildingObserver::Type::kMilitarysite) {
 			militarysites.push_back(MilitarySiteObserver());
 			militarysites.back().site = &dynamic_cast<MilitarySite&>(b);
 			militarysites.back().bo = &bo;
-			militarysites.back().checks = bo.desc->get_size();
+			militarysites.back().understaffed = 0;
 			if (found_on_load && gametime > 5 * 60 * 1000) {
 				militarysites.back().built_time = gametime - 5 * 60 * 1000;
 			} else {
 				militarysites.back().built_time = gametime;
 			}
-			militarysites.back().enemies_nearby = true;
+			militarysites.back().last_change = 0;  // or gametime?
 			msites_per_size[bo.desc->get_size()].finished += 1;
-			vacant_mil_positions_ += 2;  // at least some indication that there are vacant positions
 
 		} else if (bo.type == BuildingObserver::Type::kTrainingsite) {
 			ts_without_trainers_ += 1;
+			ts_finished_count_ += 1;
 			trainingsites.push_back(TrainingSiteObserver());
 			trainingsites.back().site = &dynamic_cast<TrainingSite&>(b);
 			trainingsites.back().bo = &bo;
-			if (bo.trainingsite_type == TrainingSiteType::kBasic) {
-				ts_basic_count_ += 1;
-			}
-			if (bo.trainingsite_type == TrainingSiteType::kAdvanced) {
-				ts_advanced_count_ += 1;
-			}
-			vacant_mil_positions_ += 8;  // at least some indication that there are vacant positions
 
 		} else if (bo.type == BuildingObserver::Type::kWarehouse) {
 			++numof_warehouses_;
 			warehousesites.push_back(WarehouseSiteObserver());
 			warehousesites.back().site = &dynamic_cast<Warehouse&>(b);
 			warehousesites.back().bo = &bo;
-			if (bo.is_port) {
+			if (bo.is_what.count(BuildingAttribute::kPort)) {
 				++num_ports;
 				seafaring_economy = true;
 				// unblock nearby fields, might be used for other buildings...
@@ -4377,7 +5611,7 @@
 		   get_building_observer(dynamic_cast<const ConstructionSite&>(b).building().name().c_str());
 		--target_bo.cnt_under_construction;
 		if (target_bo.type == BuildingObserver::Type::kProductionsite) {
-			--num_prod_constructionsites;
+			--numof_psites_in_constr;
 		}
 		if (target_bo.type == BuildingObserver::Type::kMilitarysite) {
 			msites_per_size[target_bo.desc->get_size()].in_construction -= 1;
@@ -4385,15 +5619,12 @@
 		if (target_bo.type == BuildingObserver::Type::kMine) {
 			mines_per_type[target_bo.mines].in_construction -= 1;
 		}
+		if (target_bo.type == BuildingObserver::Type::kWarehouse) {
+			numof_warehouses_in_const_ -= 1;
+		}
 		if (target_bo.type == BuildingObserver::Type::kTrainingsite) {
-			if (target_bo.trainingsite_type == TrainingSiteType::kBasic) {
-				ts_basic_const_count_ -= 1;
-				assert(ts_basic_const_count_ >= 0);
-			}
-			if (target_bo.trainingsite_type == TrainingSiteType::kAdvanced) {
-				ts_advanced_const_count_ -= 1;
-				assert(ts_advanced_const_count_ >= 0);
-			}
+			assert(ts_in_const_count_ > 0);
+			ts_in_const_count_ -= 1;
 		}
 
 	} else {
@@ -4430,6 +5661,15 @@
 			for (uint32_t i = 0; i < bo.inputs.size(); ++i) {
 				--wares.at(bo.inputs.at(i)).consumers;
 			}
+			if (bo.is(BuildingAttribute::kFisher)) {
+				assert(fishers_count_ > 0);
+				fishers_count_ -= 1;
+			}
+
+			if (bo.is_what.count(BuildingAttribute::kBakery)) {
+				assert(bakeries_count_ > 0);
+				bakeries_count_ -= 1;
+			}
 
 		} else if (bo.type == BuildingObserver::Type::kMine) {
 			for (std::list<ProductionSiteObserver>::iterator i = mines_.begin(); i != mines_.end();
@@ -4450,6 +5690,11 @@
 
 			mines_per_type[bo.mines].finished -= 1;
 
+			if (bo.is(BuildingAttribute::kBuildingMatProducer)) {
+				assert(buil_material_mines_count > 0);
+				buil_material_mines_count += 1;
+			}
+
 		} else if (bo.type == BuildingObserver::Type::kMilitarysite) {
 			msites_per_size[bo.desc->get_size()].finished -= 1;
 
@@ -4460,27 +5705,23 @@
 					break;
 				}
 			}
+
 		} else if (bo.type == BuildingObserver::Type::kTrainingsite) {
+			assert(ts_finished_count_ >= 1);
+			ts_finished_count_ -= 1;
 
 			for (std::list<TrainingSiteObserver>::iterator i = trainingsites.begin();
 			     i != trainingsites.end(); ++i) {
 				if (i->site == &b) {
 					trainingsites.erase(i);
-					if (bo.trainingsite_type == TrainingSiteType::kBasic) {
-						ts_basic_count_ -= 1;
-						assert(ts_basic_count_ >= 0);
-					}
-					if (bo.trainingsite_type == TrainingSiteType::kAdvanced) {
-						ts_advanced_count_ -= 1;
-						assert(ts_advanced_count_ >= 0);
-					}
 					break;
 				}
 			}
+
 		} else if (bo.type == BuildingObserver::Type::kWarehouse) {
 			assert(numof_warehouses_ > 0);
 			--numof_warehouses_;
-			if (bo.is_port) {
+			if (bo.is_what.count(BuildingAttribute::kPort)) {
 				--num_ports;
 			}
 
@@ -4499,7 +5740,6 @@
 // Recursively verify that all inputs have a producer.
 // TODO(unknown): this function leads to periodic freezes of ~1 second on big games on my system.
 // TODO(unknown): It needs profiling and optimization.
-// NOTE: This is not needed anymore and it seems it is not missed neither
 bool DefaultAI::check_supply(const BuildingObserver& bo) {
 	size_t supplied = 0;
 	for (const Widelands::DescriptionIndex& temp_inputs : bo.inputs) {
@@ -4517,6 +5757,72 @@
 	return supplied == bo.inputs.size();
 }
 
+// TODO (tiborb) - should be called from scheduler, once in 60s is enough
+void DefaultAI::update_player_stat(const uint32_t gametime) {
+	if (player_statistics.get_update_time() > 0 &&
+	    player_statistics.get_update_time() + 15 * 1000 > gametime) {
+		return;
+	}
+	player_statistics.set_update_time(gametime);
+	Map& map = game().map();
+	Widelands::PlayerNumber const pn = player_number();
+	PlayerNumber const nr_players = map.get_nrplayers();
+	uint32_t plr_in_game = 0;
+	iterate_players_existing_novar(p, nr_players, game())++ plr_in_game;
+
+	// receiving games statistics and parsing it (reading latest entry)
+	const Game::GeneralStatsVector& genstats = game().get_general_statistics();
+
+	// Collecting statistics and saving them in player_statistics object
+	const Player* me = game().get_player(pn);
+	for (Widelands::PlayerNumber j = 1; j <= plr_in_game; ++j) {
+		const Player* this_player = game().get_player(j);
+		if (this_player) {
+			try {
+				const uint32_t vsize = genstats.at(j - 1).miltary_strength.size();
+
+				uint32_t cur_strength = 0;
+				uint32_t cur_land = 0;
+				uint32_t old_strength = 0;
+				uint32_t old60_strength = 0;
+				uint32_t old_land = 0;
+				uint32_t old60_land = 0;
+				uint32_t cass = 0;
+				if (vsize > 0) {
+					cur_strength = genstats.at(j - 1).miltary_strength.back();
+					cur_land = genstats.at(j - 1).land_size.back();
+					cass = genstats.at(j - 1).nr_casualties.back();
+
+					if (vsize > 21) {
+						old_strength = genstats.at(j - 1).miltary_strength[vsize - 20];
+						old_land = genstats.at(j - 1).land_size[vsize - 20];
+					} else {
+						old_strength = genstats.at(j - 1).miltary_strength[0];
+						old_land = genstats.at(j - 1).land_size[0];
+					}
+					if (vsize > 91) {
+						old60_strength = genstats.at(j - 1).miltary_strength[vsize - 90];
+						old60_land = genstats.at(j - 1).land_size[vsize - 90];
+					} else {
+						old60_strength = genstats.at(j - 1).miltary_strength[0];
+						old60_land = genstats.at(j - 1).land_size[0];
+					}
+				}
+
+				player_statistics.add(pn, j, me->team_number(), this_player->team_number(),
+				                      cur_strength, old_strength, old60_strength, cass, cur_land,
+				                      old_land, old60_land);
+			} catch (const std::out_of_range&) {
+				log("ComputerPlayer(%d): genstats entry missing - size :%d\n",
+				    static_cast<unsigned int>(player_number()),
+				    static_cast<unsigned int>(genstats.size()));
+			}
+		}
+	}
+
+	player_statistics.recalculate_team_power();
+}
+
 // This runs once in 15 minutes, and adjust wares targets based on number of
 // productionsites and ports
 void DefaultAI::review_wares_targets(uint32_t const gametime) {
@@ -4542,10 +5848,10 @@
 			// It seems that when default target for ware is not set, it returns
 			// kInvalidWare (=254), this is confusing for AI so we change it to 10
 			if (default_target == Widelands::kInvalidWare) {
-				default_target = 10;
+				default_target = kTargetQuantCap;
 			}
 
-			uint16_t new_target = std::max<uint16_t>(default_target * multiplier / 10, 2);
+			const uint16_t new_target = std::max<uint16_t>(default_target * multiplier / 10, 3);
 			assert(new_target > 1);
 
 			game().send_player_command(*new Widelands::CmdSetWareTargetQuantity(
@@ -4630,7 +5936,7 @@
 // and needs to know what resourcess are missing for which player and so on.
 // By default it is off (see kPrintStats)
 // TODO(tiborb ?): - it would be nice to have this activated by a command line switch
-void DefaultAI::print_stats() {
+void DefaultAI::print_stats(uint32_t const gametime) {
 
 	if (!kPrintStats) {
 		set_taskpool_task_time(std::numeric_limits<int32_t>::max(), SchedulerTaskId::kPrintStats);
@@ -4639,42 +5945,147 @@
 
 	PlayerNumber const pn = player_number();
 
-	// we test following materials
-	const std::vector<std::string> materials = {"coal",
-	                                            "log",
-	                                            "iron_ore",
-	                                            "iron",
-	                                            "marble",
-	                                            "planks",
-	                                            "water",
-	                                            "gold_ore",
-	                                            "granite",
-	                                            "fish",
-	                                            "diamond",
-	                                            "corn",
-	                                            "wheat",
-	                                            "grape",
-	                                            "quartz",
-	                                            "atlanteans_bread",
-	                                            "barbarians_bread",
-	                                            "empire_bread",
-	                                            "meat"};
+	const DescriptionIndex& nr_buildings = game().tribes().nrbuildings();
+	std::set<DescriptionIndex> materials;
+
+	// Collect information about the different buildings that our tribe can have
+	for (DescriptionIndex building_index = 0; building_index < nr_buildings; ++building_index) {
+		const BuildingDescr& bld = *tribe_->get_building_descr(building_index);
+		if (!tribe_->has_building(building_index)) {
+			continue;
+		}
+		if (bld.type() == MapObjectType::PRODUCTIONSITE) {
+			const ProductionSiteDescr& prod = dynamic_cast<const ProductionSiteDescr&>(bld);
+			for (const auto& temp_input : prod.input_wares()) {
+				if (materials.count(temp_input.first) == 0) {
+					materials.insert(temp_input.first);
+				}
+			}
+			for (const auto& temp_cost : prod.buildcost()) {
+				if (materials.count(temp_cost.first) == 0) {
+					materials.insert(temp_cost.first);
+				}
+			}
+		}
+
+		if (bld.type() == MapObjectType::TRAININGSITE) {
+			const ProductionSiteDescr& train = dynamic_cast<const TrainingSiteDescr&>(bld);
+			for (const auto& temp_cost : train.buildcost()) {
+				if (materials.count(temp_cost.first) == 0) {
+					materials.insert(temp_cost.first);
+				}
+			}
+		}
+	}
+
 	std::string summary = "";
-	for (uint32_t j = 0; j < materials.size(); ++j) {
-		DescriptionIndex const index = tribe_->ware_index(materials.at(j));
-		if (!tribe_->has_ware(index)) {
-			continue;
-		}
-		if (get_warehoused_stock(index) > 0) {
-			continue;
-		}
-		summary = summary + materials.at(j) + ", ";
-	}
-
-	log(" %1d: Buildings: Pr:%3u, Ml:%3u, Mi:%2u, Wh:%2u, Po:%u. Missing: %s\n", pn,
-	    static_cast<uint32_t>(productionsites.size()), static_cast<uint32_t>(militarysites.size()),
-	    static_cast<uint32_t>(mines_.size()),
-	    static_cast<uint32_t>(warehousesites.size() - num_ports), num_ports, summary.c_str());
+	for (const auto material : materials) {
+		uint32_t stock = calculate_stocklevel(material);
+		if (stock == 0) {
+			summary = summary + game().tribes().get_ware_descr(material)->descname() + ", ";
+		}
+	}
+
+	if (false)
+		log(" %1d: %s Buildings count: Pr:%3u, Ml:%3u, Mi:%2u, Wh:%2u, Po:%u.\n", pn,
+		    gamestring_with_leading_zeros(gametime), static_cast<uint32_t>(productionsites.size()),
+		    static_cast<uint32_t>(militarysites.size()), static_cast<uint32_t>(mines_.size()),
+		    static_cast<uint32_t>(warehousesites.size() - num_ports), num_ports);
+
+	if (false)
+		log(" %1s %-30s   %5s(perf)  %6s %6s %6s %8s %5s %5s %5s %5s\n", "T", "Buildings", "work.",
+		    "const.", "unocc.", "uncon.", "needed", "prec.", "pprio", "stock", "targ.");
+	for (uint32_t j = 0; j < buildings_.size(); ++j) {
+		BuildingObserver& bo = buildings_.at(j);
+		if ((bo.total_count() > 0 || bo.new_building == BuildingNecessity::kNeeded ||
+		     bo.new_building == BuildingNecessity::kForced ||
+		     bo.new_building == BuildingNecessity::kNeededPending ||
+		     bo.new_building == BuildingNecessity::kAllowed) &&
+		    bo.type != BuildingObserver::Type::kMilitarysite) {
+			std::string needeness;
+			if (bo.new_building == BuildingNecessity::kNeededPending) {
+				needeness = "pend";
+			} else if (bo.new_building == BuildingNecessity::kForced) {
+				needeness = "forc";
+			} else if (bo.new_building == BuildingNecessity::kAllowed) {
+				needeness = "allw";
+			} else if (bo.new_building == BuildingNecessity::kNotNeeded ||
+			           bo.new_building == BuildingNecessity::kForbidden) {
+				needeness = "no";
+			} else {
+				needeness = "yes";
+			}
+			std::string btype;
+			switch (bo.type) {
+			case BuildingObserver::Type::kWarehouse:
+				btype = "W";
+				break;
+			case BuildingObserver::Type::kMine:
+				btype = "M";
+				break;
+			case BuildingObserver::Type::kTrainingsite:
+				btype = "T";
+				break;
+			case BuildingObserver::Type::kProductionsite:
+				btype = "P";
+				break;
+			default:
+				btype = "?";
+			}
+
+			if (false)
+				log(" %1s %-30s %5d(%3d%%)  %6d %6d %6d %8s %5d %5d %5d %5d\n", btype.c_str(), bo.name,
+				    bo.total_count() - bo.cnt_under_construction - bo.unoccupied_count -
+				       bo.unconnected_count,
+				    bo.current_stats, bo.cnt_under_construction, bo.unoccupied_count,
+				    bo.unconnected_count, needeness.c_str(), bo.max_needed_preciousness,
+				    bo.primary_priority, get_stocklevel(bo, gametime), bo.cnt_target);
+		}
+	}
+
+	std::string why = "; Why: ";
+
+	if ((numof_psites_in_constr + mines_in_constr()) >
+	    (productionsites.size() + mines_built()) / persistent_data->ai_productionsites_ratio + 2) {
+		why += " too many constr.";
+	}
+	// 3. too keep some proportions production sites vs military sites
+	if ((numof_psites_in_constr + productionsites.size()) >
+	    (msites_in_constr() + militarysites.size()) * 5) {
+		why += ", too many productionsites";
+	}
+	// 4. if we do not have 2 mines at least
+	if (mines_.size() < 2) {
+		why += ", less then 2 mines";
+	}
+
+	if (false)
+		log("Prodsites in constr: %2d, mines in constr: %2d %s %s\n", numof_psites_in_constr,
+		    mines_in_constr(),
+		    (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) ? "NEW BUILDING STOP" :
+		                                                                       "",
+		    why.c_str());
+
+	if (false)
+		log("Least military score: %5d/%3d, msites in constr: %3d,"
+		    "soldier st: %2d, strength: %3d\n",
+		    persistent_data->least_military_score, persistent_data->ai_personality_mil_upper_limit,
+		    msites_in_constr(), static_cast<int8_t>(soldier_status_),
+		    player_statistics.get_modified_player_power(player_number()));
+	std::string wpolicy = "";
+	switch (wood_policy_) {
+	case WoodPolicy::kDismantleRangers:
+		wpolicy = "Dismantle rangers";
+		break;
+	case WoodPolicy::kAllowRangers:
+		wpolicy = "Allow rangers";
+		break;
+	case WoodPolicy::kStopRangers:
+		wpolicy = "Stop rangers";
+		break;
+	default:
+		wpolicy = "unknown";
+	}
 }
 
 template <typename T>
@@ -4708,3 +6119,74 @@
 
 	return new_target;
 }
+
+// Looking for situation that for a critical mine (iron, or marble) there is just one mine and it is
+// unoccupied, probably we need to dismantle another one to release miner
+bool DefaultAI::critical_mine_unoccupied(uint32_t gametime) {
+	// resetting unoccupied
+	for (auto& mine : mines_per_type) {
+		mine.second.unoccupied = 0;
+	}
+	for (auto& mine : mines_) {
+		if (!mines_per_type[mine.bo->mines].is_critical) {
+			continue;
+		}
+		if (mine.built_time + 3 * 60 * 1000 < gametime && !mine.site->can_start_working()) {
+			mines_per_type[mine.bo->mines].unoccupied += 1;
+		}
+	}
+
+	// Now check that that there is single and unworking mine of critical type
+	for (auto& mine : mines_per_type) {
+		if (mine.second.is_critical && mine.second.total_count() == 1 &&
+		    mine.second.unoccupied == 1) {
+			return true;
+		}
+		assert(mine.second.unoccupied <= mines_.size());
+		assert(mine.second.unoccupied <= mine.second.total_count());
+	}
+	return false;
+}
+
+// Sets all inputs to zero and return true if inputs are already empty
+bool DefaultAI::set_inputs_to_zero(const Widelands::ProductionSiteObserver& site) {
+	uint16_t remaining_wares = 0;
+
+	for (auto& queue : site.site->inputqueues()) {
+		remaining_wares += queue->get_filled();
+		if (queue->get_max_fill() > 0) {
+			game().send_player_set_input_max_fill(
+			   *site.site, queue->get_index(), queue->get_type(), 0);
+		}
+	}
+	if (remaining_wares == 0) {
+		return true;
+	}
+	return false;
+}
+
+void DefaultAI::set_inputs_to_max(const Widelands::ProductionSiteObserver& site) {
+	for (auto& queue : site.site->inputqueues()) {
+		if (queue->get_max_fill() < queue->get_max_size()) {
+			game().send_player_set_input_max_fill(
+			   *site.site, queue->get_index(), queue->get_type(), queue->get_max_size());
+		}
+	}
+}
+void DefaultAI::stop_site(const Widelands::ProductionSiteObserver& site) {
+	if (!site.site->is_stopped()) {
+		game().send_player_start_stop_building(*site.site);
+	}
+}
+
+void DefaultAI::initiate_dismantlement(Widelands::ProductionSiteObserver& site, uint32_t gametime) {
+	site.dismantle_pending_since = gametime;
+	set_inputs_to_zero(site);
+	site.bo->construction_decision_time = gametime;
+}
+
+Widelands::PlayerNumber DefaultAI::get_land_owner(const Widelands::Map& map,
+                                                  const uint32_t coords) {
+	FCoords f = map.get_fcoords(Widelands::Coords::unhash(coords));
+	return f.field->get_owned_by();
+}

=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h	2017-01-25 18:55:59 +0000
+++ src/ai/defaultai.h	2017-06-16 05:22:22 +0000
@@ -23,6 +23,7 @@
 
 #include <map>
 #include <memory>
+#include <unordered_map>
 #include <unordered_set>
 
 #include "ai/ai_help_structs.h"
@@ -73,13 +74,8 @@
 //   out, to have some more forces. Reincrease the number of soldiers that
 //   should be trained if inputs_ get filled again.).
 struct DefaultAI : ComputerPlayer {
-	enum class Type {
-		kVeryWeak,
-		kWeak,
-		kNormal,
-	};
 
-	DefaultAI(Widelands::Game&, const Widelands::PlayerNumber, DefaultAI::Type);
+	DefaultAI(Widelands::Game&, const Widelands::PlayerNumber, Widelands::AiType);
 	~DefaultAI();
 	void think() override;
 
@@ -87,12 +83,11 @@
 	enum class WoodPolicy : uint8_t { kDismantleRangers, kStopRangers, kAllowRangers };
 	enum class NewShip : uint8_t { kBuilt, kFoundOnLoad };
 	enum class PerfEvaluation : uint8_t { kForConstruction, kForDismantle };
-	enum class Attackable : uint8_t {
-		kNotAttackable,
-		kAttackable,
-		kAttackableAndWeak,
-		kAttackableVeryWeak
-	};
+	enum class BasicEconomyBuildingStatus : uint8_t { kEncouraged, kDiscouraged, kNeutral, kNone };
+
+	enum class SoldiersStatus : uint8_t { kFull = 0, kEnough = 1, kShortage = 3, kBadShortage = 6 };
+
+	enum class WareWorker : uint8_t { kWare, kWorker };
 
 	/// Implementation for Strong
 	struct NormalImpl : public ComputerPlayer::Implementation {
@@ -105,7 +100,7 @@
 		}
 		ComputerPlayer* instantiate(Widelands::Game& game,
 		                            Widelands::PlayerNumber const p) const override {
-			return new DefaultAI(game, p, DefaultAI::Type::kNormal);
+			return new DefaultAI(game, p, Widelands::AiType::kNormal);
 		}
 	};
 
@@ -119,7 +114,7 @@
 		}
 		ComputerPlayer* instantiate(Widelands::Game& game,
 		                            Widelands::PlayerNumber const p) const override {
-			return new DefaultAI(game, p, DefaultAI::Type::kWeak);
+			return new DefaultAI(game, p, Widelands::AiType::kWeak);
 		}
 	};
 
@@ -133,7 +128,7 @@
 		}
 		ComputerPlayer* instantiate(Widelands::Game& game,
 		                            Widelands::PlayerNumber const p) const override {
-			return new DefaultAI(game, p, DefaultAI::Type::kVeryWeak);
+			return new DefaultAI(game, p, Widelands::AiType::kVeryWeak);
 		}
 	};
 
@@ -143,7 +138,7 @@
 
 private:
 	// Variables of default AI
-	DefaultAI::Type type_;
+	Widelands::AiType type_;
 	Widelands::Player* player_;
 	Widelands::TribeDescr const* tribe_;
 
@@ -154,19 +149,25 @@
 	static constexpr uint8_t kFalse = 0;
 	static constexpr uint8_t kTrue = 1;
 
+	static constexpr bool kAbsValue = true;
+	static constexpr int32_t kSpotsTooLittle = 15;
+	static constexpr int kManagementUpdateInterval = 10 * 60 * 1000;
+	static constexpr int kStatUpdateInterval = 60 * 1000;
+
 	void late_initialization();
 
 	void update_all_buildable_fields(uint32_t);
 	void update_all_mineable_fields(uint32_t);
 	void update_all_not_buildable_fields();
-	void update_buildable_field(Widelands::BuildableField&, uint16_t = 6, bool = false);
+	void update_buildable_field(Widelands::BuildableField&);
 	void update_mineable_field(Widelands::MineableField&);
 	void update_productionsite_stats();
 
 	// for production sites
 	Widelands::BuildingNecessity
 	check_building_necessity(Widelands::BuildingObserver& bo, PerfEvaluation purpose, uint32_t);
-
+	Widelands::BuildingNecessity check_warehouse_necessity(Widelands::BuildingObserver&,
+	                                                       uint32_t gametime);
 	void sort_task_pool();
 	void sort_by_priority();
 	void set_taskpool_task_time(uint32_t, Widelands::SchedulerTaskId);
@@ -189,14 +190,18 @@
 	bool check_productionsites(uint32_t);
 	bool check_mines_(uint32_t);
 
-	void print_stats();
+	void print_stats(uint32_t);
 
 	uint32_t get_stocklevel_by_hint(size_t);
-	uint32_t get_stocklevel(Widelands::BuildingObserver&);
-	uint32_t get_warehoused_stock(Widelands::DescriptionIndex wt);
-	uint32_t get_stocklevel(Widelands::DescriptionIndex);  // count all direct outputs_
+	uint32_t get_stocklevel(Widelands::BuildingObserver&, uint32_t, WareWorker = WareWorker::kWare);
+	uint32_t calculate_stocklevel(Widelands::BuildingObserver&, WareWorker = WareWorker::kWare);
+	uint32_t calculate_stocklevel(Widelands::DescriptionIndex,
+	                              WareWorker = WareWorker::kWare);  // count all direct outputs_
+
 	void review_wares_targets(uint32_t);
 
+	void update_player_stat(uint32_t);
+
 	// sometimes scanning an area in radius gives inappropriate results, so this is to verify that
 	// other player is accessible
 	// via walking
@@ -214,6 +219,9 @@
 
 	Widelands::EconomyObserver* get_economy_observer(Widelands::Economy&);
 	Widelands::BuildingObserver& get_building_observer(char const*);
+	bool has_building_observer(char const*);
+	Widelands::BuildingObserver& get_building_observer(Widelands::BuildingAttribute);
+	Widelands::BuildingObserver& get_building_observer(Widelands::DescriptionIndex);
 
 	void gain_immovable(Widelands::PlayerImmovable&, bool found_on_load = false);
 	void lose_immovable(const Widelands::PlayerImmovable&);
@@ -221,6 +229,11 @@
 	void lose_building(const Widelands::Building&);
 	void out_of_resources_site(const Widelands::ProductionSite&);
 	bool check_supply(const Widelands::BuildingObserver&);
+	bool set_inputs_to_zero(const Widelands::ProductionSiteObserver&);
+	void set_inputs_to_max(const Widelands::ProductionSiteObserver&);
+	void stop_site(const Widelands::ProductionSiteObserver&);
+	void initiate_dismantlement(Widelands::ProductionSiteObserver&, const uint32_t);
+	// bool set_inputs_to_zero(const Widelands::ProductionSite&);
 	void print_land_stats();
 
 	// Checks whether first value is in range, or lesser then...
@@ -237,16 +250,26 @@
 	bool marine_main_decisions();
 	bool check_ships(uint32_t);
 
+	// finding and owner
+	Widelands::PlayerNumber get_land_owner(const Widelands::Map&, const uint32_t);
+
 	// Functions used for war and training stuff / defaultai_warfare.cc
 	bool check_militarysites(uint32_t);
 	bool check_enemy_sites(uint32_t);
 	void count_military_vacant_positions();
 	bool check_trainingsites(uint32_t);
+	uint32_t barracks_count();
 	// return single number of strength of vector of soldiers
 	int32_t calculate_strength(const std::vector<Widelands::Soldier*>&);
 	// for militarysites (overloading the function)
-	Widelands::BuildingNecessity check_building_necessity(uint8_t, uint32_t);
+	Widelands::BuildingNecessity check_building_necessity(Widelands::BuildingObserver&, uint32_t);
 	void soldier_trained(const Widelands::TrainingSite&);
+	SoldiersStatus soldier_status_;
+	int32_t vacant_mil_positions_average_;
+	uint16_t attackers_count_;
+	// uint16_t overfilled_msites_count;
+	Widelands::EventTimeQueue soldier_trained_log;
+	Widelands::EventTimeQueue soldier_attacks_log;
 
 	// used by AI scheduler
 	uint32_t sched_stat_[20] = {0};
@@ -256,12 +279,15 @@
 	int32_t scheduler_delay_counter_;
 
 	WoodPolicy wood_policy_;
+	uint16_t trees_nearby_treshold_;
 
 	std::vector<Widelands::BuildingObserver> buildings_;
 	std::list<Widelands::FCoords> unusable_fields;
 	std::list<Widelands::BuildableField*> buildable_fields;
 	Widelands::BlockedFields blocked_fields;
 	Widelands::PlayersStrengths player_statistics;
+	Widelands::ManagementData management_data;
+	Widelands::ExpansionType expansion_type;
 	std::unordered_set<uint32_t> port_reserved_coords;
 	std::list<Widelands::MineableField*> mineable_fields;
 	std::list<Widelands::Flag const*> new_flags;
@@ -279,31 +305,35 @@
 	// and no items are added/removed afterwards
 	std::vector<Widelands::SchedulerTask> taskPool;
 	std::map<uint32_t, Widelands::EnemySiteObserver> enemy_sites;
+	std::set<uint32_t> enemy_warehouses;
 	// it will map mined material to observer
 	std::map<int32_t, Widelands::MineTypesObserver> mines_per_type;
+	bool critical_mine_unoccupied(const uint32_t);
+	std::vector<uint32_t> spots_avail;
 
 	// used for statistics of buildings
-	uint32_t num_prod_constructionsites;
+	uint32_t numof_psites_in_constr;
 	uint32_t num_ports;
 	uint16_t numof_warehouses_;
+	uint16_t numof_warehouses_in_const_;
 	uint32_t mines_in_constr() const;
 	uint32_t mines_built() const;
 	std::map<int32_t, Widelands::MilitarySiteSizeObserver> msites_per_size;
 	// for militarysites
 	uint32_t msites_in_constr() const;
 	uint32_t msites_built() const;
-	uint16_t military_last_dismantle_;
+	uint32_t military_last_dismantle_;
 	uint32_t military_last_build_;  // sometimes expansions just stops, this is time of last military
 	                                // building build
 	int32_t limit_cnt_target(int32_t, int32_t);
 	uint32_t time_of_last_construction_;
 	uint32_t next_mine_construction_due_;
+	uint16_t fishers_count_;
+	uint16_t bakeries_count_;
 
 	// for training sites per type
-	int16_t ts_basic_count_;
-	int16_t ts_basic_const_count_;
-	int16_t ts_advanced_count_;
-	int16_t ts_advanced_const_count_;
+	int16_t ts_finished_count_;
+	int16_t ts_in_const_count_;
 	int16_t ts_without_trainers_;
 
 	// for roads
@@ -311,25 +341,27 @@
 	uint32_t last_road_dismantled_;  // uses to prevent too frequent road dismantling
 
 	uint32_t enemy_last_seen_;
-	int32_t vacant_mil_positions_;  // sum of vacant positions in militarysites and training sites
+	// int32_t vacant_mil_positions_;  // sum of vacant positions in militarysites and training sites
 	uint32_t last_attack_time_;
 	// check ms in this interval - will auto-adjust
 	uint32_t enemysites_check_delay_;
 
 	int32_t spots_;  // sum of buildable fields
-	bool new_buildings_stop_;
-
-	// when territory is expanded for every candidate field benefits are calculated
-	// but need for water, space, mines can vary
-	// so if 255 = resource is needed, 0 = not needed
-	int32_t resource_necessity_territory_;
-	int32_t resource_necessity_mines_;
-	int32_t resource_necessity_water_;
+
+	int16_t productionsites_ratio_;
+
 	bool resource_necessity_water_needed_;  // unless atlanteans
 
 	// This stores highest priority for new buildings except for militarysites
 	int32_t highest_nonmil_prio_;
 
+	// if the basic economy is not established, there must be non-empty list of remaining basic
+	// buildings
+	bool basic_economy_established;
+
+	// id of iron_ore to identify iron mines in mines_per_type map
+	int32_t iron_ore_id = -1;
+
 	// this is a bunch of patterns that have to identify weapons and armors for input queues of
 	// trainingsites
 	std::vector<std::string> const armors_and_weapons = {
@@ -343,6 +375,9 @@
 	std::vector<int16_t> marine_task_queue;
 	std::unordered_set<uint32_t> expedition_visited_spots;
 
+	std::vector<std::vector<int16_t>> AI_military_matrix;
+	std::vector<int16_t> AI_military_numbers;
+
 	// common for defaultai.cc and defaultai_seafaring.cc
 	static constexpr uint32_t kColonyScanStartArea = 35;
 	static constexpr uint32_t kColonyScanMinArea = 12;
@@ -358,6 +393,9 @@
 	static constexpr int kCampaignDuration = 15 * 60 * 1000;
 	static constexpr int kTrainingSitesCheckInterval = 15 * 1000;
 
+	bool has_critical_mines = false;
+	uint16_t buil_material_mines_count = 0;
+
 	// Notification subscribers
 	std::unique_ptr<Notifications::Subscriber<Widelands::NoteFieldPossession>>
 	   field_possession_subscriber_;

=== modified file 'src/ai/defaultai_seafaring.cc'
--- src/ai/defaultai_seafaring.cc	2017-04-25 08:05:09 +0000
+++ src/ai/defaultai_seafaring.cc	2017-06-16 05:22:22 +0000
@@ -118,7 +118,7 @@
 
 	// goes over all warehouses (these includes ports)
 	for (const WarehouseSiteObserver& wh_obs : warehousesites) {
-		if (wh_obs.bo->is_port) {
+		if (wh_obs.bo->is_what.count(BuildingAttribute::kPort)) {
 			ports_count += 1;
 			if (Widelands::PortDock* pd = wh_obs.site->get_portdock()) {
 				if (pd->expedition_started()) {
@@ -130,7 +130,7 @@
 
 	// goes over productionsites and gets status of shipyards
 	for (const ProductionSiteObserver& ps_obs : productionsites) {
-		if (ps_obs.bo->is_shipyard) {
+		if (ps_obs.bo->is(BuildingAttribute::kShipyard)) {
 			shipyards_count += 1;
 
 			// counting stocks
@@ -186,7 +186,7 @@
 	if (enough_ships == FleetStatus::kNeedShip) {
 
 		for (const ProductionSiteObserver& ps_obs : productionsites) {
-			if (ps_obs.bo->is_shipyard && ps_obs.site->can_start_working() &&
+			if (ps_obs.bo->is(BuildingAttribute::kShipyard) && ps_obs.site->can_start_working() &&
 			    ps_obs.site->is_stopped()) {
 				// make sure it is fully stocked
 				// counting stocks
@@ -213,7 +213,7 @@
 
 		// we need to find a port
 		for (const WarehouseSiteObserver& wh_obs : warehousesites) {
-			if (wh_obs.bo->is_port) {
+			if (wh_obs.bo->is_what.count(BuildingAttribute::kPort)) {
 				game().send_player_start_or_cancel_expedition(*wh_obs.site);
 				return true;
 			}
@@ -303,7 +303,7 @@
 			// iterate over all production sites searching for shipyard
 			for (std::list<ProductionSiteObserver>::iterator site = productionsites.begin();
 			     site != productionsites.end(); ++site) {
-				if (site->bo->is_shipyard) {
+				if (site->bo->is(BuildingAttribute::kShipyard)) {
 					if (!site->site->is_stopped()) {
 						game().send_player_start_stop_building(*site->site);
 					}
@@ -314,7 +314,7 @@
 		if (marine_task_queue.back() == kReprioritize) {
 			for (std::list<ProductionSiteObserver>::iterator site = productionsites.begin();
 			     site != productionsites.end(); ++site) {
-				if (site->bo->is_shipyard) {
+				if (site->bo->is(BuildingAttribute::kShipyard)) {
 					for (uint32_t k = 0; k < site->bo->inputs.size(); ++k) {
 						game().send_player_set_ware_priority(
 						   *site->site, wwWARE, site->bo->inputs.at(k), HIGH_PRIORITY);

=== modified file 'src/ai/defaultai_warfare.cc'
--- src/ai/defaultai_warfare.cc	2017-05-20 22:42:49 +0000
+++ src/ai/defaultai_warfare.cc	2017-06-16 05:22:22 +0000
@@ -25,86 +25,16 @@
 
 	Map& map = game().map();
 
-	// define which players are attackable
-	std::vector<Attackable> player_attackable;
 	PlayerNumber const nr_players = map.get_nrplayers();
-	player_attackable.resize(nr_players);
 	uint32_t plr_in_game = 0;
 	Widelands::PlayerNumber const pn = player_number();
 
 	iterate_players_existing_novar(p, nr_players, game())++ plr_in_game;
 
-	// receiving games statistics and parsing it (reading latest entry)
-	const Game::GeneralStatsVector& genstats = game().get_general_statistics();
-
-	// Collecting statistics and saving them in player_statistics object
-	for (Widelands::TeamNumber j = 1; j <= plr_in_game; ++j) {
-		const Player* this_player = game().get_player(j);
-		if (this_player) {
-			try {
-				player_statistics.add(
-				   j, this_player->team_number(), genstats.at(j - 1).miltary_strength.back());
-			} catch (const std::out_of_range&) {
-				log("ComputerPlayer(%d): genstats entry missing - size :%d\n",
-				    static_cast<unsigned int>(player_number()),
-				    static_cast<unsigned int>(genstats.size()));
-			}
-		}
-	}
-
-	player_statistics.recalculate_team_power();
-
-	// defining treshold ratio of own_strength/enemy's strength
-	uint32_t treshold_ratio = 100;
-	if (type_ == DefaultAI::Type::kNormal) {
-		treshold_ratio = 80;
-	}
-	if (type_ == DefaultAI::Type::kVeryWeak) {
-		treshold_ratio = 120;
-	}
-
-	// let's say a 'campaign' is a series of attacks,
-	// if there is more then 3 minutes without attack after last
-	// attack, then a campaign is over.
-	// To start new campaign (=attack again), our strenth must exceed
-	// target values (calculated above) by some treshold =
-	// ai_personality_attack_margin
-	// Once a new campaign started we will fight until
-	// we get below above treshold or there will be 3
-	// minutes gap since last attack
-	// note - AI is not aware of duration of attacks
-	// everywhere we consider time when an attack is ordered.
-	if (last_attack_time_ < gametime - kCampaignDuration) {
-		treshold_ratio += persistent_data->ai_personality_attack_margin;
-	}
+	update_player_stat(gametime);
 
 	const uint32_t my_power = player_statistics.get_modified_player_power(pn);
 
-	// now we test all players to identify 'attackable' ones
-	for (Widelands::PlayerNumber j = 1; j <= plr_in_game; ++j) {
-		// if we are the same team, or it is just me
-		if (player_statistics.players_in_same_team(pn, j) || pn == j) {
-			player_attackable[j - 1] = Attackable::kNotAttackable;
-			continue;
-		}
-
-		// now we compare strength
-		// strength of the other player (considering his team)
-		uint32_t players_power = player_statistics.get_modified_player_power(j);
-
-		if (players_power == 0) {
-			player_attackable.at(j - 1) = Attackable::kAttackable;
-		} else if (my_power * 100 / players_power > treshold_ratio * 8) {
-			player_attackable.at(j - 1) = Attackable::kAttackableVeryWeak;
-		} else if (my_power * 100 / players_power > treshold_ratio * 4) {
-			player_attackable.at(j - 1) = Attackable::kAttackableAndWeak;
-		} else if (my_power * 100 / players_power > treshold_ratio) {
-			player_attackable.at(j - 1) = Attackable::kAttackable;
-		} else {
-			player_attackable.at(j - 1) = Attackable::kNotAttackable;
-		}
-	}
-
 	// first we scan vicitnity of couple of militarysites to get new enemy sites
 	// Militarysites rotate (see check_militarysites())
 	int32_t i = 0;
@@ -126,16 +56,26 @@
 
 		for (uint32_t j = 0; j < immovables.size(); ++j) {
 			if (upcast(MilitarySite const, bld, immovables.at(j).object)) {
-				if (player_->is_hostile(bld->owner())) {
+				const PlayerNumber opn = bld->owner().player_number();
+				if (player_statistics.get_is_enemy(opn)) {
+					assert(opn != pn);
+					player_statistics.set_last_time_seen(gametime, opn);
 					if (enemy_sites.count(bld->get_position().hash()) == 0) {
 						enemy_sites[bld->get_position().hash()] = EnemySiteObserver();
+					} else {
+						enemy_sites[bld->get_position().hash()].last_time_seen = gametime;
 					}
 				}
 			}
 			if (upcast(Warehouse const, wh, immovables.at(j).object)) {
-				if (player_->is_hostile(wh->owner())) {
+				const PlayerNumber opn = wh->owner().player_number();
+				if (player_statistics.get_is_enemy(opn)) {
+					assert(opn != pn);
+					player_statistics.set_last_time_seen(gametime, opn);
 					if (enemy_sites.count(wh->get_position().hash()) == 0) {
 						enemy_sites[wh->get_position().hash()] = EnemySiteObserver();
+					} else {
+						enemy_sites[wh->get_position().hash()].last_time_seen = gametime;
 					}
 				}
 			}
@@ -150,62 +90,53 @@
 	std::vector<uint32_t> disappeared_sites;
 
 	// Willingness to attack depend on how long ago the last soldier has been trained. This is used
-	// as
-	// indicator how busy our trainingsites are.
+	// as indicator how busy our trainingsites are.
 	// Moreover the stronger AI the more sensitive to it it is (a score of attack willingness is more
 	// decreased if promotion of soldiers is stalled)
-	int8_t training_score = 0;
-	if (persistent_data->last_soldier_trained > gametime) {
-		// No soldier was ever trained ...
-		switch (type_) {
-		case DefaultAI::Type::kNormal:
-			training_score = -8;
-			break;
-		case DefaultAI::Type::kWeak:
-			training_score = -4;
-			break;
-		case DefaultAI::Type::kVeryWeak:
-			training_score = -2;
-		}
-	} else if (persistent_data->last_soldier_trained + 10 * 60 * 1000 < gametime) {
-		// was any soldier trained within last 10 minutes
-		switch (type_) {
-		case DefaultAI::Type::kNormal:
-			training_score = -4;
-			break;
-		case DefaultAI::Type::kWeak:
-			training_score = -2;
-			break;
-		case DefaultAI::Type::kVeryWeak:
-			training_score = -1;
-		}
-	}
-	// Also we should have at least some training sites to be more willing to attack
-	// Of course, very weak AI can have only one trainingsite so will be always penalized by this
-	switch (ts_basic_count_ + ts_advanced_count_ - ts_without_trainers_) {
-	case 0:
-		training_score -= 6;
-		break;
-	case 1:
-		training_score -= 3;
-		break;
-	case 2:
-		training_score -= 1;
-		break;
-	default:;
+	int8_t general_score = 0;
+	if (soldier_trained_log.count(gametime) == 0) {
+		// No soldier was trained lately ...
+		switch (type_) {
+		case Widelands::AiType::kNormal:
+			general_score = 1;
+			break;
+		case Widelands::AiType::kWeak:
+			general_score = 0;
+			break;
+		case Widelands::AiType::kVeryWeak:
+			general_score = -1;
+		}
 	}
 
 	const bool strong_enough = player_statistics.strong_enough(pn);
 
-	for (std::map<uint32_t, EnemySiteObserver>::iterator site = enemy_sites.begin();
-	     site != enemy_sites.end(); ++site) {
+	// removing sites we saw too long ago
+	for (std::map<uint32_t, EnemySiteObserver>::iterator site = enemy_sites.begin();
+	     site != enemy_sites.end(); ++site) {
+		if (site->second.last_time_seen + 20 * 60 * 1000 < gametime) {
+			disappeared_sites.push_back(site->first);
+		}
+	}
+	while (!disappeared_sites.empty()) {
+		enemy_sites.erase(disappeared_sites.back());
+		disappeared_sites.pop_back();
+	}
+
+	for (std::map<uint32_t, EnemySiteObserver>::iterator site = enemy_sites.begin();
+	     site != enemy_sites.end(); ++site) {
+
+		assert(site->second.last_time_attacked <= gametime);
+		// Do not attack too soon
+		if (std::min<uint32_t>(site->second.attack_counter, 10) * 20 * 1000 >
+		    (gametime - site->second.last_time_attacked)) {
+			continue;
+		}
 
 		// we test max 12 sites and prefer ones tested more then 1 min ago
 		if (((site->second.last_tested + (enemysites_check_delay_ * 1000)) > gametime && count > 4) ||
 		    count > 12) {
 			continue;
 		}
-		count += 1;
 
 		site->second.last_tested = gametime;
 		uint8_t defenders_strength = 0;
@@ -219,7 +150,7 @@
 		// testing if we can attack the building - result is a flag
 		// if we dont get a flag, we remove the building from observers list
 		FCoords f = map.get_fcoords(Coords::unhash(site->first));
-		uint32_t site_to_be_removed = std::numeric_limits<uint32_t>::max();
+
 		Flag* flag = nullptr;
 
 		if (upcast(MilitarySite, bld, f.field->get_immovable())) {
@@ -253,6 +184,16 @@
 
 		// if flag is defined it is a good taget
 		if (flag) {
+
+			// Site is still there but not visible for us
+			if (!is_visible) {
+				if (site->second.last_time_seen + 20 * 60 * 1000 < gametime) {
+					log("site %d not visible for more than 20 minutes\n", site->first);
+					disappeared_sites.push_back(site->first);
+				}
+				continue;
+			}
+
 			// updating some info
 			// updating info on mines nearby if needed
 			if (site->second.mines_nearby == ExtendedBool::kUnset) {
@@ -268,100 +209,233 @@
 
 			site->second.is_warehouse = is_warehouse;
 
-			// getting rid of default
-			if (site->second.last_time_attackable == kNever) {
-				site->second.last_time_attackable = gametime;
-			}
-
 			// can we attack:
 			if (is_attackable) {
 				std::vector<Soldier*> attackers;
 				player_->find_attack_soldiers(*flag, &attackers);
-				int32_t strength = calculate_strength(attackers);
+				if (attackers.empty()) {
+					site->second.attack_soldiers_strength = 0;
+				} else {
+					int32_t strength = calculate_strength(attackers);
 
-				site->second.attack_soldiers_strength = strength;
+					site->second.attack_soldiers_strength = strength;
+					assert(!attackers.empty());
+					site->second.attack_soldiers_competency = strength * 10 / attackers.size();
+				}
 			} else {
 				site->second.attack_soldiers_strength = 0;
 			}
 
 			site->second.defenders_strength = defenders_strength;
 
+			site->second.score = 0;
+
 			if (site->second.attack_soldiers_strength > 0 &&
-			    (player_attackable[owner_number - 1] == Attackable::kAttackable ||
-			     player_attackable[owner_number - 1] == Attackable::kAttackableAndWeak ||
-			     player_attackable[owner_number - 1] == Attackable::kAttackableVeryWeak)) {
-				site->second.score =
-				   site->second.attack_soldiers_strength - site->second.defenders_strength / 2;
-
-				if (is_warehouse) {
-					site->second.score += 2;
-				} else {
-					site->second.score -= 2;
-				}
-
-				site->second.score -= vacant_mil_positions_ / 8;
-
-				if (site->second.mines_nearby == ExtendedBool::kFalse) {
-					site->second.score -= 1;
-				} else {
-					site->second.score += 1;
-				}
-				// we dont want to attack multiple players at the same time too eagerly
-				if (owner_number != persistent_data->last_attacked_player) {
-					site->second.score -= 3;
-				}
-				// if we dont have mines yet
-				if (mines_.size() <= 2) {
-					site->second.score -= 8;
-				}
-
-				// Applying (decreasing score) if trainingsites are not working
-				site->second.score += training_score;
-
-				// We have an advantage over stongest opponent
-				if (strong_enough) {
-					site->second.score += 3;
-				}
-
-				// Enemy is too weak, be more aggressive attacking him
-				if (player_attackable[owner_number - 1] == Attackable::kAttackableAndWeak) {
-					site->second.score += 4;
-				}
-				if (player_attackable[owner_number - 1] == Attackable::kAttackableVeryWeak) {
-					site->second.score += 8;
-				}
-
-				// treating no attack score
-				if (site->second.no_attack_counter < 0) {
-					// we cannot attack yet
-					site->second.score = 0;
-					// but increase the counter by 1
-					site->second.no_attack_counter += 1;
-				}
-
-			} else {
+			    !player_statistics.players_in_same_team(pn, owner_number)) {
+
+				const uint16_t enemys_power = player_statistics.get_modified_player_power(owner_number);
+				uint16_t my_to_enemy_power_ratio = 100;
+				if (enemys_power) {
+					my_to_enemy_power_ratio = my_power * 100 / enemys_power;
+				}
+				uint16_t enemys_power_growth = 10;
+				if (player_statistics.get_old60_player_land(owner_number)) {
+					enemys_power_growth = player_statistics.get_player_power(owner_number) * 100 /
+					                      player_statistics.get_old60_player_land(owner_number);
+				}
+				uint16_t own_power_growth = 10;
+				if (player_statistics.get_old60_player_land(pn)) {
+					enemys_power_growth = player_statistics.get_player_power(pn) * 100 /
+					                      player_statistics.get_old60_player_land(pn);
+				}
+
+				int16_t inputs[3 * f_neuron_bit_size] = {0};
+				inputs[0] = (site->second.attack_soldiers_strength - site->second.defenders_strength) *
+				            std::abs(management_data.get_military_number_at(114)) / 30;
+				inputs[1] = (site->second.attack_soldiers_strength - site->second.defenders_strength) *
+				            std::abs(management_data.get_military_number_at(115)) / 30;
+				inputs[2] = (is_warehouse) ? 4 : 0;
+				inputs[3] = (is_warehouse) ? 2 : 0;
+				inputs[4] = (site->second.attack_soldiers_competency > 15) ? 2 : 0;
+				inputs[5] = (site->second.attack_soldiers_competency > 25) ? 4 : 0;
+				;
+				inputs[6] =
+				   (2 * site->second.defenders_strength > 3 * site->second.attack_soldiers_strength) ?
+				      2 :
+				      0;
+				inputs[7] =
+				   (3 * site->second.defenders_strength > 2 * site->second.attack_soldiers_strength) ?
+				      2 :
+				      0;
+				inputs[8] = (soldier_status_ == SoldiersStatus::kBadShortage ||
+				             soldier_status_ == SoldiersStatus::kShortage) ?
+				               -2 :
+				               0;
+				inputs[8] = (soldier_status_ == SoldiersStatus::kBadShortage) ? -2 : 0;
+				inputs[9] = (soldier_status_ == SoldiersStatus::kBadShortage ||
+				             soldier_status_ == SoldiersStatus::kShortage) ?
+				               -3 :
+				               0;
+				inputs[10] = (site->second.mines_nearby == ExtendedBool::kTrue) ? 3 : -1;
+				inputs[11] = (site->second.mines_nearby == ExtendedBool::kTrue) ? 0 : 0;
+				inputs[12] = (owner_number == persistent_data->last_attacked_player) ? 2 : -2;
+				inputs[13] = (owner_number == persistent_data->last_attacked_player) ? 4 : -4;
+				inputs[14] = (strong_enough) ? 1 : -1;
+				inputs[15] = (strong_enough) ? 3 : -3;
+				inputs[16] = (player_statistics.get_player_power(pn) >
+				              player_statistics.get_old_player_power(pn)) ?
+				                2 :
+				                -2;
+				inputs[17] = (player_statistics.get_player_power(pn) >
+				              player_statistics.get_old60_player_power(pn)) ?
+				                3 :
+				                -3;
+				inputs[18] = (player_statistics.get_visible_enemies_power(pn) >
+				              player_statistics.get_old_visible_enemies_power(pn)) ?
+				                -1 :
+				                1;
+				inputs[19] = (player_statistics.get_visible_enemies_power(pn) >
+				              player_statistics.get_old_visible_enemies_power(pn)) ?
+				                -3 :
+				                3;
+				inputs[20] = (player_statistics.get_player_power(owner_number) >
+				              player_statistics.get_old_player_power(owner_number)) ?
+				                -2 :
+				                2;
+				inputs[21] = (player_statistics.get_player_power(owner_number) >
+				              player_statistics.get_old_player_power(owner_number)) ?
+				                -1 :
+				                1;
+				inputs[22] = (my_to_enemy_power_ratio > 80) ? 2 : -2;
+				inputs[23] = (my_to_enemy_power_ratio > 90) ? 2 : -2;
+				inputs[24] = (my_to_enemy_power_ratio > 110) ? 2 : -2;
+				inputs[55] = (my_to_enemy_power_ratio > 120) ? 2 : -2;
+				inputs[26] = management_data.get_military_number_at(62) / 10;
+				inputs[27] = (ts_finished_count_ - ts_without_trainers_) * 2;
+				inputs[28] = general_score * 3;
+				inputs[29] = general_score;
+				inputs[30] = ((mines_per_type[iron_ore_id].in_construction +
+				               mines_per_type[iron_ore_id].finished) > 0) ?
+				                1 :
+				                -1;
+				inputs[31] = (player_statistics.get_player_power(pn) >
+				              player_statistics.get_old60_player_power(pn) + 5) ?
+				                2 :
+				                -2;
+				inputs[32] = soldier_trained_log.count(gametime);
+				inputs[33] = soldier_trained_log.count(gametime) / 2;
+				inputs[34] = general_score * 2;
+				inputs[35] = -1;
+				inputs[36] = (gametime < 15 * 60 * 1000) ? -1 : 0;
+				inputs[37] = (gametime < 20 * 60 * 1000) ? -1 : 0;
+				inputs[38] = (gametime < 25 * 60 * 1000) ? -1 : 0;
+				inputs[39] = (gametime < 30 * 60 * 1000) ? -1 : 0;
+				inputs[40] = (gametime < 35 * 60 * 1000) ? -1 : 0;
+				inputs[41] = (gametime < 40 * 60 * 1000) ? -1 : 0;
+				inputs[42] = (site->second.last_time_attacked + 1 * 60 * 1000 > gametime) ? -3 : 0;
+				inputs[43] = (site->second.last_time_attacked + 30 * 1000 > gametime) ? -1 : 0;
+				inputs[44] = (site->second.last_time_attacked + 2 * 60 * 1000 > gametime) ? -2 : 0;
+				inputs[45] = (site->second.last_time_attacked + 40 * 1000 > gametime) ? -1 : 0;
+				inputs[46] = (site->second.last_time_attacked + 3 * 60 * 1000 > gametime) ? -1 : 0;
+				inputs[47] = (site->second.last_time_attacked + 30 * 1000 > gametime) ? -1 : 0;
+				inputs[48] = (site->second.last_time_attacked + 90 * 1000 > gametime) ? -1 : 0;
+				inputs[49] = (site->second.last_time_attacked + 2 * 60 * 1000 > gametime) ? -1 : 0;
+				inputs[50] = soldier_trained_log.count(gametime);
+				inputs[51] = soldier_trained_log.count(gametime) / 2;
+				inputs[52] = (my_to_enemy_power_ratio - 100) / 50;
+				inputs[53] = (my_to_enemy_power_ratio > 60) ? 0 : -4;
+				inputs[54] = (my_to_enemy_power_ratio > 70) ? 0 : -3;
+				inputs[55] = (my_to_enemy_power_ratio > 80) ? 2 : -2;
+				inputs[56] = (my_to_enemy_power_ratio > 90) ? 2 : -2;
+				inputs[57] = (my_to_enemy_power_ratio > 100) ? 2 : -2;
+				inputs[58] = (my_to_enemy_power_ratio > 110) ? 2 : -2;
+				inputs[59] = (my_to_enemy_power_ratio > 120) ? 2 : -2;
+				inputs[60] = (my_to_enemy_power_ratio > 130) ? 2 : -2;
+				inputs[61] = (my_to_enemy_power_ratio > 140) ? 3 : 0;
+				inputs[62] = (my_to_enemy_power_ratio > 150) ? 4 : 0;
+				inputs[63] = (my_to_enemy_power_ratio - 100) / 50;
+				inputs[64] = (enemys_power_growth > 105) ? -1 : 0;
+				inputs[65] = (enemys_power_growth > 110) ? -2 : 0;
+				inputs[66] = (enemys_power_growth > 115) ? -1 : 0;
+				inputs[67] = (enemys_power_growth > 120) ? -2 : 0;
+				inputs[68] = (enemys_power_growth < 95) ? 1 : 0;
+				inputs[69] = (enemys_power_growth < 90) ? 2 : 0;
+				inputs[70] = (enemys_power_growth < 85) ? 1 : 0;
+				inputs[71] = (enemys_power_growth < 80) ? 2 : 0;
+				inputs[72] = (own_power_growth > 105) ? 1 : 0;
+				inputs[73] = (own_power_growth > 110) ? 2 : 0;
+				inputs[74] = (own_power_growth > 115) ? 1 : 0;
+				inputs[75] = (own_power_growth > 120) ? 2 : 0;
+				inputs[76] = (own_power_growth < 95) ? -1 : 0;
+				inputs[77] = (own_power_growth < 90) ? -2 : 0;
+				inputs[77] = (own_power_growth < 85) ? -1 : 0;
+				inputs[78] = (own_power_growth < 80) ? -2 : 0;
+				inputs[79] = ((gametime - last_attack_time_) < kCampaignDuration) ? +2 : -2;
+				inputs[80] = -1;
+				inputs[81] = +1;
+				inputs[82] = -1;
+				inputs[83] = (soldier_status_ == SoldiersStatus::kBadShortage ||
+				              soldier_status_ == SoldiersStatus::kShortage) ?
+				                -3 :
+				                1;
+				inputs[84] = (soldier_status_ == SoldiersStatus::kBadShortage ||
+				              soldier_status_ == SoldiersStatus::kShortage) ?
+				                -4 :
+				                1;
+				inputs[85] = (soldier_status_ == SoldiersStatus::kBadShortage) ? -2 : 1;
+				inputs[86] = (soldier_status_ == SoldiersStatus::kBadShortage) ? -4 : 1;
+				inputs[87] = (soldier_status_ == SoldiersStatus::kBadShortage ||
+				              soldier_status_ == SoldiersStatus::kShortage) ?
+				                -2 :
+				                1;
+				inputs[88] = (site->second.attack_soldiers_strength < 2) ? -3 : 0;
+				inputs[89] = (site->second.attack_soldiers_strength < 4) ? -2 : 0;
+				inputs[90] = (site->second.attack_soldiers_strength < 5) ? -3 : 0;
+				inputs[90] = (site->second.attack_soldiers_strength < 7) ? -3 : 0;
+				inputs[91] = (site->second.attack_soldiers_competency < 15) ? -4 : 0;
+				inputs[92] = (site->second.attack_soldiers_competency < 20) ? -2 : 0;
+				inputs[93] = ((gametime - last_attack_time_) < kCampaignDuration) ? +2 : -2;
+				inputs[94] = ((gametime - last_attack_time_) < kCampaignDuration) ? +2 : -2;
+				inputs[95] = -player_statistics.enemies_seen_lately_count(gametime);
+
 				site->second.score = 0;
-			}  // or the score will remain 0
+				for (uint8_t j = 0; j < f_neuron_bit_size; j += 1) {
+					if (management_data.f_neuron_pool[47].get_position(j)) {
+						site->second.score += inputs[j];
+						if (inputs[j] < -10 || inputs[j] > 10) {
+							log(" pos: %d - value %d\n", j, inputs[j]);
+						}
+					}
+					if (management_data.f_neuron_pool[0].get_position(j)) {
+						site->second.score += inputs[j + f_neuron_bit_size];
+						if (inputs[j + f_neuron_bit_size] < -10 || inputs[j + f_neuron_bit_size] > 10) {
+							log(" pos: %d - value %d\n", j + f_neuron_bit_size,
+							    inputs[j + f_neuron_bit_size]);
+						}
+					}
+					if (management_data.f_neuron_pool[16].get_position(j)) {
+						site->second.score += inputs[j + 2 * f_neuron_bit_size];
+						if (inputs[j + 2 * f_neuron_bit_size] < -10 ||
+						    inputs[j + 2 * f_neuron_bit_size] > 10) {
+							log(" pos: %d - value %d\n", j + 2 * f_neuron_bit_size,
+							    inputs[j + 2 * f_neuron_bit_size]);
+						}
+					}
+				}
+			}
+			site->second.score += management_data.get_military_number_at(138) / 4;
 
 			if (site->second.score > 0) {
+				assert(is_visible);
 				if (site->second.score > best_score) {
 					best_score = site->second.score;
 					best_target = site->first;
 				}
 			}
 
-			if (site->second.attack_soldiers_strength > 0) {
-				site->second.last_time_attackable = gametime;
-			}
-			if (site->second.last_time_attackable + 20 * 60 * 1000 < gametime) {
-				site_to_be_removed = site->first;
-			}
-		} else {  // we dont have a flag, let remove the site from out observer list
-			site_to_be_removed = site->first;
-		}
-
-		if (site_to_be_removed < std::numeric_limits<uint32_t>::max()) {
-			disappeared_sites.push_back(site_to_be_removed);
+		} else {  // we dont have a flag = site does not exists anymore, let remove the site from out
+		          // observer list
+			disappeared_sites.push_back(site->first);
 		}
 	}
 
@@ -372,10 +446,10 @@
 
 	// modifying enemysites_check_delay_,this depends on the count
 	// of enemysites in observer
-	if (count >= 13 && enemysites_check_delay_ < 180) {
+	if (enemy_sites.size() >= 13 && enemysites_check_delay_ < 180) {
 		enemysites_check_delay_ += 3;
 	}
-	if (count < 10 && enemysites_check_delay_ > 45) {
+	if (enemy_sites.size() < 10 && enemysites_check_delay_ > 30) {
 		enemysites_check_delay_ -= 2;
 	}
 
@@ -388,10 +462,6 @@
 
 	// attacking
 	FCoords f = map.get_fcoords(Coords::unhash(best_target));
-	// setting no attack counter here
-	// this gauranties that it will not be attacked in next 3
-	// turns
-	enemy_sites[best_target].no_attack_counter = -3;
 
 	Flag* flag = nullptr;  // flag of a building to be attacked
 	if (upcast(MilitarySite, bld, f.field->get_immovable())) {
@@ -403,44 +473,81 @@
 	}
 
 	// how many attack soldiers we can send?
-	uint32_t attackers = player_->find_attack_soldiers(*flag);
-
-	// Of course not all of them:
-	// reduce by 0-3 for attackers below 10
-	// but for soldiers in range 10-40 reduce by much more.
-	// Soldiers above 40 are ignored for calculation
-
-	// Number of soldiers in the range 10-40, random portion of
-	// them will be used
-	uint32_t above_ten = (attackers > 10) ? attackers - 10 : 0;
-	above_ten = (above_ten > 30) ? 30 : above_ten;
-
-	attackers = attackers - (gametime % 3) - ((above_ten > 0) ? gametime % above_ten : 0);
+	int32_t attackers = player_->find_attack_soldiers(*flag);
+	assert(attackers < 500);
+
+	if (attackers > 5) {
+		attackers = 5 + std::rand() % (attackers - 5);
+	}
+
+	assert(attackers < 500);
 
 	if (attackers <= 0) {
 		return false;
 	}
 
-	game().send_player_enemyflagaction(*flag, player_number(), attackers);
+	log("%2d: attacking site at %3dx%3d, score %3d, with %2d soldiers, attacking %2d times, after "
+	    "%5d seconds\n",
+	    player_number(), flag->get_position().x, flag->get_position().y, best_score, attackers,
+	    enemy_sites[best_target].attack_counter + 1,
+	    (gametime - enemy_sites[best_target].last_time_attacked) / 1000);
+	game().send_player_enemyflagaction(*flag, player_number(), static_cast<uint16_t>(attackers));
+	assert(1 <
+	       player_->vision(Map::get_index(flag->get_building()->get_position(), map.get_width())));
+	attackers_count_ += attackers;
+	enemy_sites[best_target].last_time_attacked = gametime;
+	enemy_sites[best_target].attack_counter += 1;
 
 	last_attack_time_ = gametime;
+	for (int j = 0; j < attackers; j += 1) {
+		soldier_attacks_log.push(gametime);
+	}
 	persistent_data->last_attacked_player = flag->owner().player_number();
 
 	return true;
 }
-
 // this just counts free positions in military and training sites
 void DefaultAI::count_military_vacant_positions() {
 	// counting vacant positions
-	vacant_mil_positions_ = 0;
+	int32_t vacant_mil_positions_ = 0;
+	int32_t understaffed_ = 0;
+	int32_t on_stock_ = 0;
 	for (TrainingSiteObserver tso : trainingsites) {
 		vacant_mil_positions_ +=
-		   10 * (tso.site->soldier_capacity() - tso.site->stationed_soldiers().size());
-		vacant_mil_positions_ += (tso.site->can_start_working()) ? 0 : 10;
+		   5 * std::min<int32_t>(
+		          (tso.site->soldier_capacity() - tso.site->stationed_soldiers().size()), 2);
 	}
 	for (MilitarySiteObserver mso : militarysites) {
 		vacant_mil_positions_ += mso.site->soldier_capacity() - mso.site->stationed_soldiers().size();
-	}
+		understaffed_ += mso.understaffed;
+	}
+	vacant_mil_positions_ += understaffed_;
+
+	// also available in warehouses
+	for (auto wh : warehousesites) {
+		on_stock_ += wh.site->stationed_soldiers().size();
+	}
+
+	vacant_mil_positions_ += on_stock_;
+
+	// to avoid floats this is actual number * 100
+	vacant_mil_positions_average_ =
+	   vacant_mil_positions_average_ * 8 / 10 + 20 * vacant_mil_positions_;
+
+	if (vacant_mil_positions_ <= 1 || on_stock_ > 4) {
+		soldier_status_ = SoldiersStatus::kFull;
+	} else if (vacant_mil_positions_ * 4 <= static_cast<int32_t>(militarysites.size()) ||
+	           on_stock_ > 2) {
+		soldier_status_ = SoldiersStatus::kEnough;
+	} else if (vacant_mil_positions_ > static_cast<int32_t>(militarysites.size())) {
+		soldier_status_ = SoldiersStatus::kBadShortage;
+	} else {
+		soldier_status_ = SoldiersStatus::kShortage;
+	}
+
+	assert(soldier_status_ == SoldiersStatus::kFull || soldier_status_ == SoldiersStatus::kEnough ||
+	       soldier_status_ == SoldiersStatus::kShortage ||
+	       soldier_status_ == SoldiersStatus::kBadShortage);
 }
 
 // this function only check with trainingsites
@@ -462,7 +569,7 @@
 	const DescriptionIndex enhancement = ts->descr().enhancement();
 
 	if (enhancement != INVALID_INDEX && ts_without_trainers_ == 0 && mines_.size() > 3 &&
-	    (ts_basic_const_count_ + ts_advanced_const_count_) == 0 && ts_advanced_count_ > 0) {
+	    ts_finished_count_ > 1 && ts_in_const_count_ == 0) {
 
 		if (player_->is_building_type_allowed(enhancement)) {
 			game().send_player_enhance_building(*tso.site, enhancement);
@@ -478,7 +585,6 @@
 	// reducing ware queues
 	// - for armours and weapons to 1
 	// - for others to 6
-	// - for others to 6
 	for (InputQueue* queue : tso.site->inputqueues()) {
 
 		if (queue->get_type() != wwWARE) {
@@ -503,12 +609,9 @@
 		}
 	}
 
-	// changing priority if basic
-	if (tso.bo->trainingsite_type == TrainingSiteType::kBasic) {
-		for (uint32_t k = 0; k < tso.bo->inputs.size(); ++k) {
-			game().send_player_set_ware_priority(*ts, wwWARE, tso.bo->inputs.at(k), HIGH_PRIORITY);
-		}
-	}
+	// are we willing to train another soldier
+	// bool want_train = true;
+	const PlayerNumber pn = player_number();
 
 	// if soldier capacity is set to 0, we need to find out if the site is
 	// supplied enough to incrase the capacity to 1
@@ -547,20 +650,100 @@
 			}
 		}
 
-		// Has any soldier been trained up to now?
-		if (gametime > 10 * 60 * 1000 && persistent_data->last_soldier_trained < gametime) {
-			if (persistent_data->last_soldier_trained + 10 * 60 * 1000 < gametime) {
-				if (shortage <= 3) {
-					game().send_player_change_soldier_capacity(*ts, 1);
-				}
-			} else {
-				if (shortage <= 1) {
-					game().send_player_change_soldier_capacity(*ts, 1);
-				}
-			}
-			// or this can be very first soldier to be trained
-		} else if (shortage <= 3) {
-			game().send_player_change_soldier_capacity(*ts, 1);
+		if (shortage <= 3) {  // training only if supplied
+
+			int16_t inputs[f_neuron_bit_size] = {0};
+			inputs[0] = -shortage;
+			inputs[1] = (player_statistics.get_visible_enemies_power(gametime) >
+			             player_statistics.get_old_visible_enemies_power(gametime)) ?
+			               1 :
+			               0;
+			inputs[2] = (mines_.size() < 3) ? -1 : 0;
+			inputs[3] = (mines_per_type[iron_ore_id].total_count() == 0) ? -1 : 0;
+			inputs[4] = (player_statistics.get_player_power(pn) * 2 >
+			             player_statistics.get_visible_enemies_power(gametime)) ?
+			               -1 :
+			               0;
+			inputs[5] = (player_statistics.get_player_power(pn) * 2 >
+			             player_statistics.get_enemies_average_power()) ?
+			               -1 :
+			               0;
+			inputs[6] = (player_statistics.get_player_power(pn) >
+			             player_statistics.get_visible_enemies_power(gametime)) ?
+			               -1 :
+			               1;
+			inputs[7] = (player_statistics.get_player_power(pn) >
+			             player_statistics.get_enemies_average_power()) ?
+			               -1 :
+			               1;
+			inputs[8] = (player_statistics.get_player_power(pn) * 2 >
+			             player_statistics.get_visible_enemies_power(gametime)) ?
+			               -1 :
+			               1;
+			inputs[9] = (player_statistics.get_player_power(pn) * 2 >
+			             player_statistics.get_enemies_average_power()) ?
+			               -1 :
+			               1;
+			inputs[10] = (3 - shortage) * 1;
+			inputs[11] = (3 - shortage) * 2;
+			inputs[12] = +1;
+			inputs[13] = +2;
+			inputs[14] = -1;
+			inputs[15] = -2;
+			inputs[16] = (player_statistics.get_player_power(pn) <
+			              player_statistics.get_old60_player_power(pn)) ?
+			                1 :
+			                0;
+			inputs[17] =
+			   (player_statistics.get_player_power(pn) < player_statistics.get_old_player_power(pn)) ?
+			      1 :
+			      0;
+			inputs[18] = (player_statistics.get_player_power(pn) <
+			              player_statistics.get_old60_player_power(pn) + 4) ?
+			                1 :
+			                0;
+			inputs[19] = (player_statistics.get_player_power(pn) <
+			              player_statistics.get_old_player_power(pn) + 1) ?
+			                1 :
+			                0;
+			inputs[20] = (player_statistics.get_player_power(pn) <
+			              player_statistics.get_old60_player_power(pn) + 4) ?
+			                0 :
+			                1;
+			inputs[21] = (player_statistics.get_player_power(pn) <
+			              player_statistics.get_old_player_power(pn) + 2) ?
+			                0 :
+			                1;
+
+			if (player_statistics.any_enemy_seen_lately(gametime)) {
+				inputs[20] = (player_statistics.get_player_power(pn) <
+				              player_statistics.get_old60_player_power(pn) + 4) ?
+				                0 :
+				                1;
+				inputs[21] = (player_statistics.get_player_power(pn) <
+				              player_statistics.get_old_player_power(pn) + 2) ?
+				                0 :
+				                1;
+				inputs[22] = (player_statistics.get_player_power(pn) <
+				              player_statistics.get_old60_player_power(pn) + 4) ?
+				                1 :
+				                0;
+				inputs[23] = (player_statistics.get_player_power(pn) <
+				              player_statistics.get_old_player_power(pn) + 2) ?
+				                1 :
+				                0;
+			}
+
+			int16_t tmp_score = 0;
+			for (uint8_t i = 0; i < f_neuron_bit_size; i += 1) {
+				if (management_data.f_neuron_pool[29].get_position(i)) {
+					tmp_score += inputs[i];
+				}
+			}
+
+			if (tmp_score > 0) {
+				game().send_player_change_soldier_capacity(*ts, 1);
+			}
 		}
 	}
 
@@ -594,102 +777,98 @@
 	bool changed = false;
 	Map& map = game().map();
 	MilitarySite* ms = militarysites.front().site;
-	MilitarySiteObserver& mso = militarysites.front();
-	uint32_t const vision = ms->descr().vision_range();
+
+	// Dont do anything if last change took place lately
+	if (militarysites.front().last_change + 2 * 60 * 1000 > gametime) {
+		militarysites.push_back(militarysites.front());
+		militarysites.pop_front();
+		return false;
+	}
+
 	FCoords f = map.get_fcoords(ms->get_position());
-	// look if there are any enemies building
-	FindNodeEnemiesBuilding find_enemy(player_, game());
-
-	// first we make sure there is no enemy at all
-	if (map.find_fields(Area<FCoords>(f, vision + 4), nullptr, find_enemy) == 0) {
-
-		mso.enemies_nearby = false;
-
-		// If no enemy in sight - decrease the number of stationed soldiers
-		// as long as it is > 1 - BUT take care that there is a warehouse in the
-		// same economy where the thrown out soldiers can go to.
-
-		if (ms->economy().warehouses().size()) {
-			uint32_t const j = ms->soldier_capacity();
-
-			if (MilitarySite::kPrefersRookies != ms->get_soldier_preference()) {
-				game().send_player_militarysite_set_soldier_preference(
-				   *ms, MilitarySite::kPrefersRookies);
-			} else if (j > 1) {
-				game().send_player_change_soldier_capacity(*ms, (j > 2) ? -2 : -1);
-			}
-			// if the building is in inner land and other militarysites still
-			// hold the miliary influence of the field, consider to destruct the
-			// building to free some building space.
-			else {
-				// treat this field like a buildable and write military info to it.
-				BuildableField bf(f);
-				update_buildable_field(bf, vision, true);
-				const int32_t size_penalty = ms->get_size() - 1;
-				FindNodeAllyOwned find_ally(player_, game(), player_number());
-				const int32_t allyOwnedFields =
-				   map.find_fields(Area<FCoords>(f, vision), nullptr, find_ally);
-
-				int16_t score = 0;
-				score += (bf.area_military_capacity > 6);
-				score += (bf.area_military_capacity > 22);
-				score += (bf.area_military_presence > 4);
-				score += (bf.military_loneliness <
-				          (180 + persistent_data->ai_personality_military_loneliness));
-				score += (bf.military_stationed > 2);
-				score -= size_penalty;
-				score += ((bf.unowned_land_nearby + allyOwnedFields) < 10);
-				score -= (mso.built_time + 10 * 60 * 1000 > gametime);
-
-				if (score >= 4) {
-					if (ms->get_playercaps() & Widelands::Building::PCap_Dismantle) {
-						flags_to_be_removed.push_back(ms->base_flag().get_position());
-						game().send_player_dismantle(*ms);
-						military_last_dismantle_ = game().get_gametime();
-					} else {
-						game().send_player_bulldoze(*ms);
-						military_last_dismantle_ = game().get_gametime();
-					}
-				}
-			}
-		}
-	} else {
-
-		uint32_t unused1 = 0;
-		uint16_t unused2 = 0;
-
-		mso.enemies_nearby = false;
-
-		// yes enemy is nearby, but still we must distinguish whether
-		// he is accessible (over the land)
-		if (other_player_accessible(
-		       vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kEnemy)) {
-
-			Quantity const total_capacity = ms->max_soldier_capacity();
-			Quantity const target_capacity = ms->soldier_capacity();
-
-			game().send_player_change_soldier_capacity(*ms, total_capacity - target_capacity);
-			changed = true;
-
-			// and also set preference to Heroes
-			if (MilitarySite::kPrefersHeroes != ms->get_soldier_preference()) {
-				game().send_player_militarysite_set_soldier_preference(
-				   *ms, MilitarySite::kPrefersHeroes);
-				changed = true;
-			}
-
-			mso.enemies_nearby = true;
-			enemy_last_seen_ = gametime;
-		} else {  // otherwise decrease soldiers
-			uint32_t const j = ms->soldier_capacity();
-
-			if (MilitarySite::kPrefersRookies != ms->get_soldier_preference()) {
-				game().send_player_militarysite_set_soldier_preference(
-				   *ms, MilitarySite::kPrefersRookies);
-			} else if (j > 1) {
-				game().send_player_change_soldier_capacity(*ms, (j > 2) ? -2 : -1);
-			}
-		}
+
+	BuildableField bf(f);
+	update_buildable_field(bf);
+
+	Quantity const total_capacity = ms->max_soldier_capacity();
+	Quantity const current_target = ms->soldier_capacity();
+	Quantity const current_soldiers = ms->present_soldiers().size();
+	Quantity target_occupancy = total_capacity;
+	if (soldier_status_ == SoldiersStatus::kBadShortage) {
+		target_occupancy = total_capacity / 3 + 1;
+	} else if (soldier_status_ == SoldiersStatus::kShortage) {
+		target_occupancy = total_capacity * 2 / 3 + 1;
+	}
+
+	militarysites.front().understaffed = 0;
+
+	const bool can_be_dismantled =
+	   (current_soldiers == 1 || militarysites.front().built_time + 10 * 60 * 1000 < gametime) &&
+	   bf.military_loneliness < 1000 - 2 * std::abs(management_data.get_military_number_at(14));
+
+	bool should_be_dismantled = false;
+	const int32_t enemy_military_capacity = std::max<int32_t>(
+	   {bf.enemy_military_presence,
+	    bf.enemy_military_sites * (1 + std::abs(management_data.get_military_number_at(77) / 20)),
+	    (bf.enemy_owned_land_nearby) ?
+	       4 + std::abs(management_data.get_military_number_at(99) / 20) :
+	       0});
+	if (bf.enemy_owned_land_nearby) {
+		if (bf.military_score_ < std::abs(management_data.get_military_number_at(91) * 10) &&
+		    bf.area_military_capacity - static_cast<int16_t>(total_capacity) -
+		          std::abs(management_data.get_military_number_at(84) / 10) >
+		       enemy_military_capacity) {
+			should_be_dismantled = true;
+		}
+	} else {
+		const uint16_t size_bonus =
+		   total_capacity * std::abs(management_data.get_military_number_at(89)) / 5;
+		if (bf.military_score_ + size_bonus < management_data.get_military_number_at(88) * 5 &&
+		    bf.area_military_capacity > static_cast<int16_t>(total_capacity)) {
+			should_be_dismantled = true;
+		}
+	}
+
+	if (bf.enemy_accessible_ && !should_be_dismantled) {
+
+		assert(total_capacity >= target_occupancy);
+
+		militarysites.front().understaffed = total_capacity - target_occupancy;
+
+		if (current_target < target_occupancy) {
+			game().send_player_change_soldier_capacity(*ms, 1);
+			changed = true;
+		}
+		if (current_target > target_occupancy) {
+			game().send_player_change_soldier_capacity(*ms, -1);
+			changed = true;
+		}
+		if (ms->get_soldier_preference() == MilitarySite::kPrefersRookies) {
+			game().send_player_militarysite_set_soldier_preference(*ms, MilitarySite::kPrefersHeroes);
+			changed = true;
+		}
+	} else if (should_be_dismantled && can_be_dismantled) {
+		changed = true;
+		if (ms->get_playercaps() & Widelands::Building::PCap_Dismantle) {
+			flags_to_be_removed.push_back(ms->base_flag().get_position());
+			game().send_player_dismantle(*ms);
+			military_last_dismantle_ = game().get_gametime();
+		} else {
+			game().send_player_bulldoze(*ms);
+			military_last_dismantle_ = game().get_gametime();
+		}
+	} else {
+		if (current_target > 1) {  // reduce number of soldiers here at least....
+			game().send_player_change_soldier_capacity(*ms, -1);
+			changed = true;
+		}
+		if (ms->get_soldier_preference() == MilitarySite::kPrefersHeroes) {
+			game().send_player_militarysite_set_soldier_preference(*ms, MilitarySite::kPrefersRookies);
+			changed = true;
+		}
+	}
+	if (changed) {
+		militarysites.front().last_change = gametime;
 	}
 
 	// reorder:;
@@ -698,6 +877,16 @@
 	return changed;
 }
 
+uint32_t DefaultAI::barracks_count() {
+	uint32_t count = 0;
+	for (auto ps : productionsites) {
+		if (ps.bo->is(BuildingAttribute::kBarracks)) {
+			count += ps.bo->total_count();
+		}
+	}
+	return count;
+}
+
 // This calculates strength of vector of soldiers, f.e. soldiers in a building or
 // ones ready to attack
 int32_t DefaultAI::calculate_strength(const std::vector<Widelands::Soldier*>& soldiers) {
@@ -714,8 +903,8 @@
 
 	for (Soldier* soldier : soldiers) {
 		const SoldierDescr& descr = soldier->descr();
-		health =
-		   descr.get_base_health() + descr.get_health_incr_per_level() * soldier->get_health_level();
+		health = soldier->get_current_health();
+		// descr.get_base_health() + descr.get_health_incr_per_level() * soldier->get_health_level();
 		attack = (descr.get_base_max_attack() - descr.get_base_min_attack()) / 2.f +
 		         descr.get_base_min_attack() +
 		         descr.get_attack_incr_per_level() * soldier->get_attack_level();
@@ -724,7 +913,8 @@
 		        descr.get_evade_incr_per_level() / 100.f * soldier->get_evade_level();
 		final += (attack * health) / (defense * evade);
 	}
-
+	assert(final >= 0);
+	assert(final <= 10000 * soldiers.size());
 	// 2500 is aproximate strength of one unpromoted soldier
 	return static_cast<int32_t>(final / 2500);
 }
@@ -734,45 +924,311 @@
 // We count bigger buildings, medium ones get 1 points, big ones 2 points
 // and we force some proportion to the number of military sites
 // sidenote: function can return kNotNeeded, but it means 'not allowed'
-BuildingNecessity DefaultAI::check_building_necessity(const uint8_t size, const uint32_t gametime) {
+BuildingNecessity DefaultAI::check_building_necessity(BuildingObserver& bo,
+                                                      const uint32_t gametime) {
 
 	assert(militarysites.size() == msites_built());
+
+	const PlayerNumber pn = player_number();
+
 	// logically size of militarysite must in between 1 and 3 (including)
+	const uint8_t size = bo.desc->get_size();
 	assert(size >= BaseImmovable::SMALL && size <= BaseImmovable::BIG);
 
-	if (size == BaseImmovable::SMALL) {  // this function is intended for medium and bigger sites
-		return BuildingNecessity::kAllowed;
+	if (military_last_build_ >
+	    gametime - (10 + std::abs(management_data.get_military_number_at(43)) * 1000 / 2)) {
+		return BuildingNecessity::kForbidden;
 	}
 
-	uint32_t const big_buildings_score =
-	   msites_per_size[2].in_construction + msites_per_size[2].finished +
-	   msites_per_size[3].in_construction * 2 + msites_per_size[3].finished * 2;
+	bo.primary_priority = 0;
 
 	const uint32_t msites_total = msites_built() + msites_in_constr();
-
-	// this is final proportion of big_buildings_score / msites_total
-	// two exeptions:
-	// if enemy nearby - can be higher
-	// for early game - must be lower
-	uint32_t limit = (msites_built() + msites_in_constr()) * 2 / 3;
-
-	// exemption first
-	if (militarysites.size() > 3 && vacant_mil_positions_ == 0 && msites_in_constr() == 0) {
-		return BuildingNecessity::kAllowed;  // it seems the expansion is stuck so we allow big
-		                                     // buildings
-	} else if (gametime > enemy_last_seen_ && gametime < enemy_last_seen_ + 30 * 60 * 1000 &&
-	           mines_.size() > 2) {  // if enemies were nearby in last 30 minutes
-		// we allow more big buidings
-		limit *= 2;
-	} else if (msites_total < persistent_data->ai_personality_early_militarysites) {
-		// for the beginning of the game (first 30 military sites)
-		limit = limit * msites_total / persistent_data->ai_personality_early_militarysites;
-	}
-
-	if (big_buildings_score + size - 1 > limit) {
-		return BuildingNecessity::kNotNeeded;
+	const uint16_t scores[3] = {
+	   static_cast<uint16_t>(msites_per_size[1].in_construction + msites_per_size[1].finished),
+	   static_cast<uint16_t>((msites_per_size[2].in_construction + msites_per_size[2].finished) * 2),
+	   static_cast<uint16_t>((msites_per_size[3].in_construction + msites_per_size[3].finished) *
+	                         3)};
+	const uint16_t total_score = scores[0] + scores[1] + scores[2];
+
+	int32_t inputs[4 * f_neuron_bit_size] = {0};
+	inputs[0] = (msites_total < 1) ? 1 : 0;
+	inputs[1] = (msites_total < 2) ? 1 : 0;
+	inputs[2] = (msites_total < 3) ? 1 : 0;
+	inputs[3] = (msites_total < 4) ? 1 : 0;
+	inputs[3] = (msites_total < 5) ? 1 : 0;
+	inputs[4] = (msites_in_constr() > msites_built()) ? -1 : 0;
+	inputs[5] = -3;
+	inputs[6] = (msites_in_constr() > msites_built() / 2) ? -1 : 0;
+	inputs[7] = (msites_in_constr() > msites_built() / 3) ? -1 : 0;
+	inputs[8] = (soldier_status_ == SoldiersStatus::kBadShortage) ? -2 : 0;
+	inputs[9] = (soldier_status_ == SoldiersStatus::kShortage) ? -1 : 0;
+	inputs[10] = (scores[size - 1] > total_score) ? -2 : 0;
+	inputs[11] = (scores[size - 1] > total_score / 2) ? -2 : 0;
+	inputs[12] = (scores[size - 1] > total_score / 3) ? -2 : 0;
+	inputs[13] =
+	   (player_statistics.get_enemies_max_land() < player_statistics.get_player_land(pn)) ? -1 : 0;
+	inputs[14] = (mines_per_type[iron_ore_id].total_count() == 0) ? +1 : 0;
+	inputs[15] = (spots_ < kSpotsTooLittle) ? +1 : 0;
+	inputs[16] = +1;
+	inputs[17] = +2;
+	inputs[18] = -1;
+	inputs[19] = -2;
+	inputs[20] = (scores[size - 1] > total_score / 2) ? -1 : 0;
+	inputs[21] = (msites_in_constr() > msites_built() / 3) ? -1 : 0;
+	inputs[22] = (scores[size - 1] > total_score / 4) ? -1 : 0;
+	inputs[23] = (((3 - size) * msites_in_constr()) < 1) ? +1 : 0;
+	inputs[24] = (3 - size) * ((msites_in_constr() < 3) ? +1 : 0);
+	inputs[25] = (((3 - size) * msites_in_constr()) < 5) ? +1 : 0;
+	inputs[26] = (msites_in_constr() < 7) ? +1 : 0;
+	inputs[27] = +5;
+	inputs[28] = -5;
+	inputs[29] = +3;
+	inputs[30] = (soldier_status_ == SoldiersStatus::kBadShortage) ? -4 : 0;
+	inputs[31] = (soldier_status_ == SoldiersStatus::kShortage) ? -2 : 0;
+
+	inputs[32] = (soldier_status_ == SoldiersStatus::kBadShortage) ? -3 : 0;
+	inputs[33] = (soldier_status_ == SoldiersStatus::kShortage) ? -2 : 0;
+
+	inputs[34] =
+	   (player_statistics.get_player_land(pn) < player_statistics.get_enemies_max_land()) ? 1 : 0;
+	inputs[35] =
+	   (!player_statistics.any_enemy_seen_lately(gametime) &&
+	    (player_statistics.get_player_land(pn) < player_statistics.get_enemies_max_land())) ?
+	      2 :
+	      0;
+	inputs[36] =
+	   (!player_statistics.any_enemy_seen_lately(gametime)) &&
+	         (player_statistics.get_player_land(pn) < player_statistics.get_enemies_max_land() * 2) ?
+	      1 :
+	      0;
+	inputs[37] =
+	   (!player_statistics.any_enemy_seen_lately(gametime)) &&
+	         (player_statistics.get_player_land(pn) < player_statistics.get_enemies_max_land() / 2) ?
+	      1 :
+	      0;
+
+	inputs[38] = (!player_statistics.any_enemy_seen_lately(gametime)) &&
+	                   (player_statistics.get_player_land(pn) <
+	                    player_statistics.get_old_player_land(pn) * 105 / 100) ?
+	                2 :
+	                0;
+	inputs[39] = (!player_statistics.any_enemy_seen_lately(gametime)) &&
+	                   (player_statistics.get_player_land(pn) <
+	                    player_statistics.get_old_player_land(pn) + 110) ?
+	                3 :
+	                0;
+
+	inputs[40] =
+	   (player_statistics.get_player_power(pn) < player_statistics.get_old60_player_power(pn)) ? 1 :
+	                                                                                             0;
+	inputs[41] =
+	   (player_statistics.get_player_power(pn) > player_statistics.get_old60_player_power(pn)) ? 1 :
+	                                                                                             0;
+	inputs[42] = (!player_statistics.any_enemy_seen_lately(gametime)) &&
+	                   (player_statistics.get_player_power(pn) <
+	                    player_statistics.get_old60_player_power(pn)) ?
+	                1 :
+	                0;
+	inputs[43] = (!player_statistics.any_enemy_seen_lately(gametime)) &&
+	                   (player_statistics.get_player_power(pn) >
+	                    player_statistics.get_old60_player_power(pn)) ?
+	                1 :
+	                0;
+
+	inputs[44] =
+	   (player_statistics.get_player_land(pn) < player_statistics.get_enemies_average_land()) ? 1 :
+	                                                                                            0;
+	inputs[45] =
+	   (player_statistics.get_player_land(pn) > player_statistics.get_enemies_average_land()) ? 1 :
+	                                                                                            0;
+
+	inputs[46] =
+	   (!player_statistics.any_enemy_seen_lately(gametime)) &&
+	         (player_statistics.get_player_land(pn) < player_statistics.get_enemies_average_land()) ?
+	      2 :
+	      0;
+	inputs[47] =
+	   (!player_statistics.any_enemy_seen_lately(gametime)) &&
+	         (player_statistics.get_player_land(pn) > player_statistics.get_enemies_average_land()) ?
+	      2 :
+	      0;
+
+	inputs[48] = (soldier_status_ == SoldiersStatus::kBadShortage) ? -3 : 0;
+	inputs[49] = (soldier_status_ == SoldiersStatus::kShortage) ? -2 : 0;
+
+	inputs[50] = (!player_statistics.any_enemy_seen_lately(gametime)) &&
+	                   (player_statistics.get_player_land(pn) <
+	                    player_statistics.get_old_player_land(pn) * 110 / 100) ?
+	                1 :
+	                0;
+	inputs[51] = (!player_statistics.any_enemy_seen_lately(gametime)) &&
+	                   (player_statistics.get_player_land(pn) <
+	                    player_statistics.get_old_player_land(pn) * 105 / 100) ?
+	                2 :
+	                0;
+
+	inputs[52] =
+	   (player_statistics.get_player_land(pn) < player_statistics.get_enemies_max_land()) ? 1 : 0;
+	inputs[53] =
+	   (player_statistics.get_player_land(pn) < player_statistics.get_enemies_max_land()) ? 3 : 0;
+	inputs[54] =
+	   (player_statistics.get_player_land(pn) < player_statistics.get_enemies_max_land() * 2) ? 2 :
+	                                                                                            0;
+	inputs[55] =
+	   (player_statistics.get_player_land(pn) < player_statistics.get_enemies_max_land() / 2) ? 2 :
+	                                                                                            0;
+	inputs[56] =
+	   !player_statistics.any_enemy_seen_lately(gametime) && (spots_ < kSpotsTooLittle) ? +2 : 0;
+	inputs[57] =
+	   player_statistics.any_enemy_seen_lately(gametime) && (spots_ < kSpotsTooLittle) ? +2 : 0;
+	inputs[58] =
+	   ((mines_per_type[iron_ore_id].in_construction + mines_per_type[iron_ore_id].finished) == 0) ?
+	      +3 :
+	      0;
+	inputs[59] =
+	   ((mines_per_type[iron_ore_id].in_construction + mines_per_type[iron_ore_id].finished) == 0) ?
+	      +1 :
+	      0;
+	inputs[60] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) ? -2 : 0;
+	inputs[61] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy ||
+	              expansion_type.get_expansion_type() == ExpansionMode::kBoth) ?
+	                -4 :
+	                0;
+	inputs[62] = (soldier_status_ == SoldiersStatus::kBadShortage) ? -3 : 0;
+	inputs[63] = (soldier_status_ == SoldiersStatus::kShortage) ? -2 : 0;
+
+	inputs[64] = (bo.build_material_shortage) ? -3 : 0;
+	inputs[65] = (bo.build_material_shortage) ? -1 : 0;
+	inputs[66] = (bo.build_material_shortage) ? -2 : 0;
+	inputs[67] = (bo.build_material_shortage) ? -8 : 0;
+	inputs[68] = (gametime < 15 * 60 * 1000) ? (size - 1) * -1 : 0;
+	inputs[69] = (gametime < 30 * 60 * 1000) ? (size - 1) * -1 : 0;
+	inputs[70] = (gametime < 45 * 60 * 1000) ? (size - 1) * -1 : 0;
+	inputs[71] = (gametime < 15 * 60 * 1000) ? (size - 1) * -2 : 0;
+	inputs[72] = (gametime < 30 * 60 * 1000) ? (size - 1) * -2 : 0;
+	inputs[73] = (gametime < 45 * 60 * 1000) ? (size - 1) * -2 : 0;
+	inputs[74] = (gametime < 15 * 60 * 1000) ? (size - 1) * -3 : 0;
+	inputs[75] = (gametime < 30 * 60 * 1000) ? (size - 1) * -3 : 0;
+	inputs[76] = (gametime < 45 * 60 * 1000) ? (size - 1) * -3 : 0;
+	inputs[77] =
+	   (player_statistics.get_player_power(pn) < player_statistics.get_old60_player_power(pn) + 2) ?
+	      1 :
+	      0;
+	inputs[78] =
+	   (player_statistics.get_player_power(pn) > player_statistics.get_old60_player_power(pn) + 5) ?
+	      1 :
+	      0;
+	inputs[79] =
+	   (player_statistics.get_player_power(pn) < player_statistics.get_old60_player_power(pn) + 10) ?
+	      1 :
+	      0;
+	inputs[80] =
+	   (player_statistics.get_player_power(pn) > player_statistics.get_old60_player_power(pn) + 20) ?
+	      1 :
+	      0;
+	if (!player_statistics.any_enemy_seen_lately(gametime)) {
+		inputs[81] = (player_statistics.get_player_land(pn) * 130 / 100 <
+		              player_statistics.get_old60_player_land(pn)) ?
+		                2 :
+		                0;
+		inputs[82] = (player_statistics.get_old60_player_land(pn) * 130 / 100 <
+		              player_statistics.get_player_land(pn)) ?
+		                0 :
+		                2;
 	} else {
+		inputs[83] = (player_statistics.get_player_land(pn) * 130 / 100 <
+		              player_statistics.get_old60_player_land(pn)) ?
+		                2 :
+		                0;
+		inputs[84] = (player_statistics.get_old60_player_land(pn) * 130 / 100 <
+		              player_statistics.get_player_land(pn)) ?
+		                0 :
+		                2;
+	}
+	inputs[85] = -1 * static_cast<int32_t>(msites_in_constr());
+	inputs[86] = -1 * static_cast<int32_t>(msites_in_constr()) / 2;
+	inputs[87] = -1 * static_cast<int32_t>(msites_in_constr()) / 3;
+	inputs[88] = (msites_in_constr() > 2 && msites_in_constr() > msites_built() / 2) ? -1 : 0;
+	inputs[89] = (msites_in_constr() > 2 && msites_in_constr() > msites_built() / 3) ? -1 : 0;
+	inputs[90] = (msites_in_constr() > 2 && msites_in_constr() > msites_built() / 4) ? -1 : 0;
+	inputs[91] = -static_cast<int32_t>(msites_in_constr()) / 4;
+	inputs[92] = (player_statistics.get_player_land(pn) <
+	              player_statistics.get_old_player_land(pn) * 130 / 100) ?
+	                2 :
+	                0;
+	inputs[93] = (player_statistics.get_player_land(pn) <
+	              player_statistics.get_old_player_land(pn) * 140 / 100) ?
+	                2 :
+	                0;
+	inputs[94] = (!player_statistics.any_enemy_seen_lately(gametime)) &&
+	                   (player_statistics.get_player_land(pn) <
+	                    player_statistics.get_old_player_land(pn) * 120 / 100) ?
+	                2 :
+	                0;
+	inputs[95] = (!player_statistics.any_enemy_seen_lately(gametime)) &&
+	                   (player_statistics.get_player_land(pn) <
+	                    player_statistics.get_old_player_land(pn) * 140 / 100) ?
+	                2 :
+	                0;
+	if (msites_built() > 5) {
+		inputs[96] = -1 * static_cast<int32_t>(msites_in_constr());
+		inputs[97] = -1 * static_cast<int32_t>(msites_in_constr()) / 2;
+		inputs[98] = -1 * static_cast<int32_t>(msites_in_constr()) / 3;
+		inputs[99] = (msites_in_constr() > msites_built() / 2) ? -2 : 0;
+		inputs[100] = (msites_in_constr() > msites_built() / 3) ? -2 : 0;
+		inputs[101] = (msites_in_constr() > msites_built() / 4) ? -2 : 0;
+	}
+	inputs[102] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) ? -4 : 0;
+	inputs[104] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy ||
+	               expansion_type.get_expansion_type() == ExpansionMode::kBoth) ?
+	                 -3 :
+	                 0;
+	inputs[105] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) ? -1 : 0;
+	inputs[106] = (wood_policy_ == WoodPolicy::kAllowRangers) ? -1 * (size - 1) : 0;
+	inputs[107] = (wood_policy_ == WoodPolicy::kAllowRangers) ? -3 * (size - 1) : 0;
+	inputs[108] = (wood_policy_ == WoodPolicy::kAllowRangers) ? -5 * size : 0;
+	inputs[109] = (wood_policy_ == WoodPolicy::kAllowRangers) ? -5 * (size - 1) : 0;
+	if (!bo.critical_building_material.empty() && buil_material_mines_count == 0) {
+		inputs[110] = -5;
+		inputs[111] = -2;
+		inputs[111] = -10;
+	}
+	if (bo.build_material_shortage) {
+		inputs[112] = -5;
+		inputs[113] = -2;
+		inputs[114] = -10;
+	}
+
+	for (int i = 0; i < 4 * f_neuron_bit_size; i = i + 1) {
+		if (inputs[i] < -35 || inputs[i] > 6) {
+			log("Warning check_building_necessity score on position %2d too high %2d\n", i, inputs[i]);
+		}
+	}
+
+	int32_t final_score = 0;
+	for (int i = 0; i < f_neuron_bit_size; i = i + 1) {
+		if (management_data.f_neuron_pool[56].get_position(i)) {
+			final_score += inputs[i];
+		}
+		if (management_data.f_neuron_pool[57].get_position(i)) {
+			final_score += inputs[f_neuron_bit_size + i];
+		}
+		if (management_data.f_neuron_pool[58].get_position(i)) {
+			final_score += inputs[2 * f_neuron_bit_size + i];
+		}
+		if (management_data.f_neuron_pool[13].get_position(i)) {
+			final_score += inputs[3 * f_neuron_bit_size + i];
+		}
+	}
+
+	final_score += std::abs(management_data.get_military_number_at(76) / 10);
+	final_score += management_data.get_military_number_at(100) / 5;
+
+	if (final_score > 0) {
+		bo.primary_priority = final_score * std::abs(management_data.get_military_number_at(79) / 2);
 		return BuildingNecessity::kAllowed;
+	} else {
+		return BuildingNecessity::kForbidden;
 	}
 }
 
@@ -781,7 +1237,8 @@
 // (AI will then wait till training site is stocked)
 void DefaultAI::soldier_trained(const TrainingSite& site) {
 
-	persistent_data->last_soldier_trained = game().get_gametime();
+	const uint32_t gametime = game().get_gametime();
+	soldier_trained_log.push(gametime);
 
 	for (TrainingSiteObserver& trainingsite_obs : trainingsites) {
 		if (trainingsite_obs.site == &site) {

=== modified file 'src/game_io/game_player_ai_persistent_packet.cc'
--- src/game_io/game_player_ai_persistent_packet.cc	2017-01-25 18:55:59 +0000
+++ src/game_io/game_player_ai_persistent_packet.cc	2017-06-16 05:22:22 +0000
@@ -28,7 +28,7 @@
 
 namespace Widelands {
 
-constexpr uint16_t kCurrentPacketVersion = 2;
+constexpr uint16_t kCurrentPacketVersion = 4;
 
 void GamePlayerAiPersistentPacket::read(FileSystem& fs, Game& game, MapObjectLoader*) {
 	try {
@@ -49,12 +49,39 @@
 				player->ai_data.last_attacked_player = fr.signed_16();
 				player->ai_data.least_military_score = fr.unsigned_32();
 				player->ai_data.target_military_score = fr.unsigned_32();
-				player->ai_data.ai_personality_military_loneliness = fr.signed_16();
-				player->ai_data.ai_personality_attack_margin = fr.signed_32();
 				player->ai_data.ai_productionsites_ratio = fr.unsigned_32();
 				player->ai_data.ai_personality_wood_difference = fr.signed_32();
-				player->ai_data.ai_personality_early_militarysites = fr.unsigned_32();
-				player->ai_data.last_soldier_trained = fr.unsigned_32();
+				player->ai_data.ai_personality_mil_upper_limit = fr.signed_32();
+				// Magic numbers
+				player->ai_data.magic_numbers_size = fr.unsigned_32();
+				for (uint16_t i = 0; i < player->ai_data.magic_numbers_size; i = i + 1) {
+					player->ai_data.magic_numbers.push_back(fr.signed_16());
+				}
+				assert(player->ai_data.magic_numbers_size == player->ai_data.magic_numbers.size());
+				// Neurons
+				player->ai_data.neuron_pool_size = fr.unsigned_32();
+				for (uint16_t i = 0; i < player->ai_data.neuron_pool_size; i = i + 1) {
+					player->ai_data.neuron_weights.push_back(fr.signed_8());
+				}
+				for (uint16_t i = 0; i < player->ai_data.neuron_pool_size; i = i + 1) {
+					player->ai_data.neuron_functs.push_back(fr.signed_8());
+				}
+				assert(player->ai_data.neuron_pool_size == player->ai_data.neuron_weights.size());
+				assert(player->ai_data.neuron_pool_size == player->ai_data.neuron_functs.size());
+
+				// F-neurons
+				player->ai_data.f_neuron_pool_size = fr.unsigned_32();
+				for (uint16_t i = 0; i < player->ai_data.f_neuron_pool_size; i = i + 1) {
+					player->ai_data.f_neurons.push_back(fr.unsigned_32());
+				}
+				assert(player->ai_data.f_neuron_pool_size == player->ai_data.f_neurons.size());
+
+				// remaining buildings for basic economy
+				player->ai_data.remaining_buildings_size = fr.unsigned_32();
+				for (uint16_t i = 0; i < player->ai_data.remaining_buildings_size; i = i + 1) {
+					player->ai_data.remaining_basic_buildings[fr.unsigned_32()] = fr.unsigned_32();
+				}
+				assert(player->ai_data.remaining_buildings_size == player->ai_data.remaining_basic_buildings.size());
 
 			} catch (const WException& e) {
 				throw GameDataError("player %u: %s", p, e.what());
@@ -88,12 +115,42 @@
 		fw.signed_16(player->ai_data.last_attacked_player);
 		fw.unsigned_32(player->ai_data.least_military_score);
 		fw.unsigned_32(player->ai_data.target_military_score);
-		fw.signed_16(player->ai_data.ai_personality_military_loneliness);
-		fw.signed_32(player->ai_data.ai_personality_attack_margin);
 		fw.unsigned_32(player->ai_data.ai_productionsites_ratio);
 		fw.signed_32(player->ai_data.ai_personality_wood_difference);
-		fw.unsigned_32(player->ai_data.ai_personality_early_militarysites);
-		fw.unsigned_32(player->ai_data.last_soldier_trained);
+		fw.signed_32(player->ai_data.ai_personality_mil_upper_limit);
+
+		// Magic numbers
+		fw.unsigned_32(player->ai_data.magic_numbers_size);
+		assert(player->ai_data.magic_numbers_size == player->ai_data.magic_numbers.size());
+		for (uint16_t i = 0; i < player->ai_data.magic_numbers_size; i = i + 1) {
+			fw.signed_16(player->ai_data.magic_numbers[i]);
+		}
+		// Neurons
+		fw.unsigned_32(player->ai_data.neuron_pool_size);
+		assert(player->ai_data.neuron_pool_size == player->ai_data.neuron_weights.size());
+		assert(player->ai_data.neuron_pool_size == player->ai_data.neuron_functs.size());
+		for (uint16_t i = 0; i < player->ai_data.neuron_pool_size; i = i + 1) {
+			fw.signed_8(player->ai_data.neuron_weights[i]);
+		}
+		for (uint16_t i = 0; i < player->ai_data.neuron_pool_size; i = i + 1) {
+			fw.signed_8(player->ai_data.neuron_functs[i]);
+		}
+
+		// F-Neurons
+		fw.unsigned_32(player->ai_data.f_neuron_pool_size);
+		assert(player->ai_data.f_neuron_pool_size == player->ai_data.f_neurons.size());
+
+		for (uint16_t i = 0; i < player->ai_data.f_neuron_pool_size; i = i + 1) {
+			fw.unsigned_32(player->ai_data.f_neurons[i]);
+		}
+
+		// Remaining buildings for basic economy
+		assert(player->ai_data.remaining_buildings_size == player->ai_data.remaining_basic_buildings.size());
+		fw.unsigned_32(player->ai_data.remaining_buildings_size);
+		for (auto bb : player->ai_data.remaining_basic_buildings) {
+			fw.unsigned_32(bb.first);
+			fw.unsigned_32(bb.second);
+		}
 	}
 
 	fw.write(fs, "binary/player_ai");

=== modified file 'src/logic/map_objects/tribes/building.cc'
--- src/logic/map_objects/tribes/building.cc	2017-05-23 07:00:23 +0000
+++ src/logic/map_objects/tribes/building.cc	2017-06-16 05:22:22 +0000
@@ -189,6 +189,10 @@
 	               size_ <= (fc.field->nodecaps() & Widelands::BUILDCAPS_SIZEMASK);
 }
 
+void BuildingDescr::set_hints_trainingsites_max_percent(int percent) {
+	hints_.set_trainingsites_max_percent(percent);
+}
+
 /**
  * Normal buildings don't conquer anything, so this returns 0 by default.
  *

=== modified file 'src/logic/map_objects/tribes/building.h'
--- src/logic/map_objects/tribes/building.h	2017-05-21 11:16:46 +0000
+++ src/logic/map_objects/tribes/building.h	2017-06-16 05:22:22 +0000
@@ -160,6 +160,8 @@
 	const BuildingHints& hints() const {
 		return hints_;
 	}
+	void set_hints_trainingsites_max_percent(int percent);
+
 
 protected:
 	virtual Building& create_object() const = 0;

=== modified file 'src/logic/map_objects/tribes/tribe_descr.cc'
--- src/logic/map_objects/tribes/tribe_descr.cc	2017-04-30 10:30:02 +0000
+++ src/logic/map_objects/tribes/tribe_descr.cc	2017-06-16 05:22:22 +0000
@@ -172,6 +172,11 @@
 				}
 				buildings_.push_back(index);
 
+				// Register trainigsites
+				if (get_building_descr(index)->type() == MapObjectType::TRAININGSITE) {
+					trainingsites_.push_back(index);
+				}
+
 				// Register construction materials
 				for (const auto& build_cost : get_building_descr(index)->buildcost()) {
 					if (!is_construction_material(build_cost.first)) {
@@ -188,6 +193,36 @@
 			}
 		}
 
+		// Set default trainingsites proportions for AI. Make sure that we get a sum of ca. 100
+		float trainingsites_without_percent = 0.f;
+		int used_percent = 0;
+		for (const DescriptionIndex& index : trainingsites_) {
+			const BuildingDescr& descr = *tribes_.get_building_descr(index);
+			if (descr.hints().trainingsites_max_percent() == 0) {
+				++trainingsites_without_percent;
+			} else {
+				used_percent += descr.hints().trainingsites_max_percent();
+			}
+		}
+		if (trainingsites_without_percent > 0.f && used_percent > 100) {
+			throw GameDataError("Predefined training sites proportions add up to > 100%%: %d", used_percent);
+		} else if (trainingsites_without_percent > 0) {
+			const int percent_to_use = std::ceil((100 - used_percent) / trainingsites_without_percent);
+			if (percent_to_use < 1) {
+				throw GameDataError("Training sites without predefined proportions add up to < 1%% and will never be built: %d", used_percent);
+			}
+			for (const DescriptionIndex& index : trainingsites_) {
+				BuildingDescr* descr = tribes_.get_mutable_building_descr(index);
+				if (descr->hints().trainingsites_max_percent() == 0) {
+					descr->set_hints_trainingsites_max_percent(percent_to_use);
+				used_percent += percent_to_use;
+				}
+			}
+		}
+		if (used_percent < 100) {
+			throw GameDataError("Final training sites proportions add up to < 100%%: %d", used_percent);
+		}
+
 		// Special types
 		builder_ = add_special_worker(table.get_string("builder"));
 		carrier_ = add_special_worker(table.get_string("carrier"));
@@ -329,6 +364,9 @@
 	assert(tribes_.building_exists(port_));
 	return port_;
 }
+const std::vector<DescriptionIndex>& TribeDescr::trainingsites() const {
+	return trainingsites_;
+}
 DescriptionIndex TribeDescr::barracks() const {
 	assert(tribes_.building_exists(barracks_));
 	return barracks_;

=== modified file 'src/logic/map_objects/tribes/tribe_descr.h'
--- src/logic/map_objects/tribes/tribe_descr.h	2017-04-30 10:30:02 +0000
+++ src/logic/map_objects/tribes/tribe_descr.h	2017-06-16 05:22:22 +0000
@@ -104,6 +104,7 @@
 	DescriptionIndex ship() const;
 	DescriptionIndex headquarters() const;
 	DescriptionIndex port() const;
+	const std::vector<DescriptionIndex>& trainingsites() const;
 	DescriptionIndex barracks() const;
 	const std::vector<DescriptionIndex>& worker_types_without_cost() const;
 
@@ -186,6 +187,7 @@
 	DescriptionIndex port_;          // The port that this tribe uses
 	DescriptionIndex barracks_;      // The barracks to create soldiers
 	std::vector<DescriptionIndex> worker_types_without_cost_;
+	std::vector<DescriptionIndex> trainingsites_;
 	// Order and positioning of wares in the warehouse display
 	WaresOrder wares_order_;
 	WaresOrderCoords wares_order_coords_;

=== modified file 'src/logic/map_objects/tribes/tribes.cc'
--- src/logic/map_objects/tribes/tribes.cc	2017-01-30 14:40:12 +0000
+++ src/logic/map_objects/tribes/tribes.cc	2017-06-16 05:22:22 +0000
@@ -284,6 +284,10 @@
 	return buildings_->get_mutable(buildingindex);
 }
 
+BuildingDescr* Tribes::get_mutable_building_descr(DescriptionIndex buildingindex) const {
+	return buildings_->get_mutable(buildingindex);
+}
+
 const ImmovableDescr* Tribes::get_immovable_descr(DescriptionIndex immovableindex) const {
 	return immovables_->get_mutable(immovableindex);
 }

=== modified file 'src/logic/map_objects/tribes/tribes.h'
--- src/logic/map_objects/tribes/tribes.h	2017-01-30 14:40:12 +0000
+++ src/logic/map_objects/tribes/tribes.h	2017-06-16 05:22:22 +0000
@@ -131,6 +131,7 @@
 	DescriptionIndex worker_index(const std::string& workername) const;
 
 	const BuildingDescr* get_building_descr(DescriptionIndex building_index) const;
+	BuildingDescr* get_mutable_building_descr(DescriptionIndex building_index) const;
 	const ImmovableDescr* get_immovable_descr(DescriptionIndex immovable_index) const;
 	const ShipDescr* get_ship_descr(DescriptionIndex ship_index) const;
 	const WareDescr* get_ware_descr(DescriptionIndex ware_index) const;

=== modified file 'src/logic/player.h'
--- src/logic/player.h	2017-04-23 07:09:25 +0000
+++ src/logic/player.h	2017-06-16 05:22:22 +0000
@@ -22,6 +22,7 @@
 
 #include <memory>
 #include <unordered_set>
+#include <unordered_map>
 
 #include "base/macros.h"
 #include "graphic/color.h"
@@ -35,11 +36,6 @@
 #include "logic/message_queue.h"
 #include "logic/widelands.h"
 
-// there are three arrays to be used by AI
-// their size is defined here
-// (all are of the same size)
-constexpr int kAIDataSize = 8;
-
 class Node;
 namespace Widelands {
 
@@ -169,12 +165,13 @@
 		     last_attacked_player(0),
 		     least_military_score(0),
 		     target_military_score(0),
-		     ai_personality_military_loneliness(0),
-		     ai_personality_attack_margin(0),
 		     ai_productionsites_ratio(0),
 		     ai_personality_wood_difference(0),
-		     ai_personality_early_militarysites(0),
-		     last_soldier_trained(0) {
+		     ai_personality_mil_upper_limit(0),
+		     magic_numbers_size(0),
+		     neuron_pool_size(0),
+		     f_neuron_pool_size(0),
+		     remaining_buildings_size(0) {
 		}
 
 		// Was initialized
@@ -188,12 +185,18 @@
 		int16_t last_attacked_player;
 		int32_t least_military_score;
 		int32_t target_military_score;
-		int16_t ai_personality_military_loneliness;
-		int32_t ai_personality_attack_margin;
 		uint32_t ai_productionsites_ratio;
 		int32_t ai_personality_wood_difference;
-		uint32_t ai_personality_early_militarysites;
-		uint32_t last_soldier_trained;
+		int32_t ai_personality_mil_upper_limit;
+		uint32_t magic_numbers_size;
+		uint32_t neuron_pool_size;
+		uint32_t f_neuron_pool_size;
+		uint32_t remaining_buildings_size;
+		std::vector<int16_t> magic_numbers;
+		std::vector<int8_t> neuron_weights;
+		std::vector<int8_t> neuron_functs;
+		std::vector<uint32_t> f_neurons;
+		std::unordered_map<Widelands::DescriptionIndex, uint32_t>  remaining_basic_buildings;
 	} ai_data;
 
 	AiPersistentState* get_mutable_ai_persistent_state() {

=== modified file 'src/main.cc'
--- src/main.cc	2017-02-26 12:34:43 +0000
+++ src/main.cc	2017-06-16 05:22:22 +0000
@@ -1,3 +1,218 @@
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <unistd.h>
 /*
  * Copyright (C) 2002-2017 by the Widelands Development Team
  *

=== modified file 'src/wui/interactive_base.cc'
--- src/wui/interactive_base.cc	2017-06-15 16:34:58 +0000
+++ src/wui/interactive_base.cc	2017-06-16 05:22:22 +0000
@@ -347,6 +347,29 @@
 	frametime_ = curframe - lastframe_;
 	avg_usframetime_ = ((avg_usframetime_ * 15) + (frametime_ * 1000)) / 16;
 	lastframe_ = curframe;
+	
+	if (upcast(Game, game, &egbase())) {
+		uint32_t cur_fps = 1000000 / avg_usframetime_;
+		int32_t speed_diff = 0;
+		if (cur_fps < 13) {
+			speed_diff = -100;
+			}
+		if (cur_fps > 15) {
+			speed_diff = +100;
+			}
+		if (speed_diff != 0) { //NOCOM this has to be reverted to the state in trunk
+			
+			if (GameController* const ctrl = game->game_controller()) {
+				//printf ("desired speed: %d, current fps: %d\n", ctrl->desired_speed(), cur_fps);
+				if ((ctrl->desired_speed() > 950 and ctrl->desired_speed() < 30000) ||
+				(ctrl->desired_speed() <1000 and speed_diff > 0) ||
+				(ctrl->desired_speed() >29999 and speed_diff < 0)) {
+					ctrl->set_desired_speed(ctrl->desired_speed() + speed_diff);
+					//printf ("speed diff %d, currently: %d\n", speed_diff,ctrl->desired_speed());
+				}
+			}
+		}
+	}  
 
 	const Map& map = egbase().map();
 	const bool is_game = dynamic_cast<const Game*>(&egbase());

=== modified file 'src/wui/interactive_base.h'
--- src/wui/interactive_base.h	2017-05-10 10:28:49 +0000
+++ src/wui/interactive_base.h	2017-06-16 05:22:22 +0000
@@ -42,6 +42,9 @@
 #include "wui/minimap.h"
 #include "wui/quicknavigation.h"
 
+#include <iostream> //NOCOM
+#include <fstream> //NOCOM
+
 namespace Widelands {
 struct CoordPath;
 }


Follow ups