← Back to team overview

widelands-dev team mailing list archive

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

 

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

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/ai-scheduler/+merge/251327

This is rework of DefaultAI::think() function. In itself it does not bring a lost of visible effects but reorganizes the logic how AI defines what "job" (one of about 15 AI functions) are to be run in this turn.

Previously AI used to run once in 0.1 second something "small" and once in 1 second more functions. It was pretty chaotic and unclear. Now it runs twice in a second and runs only one function. (with slight exemptions). 

And this is meaning of a 'scheduler' - pick the only one function and be fair. Longest overdue first. And sometimes nothing as no function is overdue yet.

I think performance is improved but I have no 'metric' to quantify it.

There were some other slight changes to the code as I noticed the AI behaves bit differently.

There are still few nocoms and prints but I will remove as soon as you say it can go.

-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/ai-scheduler into lp:widelands.
=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc	2015-02-16 20:23:15 +0000
+++ src/ai/defaultai.cc	2015-02-27 20:46:25 +0000
@@ -65,7 +65,7 @@
 constexpr int kMinBFCheckInterval = 5 * 1000;
 constexpr int kShipCheckInterval = 5 * 1000;
 constexpr int kMarineDecisionInterval = 20 * 1000;
-constexpr int kTrainingSitesCheckInterval = 30 * 1000;
+constexpr int kTrainingSitesCheckInterval = 5 * 60 * 1000;
 
 // this is intended for map developers, by default should be off
 constexpr bool kPrintStats = false;
@@ -85,14 +85,15 @@
 DefaultAI::DefaultAI(Game& ggame, PlayerNumber const pid, uint8_t const t)
    : ComputerPlayer(ggame, pid),
      type_(t),
-     m_buildable_changed(true),
-     m_mineable_changed(true),
+     // m_buildable_changed(true),
+     // m_mineable_changed(true),
      player_(nullptr),
      tribe_(nullptr),
      num_constructionsites_(0),
      num_milit_constructionsites(0),
      num_prod_constructionsites(0),
      num_ports(0),
+     next_ai_think_(0),
      next_road_due_(2000),
      next_stats_update_due_(30000),
      next_construction_due_(1000),
@@ -100,11 +101,13 @@
      next_productionsite_check_due_(0),
      next_mine_check_due_(0),
      next_militarysite_check_due_(0),
-     next_ship_check_due(30 * 1000),
+     next_ship_check_due_(30 * 1000),
+     next_economies_check_due_(1000),
      next_marine_decisions_due(30 * 1000),
      next_attack_consideration_due_(300000),
      next_trainingsites_check_due_(15 * 60 * 1000),
      next_bf_check_due_(1000),
+     next_uf_check_due_(1000),
      next_wares_review_due_(15 * 60 * 1000),
      next_statistics_report_(30 * 60 * 1000),
      inhibit_road_building_(0),
@@ -195,7 +198,7 @@
 			   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;
@@ -238,111 +241,117 @@
 
 	const int32_t gametime = game().get_gametime();
 
