← Back to team overview

widelands-dev team mailing list archive

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

 

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

Requested reviews:
  Widelands Developers (widelands-dev)

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

Changes in AI - related to mining
- AI now calculates count of available mineable spots per resource
- for above it distinguishes critical ores (iron + granite if output of mine is used for building)
- fixed stupid bug with iron_ore_id which contained index of iron ore as ware, but was used as index of iron as underground resource
- few changes in GA (so some training will be needed afterwards)
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/ai_mineable_fields into lp:widelands.
=== modified file 'src/ai/ai_help_structs.cc'
--- src/ai/ai_help_structs.cc	2018-02-16 20:42:21 +0000
+++ src/ai/ai_help_structs.cc	2018-03-23 20:35:11 +0000
@@ -335,6 +335,52 @@
    : in_construction(0), finished(0), is_critical(false), unoccupied(0) {
 }
 
+// Reset counter for all field types
+void MineFieldsObserver::zero() {
+	for (auto& material : stat) {
+		material.second = 0;
+	}
+}
+
+// Increase counter by one for specific ore/minefield type
+void MineFieldsObserver::add(const Widelands::DescriptionIndex idx) {
+	stat[idx] += 1;
+}
+
+// Add ore into critical_ores
+void MineFieldsObserver::add_critical_ore(const Widelands::DescriptionIndex idx) {
+	critical_ores.insert(idx);
+}
+
+// Does the player has at least one mineable field with positive amount for each critical ore?
+bool MineFieldsObserver::has_critical_ore_fields() {
+	for (auto ore : critical_ores) {
+		if (get(ore) == 0) {
+			return false;
+		}
+	}
+	return true;
+}
+
+// Returns count of fields with desired ore
+uint16_t MineFieldsObserver::get(const Widelands::DescriptionIndex idx) {
+	if (stat.count(idx) == 0) {
+		return 0;
+	}
+	return stat[idx];
+}
+
+// Count of types of mineable fields, up to 4 currently
+uint8_t MineFieldsObserver::count_types() {
+	uint16_t count = 0;
+	for (auto material : stat) {
+		if (material.second > 0) {
+			count += 1;
+		}
+	}
+	return count;
+}
+
 ExpansionType::ExpansionType() {
 	type = ExpansionMode::kResources;
 }

=== modified file 'src/ai/ai_help_structs.h'
--- src/ai/ai_help_structs.h	2018-02-16 21:04:59 +0000
+++ src/ai/ai_help_structs.h	2018-03-23 20:35:11 +0000
@@ -556,6 +556,22 @@
 	uint16_t unoccupied;
 };
 
+// This struct contains count of mineable fields grouped by ore/resource type
+struct MineFieldsObserver {
+
+	void zero();
+	void add(Widelands::DescriptionIndex);
+	void add_critical_ore(Widelands::DescriptionIndex);
+	bool has_critical_ore_fields();
+	uint16_t get(Widelands::DescriptionIndex);
+	uint8_t count_types();
+
+private:
+	// This is the central information of the struct: a pair of resource and count of fields
+	std::map<Widelands::DescriptionIndex, uint16_t> stat;
+	std::set<Widelands::DescriptionIndex> critical_ores;
+};
+
 constexpr int kNeuronWeightLimit = 100;
 constexpr size_t kNeuronMaxPosition = 20;
 constexpr size_t kSecondParentProbability = 50;

=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc	2018-03-02 07:50:34 +0000
+++ src/ai/defaultai.cc	2018-03-23 20:35:11 +0000
@@ -695,7 +695,8 @@
 				bo.set_is(BuildingAttribute::kSupportingProducer);
 			}
 
-			iron_ore_id = tribe_->ironore();
+			iron_resource_id = game().world().get_resource("iron");
+			assert (iron_resource_id != INVALID_INDEX);
 
 			if (bo.type == BuildingObserver::Type::kMine) {
 				// get the resource needed by the mine
@@ -710,9 +711,10 @@
 					mines_per_type[bo.mines] = MineTypesObserver();
 				}
 				// Identify iron mines based on output
-				if (bo.outputs[0] == iron_ore_id) {
+				if (bo.outputs[0] == tribe_->ironore()) {
 					bo.set_is(BuildingAttribute::kIronMine);
 					mines_per_type[bo.mines].is_critical = true;
+					mine_fields_stat.add_critical_ore(bo.mines);
 				}
 			}
 
