← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/ai-military-changes into lp:widelands

 

TiborB has proposed merging lp:~widelands-dev/widelands/ai-military-changes into lp:widelands.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/ai-military-changes/+merge/253881

Another bunch of AI changes, this time only files within src/ai/ were changed. Reworked were:
- attacking
- training sites
- mines
- further small things


-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/ai-military-changes into lp:widelands.
=== modified file 'src/ai/ai_help_structs.h'
--- src/ai/ai_help_structs.h	2015-03-05 20:57:07 +0000
+++ src/ai/ai_help_structs.h	2015-03-23 21:37:01 +0000
@@ -40,7 +40,7 @@
 class ProductionSite;
 class MilitarySite;
 
-enum class ExtendedBool : uint8_t {kUnset, kTrue, kFalse };
+enum class ExtendedBool : uint8_t {kUnset, kTrue, kFalse};
 
 struct CheckStepRoadAI {
 	CheckStepRoadAI(Player* const pl, uint8_t const mc, bool const oe)
@@ -110,6 +110,22 @@
 	}
 };
 
+// Sometimes we need to know how many nodes our allies owns
+struct FindNodeAllyOwned {
+	bool accept(const Map&, const FCoords& fc) const {
+		return (fc.field->nodecaps() & MOVECAPS_WALK) && (fc.field->get_owned_by() != 0) &&
+		       (fc.field->get_owned_by() != pn) &&
+		       !player_->is_hostile(*game.get_player(fc.field->get_owned_by()));
+	}
+
+	Player* player_;
+	Game& game;
+	PlayerNumber pn;
+
+	FindNodeAllyOwned(Player* p, Game& g, PlayerNumber n) : player_(p), game(g), pn(n) {
+	}
+};
+
 // When looking for unowned terrain to acquire, we must
 // pay speciall attention to fields where mines can be built.
 // Fields should be completely unowned
@@ -148,8 +164,9 @@
 
 	bool accept(const Map& /* map */, const FCoords& coord) const {
 		return (world_.terrain_descr(coord.field->terrain_d()).get_is() &
-				  TerrainDescription::Type::kWater) ||
-				 (world_.terrain_descr(coord.field->terrain_r()).get_is() & TerrainDescription::Type::kWater);
+		        TerrainDescription::Type::kWater) ||
+		       (world_.terrain_descr(coord.field->terrain_r()).get_is() &
+		        TerrainDescription::Type::kWater);
 	}
 
 private:
@@ -213,7 +230,7 @@
 struct BuildableField {
 	Widelands::FCoords coords;
 
-	uint32_t next_update_due_;
+	uint32_t field_info_expiration_;
 
 	bool preferred_;
 	bool enemy_nearby_;
@@ -232,7 +249,7 @@
 	uint8_t space_consumers_nearby_;
 	// to manage the military better following variables exists:
 	// capacity of nearby buildings:
-	int16_t military_capacity_;
+	int16_t area_military_capacity_;
 	// distance to near buldings:
 	int16_t military_loneliness_;
 	// count of military buildings in construction
@@ -241,7 +258,7 @@
 	// 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 military_presence_;
+	int16_t area_military_presence_;
 	// stationed (manned) military buildings nearby
 	int16_t military_stationed_;
 	// stationed (manned) military buildings nearby
@@ -256,7 +273,7 @@
 
 	BuildableField(const Widelands::FCoords& fc)
 	   : coords(fc),
-	     next_update_due_(0),
+	     field_info_expiration_(20000),
 	     preferred_(false),
 	     enemy_nearby_(0),
 	     unowned_land_nearby_(0),
@@ -277,10 +294,10 @@
 	     critters_nearby_(-1),
 	     ground_water_(1),
 	     space_consumers_nearby_(0),
-	     military_capacity_(0),
+	     area_military_capacity_(0),
 	     military_loneliness_(1000),
 	     military_in_constr_nearby_(0),
-	     military_presence_(0),
+	     area_military_presence_(0),
 	     military_stationed_(0),
 	     military_unstationed_(0),
 	     is_portspace_(false),
@@ -292,7 +309,7 @@
 struct MineableField {
 	Widelands::FCoords coords;
 
-	uint32_t next_update_due_;
+	uint32_t field_info_expiration_;
 
 	bool preferred_;
 
@@ -302,7 +319,7 @@
 
 	MineableField(const Widelands::FCoords& fc)
 	   : coords(fc),
-	     next_update_due_(0),
+	     field_info_expiration_(20000),
 	     preferred_(false),
 	     mines_nearby_(0),
 	     same_mine_fields_nearby_(0) {
@@ -346,14 +363,16 @@
 	bool is_fisher_;    // need to identify fishers
 	bool is_port_;
 	bool is_shipyard_;
-	bool space_consumer_;      // farm, vineyard... = true
-	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
+	bool space_consumer_;       // farm, vineyard... = true
+	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
+	uint8_t ts_type_;
 
-	bool unoccupied_;  //
+	bool unoccupied_;
+	uint16_t unconnected_;  // to any warehouse (count of such buildings)
 
 	int32_t mines_;           // type of resource it mines_
 	uint16_t mines_percent_;  // % of res it can mine
@@ -429,4 +448,44 @@
 	uint8_t preciousness_;
 };
 
+//Computer player does not get notification messages about enemy militarysites
+//and warehouses, so following is collected based on observation
+//It is conventient to have some information preserved, like nearby minefields,
+//when it was attacked, whether it is warehouse and so on
+//Also AI test more such targets when considering attack and calculated score is
+//is stored in the observer
+struct EnemySiteObserver {
+	bool warehouse_;
+	uint8_t attack_soldiers;
+	uint8_t defenders;
+	uint8_t stationed_soldiers;
+	uint32_t last_time_attackable;
+	uint32_t last_tested;
+	int16_t score;
+	bool warehouse;
+	Widelands::ExtendedBool mines_nearby;
+	int16_t no_attack_counter;
+
+	EnemySiteObserver()
+	   : warehouse_(false),
+	     attack_soldiers(0),
+	     stationed_soldiers(0),
+	     last_time_attackable(std::numeric_limits<uint32_t>::max()),
+	     last_tested(0),
+	     score(0),
+	     mines_nearby(Widelands::ExtendedBool::kUnset),
+	     no_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
+struct MineTypesObserver {
+	uint16_t in_construction;
+	uint16_t finished;
+
+	MineTypesObserver() : in_construction(0), finished(0) {
+	}
+};
+
 #endif  // end of include guard: WL_AI_AI_HELP_STRUCTS_H

=== modified file 'src/ai/ai_hints.cc'
--- src/ai/ai_hints.cc	2015-03-05 20:57:07 +0000
+++ src/ai/ai_hints.cc	2015-03-23 21:37:01 +0000
@@ -33,7 +33,8 @@
      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
-     mines_percent_(section ? section->get_int("mines_percent", 100) : 0)
+     mines_percent_(section ? section->get_int("mines_percent", 100) : 0),
+     ts_type_(section ? section->get_int("ts_type", 0) : 0)
 {
 	if (section) {
 		if (section->has_val("renews_map_resource"))

=== modified file 'src/ai/ai_hints.h'
--- src/ai/ai_hints.h	2015-03-05 20:57:07 +0000
+++ src/ai/ai_hints.h	2015-03-23 21:37:01 +0000
@@ -94,6 +94,10 @@
 		return mines_percent_;
 	}
 
+	uint8_t get_ts_type() const {
+		return ts_type_;
+	}
+
 private:
 	std::string renews_map_resource_;
 	std::string mines_;
@@ -109,6 +113,7 @@
 	int32_t prohibited_till_;
 	int32_t forced_after_;
 	uint8_t mines_percent_;
+	uint8_t ts_type_;
 
 	DISALLOW_COPY_AND_ASSIGN(BuildingHints);
 };

=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc	2015-03-06 07:36:40 +0000
+++ src/ai/defaultai.cc	2015-03-23 21:37:01 +0000
@@ -51,12 +51,15 @@
 #include "profile/profile.h"
 
 // following is in miliseconds (widelands counts time in ms)
-constexpr int kFieldUpdateInterval = 2000;
-constexpr int kIdleMineUpdateInterval = 22000;
+// constexpr int kFieldUpdateInterval = 2000;
+constexpr int kFieldInfoExpiration = 12 * 1000;
+constexpr int kMineFieldInfoExpiration = 20 * 1000;
+constexpr int kNewMineConstInterval = 19000;
 constexpr int kBusyMineUpdateInterval = 2000;
 // building of the same building can be started after 25s at earliest
 constexpr int kBuildingMinInterval = 25 * 1000;
 constexpr int kMinBFCheckInterval = 5 * 1000;
+constexpr int kMinMFCheckInterval = 19 * 1000;
 constexpr int kShipCheckInterval = 5 * 1000;
 constexpr int kMarineDecisionInterval = 20 * 1000;
 constexpr int kTrainingSitesCheckInterval = 5 * 60 * 1000;
@@ -85,6 +88,8 @@
      num_milit_constructionsites(0),
      num_prod_constructionsites(0),
      num_ports(0),
+     last_attacked_player_(std::numeric_limits<uint16_t>::max()),
+     enemysites_check_delay_(60),
      next_ai_think_(0),
      next_mine_construction_due_(0),
      inhibit_road_building_(0),
@@ -92,9 +97,8 @@
      enemy_last_seen_(0),
      numof_warehouses_(0),
      new_buildings_stop_(false),
-     resource_necessity_territory_(255),
-     resource_necessity_mines_(255),
-     resource_necessity_stones_(255),
+     resource_necessity_territory_(100),
+     resource_necessity_mines_(100),
      resource_necessity_water_(0),
      resource_necessity_water_needed_(false),
      unstationed_milit_buildings_(0),
@@ -105,7 +109,13 @@
      next_attack_waittime_(10),
      seafaring_economy(false),
      colony_scan_area_(35),
-     spots_(0) {
+     spots_(0),
+     vacant_mil_positions_(0),
+     ts_type1_count_(0),
+     ts_type1_const_count_(0),
+     ts_type2_count_(0),
+     ts_type2_const_count_(0),
+     ts_without_trainers_(0) {
 
 	// Subscribe to NoteFieldPossession.
 	field_possession_subscriber_ =
@@ -166,23 +176,23 @@
 			   break;
 
 		   	case NoteShipMessage::Message::kLost:
-			   for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
+			   	for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
 				   	if (i->ship == note.ship) {
 					   allships.erase(i);
 					   break;
 				   }
 			   }
-			   break;
+			   	break;
 
 		   	case NoteShipMessage::Message::kWaitingForCommand:
-			   for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
+			   	for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
 				   	if (i->ship == note.ship) {
 					   i->waiting_for_command_ = true;
 					   break;
 				   }
 			   }
-			   break;
-		   default:
+			   	break;
+		   	default:
 				;
 		   }
 		});
