← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/ai_persistent_data into lp:widelands

 

TiborB has proposed merging lp:~widelands-dev/widelands/ai_persistent_data into lp:widelands.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/ai_persistent_data/+merge/271853

This is extension of player class to keep some AI data. Not all, only handfull of them. So the savegame compatibility is broken.

This contains also redesign of placing of new buidings - both production and militarysites. I tried to simplify the code and improve performance.


-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/ai_persistent_data into lp:widelands.
=== modified file 'src/ai/ai_help_structs.h'
--- src/ai/ai_help_structs.h	2015-08-19 19:29:56 +0000
+++ src/ai/ai_help_structs.h	2015-09-21 18:31:31 +0000
@@ -42,6 +42,7 @@
 class MilitarySite;
 
 enum class ExtendedBool : uint8_t {kUnset, kTrue, kFalse};
+enum class BuildingNecessity : uint8_t {kForced, kNeeded, kNotNeeded, kUnset, kNotBuildable, kAllowed};
 
 struct CheckStepRoadAI {
 	CheckStepRoadAI(Player* const pl, uint8_t const mc, bool const oe)
@@ -268,6 +269,7 @@
 	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_;
 
 	std::vector<uint8_t> consumers_nearby_;
 	std::vector<uint8_t> producers_nearby_;
@@ -303,7 +305,12 @@
 	     military_unstationed_(0),
 	     is_portspace_(false),
 	     port_nearby_(false),
-	     portspace_nearby_(Widelands::ExtendedBool::kUnset) {
+	     portspace_nearby_(Widelands::ExtendedBool::kUnset),
+	     max_buildcap_nearby_(0) {
+	}
+
+	int32_t own_military_sites_nearby_(){
+		return military_stationed_ + military_unstationed_;
 	}
 };
 
@@ -352,9 +359,11 @@
 		MINE
 	} type;
 
-	bool prod_build_material_;
 	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_stones_;  // quarry = true
@@ -372,8 +381,7 @@
 	uint32_t forced_after_;     // do not wait until ware is needed
 	TrainingSiteType trainingsite_type_;
 
-	bool unoccupied_;
-	uint16_t unconnected_;  // to any warehouse (count of such buildings)
+	uint16_t unconnected_count_;  // to any warehouse (count of such buildings)
 
 	int32_t mines_;           // type of resource it mines_
 	uint16_t mines_percent_;  // % of res it can mine
@@ -383,7 +391,13 @@
 	std::vector<int16_t> outputs_;
 	std::vector<Widelands::WareIndex> critical_built_mat_;
 
+	bool built_mat_producer_;
+
+	// 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
@@ -394,10 +408,7 @@
 	int16_t production_hint_;
 
 	// information needed for decision on new building construction
-	// these should be calculated only once during one run of construct_building()
-	// function
-	Widelands::ExtendedBool output_needed_;
-	int16_t max_preciousness;
+	int16_t max_preciousness_;
 	int16_t max_needed_preciousness_;
 
 	int32_t cnt_built_;
@@ -409,6 +420,9 @@
 	int32_t stocklevel_time;  // time when stocklevel_ was last time recalculated
 	int32_t last_dismantle_time_;
 	int32_t construction_decision_time_;
+
+	uint32_t unoccupied_count_;
+
 	bool build_material_shortage_;
 
 	int32_t total_count() const {
@@ -424,7 +438,7 @@
 	uint32_t built_time_;
 	uint32_t unoccupied_till_;
 	uint8_t stats_zero_;
-	uint8_t no_resources_count;
+	uint32_t no_resources_since_;
 	BuildingObserver* bo;
 };
 

=== modified file 'src/ai/ai_hints.cc'
--- src/ai/ai_hints.cc	2015-05-07 20:46:32 +0000
+++ src/ai/ai_hints.cc	2015-09-21 18:31:31 +0000
@@ -32,7 +32,7 @@
      fighting_(section ? section->get_bool("fighting") : false),
      mountain_conqueror_(section ? section->get_bool("mountain_conqueror") : false),
      prohibited_till_(section ? section->get_natural("prohibited_till", 0) : 0),
-     forced_after_(section ? section->get_natural("forced_after", 864000) : 0),  // 10 days default
+     forced_after_(section ? section->get_natural("forced_after", 864000) : 864000),  // 10 days default
      mines_percent_(section ? section->get_int("mines_percent", 100) : 0),
      trainingsite_type_(TrainingSiteType::kNoTS)
 

=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc	2015-08-28 19:09:59 +0000
+++ src/ai/defaultai.cc	2015-09-21 18:31:31 +0000
@@ -52,7 +52,6 @@
 #include "profile/profile.h"
 
 // following is in miliseconds (widelands counts time in ms)
-// constexpr int kFieldUpdateInterval = 2000;
 constexpr int kFieldInfoExpiration = 12 * 1000;
 constexpr int kMineFieldInfoExpiration = 20 * 1000;
 constexpr int kNewMineConstInterval = 19000;
@@ -68,10 +67,20 @@
 // this is intended for map developers, by default should be off
 constexpr bool kPrintStats = false;
 
-// Some buildings have to be built close to borders and their
-// priority might be decreased below 0, so this is to
-// compensate
-constexpr int32_t kDefaultPrioBoost = 12;
+constexpr int kPersData     = 0; //uint16_t
+constexpr int kMilitLonel   = 1;
+constexpr int kAttacker     = 2;
+constexpr int kAttackMargin = 0; //uint32_t
+constexpr int kLastAttack   = 1;
+constexpr int kProdRatio    = 2;
+constexpr int kColonyScan   = 3;
+constexpr int kTreesAround  = 4;
+constexpr int kWoodDiff     = 0; //int32_t
+constexpr int kTargetMilit  = 1;
+constexpr int kLeastMilit   = 2;
+
+//duration of military campaig
+constexpr int kCampaignDuration = 15 * 60 * 1000;
 
 using namespace Widelands;
 
@@ -90,8 +99,11 @@
      num_prod_constructionsites(0),
      num_ports(0),
      last_attacked_player_(std::numeric_limits<uint16_t>::max()),
+     last_attack_time_(0),
      enemysites_check_delay_(60),
-     wood_policy_(WoodPolicy::kStartRangers),
+     target_military_score_(0),
+     least_military_score_(100),
+     wood_policy_(WoodPolicy::kAllowRangers),
      next_ai_think_(0),
      next_mine_construction_due_(0),
      inhibit_road_building_(0),
@@ -103,6 +115,7 @@
      resource_necessity_mines_(100),
      resource_necessity_water_(0),
      resource_necessity_water_needed_(false),
+     trees_around_cutters_(0),
      unstationed_milit_buildings_(0),
      military_last_dismantle_(0),
      military_last_build_(0),
@@ -115,7 +128,11 @@
      ts_advanced_count_(0),
      ts_advanced_const_count_(0),
      ts_without_trainers_(0),
-     scheduler_delay_counter_(0) {
+     scheduler_delay_counter_(0),
+     ai_personality_military_loneliness_(0),
+     ai_personality_attack_margin_(0),
+     ai_personality_wood_difference_(0),
+     ai_productionsites_ratio_(0) {
 
 	// Subscribe to NoteFieldPossession.
 	field_possession_subscriber_ =
@@ -296,7 +313,11 @@
 			if (check_economies()) {  // economies must be consistent
 				return;
 			}
-			taskDue[ScheduleTasks::kConstructBuilding] = gametime + 6000;
+			if (gametime < 15000) { //more frequent on the begining of game
+				taskDue[ScheduleTasks::kConstructBuilding] = gametime + 2000;
+			} else {
+				taskDue[ScheduleTasks::kConstructBuilding] = gametime + 6000;
+			}
 			if (construct_building(gametime)) {
 				time_of_last_construction_ = gametime;
 			}
@@ -420,8 +441,10 @@
 		bo.build_material_shortage_ = false;
 		bo.production_hint_ = -1;
 		bo.current_stats_ = 0;
-		bo.unoccupied_ = false;
-		bo.unconnected_ = 0;
+		bo.unoccupied_count_ = 0;
+		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_stones_ = bh.is_stoneproducer();
@@ -437,8 +460,9 @@
 		bo.is_port_ = bld.get_isport();
 		bo.trainingsite_type_ = TrainingSiteType::kNoTS;
 		bo.upgrade_substitutes_ = false;
-		bo.output_needed_ = ExtendedBool::kUnset;
-		bo.max_preciousness = 0;
+		bo.upgrade_extends_ = false;
+		bo.built_mat_producer_ = false;
+		bo.max_preciousness_ = 0;
 		bo.max_needed_preciousness_ = 0;
 
 		if (bh.renews_map_resource()) {
@@ -519,13 +543,30 @@
 						break;
 					}
 				}
+
+				std::unordered_set<WareIndex> cur_outputs;
+				// collecting wares that are produced in enhanced building
+				for (const WareIndex& ware : bo.outputs_) {
+						cur_outputs.insert(ware);
+					}
+				bo.upgrade_extends_ = false;
+				for (WareIndex ware : enh_outputs) {
+					if (cur_outputs.count(ware) == 0) {
+						bo.upgrade_extends_ = true;
+						break;
+					}
+				}
 			}
 
-			// plus some manually picked buildings,
-			// see preferred_upgrade list
-			for (const char* pb : preferred_upgrade) {
-				if (strcmp(bld.name().c_str(), pb) == 0) {
-					bo.upgrade_substitutes_ = true;
+			// now we identify producers of critical build materials
+			// hardwood now
+			for (WareIndex 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.built_mat_producer_ = true;
 				}
 			}
 
@@ -545,6 +586,10 @@
 				    tribe_->ware_index("planks") == temp_buildcosts.first ||
 				    tribe_->ware_index("wood") == temp_buildcosts.first ||
 				    tribe_->ware_index("raw_stone") == temp_buildcosts.first ||
+				    tribe_->ware_index("grout") == temp_buildcosts.first ||
+				    tribe_->ware_index("quartz") == temp_buildcosts.first ||
+				    tribe_->ware_index("spidercloth") == temp_buildcosts.first ||
+				    tribe_->ware_index("diamond") == temp_buildcosts.first ||
 				    tribe_->ware_index("stone") == temp_buildcosts.first)
 					continue;
 
@@ -586,8 +631,6 @@
 		}
 	}
 