-	if (m_buildable_changed || next_bf_check_due_ < gametime) {
-		// update statistics about buildable fields
+	if (next_ai_think_ > gametime) {
+		return;
+	}
+
+	// AI now thinks twice in a seccond, if the game engine allows this
+	// if too busy, the period can be many seconds.
+	next_ai_think_ = gametime + 500;
+
+	// here we check due times of individual jobs and pick a one longest overdue
+	int32_t oldestTaskTime = gametime;  // we are looking for jobs older then ...
+	ScheduleTasks DueTask = ScheduleTasks::kIdle;  // just to flush previous value
+	scheduler_review(&next_bf_check_due_, &oldestTaskTime, &DueTask, ScheduleTasks::kBFCheck);
+	scheduler_review(&next_road_due_, &oldestTaskTime, &DueTask, ScheduleTasks::kRoadCheck);
+	scheduler_review(
+	   &next_uf_check_due_, &oldestTaskTime, &DueTask, ScheduleTasks::kUnbuildableFCheck);
+	scheduler_review(
+	   &next_attack_consideration_due_, &oldestTaskTime, &DueTask, ScheduleTasks::kConsiderAttack);
+	scheduler_review(
+	   &next_economies_check_due_, &oldestTaskTime, &DueTask, ScheduleTasks::kCheckEconomies);
+	scheduler_review(
+	   &next_stats_update_due_, &oldestTaskTime, &DueTask, ScheduleTasks::kProductionsitesStats);
+	scheduler_review(
+	   &next_construction_due_, &oldestTaskTime, &DueTask, ScheduleTasks::kConstructBuilding);
+	scheduler_review(&next_productionsite_check_due_,
+	                 &oldestTaskTime,
+	                 &DueTask,
+	                 ScheduleTasks::kCheckProductionsites);
+	scheduler_review(&next_ship_check_due_, &oldestTaskTime, &DueTask, ScheduleTasks::kCheckShips);
+	scheduler_review(
+	   &next_marine_decisions_due, &oldestTaskTime, &DueTask, ScheduleTasks::KMarineDecisions);
+	scheduler_review(&next_mine_check_due_, &oldestTaskTime, &DueTask, ScheduleTasks::kCheckMines);
+	scheduler_review(
+	   &next_militarysite_check_due_, &oldestTaskTime, &DueTask, ScheduleTasks::kCheckMilitarysites);
+	scheduler_review(&next_trainingsites_check_due_,
+	                 &oldestTaskTime,
+	                 &DueTask,
+	                 ScheduleTasks::kCheckTrainingsites);
+	scheduler_review(&next_wares_review_due_, &oldestTaskTime, &DueTask, ScheduleTasks::kWareReview);
+	scheduler_review(
+	   &next_statistics_report_, &oldestTaskTime, &DueTask, ScheduleTasks::kprintStats);
+
+	schedStat[static_cast<uint32_t>(DueTask)] += 1;  // NOCOM
+	printf(" %d: job to do: %2d (counter: %8d), time %6d, delayed: %2d sec\n",
+	       player_number(),
+	       static_cast<uint8_t>(DueTask),
+	       schedStat[static_cast<uint32_t>(DueTask)],
+	       gametime / 1000,
+	       (gametime - oldestTaskTime) / 1000);
+
+	// now AI runs a job selected above to be performed in this turn
+	// (only one but some of them needs to run check_economies() to
+	// guarantee consistency)
+	// job names are selfexplanatory
+	if (DueTask == ScheduleTasks::kBFCheck) {
 		update_all_buildable_fields(gametime);
 		next_bf_check_due_ = gametime + kMinBFCheckInterval;
-	}
-
-	m_buildable_changed = false;
-
-	// perpetually tries to improve roads
-	if (next_road_due_ <= gametime) {
-		next_road_due_ = gametime + 1000;
-
-		if (improve_roads(gametime)) {
-			m_buildable_changed = true;
+	} else if (DueTask == ScheduleTasks::kRoadCheck) {
+		if (check_economies()) {  // is a must
 			return;
-		}
-	} else {
-		// only go on, after defaultAI tried to improve roads.
-		return;
-	}
-
-	// NOTE Because of the check above, the following parts of think() are used
-	// NOTE only once every second at maximum. This increases performance and as
-	// NOTE human player_s can not even react that fast, it should not be a
-	// NOTE disadvantage for the defaultAI.
-	// This must be checked every time as changes of bobs in AI area aren't
-	// handled by the AI itself.
-	update_all_not_buildable_fields();
-
-	// considering attack
-	if (next_attack_consideration_due_ <= gametime) {
+		};
+		next_road_due_ = gametime + 400;
+		improve_roads(gametime);
+	} else if (DueTask == ScheduleTasks::kUnbuildableFCheck) {
+		next_uf_check_due_ = gametime + 4000;
+		update_all_not_buildable_fields();
+	} else if (DueTask == ScheduleTasks::kConsiderAttack) {
 		consider_attack(gametime);
-	}
-
-	// check if anything in the economies changed.
-	// This needs to be done before new buildings are placed, to ensure that no
-	// empty economy is left.
-	if (check_economies()) {
-		return;
-	}
-
-	// Before thinking about a new construction, update current stats, to have
-	// a better view on current economy.
-	if (next_stats_update_due_ <= gametime) {
+	} else if (DueTask == ScheduleTasks::kCheckEconomies) {
+		check_economies();
+		next_economies_check_due_ = gametime + 8000;
+	} else if (DueTask == ScheduleTasks::kProductionsitesStats) {
 		update_productionsite_stats(gametime);
-	}
-
-	// Now try to build something if possible
-	if (next_construction_due_ <= gametime) {
-		next_construction_due_ = gametime + 2000;
-
+	} else if (DueTask == ScheduleTasks::kConstructBuilding) {
+		if (check_economies()) {  // economies must be consistent
+			return;
+		}
+		next_construction_due_ = gametime + 6000;
 		if (construct_building(gametime)) {
 			time_of_last_construction_ = gametime;
-			m_buildable_changed = true;
-			return;
-		}
-	}
-
-	// verify that our production sites are doing well
-	if (check_productionsites(gametime)) {
-		return;
-	}
-
-	if (check_ships(gametime)) {
-		return;
-	}
-
-	if (marine_main_decisions(gametime)) {
-		return;
-	}
-
-	// Check the mines and consider upgrading or destroying one
-	if (check_mines_(gametime)) {
-		return;
-	}
-
-	// consider whether a change of the soldier capacity of some militarysites
-	// would make sense.
-	if (check_militarysites(gametime)) {
-		return;
-	}
-
-	if (check_trainingsites(gametime)) {
-		return;
-	}
-
-	// improve existing roads!
-	// main part of this improvment is creation 'shortcut roads'
-	// this includes also connection of new buildings
-	if (improve_roads(gametime)) {
-		m_buildable_changed = true;
-		m_mineable_changed = true;
-		return;
-	}
-
-	// once in 15 minutes we increase(or decrease) targets for wares
-	if (next_wares_review_due_ <= gametime) {
+		}
+	} else if (DueTask == ScheduleTasks::kCheckProductionsites) {
+		if (check_economies()) {  // economies must be consistent
+			return;
+		}
+		check_productionsites(gametime);
+		next_productionsite_check_due_ = gametime + 5000;
+	} else if (DueTask == ScheduleTasks::kCheckShips) {
+		check_ships(gametime);
+	} else if (DueTask == ScheduleTasks::KMarineDecisions) {
+		marine_main_decisions(gametime);
+	} else if (DueTask == ScheduleTasks::kCheckMines) {
+		if (check_economies()) {  // economies must be consistent
+			return;
+		}
+		next_mine_check_due_ = gametime + 7000;  // 7 seconds is enough
+		check_mines_(gametime);
+	} else if (DueTask == ScheduleTasks::kCheckMilitarysites) {
+		check_militarysites(gametime);
+	} else if (DueTask == ScheduleTasks::kCheckTrainingsites) {
+		check_trainingsites(gametime);
+	} else if (DueTask == ScheduleTasks::kWareReview) {
+		if (check_economies()) {  // economies must be consistent
+			return;
+		}
 		next_wares_review_due_ = gametime + 15 * 60 * 1000;
 		review_wares_targets(gametime);
-	}
-
-	// print statistics
-	if (kPrintStats && next_statistics_report_ <= gametime) {
-		print_stats();
-		next_statistics_report_ += 60 * 60 * 1000;
+	} else if (DueTask == ScheduleTasks::kprintStats) {
+		if (check_economies()) {  // economies must be consistent
+			return;
+		}
+		print_stats(gametime);
 	}
 }
 
