← Back to team overview

widelands-dev team mailing list archive

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

 

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

Requested reviews:
  Widelands Developers (widelands-dev)

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

Another bunch of changes to AI
- better management of trainingsites
- better expansion policy (building farms and other internal paramenters)
- better calculation of military strength of soldiers
- better management of rangers
- multiple smaller changes
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands.
=== modified file 'src/ai/ai_help_structs.h'
--- src/ai/ai_help_structs.h	2015-05-05 20:00:21 +0000
+++ src/ai/ai_help_structs.h	2015-05-28 19:39:10 +0000
@@ -382,6 +382,15 @@
 	std::vector<int16_t> inputs_;
 	std::vector<int16_t> outputs_;
 	std::vector<Widelands::WareIndex> critical_built_mat_;
+
+	bool upgrade_substitutes_;
+
+	//it seems that fish and meat are subsitutes (for trainingsites), so
+	// when testing ifa trainingsite is supplied enough
+	// we count the wares together
+	std::unordered_set<Widelands::WareIndex> substitute_inputs_;
+	int32_t substitutes_count_;
+
 	int16_t production_hint_;
 
 	int32_t cnt_built_;
@@ -457,8 +466,8 @@
 //is stored in the observer
 struct EnemySiteObserver {
 	bool warehouse_;
-	uint8_t attack_soldiers;
-	uint8_t defenders;
+	int32_t attack_soldiers_strength;
+	int32_t defenders_strength;
 	uint8_t stationed_soldiers;
 	uint32_t last_time_attackable;
 	uint32_t last_tested;
@@ -468,8 +477,8 @@
 
 	EnemySiteObserver()
 	   : warehouse_(false),
-	     attack_soldiers(0),
-	     defenders(0),
+	     attack_soldiers_strength(0),
+	     defenders_strength(0),
 	     stationed_soldiers(0),
 	     last_time_attackable(std::numeric_limits<uint32_t>::max()),
 	     last_tested(0),

=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc	2015-05-07 20:46:32 +0000
+++ src/ai/defaultai.cc	2015-05-28 19:39:10 +0000
@@ -44,6 +44,7 @@
 #include "logic/playercommand.h"
 #include "logic/productionsite.h"
 #include "logic/ship.h"
+#include "logic/soldier.h"
 #include "logic/trainingsite.h"
 #include "logic/tribe.h"
 #include "logic/warehouse.h"
@@ -62,7 +63,7 @@
 constexpr int kMinMFCheckInterval = 19 * 1000;
 constexpr int kShipCheckInterval = 5 * 1000;
 constexpr int kMarineDecisionInterval = 20 * 1000;
-constexpr int kTrainingSitesCheckInterval = 5 * 60 * 1000;
+constexpr int kTrainingSitesCheckInterval = 45 * 1000;
 
 // this is intended for map developers, by default should be off
 constexpr bool kPrintStats = false;
@@ -90,6 +91,7 @@
      num_ports(0),
      last_attacked_player_(std::numeric_limits<uint16_t>::max()),
      enemysites_check_delay_(60),
+     wood_policy_(WoodPolicy::kStartRangers),
      next_ai_think_(0),
      next_mine_construction_due_(0),
      inhibit_road_building_(0),
@@ -112,7 +114,8 @@
      ts_basic_const_count_(0),
      ts_advanced_count_(0),
      ts_advanced_const_count_(0),
-     ts_without_trainers_(0) {
+     ts_without_trainers_(0),
+     scheduler_delay_counter_(0) {
 
 	// Subscribe to NoteFieldPossession.
 	field_possession_subscriber_ =
@@ -152,6 +155,18 @@
 
 		});
 
+	// Subscribe to TrainingSiteSoldierTrained.
+	soldiertrained_subscriber_ = Notifications::subscribe<NoteTrainingSiteSoldierTrained>(
+	   [this](const NoteTrainingSiteSoldierTrained& note) {
+		   	if (note.ts->owner().player_number() != player_->player_number()) {
+			   return;
+		   }
+
+		   soldier_trained(*note.ts);
+
+		});
+
+
 	// Subscribe to ShipNotes.
 	shipnotes_subscriber_ =
 	   Notifications::subscribe<NoteShipMessage>([this](const NoteShipMessage& note) {
@@ -253,8 +268,16 @@
 			if (check_economies()) {  // is a must
 				return;
 			};
-			taskDue[ScheduleTasks::kRoadCheck] = gametime + 400;
-			improve_roads(gametime);
+			taskDue[ScheduleTasks::kRoadCheck] = gametime + 1000;
+			//testing 5 roads
+			{int32_t roads_to_check = (roads.size() + 1 < 5) ? roads.size() + 1:5;
+			for (int i = 0; i < roads_to_check; i += 1){
+				if (improve_roads(gametime)){
+					//if significant change takes place do not go on
+					break;
+					};
+				}
+			}
 			break;
 		case ScheduleTasks::kUnbuildableFCheck :
 			taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000;
@@ -280,8 +303,15 @@
 			if (check_economies()) {  // economies must be consistent
 				return;
 			}
-			check_productionsites(gametime);
-			taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 5000;
+			{int32_t ps_to_check = (productionsites.size()<5)?productionsites.size():5;
+			for (int i = 0; i < ps_to_check; i += 1){
+				if (check_productionsites(gametime)){
+					//if significant change takes place do not go on
+					break;
+					};
+				}
+			}
+			taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 15000;
 			break;
 		case ScheduleTasks::kCheckShips :
 			check_ships(gametime);
@@ -293,8 +323,16 @@
 			if (check_economies()) {  // economies must be consistent
 				return;
 			}
-			taskDue[ScheduleTasks::kCheckMines] = gametime + 7000;  // 7 seconds is enough
-			check_mines_(gametime);
+			taskDue[ScheduleTasks::kCheckMines] = gametime + 15000;
+			//checking 3 mines if possible
+			{int32_t mines_to_check = (mines_.size()<5)?mines_.size():5;
+			for (int i = 0; i < mines_to_check; i += 1){
+				if (check_mines_(gametime)){
+					//if significant change takes place do not go on
+					break;
+					};
+				}
+			}
 			break;
 		case ScheduleTasks::kCheckMilitarysites :
 			check_militarysites(gametime);
@@ -304,7 +342,7 @@
 			break;
 		case ScheduleTasks::kCountMilitaryVacant :
 			count_military_vacant_positions();
-			taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 90 * 1000;
+			taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 60 * 1000;
 			break;
 		case ScheduleTasks::kWareReview :
 			if (check_economies()) {  // economies must be consistent
@@ -391,6 +429,7 @@
 		bo.forced_after_ = bh.get_forced_after() * 1000;        // value in conf is in seconds
 		bo.is_port_ = bld.get_isport();
 		bo.trainingsite_type_ = TrainingSiteType::kNoTS;
+		bo.upgrade_substitutes_ = false;
 
 		if (bh.renews_map_resource()) {
 			bo.production_hint_ = tribe_->safe_ware_index(bh.get_renews_map_resource());
@@ -448,6 +487,38 @@
 				bo.is_shipyard_ = false;
 			}
 
+			// now we find out if the upgrade of the building is a full substitution
+			// (produces all wares as current one)
+			const BuildingIndex enhancement = bld.enhancement();
+			if (enhancement != INVALID_INDEX && bo.type == BuildingObserver::PRODUCTIONSITE) {
+				std::unordered_set<WareIndex> enh_outputs;
+				const ProductionSiteDescr& enh_prod
+					=
+					dynamic_cast<const ProductionSiteDescr&>(*tribe_->get_building_descr(enhancement));
+
+				// collecting wares that are produced in enhanced building
+				for (const WareIndex& ware : enh_prod.output_ware_types()) {
+					enh_outputs.insert(ware);
+				}
+				//now testing outputs of current building
+				//and comparing
+				bo.upgrade_substitutes_ = true;
+				for (WareIndex ware : bo.outputs_) {
+					if (enh_outputs.count(ware) == 0){
+						bo.upgrade_substitutes_ = false;
+						break;
+					}
+				}
+			}
+
+			// plus some manually picked buildings,
+			// see preffered_upgrade list
+			for (const char* pb : preffered_upgrade) {
+				if (strcmp(bld.name().c_str(), pb) == 0) {
+					bo.upgrade_substitutes_ = true;
+				}
+			}
+
 			continue;
 		}
 
@@ -482,7 +553,16 @@
 			const TrainingSiteDescr& train = dynamic_cast<const TrainingSiteDescr&>(bld);
 			for (const WareAmount& temp_input : train.inputs()) {
 				bo.inputs_.push_back(temp_input.first);
+
+				//collecting subsitutes
+				if (tribe_->ware_index("meat") == temp_input.first ||
+				    tribe_->ware_index("fish") == temp_input.first ||
+				    tribe_->ware_index("smoked_meat") == temp_input.first ||
+				    tribe_->ware_index("smoked_fish") == temp_input.first) {
+					bo.substitute_inputs_.insert(temp_input.first);
+				}
 			}
+
 			bo.trainingsite_type_ = bh.get_trainingsite_type();
 			// it would behave badly if no type was set
 			// make sure all TS have its type set properly in conf files
@@ -496,6 +576,8 @@
 		}
 	}
 
+
+
 	// atlanteans they consider water as a resource
 	// (together with mines, stones and wood)
 	if (tribe_->name() == "atlanteans") {
@@ -592,7 +674,7 @@
 	taskDue[ScheduleTasks::kCheckShips] = 30 * 1000;
 	taskDue[ScheduleTasks::kCheckEconomies] = 1000;
 	taskDue[ScheduleTasks::KMarineDecisions] = 30 * 1000;
-	taskDue[ScheduleTasks::kCheckTrainingsites] = 15 * 60 * 1000;
+	taskDue[ScheduleTasks::kCheckTrainingsites] = 2 * 60 * 1000;
 	taskDue[ScheduleTasks::kBbuildableFieldsCheck] = 1000;
 	taskDue[ScheduleTasks::kMineableFieldsCheck] = 1000;
 	taskDue[ScheduleTasks::kUnbuildableFCheck] = 1000;
@@ -870,7 +952,6 @@
 					if (player_->is_hostile(player_immovable->owner())) {
 						field.enemy_nearby_ = true;
 					}
-					enemy_last_seen_ = gametime;
 
 					continue;
 				}
@@ -1172,6 +1253,21 @@
 	new_buildings_stop_ = false;
 	MilitaryStrategy expansion_mode = MilitaryStrategy::kResourcesOrDefense;
 
+	// helper variable - we need some proportion of free spots vs productionsites
+	// the proportion depends on size of economy
+	// this proportion defines how dense the buildings will be
+	// it is degressive (allows high density on the beginning)
+	int32_t needed_spots = 0;
+	if (productionsites.size() < 50) {
+		needed_spots = productionsites.size();
+	} else if (productionsites.size() < 100) {
+		needed_spots =   50 + (productionsites.size() -  50) *  5;
+	} else if (productionsites.size() < 200) {
+		needed_spots =  300 + (productionsites.size() - 100) * 10;
+	} else {
+		needed_spots = 1300 + (productionsites.size() - 200) * 20;
+	}
+
 	// there are many reasons why to stop building production buildings
 	// (note there are numerous exceptions)
 	// 1. to not have too many constructionsites
@@ -1179,12 +1275,12 @@
 		new_buildings_stop_ = true;
 	}
 	// 2. to not exhaust all free spots