-
-
 	// atlanteans they consider water as a resource
 	// (together with mines, stones and wood)
 	if (tribe_->name() == "atlanteans") {
@@ -692,6 +735,88 @@
 	taskDue[ScheduleTasks::kPrintStats] = 30 * 60 * 1000;
 	taskDue[ScheduleTasks::kCountMilitaryVacant] = 10 * 60 * 1000;
 	taskDue[ScheduleTasks::kCheckEnemySites] = 10 * 60 * 1000;
+
+	// Here the AI persistent data either exists - then they are read
+	// or does not exist, then they are created and saved
+	const int16_t persisten_data_exists_ = player_->get_ai_data_int16(kPersData);
+
+	// 0 implies no saved data exits yet
+	if (persisten_data_exists_ == 0) {
+		player_->set_ai_data(static_cast<int16_t>(1), kPersData);
+
+		// these random values to make some small differences between players
+		// they are immediately saved
+		// these values are never changed
+		ai_personality_military_loneliness_ = std::rand() % 5 * 30 - 60;
+		player_->set_ai_data(ai_personality_military_loneliness_, kMilitLonel);
+
+		ai_personality_attack_margin_ = std::max(std::rand() % 20 - 5, 0);
+		player_->set_ai_data(ai_personality_attack_margin_, kAttackMargin);
+
+		ai_personality_wood_difference_ = std::rand() % 40 - 20;
+		player_->set_ai_data(ai_personality_wood_difference_, kWoodDiff);
+
+		ai_productionsites_ratio_ = std::rand() % 5 + 7;
+		player_->set_ai_data(ai_productionsites_ratio_, kProdRatio);
+
+	} else {
+		log (" %d: restoring saved AI data...\n", player_number());
+
+
+		//restoring data and doing some basic check
+		ai_personality_military_loneliness_ = player_->get_ai_data_int16(kMilitLonel);
+		if (ai_personality_military_loneliness_ < -60 || ai_personality_military_loneliness_ > 60) {
+			log(" %d: unexpected value for ai_personality_military_loneliness_: %d\n",
+			player_number(), ai_personality_military_loneliness_);
+		}
+
+		ai_personality_attack_margin_ = player_->get_ai_data_uint32(kAttackMargin);
+		if (ai_personality_attack_margin_ > 15) {
+			log(" %d: unexpected value for ai_personality_attack_margin_: %d\n",
+			player_number(), ai_personality_attack_margin_);
+		}
+
+		ai_personality_wood_difference_ = player_->get_ai_data_int32(kWoodDiff);
+		if (ai_personality_wood_difference_ < -20 || ai_personality_wood_difference_ > 19) {
+			log(" %d: unexpected value for ai_personality_wood_difference_: %d\n",
+			player_number(), ai_personality_wood_difference_);
+		}
+
+		ai_productionsites_ratio_ = player_->get_ai_data_uint32(kProdRatio);
+		if (ai_productionsites_ratio_< 5 || ai_productionsites_ratio_ > 15) {
+			log(" %d: unexpected value for ai_productionsites_ratio_: %d\n",
+			player_number(), ai_productionsites_ratio_);
+		}
+
+
+		//now some runtime values that are generated and saved during course of game
+		last_attack_time_ = player_->get_ai_data_uint32(kLastAttack);
+
+		last_attacked_player_ = static_cast<uint16_t>(player_->get_ai_data_int16(kAttacker));
+		if (last_attacked_player_ > 8) {
+			log(" %d: unexpected value for last_attacked_player_: %d\n",
+			player_number(), ai_personality_attack_margin_);
+		}
+
+		colony_scan_area_ = player_->get_ai_data_uint32(kColonyScan);
+		if (colony_scan_area_ > 50) {
+			log(" %d: unexpected value for colony_scan_area_: %d\n",
+			player_number(), colony_scan_area_);
+		}
+
+		trees_around_cutters_ = player_->get_ai_data_uint32(kTreesAround);
+
+		least_military_score_ = player_->get_ai_data_int32(kLeastMilit);
+		if (least_military_score_< 0 || least_military_score_ > 1000) {
+			log(" %d: unexpected value for least_military_score_: %d\n",
+			player_number(), least_military_score_);
+		}
+		target_military_score_ = player_->get_ai_data_int32(kTargetMilit);
+		if (target_military_score_< least_military_score_ || target_military_score_ > 1000) {
+			log(" %d: unexpected value for target_military_score_: %d\n",
+			player_number(), target_military_score_);
+		}
+	}
 }
 
 /**
@@ -889,6 +1014,20 @@
 		field.port_nearby_ = false;
 	}
 
+	// testing fields in radius 1 to find biggest buildcaps.
+	// This is to calculate capacity that will be lost if something is
+	// built here
+	field.max_buildcap_nearby_ = 0;
+	MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, 1));
+		do {
+			if ((player_->get_buildcaps(mr.location()) & BUILDCAPS_SIZEMASK) > field.max_buildcap_nearby_) {
+				field.max_buildcap_nearby_ = player_->get_buildcaps(mr.location()) & BUILDCAPS_SIZEMASK;
+				//break;
+			}
+		} while (mr.advance(map));
+
+	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
@@ -967,26 +1106,6 @@
 				}
 			}
 
-			if (upcast(Building const, building, &base_immovable)) {
-				if (upcast(ConstructionSite const, constructionsite, building)) {
-					const BuildingDescr& target_descr = constructionsite->building();
-
-					if (dynamic_cast<ProductionSiteDescr const*>(&target_descr)) {
-						consider_productionsite_influence(
-						   field,
-						   immovables.at(i).coords,
-						   get_building_observer(constructionsite->descr().name().c_str()));
-					}
-				}
-
-				if (dynamic_cast<const ProductionSite*>(building)) {
-					consider_productionsite_influence(
-					   field,
-					   immovables.at(i).coords,
-					   get_building_observer(building->descr().name().c_str()));
-				}
-			}
-
 			if (immovables.at(i).object->has_attribute(tree_attr)) {
 				++field.trees_nearby_;
 			}
@@ -1142,8 +1261,8 @@
 	// Reset statistics for all buildings
 	for (uint32_t i = 0; i < buildings_.size(); ++i) {
 		buildings_.at(i).current_stats_ = 0;
-		buildings_.at(i).unoccupied_ = false;
-		buildings_.at(i).unconnected_ = 0;
+		buildings_.at(i).unoccupied_count_ = 0;
+		buildings_.at(i).unconnected_count_ = 0;
 	}
 
 	// Check all available productionsites
@@ -1165,10 +1284,11 @@
 			}
 
 			// Check whether this building is completely occupied
-			productionsites.front().bo->unoccupied_ |=
-			   !productionsites.front().site->can_start_working();
+			if (!productionsites.front().site->can_start_working()) {
+				productionsites.front().bo->unoccupied_count_ += 1;
+			}
 		} else {
-			productionsites.front().bo->unconnected_ += 1;
+			productionsites.front().bo->unconnected_count_ += 1;
 		}
 
 		// Now reorder the buildings
@@ -1199,9 +1319,11 @@
 			// Add statistics value
 			mines_.front().bo->current_stats_ += mines_.front().site->get_statistics_percent();
 			// Check whether this building is completely occupied
-			mines_.front().bo->unoccupied_ |= !mines_.front().site->can_start_working();
+			if (!mines_.front().site->can_start_working()) {
+				mines_.front().bo->unoccupied_count_ += 1;
+			}
 		} else {
-			mines_.front().bo->unconnected_ += 1;
+			mines_.front().bo->unconnected_count_ += 1;
 		}
 
 		// Now reorder the buildings
@@ -1211,9 +1333,9 @@
 
 	// Scale statistics down
 	for (uint32_t i = 0; i < buildings_.size(); ++i) {
-		if ((buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_) > 0) {
+		if ((buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_count_) > 0) {
 			buildings_.at(i).current_stats_ /=
-			   (buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_);
+			   (buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_count_);
 		}
 	}
 }
@@ -1261,7 +1383,6 @@
 
 	// here we possible stop building of new buildings
 	new_buildings_stop_ = false;
-	MilitaryStrategy expansion_mode = MilitaryStrategy::kResourcesOrDefense;
 
 	// helper variable - we need some proportion of free spots vs productionsites
 	// the proportion depends on size of economy
@@ -1278,11 +1399,56 @@
 		needed_spots = 1300 + (productionsites.size() - 200) * 20;
 	}
 
+	// *_military_scores are used as minimal score for a new military building
+	// to be build. As AI does not traverse all building fields at once, these tresholds
+	// are gradually going down until it finds a field&building that are above treshold
+	// and this combination is used...
+	// least_military_score_ is hardlimit, floating very slowly
+	// target_military_score_ is always set according to latest best building (using the same
+	// score) and quickly falling down until it reaches the least_military_score_
+	// this one (=target_military_score_) is actaully 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
+
+	// this is just helpers to improve readability of code
+	const bool too_many_ms_constructionsites =
+		((num_milit_constructionsites * num_milit_constructionsites) > militarysites.size());
+	const bool too_many_vacant_mil =
+		(vacant_mil_positions_ * 3 > static_cast<int32_t>(militarysites.size()));
+	// modifying least_military_score_, down if more military sites are needed and vice verse
+	if (too_many_ms_constructionsites || too_many_vacant_mil) {
+		if (least_military_score_ < 200){ //no sense to let it grow too hight
+			least_military_score_ += 2;
+		}
+	} else {
+		least_military_score_ -= 4;
+		// do not get bellow 100 if there is at least one ms in construction
+		if ((num_milit_constructionsites > 0 || too_many_vacant_mil) && least_military_score_ < 100){
+			least_military_score_ = 100;
+		}
+		if (least_military_score_ < 0){
+			least_military_score_ = 0;
+		}
+	}
+	//this is effective score, falling down very quickly
+	target_military_score_ = 9 * target_military_score_ / 10;
+	if (target_military_score_ < least_military_score_){
+		target_military_score_ = least_military_score_;
+	}
+	player_->set_ai_data(target_military_score_, kTargetMilit);
+	player_->set_ai_data(least_military_score_, kLeastMilit);
+
+
 	// 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 > productionsites.size() / 7 + 2) {
-		new_buildings_stop_ = true;
+	if ((num_prod_constructionsites + mines_in_constr())
+		>
+		(productionsites.size() + mines_built())
+		/
+		ai_productionsites_ratio_ + 2) {
+			new_buildings_stop_ = true;
 	}
 	// 2. to not exhaust all free spots
 	if (spots_ < needed_spots) {
@@ -1297,10 +1463,6 @@
 	if (mines_.size() < 3) {
 		new_buildings_stop_ = true;
 	}
-	// BUT if enemy is nearby, we cancel above stop
-	if (new_buildings_stop_ && enemy_last_seen_ + 10 * 60 * 1000 > gametime) {
-		new_buildings_stop_ = false;
-	}
 
 	// we must calculate wood policy
 	const WareIndex wood_index = tribe_->safe_ware_index("log");
@@ -1309,15 +1471,16 @@
 	// 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;
-	if (stocked_wood_margin > 80) {
+		num_prod_constructionsites +
+		ai_personality_wood_difference_;
+	if (gametime < 15 * 60 * 1000) {
+		wood_policy_ = WoodPolicy::kAllowRangers;
+	} else if (stocked_wood_margin > 80) {
 		wood_policy_ = WoodPolicy::kDismantleRangers;
 	} else if  (stocked_wood_margin > 25) {
 		wood_policy_ = WoodPolicy::kStopRangers;
-	} else if  (stocked_wood_margin > 10) {
-		wood_policy_ = WoodPolicy::kStartRangers;
 	} else {
-		wood_policy_ = WoodPolicy::kBuildRangers;
+		wood_policy_ = WoodPolicy::kAllowRangers;
 	}
 
 	// we must consider need for mines
@@ -1349,27 +1512,6 @@
 		}
 	}
 
-	// this controls a speed and willingness to expand the teritorry
-	const int32_t vacant_plus_in_construction_minus_prod =
-	   vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 7;
-	if (vacant_plus_in_construction_minus_prod > 20) {
-		expansion_mode = MilitaryStrategy::kNoNewMilitary;
-	} else if (vacant_plus_in_construction_minus_prod > 13) {
-		expansion_mode = MilitaryStrategy::kDefenseOnly;
-	} else if (vacant_plus_in_construction_minus_prod > 6) {
-		expansion_mode = MilitaryStrategy::kResourcesOrDefense;
-	} else {
-		// this is intended for initial phase of game when the player still has enough soldiers
-		// but we still want to force it to follow resources instead for plain expansion
-		if (virtual_mines <= 2 && (unstationed_milit_buildings_ + num_milit_constructionsites) > 2) {
-			expansion_mode = MilitaryStrategy::kResourcesOrDefense;
-		} else if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
-			expansion_mode = MilitaryStrategy::kExpansion;
-		} else {
-			expansion_mode = MilitaryStrategy::kPushExpansion;
-		}
-	}
-
 	BuildingObserver* best_building = nullptr;
 	int32_t proposed_priority = 0;
 	Coords proposed_coords;
@@ -1412,11 +1554,70 @@
 		}
 	}
 
-	//Resetting output_needed_ in building observer
+	//calculating actual needness
 	for (uint32_t j = 0; j < buildings_.size(); ++j) {
 		BuildingObserver& bo = buildings_.at(j);
-		if (bo.type == BuildingObserver::PRODUCTIONSITE || bo.type == BuildingObserver::MINE){
-			bo.output_needed_ = ExtendedBool::kUnset;
+
+		if (!bo.buildable(*player_)) {
+			bo.new_building_ = BuildingNecessity::kNotNeeded;
+		} else if (bo.type == BuildingObserver::PRODUCTIONSITE || bo.type == BuildingObserver::MINE) {
+
+			bo.new_building_ = check_building_necessity(bo, PerfEvaluation::kForConstruction, gametime);
+
+			if (bo.new_building_ == BuildingNecessity::kAllowed) {
+				bo.new_building_overdue_ = 0;
+			}
+
+			//sometimes a building is required though the ware is not needed
+			if ((bo.new_building_ == BuildingNecessity::kNeeded
+				|| bo.new_building_ == BuildingNecessity::kForced)
+				&& bo.max_needed_preciousness_ == 0) {
+					bo.max_needed_preciousness_ = bo.max_preciousness_;
+			}
+
+			// Exemptions bellow has to guarantee that no further building is going to be built
+			// if there is any in construction of unoccupied building.
+			// Rangers, cutters and buildings with preciosness>10 can have 1 such building
+			// Or rather, they should guarantee that there is no logical error in previous steps, or
+			// inconstinancy in AI data
+			if (bo.new_building_ == BuildingNecessity::kNeeded
+				|| bo.new_building_ == BuildingNecessity::kForced
+				|| bo.new_building_ == BuildingNecessity::kAllowed) {
+				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_);
+					}
+				}
+			}
+
+			// calculating primary_priority_, basically it is max_needed_preciousness +
+			// some bonus for being late
+			bo.primary_priority_ = bo.max_needed_preciousness_ +
+			bo.max_needed_preciousness_ * bo.new_building_overdue_ / 100 +
+			bo.new_building_overdue_ / 20;
+
+			//another set of tests to guarantee consistency
+			if (bo.new_building_ == BuildingNecessity::kNeeded ||
+				bo.new_building_ == BuildingNecessity::kForced ||
+				bo.new_building_ == BuildingNecessity::kAllowed) {
+
+				if (bo.new_building_ == BuildingNecessity::kAllowed) {
+					assert (bo.new_building_overdue_ == 0);
+				} else if (bo.outputs_.empty()) {
+					assert (bo.primary_priority_ == 0);
+				} else if (bo.new_building_ == BuildingNecessity::kNeeded){
+					assert (bo.primary_priority_ > 0);
+				}
+			}
+		} else {
+			bo.new_building_ = BuildingNecessity::kAllowed;
+			bo.primary_priority_ = 0;
 		}
 	}
 
@@ -1456,9 +1657,13 @@
 				continue;
 			}
 
-			if (bo.prohibited_till_ > gametime) {
+			if (bo.new_building_ == BuildingNecessity::kNotNeeded) {
 				continue;
-			}
+				}
+
+			assert (bo.new_building_ == BuildingNecessity::kForced ||
+			bo.new_building_ == BuildingNecessity::kNeeded ||
+			bo.new_building_ == BuildingNecessity::kAllowed);
 
 			// if current field is not big enough
 			if (bo.desc->get_size() > maxsize) {
@@ -1486,10 +1691,6 @@
 				continue;
 			}
 
-			if (bo.unoccupied_) {
-				continue;
-			}
-
 			if (!(bo.type == BuildingObserver::MILITARYSITE) && bo.cnt_under_construction_ >= 2) {
 				continue;
 			}
@@ -1498,34 +1699,28 @@
 
 			if (bo.type == BuildingObserver::PRODUCTIONSITE) {
 
-				// exclude spots on border
-				if (bf->near_border_ && !bo.need_trees_ && !bo.need_stones_ && !bo.is_fisher_) {
-					continue;
-				}
-
-				// this just indicates that values must be recalculated - if this building
-				// is to be considered later on in this function
-				if (bo.output_needed_ == ExtendedBool::kUnset) {
-					check_building_necessity(bo);
-				}
-
 				// this can be only a well (as by now)
 				if (bo.mines_water_) {
+
+					assert(bo.new_building_ == BuildingNecessity::kForced
+					||
+					(bo.new_building_ == BuildingNecessity::kNeeded
+					&&
+					bo.new_building_overdue_ > 0));
+
+					if (bo.new_building_ == BuildingNecessity::kForced) {
+						assert (bo.total_count() - bo.unconnected_count_ == 0);
+					}
+
 					if (bf->ground_water_ < 2) {
 						continue;
 					}
 
-					if (bo.cnt_under_construction_ + bo.unoccupied_ > 0) {
-						continue;
-					}
+					prio = bo.primary_priority_;
 
-					prio = 0;
 					// one well is forced
-					if (bo.total_count() == 0) {
-						prio = 200;
-					}  // boost for first/only well
-					else if (new_buildings_stop_) {
-						continue;
+					if (bo.new_building_ == BuildingNecessity::kForced) {
+						prio += 200;
 					}
 
 					bo.cnt_target_ = 1 + productionsites.size() / 50;
@@ -1538,55 +1733,31 @@
 						continue;
 					}
 					prio += bf->ground_water_ - 2;
-					prio = recalc_with_border_range(*bf, prio);
 
 				} else if (bo.need_trees_) {  // LUMBERJACS
 
-					bo.cnt_target_ =
-					   3 + static_cast<int32_t>(mines_.size() + productionsites.size()) / 15;
-
-					if (bo.total_count() == 0) {
-						prio = 500 + bf->trees_nearby_;
-					}
-
-					else if (bo.total_count() == 1) {
-						prio = 400 + bf->trees_nearby_;
-					}
-
-					else if (bf->trees_nearby_ < 2) {
+					prio = bo.primary_priority_;
+
+					prio += 200 / (bo.total_count() + 1);
+
+					if (bo.new_building_ == BuildingNecessity::kForced) {
+						prio *= 2;
+					} else if (bf->trees_nearby_ < 2) {
 						continue;
 					}
 
-					else {
-
-						if (bo.total_count() < bo.cnt_target_) {
-							prio = 75;
-						} else {
-							prio = 0;
-						}
-
-						if (bf->producers_nearby_.at(bo.outputs_.at(0)) > 1) {
-							continue;
-						}
-
-						prio += 2 * bf->trees_nearby_ - 10 -
-						        bf->producers_nearby_.at(bo.outputs_.at(0)) * 5 -
-						        new_buildings_stop_ * 15;
-
-						if (bf->near_border_) {
-							prio = prio / 2;
-						}
+					if (bf->producers_nearby_.at(bo.outputs_.at(0)) > 1) {
+						prio -= bf->producers_nearby_.at(bo.outputs_.at(0)) * 30;
 					}
 
+					prio += 2 * bf->trees_nearby_;
+
 				} else if (bo.need_stones_) {
 
 					// quaries are generally to be built everywhere where stones are
 					// no matter the need for stones, as stones are considered an obstacle
 					// to expansion
-					if (bo.cnt_under_construction_ > 0) {
-						continue;
-					}
-					prio = bf->stones_nearby_;
+					prio = 2 * bf->stones_nearby_;
 
 					// value is initialized with 1 but minimal value that can be
 					// calculated is 11
@@ -1594,7 +1765,7 @@
 						continue;
 					}
 
-					if (bo.total_count() - bo.unconnected_ == 0) {
+					if (bo.total_count() - bo.unconnected_count_ == 0) {
 						prio += 150;
 					}
 
@@ -1610,119 +1781,90 @@
 					// to prevent to many quaries on one spot
 					prio = prio - 50 * bf->producers_nearby_.at(bo.outputs_.at(0));
 
-					if (bf->near_border_) {
-						prio = prio / 2;
-					}
-
 				} else if (bo.is_hunter_) {
+
+					assert (bo.new_building_ == BuildingNecessity::kNeeded ||
+					bo.new_building_ == BuildingNecessity::kForced);
+
 					if (bf->critters_nearby_ < 5) {
 						continue;
 					}
 
-					if (gametime > 5 * 60 * 1000 &&
-					(bo.total_count() - bo.unconnected_  == 0)) {
-						prio += 10;
-					} else if (new_buildings_stop_) {
-						continue;
+					if (bo.new_building_ == BuildingNecessity::kForced) {
+						prio += 20;
 					}
 
+					//overdue priority here
+					prio += bo.primary_priority_;
+
 					prio +=
 					   (bf->critters_nearby_ * 3) - 8 - 5 * bf->producers_nearby_.at(bo.outputs_.at(0));
 
 				} else if (bo.is_fisher_) {  // fisher
 
-					// ~are fishes needed?
-					if (bo.max_needed_preciousness_ == 0) {
-						continue;
-					}
-
-					if (bo.cnt_under_construction_ + bo.unoccupied_ > 0) {
-						continue;
-					}
-
-					if (bf->water_nearby_ < 2) {
-						continue;
-					}
-
-					// we use preciousness to allow atlanteans to build the fishers huts
-					// atlanteans have preciousness 4, other tribes 3
-					if (bo.max_needed_preciousness_ < 4 && new_buildings_stop_) {
-						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) {
-						continue;
-					}
+					assert (bo.new_building_ == BuildingNecessity::kNeeded ||
+					bo.new_building_ == BuildingNecessity::kForced);
+
+					if (bf->water_nearby_ < 2 || bf->fish_nearby_ < 2) {
+						continue;
+					}
+
+					if (bo.new_building_ == BuildingNecessity::kForced) {
+						prio += 20;
+					}
+
+					//overdue priority here
+					prio += bo.primary_priority_;
 
 					if (bf->producers_nearby_.at(bo.outputs_.at(0)) >= 1) {
 						continue;
 					}
 
-					prio = bf->fish_nearby_ - new_buildings_stop_ * 15 * bo.total_count();
+					prio += bf->fish_nearby_;
 
 				} else if (bo.production_hint_ >= 0) {
-					// first setting targets (needed also for dismantling)
 					if (bo.plants_trees_) {
-						bo.cnt_target_ =
-						   2 + static_cast<int32_t>(mines_.size() + productionsites.size()) / 15;
+						assert (bo.cnt_target_ > 0);
 					} else {
 						bo.cnt_target_ =
 						   1 + static_cast<int32_t>(mines_.size() + productionsites.size()) / 20;
 					}
 
-					if ((bo.cnt_under_construction_ + bo.unoccupied_) > 1) {
-						continue;
-					}
-
 					if (bo.plants_trees_) {  // 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_ < (10 * bo.total_count()) && bo.total_count() > 0) {
+						if (spots_ < (4 * bo.total_count()) && bo.total_count() > 0) {
 							continue;
 						}
 
 						if (bo.total_count() == 0) {
 							prio = 200;
 						}
-						if (bo.total_count() > 2 * bo.cnt_target_) {
-							continue;
-						}
-						// we can go above target if there is shortage of logs on stock
-						else if (bo.total_count() >= bo.cnt_target_) {
-							if (wood_policy_ != WoodPolicy::kBuildRangers) {
-								continue;
-							}
-						}
 
 						// considering near trees and producers
 						prio += (30 - bf->trees_nearby_) * 2 +
 						        bf->producers_nearby_.at(bo.production_hint_) * 5 -
-						        new_buildings_stop_ * 15;
+						        new_buildings_stop_ * 15 -
+						        bf->stones_nearby_;
 
 					} else {  // FISH BREEDERS and GAME KEEPERS
-						if (new_buildings_stop_ && (bo.total_count() - bo.unconnected_) > 0) {
-							continue;
-						}
 
 						// especially for fish breeders
-						if (bo.need_water_ && bf->water_nearby_ < 2) {
+						if (bo.need_water_ && bf->water_nearby_ < 6) {
 							continue;
 						}
 						if (bo.need_water_) {
 							prio += bf->water_nearby_ / 5;
 						}
 
-						if ((bo.total_count() - bo.unconnected_) > bo.cnt_target_) {
+						if ((bo.total_count() - bo.unconnected_count_) > bo.cnt_target_) {
 							continue;
 						}
 
@@ -1752,56 +1894,32 @@
 					// 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_) == 0 &&
+					    (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 = 4 + kDefaultPrioBoost;
+							prio = 10;
 						}
 				} else {  // finally normal productionsites
-					if (bo.production_hint_ >= 0) {
-						continue;
-					}
-
-					// generally we allow 1 building in construction, but if
-					// preciousness of missing ware is >=10 and it is farm-like building
-					// we allow 2 in construction
-					if (bo.max_needed_preciousness_ >= 10
-						&& bo.inputs_.empty()
-						&& gametime > 30 * 60 * 1000) {
-						if ((bo.cnt_under_construction_ + bo.unoccupied_) > 1) {
-							continue;
-						}
-					} else {
-						if ((bo.cnt_under_construction_ + bo.unoccupied_) > 0) {
-							continue;
-						}
-					}
-
-					if (bo.forced_after_ < gametime && (bo.total_count() - bo.unconnected_) == 0) {
+					assert (bo.production_hint_ < 0);
+					assert (bo.cnt_under_construction_ + bo.unoccupied_count_ <= 1);
+
+					if (bo.new_building_ == BuildingNecessity::kForced) {
 						prio += 150;
-					} else if ((bo.cnt_built_ - bo.unconnected_) == 1 &&
-					           game().get_gametime() > 40 * 60 * 1000 &&
-					           bo.desc->enhancement() != INVALID_INDEX && !mines_.empty()) {
-						prio += 10;
 					} else if (bo.is_shipyard_) {
+						assert (bo.new_building_ == BuildingNecessity::kAllowed);
 						if (!seafaring_economy) {
 							continue;
 						}
-					} else if (bo.output_needed_ == ExtendedBool::kFalse) {
-						continue;
-					} else if ((bo.cnt_built_ - bo.unconnected_) == 0 &&
-					           game().get_gametime() > 40 * 60 * 1000) {
-						prio += kDefaultPrioBoost;
-					} else if ((bo.cnt_built_ - bo.unconnected_) > 1 && bo.current_stats_ > 97) {
-						prio -= kDefaultPrioBoost * (new_buildings_stop_);
-					} else if (new_buildings_stop_) {
-						continue;
+					} else {
+						assert (bo.new_building_ == BuildingNecessity::kNeeded);
 					}
+
+					//overdue priority here
+					prio += bo.primary_priority_;
+
 					// we check separatelly buildings with no inputs and some inputs
 					if (bo.inputs_.empty()) {
 
-						prio += bo.max_needed_preciousness_ + kDefaultPrioBoost;
-
 						if (bo.space_consumer_) {  // need to consider trees nearby
 							prio += 20 - (bf->trees_nearby_ / 3);
 						}
@@ -1828,22 +1946,9 @@
 
 					else if (bo.is_shipyard_) {
 						// for now AI builds only one shipyard
-						if (bf->water_nearby_ > 3 && (bo.total_count() - bo.unconnected_) == 0 &&
+						if (bf->water_nearby_ > 3 && (bo.total_count() - bo.unconnected_count_) == 0 &&
 						    seafaring_economy) {
-							prio += kDefaultPrioBoost + productionsites.size() * 5 + bf->water_nearby_;
-						}
-
-					} else if (!bo.inputs_.empty()) {
-						if ((bo.total_count() - bo.unconnected_ == 0)) {
-							prio += bo.max_needed_preciousness_ + kDefaultPrioBoost;
-						}
-						if ((bo.cnt_built_ - bo.unconnected_) > 0
-							&&
-							is_productionsite_needed(bo.outputs_.size(),
-													bo.current_stats_,
-													PerfEvaluation::kForConstruction)) {
-							prio += bo.max_needed_preciousness_ + kDefaultPrioBoost - 3 +
-							        (bo.current_stats_ - 55) / 8;
+							prio += productionsites.size() * 5 + bf->water_nearby_;
 						}
 					}
 
@@ -1871,19 +1976,14 @@
 					}
 				}
 
-				// consider borders (for medium + big buildings and ones with input)
-				// =>decreasing the score
-				// but only if we have enough free spots to built on
-				// otherwise it will slow down the expansion - small buildings would be preferred
-				if (spots_avail.at(BUILDCAPS_MEDIUM) > 40
-					&&
-					spots_avail.at(BUILDCAPS_BIG) > 20
-					&&
-					(bo.desc->get_size() == 2 ||
-					 bo.desc->get_size() == 3 ||
-					 !bo.inputs_.empty())) {
+				//consider border with exemption of some huts
+				if (! (bo.need_trees_ || bo.need_water_ || bo.is_fisher_)) {
 						prio = recalc_with_border_range(*bf, prio);
-					}
+				} else if (bf->near_border_
+					&&
+					(bo.need_trees_ || bo.need_water_)) {
+						prio /= 2;
+				}
 
 			}  // production sites done
 			else if (bo.type == BuildingObserver::MILITARYSITE) {
@@ -1896,56 +1996,8 @@
 					continue;
 				}
 
-				prio = 0;
-
-				// calculating some sub-scores, some of them are prohibitive
-				// decreasing score if some critical materials are missing
-				// (relevant for medium and big buildings)
-				int32_t prio_for_mat_shortage = 0;
-				prio_for_mat_shortage -= bo.cnt_under_construction_ * bo.build_material_shortage_;
-				if (bf->enemy_nearby_) {
-					prio_for_mat_shortage *= 50;
-				} else {
-					prio_for_mat_shortage *= 1000;  // = prohibitive
-				}
-
-				// decreasing score if other militarysites constuctions
-				// are nearby
-				int32_t prio_for_in_constr = 0;
-				prio_for_in_constr -= 700 * (((static_cast<int32_t>(num_milit_constructionsites) - 2) < 0) ?
-									  0 :
-									  (num_milit_constructionsites - 2)) /
-						(militarysites.size() + 2);
-				if (!bf->enemy_nearby_) {
-					prio_for_in_constr *= 3;
-				}
-
-				// similarly if unmanned militarysites are nearby
-				int32_t prio_for_unmanned_nearby = 0;
-				prio_for_unmanned_nearby -= bf->military_in_constr_nearby_ + bf->military_unstationed_;
-				if (bf->enemy_nearby_) {
-					prio_for_unmanned_nearby *= 20;
-				} else {
-					prio_for_unmanned_nearby *= 1000;
-				}
-
-				// not continuing if score too low
-				if (prio_for_in_constr + prio_for_mat_shortage + prio_for_unmanned_nearby <= -1000) {
-					continue;
-				}
-
-				// is the building suitable for the situation?
-				if (expansion_mode == MilitaryStrategy::kNoNewMilitary) {
-					continue;
-				}
-
-				if (expansion_mode == MilitaryStrategy::kDefenseOnly && !bf->enemy_nearby_) {
-					continue;
-				}
-
-				if (expansion_mode == MilitaryStrategy::kResourcesOrDefense &&
-				    !(bf->unowned_mines_pots_nearby_ || bf->stones_nearby_ || bf->water_nearby_ ||
-				      (bf->distant_water_ && resource_necessity_water_needed_) || bf->enemy_nearby_)) {
+				// postpone medium and big buildings by 4 and 8 minutes
+				if (static_cast<uint32_t>(bo.desc->get_size() - 1) * 4 * 60 * 1000 > gametime) {
 					continue;
 				}
 
@@ -1960,87 +2012,49 @@
 					if (bo.desc->get_size() == 2 && gametime % 2 >= 1) {
 						continue;
 					}
-					if (bo.desc->get_size() == 3 && gametime % 3 >= 1) {
+					if (bo.desc->get_size() == 3 && gametime % 4 >= 1) {
 						continue;
 					};
 				} else {
 					continue;
 				}  // the building is not suitable for situation
 
-
-				// calculating other sub scores
-				// for resources (mines, water, stones)
-				int32_t prio_for_resources = 0;
-				prio_for_resources += 2 * (bf->unowned_mines_pots_nearby_ * resource_necessity_mines_) / 100 +
-						bf->stones_nearby_ + (bf->water_nearby_ * resource_necessity_water_) / 100;
-				// special bonus due to remote water for atlanteans
-				if (resource_necessity_water_needed_) {
-					prio_for_resources += (bf->distant_water_ * resource_necessity_water_) / 100 / 3;
-				}
-				// reducing score if too little unowned land
-				if (bf->unowned_land_nearby_ < 10) {
-					prio_for_resources = prio_for_resources * bf->unowned_land_nearby_ / 10;
-				}
-				if (bf->enemy_nearby_) {  // not important when fighting enemies
-					prio_for_resources /= 5;
-				}
-
-				// for unowned land nearby
-				int32_t prio_for_unowned_land = 0;
-				if (expansion_mode == MilitaryStrategy::kExpansion ||
-				    expansion_mode == MilitaryStrategy::kPushExpansion) {
-					prio_for_unowned_land +=
-					   (bf->unowned_land_nearby_ * resource_necessity_territory_) / 100;
-				}
-
-				// for distance to nearest military sites
-				int32_t prio_for_loneliness = bf->military_loneliness_;
-				if (!bf->enemy_nearby_) {
-					prio_for_loneliness /= 10;
-				}
+				// 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_) * 25;
+					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;
+					prio -= (bf->military_in_constr_nearby_ + bf->military_unstationed_) * 150;
+					prio += (4 - bf->own_military_sites_nearby_()) * 15;
+				}
+				prio += bf->unowned_land_nearby_ * resource_necessity_territory_ / 100;
+				prio += bf->unowned_mines_pots_nearby_;
+				prio += bf->stones_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) prio += 25;
 
 				// 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 *= 15;
-				}
-
-				// additional score if enemy is nearby
-				int32_t prio_for_enemy = 0;
-				if (bf->enemy_nearby_) {
-					prio_for_enemy += 50;
-				}
-
-				// a boost to prevent an expansion halt
-				int32_t local_boost = 0;
-				if (expansion_mode == MilitaryStrategy::kPushExpansion) {
-					local_boost = 200;
-				}
-
-				// summing
-				prio += prio_for_mat_shortage + prio_for_in_constr + prio_for_unmanned_nearby +
-				        prio_for_resources + prio_for_unowned_land + prio_for_loneliness +
-				        prio_for_size + local_boost + prio_for_enemy;
-
-				// special bonus if a portspace is close
-				if (bf->portspace_nearby_ == ExtendedBool::kTrue) {
-					if (num_ports == 0) {
-						prio += 25;
-					} else {
-						prio += 5;
-					}
-				}
-
-				// penalty if we build lesser than possible building
-				if (bo.desc->get_size() < maxsize) {
-					prio = prio - 5;
-				}
-
-				// just generally prevent too many buildings
-				prio -= 40;
-
+					prio_for_size *= 5;
+				}
+				prio += prio_for_size;
+
+				// if place+building is not good enough
+				if (prio <= target_military_score_) {
+					continue;
+				}
 			} else if (bo.type == BuildingObserver::WAREHOUSE) {
 
 				// exclude spots on border
@@ -2074,7 +2088,7 @@
 				// 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 += kDefaultPrioBoost + productionsites.size();
+					prio += productionsites.size();
 					warehouse_needed = true;
 				}
 
@@ -2158,7 +2172,7 @@
 					continue;
 				}
 
-				prio = 4 + kDefaultPrioBoost;
+				prio = 10;
 
 				// take care about borders and enemies
 				if (bf->enemy_nearby_) {
@@ -2166,7 +2180,7 @@
 				}
 
 				if (bf->unowned_land_nearby_) {
-					prio /= 2;
+					prio -= bf->unowned_land_nearby_ / 10;
 				}
 			}
 
@@ -2189,8 +2203,20 @@
 
 			// Prefer road side fields
 			prio += bf->preferred_ ? 5 : 0;
+
 			// don't waste good land for small huts
-			prio -= (maxsize - bo.desc->get_size()) * 20;
+			const bool space_stress =
+				(spots_avail.at(BUILDCAPS_MEDIUM) < 5
+				||
+				spots_avail.at(BUILDCAPS_BIG) < 5);
+
+			if (space_stress && bo.type == BuildingObserver::MILITARYSITE){
+				prio -= (bf->max_buildcap_nearby_ - bo.desc->get_size()) * 3;
+			} else if (space_stress) {
+				prio -= (bf->max_buildcap_nearby_ - bo.desc->get_size()) * 10;
+			} else {
+				prio -= (bf->max_buildcap_nearby_ - bo.desc->get_size()) * 3;
+			}
 
 			// prefer vicinity of ports (with exemption of warehouses)
 			if (bf->port_nearby_ && bo.type == BuildingObserver::MILITARYSITE) {
@@ -2217,11 +2243,7 @@
 			for (uint32_t i = 0; i < buildings_.size() && productionsites.size() > 8; ++i) {
 				BuildingObserver& bo = buildings_.at(i);
 
-				if (!bo.buildable(*player_) || bo.type != BuildingObserver::MINE) {
-					continue;
-				}
-
-				if (bo.prohibited_till_ > gametime) {
+				if (bo.type != BuildingObserver::MINE) {
 					continue;
 				}
 
@@ -2229,27 +2251,13 @@
 					continue;
 				}
 
-				// Don't build another building of this type, if there is already
-				// one that is unoccupied_ at the moment
-				// or under construction
-				if ((bo.cnt_under_construction_ + bo.unoccupied_) > 0) {
-					continue;
-				}
-
-				if (bo.output_needed_ == ExtendedBool::kUnset) {
-					check_building_necessity(bo);
-				}
-
-				if (bo.output_needed_ == ExtendedBool::kFalse
-					&&
-				 	(bo.total_count() - bo.unconnected_) > 0) {
+				assert(bo.new_building_ != BuildingNecessity::kAllowed);
+
+				// skip if a mine is not required
+				if (!(bo.new_building_ == BuildingNecessity::kNeeded ||
+					bo.new_building_ == BuildingNecessity::kForced)) {
 						continue;
-				}
-
-				// if current one(s) are performing badly
-				if ((bo.total_count() - bo.unconnected_) >= 1 && bo.current_stats_ < 50) {
-					continue;
-				}
+					}
 
 				// this is penalty if there are existing mines too close
 				// it is treated as multiplicator for count of near mines
@@ -2265,10 +2273,7 @@
 				uint32_t bonus_score = 0;
 				if ((mines_per_type[bo.mines_].in_construction + mines_per_type[bo.mines_].finished) ==
 				    0) {
-					bonus_score = 15;
-				} else if ((mines_per_type[bo.mines_].in_construction +
-				            mines_per_type[bo.mines_].finished) == 1) {
-					bonus_score = 5;
+					bonus_score = 2 * bo.primary_priority_;
 				}
 
 				// iterating over fields
@@ -2308,7 +2313,7 @@
 					prio += bonus_score;
 
 					// applying max needed
-					prio += bo.max_needed_preciousness_ * 3;
+					prio += bo.primary_priority_;
 
 					// prefer mines in the middle of mine fields of the
 					// same type, so we add a small bonus here
@@ -2336,6 +2341,8 @@
 					// Prefer road side fields
 					prio += mf->preferred_ ? 1 : 0;
 
+					prio += bo.primary_priority_;
+
 					if (prio > proposed_priority) {
 						best_building = &bo;
 						proposed_priority = prio;
@@ -2353,12 +2360,21 @@
 		return false;
 	}
 
+	if (best_building->type == BuildingObserver::MILITARYSITE) {
+		target_military_score_ = proposed_priority;
+		player_->set_ai_data(target_military_score_, kTargetMilit);
+	}
+
+
 	// send the command to construct a new building
 	game().send_player_build(player_number(), proposed_coords, best_building->id);
 	BlockedField blocked(
 	   game().map().get_fcoords(proposed_coords), game().get_gametime() + 120000);  // two minutes
 	blocked_fields.push_back(blocked);
 
+	//resetting new_building_overdue_
+	best_building->new_building_overdue_ = 0;
+
 	// 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
@@ -2583,27 +2599,6 @@
 	return false;
 }
 
-// is productionsite needed
-// used for building new buildings or dismantle of old, intended for ones
-// that have inputs
-bool DefaultAI::is_productionsite_needed(int32_t outputs,
-										int32_t performance,
-										PerfEvaluation purpose) {
-	int32_t expected_performance = 0;
-	if (outputs > 0) {
-		expected_performance = 10 + 70 / outputs;
-	} else {
-		expected_performance = 80;
-	}
-	if (purpose == PerfEvaluation::kForDismantle) {
-		expected_performance /= 2;
-	}
-	if (performance > expected_performance) {
-		return true;
-	}
-	return false;
-}
-
 // trying to connect the flag to another one, be it from own economy
 // or other economy
 bool DefaultAI::create_shortcut_road(const Flag& flag,
@@ -2989,8 +2984,10 @@
 
 	const BuildingIndex enhancement = site.site->descr().enhancement();
 	if (connected_to_wh && enhancement != INVALID_INDEX &&
-	    ((site.bo->cnt_built_ - site.bo->unoccupied_) > 1 ||
-	    site.bo->upgrade_substitutes_)) {
+		(site.bo->cnt_built_ - site.bo->unoccupied_count_ > 1 ||
+		((site.bo->upgrade_substitutes_ || site.bo->upgrade_extends_) &&
+	    gametime > 45 * 60 * 1000 &&
+	    gametime > site.built_time_ + 20 * 60 * 1000))) {
 
 		BuildingIndex enbld = INVALID_INDEX;  // to get rid of this
 
@@ -3003,13 +3000,13 @@
 			BuildingObserver* bestbld = nullptr;
 
 			if (gametime - en_bo.construction_decision_time_ >= kBuildingMinInterval &&
-			    (en_bo.cnt_under_construction_ + en_bo.unoccupied_) == 0) {
+			    (en_bo.cnt_under_construction_ + en_bo.unoccupied_count_) == 0) {
 
 				// don't upgrade without workers
 				if (site.site->has_workers(enhancement, game())) {
 
 					// forcing first upgrade
-					if (en_bo.cnt_built_ == 0) {
+					if (en_bo.total_count() == 0) {
 						enbld = enhancement;
 						bestbld = &en_bo;
 					}
@@ -3051,6 +3048,16 @@
 	// Lumberjack / Woodcutter handling
 	if (site.bo->need_trees_) {
 
+		const uint32_t remaining_trees =
+		   map.find_immovables(Area<FCoords>(map.get_fcoords(site.site->get_position()), radius),
+		                       nullptr,
+		                       FindImmovableAttribute(MapObjectDescr::get_attribute_id("tree")));
+
+		//generally, trees_around_cutters_ = remaining_trees + 9 * trees_around_cutters_
+		// but keep in mind,  that trees_around_cutters_ is multiplied by 10
+		trees_around_cutters_ = (remaining_trees * 10 + 9 * trees_around_cutters_) / 10;
+		player_->set_ai_data(trees_around_cutters_, kTreesAround);
+
 		// Do not destruct the last few lumberjacks
 		if (site.bo->cnt_built_ <= site.bo->cnt_target_) {
 			return false;
@@ -3060,10 +3067,7 @@
 			return false;
 		}
 
-		const uint32_t remaining_trees =
-		   map.find_immovables(Area<FCoords>(map.get_fcoords(site.site->get_position()), radius),
-		                       nullptr,
-		                       FindImmovableAttribute(MapObjectDescr::get_attribute_id("tree")));
+
 
 		// do not dismantle if there are some trees remaining
 		if (remaining_trees > 5) {
@@ -3182,14 +3186,14 @@
 	    site.bo->space_consumer_ && !site.bo->plants_trees_) {
 
 		// if we have more buildings then target
-		if ((site.bo->cnt_built_ - site.bo->unconnected_) > site.bo->cnt_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.site->get_statistics_percent() < 30 &&
-			    site.bo->stocklevel_ > 100) {  // production stats == 0%
+			    site.bo->stocklevel_ > 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) {
@@ -3218,15 +3222,17 @@
 
 	// buildings with inputs_, checking if we can a dismantle some due to low performance
 	if (!site.bo->inputs_.empty() &&
-		(site.bo->cnt_built_ - site.bo->unoccupied_) >= 3 &&
+		(site.bo->cnt_built_ - site.bo->unoccupied_count_) >= 3 &&
 	    site.site->can_start_working() &&
-	    !is_productionsite_needed(site.bo->outputs_.size(),
-	    							site.site->get_statistics_percent(),
-	    							PerfEvaluation::kForDismantle) &&
-	    site.bo->current_stats_ < 30 &&              // overall statistics
+	    check_building_necessity(*site.bo, PerfEvaluation::kForDismantle, gametime)
+	    	== BuildingNecessity::kNotNeeded &&
+	    gametime - site.bo->last_dismantle_time_ > 5 * 60 * 1000 &&
+
+	    site.bo->current_stats_ > site.site->get_statistics_percent() &&              // underperformer
 	    (game().get_gametime() - site.unoccupied_till_) > 10 * 60 * 1000) {
 
 		site.bo->last_dismantle_time_ = game().get_gametime();
+
 		flags_to_be_removed.push_back(site.site->base_flag().get_position());
 		if (connected_to_wh) {
 			game().send_player_dismantle(*site.site);
@@ -3238,7 +3244,6 @@
 
 	// 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 &&
@@ -3258,6 +3263,11 @@
 	// stop/start them based on stock avaiable
 	if (site.bo->production_hint_ >= 0) {
 
+		if (!site.bo->plants_trees_){
+			//other supporting sites, like fish breeders, gamekeepers are not dismantled at all
+			return false;
+		}
+
 		// dismantling the rangers hut, but only if we have them above a target
 		if (wood_policy_ == WoodPolicy::kDismantleRangers && site.bo->cnt_built_ > site.bo->cnt_target_) {
 
@@ -3294,8 +3304,7 @@
 				game().send_player_start_stop_building(*site.site);
 			}
 		// if not enough trees nearby, we can start them if required
-		} else if ((wood_policy_ == WoodPolicy::kStartRangers ||
-		 	wood_policy_ == WoodPolicy::kBuildRangers)
+		} else if ((wood_policy_ == WoodPolicy::kAllowRangers)
 		 	&&
 		 	site.site->is_stopped()) {
 				game().send_player_start_stop_building(*site.site);
@@ -3325,7 +3334,6 @@
 	player_ = game().get_player(player_number());
 	uint16_t ports_count = 0;
 	uint16_t shipyards_count = 0;
-	uint16_t working_shipyards_count = 0;
 	uint16_t expeditions_in_prep = 0;
 	uint16_t expeditions_in_progress = 0;
 	bool idle_shipyard_stocked = false;
@@ -3346,9 +3354,7 @@
 	for (const ProductionSiteObserver& ps_obs : productionsites) {
 		if (ps_obs.bo->is_shipyard_) {
 			shipyards_count += 1;
-			if (!ps_obs.site->is_stopped()) {
-				working_shipyards_count += 1;
-			}
+
 			// counting stocks
 			uint8_t stocked_wares = 0;
 			std::vector<WaresQueue*> const warequeues = ps_obs.site->warequeues();
@@ -3531,13 +3537,13 @@
 		return true;
 	}
 
-	// doing nothing when failed count is too low
-	if (site.no_resources_count < 4) {
+	// to avoid problems with uint underflow, we discourage considerations below
+	if (gametime < 10 * 60 * 1000) {
 		return false;
 	}
 
-	// dismantling when the failed count is too high
-	if (site.no_resources_count > 12) {
+	// if the mine had not been upgraded within 8 min, it is dismantled
+	if (site.no_resources_since_ < gametime - 8 * 60 * 1000) {
 		flags_to_be_removed.push_back(site.site->base_flag().get_position());
 		if (connected_to_wh) {
 			game().send_player_dismantle(*site.site);
@@ -3548,11 +3554,8 @@
 		return true;
 	}
 
-	// is output needed (compare stocked materials vs target values)
-	check_building_necessity(*site.bo);
-
-	// if we have enough of mined materials on stock - do not upgrade (yet)
-	if (site.bo->output_needed_ == ExtendedBool::kFalse) {
+	// if we dont need this building
+	if (site.bo->max_needed_preciousness_ == 0) {
 		return false;
 	}
 
@@ -3572,13 +3575,13 @@
 
 	bool changed = false;
 	if (player_->is_building_type_allowed(enhancement)) {
-		// first exclude possibility there are enhancements in construction or unoccupied_
+		// first exclude possibility there are enhancements in construction or unoccupied_count_
 		const BuildingDescr& bld = *tribe_->get_building_descr(enhancement);
 		BuildingObserver& en_bo = get_building_observer(bld.name().c_str());
 
 		// if it is too soon for enhancement and making sure there are no unoccupied mines
 		if (gametime - en_bo.construction_decision_time_ >= kBuildingMinInterval &&
-		    en_bo.unoccupied_ + en_bo.cnt_under_construction_ == 0) {
+		    en_bo.unoccupied_count_ + en_bo.cnt_under_construction_ == 0) {
 
 			// now verify that there are enough workers
 			if (site.site->has_workers(enhancement, game())) {  // enhancing
@@ -3608,12 +3611,18 @@
 	return count;
 }
 
-// goes over all outputs of a building and compare stocked material with
-// target values. The result is yes/no and some scores
-void DefaultAI::check_building_necessity(BuildingObserver& bo) {
-	// iterate over outputs of building, counts warehoused stock
-	// and deciding if enough
-	bo.max_preciousness = 0;
+// this receives an building observer and have to decide if new/one of
+// current buildings of this type is needed
+// This is core of construct_building() function
+// This is run once when construct_building() is run, or when considering
+// dismantle
+BuildingNecessity DefaultAI::check_building_necessity(BuildingObserver& bo,
+										const PerfEvaluation purpose,
+										const uint32_t gametime){
+
+	// 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) {
@@ -3631,17 +3640,156 @@
 			}
 		}
 
-		if (bo.max_preciousness < preciousness) {
-			bo.max_preciousness = preciousness;
+		if (bo.max_preciousness_ < preciousness) {
+			bo.max_preciousness_ = preciousness;
 		}
 	}
 
-	// here we decide if the building is needed
+	// positive max_needed_preciousness_ says a building type is needed
+	// here we increase of reset the counter
+	// the counter is added to score when considering new building
 	if (bo.max_needed_preciousness_ > 0) {
-		bo.output_needed_ = ExtendedBool::kTrue;
-	} else {
-		bo.output_needed_ = ExtendedBool::kFalse;
-	}
+		bo.new_building_overdue_ += 1;
+	} else {
+		bo.new_building_overdue_ = 0;
+	}
+
+	//first deal with construction of new sites
+	if (purpose == PerfEvaluation::kForConstruction) {
+		if (bo.forced_after_ < gametime && bo.total_count() == 0) {
+			return BuildingNecessity::kForced;
+		} else if (bo.prohibited_till_ > gametime) {
+			return BuildingNecessity::kNotNeeded;
+		} else if (bo.is_hunter_ || bo.is_fisher_){
+
+			if (bo.max_needed_preciousness_ == 0) {
+				return BuildingNecessity::kNotNeeded;
+			} else if (bo.cnt_under_construction_ + bo.unoccupied_count_ > 0) {
+				return BuildingNecessity::kNotNeeded;
+			} else if (bo.total_count() > 0 && new_buildings_stop_) {
+				return BuildingNecessity::kNotNeeded;
+			} else {
+				return BuildingNecessity::kNeeded;
+			}
+		} else if (bo.need_trees_) {
+			if (bo.total_count() > 1 && (bo.cnt_under_construction_ + bo.unoccupied_count_ > 0)){
+				return BuildingNecessity::kNotNeeded;
+			}
+			bo.cnt_target_ =
+					   3 + static_cast<int32_t>(mines_.size() + productionsites.size()) / 15;
+			if (bo.total_count() < bo.cnt_target_) {
+				return BuildingNecessity::kNeeded;
+			} else {
+				return BuildingNecessity::kAllowed;
+			}
+		} else if (bo.plants_trees_) {
+
+			bo.cnt_target_ =
+				   1 +
+				   static_cast<int32_t>(mines_.size() + productionsites.size()) / 15;
+			if (wood_policy_ != WoodPolicy::kAllowRangers) {
+				return BuildingNecessity::kNotNeeded;
+			}
+			// 150 corresponds to 15 trees
+			if (trees_around_cutters_ < 150) {
+				bo.cnt_target_ *= 5;
+			}
+			if (bo.total_count() > 1 && (bo.cnt_under_construction_ + bo.unoccupied_count_ > 0)) {
+				return BuildingNecessity::kNotNeeded;
+			} else if (bo.total_count() > bo.cnt_target_) {
+				return BuildingNecessity::kNotNeeded;
+			}
+			return BuildingNecessity::kNeeded;
+		} else if (bo.need_stones_ && bo.cnt_under_construction_ + bo.unoccupied_count_ == 0) {
+			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::kNotNeeded;
+		} else if (bo.cnt_under_construction_ + bo.unoccupied_count_ > 0 && gametime < 30 * 60 * 1000) {
+			return BuildingNecessity::kNotNeeded;
+		} else if (bo.cnt_under_construction_ + bo.unoccupied_count_ > 1) {
+			return BuildingNecessity::kNotNeeded; // for preciousness>=10 and after 30 min
+		} else if (bo.type == BuildingObserver::MINE) {
+			if ((mines_per_type[bo.mines_].in_construction + mines_per_type[bo.mines_].finished) == 0) {
+				return BuildingNecessity::kNeeded;
+			} else if (((mines_per_type[bo.mines_].in_construction + mines_per_type[bo.mines_].finished)
+				==
+				1) && bo.built_mat_producer_) {
+					return BuildingNecessity::kNeeded;
+			} else if (new_buildings_stop_) {
+				return BuildingNecessity::kNotNeeded;
+			} else if (bo.max_needed_preciousness_ > 0
+				&&
+				(bo.total_count() - bo.unconnected_count_) >= 1 && bo.current_stats_ < 40) {
+					return BuildingNecessity::kNotNeeded;
+			} else if (bo.max_needed_preciousness_ > 0) {
+				return BuildingNecessity::kNeeded;
+			} else {
+				return BuildingNecessity::kNotNeeded;
+			}
+		// new_buildings_stop_ is obbeyed if sooner than 25 min of game
+		} else if (new_buildings_stop_ && (gametime < 25 * 60 * 1000)) {
+			return BuildingNecessity::kNotNeeded;
+		// or if there is one building of a type (with exemption of building material producers)
+		} else if (new_buildings_stop_ && !bo.built_mat_producer_ && bo.total_count() > 0) {
+			return BuildingNecessity::kNotNeeded;
+		//and for building material producers if there are two of king
+		} else if (new_buildings_stop_ && bo.built_mat_producer_ && bo.total_count() > 1) {
+			return BuildingNecessity::kNotNeeded;
+		} if (bo.max_needed_preciousness_ > 0) {
+
+			// couple of asserts to make sure no unexpected situation gets here
+			if (new_buildings_stop_) {
+				assert (gametime >= 25 * 60 * 1000);
+				if (bo.built_mat_producer_) {
+					assert(bo.total_count() <= 1);
+				} else {
+					assert(bo.total_count() == 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.built_mat_producer_);
+				assert (gametime >= 25 * 60 * 1000);
+			}
+
+			// First 'if' is special support for hardwood producers (to have 2 of them)
+			if (bo.built_mat_producer_ && bo.total_count() <= 1 && bo.current_stats_ > 10) {
+				return BuildingNecessity::kNeeded;
+			} else if (bo.inputs_.empty()) {
+				return BuildingNecessity::kNeeded;
+			} else if (bo.total_count() == 0) {
+				return BuildingNecessity::kNeeded;
+			} else if (bo.current_stats_ > 10 + 70 / bo.outputs_.size()) {
+				return BuildingNecessity::kNeeded;
+			} else {
+				return BuildingNecessity::kNotNeeded;
+			}
+		} else if (bo.is_shipyard_) {
+			return BuildingNecessity::kAllowed;
+		} else  if (bo.max_needed_preciousness_ == 0) {
+			return BuildingNecessity::kNotNeeded;
+		} else {
+			return BuildingNecessity::kNotNeeded;
+		}
+	} else if (purpose == PerfEvaluation::kForDismantle) { // now for dismantling
+		//never dismantl last building (a care should be taken elsewhere)
+		assert (bo.total_count() > 0);
+		if (bo.total_count() == 1) {
+			return BuildingNecessity::kNeeded;
+		} else if (bo.max_preciousness_ >= 10 && bo.total_count() == 2) {
+			return BuildingNecessity::kNeeded;
+		} else if (bo.current_stats_ > (10 + 70 / bo.outputs_.size()) / 2) {
+			return BuildingNecessity::kNeeded;
+		} else {
+			return BuildingNecessity::kNotNeeded;
+		}
+	} else {
+		//impossible but still
+		assert(false);
+	}
+
 }
 
 // counts produced output on stock
@@ -3899,7 +4047,7 @@
 				score += (bf.area_military_capacity_ > 6);
 				score += (bf.area_military_capacity_ > 22);
 				score += (bf.area_military_presence_ > 4);
-				score += (bf.military_loneliness_ < 180);
+				score += (bf.military_loneliness_ < (180 + ai_personality_military_loneliness_));
 				score += (bf.military_stationed_ > 2);
 				score -= size_penalty;
 				score += ((bf.unowned_land_nearby_ + allyOwnedFields) < 10);
@@ -3979,100 +4127,24 @@
 		return prio;
 	}
 
-	// in unowned teritory, decreasing to 2/3
-	if (bf.unowned_land_nearby_ > 15) {
-		prio *= 2;
-		prio /= 3;
-	}
-
-	// to preserve positive score
-	if (prio == 0) {
-		prio = 1;
-	}
-
-	// Further decrease the score if enemy nearby
+	if (bf.enemy_nearby_ || bf.near_border_) {
+		prio /= 2;
+	}
+
+	// if unowned teritory nearby
+	prio -= bf.unowned_land_nearby_ / 4;
+
+	// further decrease the score if enemy nearby
 	if (bf.enemy_nearby_) {
-		prio /= 2;
-	}
-
-	return prio;
-}
-
-/**
- * calculates how much a productionsite of type \arg bo is needed inside it's
- * economy. \arg prio is initial value for this calculation
- *
- * \returns the calculated priority
- */
-int32_t DefaultAI::calculate_need_for_ps(BuildingObserver& bo, int32_t prio) {
-	// some randomness to avoid that defaultAI is building always
-	// the same (always == another game but same map with
-	// defaultAI on same coords)
-	prio += time(nullptr) % 3 - 1;
-
-	// check if current economy can supply enough material for
-	// production.
-	for (uint32_t k = 0; k < bo.inputs_.size(); ++k) {
-		prio += 2 * wares.at(bo.inputs_.at(k)).producers_;
-		prio -= wares.at(bo.inputs_.at(k)).consumers_;
-	}
-
-	if (bo.inputs_.empty()) {
-		prio += 4;
-	}
-
-	int32_t output_prio = 0;
-
-	for (uint32_t k = 0; k < bo.outputs_.size(); ++k) {
-		WareObserver& wo = wares.at(bo.outputs_.at(k));
-
-		if (wo.consumers_ > 0) {
-			output_prio += wo.preciousness_;
-			output_prio += wo.consumers_ * 2;
-			output_prio -= wo.producers_ * 2;
-
-			if (bo.total_count() == 0) {
-				output_prio += 10;  // add a big bonus
-			}
-		}
-	}
-
-	if (bo.outputs_.size() > 1) {
-		output_prio =
-		   static_cast<int32_t>(ceil(output_prio / sqrt(static_cast<double>(bo.outputs_.size()))));
-	}
-
-	prio += 2 * output_prio;
-
-	// If building consumes some wares, multiply with current statistics of all
-	// other buildings of this type to avoid constructing buildings where already
-	// some are running on low resources.
-	// Else at least add a part of the stats t the calculation.
-	if (!bo.inputs_.empty()) {
-		prio *= bo.current_stats_;
-		prio /= 100;
-	} else {
-		prio = ((prio * bo.current_stats_) / 100) + (prio / 2);
-	}
-
-	return prio;
-}
-
-void DefaultAI::consider_productionsite_influence(BuildableField& field,
-                                                  Coords coords,
-                                                  const BuildingObserver& bo) {
-	if (bo.space_consumer_ && !bo.plants_trees_ &&
-	    game().map().calc_distance(coords, field.coords) < 8) {
-		++field.space_consumers_nearby_;
-	}
-
-	for (size_t i = 0; i < bo.inputs_.size(); ++i) {
-		++field.consumers_nearby_.at(bo.inputs_.at(i));
-	}
-
-	for (size_t i = 0; i < bo.outputs_.size(); ++i) {
-		++field.producers_nearby_.at(bo.outputs_.at(i));
-	}
+		prio -= 10;
+	}
+
+	// and if close (up to 2 fields away) from border
+	if (bf.near_border_) {
+		prio -= 10;
+	}
+
+	return prio;
 }
 
 /// \returns the economy observer containing \arg economy