@@ -524,7 +533,7 @@
 				port_reserved_coords.insert(hash);
 		} while (mr.advance(map));
 
-		//the same for NW neighbour of a field
+		// the same for NW neighbour of a field
 		Coords c_nw;
 		map.get_tln(c, &c_nw);
 		MapRegion<Area<FCoords>> mr_nw(map, Area<FCoords>(map.get_fcoords(c_nw), 3));
@@ -606,7 +615,7 @@
 	uint16_t i = 0;
 
 	while (!buildable_fields.empty() && buildable_fields.front()->next_update_due_ <= gametime &&
-	       i < 25) {
+	       i < 40) {
 		BuildableField& bf = *buildable_fields.front();
 
 		//  check whether we lost ownership of the node
@@ -753,7 +762,7 @@
 		}
 	}
 
-	//testing for near porspaces
+	// testing for near porspaces
 	if (field.portspace_nearby_ == Widelands::ExtendedBool::kUnset) {
 		field.portspace_nearby_ = ExtendedBool::kFalse;
 		MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, 2));
@@ -1246,7 +1255,7 @@
 	     ++i) {
 		BuildableField* const bf = *i;
 
-		if (bf->next_update_due_ < gametime - 8000) {
+		if (bf->next_update_due_ < gametime - 10000) {
 			continue;
 		}
 
@@ -1518,7 +1527,7 @@
 						}
 						// 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() * 5) {
+						         bo.stocklevel_ > 40 + productionsites.size() * 2) {
 							continue;
 						}
 
