← Back to team overview

widelands-dev team mailing list archive

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

 

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

Requested reviews:
  Widelands Developers (widelands-dev)

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

ai training mode is triggered now from command line, it has three effects:
- dumps new AI files in home folder
- activate auto-speed
- uses higher mutation ratios

There is one NOCOM/question left in the code
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/ai_training_switch into lp:widelands.
=== modified file 'src/ai/ai_help_structs.cc'
--- src/ai/ai_help_structs.cc	2017-07-25 20:56:04 +0000
+++ src/ai/ai_help_structs.cc	2017-08-14 20:03:03 +0000
@@ -31,9 +31,9 @@
 constexpr int kRoadNotFound = -1000;
 constexpr int kShortcutWithinSameEconomy = 1000;
 constexpr int kRoadToDifferentEconomy = 10000;
+constexpr int kNoAiTrainingMutation = 200;
 constexpr int kUpperDefaultMutationLimit = 150;
 constexpr int kLowerDefaultMutationLimit = 75;
-constexpr int16_t kPrefNumberProbability = kAITrainingMode ? 5 : 100;
 
 // CheckStepRoadAI
 CheckStepRoadAI::CheckStepRoadAI(Player* const pl, uint8_t const mc, bool const oe)
@@ -347,6 +347,8 @@
 	next_neuron_id = 0;
 	performance_change = 0;
 	AiDnaHandler ai_dna_handler();
+	ai_training_mode_ = false;
+	pref_number_probability = 100;
 }
 
 // Initialization of neuron. Neuron is defined by curve (type) and weight [-kWeightRange,
@@ -620,11 +622,18 @@
 
 	int16_t probability =
 	   shift_weight_value(get_military_number_at(kMutationRatePosition), false) + 101;
-	if (probability > kUpperDefaultMutationLimit) {
-		probability = kUpperDefaultMutationLimit;
-	}
-	if (probability < kLowerDefaultMutationLimit) {
-		probability = kLowerDefaultMutationLimit;
+
+
+	// When doing training mutation probability is to be bigger than not in training mode
+	if (ai_training_mode_) {
+		if (probability > kUpperDefaultMutationLimit) {
+			probability = kUpperDefaultMutationLimit;
+		}
+		if (probability < kLowerDefaultMutationLimit) {
+			probability = kLowerDefaultMutationLimit;
+		}
+	} else {
+		probability = kNoAiTrainingMutation;
 	}
 
 	set_military_number_at(kMutationRatePosition, probability - 101);
@@ -639,7 +648,7 @@
 	}
 
 	// Wildcard for ai trainingmode