@@ -4163,10 +4235,14 @@
 // this is called when a mine reports "out of resources"
 void DefaultAI::out_of_resources_site(const ProductionSite& site) {
 
+	const uint32_t gametime = game().get_gametime();
+
 	// we must identify which mine matches the productionsite a note reffers to
 	for (std::list<ProductionSiteObserver>::iterator i = mines_.begin(); i != mines_.end(); ++i)
 		if (i->site == &site) {
-			i->no_resources_count += 1;
+			if (i->no_resources_since_ > gametime) {
+				i->no_resources_since_ = gametime;
+			}
 			break;
 		}
 }
@@ -4385,8 +4461,9 @@
 		}
 
 		// decreasing colony_scan_area_
-		if (colony_scan_area_ > 15 && gametime % 10 == 0) {
+		if (colony_scan_area_ > 10 && gametime % 10 == 0) {
 			colony_scan_area_ -= 1;
+			player_->set_ai_data(colony_scan_area_, kColonyScan);
 		}
 	}
 
@@ -4480,10 +4557,12 @@
 			productionsites.push_back(ProductionSiteObserver());
 			productionsites.back().site = &dynamic_cast<ProductionSite&>(b);
 			productionsites.back().bo = &bo;
+			productionsites.back().bo->new_building_overdue_ = 0;
 			productionsites.back().built_time_ = game().get_gametime();
 			productionsites.back().unoccupied_till_ = game().get_gametime();
 			productionsites.back().stats_zero_ = 0;