@@ -1732,9 +1741,9 @@
 				if (resource_necessity_water_needed_)
 					prio += bf->distant_water_ * resource_necessity_water_ / 255;
 
-				//special bonus if a portspace is close
+				// special bonus if a portspace is close
 				if (bf->portspace_nearby_ == ExtendedBool::kTrue) {
-					if (num_ports == 0){
+					if (num_ports == 0) {
 						prio += 25;
 					} else {
 						prio += 5;
@@ -2044,9 +2053,6 @@
 	// set the type of update that is needed
 	if (mine) {
 		next_mine_construction_due_ = gametime + kBusyMineUpdateInterval;
-
-	} else {
-		m_buildable_changed = true;
 	}
 
 	return true;
@@ -2134,13 +2140,24 @@
 		return true;
 	}
 
+	bool is_warehouse = false;
+	if (Building* b = flag.get_building()) {
+		BuildingObserver& bo = get_building_observer(b->descr().name().c_str());
+		if (bo.type == BuildingObserver::WAREHOUSE) {
+			is_warehouse = true;
+		}
+	}
+
 	// if this is end flag (or sole building) or just randomly
-	if (flag.nr_of_roads() <= 1 || gametime % 200 == 0) {
-		create_shortcut_road(flag, 13, 20, gametime);
+	if (flag.nr_of_roads() <= 1 || gametime % 10 == 0) {
+		create_shortcut_road(flag, 11, 20, gametime);
 		inhibit_road_building_ = gametime + 800;
 	}
 	// this is when a flag is full
-	else if (flag.current_wares() > 6 && gametime % 10 == 0) {
+	else if (is_warehouse && flag.nr_of_roads() <= 3) {
+		create_shortcut_road(flag, 9, 0, gametime);
+		inhibit_road_building_ = gametime + 400;
+	} else if (flag.current_wares() > 6 && gametime % 10 == 0) {
 		create_shortcut_road(flag, 9, 0, gametime);
 		inhibit_road_building_ = gametime + 400;
 	}
@@ -2253,8 +2270,8 @@
 		    eco->dismantle_grace_time_ != std::numeric_limits<int32_t>::max()) {
 			;
 
-		// if grace time is not set, this is probably first time without a warehouse and we must
-		// set it
+			// if grace time is not set, this is probably first time without a warehouse and we must
+			// set it
 		} else if (eco->dismantle_grace_time_ == std::numeric_limits<int32_t>::max()) {
 
 			// constructionsites
@@ -2267,14 +2284,14 @@
 					eco->dismantle_grace_time_ = gametime + 60 * 60 * 1000;  // one hour should be enough
 				} else {  // other constructionsites, usually new (standalone) constructionsites
 					eco->dismantle_grace_time_ =
-					   gametime + 30 * 1000 +  // very shot time is enough
+					   gametime + 30 * 1000 +            // very shot time is enough
 					   (eco->flags.size() * 30 * 1000);  // + 30 seconds for every flag in economy
 				}
 
-			// buildings
+				// buildings
 			} else {
 
-				//occupied military buildings get special treatment
+				// occupied military buildings get special treatment
 				//(extended grace time)
 				bool occupied_military_ = false;
 				Building* b = flag.get_building();
@@ -2287,7 +2304,7 @@
 				if (occupied_military_) {
 					eco->dismantle_grace_time_ =
 					   (gametime + 20 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
-					checkradius += 3;
+					checkradius += 5;
 
 				} else {  // for other normal buildings
 					eco->dismantle_grace_time_ =
@@ -2298,7 +2315,7 @@
 			// we have passed grace_time - it is time to dismantle
 		} else {
 			last_attempt_ = true;
-			//we increase a check radius in last attempt
+			// we increase a check radius in last attempt
 			checkradius += 2;
 		}
 	}
@@ -2552,12 +2569,10 @@
  * \returns true, if something was changed.
  */
 bool DefaultAI::check_productionsites(int32_t gametime) {
-	if ((next_productionsite_check_due_ > gametime) || productionsites.empty()) {
+	if (productionsites.empty()) {
 		return false;
 	}
 
-	next_productionsite_check_due_ = gametime + 4000;
-
 	bool changed = false;
 	// Reorder and set new values; - better now because there are multiple returns in the function
 	productionsites.push_back(productionsites.front());
@@ -2831,7 +2846,7 @@
 
 		// 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() * 5;
+		int16_t score = site.bo->stocklevel_ - productionsites.size() * 2;
 
 		if (score > 200 && site.bo->cnt_built_ > site.bo->cnt_target_) {
 
@@ -2859,16 +2874,18 @@
 // and makes two decisions:
 // - build a ship
 // - start preparation for expedition
-bool DefaultAI::marine_main_decisions(uint32_t const gametime) {
+bool DefaultAI::marine_main_decisions(int32_t const gametime) {
 	if (gametime < next_marine_decisions_due) {
 		return false;
 	}
+
+	if (!seafaring_economy) {
+		next_marine_decisions_due = std::numeric_limits<int32_t>::max();
+		return false;
+	}
+
 	next_marine_decisions_due = gametime + kMarineDecisionInterval;
 
-	if (!seafaring_economy) {
-		return false;
-	}
-
 	// getting some base statistics
 	player_ = game().get_player(player_number());
 	uint16_t ports_count = 0;
@@ -2969,17 +2986,18 @@
 }
 
 // This identifies ships that are waiting for command
-bool DefaultAI::check_ships(uint32_t const gametime) {
-	if (gametime < next_ship_check_due) {
+bool DefaultAI::check_ships(int32_t const gametime) {
+	if (gametime < next_ship_check_due_) {
 		return false;
 	}
 
-	next_ship_check_due = gametime + kShipCheckInterval;
-
 	if (!seafaring_economy) {
+		next_ship_check_due_ = std::numeric_limits<int32_t>::max();
 		return false;
 	}
 
+	bool action_taken = false;
+
 	if (!allships.empty()) {
 		// iterating over ships and executing what is needed
 		for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
@@ -3001,6 +3019,7 @@
 			// if ships is waiting for command
 			if (i->waiting_for_command_) {
 				expedition_management(*i);
+				action_taken = true;
 			}
 		}
 	}
@@ -3036,6 +3055,12 @@
 		marineTaskQueue_.pop_back();
 	}
 
+	if (action_taken) {
+		next_ship_check_due_ = gametime + kShipCheckInterval;
+	} else {
+		next_ship_check_due_ = gametime + 3 * kShipCheckInterval;
+	}
+
 	return true;
 }
 
@@ -3046,10 +3071,10 @@
  * \returns true, if something was changed.
  */
 bool DefaultAI::check_mines_(int32_t const gametime) {
-	if ((next_mine_check_due_ > gametime) || mines_.empty())
+	if (mines_.empty()) {
 		return false;
+	}
 
-	next_mine_check_due_ = gametime + 7000;  // 7 seconds is enough
 	// Reorder and set new values; - due to returns within the function
 	mines_.push_back(mines_.front());
 	mines_.pop_front();
@@ -3172,6 +3197,18 @@
 	}
 }
 
+// very primitive function - it sets thisTask to dueTask, if its due time is
+// older then due time of current dueTask
+void DefaultAI::scheduler_review(int32_t* next_check_due_,
+                                 int32_t* oldestTaskTime,
+                                 ScheduleTasks* dueTask,
+                                 ScheduleTasks thisTask) {
+	if (*next_check_due_ < *oldestTaskTime) {
+		*oldestTaskTime = *next_check_due_;
+		*dueTask = thisTask;
+	}
+}
+
 // counts produced output on stock
 // if multiple outputs, it returns lowest value
 uint32_t DefaultAI::get_stocklevel(BuildingObserver& bo) {
@@ -3235,7 +3272,7 @@
 	if (!trainingsites.empty()) {
 		next_trainingsites_check_due_ = gametime + kTrainingSitesCheckInterval;
 	} else {
-		next_trainingsites_check_due_ = gametime + 3 * kTrainingSitesCheckInterval;
+		next_trainingsites_check_due_ = gametime + 5 * kTrainingSitesCheckInterval;
 	}
 
 	uint8_t new_priority = DEFAULT_PRIORITY;
@@ -3989,9 +4026,6 @@
 			}
 		}
 	}
-
-	m_buildable_changed = true;
-	m_mineable_changed = true;
 }
 
 // Checks that supply line exists for given building.
@@ -4306,7 +4340,13 @@
 // and needs to know what resourcess are missing for which player and so on.
 // By default it is off (see kPrintStats)
 // TODO(tiborb ?): - it would be nice to have this activated by a command line switch
-void DefaultAI::print_stats() {
+void DefaultAI::print_stats(int32_t const gametime) {
+
+	if (!kPrintStats) {
+		next_statistics_report_ = std::numeric_limits<int32_t>::max();
+		return;
+	}
+	next_statistics_report_ = gametime + 30 * 60 * 1000;
 
 	PlayerNumber const pn = player_number();
 

=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h	2015-02-16 21:19:59 +0000
+++ src/ai/defaultai.h	2015-02-27 20:46:25 +0000
@@ -80,6 +80,24 @@
 
 	enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers };
 	enum class NewShip : uint8_t {kBuilt, kFoundOnLoad };
+	enum class ScheduleTasks : uint8_t {
+		kBFCheck,
+		kRoadCheck,
+		kUnbuildableFCheck,
+		kConsiderAttack,
+		kCheckEconomies,
+		kProductionsitesStats,
+		kConstructBuilding,
+		kCheckProductionsites,
+		kCheckShips,
+		KMarineDecisions,
+		kCheckMines,
+		kWareReview,
+		kprintStats,
+		kIdle,
+		kCheckMilitarysites,
+		kCheckTrainingsites
+	};
 
 	/// Implementation for Aggressive
 	struct AggressiveImpl : public ComputerPlayer::Implementation {
@@ -136,6 +154,11 @@
 	                          int16_t* max_preciousness,
 	                          int16_t* max_needed_preciousness);
 
+	void scheduler_review(int32_t* next_check_due_,
+	                      int32_t* oldestTaskTime,
+	                      ScheduleTasks* DueTask,
+	                      ScheduleTasks thisTask);
+
 	bool construct_building(int32_t);
 
 	uint32_t coords_hash(Widelands::Coords coords) {
@@ -165,14 +188,13 @@
 	bool check_trainingsites(int32_t);
 	bool check_mines_(int32_t);
 	bool check_militarysites(int32_t);
-	bool marine_main_decisions(uint32_t);
-	bool check_ships(uint32_t);
-	void print_stats();
+	bool marine_main_decisions(int32_t);
+	bool check_ships(int32_t);
+	void print_stats(int32_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 check_helpersites(int32_t);
 	void review_wares_targets(int32_t);
 
 	// sometimes scanning an area in radius gives inappropriate results, so this is to verify that
@@ -213,8 +235,7 @@
 	// Variables of default AI
 	uint8_t type_;
 
-	bool m_buildable_changed;
-	bool m_mineable_changed;
+	uint32_t schedStat[20] = {0};  // NOCOM
 
 	Widelands::Player* player_;
 	Widelands::TribeDescr const* tribe_;
@@ -243,6 +264,7 @@
 
 	std::vector<WareObserver> wares;
 
+	int32_t next_ai_think_;
 	int32_t next_road_due_;
 	int32_t next_stats_update_due_;
 	int32_t next_construction_due_;
@@ -250,11 +272,13 @@
 	int32_t next_productionsite_check_due_;
 	int32_t next_mine_check_due_;
 	int32_t next_militarysite_check_due_;
-	uint32_t next_ship_check_due;
-	uint32_t next_marine_decisions_due;
+	int32_t next_ship_check_due_;
+	int32_t next_economies_check_due_;
+	int32_t next_marine_decisions_due;
 	int32_t next_attack_consideration_due_;
 	int32_t next_trainingsites_check_due_;
 	int32_t next_bf_check_due_;
+	int32_t next_uf_check_due_;
 	int32_t next_wares_review_due_;
 	int32_t next_statistics_report_;
 	int32_t inhibit_road_building_;


Follow ups