@@ -782,8 +784,8 @@
 				    !(ware == tribe_->rawlog() || ware == tribe_->granite())) {
 					bo.set_is(BuildingAttribute::kBuildingMatProducer);
 					if (bo.type == BuildingObserver::Type::kMine) {
-						has_critical_mines = true;
 						mines_per_type[bo.mines].is_critical = true;
+						mine_fields_stat.add_critical_ore(bo.mines);
 					}
 				}
 			}
@@ -1035,9 +1037,6 @@
 		persistent_data->expedition_start_time = gametime;
 	}
 
-	// just to be sure we have iron mines identified
-	assert(iron_ore_id != INVALID_INDEX);
-
 	productionsites_ratio_ = management_data.get_military_number_at(86) / 10 + 12;
 
 	// Just to be initialized
@@ -1212,6 +1211,21 @@
 
 		i += 1;
 	}
+	// Updating overall statistics, first we flush the data and then iterate over all mine fields
+	mine_fields_stat.zero();
+	for (uint32_t j = 0; j < mineable_fields.size(); j++) {
+	    if (mineable_fields[j]->coords.field->get_resources_amount() > 0) {
+		    mine_fields_stat.add(mineable_fields[j]->coords.field->get_resources());
+		   }
+	}
+
+	// Following asserts presume that there is 1-3 critical mines
+	if (mine_fields_stat.count_types() == 4) {
+		assert(mine_fields_stat.has_critical_ore_fields());
+	}
+	if (mine_fields_stat.count_types() == 0) {
+		assert(!mine_fields_stat.has_critical_ore_fields());
+	}
 }
 
 /**
@@ -1267,7 +1281,7 @@
 	FindEnemyNodeWalkable find_enemy_owned_walkable(player_, game());
 	FindNodeUnownedBuildable find_unowned_buildable(player_, game());
 	FindNodeUnownedMineable find_unowned_mines_pots(player_, game());
-	FindNodeUnownedMineable find_unowned_iron_mines(player_, game(), iron_ore_id);
+	FindNodeUnownedMineable find_unowned_iron_mines(player_, game(), iron_resource_id);
 	FindNodeAllyOwned find_ally(player_, game(), player_number());
 	PlayerNumber const pn = player_->player_number();
 	const World& world = game().world();
@@ -1363,7 +1377,7 @@
 		if (field.unowned_mines_spots_nearby > 0 &&
 		    // for performance considerations we count iron nodes only if we have less than 2 iron
 		    // mines now...
-		    (mines_per_type[iron_ore_id].in_construction + mines_per_type[iron_ore_id].finished) <=
+		    (mines_per_type[iron_resource_id].in_construction + mines_per_type[iron_resource_id].finished) <=
 		       1) {
 			// counting iron mines, if we have less than two iron mines
 			field.unowned_iron_mines_nearby = map.find_fields(
@@ -1700,7 +1714,7 @@
 		field.inland = true;
 	}
 
-	const uint8_t score_parts_size = 64;
+	const uint8_t score_parts_size = 69;
 	int32_t score_parts[score_parts_size] = {0};
 	if (field.enemy_owned_land_nearby) {
 		score_parts[0] = 3 *
@@ -1755,7 +1769,7 @@
 		   (field.enemy_nearby) ? 3 * std::abs(management_data.get_military_number_at(68)) : 0;
 		score_parts[19] =
 		   (field.enemy_wh_nearby) ? 3 * std::abs(management_data.get_military_number_at(132)) : 0;
-		score_parts[58] = (field.enemy_wh_nearby) ?
+		score_parts[64] = (field.enemy_wh_nearby) ?
 		                     std::abs(management_data.get_military_number_at(135)) :
 		                     -std::abs(management_data.get_military_number_at(135));
 
@@ -1825,18 +1839,18 @@
 		}
 		if (msites_in_constr() > 0 && field.max_buildcap_nearby == BUILDCAPS_BIG &&
 		    spots_avail.at(BUILDCAPS_BIG) <= 2) {
-			score_parts[57] = -10 * std::abs(management_data.get_military_number_at(54));
+			score_parts[65] = -10 * std::abs(management_data.get_military_number_at(54));
 		}
 	}
 
 	// common inputs
-	if (field.unowned_iron_mines_nearby > 0 && ((mines_per_type[iron_ore_id].in_construction +
-	                                             mines_per_type[iron_ore_id].finished) == 0)) {
+	if (field.unowned_iron_mines_nearby > 0 && ((mines_per_type[iron_resource_id].in_construction +
+	                                             mines_per_type[iron_resource_id].finished) == 0)) {
 		score_parts[40] = field.unowned_iron_mines_nearby *
 		                  std::abs(management_data.get_military_number_at(92)) / 50;
 	}
-	if (field.unowned_iron_mines_nearby && ((mines_per_type[iron_ore_id].in_construction +
-	                                         mines_per_type[iron_ore_id].finished) <= 1)) {
+	if (field.unowned_iron_mines_nearby && ((mines_per_type[iron_resource_id].in_construction +
+	                                         mines_per_type[iron_resource_id].finished) <= 1)) {
 		score_parts[41] = 3 * std::abs(management_data.get_military_number_at(93));
 	}
 
@@ -1892,6 +1906,16 @@
 	score_parts[63] = (field.nearest_buildable_spot_nearby < 4) ?
 	                     std::abs(management_data.get_military_number_at(155) * 2) :
 	                     0;
+	// 64 and 65 are used above
+	score_parts[66] = (field.unowned_mines_spots_nearby > 0 && !mine_fields_stat.has_critical_ore_fields()) ?
+		                     std::abs(management_data.get_military_number_at(157)) :
+		                     0;
+	score_parts[67] = (field.unowned_mines_spots_nearby > 0 && mine_fields_stat.count_types() <= 4 ) ?
+		                     std::abs(management_data.get_military_number_at(158)) :
+		                     0;
+	score_parts[68] = (field.unowned_mines_spots_nearby == 0 && mine_fields_stat.count_types() <= 4 ) ?
+		                     -std::abs(management_data.get_military_number_at(159)) :
+		                     0;
 
 	for (uint16_t i = 0; i < score_parts_size; i++) {
 		field.military_score_ += score_parts[i];
@@ -2218,6 +2242,12 @@
 	inputs[50] = (bakeries_count_ <= 1);
 	inputs[51] = (numof_psites_in_constr > 8);
 	inputs[52] = (numof_psites_in_constr < 8);
+	inputs[53] = (mine_fields_stat.has_critical_ore_fields());
+	inputs[54] = (!mine_fields_stat.has_critical_ore_fields());
+	inputs[55] = (mine_fields_stat.count_types() == 4);
+	inputs[56] = (mine_fields_stat.count_types() != 4);
+	inputs[57] = (mine_fields_stat.has_critical_ore_fields());
+	inputs[58] = (!mine_fields_stat.has_critical_ore_fields());
 
 	static int16_t needs_boost_economy_score = management_data.get_military_number_at(61) / 5;
 	needs_boost_economy_score = management_data.get_military_number_at(61) / 5;
@@ -2240,12 +2270,10 @@
 	}
 
 	// Finding expansion policy
-	// Do we need basic resources?
-	// This is a replacement for simple count of mines
-	const int32_t virtual_mines = mines_.size() + mineable_fields.size() / 15;
+	// Do we need basic resources? Do we have basic mines?
 	const bool needs_fishers = resource_necessity_water_needed_ && fishers_count_ < 1;
 
-	if (virtual_mines < 4 || mines_per_type[iron_ore_id].total_count() < 1 || needs_fishers) {
+	if (!mine_fields_stat.has_critical_ore_fields() || mines_per_type[iron_resource_id].total_count() < 1 || needs_fishers) {
 		expansion_type.set_expantion_type(ExpansionMode::kResources);
 	} else {
 		// now we must decide if we go after spots or economy boost
@@ -2976,10 +3004,6 @@
 					prio -= 5;
 				}
 
-				// be should rather have some mines
-				if (virtual_mines < 6) {
-					prio -= (6 - virtual_mines) * 7;
-				}
 
 				// take care about borders and enemies
 				if (bf->enemy_nearby) {
@@ -4402,7 +4426,7 @@
 	// Single _critical is a critical mine if it is the only one of its type, so it needs special
 	// treatment
 	bool single_critical = false;
-	if ((site.bo->is(BuildingAttribute::kBuildingMatProducer) || site.bo->mines == iron_ore_id) &&
+	if ((site.bo->is(BuildingAttribute::kBuildingMatProducer) || site.bo->mines == iron_resource_id) &&
 	    mines_per_type[site.bo->mines].total_count() == 1) {
 		single_critical = true;
 	}
@@ -4680,6 +4704,11 @@
 			bo.primary_priority += std::abs(management_data.get_military_number_at(57) / 2);
 		}
 
+		// Do we own some minefields for each critical mine
+		if (!mine_fields_stat.has_critical_ore_fields()) {
+			bo.primary_priority -= std::abs(management_data.get_military_number_at(156));
+		}
+
 		// We build one trainig site per X military sites
 		// with some variations, of course
 		int32_t target = 1 +
@@ -5015,17 +5044,17 @@
 			}
 			inputs[31] = (persistent_data->trees_around_cutters < 100) * 2;
 			inputs[32] = (persistent_data->trees_around_cutters < 200) * 2;
-			inputs[33] = ((mines_per_type[iron_ore_id].in_construction +
-			               mines_per_type[iron_ore_id].finished) <= 1) *
-			             -1;
-			inputs[34] = ((mines_per_type[iron_ore_id].in_construction +
-			               mines_per_type[iron_ore_id].finished) <= 1) *
-			             -1;
-			inputs[35] = ((mines_per_type[iron_ore_id].in_construction +
-			               mines_per_type[iron_ore_id].finished) == 0) *
-			             -1;
-			inputs[36] = ((mines_per_type[iron_ore_id].in_construction +
-			               mines_per_type[iron_ore_id].finished) == 0) *
+			inputs[33] = ((mines_per_type[iron_resource_id].in_construction +
+			               mines_per_type[iron_resource_id].finished) <= 1) *
+			             -1;
+			inputs[34] = ((mines_per_type[iron_resource_id].in_construction +
+			               mines_per_type[iron_resource_id].finished) <= 1) *
+			             -1;
+			inputs[35] = ((mines_per_type[iron_resource_id].in_construction +
+			               mines_per_type[iron_resource_id].finished) == 0) *
+			             -1;
+			inputs[36] = ((mines_per_type[iron_resource_id].in_construction +
+			               mines_per_type[iron_resource_id].finished) == 0) *
 			             -1;
 			inputs[37] = -1;
 			inputs[38] = -1;
@@ -5164,7 +5193,7 @@
 
 				int16_t tmp_score = 1;
 				tmp_score +=
-				   mines_per_type[iron_ore_id].in_construction + mines_per_type[iron_ore_id].finished;
+				   mines_per_type[iron_resource_id].in_construction + mines_per_type[iron_resource_id].finished;
 				tmp_score += (soldier_status_ == SoldiersStatus::kBadShortage) * 2;
 				tmp_score += (soldier_status_ == SoldiersStatus::kShortage) * 2;
 				tmp_score += (gametime / 60 / 1000 - 20) / 4;
@@ -5225,7 +5254,7 @@
 			   (mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished == 1) ?
 			      2 :
 			      0;
-			inputs[5] = (bo.mines == iron_ore_id) ? 2 : 1;
+			inputs[5] = (bo.mines == iron_resource_id) ? 2 : 1;
 			inputs[6] = (bo.current_stats - 50) / 10;
 			inputs[7] = (gametime < 15 * 60 * 1000) ? -1 : 0;
 			inputs[8] = (gametime < 30 * 60 * 1000) ? -1 : 0;
@@ -5238,7 +5267,7 @@
 			   (mines_per_type[bo.mines].in_construction + mines_per_type[bo.mines].finished == 1) ?
 			      1 :
 			      0;
-			inputs[12] = (bo.mines == iron_ore_id) ? 2 : 0;
+			inputs[12] = (bo.mines == iron_resource_id) ? 2 : 0;
 			inputs[13] = (bo.current_stats - 50) / 10;
 			inputs[14] = (bo.current_stats - 50) / 10;
 			inputs[15] = management_data.get_military_number_at(123) / 10;

=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h	2018-02-20 21:11:27 +0000
+++ src/ai/defaultai.h	2018-03-23 20:35:11 +0000
@@ -287,6 +287,7 @@
 	Widelands::BuildingNecessity check_building_necessity(Widelands::BuildingObserver&, uint32_t);
 	void soldier_trained(const Widelands::TrainingSite&);
 	bool critical_mine_unoccupied(uint32_t);
+
 	SoldiersStatus soldier_status_;
 	int32_t vacant_mil_positions_average_;
 	uint16_t attackers_count_;
@@ -330,6 +331,8 @@
 	// it will map mined material to observer
 	std::map<int32_t, Widelands::MineTypesObserver> mines_per_type;
 	std::vector<uint32_t> spots_avail;
+	Widelands::MineFieldsObserver mine_fields_stat;
+
 
 	// used for statistics of buildings
 	uint32_t numof_psites_in_constr;
@@ -377,8 +380,8 @@
 	// buildings
 	bool basic_economy_established;
 
-	// id of iron_ore to identify iron mines in mines_per_type map
-	int32_t iron_ore_id = Widelands::INVALID_INDEX;
+	// id of iron as resource to identify iron mines in mines_per_type map
+	int32_t iron_resource_id = Widelands::INVALID_INDEX;
 
 	// this is a bunch of patterns that have to identify weapons and armors for input queues of
 	// trainingsites
@@ -398,7 +401,6 @@
 	std::vector<std::vector<int16_t>> AI_military_matrix;
 	std::vector<int16_t> AI_military_numbers;
 
-	bool has_critical_mines = false;
 	uint16_t buil_material_mines_count = 0;
 
 	bool ai_training_mode_ = false;

=== modified file 'src/ai/defaultai_warfare.cc'
--- src/ai/defaultai_warfare.cc	2018-02-15 20:31:17 +0000
+++ src/ai/defaultai_warfare.cc	2018-03-23 20:35:11 +0000
@@ -322,8 +322,8 @@
 				inputs[27] = (ts_finished_count_ - ts_without_trainers_) * 2;
 				inputs[28] = general_score * 3;
 				inputs[29] = general_score;
-				inputs[30] = ((mines_per_type[iron_ore_id].in_construction +
-				               mines_per_type[iron_ore_id].finished) > 0) ?
+				inputs[30] = ((mines_per_type[iron_resource_id].in_construction +
+				               mines_per_type[iron_resource_id].finished) > 0) ?
 				                1 :
 				                -1;
 				inputs[31] = (player_statistics.get_player_power(pn) >
@@ -682,7 +682,7 @@
 			               1 :
 			               0;
 			inputs[2] = (mines_.size() < 3) ? -1 : 0;
-			inputs[3] = (mines_per_type[iron_ore_id].total_count() == 0) ? -1 : 0;
+			inputs[3] = (mines_per_type[iron_resource_id].total_count() == 0) ? -1 : 0;
 			inputs[4] = (player_statistics.get_player_power(pn) * 2 >
 			             player_statistics.get_visible_enemies_power(gametime)) ?
 			               -1 :
@@ -1006,7 +1006,7 @@
 	inputs[12] = (scores[size - 1] > total_score / 3) ? -2 : 0;
 	inputs[13] =
 	   (player_statistics.get_enemies_max_land() < player_statistics.get_player_land(pn)) ? -1 : 0;
-	inputs[14] = (mines_per_type[iron_ore_id].total_count() == 0) ? +1 : 0;
+	inputs[14] = (mines_per_type[iron_resource_id].total_count() == 0) ? +1 : 0;
 	inputs[15] = (spots_ < kSpotsTooLittle) ? +1 : 0;
 	inputs[16] = +1;
 	inputs[17] = +2;
@@ -1121,11 +1121,11 @@
 	inputs[57] =
 	   player_statistics.any_enemy_seen_lately(gametime) && (spots_ < kSpotsTooLittle) ? +2 : 0;
 	inputs[58] =
-	   ((mines_per_type[iron_ore_id].in_construction + mines_per_type[iron_ore_id].finished) == 0) ?
+	   ((mines_per_type[iron_resource_id].in_construction + mines_per_type[iron_resource_id].finished) == 0) ?
 	      +3 :
 	      0;
 	inputs[59] =
-	   ((mines_per_type[iron_ore_id].in_construction + mines_per_type[iron_ore_id].finished) == 0) ?
+	   ((mines_per_type[iron_resource_id].in_construction + mines_per_type[iron_resource_id].finished) == 0) ?
 	      +1 :
 	      0;
 	inputs[60] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) ? -2 : 0;
@@ -1238,12 +1238,22 @@
 		inputs[114] = -10;
 	}
 
+	if (!mine_fields_stat.has_critical_ore_fields()) {
+		inputs[115] = -3;
+		inputs[116] = -6;
+		inputs[117] = -8;
+	}
+    inputs[118] = -mine_fields_stat.count_types();
+    inputs[119] = -mine_fields_stat.count_types() * 3;
+
+
 	for (int i = 0; i < 4 * kFNeuronBitSize; i = i + 1) {
 		if (inputs[i] < -35 || inputs[i] > 6) {
 			log("Warning check_building_necessity score on position %2d too high %2d\n", i, inputs[i]);
 		}
 	}
 
+
 	int32_t final_score = 0;
 	for (int i = 0; i < kFNeuronBitSize; i = i + 1) {
 		if (management_data.f_neuron_pool[56].get_position(i)) {


Follow ups