-			productionsites.back().no_resources_count = 0;
+			productionsites.back().no_resources_since_ =  std::numeric_limits<uint32_t>::max();
+			productionsites.back().bo->unoccupied_count_ += 1;
 			if (bo.is_shipyard_) {
 				marineTaskQueue_.push_back(kStopShipyard);
 				marineTaskQueue_.push_back(kReprioritize);
@@ -4499,6 +4578,8 @@
 			mines_.back().site = &dynamic_cast<ProductionSite&>(b);
 			mines_.back().bo = &bo;
 			mines_.back().built_time_ = game().get_gametime();
+			mines_.back().no_resources_since_ =  std::numeric_limits<uint32_t>::max();
+			mines_.back().bo->unoccupied_count_ += 1;
 
 			for (uint32_t i = 0; i < bo.outputs_.size(); ++i)
 				++wares.at(bo.outputs_.at(i)).producers_;
@@ -4580,6 +4661,18 @@
 	} else {
 		--bo.cnt_built_;
 
+		// we are not able to reliable identify if lost building is counted in
+		// unconnected or unoccupied count, but we must adjust the value to
+		// avoid inconsistency
+		const uint32_t cnt_built = bo.cnt_built_;
+		if (bo.unconnected_count_ > cnt_built) {
+			bo.unconnected_count_ = cnt_built;
+		}
+		if (bo.unoccupied_count_ > cnt_built) {
+			bo.unoccupied_count_ = cnt_built;
+		}
+
+
 		if (bo.type == BuildingObserver::PRODUCTIONSITE) {
 
 			for (std::list<ProductionSiteObserver>::iterator i = productionsites.begin();
@@ -4597,6 +4690,7 @@
 			for (uint32_t i = 0; i < bo.inputs_.size(); ++i) {
 				--wares.at(bo.inputs_.at(i)).consumers_;
 			}
+
 		} else if (bo.type == BuildingObserver::MINE) {
 			for (std::list<ProductionSiteObserver>::iterator i = mines_.begin(); i != mines_.end();
 			     ++i) {
@@ -4787,6 +4881,21 @@
 		treshold_ratio = 120;
 	}
 
+	// let 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 lanst 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 += ai_personality_attack_margin_;
+	}
+
 	uint32_t my_power = 0;
 	try {
 		my_power = genstats.at(pn - 1).miltary_strength.back();
@@ -5098,7 +5207,11 @@
 	}
 
 	game().send_player_enemyflagaction(*flag, player_number(), attackers);
+
+	last_attack_time_ = gametime;
+	player_->set_ai_data(last_attack_time_, kLastAttack);
 	last_attacked_player_ = flag->owner().player_number();
+	player_->set_ai_data(static_cast<int16_t>(last_attacked_player_), kAttacker);
 
 	return true;
 }
@@ -5160,6 +5273,24 @@
 	return DueTask;
 }
 