-	if (spots_ * 3 / 2 + 5 < static_cast<int32_t>(productionsites.size())) {
+	if (spots_ < needed_spots) {
 		new_buildings_stop_ = true;
 	}
 	// 3. too keep some proportions production sites vs military sites
 	if ((num_prod_constructionsites + productionsites.size()) >
-	    (num_milit_constructionsites + militarysites.size()) * 3) {
+	    (num_milit_constructionsites + militarysites.size()) * 5) {
 		new_buildings_stop_ = true;
 	}
 	// 4. if we do not have 3 mines at least
@@ -1192,26 +1288,24 @@
 		new_buildings_stop_ = true;
 	}
 	// BUT if enemy is nearby, we cancel above stop
-	if (new_buildings_stop_ && enemy_last_seen_ + 2 * 60 * 1000 > gametime) {
+	if (new_buildings_stop_ && enemy_last_seen_ + 10 * 60 * 1000 > gametime) {
 		new_buildings_stop_ = false;
 	}
 
-	// sometimes there is too many military buildings in construction, so we must
-	// prevent initialization of further buildings start
-	const int32_t vacant_plus_in_construction_minus_prod =
-	   vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 7;
-	if (vacant_plus_in_construction_minus_prod > 20) {
-		expansion_mode = MilitaryStrategy::kNoNewMilitary;
-	} else if (vacant_plus_in_construction_minus_prod > 13) {
-		expansion_mode = MilitaryStrategy::kDefenseOnly;
-	} else if (vacant_plus_in_construction_minus_prod > 6) {
-		expansion_mode = MilitaryStrategy::kResourcesOrDefense;
+	//we must calculate wood policy
+	const WareIndex wood_index = tribe_->safe_ware_index("log");
+	// the name of variable is not 100% proper
+	const int32_t stocked_wood = get_warehoused_stock(wood_index) -
+		productionsites.size() * 2 -
+		num_prod_constructionsites;
+	if (stocked_wood > 80) {
+		wood_policy_ = WoodPolicy::kDismantleRangers;
+	} else if  (stocked_wood > 25) {
+		wood_policy_ = WoodPolicy::kStopRangers;
+	} else if  (stocked_wood > 10) {
+		wood_policy_ = WoodPolicy::kStartRangers;
 	} else {
-		if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
-			expansion_mode = MilitaryStrategy::kExpansion;
-		} else {
-			expansion_mode = MilitaryStrategy::kPushExpansion;
-		}
+		wood_policy_ = WoodPolicy::kBuildRangers;
 	}
 
 	// we must consider need for mines
@@ -1243,6 +1337,27 @@
 		}
 	}
 