-	if (kAITrainingMode && std::rand() % 8 == 0) {
+	if (ai_training_mode_ && std::rand() % 8 == 0) {
 		probability /= 3;
 	}
 
@@ -654,7 +663,7 @@
 			// Preferred numbers are ones that will be mutated agressively in full range
 			// [-kWeightRange, kWeightRange]
 			std::set<int32_t> preferred_numbers = {std::rand() % kMagicNumbersSize *
-			                                       kPrefNumberProbability};
+			                                       pref_number_probability};
 
 			for (uint16_t i = 0; i < kMagicNumbersSize; i += 1) {
 				if (i == kMutationRatePosition) {  // mutated above
@@ -680,7 +689,7 @@
 		{
 			// Neurons to be mutated more agressively
 			std::set<int32_t> preferred_neurons = {std::rand() % kNeuronPoolSize *
-			                                       kPrefNumberProbability};
+			                                       pref_number_probability};
 			for (auto& item : neuron_pool) {
 
 				const MutatingIntensity mutating_intensity =
@@ -711,7 +720,7 @@
 		{
 			// FNeurons to be mutated more agressively
 			std::set<int32_t> preferred_f_neurons = {std::rand() % kFNeuronPoolSize *
-			                                         kPrefNumberProbability};
+			                                         pref_number_probability};
 			for (auto& item : f_neuron_pool) {
 				uint8_t changed_bits = 0;
 				// is this a preferred neuron

=== modified file 'src/ai/ai_help_structs.h'
--- src/ai/ai_help_structs.h	2017-08-12 06:51:08 +0000
+++ src/ai/ai_help_structs.h	2017-08-14 20:03:03 +0000
@@ -119,12 +119,13 @@
 };
 
 // TODO(tiborb): this should be replaced by command line switch
-constexpr bool kAITrainingMode = false;
 constexpr int kMagicNumbersSize = 150;
 constexpr int kNeuronPoolSize = 80;
 constexpr int kFNeuronPoolSize = 60;
 constexpr int kFNeuronBitSize = 32;
 constexpr int kMutationRatePosition = 42;
+constexpr int16_t AiPrefNumberProbability = 5;
+
 
 constexpr uint32_t kNever = std::numeric_limits<uint32_t>::max();
 
@@ -665,6 +666,11 @@
 	void reset_bi_neuron_id() {
 		next_bi_neuron_id = 0;
 	}
+	void set_ai_training_mode() {
+		ai_training_mode_ = true;
+		pref_number_probability = AiPrefNumberProbability;
+	}
+
 	int16_t get_military_number_at(uint8_t);
 	void set_military_number_at(uint8_t, int16_t);
 	MutatingIntensity do_mutate(uint8_t, int16_t);
@@ -672,6 +678,7 @@
 	void test_consistency(bool = false);
 	AiDnaHandler ai_dna_handler;
 
+
 private:
 	int32_t score;
 	uint8_t primary_parent;
@@ -680,6 +687,8 @@
 	uint16_t performance_change;
 	Widelands::AiType ai_type;
 	void dump_output(Widelands::Player::AiPersistentState, PlayerNumber);
+	bool ai_training_mode_;
+	uint16_t pref_number_probability;
 };
 
 // this is used to count militarysites by their size

=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc	2017-08-01 12:03:03 +0000
+++ src/ai/defaultai.cc	2017-08-14 20:03:03 +0000
@@ -482,10 +482,14 @@
 void DefaultAI::late_initialization() {
 	player_ = game().get_player(player_number());
 	tribe_ = &player_->tribe();
+	if (game().is_ai_training_mode()) {
+		ai_training_mode_ = true;
+		management_data.set_ai_training_mode();
+	}
 	const uint32_t gametime = game().get_gametime();
 
-	log("ComputerPlayer(%d): initializing as type %u\n", player_number(),
-	    static_cast<unsigned int>(type_));
+	log("ComputerPlayer(%d): initializing as type %u%s\n", player_number(),
+	    static_cast<unsigned int>(type_), (ai_training_mode_)?", in ai training mode":"");
 	if (player_->team_number() > 0) {
 		log("    ... member of team %d\n", player_->team_number());
 	}
@@ -530,7 +534,7 @@
 		management_data.new_dna_for_persistent(player_number(), type_);
 		management_data.copy_persistent_to_local();
 		management_data.mutate(player_number());
-		if (kAITrainingMode) {
+		if (ai_training_mode_) {
 			management_data.dump_data(player_number());
 		}
 
@@ -553,7 +557,7 @@
 			persistent_data->least_military_score = persistent_data->target_military_score;
 		}
 
-		if (kAITrainingMode) {
+		if (ai_training_mode_) {
 			log("%2d: reinitializing dna (kAITrainingMode set true)", player_number());
 			management_data.new_dna_for_persistent(player_number(), type_);
 			management_data.copy_persistent_to_local();
@@ -912,7 +916,7 @@
 	                                 "count military vacant"));
 	taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 10 * 60 * 1000),
 	                                 SchedulerTaskId::kCheckEnemySites, 6, "check enemy sites"));
-	if (kAITrainingMode) {
+	if (ai_training_mode_) {
 		taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 10 * 1000),
 		                                 SchedulerTaskId::kManagementUpdate, 8, "reviewing"));
 	}
@@ -1782,7 +1786,7 @@
 		field.military_score_ += score_parts[i];
 	}
 