+// following two functions count mines of the same type (same output,
+// all levels)
+uint32_t DefaultAI::mines_in_constr() const {
+	uint32_t count = 0;
+	for (const std::pair<const int, MineTypesObserver> m : mines_per_type) {
+		count += m.second.in_construction;
+	}
+	return count;
+}
+uint32_t DefaultAI::mines_built() const{
+	uint32_t count = 0;
+	for (const std::pair<const int, MineTypesObserver> m : mines_per_type) {
+		count += m.second.finished;
+	}
+	return count;
+}
+
+
 // This prints some basic statistics during a game to the command line -
 // missing materials and counts of different types of buildings.
 // The main purpose of this is when a game creator needs to finetune a map

=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h	2015-09-04 06:16:58 +0000
+++ src/ai/defaultai.h	2015-09-21 18:31:31 +0000
@@ -82,7 +82,7 @@
 	};
 
 	enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers, kEnemy};
-	enum class WoodPolicy : uint8_t {kDismantleRangers, kStopRangers, kStartRangers, kBuildRangers};
+	enum class WoodPolicy : uint8_t {kDismantleRangers, kStopRangers, kAllowRangers};
 	enum class NewShip : uint8_t {kBuilt, kFoundOnLoad};
 	enum class PerfEvaluation : uint8_t {kForConstruction, kForDismantle};
 	enum class ScheduleTasks : uint8_t {
@@ -105,13 +105,6 @@
 		kCountMilitaryVacant,
 		kCheckEnemySites
 	};