+	// this controls a speed and willingness to expand the teritorry
+	const int32_t vacant_plus_in_construction_minus_prod =
+	   vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 7;
+	if (vacant_plus_in_construction_minus_prod > 20) {
+		expansion_mode = MilitaryStrategy::kNoNewMilitary;
+	} else if (vacant_plus_in_construction_minus_prod > 13) {
+		expansion_mode = MilitaryStrategy::kDefenseOnly;
+	} else if (vacant_plus_in_construction_minus_prod > 6) {
+		expansion_mode = MilitaryStrategy::kResourcesOrDefense;
+	} else {
+		//this is intended for initial phase of game when the player has enough soldiers yet
+		//but we still want to force it to follow resources instead for plain expansion
+		if (virtual_mines <= 2 && (unstationed_milit_buildings_ + num_milit_constructionsites) > 2) {
+			expansion_mode = MilitaryStrategy::kResourcesOrDefense;
+		} else if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
+			expansion_mode = MilitaryStrategy::kExpansion;
+		} else {
+			expansion_mode = MilitaryStrategy::kPushExpansion;
+		}
+	}
+
 	BuildingObserver* best_building = nullptr;
 	int32_t proposed_priority = 0;
 	Coords proposed_coords;
@@ -1559,12 +1674,6 @@
 							continue;
 						}
 
-						if (bo.stocklevel_time < game().get_gametime() - 5 * 1000) {
-							bo.stocklevel_ =
-							   get_stocklevel_by_hint(static_cast<size_t>(bo.production_hint_));
-							bo.stocklevel_time = game().get_gametime();
-						}
-
 						if (bo.total_count() == 0) {
 							prio = 200;
 						}
@@ -1572,9 +1681,10 @@
 							continue;
 						}
 						// we can go above target if there is shortage of logs on stock
-						else if (bo.total_count() >= bo.cnt_target_ &&
-						         bo.stocklevel_ > 40 + productionsites.size() * 2) {
+						else if (bo.total_count() >= bo.cnt_target_) {
+							if (wood_policy_ != WoodPolicy::kBuildRangers) {
 							continue;
+							}
 						}
 
 						// considering near trees and producers
@@ -1632,8 +1742,19 @@
 						continue;
 					}
 
-					if ((bo.cnt_under_construction_ + bo.unoccupied_) > 0) {
-						continue;
+					// generally we allow 1 building in construction, but if
+					// preciousness of missing ware is >=10 and it is farm-like building
+					// we allow 2 in construction
+					if (max_needed_preciousness >= 10
+						&& bo.inputs_.empty()
+						&& gametime > 30 * 60 * 1000) {
+						if ((bo.cnt_under_construction_ + bo.unoccupied_) > 1) {
+							continue;
+						}
+					} else {
+						if ((bo.cnt_under_construction_ + bo.unoccupied_) > 0) {
+							continue;
+						}
 					}
 
 					if (bo.forced_after_ < gametime && (bo.total_count() - bo.unconnected_) == 0) {
@@ -1653,9 +1774,9 @@
 						prio += kDefaultPrioBoost;
 					} else if ((bo.cnt_built_ - bo.unconnected_) > 1 && bo.current_stats_ > 97) {
 						prio -= kDefaultPrioBoost * (new_buildings_stop_);
-					} else if (new_buildings_stop_)
+					} else if (new_buildings_stop_){
 						continue;
-
+					}
 					// we check separatelly buildings with no inputs and some inputs
 					if (bo.inputs_.empty()) {
 
@@ -1683,7 +1804,6 @@
 						if (!bo.space_consumer_) {
 							prio -= bf->producers_nearby_.at(bo.outputs_.at(0)) * 20;
 						}  // leave some free space between them
-
 					}
 
 					else if (bo.is_shipyard_) {
@@ -1699,12 +1819,9 @@
 						}
 						if ((bo.cnt_built_ - bo.unconnected_) > 0
 							&&
-							//due to very badly designed statistics and the way how
-							//productionsites are working we must distinguish how many
-							//outputs the site has.
-							((bo.outputs_.size() == 1 && bo.current_stats_ > 75)
-							||
-							(bo.outputs_.size() > 1 && bo.current_stats_ > 55))) {
+							is_productionsite_needed(bo.outputs_.size(),
+													bo.current_stats_,
+													PerfEvaluation::kForConstruction)) {
 							prio += max_needed_preciousness + kDefaultPrioBoost - 3 +
 							        (bo.current_stats_ - 55) / 8;
 						}
@@ -1714,6 +1831,15 @@
 						continue;
 					}
 
+					//bonus for big buildings if shortage of big fields
+					if (spots_avail.at(BUILDCAPS_BIG) <= 5 && bo.desc->get_size() == 3) {
+						prio += 10;
+						}
+
+					if (spots_avail.at(BUILDCAPS_MEDIUM) <= 5 && bo.desc->get_size() == 2) {
+						prio += 5;
+						}
+
 					//+1 if any consumers_ are nearby
 					consumers_nearby_count = 0;
 
@@ -1723,6 +1849,7 @@
 					if (consumers_nearby_count > 0) {
 						prio += 1;
 					}
+
 				}
 			}  // production sites done
 			else if (bo.type == BuildingObserver::MILITARYSITE) {
@@ -2029,14 +2156,14 @@
 					continue;
 				}
 
-				// we build one training site for 100 militarysites
+				// we build one basic training site for 50 militarysites
 				if (bo.trainingsite_type_ == TrainingSiteType::kBasic &&
-				    militarysites.size() / 100 < static_cast<int32_t>(ts_basic_count_)) {
+				    militarysites.size() / 50 < static_cast<int32_t>(ts_basic_count_)) {
 					continue;
 				}
-				// we build one training site for 100 militarysites
+				// we build one advanced training site for 75 militarysites
 				if (bo.trainingsite_type_ == TrainingSiteType::kAdvanced &&
-				    militarysites.size() / 100 < static_cast<int32_t>(ts_advanced_count_)) {
+				    militarysites.size() / 75 < static_cast<int32_t>(ts_advanced_count_)) {
 					continue;
 				}
 
@@ -2080,9 +2207,9 @@
 			}
 
 			// Prefer road side fields
-			prio += bf->preferred_ ? 1 : 0;
+			prio += bf->preferred_ ? 5 : 0;
 			// don't waste good land for small huts
-			prio -= (maxsize - bo.desc->get_size()) * 5;
+			prio -= (maxsize - bo.desc->get_size()) * 20;
 
 			// prefer vicinity of ports (with exemption of warehouses)
 			if (bf->port_nearby_ && bo.type == BuildingObserver::MILITARYSITE) {
@@ -2148,7 +2275,7 @@
 				    0) {
 					nearness_penalty = 0;
 				} else {
-					nearness_penalty = 30;
+					nearness_penalty = 40;
 				}
 
 				// bonus score to prefer if too few mines