@@ -236,7 +246,11 @@
 	if (DueTask == ScheduleTasks::kBbuildableFieldsCheck) {
 		update_all_buildable_fields(gametime);
 		taskDue[ScheduleTasks::kBbuildableFieldsCheck] = gametime + kMinBFCheckInterval;
-	} else if (DueTask == ScheduleTasks::kRoadCheck) {
+	} else if (DueTask == ScheduleTasks::kMineableFieldsCheck) {
+		update_all_mineable_fields(gametime);
+		taskDue[ScheduleTasks::kMineableFieldsCheck] = gametime + kMinMFCheckInterval;
+	}
+	if (DueTask == ScheduleTasks::kRoadCheck) {
 		if (check_economies()) {  // is a must
 			return;
 		};
@@ -245,8 +259,6 @@
 	} else if (DueTask == ScheduleTasks::kUnbuildableFCheck) {
 		taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000;
 		update_all_not_buildable_fields();
-	} else if (DueTask == ScheduleTasks::kConsiderAttack) {
-		consider_attack(gametime);
 	} else if (DueTask == ScheduleTasks::kCheckEconomies) {
 		check_economies();
 		taskDue[ScheduleTasks::kCheckEconomies] = gametime + 8000;
@@ -280,6 +292,9 @@
 		check_militarysites(gametime);
 	} else if (DueTask == ScheduleTasks::kCheckTrainingsites) {
 		check_trainingsites(gametime);
+	} else if (DueTask == ScheduleTasks::kCountMilitaryVacant) {
+		count_military_vacant_positions();
+		taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 90 * 1000;
 	} else if (DueTask == ScheduleTasks::kWareReview) {
 		if (check_economies()) {  // economies must be consistent
 			return;
@@ -291,6 +306,9 @@
 			return;
 		}
 		print_stats(gametime);
+	} else if (DueTask == ScheduleTasks::kCheckEnemySites) {
+		check_enemy_sites(gametime);
+		taskDue[ScheduleTasks::kCheckEnemySites] = gametime + 19 * 1000;
 	}
 }
 
@@ -340,6 +358,7 @@
 		bo.production_hint_ = -1;
 		bo.current_stats_ = 0;
 		bo.unoccupied_ = false;
+		bo.unconnected_ = 0;
 		bo.is_buildable_ = bld.is_buildable();
 		bo.need_trees_ = bh.is_logproducer();
 		bo.need_stones_ = bh.is_stoneproducer();
@@ -353,6 +372,8 @@
 		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.ts_type_ = bh.get_ts_type();
+
 		if (bh.renews_map_resource()) {
 			bo.production_hint_ = tribe_->safe_ware_index(bh.get_renews_map_resource());
 		}
@@ -382,6 +403,11 @@
 				}
 
 				bo.mines_percent_ = bh.get_mines_percent();
+
+				// populating mines_per_type map
+				if (mines_per_type.count(bo.mines_) == 0) {
+					mines_per_type[bo.mines_] = MineTypesObserver();
+				}
 			}
 
 			// here we identify hunters
@@ -439,6 +465,7 @@
 			for (const WareAmount& temp_input : train.inputs()) {
 				bo.inputs_.push_back(temp_input.first);
 			}
+			bo.ts_type_ = bh.get_ts_type();
 			continue;
 		}
 
@@ -544,12 +571,14 @@
 	taskDue[ScheduleTasks::kCheckShips] = 30 * 1000;
 	taskDue[ScheduleTasks::kCheckEconomies] = 1000;
 	taskDue[ScheduleTasks::KMarineDecisions] = 30 * 1000;
-	taskDue[ScheduleTasks::kConsiderAttack] = 300000;
 	taskDue[ScheduleTasks::kCheckTrainingsites] = 15 * 60 * 1000;
 	taskDue[ScheduleTasks::kBbuildableFieldsCheck] = 1000;
+	taskDue[ScheduleTasks::kMineableFieldsCheck] = 1000;
 	taskDue[ScheduleTasks::kUnbuildableFCheck] = 1000;
 	taskDue[ScheduleTasks::kWareReview] = 15 * 60 * 1000;
 	taskDue[ScheduleTasks::kPrintStats] = 30 * 60 * 1000;