-	enum class MilitaryStrategy : uint8_t {
-		kNoNewMilitary,
-		kDefenseOnly,
-		kResourcesOrDefense,
-		kExpansion,
-		kPushExpansion
-	};
 	enum class Tribes : uint8_t {
 		kNone,
 		kBarbarians,
@@ -119,6 +112,7 @@
 		kEmpire
 	};
 
+
 	/// Implementation for Aggressive
 	struct AggressiveImpl : public ComputerPlayer::Implementation {
 		AggressiveImpl() {
@@ -169,7 +163,8 @@
 
 	void update_productionsite_stats(uint32_t);
 
-	void check_building_necessity(BuildingObserver& bo);
+	Widelands::BuildingNecessity check_building_necessity
+		(BuildingObserver& bo, PerfEvaluation purpose, uint32_t);
 
 	ScheduleTasks get_oldest_task(uint32_t);
 
@@ -225,10 +220,7 @@
 	                             const WalkSearch type);
 
 	int32_t recalc_with_border_range(const BuildableField&, int32_t);
-	int32_t calculate_need_for_ps(BuildingObserver&, int32_t);
 
-	void
-	consider_productionsite_influence(BuildableField&, Widelands::Coords, const BuildingObserver&);
 	// considering wood, stones, mines, water, fishes for candidate for colonization (new port)
 	uint8_t spot_scoring(Widelands::Coords candidate_spot);
 
@@ -243,9 +235,6 @@
 	void expedition_management(ShipObserver&);
 	void out_of_resources_site(const Widelands::ProductionSite&);
 	void soldier_trained(const Widelands::TrainingSite&);
-	bool is_productionsite_needed(int32_t outputs,
-										int32_t performance,
-										PerfEvaluation purpose);
 
 	bool check_supply(const BuildingObserver&);
 
@@ -266,13 +255,19 @@
 	std::vector<BuildingObserver> buildings_;
 	uint32_t num_constructionsites_;
 	uint32_t num_milit_constructionsites;
+	uint32_t num_mine_constructionsites;
 	uint32_t num_prod_constructionsites;
 	uint32_t num_ports;
 
 	uint16_t last_attacked_player_;
+	uint32_t last_attack_time_;
 	// check ms in this interval - will auto-adjust
 	uint32_t enemysites_check_delay_;
 
+	// helping scores for building new military sites
+	int32_t target_military_score_;
+	int32_t least_military_score_;
+
 	WoodPolicy wood_policy_;
 
 	std::list<Widelands::FCoords> unusable_fields;
@@ -294,6 +289,9 @@
 	std::map<uint32_t, EnemySiteObserver> enemy_sites;
 	// it will map mined material to observer
 	std::map<int32_t, MineTypesObserver> mines_per_type;
+	// returns count of mines of the same type (output)
+	uint32_t mines_in_constr() const;
+	uint32_t mines_built() const;
 
 	std::vector<WareObserver> wares;
 
@@ -315,6 +313,9 @@
 	int32_t resource_necessity_water_;
 	bool resource_necessity_water_needed_;  // unless atlanteans
 
+	// average count of trees around cutters
+	uint32_t trees_around_cutters_;
+
 	uint16_t unstationed_milit_buildings_;  // counts empty military buildings (ones where no soldier
 	                                        // is belogning to)
 	uint16_t military_last_dismantle_;
@@ -337,12 +338,14 @@
 	// the purpose is to print out a warning that the game is pacing too fast
 	int32_t scheduler_delay_counter_;
 
+	int16_t ai_personality_military_loneliness_;
+	uint32_t ai_personality_attack_margin_;
+	int32_t ai_personality_wood_difference_;
+	uint32_t ai_productionsites_ratio_;
+
 	// 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 =
 		{"ax", "lance", "armor", "helm", "lance", "trident", "tabard", "shield", "mask"};
-	// some buildings can be upgraded even when they are only one
-	// now only microbrewery get this special treatment
-	const char* preferred_upgrade[1] = {"micro-brewery"};
 
 	enum {kReprioritize, kStopShipyard, kStapShipyard};
 

=== modified file 'src/game_io/game_player_info_packet.cc'
--- src/game_io/game_player_info_packet.cc	2014-09-20 09:37:47 +0000
+++ src/game_io/game_player_info_packet.cc	2015-09-21 18:31:31 +0000
@@ -81,6 +81,11 @@
 					player.m_msites_defeated     = fr.unsigned_32();
 					player.m_civil_blds_lost     = fr.unsigned_32();
 					player.m_civil_blds_defeated = fr.unsigned_32();
+					for (int32_t ai_pos=0;ai_pos<kAIDataSize;ai_pos++) {
+						player.m_ai_data_int32[ai_pos] = fr.signed_32();
+						player.m_ai_data_uint32[ai_pos] = fr.unsigned_32();
+						player.m_ai_data_int16[ai_pos] = fr.unsigned_16();
+					}
 				}
 			}
 