-	if (kAITrainingMode) {
+	if (ai_training_mode_) {
 		if (field.military_score_ < -5000 || field.military_score_ > 2000) {
 			log("Warning field.military_score_ %5d, compounds: ", field.military_score_);
 			for (uint16_t i = 0; i < score_parts_size; i++) {
@@ -2351,7 +2355,7 @@
 			bo.primary_priority = 0;
 		}
 
-		if (kAITrainingMode && bo.type == BuildingObserver::Type::kProductionsite) {
+		if (ai_training_mode_ && bo.type == BuildingObserver::Type::kProductionsite) {
 			log("%2d: %-35s(%2d now) %-11s: max prec: %2d/%2d, primary priority: %4d, overdue: %3d\n",
 			    player_number(), bo.name, bo.total_count(),
 			    (bo.new_building == BuildingNecessity::kAllowed ||

=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h	2017-07-19 20:40:32 +0000
+++ src/ai/defaultai.h	2017-08-14 20:03:03 +0000
@@ -395,6 +395,8 @@
 	bool has_critical_mines = false;
 	uint16_t buil_material_mines_count = 0;
 
+	bool ai_training_mode_ = false;
+
 	// Notification subscribers
 	std::unique_ptr<Notifications::Subscriber<Widelands::NoteFieldPossession>>
 	   field_possession_subscriber_;

=== modified file 'src/logic/game.cc'
--- src/logic/game.cc	2017-07-02 19:11:13 +0000
+++ src/logic/game.cc	2017-08-14 20:03:03 +0000
@@ -122,6 +122,7 @@
      ctrl_(nullptr),
      writereplay_(true),
      writesyncstream_(false),
+     ai_training_mode_(false),
      state_(gs_notrunning),
      cmdqueue_(*this),
      /** TRANSLATORS: Win condition for this game has not been set. */
@@ -151,6 +152,10 @@
 	ctrl_ = ctrl;
 }
 
+void Game::set_ai_training_mode(const bool mode) {
+	ai_training_mode_ = mode;
+}
+
 GameController* Game::game_controller() {
 	return ctrl_;
 }

=== modified file 'src/logic/game.h'
--- src/logic/game.h	2017-06-23 17:23:04 +0000
+++ src/logic/game.h	2017-08-14 20:03:03 +0000
@@ -126,7 +126,8 @@
 	// Run a single player loaded game directly via --loadgame on the cmdline. Will
 	// run the 'script_to_run' directly after the game was loaded.
 	// Returns the result of run().
-	bool run_load_game(const std::string& filename, const std::string& script_to_run);
+	bool run_load_game(const std::string& filename,
+		const std::string& script_to_run);
 
 	void postload() override;
 
@@ -231,6 +232,12 @@
 		return replay_;
 	}
 
+	bool is_ai_training_mode() const {
+		return ai_training_mode_;
+	}
+
+	void set_ai_training_mode(bool);
+
 private:
 	void sync_reset();
 
@@ -281,6 +288,8 @@
 	/// is written only if \ref writereplay_ is true too.
 	bool writesyncstream_;
 
+	bool ai_training_mode_;
+
 	int32_t state_;
 
 	RNG rng_;

=== modified file 'src/network/gamehost.cc'
--- src/network/gamehost.cc	2017-07-02 21:09:23 +0000
+++ src/network/gamehost.cc	2017-08-14 20:03:03 +0000
@@ -664,6 +664,7 @@
 	broadcast(s);
 
 	Widelands::Game game;
+	game.set_ai_training_mode(g_options.pull_section("global").get_bool("ai_training", false));
 	game.set_write_syncstream(g_options.pull_section("global").get_bool("write_syncstreams", true));
 
 	try {

=== modified file 'src/wlapplication.cc'
--- src/wlapplication.cc	2017-07-25 20:56:04 +0000
+++ src/wlapplication.cc	2017-08-14 20:03:03 +0000
@@ -413,6 +413,7 @@
 		replay();
 	} else if (game_type_ == LOADGAME) {
 		Widelands::Game game;
+		game.set_ai_training_mode(g_options.pull_section("global").get_bool("ai_training", false));
 		try {
 			game.run_load_game(filename_, script_to_run_);
 		} catch (const Widelands::GameDataError& e) {
@@ -955,6 +956,15 @@
 		commandline_.erase("script");
 	}
 
+	// NOCOM why non-existing section returns 'true' by default? It does not make sense to me....
+	// to be sure I set value in both alternatives
+	if (commandline_.count("ai_training")) {
+		g_options.pull_section("global").create_val("ai_training", "true");
+		commandline_.erase("ai_training");
+	} else {
+		g_options.pull_section("global").create_val("ai_training", "false");
+	}
+
 	// If it hasn't been handled yet it's probably an attempt to
 	// override a conffile setting
 	// With typos, this will create invalid config settings. They
@@ -1211,6 +1221,8 @@
 
 	Widelands::Game game;
 
+	game.set_ai_training_mode(g_options.pull_section("global").get_bool("ai_training", false));
+
 	if (code == FullscreenMenuBase::MenuTarget::kScenarioGame) {  // scenario
 		try {
 			game.run_splayer_scenario_direct(sp.get_map().c_str(), "");
@@ -1261,6 +1273,7 @@
 	Widelands::Game game;
 	std::string filename;
 
+	game.set_ai_training_mode(g_options.pull_section("global").get_bool("ai_training", false));
 	SinglePlayerGameSettingsProvider sp;
 	FullscreenMenuLoadGame ssg(game, &sp, nullptr);
 
@@ -1411,14 +1424,11 @@
  * Delete old ai dna files generated during AI initialization
  */
 void WLApplication::cleanup_ai_files() {
-	for (const std::string& filename :
-	     filter(g_fs->list_directory(
-	               Widelands::AiDnaHandler::get_ai_dir()),  // NOCOM repace with common defineds
-	            // used by both locations (here and ai_dna_handler)
-	            [](const std::string& fn) {
-		            return boost::ends_with(fn, Widelands::AiDnaHandler::get_ai_suffix()) ||
-		                   boost::contains(fn, "ai_player");
-		         })) {
+	for (const std::string& filename : filter(
+	        g_fs->list_directory(Widelands::AiDnaHandler::get_ai_dir()), [](const std::string& fn) {
+		        return boost::ends_with(fn, Widelands::AiDnaHandler::get_ai_suffix()) ||
+		               boost::contains(fn, "ai_player");
+		     })) {
 		if (is_autogenerated_and_expired(filename, kAIFilesKeepAroundTime)) {
 			log("Deleting generated ai file: %s\n", filename.c_str());
 			g_fs->fs_unlink(filename);

=== modified file 'src/wlapplication_messages.cc'
--- src/wlapplication_messages.cc	2017-01-25 18:55:59 +0000
+++ src/wlapplication_messages.cc	2017-08-14 20:03:03 +0000
@@ -90,6 +90,10 @@
 	               "                      You can add a =FILENAME to directly load\n"
 	               "                      the map FILENAME in editor.")
 	          << endl
+	          << _(" --ai_training        Starts in AI training mode: wai files are generated,\n"
+	               "                      autospeed is activated, and higher mutation rates are\n"
+	               "                      used.")
+	          << endl
 	          << _(" --scenario=FILENAME  Directly starts the map FILENAME as scenario\n"
 	               "                      map.")
 	          << endl

=== modified file 'src/wui/interactive_base.cc'
--- src/wui/interactive_base.cc	2017-08-12 20:13:24 +0000
+++ src/wui/interactive_base.cc	2017-08-14 20:03:03 +0000
@@ -55,9 +55,6 @@
 #include "wui/minimap.h"
 #include "wui/unique_window_handler.h"
 
-// TODO(tiborb): This constant is temporary and should be replaced by command line switch
-constexpr bool AItrainingMode = false;
-
 using Widelands::Area;
 using Widelands::CoordPath;
 using Widelands::Coords;
@@ -348,33 +345,37 @@
 	avg_usframetime_ = ((avg_usframetime_ * 15) + (frametime_ * 1000)) / 16;
 	lastframe_ = curframe;
 
+	const Map& map = egbase().map();
+	const bool is_game = dynamic_cast<const Game*>(&egbase());
+
 	// This portion of code keeps the speed of game so that FPS are kept within
 	// range 13 - 15, this is used for training of AI
-	if (AItrainingMode) {
+	if (is_game) {
 		if (upcast(Game, game, &egbase())) {
-			uint32_t cur_fps = 1000000 / avg_usframetime_;
-			int32_t speed_diff = 0;
-			if (cur_fps < 13) {
-				speed_diff = -100;
-			}
-			if (cur_fps > 15) {
-				speed_diff = +100;
-			}
-			if (speed_diff != 0) {
+				if (game->is_ai_training_mode()) {
+				uint32_t cur_fps = 1000000 / avg_usframetime_;
+				int32_t speed_diff = 0;
+				if (cur_fps < 13) {
+					speed_diff = -100;
+				}
+				if (cur_fps > 15) {
+					speed_diff = +100;
+				}
+				if (speed_diff != 0) {
 
-				if (GameController* const ctrl = game->game_controller()) {
-					if ((ctrl->desired_speed() > 950 && ctrl->desired_speed() < 30000) ||
-					    (ctrl->desired_speed() < 1000 && speed_diff > 0) ||
-					    (ctrl->desired_speed() > 29999 && speed_diff < 0)) {
-						ctrl->set_desired_speed(ctrl->desired_speed() + speed_diff);
+					if (GameController* const ctrl = game->game_controller()) {
+						if ((ctrl->desired_speed() > 950 && ctrl->desired_speed() < 30000) ||
+						    (ctrl->desired_speed() < 1000 && speed_diff > 0) ||
+						    (ctrl->desired_speed() > 29999 && speed_diff < 0)) {
+							ctrl->set_desired_speed(ctrl->desired_speed() + speed_diff);
+						}
 					}
 				}
 			}
 		}
 	}
 
-	const Map& map = egbase().map();
-	const bool is_game = dynamic_cast<const Game*>(&egbase());
+
 
 	// Blit node information when in debug mode.
 	if (get_display_flag(dfDebug) || !is_game) {


Follow ups