+	taskDue[ScheduleTasks::kCountMilitaryVacant] = 10 * 60 * 1000;
+	taskDue[ScheduleTasks::kCheckEnemySites] = 10 * 60 * 1000;
 }
 
 /**
@@ -562,7 +591,10 @@
 
 	uint16_t i = 0;
 
-	while (!buildable_fields.empty() && buildable_fields.front()->next_update_due_ <= gametime &&
+	// we test 40 fields that were update more than 1 seconds ago
+	while (!buildable_fields.empty() &&
+	       (buildable_fields.front()->field_info_expiration_ - kFieldInfoExpiration + 1000) <=
+	          gametime &&
 	       i < 40) {
 		BuildableField& bf = *buildable_fields.front();
 
@@ -582,7 +614,7 @@
 		}
 
 		update_buildable_field(bf);
-		bf.next_update_due_ = gametime + kFieldUpdateInterval;
+		bf.field_info_expiration_ = gametime + kFieldInfoExpiration;
 		buildable_fields.push_back(&bf);
 		buildable_fields.pop_front();
 
@@ -600,8 +632,12 @@
 
 	uint16_t i = 0;  // counter, used to track # of checked fields
 
-	while (!mineable_fields.empty() && mineable_fields.front()->next_update_due_ <= gametime &&
-	       i < 40) {
+	// we test 30 fields that were updated more than 1 seconds ago
+	// to avoid re-test of the same field twice
+	while (!mineable_fields.empty() &&
+	       (mineable_fields.front()->field_info_expiration_ - kMineFieldInfoExpiration + 1000) <=
+	          gametime &&
+	       i < 30) {
 		MineableField* mf = mineable_fields.front();
 
 		//  check whether we lost ownership of the node
@@ -620,7 +656,7 @@
 		}
 
 		update_mineable_field(*mf);
-		mf->next_update_due_ = gametime + kFieldUpdateInterval;  // in fact this has very small effect
+		mf->field_info_expiration_ = gametime + kMineFieldInfoExpiration;
 		mineable_fields.push_back(mf);
 		mineable_fields.pop_front();
 
@@ -672,32 +708,38 @@
 void DefaultAI::update_buildable_field(BuildableField& field, uint16_t range, bool military) {
 	// look if there is any unowned land nearby
 	Map& map = game().map();
+	const int32_t gametime = game().get_gametime();
 	FindNodeUnowned find_unowned(player_, game());
 	FindNodeUnownedMineable find_unowned_mines_pots(player_, game());
 	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);
+	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 (field.unowned_land_nearby_ > 0) {
+	if (AllyOwnedFields > 0) {
+		field.near_border_ = true;
+	} else if (field.unowned_land_nearby_ > 0) {
 		if (map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned) > 0) {
 			field.near_border_ = true;
 		}
 	}
 
 	// to save some CPU
-	if ((mines_.size() > 8 && game().get_gametime() % 3 > 0) || field.unowned_land_nearby_ == 0) {
+	if ((mines_.size() > 8 && gametime % 3 > 0) || field.unowned_land_nearby_ == 0) {
 		field.unowned_mines_pots_nearby_ = 0;
 	} else {
 		uint32_t close_mines =
 		   map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned_mines_pots);
 		uint32_t distant_mines =
-		   map.find_fields(Area<FCoords>(field.coords, (range + 6 < 12) ? 12 : range + 6),
+		   map.find_fields(Area<FCoords>(field.coords, (range + 6 < 14) ? 14 : range + 6),
 		                   nullptr,
 		                   find_unowned_mines_pots);
 		distant_mines = distant_mines - close_mines;
-		field.unowned_mines_pots_nearby_ = 3 * close_mines + distant_mines / 2;
+		field.unowned_mines_pots_nearby_ = 4 * close_mines + distant_mines / 2;
 		if (distant_mines > 0) {
 			field.unowned_mines_pots_nearby_ += 15;
 		}
@@ -710,7 +752,7 @@
 		}
 	}
 
-	// testing for near porspaces
+	// testing for near portspaces
 	if (field.portspace_nearby_ == Widelands::ExtendedBool::kUnset) {
 		field.portspace_nearby_ = ExtendedBool::kFalse;
 		MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, 2));
@@ -745,9 +787,9 @@
 		int32_t const tree_attr = MapObjectDescr::get_attribute_id("tree");
 		field.preferred_ = false;
 		field.enemy_nearby_ = false;
-		field.military_capacity_ = 0;
+		field.area_military_capacity_ = 0;
 		field.military_loneliness_ = 1000;  // instead of floats(v-
-		field.military_presence_ = 0;
+		field.area_military_presence_ = 0;
 		field.military_stationed_ = 0;
 		field.trees_nearby_ = 0;
 		field.space_consumers_nearby_ = 0;
@@ -772,8 +814,7 @@
 		}
 
 		// counting fields with fish
-		if (field.water_nearby_ > 0 &&
-		    (field.fish_nearby_ == -1 || game().get_gametime() % 10 == 0)) {
+		if (field.water_nearby_ > 0 && (field.fish_nearby_ == -1 || gametime % 10 == 0)) {
 			map.find_fields(Area<FCoords>(field.coords, 6),
 			                &resource_list,
 			                FindNodeResource(world.get_resource("fish")));
@@ -782,7 +823,7 @@
 
 		// counting fields with critters (game)
 		// not doing this always, this does not change fast
-		if (game().get_gametime() % 10 == 0) {
+		if (gametime % 10 == 0) {
 			map.find_bobs(Area<FCoords>(field.coords, 6), &critters_list, FindBobCritter());
 			field.critters_nearby_ = critters_list.size();
 		}
@@ -808,7 +849,7 @@
 					if (player_->is_hostile(player_immovable->owner())) {
 						field.enemy_nearby_ = true;
 					}
-					enemy_last_seen_ = game().get_gametime();
+					enemy_last_seen_ = gametime;
 
 					continue;
 				}
@@ -840,7 +881,7 @@
 		}
 
 		// stones are not renewable, we will count them only if previous state si nonzero
-		if (field.stones_nearby_ > 0) {
+		if (field.stones_nearby_ > 0 && gametime % 3 == 0) {
 
 			int32_t const stone_attr = MapObjectDescr::get_attribute_id("granite");
 			field.stones_nearby_ = 0;
@@ -863,13 +904,13 @@
 
 	// we get immovables with higher radius
 	immovables.clear();
-	map.find_immovables(Area<FCoords>(field.coords, (range < 10) ? 10 : range), &immovables);
+	map.find_immovables(Area<FCoords>(field.coords, (range < 11) ? 11 : range), &immovables);
 	field.military_stationed_ = 0;
 	field.military_unstationed_ = 0;
 	field.military_in_constr_nearby_ = 0;
-	field.military_capacity_ = 0;
+	field.area_military_capacity_ = 0;
 	field.military_loneliness_ = 1000;
-	field.military_presence_ = 0;
+	field.area_military_presence_ = 0;
 
 	for (uint32_t i = 0; i < immovables.size(); ++i) {
 
@@ -890,6 +931,7 @@
 			}
 		}
 
+		// if we are here, immovable is ours
 		if (upcast(Building const, building, &base_immovable)) {
 			if (upcast(ConstructionSite const, constructionsite, building)) {
 				const BuildingDescr& target_descr = constructionsite->building();
@@ -899,7 +941,8 @@
 					const int32_t radius = target_ms_d->get_conquers() + 4;
 
 					if (radius > dist) {
-						field.military_capacity_ += target_ms_d->get_max_number_of_soldiers() / 2 + 1;
+						field.area_military_capacity_ +=
+						   target_ms_d->get_max_number_of_soldiers() / 2 + 1;
 						field.military_loneliness_ *= static_cast<double_t>(dist) / radius;
 						field.military_in_constr_nearby_ += 1;
 					}
@@ -912,8 +955,8 @@
 
 				if (radius > dist) {
 
-					field.military_capacity_ += militarysite->max_soldier_capacity();
-					field.military_presence_ += militarysite->stationed_soldiers().size();
+					field.area_military_capacity_ += militarysite->max_soldier_capacity();
+					field.area_military_presence_ += militarysite->stationed_soldiers().size();
 
 					if (militarysite->stationed_soldiers().empty()) {
 						field.military_unstationed_ += 1;
@@ -935,7 +978,7 @@
 	Map& map = game().map();
 	map.find_immovables(Area<FCoords>(field.coords, 5), &immovables);
 	field.preferred_ = false;
-	field.mines_nearby_ = 1;
+	field.mines_nearby_ = 0;
 	FCoords fse;
 	map.get_brn(field.coords, &fse);
 
@@ -948,11 +991,20 @@
 
 	for (const ImmovableFound& temp_immovable : immovables) {
 		if (upcast(Building const, bld, temp_immovable.object)) {
+			if (player_number() != bld->owner().player_number()) {
+				continue;
+			}
 			if (bld->descr().get_ismine()) {
-				++field.mines_nearby_;
+				if (get_building_observer(bld->descr().name().c_str()).mines_ ==
+				    field.coords.field->get_resources()) {
+					++field.mines_nearby_;
+				}
 			} else if (upcast(ConstructionSite const, cs, bld)) {
 				if (cs->building().get_ismine()) {
-					++field.mines_nearby_;
+					if (get_building_observer(cs->building().name().c_str()).mines_ ==
+					    field.coords.field->get_resources()) {
+						++field.mines_nearby_;
+					}
 				}
 			}
 		}
@@ -976,23 +1028,34 @@
 	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;
 	}
 
 	// Check all available productionsites
 	for (uint32_t i = 0; i < productionsites.size(); ++i) {
 		assert(productionsites.front().bo->cnt_built_ > 0);
-		// Add statistics value
-		productionsites.front().bo->current_stats_ +=
-		   productionsites.front().site->get_crude_statistics();
-
-		// counting fishers
-		if (productionsites.front().bo->is_fisher_) {
-			fishers_count += 1;
+		// is connected
+		const bool connected_to_wh =
+		   !productionsites.front().site->get_economy()->warehouses().empty();
+
+		// unconnected buildings are excluded from statistics review
+		if (connected_to_wh) {
+			// Add statistics value
+			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
+			productionsites.front().bo->unoccupied_ |=
+			   !productionsites.front().site->can_start_working();
+		} else {
+			productionsites.front().bo->unconnected_ += 1;
 		}
 
-		// Check whether this building is completely occupied
-		productionsites.front().bo->unoccupied_ |= !productionsites.front().site->can_start_working();
-
 		// Now reorder the buildings
 		productionsites.push_back(productionsites.front());
 		productionsites.pop_front();
@@ -1000,11 +1063,11 @@
 
 	if (resource_necessity_water_needed_) {
 		if (fishers_count == 0) {
-			resource_necessity_water_ = 255;
+			resource_necessity_water_ = 100;
 		} else if (fishers_count == 1) {
-			resource_necessity_water_ = 150;
+			resource_necessity_water_ = 50;
 		} else {
-			resource_necessity_water_ = 18;
+			resource_necessity_water_ = 10;
 		}
 	}
 
@@ -1012,10 +1075,20 @@
 	// Check all available mines
 	for (uint32_t i = 0; i < mines_.size(); ++i) {
 		assert(mines_.front().bo->cnt_built_ > 0);
-		// 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();
+
+		const bool connected_to_wh =
+		   !productionsites.front().site->get_economy()->warehouses().empty();
+
+		// unconnected mines are excluded from statistics review
+		if (connected_to_wh) {
+			// 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();
+		} else {
+			buildings_.at(i).unconnected_ += 1;
+		}
+
 		// Now reorder the buildings
 		mines_.push_back(mines_.front());
 		mines_.pop_front();
@@ -1023,8 +1096,9 @@
 
 	// Scale statistics down
 	for (uint32_t i = 0; i < buildings_.size(); ++i) {
-		if (buildings_.at(i).cnt_built_ > 0) {
-			buildings_.at(i).current_stats_ /= buildings_.at(i).cnt_built_;
+		if ((buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_) > 0) {
+			buildings_.at(i).current_stats_ /=
+			   (buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_);
 		}
 	}
 }
@@ -1100,54 +1174,48 @@
 
 	// sometimes there is too many military buildings in construction, so we must
 	// prevent initialization of further buildings start
-	const uint32_t treshold = militarysites.size() / 40 + 2;
-
-	if (unstationed_milit_buildings_ + num_milit_constructionsites > 3 * treshold) {
+	const int32_t vacant_plus_in_construction_minus_prod =
+	   vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 15;
+	if (vacant_plus_in_construction_minus_prod > 20) {
 		expansion_mode = MilitaryStrategy::kNoNewMilitary;
-	} else if (unstationed_milit_buildings_ + num_milit_constructionsites > 2 * treshold) {
+	} else if (vacant_plus_in_construction_minus_prod > 13) {
 		expansion_mode = MilitaryStrategy::kDefenseOnly;
-	} else if (unstationed_milit_buildings_ + num_milit_constructionsites >= treshold - 1) {
+	} else if (vacant_plus_in_construction_minus_prod > 6) {
 		expansion_mode = MilitaryStrategy::kResourcesOrDefense;
-	} else if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
-		expansion_mode = MilitaryStrategy::kExpansion;
 	} else {
-		expansion_mode = MilitaryStrategy::kPushExpansion;
+		if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
+			expansion_mode = MilitaryStrategy::kExpansion;
+		} else {
+			expansion_mode = MilitaryStrategy::kPushExpansion;
+		}
 	}
 
 	// 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
-	const int32_t virtual_mines = mines_.size() + mineable_fields.size() / 15;
-	if (virtual_mines <= 7) {
-		resource_necessity_mines_ = std::numeric_limits<uint8_t>::max();
-	} else if (virtual_mines > 19) {
-		resource_necessity_mines_ = 50;
+	const int32_t virtual_mines =
+	   mines_.size() + mineable_fields.size() / 15 - productionsites.size() / 25;
+	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 {
-		const uint32_t tmp = (24 - virtual_mines) * 10;
-		resource_necessity_mines_ = tmp;
-	}
-
-	// here we calculate a need for expansion and reduce necessity for new land
-	// the game has two stages:
-	// First: virtual mines<=5 - stage of building the economics
-	// Second: virtual mines>5 - teritorial expansion
-	if (virtual_mines <= 5) {
-		if (spots_avail.at(BUILDCAPS_BIG) <= 4) {
-			resource_necessity_territory_ = 255;
-		} else {
-			resource_necessity_territory_ = 0;
+		resource_necessity_territory_ = 100 * 5 * (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;
 		}
-	} else {  // or we have enough mines and regulate speed of expansion
-		if (spots_ == 0) {
-			resource_necessity_territory_ = 255;
-		} else {
-			const uint32_t tmp = 255 * 4 * productionsites.size() / spots_;
-			if (tmp > 255) {
-				resource_necessity_territory_ = 255;
-			} else {
-				resource_necessity_territory_ = tmp;
-			}
+		if (spots_avail.at(BUILDCAPS_MEDIUM) < 4) {
+			resource_necessity_territory_ = 100;
 		}
 	}
 
@@ -1205,7 +1273,7 @@
 	     ++i) {
 		BuildableField* const bf = *i;
 
-		if (bf->next_update_due_ < gametime - 10000) {
+		if (bf->field_info_expiration_ < gametime) {
 			continue;
 		}
 
@@ -1369,7 +1437,7 @@
 						continue;
 					}
 
-					if (bo.total_count() == 0) {
+					if (bo.total_count() - bo.unconnected_ == 0) {
 						prio += 150;
 					}
 
@@ -1394,12 +1462,15 @@
 						continue;
 					}
 
-					if (new_buildings_stop_) {
+					if (gametime > 5 * 60 * 1000 &&
+					(bo.total_count() - bo.unconnected_  == 0)) {
+						prio += 10;
+					} else if (new_buildings_stop_) {
 						continue;
 					}
 
 					prio +=
-					   (bf->critters_nearby_ * 2) - 8 - 5 * bf->producers_nearby_.at(bo.outputs_.at(0));
+					   (bf->critters_nearby_ * 3) - 8 - 5 * bf->producers_nearby_.at(bo.outputs_.at(0));
 
 				} else if (bo.is_fisher_) {  // fisher
 
@@ -1490,7 +1561,7 @@
 						prio -= bf->space_consumers_nearby_ * 5;
 
 					} else {  // FISH BREEDERS and GAME KEEPERS
-						if (new_buildings_stop_ && bo.total_count() > 0) {
+						if (new_buildings_stop_ && (bo.total_count() - bo.unconnected_) > 0) {
 							continue;
 						}
 
@@ -1502,7 +1573,7 @@
 							prio += bf->water_nearby_ / 5;
 						}
 
-						if (bo.total_count() > bo.cnt_target_) {
+						if ((bo.total_count() - bo.unconnected_) > bo.cnt_target_) {
 							continue;
 						}
 
@@ -1543,9 +1614,10 @@
 						continue;
 					}
 
-					if (bo.forced_after_ < gametime && bo.total_count() == 0) {
+					if (bo.forced_after_ < gametime && (bo.total_count() - bo.unconnected_) == 0) {
 						prio += 150;
-					} else if (bo.cnt_built_ == 1 && game().get_gametime() > 40 * 60 * 1000 &&
+					} 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_) {
@@ -1554,9 +1626,10 @@
 						}
 					} else if (!output_is_needed) {
 						continue;
-					} else if (bo.cnt_built_ == 0 && game().get_gametime() > 40 * 60 * 1000) {
+					} else if ((bo.cnt_built_ - bo.unconnected_) == 0 &&
+					           game().get_gametime() > 40 * 60 * 1000) {
 						prio += kDefaultPrioBoost;
-					} else if (bo.cnt_built_ > 1 && bo.current_stats_ > 97) {
+					} else if ((bo.cnt_built_ - bo.unconnected_) > 1 && bo.current_stats_ > 97) {
 						prio -= kDefaultPrioBoost * (new_buildings_stop_);
 					} else if (new_buildings_stop_)
 						continue;
@@ -1593,15 +1666,16 @@
 
 					else if (bo.is_shipyard_) {
 						// for now AI builds only one shipyard
-						if (bf->water_nearby_ > 3 && bo.total_count() == 0 && seafaring_economy) {
+						if (bf->water_nearby_ > 3 && (bo.total_count() - bo.unconnected_) == 0 &&
+						    seafaring_economy) {
 							prio += kDefaultPrioBoost + productionsites.size() * 5 + bf->water_nearby_;
 						}
 
 					} else if (!bo.inputs_.empty()) {
-						if (bo.total_count() == 0) {
+						if ((bo.total_count() == 0 - bo.unconnected_)) {
 							prio += max_needed_preciousness + kDefaultPrioBoost;
 						}
-						if (bo.cnt_built_ > 0 && bo.current_stats_ > 70) {
+						if ((bo.cnt_built_ - bo.unconnected_) > 0 && bo.current_stats_ > 70) {
 							prio += max_needed_preciousness + kDefaultPrioBoost - 3 +
 							        (bo.current_stats_ - 70) / 5;
 						}
@@ -1623,7 +1697,6 @@
 				}
 			}  // production sites done
 			else if (bo.type == BuildingObserver::MILITARYSITE) {
-
 				// we allow 1 exemption from big buildings prohibition
 				if (bo.build_material_shortage_ &&
 				    (bo.cnt_under_construction_ > 0 || !(bf->enemy_nearby_))) {
@@ -1631,6 +1704,7 @@
 				}
 
 				if (!bf->unowned_land_nearby_) {
+
 					continue;
 				}
 
@@ -1648,8 +1722,7 @@
 
 				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_)) {
+				      (bf->distant_water_ && resource_necessity_water_needed_) || bf->enemy_nearby_)) {
 					continue;
 				}
 
@@ -1667,31 +1740,40 @@
 					if (bo.desc->get_size() == 3 && gametime % 3 >= 1) {
 						continue;
 					};
-				}
-				else {
+				} else {
 					continue;
 				}  // the building is not suitable for situation
-
 				// not to build so many military buildings nearby
 				if (!bf->enemy_nearby_ &&
 				    (bf->military_in_constr_nearby_ + bf->military_unstationed_) > 0) {
 					continue;
 				}
-
 				// a boost to prevent an expansion halt
 				int32_t local_boost = 0;
 				if (expansion_mode == MilitaryStrategy::kPushExpansion) {
 					local_boost = 200;
 				}
 
-				prio = ((bf->unowned_land_nearby_ * 2 * resource_necessity_territory_) / 255 +
-				        (bf->unowned_mines_pots_nearby_ * resource_necessity_mines_) / 255 +
-				        bf->stones_nearby_ / 2 + bf->military_loneliness_ / 10 - 60 + local_boost +
-				        (bf->water_nearby_ * resource_necessity_water_) / 255);
+				// priority based on basic resources
+				prio = ((bf->unowned_mines_pots_nearby_ * resource_necessity_mines_) / 100 +
+				        bf->stones_nearby_ + bf->military_loneliness_ / 10 - 40 + local_boost +
+				        (bf->water_nearby_ * resource_necessity_water_) / 100);
+
+				// Depending on wheter resource only are considered or no
+				if (expansion_mode == MilitaryStrategy::kResourcesOrDefense) {
+					prio *= 2;
+					prio += bf->unowned_land_nearby_ * resource_necessity_territory_ / 100 / 2;
+				} else {  // addding score for territory
+					prio += (bf->unowned_land_nearby_ * resource_necessity_territory_) / 100 * 3 / 2;
+				}
+
+				// adding score for distance to other military sites
+				prio += bf->military_loneliness_ / 10 - 40;
 
 				// special bonus due to remote water for atlanteans
-				if (resource_necessity_water_needed_)
-					prio += (bf->distant_water_ * resource_necessity_water_) / 255;
+				if (resource_necessity_water_needed_) {
+					prio += (bf->distant_water_ * resource_necessity_water_) / 100 / 2;
+				}
 
 				// special bonus if a portspace is close
 				if (bf->portspace_nearby_ == ExtendedBool::kTrue) {
@@ -1702,6 +1784,11 @@
 					}
 				}
 
+				//special bonus for bigger buildings in enemy is nearby
+				if (bf->enemy_nearby_) {
+					prio += (bo.desc->get_size() - 1) * 15;
+				}
+
 				if (bo.desc->get_size() < maxsize) {
 					prio = prio - 5;
 				}  // penalty
@@ -1713,12 +1800,12 @@
 				// for expansion)
 				const int16_t bottom_treshold =
 				   15 - ((virtual_mines <= 4) ? (5 - virtual_mines) * 2 : 0);
-				if (bf->enemy_nearby_ && bf->military_capacity_ < bottom_treshold) {
-					prio += 50 + (bottom_treshold - bf->military_capacity_) * 20;
+				if (bf->enemy_nearby_ && bf->area_military_capacity_ < bottom_treshold) {
+					prio += 50 + (bottom_treshold - bf->area_military_capacity_) * 20;
 				}
 
-				if (bf->enemy_nearby_ && bf->military_capacity_ > bottom_treshold + 4) {
-					prio -= (bf->military_capacity_ - (bottom_treshold + 4)) * 5;
+				if (bf->enemy_nearby_ && bf->area_military_capacity_ > bottom_treshold + 4) {
+					prio -= (bf->area_military_capacity_ - (bottom_treshold + 4)) * 5;
 				}
 
 			} else if (bo.type == BuildingObserver::WAREHOUSE) {
@@ -1780,7 +1867,7 @@
 
 				// take care about and enemies
 				if (bf->enemy_nearby_) {
-					prio /= 2;
+					prio /= 4;
 				}
 
 				if (bf->unowned_land_nearby_ && !bo.is_port_) {
@@ -1789,24 +1876,43 @@
 
 			} else if (bo.type == BuildingObserver::TRAININGSITE) {
 
-				if (virtual_mines < 5) {
-					continue;
-				}
-
 				// exclude spots on border
 				if (bf->near_border_) {
 					continue;
 				}
 
-				if (virtual_mines < 3) {
-					continue;
-				}
-
-				// build after 20 production sites and then after each 50 production site
-				if (static_cast<int32_t>((productionsites.size() + 40) / 60) > bo.total_count() &&
-				    bo.cnt_under_construction_ == 0) {
-					prio = 4 + kDefaultPrioBoost;
-				}
+				// it is a bit difficult to get a new trainer.....
+				if (ts_without_trainers_) {
+					continue;
+				}
+
+				// target is only one for both types
+				if ((ts_type1_const_count_ + ts_type2_const_count_) > 0) {
+					continue;
+				}
+
+				// we build one training site for 100 militarysites
+				if (bo.ts_type_ == 1 &&
+				    militarysites.size() / 100 < static_cast<int32_t>(ts_type1_count_)) {
+					continue;
+				}
+				// we build one training site for 100 militarysites
+				if (bo.ts_type_ == 2 &&
+				    militarysites.size() / 100 < static_cast<int32_t>(ts_type2_count_)) {
+					continue;
+				}
+
+				// for type1 we need 15 productionsties
+				if (bo.ts_type_ == 1 && productionsites.size() < 15) {
+					continue;
+				}
+
+				// for type2 we need 4 mines
+				if (bo.ts_type_ == 2 && virtual_mines < 4) {
+					continue;
+				}
+
+				prio = 4 + kDefaultPrioBoost;
 
 				// take care about borders and enemies
 				if (bf->enemy_nearby_) {
@@ -1854,8 +1960,9 @@
 	// then try all mines_ - as soon as basic economy is build up.
 	if (gametime > next_mine_construction_due_) {
 
-		update_all_mineable_fields(gametime);
-		next_mine_construction_due_ = gametime + kIdleMineUpdateInterval;
+		// not done here
+		// update_all_mineable_fields(gametime);
+		next_mine_construction_due_ = gametime + kNewMineConstInterval;
 
 		if (!mineable_fields.empty()) {
 
@@ -1885,22 +1992,33 @@
 				check_ware_necessity(
 				   bo, &output_is_needed, &max_preciousness, &max_needed_preciousness);
 
-				if (!output_is_needed && bo.total_count() > 0) {
+				if (!output_is_needed && (bo.total_count() - bo.unconnected_) > 0) {
 					continue;
 				}
 
 				// if current one(s) are performing badly
-				if (bo.total_count() >= 1 && bo.current_stats_ < 50) {
+				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
 				uint32_t nearness_penalty = 0;
-				if ((bo.cnt_built_ + bo.cnt_under_construction_) == 0) {
+				if ((mines_per_type[bo.mines_].in_construction + mines_per_type[bo.mines_].finished) ==
+				    0) {
 					nearness_penalty = 0;
 				} else {
-					nearness_penalty = 10;
+					nearness_penalty = 30;
+				}
+
+				// 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 = 15;
+				} else if ((mines_per_type[bo.mines_].in_construction +
+				            mines_per_type[bo.mines_].finished) == 1) {
+					bonus_score = 5;
 				}
 
 				// iterating over fields
@@ -1908,26 +2026,46 @@
 				     j != mineable_fields.end();
 				     ++j) {
 
-					if ((*j)->coords.field->get_resources() != bo.mines_) {
-						continue;
-					}
-
-					int32_t prio = (*j)->coords.field->get_resources_amount();
+					MineableField* const mf = *j;
+
+					if (mf->field_info_expiration_ <= gametime) {
+						continue;
+					}
+
+					if (mf->coords.field->get_resources() != bo.mines_) {
+						continue;
+					}
+
+					int32_t prio = 0;
+					MapRegion<Area<FCoords>> mr(map, Area<FCoords>(mf->coords, 2));
+					do {
+						if (bo.mines_ == mr.location().field->get_resources()) {
+							prio += mr.location().field->get_resources_amount();
+						}
+					} while (mr.advance(map));
+
+					prio /= 10;
+
+					// Only build mines_ on locations where some material can be mined
+					if (prio < 1) {
+						continue;
+					}
 
 					// applying nearnes penalty
-					prio = prio - (*j)->mines_nearby_ * nearness_penalty;
-
-					// Only build mines_ on locations where some material can be mined
-					if (prio < 2) {
-						continue;
-					}
+					prio -= mf->mines_nearby_ * nearness_penalty;
+
+					// applying bonus score
+					prio += bonus_score;
+
+					// applying max needed
+					prio += max_needed_preciousness * 3;
 
 					// prefer mines in the middle of mine fields of the
 					// same type, so we add a small bonus here
 					// depending on count of same mines nearby,
 					// though this does not reflects how many resources
 					// are (left) in nearby mines
-					prio += (*j)->same_mine_fields_nearby_ / 3;
+					prio += mf->same_mine_fields_nearby_;
 
 					// Continue if field is blocked at the moment
 					bool blocked = false;
@@ -1946,13 +2084,12 @@
 					}
 
 					// Prefer road side fields
-					prio += (*j)->preferred_ ? 1 : 0;
+					prio += mf->preferred_ ? 1 : 0;
 
 					if (prio > proposed_priority) {
-						// proposed_building = bo.id;
 						best_building = &bo;
 						proposed_priority = prio;
-						proposed_coords = (*j)->coords;
+						proposed_coords = mf->coords;
 						mine = true;
 					}
 				}  // end of evaluation of field
@@ -2257,11 +2394,11 @@
 
 				if (occupied_military_) {
 					eco->dismantle_grace_time_ =
-					   (gametime + 20 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
+					   (gametime + 90 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
 
 				} else {  // for other normal buildings
 					eco->dismantle_grace_time_ =
-					   gametime + (5 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
+					   gametime + (45 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
 				}
 			}
 
@@ -2547,6 +2684,9 @@
 		site.unoccupied_till_ = game().get_gametime();
 	}
 
+	// is it connected to wh at all?
+	const bool connected_to_wh = !site.site->get_economy()->warehouses().empty();
+
 	// do not dismantle or upgrade the same type of building too soon - to give some time to update
 	// statistics
 	if (site.bo->last_dismantle_time_ > game().get_gametime() - 30 * 1000) {
@@ -2571,7 +2711,8 @@
 	// b) if there are two buildings
 	// statistics percents are decisive
 	const BuildingIndex enhancement = site.site->descr().enhancement();
-	if (enhancement != INVALID_INDEX && (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) {
+	if (connected_to_wh && enhancement != INVALID_INDEX &&
+	    (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) {
 
 		BuildingIndex enbld = INVALID_INDEX;  // to get rid of this
 
@@ -2664,7 +2805,11 @@
 		// so finally we dismantle the lumberjac
 		site.bo->last_dismantle_time_ = game().get_gametime();
 		flags_to_be_removed.push_back(site.site->base_flag().get_position());
-		game().send_player_dismantle(*site.site);
+		if (connected_to_wh) {
+			game().send_player_dismantle(*site.site);
+		} else {
+			game().send_player_bulldoze(*site.site);
+		}
 
 		return true;
 	}
@@ -2675,7 +2820,11 @@
 		    site.site->get_statistics_percent() == 0) {
 			site.bo->last_dismantle_time_ = gametime;
 			flags_to_be_removed.push_back(site.site->base_flag().get_position());
-			game().send_player_dismantle(*site.site);
+			if (connected_to_wh) {
+				game().send_player_dismantle(*site.site);
+			} else {
+				game().send_player_bulldoze(*site.site);
+			}
 
 			return true;
 		}
@@ -2695,7 +2844,11 @@
 		if (site.bo->stocklevel_ > 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());
-			game().send_player_dismantle(*site.site);
+			if (connected_to_wh) {
+				game().send_player_dismantle(*site.site);
+			} else {
+				game().send_player_bulldoze(*site.site);
+			}
 			return true;
 		}
 
@@ -2714,7 +2867,11 @@
 			// the destruction of the flag avoids that defaultAI will have too many
 			// unused roads - if needed the road will be rebuild directly.
 			flags_to_be_removed.push_back(site.site->base_flag().get_position());
-			game().send_player_dismantle(*site.site);
+			if (connected_to_wh) {
+				game().send_player_dismantle(*site.site);
+			} else {
+				game().send_player_bulldoze(*site.site);
+			}
 			return true;
 		}
 
@@ -2723,7 +2880,11 @@
 			// it is possible that there are stones but quary is not able to mine them
 			site.bo->last_dismantle_time_ = game().get_gametime();
 			flags_to_be_removed.push_back(site.site->base_flag().get_position());
-			game().send_player_dismantle(*site.site);
+			if (connected_to_wh) {
+				game().send_player_dismantle(*site.site);
+			} else {
+				game().send_player_bulldoze(*site.site);
+			}
 
 			return true;
 		}
@@ -2743,7 +2904,7 @@
 	    site.bo->space_consumer_ && !site.bo->plants_trees_) {
 
 		// if we have more buildings then target
-		if (site.bo->cnt_built_ > site.bo->cnt_target_) {
+		if ((site.bo->cnt_built_ - site.bo->unconnected_) > 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();
@@ -2753,7 +2914,11 @@
 			    site.bo->stocklevel_ > 100) {  // production stats == 0%
 				site.bo->last_dismantle_time_ = game().get_gametime();
 				flags_to_be_removed.push_back(site.site->base_flag().get_position());
-				game().send_player_dismantle(*site.site);
+				if (connected_to_wh) {
+					game().send_player_dismantle(*site.site);
+				} else {
+					game().send_player_bulldoze(*site.site);
+				}
 				return true;
 			}
 		}
@@ -2762,7 +2927,11 @@
 		if (site.site->get_statistics_percent() <= 10 && site.bo->cnt_built_ > 1) {
 
 			flags_to_be_removed.push_back(site.site->base_flag().get_position());
-			game().send_player_dismantle(*site.site);
+			if (connected_to_wh) {
+				game().send_player_dismantle(*site.site);
+			} else {
+				game().send_player_bulldoze(*site.site);
+			}
 			return true;
 		}
 
@@ -2778,7 +2947,11 @@
 
 		site.bo->last_dismantle_time_ = game().get_gametime();
 		flags_to_be_removed.push_back(site.site->base_flag().get_position());
-		game().send_player_dismantle(*site.site);
+		if (connected_to_wh) {
+			game().send_player_dismantle(*site.site);
+		} else {
+			game().send_player_bulldoze(*site.site);
+		}
 		return true;
 	}
 
@@ -2792,7 +2965,11 @@
 
 		site.bo->last_dismantle_time_ = game().get_gametime();
 		flags_to_be_removed.push_back(site.site->base_flag().get_position());
-		game().send_player_dismantle(*site.site);
+		if (connected_to_wh) {
+			game().send_player_dismantle(*site.site);
+		} else {
+			game().send_player_bulldoze(*site.site);
+		}
 		return true;
 	}
 
@@ -2813,16 +2990,29 @@
 
 			site.bo->last_dismantle_time_ = game().get_gametime();
 			flags_to_be_removed.push_back(site.site->base_flag().get_position());
-			game().send_player_dismantle(*site.site);
+			if (connected_to_wh) {
+				game().send_player_dismantle(*site.site);
+			} else {
+				game().send_player_bulldoze(*site.site);
+			}
 			return true;
 		}
 
 		if (score > 120 && !site.site->is_stopped()) {
 
 			game().send_player_start_stop_building(*site.site);
+			return true;
 		}
+		const uint32_t trees_in_vicinity =
+		   map.find_immovables(Area<FCoords>(map.get_fcoords(site.site->get_position()), 5),
+		                       nullptr,
+		                       FindImmovableAttribute(MapObjectDescr::get_attribute_id("tree")));
 
-		if (score < 80 && site.site->is_stopped()) {
+		if (trees_in_vicinity > 25) {
+			if (!site.site->is_stopped()) {
+				game().send_player_start_stop_building(*site.site);
+			}
+		} else if (score < 80 && site.site->is_stopped()) {
 
 			game().send_player_start_stop_building(*site.site);
 		}
@@ -3043,11 +3233,17 @@
 	// Get link to productionsite that should be checked
 	ProductionSiteObserver& site = mines_.front();
 
+	const bool connected_to_wh = !site.site->get_economy()->warehouses().empty();
+
 	// 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());
-		game().send_player_dismantle(*site.site);
+		if (connected_to_wh) {
+			game().send_player_dismantle(*site.site);
+		} else {
+			game().send_player_bulldoze(*site.site);
+		}
 		return true;
 	}
 
@@ -3059,7 +3255,11 @@
 	// dismantling when the failed count is too high
 	if (site.no_resources_count > 12) {
 		flags_to_be_removed.push_back(site.site->base_flag().get_position());
-		game().send_player_dismantle(*site.site);
+		if (connected_to_wh) {
+			game().send_player_dismantle(*site.site);
+		} else {
+			game().send_player_bulldoze(*site.site);
+		}
 		site.bo->construction_decision_time_ = gametime;
 		return true;
 	}
@@ -3084,6 +3284,11 @@
 		return false;
 	}
 
+	if (!connected_to_wh) {
+		// no enhancement possible
+		return false;
+	}
+
 	bool changed = false;
 	if (player_->is_building_type_allowed(enhancement)) {
 		// first exclude possibility there are enhancements in construction or unoccupied_
@@ -3212,6 +3417,18 @@
 	return count;
 }
 
+// this just counts free positions in military and training sites
+void DefaultAI::count_military_vacant_positions() {
+	// counting vacant positions
+	vacant_mil_positions_ = 0;
+	for (TrainingSiteObserver tso : trainingsites) {
+		vacant_mil_positions_ += tso.site->soldier_capacity() - tso.site->stationed_soldiers().size();
+	}
+	for (MilitarySiteObserver mso : militarysites) {
+		vacant_mil_positions_ += mso.site->soldier_capacity() - mso.site->stationed_soldiers().size();
+	}
+}
+
 // this function only manipulates with trainingsites' inputs priority
 // decreases it when too many unoccupied military buildings
 bool DefaultAI::check_trainingsites(uint32_t gametime) {
@@ -3221,22 +3438,38 @@
 	if (!trainingsites.empty()) {
 		taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + kTrainingSitesCheckInterval;
 	} else {
-		taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + 5 * kTrainingSitesCheckInterval;
-	}
-
-	uint8_t new_priority = DEFAULT_PRIORITY;
-	if (unstationed_milit_buildings_ > 2) {
-		new_priority = LOW_PRIORITY;
-	} else {
-		new_priority = DEFAULT_PRIORITY;
-	}
+		taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + 2 * kTrainingSitesCheckInterval;
+		return false;
+	}
+
+	TrainingSite* ts = trainingsites.front().site;
+	TrainingSiteObserver& tso = trainingsites.front();
+
+	const BuildingIndex enhancement = ts->descr().enhancement();
+
+	if (enhancement != INVALID_INDEX && ts_without_trainers_ == 0 && mines_.size() > 3 &&
+	    (ts_type1_const_count_ + ts_type2_const_count_) == 0 && ts_type2_count_ > 0) {
+
+		if (player_->is_building_type_allowed(enhancement)) {
+			game().send_player_enhance_building(*tso.site, enhancement);
+		}
+	}
+
+	trainingsites.push_back(trainingsites.front());
+	trainingsites.pop_front();
+
+	// changing capacity
+	if (tso.site->soldier_capacity() != 2) {
+		game().send_player_change_soldier_capacity(*ts, 2 - tso.site->soldier_capacity());
+	}
+
+	ts_without_trainers_ = 0;  // zeroing
 	for (std::list<TrainingSiteObserver>::iterator site = trainingsites.begin();
 	     site != trainingsites.end();
 	     ++site) {
 
-		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), new_priority);
+		if (!site->site->can_start_working()) {
+			ts_without_trainers_ += 1;
 		}
 	}
 	return true;
@@ -3310,14 +3543,18 @@
 				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.military_capacity_ > 5);
-				score += (bf.military_presence_ > 3);
+				score += (bf.area_military_capacity_ > 6);
+				score += (bf.area_military_capacity_ > 18);
+				score += (bf.area_military_presence_ > 4);
 				score += (bf.military_loneliness_ < 180);
-				score += (bf.military_stationed_ > (2 + size_penalty));
-				score -= (ms->soldier_capacity() * 2 > static_cast<uint32_t>(bf.military_capacity_));
-				score += (bf.unowned_land_nearby_ < 10);
+				score += (bf.military_stationed_ > 2);
+				score -= size_penalty;
+				score += ((bf.unowned_land_nearby_ + allyOwnedFields) < 10);
 
 				if (score >= 4) {
 					if (ms->get_playercaps() & Widelands::Building::PCap_Dismantle) {
@@ -3340,7 +3577,6 @@
 
 		// 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::kOtherPlayers)) {
 
@@ -3502,7 +3738,10 @@
 		}
 	}
 
-	throw wexception("Help: I do not know what to do with a %s", name);
+	throw wexception("Help: I (player %d / tribe %s) do not know what to do with a %s",
+	                 player_number(),
+	                 tribe_->name().c_str(),
+	                 name);
 }
 
 // this is called whenever we gain ownership of a PlayerImmovable
@@ -3804,6 +4043,7 @@
 
 // this is called whenever we gain a new building
 void DefaultAI::gain_building(Building& b) {
+
 	BuildingObserver& bo = get_building_observer(b.descr().name().c_str());
 
 	if (bo.type == BuildingObserver::CONSTRUCTIONSITE) {
@@ -3817,6 +4057,17 @@
 		if (target_bo.type == BuildingObserver::MILITARYSITE) {
 			++num_milit_constructionsites;
 		}
+		if (target_bo.type == BuildingObserver::MINE) {
+			mines_per_type[target_bo.mines_].in_construction += 1;
+		}
+		if (target_bo.type == BuildingObserver::TRAININGSITE) {
+			if (target_bo.ts_type_ == 1) {
+				ts_type1_const_count_ += 1;
+			}
+			if (target_bo.ts_type_ == 2) {
+				ts_type2_const_count_ += 1;
+			}
+		}
 
 		// Let defaultAI try to directly connect the constructionsite
 		taskDue[ScheduleTasks::kRoadCheck] = game().get_gametime();
@@ -3852,6 +4103,9 @@
 
 			for (uint32_t i = 0; i < bo.inputs_.size(); ++i)
 				++wares.at(bo.inputs_.at(i)).consumers_;
+
+			mines_per_type[bo.mines_].finished += 1;
+
 		} else if (bo.type == BuildingObserver::MILITARYSITE) {
 			militarysites.push_back(MilitarySiteObserver());
 			militarysites.back().site = &dynamic_cast<MilitarySite&>(b);
@@ -3860,9 +4114,16 @@
 			militarysites.back().enemies_nearby_ = true;
 
 		} else if (bo.type == BuildingObserver::TRAININGSITE) {
+			ts_without_trainers_ += 1;
 			trainingsites.push_back(TrainingSiteObserver());
 			trainingsites.back().site = &dynamic_cast<TrainingSite&>(b);
 			trainingsites.back().bo = &bo;
+			if (bo.ts_type_ == 1) {
+				ts_type1_count_ += 1;
+			}
+			if (bo.ts_type_ == 2) {
+				ts_type2_count_ += 1;
+			}
 
 		} else if (bo.type == BuildingObserver::WAREHOUSE) {
 			++numof_warehouses_;
@@ -3888,6 +4149,7 @@
 
 // this is called whenever we lose a building
 void DefaultAI::lose_building(const Building& b) {
+
 	BuildingObserver& bo = get_building_observer(b.descr().name().c_str());
 
 	if (bo.type == BuildingObserver::CONSTRUCTIONSITE) {
@@ -3901,6 +4163,17 @@
 		if (target_bo.type == BuildingObserver::MILITARYSITE) {
 			--num_milit_constructionsites;
 		}
+		if (target_bo.type == BuildingObserver::MINE) {
+			mines_per_type[target_bo.mines_].in_construction -= 1;
+		}
+		if (target_bo.type == BuildingObserver::TRAININGSITE) {
+			if (target_bo.ts_type_ == 1) {
+				ts_type1_const_count_ -= 1;
+			}
+			if (target_bo.ts_type_ == 2) {
+				ts_type2_const_count_ -= 1;
+			}
+		}
 
 	} else {
 		--bo.cnt_built_;
@@ -3938,6 +4211,9 @@
 			for (uint32_t i = 0; i < bo.inputs_.size(); ++i) {
 				--wares.at(bo.inputs_.at(i)).consumers_;
 			}
+
+			mines_per_type[bo.mines_].finished -= 1;
+
 		} else if (bo.type == BuildingObserver::MILITARYSITE) {
 
 			for (std::list<MilitarySiteObserver>::iterator i = militarysites.begin();
@@ -3955,6 +4231,12 @@
 			     ++i) {
 				if (i->site == &b) {
 					trainingsites.erase(i);
+					if (bo.ts_type_ == 1) {
+						ts_type1_count_ -= 1;
+					}
+					if (bo.ts_type_ == 2) {
+						ts_type2_count_ -= 1;
+					}
 					break;
 				}
 			}
@@ -3999,49 +4281,21 @@
 	return supplied == bo.inputs_.size();
 }
 
-/**
- * The defaultAi "considers" via this function whether to attack an
- * enemy, if opposing military buildings are in sight. In case of an attack it
- * sends all available forces.
- *
- * \returns true, if attack was started.
- */
-
-bool DefaultAI::consider_attack(int32_t const gametime) {
-
-	// we assume that we are not attacking so we extend waitperiod
-	// in case of attack the variable will be decreased below
-	// this is intended to save some CPU and add randomness in attacking
-	// and also differentiate according to type
-	next_attack_waittime_ += gametime % 30;
-	if (next_attack_waittime_ > 600 && type_ == DEFENSIVE) {
-		next_attack_waittime_ = 20;
-	}
-	if (next_attack_waittime_ > 450 && type_ == NORMAL) {
-		next_attack_waittime_ = 20;
-	}
-	if (next_attack_waittime_ > 300 && type_ == AGGRESSIVE) {
-		next_attack_waittime_ = 20;
-	}
-
-	// Only useable, if it owns at least one militarysite
-	if (militarysites.empty()) {
-		taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
-		return false;
-	}
-
-	// First we iterate over all players and define which ones (if any)
-	// are attackable (comparing overal strength)
-	// counting players in game
-	uint32_t plr_in_game = 0;
+bool DefaultAI::check_enemy_sites(uint32_t const gametime) {
+
+	Map& map = game().map();
+
+	// define which players are attackable
 	std::vector<bool> player_attackable;
-	PlayerNumber const nr_players = game().map().get_nrplayers();
+	PlayerNumber const nr_players = map.get_nrplayers();
 	player_attackable.resize(nr_players);
-	bool any_attackable = false;
+	uint32_t plr_in_game = 0;
 	uint16_t const pn = player_number();
-	std::unordered_set<uint32_t> irrelevant_immovables;
-
-	std::vector<ImmovableFound> target_buildings;
+
+	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();
 
 	// defining treshold ratio of own_strenght/enemy's_strength
 	uint32_t treshold_ratio = 100;
@@ -4052,37 +4306,10 @@
 		treshold_ratio = 120;
 	}
 
-	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();
-
-	// first we try to prevent exhaustion of military forces (soldiers)
-	// via excessive attacking
-	// before building an economy with mines.
-	// 'Margin' is an difference between count of actual soldiers and
-	// military sites to be manned.
-	// If we have no mines yet, we need to preserve some soldiers for further
-	// expansion (if enemy allows this)
-	// TODO(sirver): this next line is completely unreadable and maybe even wrong given the
-	// precedence of ?: and +. Replace through some if/else.
-	int32_t needed_margin = (mines_.size() < 6) ?
-	                           ((6 - mines_.size()) * 3) :
-	                           0 + 2 + ((type_ == NORMAL) ? 4 : 0 + ((type_ == DEFENSIVE) ? 8 : 0));
-	const int32_t current_margin =
-	   genstats[pn - 1].miltary_strength.back() - militarysites.size() - num_milit_constructionsites;
-
-	if (current_margin < needed_margin) {  // no attacking!
-		last_attack_target_.x = std::numeric_limits<uint16_t>::max();
-		last_attack_target_.y = std::numeric_limits<uint16_t>::max();
-		taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
-		return false;
-	}
-
 	// now we test all players which one are 'attackable'
 	for (uint8_t j = 1; j <= plr_in_game; ++j) {
-		if (pn == j) {
-			player_attackable.at(j - 1) = false;
+		if (pn == j) {  // its me
+			player_attackable[j - 1] = false;
 			continue;
 		}
 
@@ -4095,12 +4322,10 @@
 				// Avoid division by zero
 			} else if (genstats.at(j - 1).miltary_strength.back() == 0) {
 				player_attackable.at(j - 1) = true;
-				any_attackable = true;
 				// Check threshold
 			} else if ((genstats.at(pn - 1).miltary_strength.back() * 100 /
 			            genstats.at(j - 1).miltary_strength.back()) > treshold_ratio) {
 				player_attackable.at(j - 1) = true;
-				any_attackable = true;
 			} else {
 				player_attackable.at(j - 1) = false;
 			}
@@ -4112,147 +4337,229 @@
 		}
 	}
 