@@ -133,6 +138,11 @@
 		fw.unsigned_32(plr->msites_defeated    ());
 		fw.unsigned_32(plr->civil_blds_lost    ());
 		fw.unsigned_32(plr->civil_blds_defeated());
+		for (int32_t ai_pos=0;ai_pos<kAIDataSize;ai_pos++) {
+			fw.signed_32(plr->m_ai_data_int32[ai_pos] );
+			fw.unsigned_32(plr->m_ai_data_uint32[ai_pos]);
+			fw.unsigned_16(plr->m_ai_data_int16[ai_pos]);
+		}
 	} else
 		fw.unsigned_8(0); //  Player is NOT in game.
 

=== modified file 'src/logic/player.cc'
--- src/logic/player.cc	2015-07-29 07:47:10 +0000
+++ src/logic/player.cc	2015-09-21 18:31:31 +0000
@@ -166,7 +166,11 @@
 	m_current_consumed_statistics(tribe_descr.get_nrwares    ()),
 	m_ware_productions  (tribe_descr.get_nrwares    ()),
 	m_ware_consumptions  (tribe_descr.get_nrwares    ()),
-	m_ware_stocks  (tribe_descr.get_nrwares          ())
+	m_ware_stocks  (tribe_descr.get_nrwares          ()),
+	m_ai_data_int32          (),
+	m_ai_data_uint32         (),
+	m_ai_data_int16          ()
+
 {
 	set_name(name);
 
@@ -1283,17 +1287,48 @@
 			 building_position.x, building_position.y);
 	}
 }