@@ -2338,12 +2465,13 @@
 	}
 
 	if (inhibit_road_building_ >= gametime) {
-		return false;
+		return true;
 	}
 
 	// now we rotate economies and flags to get one flag to go on with
 	if (economies.empty()) {
-		return check_economies();
+		check_economies();
+		return false;
 	}
 
 	if (economies.size() >= 2) {  // rotating economies
@@ -2353,7 +2481,8 @@
 
 	EconomyObserver* eco = economies.front();
 	if (eco->flags.empty()) {
-		return check_economies();
+		check_economies();
+		return false;
 	}
 	if (eco->flags.size() > 1) {
 		eco->flags.push_back(eco->flags.front());
@@ -2389,9 +2518,11 @@
 	} else if (flag.current_wares() > 5) {
 		create_shortcut_road(flag, 9, -2, gametime);
 		inhibit_road_building_ = gametime + 400;
+	} else {
+		return false;
 	}
 
-	return false;
+	return true;
 }
 
 // the function takes a road (road is smallest section of roads with two flags on the ends)
@@ -2469,6 +2600,27 @@
 	return false;
 }
 
+//is productionsite needed
+//used for building new buildings or dismantle of old, intended for ones
+//that have inputs
+bool DefaultAI::is_productionsite_needed(int32_t outputs,
+										int32_t performance,
+										PerfEvaluation purpose) {
+	int32_t expected_performance = 0;
+	if (outputs > 0) {
+		expected_performance = 10 + 70 / outputs;
+	} else {
+		expected_performance = 80;
+	}
+	if (purpose == PerfEvaluation::kForDismantle) {
+		expected_performance /= 2;
+	}
+	if (performance > expected_performance) {
+		return true;
+	}
+	return false;
+}
+
 // trying to connect the flag to another one, be it from own economy
 // or other economy
 bool DefaultAI::create_shortcut_road(const Flag& flag,
@@ -2811,7 +2963,6 @@
 		return false;
 	}
 
-	bool changed = false;
 	// Reorder and set new values; - better now because there are multiple returns in the function
 	productionsites.push_back(productionsites.front());
 	productionsites.pop_front();
@@ -2850,14 +3001,18 @@
 	// available, one is to be enhanced
 	// b) if there are two buildings
 	// statistics percents are decisive
