← Back to team overview

widelands-dev team mailing list archive

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

 

Review: Needs Fixing

Please change the AI hint from ts_type=2 to something that is more understandable outside of the AI context, maybe trainingssite_type="no_bread_just_meat" or something along these lines. Numbers are fully opaque to everybody that looks at the conf files. 

Diff comments:

> === 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) {

replacing these if /elif clases through one switch() will make them more efficient and easier to read.

>  		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

please use trainingssite_type and a string instead of an integer

> 
> === 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
> 


-- 
https://code.launchpad.net/~widelands-dev/widelands/ai-military-changes/+merge/253881
Your team Widelands Developers is subscribed to branch lp:~widelands-dev/widelands/ai-military-changes.


References