+/**
+ * Functions used by AI to save/read AI data stored in Player class.
+ */
 
-void Player::set_ai(const std::string & ai)
-{
+void Player::set_ai(const std::string & ai) {
 	m_ai = ai;
 }
 
-const std::string & Player::get_ai() const
-{
+const std::string & Player::get_ai() const {
 	return m_ai;
 }
 
+void Player::set_ai_data(int32_t value, uint32_t position) {
+	assert(position < kAIDataSize);
+	m_ai_data_int32[position] = value;
+}
+
+void Player::set_ai_data(uint32_t value, uint32_t position) {
+	assert(position < kAIDataSize);
+	m_ai_data_uint32[position] = value;
+}
+
+void Player::set_ai_data(int16_t value, uint32_t position) {
+	assert(position < kAIDataSize);
+	m_ai_data_int16[position] = value;
+}
+
+int32_t Player::get_ai_data_int32(uint32_t position) {
+	assert(position < kAIDataSize);
+	return m_ai_data_int32[position];
+}
+
+uint32_t Player::get_ai_data_uint32(uint32_t position) {
+	assert(position < kAIDataSize);
+	return m_ai_data_uint32[position];
+}
+
+int16_t Player::get_ai_data_int16(uint32_t position) {
+	assert(position < kAIDataSize);
+	return m_ai_data_int16[position];
+}
+
 /**
  * Read statistics data from a file.
  *

=== modified file 'src/logic/player.h'
--- src/logic/player.h	2015-02-08 18:16:41 +0000
+++ src/logic/player.h	2015-09-21 18:31:31 +0000
@@ -33,6 +33,11 @@
 #include "logic/warehouse.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 = 6;
+
 class Node;
 namespace Widelands {
 
@@ -517,6 +522,15 @@
 		m_further_initializations .push_back(init);
 	}
 
+	// set of functions to be used by AI to save and read own data within this class
+	void set_ai_data(int32_t value, uint32_t position);
+	void set_ai_data(uint32_t value, uint32_t position);
+	void set_ai_data(int16_t value, uint32_t position);
+	int32_t get_ai_data_int32(uint32_t position);
+	uint32_t get_ai_data_uint32(uint32_t position);
+	int16_t get_ai_data_int16(uint32_t position);
+
+
 private:
 	BuildingStatsVector* get_mutable_building_statistics(const BuildingIndex& i);
 	void update_building_statistics(Building &, NoteImmovable::Ownership ownership);
@@ -586,6 +600,15 @@
 	 */
 	std::vector< std::vector<uint32_t> > m_ware_stocks;
 
+
+	/**
+	 * AI internal data. These will be ignored by human player
+	 * AI is managing the content of these arrays
+	 */
+	int32_t m_ai_data_int32 [kAIDataSize];
+	uint32_t m_ai_data_uint32 [kAIDataSize];
+	int16_t m_ai_data_int16 [kAIDataSize];
+
 	PlayerBuildingStats m_building_stats;
 
 	DISALLOW_COPY_AND_ASSIGN(Player);

=== modified file 'tribes/atlanteans/planks/conf'
--- tribes/atlanteans/planks/conf	2014-07-28 14:04:36 +0000
+++ tribes/atlanteans/planks/conf	2015-09-21 18:31:31 +0000
@@ -1,7 +1,7 @@
 help=_Planks are an important building material of the Atlanteans. They are produced out of logs by the sawmill. The weapon smithy and the shipyard also use planks to produce the different tridents and mighty ships.
 
 default_target_quantity=40
-preciousness=7
+preciousness=10
 
 [idle]
 pics=idle.png

=== modified file 'tribes/atlanteans/quarry/conf'
--- tribes/atlanteans/quarry/conf	2015-07-26 10:59:28 +0000
+++ tribes/atlanteans/quarry/conf	2015-09-21 18:31:31 +0000
@@ -2,7 +2,7 @@
 output=stone
 
 [aihints]
-forced_after=60
+prohibited_till=60
 stoneproducer=true
 
 [buildcost]

=== modified file 'tribes/barbarians/quarry/conf'
--- tribes/barbarians/quarry/conf	2015-07-26 10:59:28 +0000
+++ tribes/barbarians/quarry/conf	2015-09-21 18:31:31 +0000
@@ -2,7 +2,6 @@
 output=raw_stone
 
 [aihints]
-forced_after=0
 stoneproducer=true
 
 [buildcost]

=== modified file 'tribes/empire/quarry/conf'
--- tribes/empire/quarry/conf	2015-07-26 10:59:28 +0000
+++ tribes/empire/quarry/conf	2015-09-21 18:31:31 +0000
@@ -3,7 +3,6 @@
 output=marble
 
 [aihints]
-forced_after=0
 stoneproducer=true
 
 [buildcost]

=== modified file 'tribes/empire/wood/conf'
--- tribes/empire/wood/conf	2014-07-28 14:04:36 +0000
+++ tribes/empire/wood/conf	2015-09-21 18:31:31 +0000
@@ -1,7 +1,7 @@
 help=_Wood is a basic building material of the Empire. It is also used by the weapon smithy.
 
 default_target_quantity=40
-preciousness=7
+preciousness=10
 
 [idle]
 pics=idle.png


Follow ups