+	// c) yet there are buildings that might be upgraded, even when
+	// there is no second buiding of the kind (flag upgrade_substitutes_)
+
 	const BuildingIndex enhancement = site.site->descr().enhancement();
 	if (connected_to_wh && enhancement != INVALID_INDEX &&
-	    (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) {
+	    ((site.bo->cnt_built_ - site.bo->unoccupied_) > 1 ||
+	    site.bo->upgrade_substitutes_)) {
 
 		BuildingIndex enbld = INVALID_INDEX;  // to get rid of this
 
 		// Only enhance buildings that are allowed (scenario mode)
-		// do not do decisions to fast
+		// do not do decisions too fast
 		if (player_->is_building_type_allowed(enhancement)) {
 
 			const BuildingDescr& bld = *tribe_->get_building_descr(enhancement);
@@ -2871,7 +3026,7 @@
 				if (site.site->has_workers(enhancement, game())) {
 
 					// forcing first upgrade
-					if (en_bo.cnt_built_ == 0 && !mines_.empty()) {
+					if (en_bo.cnt_built_ == 0) {
 						enbld = enhancement;
 						bestbld = &en_bo;
 					}
@@ -3079,9 +3234,12 @@
 	}
 
 	// buildings with inputs_, checking if we can a dismantle some due to low performance
-	if (!site.bo->inputs_.empty() && (site.bo->cnt_built_ - site.bo->unoccupied_) >= 3 &&
+	if (!site.bo->inputs_.empty() &&
+		(site.bo->cnt_built_ - site.bo->unoccupied_) >= 3 &&
 	    site.site->can_start_working() &&
-	    site.site->get_statistics_percent() < 20 &&  // statistics for the building
+	    !is_productionsite_needed(site.bo->outputs_.size(),
+	    							site.site->get_statistics_percent(),
+	    							PerfEvaluation::kForDismantle) &&
 	    site.bo->current_stats_ < 30 &&              // overall statistics
 	    (game().get_gametime() - site.unoccupied_till_) > 10 * 60 * 1000) {
 
@@ -3117,16 +3275,8 @@
 	// stop/start them based on stock avaiable
 	if (site.bo->production_hint_ >= 0) {
 
-		if (site.bo->stocklevel_time < game().get_gametime() - 5 * 1000) {
-			site.bo->stocklevel_ = get_stocklevel_by_hint(site.bo->production_hint_);
-			site.bo->stocklevel_time = game().get_gametime();
-		}
-
-		// logs can be stored also in productionsites, they are counted as on stock
-		// but are no available for random production site
-		int16_t score = site.bo->stocklevel_ - productionsites.size() * 2;
-
-		if (score > 200 && site.bo->cnt_built_ > site.bo->cnt_target_) {
+		//dismantling the rangers hut, but only if we have them above a target
+		if (wood_policy_ == WoodPolicy::kDismantleRangers && site.bo->cnt_built_ > site.bo->cnt_target_) {
 
 			site.bo->last_dismantle_time_ = game().get_gametime();
 			flags_to_be_removed.push_back(site.site->base_flag().get_position());
@@ -3138,27 +3288,38 @@
 			return true;
 		}
 
-		if (score > 120 && !site.site->is_stopped()) {
+		// stopping a ranger (sometimes the policy can be kDismantleRangers,
+		// but we still preserve some rangers for sure)
+		if ((wood_policy_ == WoodPolicy::kStopRangers
+			||
+			wood_policy_ == WoodPolicy::kDismantleRangers)
+			&&
+			!site.site->is_stopped()) {
 
 			game().send_player_start_stop_building(*site.site);
-			return true;
+			return false;
 		}
+
 		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")));
 
+		//stop ranger if enough trees around regardless of policy
 		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);
+		//if not enough trees nearby, we can start them if required
+		} else if ((wood_policy_ == WoodPolicy::kStartRangers ||
+		 	wood_policy_ == WoodPolicy::kBuildRangers)
+		 	&&
+		 	site.site->is_stopped()) {
+				game().send_player_start_stop_building(*site.site);
 		}
 	}
 
-	return changed;
+	return false;
 }
 
 // This function scans current situation with shipyards, ports, ships, ongoing expeditions
@@ -3562,15 +3723,15 @@
 	// counting vacant positions
 	vacant_mil_positions_ = 0;
 	for (TrainingSiteObserver tso : trainingsites) {
-		vacant_mil_positions_ += tso.site->soldier_capacity() - tso.site->stationed_soldiers().size();
+		vacant_mil_positions_ += 10 * (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
+// this function only check with trainingsites
+// manipulates input queues and soldier capacity
 bool DefaultAI::check_trainingsites(uint32_t gametime) {
 	if (taskDue[ScheduleTasks::kCheckTrainingsites] > gametime) {
 		return false;
@@ -3582,6 +3743,9 @@
 		return false;
 	}
 
+	trainingsites.push_back(trainingsites.front());
+	trainingsites.pop_front();
+
 	TrainingSite* ts = trainingsites.front().site;
 	TrainingSiteObserver& tso = trainingsites.front();
 
@@ -3595,12 +3759,78 @@
 		}
 	}
 
-	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());
+	// changing capacity to 0 - this will happen only once.....
+	if (tso.site->soldier_capacity() > 1) {
+		game().send_player_change_soldier_capacity(*ts, - tso.site->soldier_capacity());
+		return true;
+	}
+
+	//reducing ware quees
+	// - for armours and weapons to 1
+	// - for others to 6
+	std::vector<WaresQueue*> const warequeues1 = tso.site->warequeues();
+	size_t nr_warequeues = warequeues1.size();
+	for (size_t i = 0; i < nr_warequeues; ++i) {
+
+		//if it was decreased yet
+		if (warequeues1[i]->get_max_fill() <= 1) {
+			continue;}
+
+		//now modifying max_fill of armors and weapons
+		for (std::string pattern : armors_and_weapons) {
+
+			if (tribe_->get_ware_descr(warequeues1[i]->get_ware())->name().find(pattern) != std::string::npos) {
+				if (warequeues1[i]->get_max_fill() > 1) {
+					game().send_player_set_ware_max_fill(*ts, warequeues1[i]->get_ware(), 1);
+					continue;
+				}
+			}
+		}
+	}
+
+	//changing priority if basic
+	if (tso.bo->trainingsite_type_ == TrainingSiteType::kBasic) {
+		for (uint32_t k = 0; k < tso.bo->inputs_.size(); ++k) {
+			game().send_player_set_ware_priority(
+			   *ts, wwWARE, tso.bo->inputs_.at(k), HIGH_PRIORITY);
+		}
+	}
+
+	//if soldier capacity is set to 0, we need to find out if the site is
+	//suplied enough to incrase the capacity to 1
+	if (tso.site->soldier_capacity() == 0){
+
+		//First subsitute wares
+		//int32_t capacity = 0;
+		int32_t filled = 0;
+		bool supplied_enough = true;
+		std::vector<WaresQueue*> const warequeues2 = tso.site->warequeues();
+		nr_warequeues = warequeues2.size();
+		for (size_t i = 0; i < nr_warequeues; ++i) {
+			if (tso.bo->substitute_inputs_.count(warequeues2[i]->get_ware()) > 0){
+				filled += warequeues2[i]->get_filled();
+				//capacity += warequeues2[i]->get_max_fill();
+			}
+		}
+		if (filled < 5) {
+			supplied_enough = false;
+		}
+
+		//checking non subsitutes
+		for (size_t i = 0; i < nr_warequeues; ++i) {
+			if (tso.bo->substitute_inputs_.count(warequeues2[i]->get_ware()) == 0){
+				const uint32_t required_amount
+				 =
+				 (warequeues2[i]->get_max_fill()<5) ? warequeues2[i]->get_max_fill() : 5;
+				if (warequeues2[i]->get_filled() < required_amount) {
+					supplied_enough = false;
+				}
+			}
+		}
+
+		if (supplied_enough) {
+			game().send_player_change_soldier_capacity(*ts, 1);
+		}
 	}
 
 	ts_without_trainers_ = 0;  // zeroing
@@ -3713,12 +3943,12 @@
 		int32_t unused1 = 0;
 		uint16_t unused2 = 0;
 
-		mso.enemies_nearby_ = true;
+		mso.enemies_nearby_ = false;
 
 		// yes enemy is nearby, but still we must distinguish whether
 		// he is accessible (over the land)
 		if (other_player_accessible(
-		       vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kOtherPlayers)) {
+		       vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kEnemy)) {
 
 			uint32_t const total_capacity = ms->max_soldier_capacity();
 			uint32_t const target_capacity = ms->soldier_capacity();
@@ -3732,6 +3962,9 @@
 				   *ms, MilitarySite::kPrefersHeroes);
 				changed = true;
 			}
+
+			mso.enemies_nearby_ = true;
+			enemy_last_seen_ = gametime;
 		} else {  // otherwise decrease soldiers
 			uint32_t const j = ms->soldier_capacity();
 
@@ -3952,6 +4185,23 @@
 		}
 }
 
+//this is called when soldier left the trainingsite
+//the purpose is to set soldier capacity to 0
+// (AI will then wait till training site is stocked)
+void DefaultAI::soldier_trained(const TrainingSite& site) {
+
+	// we must identify particular training site
+	for (std::list<TrainingSiteObserver>::iterator i = trainingsites.begin(); i != trainingsites.end(); ++i)
+		if (i->site == &site) {
+			if (i->site->soldier_capacity() > 0) {
+				game().send_player_change_soldier_capacity(*i->site, - i->site->soldier_capacity());
+			}
+			return;
+		}
+	log (" %d: Computer player error - trainingsite not found\n",
+	player_number());
+}
+
 // walk and search for teritorry controlled by other player
 // usually scanning radius is enough but sometimes we must walk to
 // verify that an enemy teritory is really accessible by land
@@ -3993,10 +4243,35 @@
 		// a port location), but when testing (starting from) own military building
 		// we must ignore own teritory, of course
 		if (f->get_owned_by() > 0) {
-			if (type == WalkSearch::kAnyPlayer ||
-			    (type == WalkSearch::kOtherPlayers && f->get_owned_by() != pn)) {
-				*tested_fields = done.size();
-				return true;
+
+			//if field is owned by anybody
+			if (type == WalkSearch::kAnyPlayer){
+				*tested_fields = done.size();
+				return true;
+				}
+
+			//if anybody but not me
+			if (type == WalkSearch::kOtherPlayers && f->get_owned_by() != pn){
+				*tested_fields = done.size();
+				return true;
+				}
+
+			//if owned by enemy
+			if  (type == WalkSearch::kEnemy && f->get_owned_by() != pn){
+				//for case I am not member of a team
+				if (player_->team_number() == 0) {
+					*tested_fields = done.size();
+					return true;
+				}
+				//if I am in team, testing if the same team
+				if (player_->team_number() > 0
+				&&
+				player_->team_number()
+				!=
+				game().get_player(f->get_owned_by())->team_number()) {
+					*tested_fields = done.size();
+					return true;
+				}
 			}
 		}
 
@@ -4421,6 +4696,65 @@
 	return supplied == bo.inputs_.size();
 }
 
+//This calculates strength of vector of soldiers, f.e. soldiers in a building or
+//ones ready to attack
+int32_t DefaultAI::calculate_strength(const std::vector<Widelands::Soldier*> soldiers){
+
+	if (soldiers.empty()) {
+		return 0;
+	}
+
+	enum {BARBARIANS, ATLANTEANS, EMPIRE};
+	uint8_t tribe = std::numeric_limits<uint8_t>::max();
+
+	if (soldiers.at(0)->get_owner()->tribe().name() == "atlanteans"){
+		tribe = ATLANTEANS;
+	} else if (soldiers.at(0)->get_owner()->tribe().name() == "barbarians"){
+		tribe = BARBARIANS;
+	} else if (soldiers.at(0)->get_owner()->tribe().name() == "empire"){
+		tribe = EMPIRE;
+	} else {
+		throw wexception("AI warning: Unable to calculate strenght for player of tribe %s",
+			soldiers.at(0)->get_owner()->tribe().name().c_str());
+	}
+
+	float hp = 0;
+	float al = 0;
+	float dl = 0;
+	float el = 0;
+	float final = 0;
+
+	for (Soldier * soldier : soldiers) {
+		switch (tribe) {
+			case (ATLANTEANS):
+				hp = 135 + 40 * soldier->get_hp_level();
+				al =  14 +  8 * soldier->get_attack_level();
+				dl = static_cast<float>(94 -  8 * soldier->get_defense_level()) / 100;
+				el = static_cast<float>(70 - 17 * soldier->get_evade_level()) / 100;
+				break;
+			case (BARBARIANS):
+				hp += 130 + 28 * soldier->get_hp_level();
+				al +=  14 +  7 * soldier->get_attack_level();
+				dl += static_cast<float>(97 -  8 * soldier->get_defense_level()) / 100;
+				el += static_cast<float>(75 - 15 * soldier->get_evade_level()) / 100;
+				break;
+			case (EMPIRE):
+				hp += 130 + 21 * soldier->get_hp_level();
+				al +=  14 +  8 * soldier->get_attack_level();
+				dl += static_cast<float>(95 -  8 * soldier->get_defense_level()) / 100;
+				el += static_cast<float>(70 - 16 * soldier->get_evade_level()) / 100;
+				break;
+			default:
+				assert (false);
+		}
+
+		final += (al * hp) / (dl * el);
+	}
+
+	//2500 is aproximate strength of one unpromoted soldier
+	return static_cast<int32_t>(final / 2500);
+}
+
 bool DefaultAI::check_enemy_sites(uint32_t const gametime) {
 
 	Map& map = game().map();
@@ -4437,6 +4771,27 @@
 	// receiving games statistics and parsing it (reading latest entry)
 	const Game::GeneralStatsVector& genstats = game().get_general_statistics();
 
+	//summing team power, creating team_power std::map of team_number:strength
+	std::map<TeamNumber, uint32_t> team_power;
+	for (uint8_t j = 1; j <= plr_in_game; ++j) {
+		TeamNumber const tm = game().get_player(j)->team_number();
+		if (tm == 0) {
+			continue;
+		}
+		//for case this is new team
+		if (team_power.count(tm) == 0) {
+			//adding this team (number) to vector
+			team_power[tm] = 0;
+		}
+		try {
+			team_power[tm] += genstats.at(j - 1).miltary_strength.back();
+		} catch (const std::out_of_range&) {
+			log("ComputerPlayer(%d): genstats entry missing - size :%d\n",
+			    player_number(),
+			    static_cast<unsigned int>(genstats.size()));
+		}
+	}
+
 	// defining treshold ratio of own_strenght/enemy's_strength
 	uint32_t treshold_ratio = 100;
 	if (type_ == AGGRESSIVE) {
@@ -4446,25 +4801,49 @@
 		treshold_ratio = 120;
 	}
 
-	// now we test all players which one are 'attackable'
+	uint32_t my_power = 0;
+	try {
+		my_power = genstats.at(pn - 1).miltary_strength.back();
+	} catch (const std::out_of_range&) {
+		log("ComputerPlayer(%d): genstats entry missing - size :%d\n",
+		    player_number(),
+		    static_cast<unsigned int>(genstats.size()));
+	}
+	//adding power of team (minus my power) divided by 2
+	//(if I am a part of a team of course)
+	if (game().get_player(pn)->team_number() > 0) {
+		my_power += (team_power[game().get_player(pn)->team_number()] - my_power) / 2;
+	}
+
+	// now we test all players to identify 'attackable' ones
 	for (uint8_t j = 1; j <= plr_in_game; ++j) {
-		if (pn == j) {  // its me
+		//if it's me
+		if (pn == j) {
+			player_attackable[j - 1] = false;
+			continue;
+		}
+		//if we are the same team
+		if (game().get_player(pn)->team_number() > 0 &&
+		game().get_player(pn)->team_number() == game().get_player(j)->team_number()) {
 			player_attackable[j - 1] = false;
 			continue;
 		}
 
+		//now we compare strength
 		try {
-			// It seems that under some circumstances genstats can be empty.
-			// So, to avoid crash, the AI tests its content first.
-			if (genstats.at(j - 1).miltary_strength.empty()) {
-				log("ComputerPlayer(%d): miltary_strength is empty\n", player_number());
-				player_attackable.at(j - 1) = false;
-				// Avoid division by zero
-			} else if (genstats.at(j - 1).miltary_strength.back() == 0) {
+			//strength of the other player
+			uint32_t players_power = 0;
+			if (!genstats.at(j - 1).miltary_strength.empty()) {
+				players_power += genstats.at(j - 1).miltary_strength.back();
+			}
+			//+power of team (if member of a team)
+			if (game().get_player(j)->team_number() > 0) {
+				players_power += (team_power[game().get_player(j)->team_number()] - players_power) / 2;
+			}
+
+			if (players_power == 0){
 				player_attackable.at(j - 1) = true;
-				// Check threshold
-			} else if ((genstats.at(pn - 1).miltary_strength.back() * 100 /
-			            genstats.at(j - 1).miltary_strength.back()) > treshold_ratio) {
+			} else if (my_power * 100 / players_power > treshold_ratio) {
 				player_attackable.at(j - 1) = true;
 			} else {
 				player_attackable.at(j - 1) = false;
@@ -4532,7 +4911,7 @@
 		count += 1;
 
 		site->second.last_tested = gametime;
-		uint8_t defenders = 0;
+		uint8_t defenders_strength = 0;
 		bool is_warehouse = false;
 		bool is_attackable = false;
 		uint16_t onwer_number = 100;
@@ -4544,7 +4923,10 @@
 		Flag* flag = nullptr;
 		if (upcast(MilitarySite, bld, f.field->get_immovable())) {
 			if (player_->is_hostile(bld->owner())) {
-				defenders = bld->present_soldiers().size();
+				std::vector<Soldier *> defenders;
+				defenders = bld->present_soldiers();
+				defenders_strength = calculate_strength(defenders);
+
 				flag = &bld->base_flag();
 				if (bld->can_attack()) {
 					is_attackable = true;
@@ -4554,7 +4936,11 @@
 		}
 		if (upcast(Warehouse, Wh, f.field->get_immovable())) {
 			if (player_->is_hostile(Wh->owner())) {
-				defenders = Wh->present_soldiers().size();
+
+				std::vector<Soldier *> defenders;
+				defenders = Wh->present_soldiers();
+				defenders_strength = calculate_strength(defenders);
+
 				flag = &Wh->base_flag();
 				is_warehouse = true;
 				if (Wh->can_attack()) {
@@ -4588,31 +4974,42 @@
 
 			// can we attack:
 			if (is_attackable) {
-				site->second.attack_soldiers = player_->find_attack_soldiers(*flag);
+				std::vector<Soldier *> attackers;
+				player_->find_attack_soldiers(*flag, &attackers);
+				int32_t strength = calculate_strength(attackers);
+
+				site->second.attack_soldiers_strength = strength;
 			} else {
-				site->second.attack_soldiers = 0;
+				site->second.attack_soldiers_strength = 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;
+			site->second.defenders_strength = defenders_strength;
+
+			if (site->second.attack_soldiers_strength > 0
+				&&
+				player_attackable[onwer_number - 1]) {
+				site->second.score = site->second.attack_soldiers_strength - site->second.defenders_strength / 2;
+
+				if (is_warehouse) {
+					site->second.score += 2;
+				} else {
+					site->second.score -= 2;
+				}
 
 				// here is some differentiation based on "character" of a player
 				if (type_ == NORMAL) {
-					site->second.score -= 1;
-					site->second.score -= vacant_mil_positions_ / 10;
+					site->second.score -= 3;
+					site->second.score -= vacant_mil_positions_ / 8;
 				} else if (type_ == DEFENSIVE) {
-					site->second.score -= 2;
-					site->second.score -= vacant_mil_positions_ / 5;
+					site->second.score -= 6;
+					site->second.score -= vacant_mil_positions_ / 4;
 				} else {  //=AGRESSIVE
-					site->second.score -= vacant_mil_positions_ / 15;
+					site->second.score -= vacant_mil_positions_ / 16;
 				}
 				if (site->second.mines_nearby == ExtendedBool::kFalse) {
 					site->second.score -= 1;
+				} else {
+					site->second.score += 1;
 				}
 				// we dont want to attack multiple players at the same time too eagerly
 				if (onwer_number != last_attacked_player_) {
@@ -4620,29 +5017,32 @@
 				}
 				// if we dont have mines yet
 				if (mines_.size() <= 2) {
-					site->second.score -= 2;
+					site->second.score -= 8;
 				}
 				// also we should have at least some training sites
 				if ((ts_basic_count_ + ts_advanced_count_) == 0) {
-					site->second.score -= 2;
+					site->second.score -= 4;
 				}
 				// treating no attack score
 				if (site->second.no_attack_counter < 0) {
+					//we cannot attack yet
 					site->second.score = 0;
+					//but increase the counter by 1
 					site->second.no_attack_counter += 1;
 				}
+
 			} else {
 				site->second.score = 0;
 			}  // or the score will remain 0
 
-			if (site->second.score > 0 && player_attackable[onwer_number - 1]) {
+			if (site->second.score > 0) {
 				if (site->second.score > best_score) {
 					best_score = site->second.score;
 					best_target = site->first;
 				}
 			}
 
-			if (site->second.attack_soldiers > 0) {
+			if (site->second.attack_soldiers_strength > 0) {
 				site->second.last_time_attackable = gametime;
 			}
 			if (site->second.last_time_attackable + 20 * 60 * 1000 < gametime) {
@@ -4743,6 +5143,19 @@
 			DueTask = task.first;
 		}
 	}
+	if ((gametime - oldestTaskTime) > 5000){
+		scheduler_delay_counter_ += 1;
+	} else {
+		scheduler_delay_counter_ = 0;
+	}
+
+	if (scheduler_delay_counter_ > 10){
+		log(" %d: AI: game speed too high, jobs are too late (now %2d seconds)\n",
+		player_number(),
+		static_cast<int32_t>((gametime - oldestTaskTime) / 1000));
+		scheduler_delay_counter_ = 0;
+	}
+
 	return DueTask;
 }
 
@@ -4766,6 +5179,7 @@
 	const std::vector<std::string> materials = {"coal",
 	                                            "log",
 	                                            "ironore",
+	                                            "iron",
 	                                            "marble",
 	                                            "plank",
 	                                            "water",
@@ -4791,12 +5205,13 @@
 		}
 		summary = summary + materials.at(j) + ", ";
 	}
-	log(" %1d: Buildings: Pr:%3lu, Ml:%3lu, Mi:%2lu, Wh:%2lu, Po:%2u. Missing: %s\n",
+
+	log(" %1d: Buildings: Pr:%3u, Ml:%3u, Mi:%2u, Wh:%2u, Po:%u. Missing: %s\n",
 	    pn,
-	    productionsites.size(),
-	    militarysites.size(),
-	    mines_.size(),
-	    warehousesites.size() - num_ports,
+	    static_cast<uint32_t>(productionsites.size()),
+	    static_cast<uint32_t>(militarysites.size()),
+	    static_cast<uint32_t>(mines_.size()),
+	    static_cast<uint32_t>(warehousesites.size() - num_ports),
 	    num_ports,
 	    summary.c_str());
 }

=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h	2015-05-05 20:00:21 +0000
+++ src/ai/defaultai.h	2015-05-28 19:39:10 +0000
@@ -30,6 +30,8 @@
 #include "base/i18n.h"
 #include "logic/immovable.h"
 #include "logic/ship.h"
+#include "logic/soldier.h"
+#include "logic/trainingsite.h"
 
 namespace Widelands {
 struct Road;
@@ -79,8 +81,10 @@
 		DEFENSIVE = 0,
 	};
 
-	enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers};
+	enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers, kEnemy};
+	enum class WoodPolicy : uint8_t {kDismantleRangers, kStopRangers, kStartRangers, kBuildRangers};
 	enum class NewShip : uint8_t {kBuilt, kFoundOnLoad};
+	enum class PerfEvaluation : uint8_t {kForConstruction, kForDismantle};
 	enum class ScheduleTasks : uint8_t {
 		kBbuildableFieldsCheck,
 		kMineableFieldsCheck,
@@ -199,6 +203,8 @@
 	bool check_ships(uint32_t);
 	bool check_enemy_sites(uint32_t);
 	void print_stats(uint32_t);
+	//return single number of strength of vector of soldiers
+	int32_t calculate_strength(const std::vector<Widelands::Soldier*>);
 	uint32_t get_stocklevel_by_hint(size_t);
 	uint32_t get_stocklevel(BuildingObserver&);
 	uint32_t get_warehoused_stock(Widelands::WareIndex wt);
@@ -233,6 +239,10 @@
 	void gain_ship(Widelands::Ship&, NewShip);
 	void expedition_management(ShipObserver&);
 	void out_of_resources_site(const Widelands::ProductionSite&);
+	void soldier_trained(const Widelands::TrainingSite&);
+	bool is_productionsite_needed(int32_t outputs,
+										int32_t performance,
+										PerfEvaluation purpose);
 
 	bool check_supply(const BuildingObserver&);
 
@@ -260,6 +270,8 @@
 	// check ms in this interval - will auto-adjust
 	uint32_t enemysites_check_delay_;
 
+	WoodPolicy wood_policy_;
+
 	std::list<Widelands::FCoords> unusable_fields;
 	std::list<BuildableField*> buildable_fields;
 	std::list<BlockedField> blocked_fields;
@@ -318,6 +330,17 @@
 	uint8_t ts_advanced_const_count_;
 	uint8_t ts_without_trainers_;
 
+	//this is helping counter to track how many scheduler tasks are too delayed
+	// the purpose is to print out a warning that the game is pacing too fast
+	int32_t scheduler_delay_counter_;
+
+	//this is bunch of patterns that have to identify weapons and armors for input queues of traininsites
+	std::vector<std::string> const armors_and_weapons =
+		{"ax", "lance", "armor", "helm", "lance", "trident", "tabard", "shield", "mask"};
+	//some buildings can be upgraded even when they are only one
+	//now only microbrewery get this special treatment
+	const char* preffered_upgrade[1] = {"micro-brewery"};
+
 	enum {kReprioritize, kStopShipyard, kStapShipyard};
 
 	std::vector<int16_t> marineTaskQueue_;
@@ -327,6 +350,8 @@
 	std::unique_ptr<Notifications::Subscriber<Widelands::NoteImmovable>> immovable_subscriber_;
 	std::unique_ptr<Notifications::Subscriber<Widelands::NoteProductionSiteOutOfResources>>
 	   outofresource_subscriber_;
+	std::unique_ptr<Notifications::Subscriber<Widelands::NoteTrainingSiteSoldierTrained>>
+	   soldiertrained_subscriber_;
 	std::unique_ptr<Notifications::Subscriber<Widelands::NoteShipMessage>> shipnotes_subscriber_;
 };
 

=== modified file 'src/logic/trainingsite.cc'
--- src/logic/trainingsite.cc	2015-05-03 11:39:11 +0000
+++ src/logic/trainingsite.cc	2015-05-28 19:39:10 +0000
@@ -320,8 +320,9 @@
 		delete m_soldier_request;
 		m_soldier_request = nullptr;
 
-		while (m_soldiers.size() > m_capacity)
+		while (m_soldiers.size() > m_capacity) {
 			drop_soldier(**m_soldiers.rbegin());
+		}
 	}
 }
 
@@ -432,6 +433,7 @@
 
 	// Schedule, so that we can call new soldiers on next act()
 	schedule_act(game, 100);
+	Notifications::publish(NoteTrainingSiteSoldierTrained(this, get_owner()));
 }
 
 

=== modified file 'src/logic/trainingsite.h'
--- src/logic/trainingsite.h	2014-09-19 12:54:54 +0000
+++ src/logic/trainingsite.h	2015-05-28 19:39:10 +0000
@@ -218,6 +218,24 @@
 
 };
 
+/**
+ * Note to be published when a soldier is leaving the training center
+ */
+// A note we're using to notify the AI
+struct NoteTrainingSiteSoldierTrained {
+	CAN_BE_SEND_AS_NOTE(NoteId::TrainingSiteSoldierTrained)
+
+	// The trainingsite from where soldier is leaving.
+	TrainingSite* ts;
+
+	// The player that owns the ttraining site.
+	Player * player;
+
+	NoteTrainingSiteSoldierTrained(TrainingSite* const init_ts, Player* init_player)
+		: ts(init_ts), player(init_player) {
+	}
+};
+
 }
 
 #endif  // end of include guard: WL_LOGIC_TRAININGSITE_H

=== modified file 'src/notifications/note_ids.h'
--- src/notifications/note_ids.h	2015-03-01 09:21:20 +0000
+++ src/notifications/note_ids.h	2015-05-28 19:39:10 +0000
@@ -32,6 +32,7 @@
 	FieldPossession,
 	FieldTransformed,
 	ProductionSiteOutOfResources,
+	TrainingSiteSoldierTrained,
 	ShipMessage,
 	GraphicResolutionChanged,
 

=== modified file 'tribes/atlanteans/fish_breeders_house/conf'
--- tribes/atlanteans/fish_breeders_house/conf	2014-08-02 19:55:23 +0000
+++ tribes/atlanteans/fish_breeders_house/conf	2015-05-28 19:39:10 +0000
@@ -3,7 +3,7 @@
 [aihints]
 needs_water=true
 renews_map_resource=fish
-prohibited_till=900
+prohibited_till=300
 
 [buildcost]
 log=1

=== modified file 'tribes/atlanteans/sawmill/conf'
--- tribes/atlanteans/sawmill/conf	2014-08-02 19:55:23 +0000
+++ tribes/atlanteans/sawmill/conf	2015-05-28 19:39:10 +0000
@@ -2,7 +2,7 @@
 output=planks
 
 [aihints]
-forced_after=300
+forced_after=120
 prohibited_till=60
 
 [buildcost]

=== modified file 'tribes/atlanteans/smokery/conf'
--- tribes/atlanteans/smokery/conf	2014-08-02 19:55:23 +0000
+++ tribes/atlanteans/smokery/conf	2015-05-28 19:39:10 +0000
@@ -4,7 +4,7 @@
 
 [aihints]
 forced_after=900
-prohibited_till=60
+prohibited_till=180
 
 [buildcost]
 log=1

=== modified file 'tribes/atlanteans/spidercloth/conf'
--- tribes/atlanteans/spidercloth/conf	2014-07-29 20:48:19 +0000
+++ tribes/atlanteans/spidercloth/conf	2015-05-28 19:39:10 +0000
@@ -1,7 +1,7 @@
 help=_Spidercloth is made out of spideryarn in a weaving mill. It is used in the toolsmithy and the shipyard. Also some higher developed buildings need spidercloth for their construction.
 
 default_target_quantity=20
-preciousness=5
+preciousness=7
 
 [idle]
 pics=idle.png

=== modified file 'tribes/atlanteans/spiderfarm/conf'
--- tribes/atlanteans/spiderfarm/conf	2014-08-02 19:55:23 +0000
+++ tribes/atlanteans/spiderfarm/conf	2015-05-28 19:39:10 +0000
@@ -37,5 +37,5 @@
 hotspot=87 75
 
 [aihints]
-forced_after=60
+forced_after=150
 prohibited_till=60

=== modified file 'tribes/atlanteans/weaving-mill/conf'
--- tribes/atlanteans/weaving-mill/conf	2014-08-02 19:55:23 +0000
+++ tribes/atlanteans/weaving-mill/conf	2015-05-28 19:39:10 +0000
@@ -4,7 +4,7 @@
 output=golden_tabard
 
 [aihints]
-forced_after=60
+forced_after=150
 prohibited_till=60
 
 [buildcost]

=== modified file 'tribes/barbarians/farm/conf'
--- tribes/barbarians/farm/conf	2015-02-07 01:51:19 +0000
+++ tribes/barbarians/farm/conf	2015-05-28 19:39:10 +0000
@@ -3,6 +3,7 @@
 
 [aihints]
 space_consumer=true
+forced_after=900
 
 [buildcost]
 log=4
@@ -60,4 +61,4 @@
 
 [working]
 pics=working_??.png  # ???
-hotspot=69 76
\ No newline at end of file
+hotspot=69 76


Follow ups