-	// if we cannot attack anybody, terminating...
-	if (!any_attackable) {
-		taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
-		last_attack_target_.x = std::numeric_limits<uint16_t>::max();
-		last_attack_target_.y = std::numeric_limits<uint16_t>::max();
-		return false;
-	}
-
-	// the logic of attacking is to pick n own military buildings - random ones
-	// and test the vicinity for attackable buildings
-	// candidates are put into target_buildings vector for later processing
-	const uint16_t test_every = 4;
-	Map& map = game().map();
-	MilitarySite* best_ms_target = nullptr;
-	Warehouse* best_wh_target = nullptr;
-	int32_t best_ms_score = 0;
-	int32_t best_wh_score = 0;
-	const int8_t minimal_difference = 2;
-
-	for (uint32_t position = gametime % test_every; position < militarysites.size();
-	     position += test_every) {
-
-		std::list<MilitarySiteObserver>::iterator mso = militarysites.begin();
-		std::advance(mso, position);
-
-		MilitarySite* ms = mso->site;
-
-		if (!mso->enemies_nearby_) {
-			continue;
-		}
-
+	// first we scan vicitnity of couple of militarysites to get new enemy sites
+	// militarysites rotate
+	int32_t i = 0;
+	for (MilitarySiteObserver mso : militarysites) {
+		i += 1;
+		if (i % 4 == 0)
+			continue;
+		if (i > 20)
+			continue;
+
+		MilitarySite* ms = mso.site;
 		uint32_t const vision = ms->descr().vision_range();
 		FCoords f = map.get_fcoords(ms->get_position());
 
 		// get list of immovable around this our military site
 		std::vector<ImmovableFound> immovables;
-		map.find_immovables(Area<FCoords>(f, vision + 3), &immovables, FindImmovableAttackable());
+		map.find_immovables(Area<FCoords>(f, (vision + 3 < 13) ? 13 : vision + 3),
+		                    &immovables,
+		                    FindImmovableAttackable());
 
 		for (uint32_t j = 0; j < immovables.size(); ++j) {
-
-			// skip if in irrelevant_immovables
-			const uint32_t hash = coords_hash(immovables.at(j).coords);
-			if (irrelevant_immovables.count(hash) == 0) {
-				irrelevant_immovables.insert(hash);
-			}
-
-			// maybe these are not good candidates to attack
-			if (upcast(MilitarySite, bld, immovables.at(j).object)) {
-
-				if (!player_attackable[bld->owner().player_number() - 1]) {
-					continue;
-				}
-
-				// in case this is the same building as previously attacked
-				if (last_attack_target_ == bld->get_position()) {
-					continue;
-				}
-
+			if (upcast(MilitarySite const, bld, immovables.at(j).object)) {
+				if (player_->is_hostile(bld->owner())) {
+					if (enemy_sites.count(coords_hash(bld->get_position())) == 0) {
+						enemy_sites[coords_hash(bld->get_position())] = EnemySiteObserver();
+					}
+				}
+			}
+			if (upcast(Warehouse const, wh, immovables.at(j).object)) {
+				if (player_->is_hostile(wh->owner())) {
+					if (enemy_sites.count(coords_hash(wh->get_position())) == 0) {
+						enemy_sites[coords_hash(wh->get_position())] = EnemySiteObserver();
+					}
+				}
+			}
+		}
+	}
+
+	// now we update some of them
+	uint32_t best_target = std::numeric_limits<uint32_t>::max();
+	uint8_t best_score = 0;
+	uint32_t count = 0;
+
+	for (std::map<uint32_t, EnemySiteObserver>::iterator site = enemy_sites.begin();
+	     site != enemy_sites.end();
+	     ++site) {
+
+		// 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 = 0;
+		bool is_warehouse = false;
+		bool is_attackable = false;
+		uint16_t onwer_number = 100;
+
+		// 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())) {
+			if (player_->is_hostile(bld->owner())) {
+				defenders = bld->present_soldiers().size();
+				flag = &bld->base_flag();
 				if (bld->can_attack()) {
-
-					int32_t attack_soldiers = player_->find_attack_soldiers(bld->base_flag());
-					if (attack_soldiers < 1) {
-						continue;
-					}
-
-					const int32_t soldiers_difference =
-					   player_->find_attack_soldiers(bld->base_flag()) - bld->present_soldiers().size();
-
-					if (soldiers_difference < minimal_difference)
-						continue;
-					if (soldiers_difference <= best_ms_score)
-						continue;
-
-					best_ms_target = bld;
-					best_ms_score = soldiers_difference;
-					continue;
-				}
-			} else if (upcast(Warehouse, wh, immovables.at(j).object)) {
-				if (!player_->is_hostile(wh->owner())) {
-					continue;
-				}
-
-				// in case this is the same building as previously attacked
-				if (last_attack_target_ == wh->get_position()) {
-					continue;
-				}
-
-				if (wh->can_attack()) {
-					int32_t attack_soldiers = player_->find_attack_soldiers(wh->base_flag());
-					if (attack_soldiers < 1) {
-						continue;
-					}
-
-					const int32_t soldiers_difference = player_->find_attack_soldiers(wh->base_flag()) -
-					                                    wh->present_soldiers().size() +
-					                                    3;  //+3 is to boost attack here
-
-					if (soldiers_difference < minimal_difference)
-						continue;
-					if (soldiers_difference <= best_wh_score)
-						continue;
-
-					best_wh_target = wh;
-					best_wh_score = soldiers_difference;
-				}
-			}
-		}
-	}
-
-	// we always try to attack warehouse first
-	if (best_wh_target != nullptr && gametime % 2 == 0) {
-		// attacking with all attack-ready soldiers
-		int32_t attackers = player_->find_attack_soldiers(best_wh_target->base_flag());
-
-		game().send_player_enemyflagaction(best_wh_target->base_flag(), pn, attackers);
-		last_attack_target_ = best_wh_target->get_position();
-		taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime;
-		next_attack_waittime_ = 10;
-		return true;
-
-	} else if (best_ms_target != nullptr) {
-
-		// attacking with defenders + 6 soldiers
-		int32_t attackers = player_->find_attack_soldiers(best_ms_target->base_flag());
-		const int32_t defenders = best_ms_target->present_soldiers().size();
-		if (attackers > defenders + 10) {  // we need to leave meaningful count of soldiers
-			// for next attack
-			attackers = defenders + 6;
-		}
-
-		game().send_player_enemyflagaction(best_ms_target->base_flag(), pn, attackers);
-		last_attack_target_ = best_ms_target->get_position();
-		taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime;
-		next_attack_waittime_ = 10;
-		return true;
+					is_attackable = true;
+				}
+				onwer_number = bld->owner().player_number();
+			}
+		}
+		if (upcast(Warehouse, Wh, f.field->get_immovable())) {
+			if (player_->is_hostile(Wh->owner())) {
+				defenders = Wh->present_soldiers().size();
+				flag = &Wh->base_flag();
+				is_warehouse = true;
+				if (Wh->can_attack()) {
+					is_attackable = true;
+				}
+				onwer_number = Wh->owner().player_number();
+			}
+		}
+
+		// if flag is defined it is a good taget
+		if (flag) {
+			// updating some info
+			// updating info on mines nearby if needed
+			if (site->second.mines_nearby == ExtendedBool::kUnset) {
+				FindNodeMineable find_mines_spots_nearby(game(), f.field->get_resources());
+				const int32_t minescount =
+				   map.find_fields(Area<FCoords>(f, 6), nullptr, find_mines_spots_nearby);
+				if (minescount > 0) {
+					site->second.mines_nearby = ExtendedBool::kTrue;
+				} else {
+					site->second.mines_nearby = ExtendedBool::kFalse;
+				}
+			}
+
+			site->second.warehouse = is_warehouse;
+
+			// getting rid of default
+			if (site->second.last_time_attackable == std::numeric_limits<uint32_t>::max()) {
+				site->second.last_time_attackable = gametime;
+			}
+
+			// can we attack:
+			if (is_attackable) {
+				site->second.attack_soldiers = player_->find_attack_soldiers(*flag);
+			} else {
+				site->second.attack_soldiers = 0;
+			}
+
+			site->second.defenders = defenders;
+
+			if (site->second.attack_soldiers > 0) {
+				site->second.score = site->second.attack_soldiers - site->second.defenders / 2;
+
+				if (!is_warehouse)
+					site->second.score -= 1;
+
+				// here is some differentiation based on "character" of a player
+				if (type_ == NORMAL) {
+					site->second.score -= 1;
+					site->second.score -= vacant_mil_positions_ / 10;
+				} else if (type_ == DEFENSIVE) {
+					site->second.score -= 2;
+					site->second.score -= vacant_mil_positions_ / 5;
+				} else {  //=AGRESSIVE
+					site->second.score -= vacant_mil_positions_ / 15;
+				}
+				if (site->second.mines_nearby == ExtendedBool::kFalse) {
+					site->second.score -= 1;
+				}
+				// we dont want to attack multiple players at the same time too eagerly
+				if (onwer_number != last_attacked_player_) {
+					site->second.score -= 3;
+				}
+				// if we dont have mines yet
+				if (mines_.size() <= 2) {
+					site->second.score -= 2;
+				}
+				// also we should have at least some training sites
+				if ((ts_type1_count_ + ts_type2_count_) == 0) {
+					site->second.score -= 2;
+				}
+				// treating no attack score
+				if (site->second.no_attack_counter < 0) {
+					site->second.score = 0;
+					site->second.no_attack_counter += 1;
+				}
+			} else {
+				site->second.score = 0;
+			}  // or the score will remain 0
+
+			if (site->second.score > 0 && player_attackable[onwer_number - 1]) {
+				if (site->second.score > best_score) {
+					best_score = site->second.score;
+					best_target = site->first;
+				}
+			}
+
+			if (site->second.attack_soldiers > 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()) {
+			enemy_sites.erase(site_to_be_removed);
+			continue;
+		}
+	}
+
+	// modifying enemysites_check_delay_,this depends on the count
+	// of enemysites in observer
+	if (count >= 13 && enemysites_check_delay_ < 180) {
+		enemysites_check_delay_ += 3;
+	}
+	if (count < 10 && enemysites_check_delay_ > 45) {
+		enemysites_check_delay_ -= 2;
+	}
+
+	// if coordinates hash is not set
+	if (best_target == std::numeric_limits<uint32_t>::max()) {
+		return false;
+	}
+
+	// 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 4
+	// turns
+	enemy_sites[best_target].no_attack_counter = -4;
+
+	Flag* flag = nullptr;  // flag of a building to be attacked
+	if (upcast(MilitarySite, bld, f.field->get_immovable())) {
+		flag = &bld->base_flag();
+	} else if (upcast(Warehouse, Wh, f.field->get_immovable())) {
+		flag = &Wh->base_flag();
 	} else {
-		taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
-		last_attack_target_.x = std::numeric_limits<uint16_t>::max();
-		last_attack_target_.y = std::numeric_limits<uint16_t>::max();
+		return false;  // this should not happen
+	}
+
+	// how many attack soldiers we can send?
+	uint32_t attackers = player_->find_attack_soldiers(*flag);
+	// Just add some randomness
+	attackers -= gametime % 3;
+	if (attackers <= 0) {
 		return false;
 	}
+
+	game().send_player_enemyflagaction(*flag, player_number(), attackers);
+	last_attacked_player_ = flag->owner().player_number();
+
+	return true;
 }
 
 // This runs once in 15 minutes, and adjust wares targets based on number of
@@ -4286,7 +4593,7 @@
 // run over dueTasks map and returns task with lower duetime
 DefaultAI::ScheduleTasks DefaultAI::get_oldest_task(uint32_t const gametime) {
 
-	uint32_t oldestTaskTime = gametime;             // we are looking for jobs due before now
+	uint32_t oldestTaskTime = gametime;            // we are looking for jobs due before now
 	ScheduleTasks DueTask = ScheduleTasks::kIdle;  // default
 	taskDue[ScheduleTasks::kIdle] = gametime;
 

=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h	2015-03-05 20:57:07 +0000
+++ src/ai/defaultai.h	2015-03-23 21:37:01 +0000
@@ -9,6 +9,7 @@
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
@@ -82,9 +83,9 @@
 	enum class NewShip : uint8_t {kBuilt, kFoundOnLoad};
 	enum class ScheduleTasks : uint8_t {
 		kBbuildableFieldsCheck,
+		kMineableFieldsCheck,
 		kRoadCheck,
 		kUnbuildableFCheck,
-		kConsiderAttack,
 		kCheckEconomies,
 		kProductionsitesStats,
 		kConstructBuilding,
@@ -96,7 +97,9 @@
 		kPrintStats,
 		kIdle,
 		kCheckMilitarysites,
-		kCheckTrainingsites
+		kCheckTrainingsites,
+		kCountMilitaryVacant,
+		kCheckEnemySites
 	};
 	enum class MilitaryStrategy : uint8_t {
 		kNoNewMilitary,
@@ -161,7 +164,6 @@
 	                          int16_t* max_preciousness,
 	                          int16_t* max_needed_preciousness);
 
-
 	ScheduleTasks get_oldest_task(uint32_t);
 
 	bool construct_building(uint32_t);
@@ -195,12 +197,14 @@
 	bool check_militarysites(uint32_t);
 	bool marine_main_decisions(uint32_t);
 	bool check_ships(uint32_t);
+	bool check_enemy_sites(uint32_t);
 	void print_stats(uint32_t);
 	uint32_t get_stocklevel_by_hint(size_t);
 	uint32_t get_stocklevel(BuildingObserver&);
 	uint32_t get_warehoused_stock(Widelands::WareIndex wt);
 	uint32_t get_stocklevel(Widelands::WareIndex);  // count all direct outputs_
 	void review_wares_targets(uint32_t);
+	void count_military_vacant_positions();
 
 	// sometimes scanning an area in radius gives inappropriate results, so this is to verify that
 	// other player is accessible
@@ -232,7 +236,7 @@
 
 	bool check_supply(const BuildingObserver&);
 
-	bool consider_attack(int32_t);
+	// bool consider_attack(int32_t);
 
 	void print_land_stats();
 
@@ -252,6 +256,10 @@
 	uint32_t num_prod_constructionsites;
 	uint32_t num_ports;
 
+	uint16_t last_attacked_player_;
+	// check ms in this interval - will auto-adjust
+	uint32_t enemysites_check_delay_;
+
 	std::list<Widelands::FCoords> unusable_fields;
 	std::list<BuildableField*> buildable_fields;
 	std::list<BlockedField> blocked_fields;
@@ -268,6 +276,9 @@
 	std::list<TrainingSiteObserver> trainingsites;
 	std::list<ShipObserver> allships;
 	std::map<ScheduleTasks, uint32_t> taskDue;
+	std::map<uint32_t, EnemySiteObserver> enemy_sites;
+	// it will map mined material to observer
+	std::map<int32_t, MineTypesObserver> mines_per_type;
 
 	std::vector<WareObserver> wares;
 
@@ -284,10 +295,10 @@
 	// 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
-	uint8_t resource_necessity_territory_;
-	uint8_t resource_necessity_mines_;
-	uint8_t resource_necessity_stones_;
-	uint8_t resource_necessity_water_;
+	int32_t resource_necessity_territory_;
+	int32_t resource_necessity_mines_;
+	int32_t resource_necessity_stones_;  // NOCOM
+	int32_t resource_necessity_water_;
 	bool resource_necessity_water_needed_;  // unless atlanteans
 
 	uint16_t unstationed_milit_buildings_;  // counts empty military buildings (ones where no soldier
@@ -295,14 +306,21 @@
 	uint16_t military_under_constr_;
 	uint16_t military_last_dismantle_;
 	uint32_t military_last_build_;  // sometimes expansions just stops, this is time of last military
-	                               // building build
+	                                // building build
 	Widelands::Coords
-	   last_attack_target_;         // flag to abuilding (position) that was attacked last time
+	   last_attack_target_;          // flag to abuilding (position) that was attacked last time
 	uint32_t next_attack_waittime_;  // second till the next attack consideration
-	bool seafaring_economy;         // false by default, until first port space is found
+	bool seafaring_economy;          // false by default, until first port space is found
 	uint32_t colony_scan_area_;  // distance from a possible port that is scanned for owned territory
 	// it decreases with failed scans
 	int32_t spots_;  // sum of buildable fields
+	int32_t vacant_mil_positions_;  // sum of vacant positions in militarysites and training sites
+	//statistics for training sites per type
+	uint8_t ts_type1_count_;
+	uint8_t ts_type1_const_count_;
+	uint8_t ts_type2_count_;
+	uint8_t ts_type2_const_count_;
+	uint8_t ts_without_trainers_;
 
 	enum {kReprioritize, kStopShipyard, kStapShipyard};
 

=== modified file 'tribes/atlanteans/dungeon/conf'
--- tribes/atlanteans/dungeon/conf	2014-03-17 17:23:26 +0000
+++ tribes/atlanteans/dungeon/conf	2015-03-23 21:37:01 +0000
@@ -77,3 +77,6 @@
 [idle]
 pics=dungeon_i_??.png  # ???
 hotspot=47 48
+
+[aihints]
+ts_type=2

=== modified file 'tribes/atlanteans/labyrinth/conf'
--- tribes/atlanteans/labyrinth/conf	2014-10-07 20:06:46 +0000
+++ tribes/atlanteans/labyrinth/conf	2015-03-23 21:37:01 +0000
@@ -99,4 +99,4 @@
 hotspot=80 88
 
 [aihints]
-prohibited_till=2700
+ts_type=1

=== modified file 'tribes/barbarians/battlearena/conf'
--- tribes/barbarians/battlearena/conf	2014-07-29 09:27:08 +0000
+++ tribes/barbarians/battlearena/conf	2015-03-23 21:37:01 +0000
@@ -74,3 +74,6 @@
 pics=battlearena_w_??.png  # ???
 hotspot=110 72
 fps=10
+
+[aihints]
+ts_type=1

=== modified file 'tribes/barbarians/trainingcamp/conf'
--- tribes/barbarians/trainingcamp/conf	2014-09-24 21:06:00 +0000
+++ tribes/barbarians/trainingcamp/conf	2015-03-23 21:37:01 +0000
@@ -36,7 +36,7 @@
 max_level=4
 
 [aihints]
-prohibited_till=2700
+ts_type=2
 
 [soldier hp]
 min_level=0

=== modified file 'tribes/empire/arena/conf'
--- tribes/empire/arena/conf	2014-09-30 08:05:35 +0000
+++ tribes/empire/arena/conf	2015-03-23 21:37:01 +0000
@@ -54,3 +54,6 @@
 [build]
 pics=arena_b_??.png  # ???
 hotspot=82 83
+
+[aihints]
+ts_type=1

=== modified file 'tribes/empire/colosseum/conf'
--- tribes/empire/colosseum/conf	2014-09-30 08:05:35 +0000
+++ tribes/empire/colosseum/conf	2015-03-23 21:37:01 +0000
@@ -60,3 +60,6 @@
 [idle]
 pics=colosseum_i_??.png  # ???
 hotspot=81 106
+
+[aihints]
+ts_type=1

=== modified file 'tribes/empire/piggery/conf'
--- tribes/empire/piggery/conf	2014-08-02 19:55:23 +0000
+++ tribes/empire/piggery/conf	2015-03-23 21:37:01 +0000
@@ -2,6 +2,7 @@
 output=meat
 
 [aihints]
+forced_after=9000
 
 [buildcost]
 log=2

=== modified file 'tribes/empire/trainingcamp/conf'
--- tribes/empire/trainingcamp/conf	2014-08-02 19:55:23 +0000
+++ tribes/empire/trainingcamp/conf	2015-03-23 21:37:01 +0000
@@ -127,4 +127,4 @@
 hotspot=82 105
 
 [aihints]
-prohibited_till=2700
+ts_type=2


Follow ups