← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~s3734770/widelands/carli-ai into lp:widelands

 

Carli has proposed merging lp:~s3734770/widelands/carli-ai into lp:widelands.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~s3734770/widelands/carli-ai/+merge/82061

Hi,

I improved the AI a bit and fixed some bugs.
The AI now takes in account that it needs material for building and for creating soldiers.
The barbarians axefactory upgrade works now.

What I further will do:
 - Treat upgradable buildings like normal buildings and create construction time plans
 - find an automatic balance for the consumer-producer problem
-- 
https://code.launchpad.net/~s3734770/widelands/carli-ai/+merge/82061
Your team Widelands Developers is requested to review the proposed merge of lp:~s3734770/widelands/carli-ai into lp:widelands.
=== modified file 'compile.sh'
--- compile.sh	2010-11-15 21:23:02 +0000
+++ compile.sh	2011-11-13 00:10:30 +0000
@@ -136,7 +136,7 @@
 
     echo " "
     cmake -DWL_PORTABLE=true .. -DCMAKE_EXE_CXX_FLAGS="${CFLAGS}" -DCMAKE_BUILD_TYPE="${var_build_type}"
-    make ${MAKEOPTS}
+    make ${MAKEOPTS} -j
     return 0
   }
 
@@ -182,7 +182,7 @@
             echo " "
             echo "bzr pull"
             echo "cd build"
-            echo "make"
+            echo "make -j"
             if [ $var_build_lang -eq 1 ] ; then
               echo "make lang"
             fi

=== modified file 'src/ai/ai_help_structs.h'
--- src/ai/ai_help_structs.h	2010-11-01 22:23:29 +0000
+++ src/ai/ai_help_structs.h	2011-11-13 00:10:30 +0000
@@ -268,6 +268,8 @@
 	uint8_t producers;
 	uint8_t consumers;
 	uint8_t preciousness;
+	uint32_t build_prio;
+	uint32_t fight_prio;
 };
 
 #endif

=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc	2011-10-12 21:05:21 +0000
+++ src/ai/defaultai.cc	2011-11-13 00:10:30 +0000
@@ -232,6 +232,30 @@
 		wares.at(i).producers    = 0;
 		wares.at(i).consumers    = 0;
 		wares.at(i).preciousness = tribe->get_ware_descr(i)->preciousness();
+		wares.at(i).build_prio   = 0;
+		wares.at(i).fight_prio   = 0;
+	}
+
+	Ware_Index const nr_workers = tribe->get_nrworkers();
+	for (Ware_Index i = Ware_Index::First(); i < nr_workers; ++i) {
+		// Find all build costs for soldiers
+		// is it a soldier?
+		if (tribe->get_worker_descr(i)->get_worker_type() == Worker_Descr::SOLDIER) {
+			Worker_Descr::Buildcost::const_iterator end = tribe->get_worker_descr(i)->buildcost().end();
+			for
+			  (Worker_Descr::Buildcost::const_iterator it = tribe->get_worker_descr(i)->buildcost().begin();
+			  it != end; ++it)
+			{
+				if (tribe->ware_index(it->first))
+					wares.at(tribe->ware_index(it->first)).fight_prio += it->second; // Add soldier-need priority
+			}
+		}
+	}
+
+	for (Ware_Index i = Ware_Index::First(); i < nr_wares; ++i) {
+		// We have three building sites
+		wares.at(i).consumers    += wares.at(i).build_prio > 0 ?
+			(wares.at(i).build_prio > 30 ? 2 : 1) : 0;
 	}
 
 	// collect information about the different buildings our tribe can construct
@@ -255,6 +279,12 @@
 		bo.current_stats          = 100;
 		bo.unoccupied             = false;
 
+		// Find all build costs
+		Buildcost::const_iterator end = bld.buildcost().end();
+		for (Buildcost::const_iterator it = bld.buildcost().begin(); it != end; ++it) {
+			wares.at(it->first).build_prio += it->second; // Add build-need priority
+		}
+
 		bo.is_basic               = false;
 
 		bo.is_buildable           = bld.is_buildable();
@@ -278,7 +308,8 @@
 				BuildingObserver::PRODUCTIONSITE;
 
 			container_iterate_const(Ware_Types, prod.inputs(), j)
-				bo.inputs.push_back(j.current->first.value());
+				for (uint32_t i = 0; i < j.current->second; i++) // consider input count
+					bo.inputs.push_back(j.current->first.value());
 
 			container_iterate_const
 				(ProductionSite_Descr::Output, prod.output_ware_types(), j)
@@ -318,6 +349,17 @@
 		}
 	}
 
+	for (Ware_Index i = Ware_Index::First(); i < nr_wares; ++i) {
+		if (wares.at(i).build_prio)
+			printf
+			  ("Ware %s ist construction material with priority %d (ToDo: use this information)\n",
+			  tribe->get_ware_descr(i)->name().data(), wares.at(i).build_prio);
+		if (wares.at(i).fight_prio)
+			printf
+			  ("Ware %s ist fight material with priority %d (ToDo: use this information)\n",
+			  tribe->get_ware_descr(i)->name().data(), wares.at(i).fight_prio);
+	}
+
 	total_constructionsites       =    0;
 	next_construction_due         =    0;
 	next_road_due                 = 1000;
@@ -767,7 +809,7 @@
 		spots += spots_avail.at(BUILDCAPS_MEDIUM);
 		spots += spots_avail.at(BUILDCAPS_BIG);
 		if (type == AGGRESSIVE)
-			spots -= militarysites.size() / 20;
+			spots -= militarysites.size() / 30;
 		if (spots < 16)
 			expand_factor *= 2;
 		if ((type == AGGRESSIVE) && spots < 32)
@@ -791,7 +833,7 @@
 	//if (TODO) expand_factor = 0;
 
 	// Defensive AIs also attack sometimes (when they want to expand)
-	if (type == DEFENSIVE && expand_factor > 1)
+	if (expand_factor > 1)
 		if (next_attack_consideration_due <= game().get_gametime())
 			consider_attack(game().get_gametime());
 
@@ -863,8 +905,8 @@
 					continue;
 				if (bo.need_trees) {
 					// Priority of woodcutters depend on the number of near trees
-					prio += bf->trees_nearby * 3;
-					prio /= 3 * (1 + bf->producers_nearby.at(bo.outputs.at(0)));
+					prio += bf->trees_nearby * 4;
+					prio /= 2 * (1 + bf->producers_nearby.at(bo.outputs.at(0)));
 
 					// TODO improve this - it's still useless to place lumberjack huts randomly
 					/*if (prio <= 0) // no, sometimes we need wood without having a forest
@@ -898,6 +940,7 @@
 					// production hint (f.e. associate forester with trunks)
 
 					// Calculate the need for this building
+
 					int16_t inout = wares.at(bo.production_hint).consumers;
 					if
 						(tribe->safe_ware_index("trunk").value()
@@ -907,6 +950,8 @@
 					inout -= wares.at(bo.production_hint).producers;
 					if (inout < 1)
 						inout = 1;
+					if (inout > 3)
+						inout = 2;
 					// the ware they're refreshing
 					Ware_Index wt(static_cast<size_t>(bo.production_hint));
 					container_iterate(std::list<EconomyObserver *>, economies, l) {
@@ -948,12 +993,23 @@
 					prio = recalc_with_border_range(*bf, prio);
 				} else { // "normal" productionsites
 
-					// ToDo: prefer soldier producing things
 					// Ware_Index const soldier_index = tribe().worker_index("soldier");
 
 					if (bo.is_basic && (bo.total_count() == 0))
 						prio += 100; // for very important buildings
 
+
+
+					// we can enhance this building. build more
+					// maybe the enhancement can produce needed ware
+					if (bo.desc->enhancements().size() > 0) {
+						// this code builds more metalworks
+						if (bo.total_count() == 0)
+							prio += 10;
+						if (bo.total_count() == 1)
+							prio += 2;
+					}
+
 					// Check if the produced wares are needed
 					container_iterate(std::list<EconomyObserver *>, economies, l) {
 						// Don't check if the economy has no warehouse.
@@ -968,6 +1024,11 @@
 								 (*l.current)->economy.ware_target_quantity(wt).permanent)
 								prio -= 20;
 
+							// if we have none of them, but they are construction material, build!
+							if (bo.total_count() == 0 && wares.at(bo.outputs.at(m)).build_prio) {
+								prio += 100;
+							}
+
 							// if the economy needs this ware
 							if ((*l.current)->economy.needs_ware(wt)) {
 								prio += 1 + wares.at(bo.outputs.at(m)).preciousness;
@@ -975,16 +1036,6 @@
 									// big bonus, this site might be elemental
 									prio += 3 * wares.at(bo.outputs.at(m)).preciousness;
 							}
-
-							// we can enhance this building. build more
-							// maybe the enhancement can produce needed ware
-							if (bo.desc->enhancements().size() > 0) {
-								// this code builds more metalworks
-								if (bo.total_count() == 0)
-									prio += 2;
-								if (bo.total_count() == 1)
-									prio += 8;
-							}
 						}
 						for (uint32_t m = 0; m < bo.inputs.size(); ++m) {
 							Ware_Index wt(static_cast<size_t>(bo.inputs.at(m)));
@@ -1018,14 +1069,14 @@
 
 					// do not construct more than one building,
 					// if supply line is already broken.
-					if (!check_supply(bo) && bo.total_count() > 0)
+					if (bo.total_count() > 0 && !check_supply(bo))
 						prio -= 12;
 
 				}
 			} else if (bo.type == BuildingObserver::MILITARYSITE) {
 				if (!bf->unowned_land_nearby)
 					continue;
-				prio  = bf->unowned_land_nearby * (1 + type);
+				prio  = bf->unowned_land_nearby * (2 + type);
 				prio -= bf->military_influence * (5 - type);
 				// set to at least 1
 				prio  = prio > 0 ? prio : 1;
@@ -1033,14 +1084,14 @@
 				prio /= 2;
 
 				if (bf->enemy_nearby)
-					prio *= 2;
+					prio = (prio * 5) / 4;
 				else
 					prio -= bf->military_influence * 2;
 
 				if (bf->avoid_military)
 					prio /= 5;
 
-				prio -= militarysites.size() - productionsites.size() / (3 - type);
+				prio -= militarysites.size() - productionsites.size() / (4 - type);
 
 			} else if (bo.type == BuildingObserver::WAREHOUSE) {
 				//  Build one warehouse for ~every 35 productionsites and mines.
@@ -1209,7 +1260,7 @@
 			// multiply with current statistics of all other buildings of this
 			// type to avoid constructing buildings where already some are running
 			// on low resources.
-			prio *= 5 + bo.current_stats;
+			prio *= 40 + bo.current_stats;
 			prio /= 100;
 
 			if (onlymissing) // mines aren't *that* important
@@ -1613,7 +1664,7 @@
 			// destruct the building and it's flag (via flag destruction)
 			// the destruction of the flag avoids that defaultAI will have too many
 			// unused roads - if needed the road will be rebuild directly.
-			game().send_player_bulldoze(site.site->base_flag());
+			game().send_player_dismantle(*(site.site));
 			return true;
 		}
 	}
@@ -1632,7 +1683,7 @@
 		// destruct the building and it's flag (via flag destruction)
 		// the destruction of the flag avoids that defaultAI will have too many
 		// unused roads - if needed the road will be rebuild directly.
-		game().send_player_bulldoze(site.site->base_flag());
+		game().send_player_dismantle(*(site.site));
 		return true;
 	}
 
@@ -1662,7 +1713,7 @@
 				//
 				// Add a bonus if one building of this type is still unoccupied
 				if (((game().get_gametime() % 4) + site.bo->unoccupied) > 2) {
-					game().send_player_bulldoze(site.site->base_flag());
+					game().send_player_dismantle(*(site.site));
 					return true;
 				}
 				else
@@ -1675,7 +1726,7 @@
 
 	// Do not have too many constructionsites
 	uint32_t producers = mines.size() + productionsites.size();
-	if (total_constructionsites >= (5 + (producers / 10)))
+	if (total_constructionsites >= (5 + (producers / 5)))
 		return false;
 
 	// Check whether building is enhanceable and if wares of the enhanced
@@ -1709,7 +1760,7 @@
 						Ware_Index wt(static_cast<size_t>(current_outputs.at(j)));
 						if (site.site->economy().needs_ware(wt))
 							prio -=
-								(2 + wares.at(current_outputs.at(j)).preciousness) / 2;
+								(2 + wares.at(current_outputs.at(j)).preciousness) / 4;
 						continue;
 					}
 				new_outputs.push_back(static_cast<int16_t>(i));
@@ -1718,28 +1769,30 @@
 			// Check if the new wares are needed in economy of the building
 			for (uint32_t i = 0; i < new_outputs.size(); ++i) {
 				Ware_Index wt(static_cast<size_t>(new_outputs.at(i)));
-				if (site.site->economy().needs_ware(wt))
+				if (site.site->economy().needs_ware(wt) || wares.at(wt).fight_prio)
 					prio += 2 + wares.at(new_outputs.at(i)).preciousness;
 			}
 
 			// Compare the number of buildings of current type with the number
 			// of buildings of enhanced type
-			prio += (site.bo->total_count() - en_bo.total_count()) * 2;
+			prio += (2 * site.bo->total_count() - en_bo.total_count());
 
 			// If the new wares are needed
 			if (prio > 0) {
 				prio = calculate_need_for_ps(en_bo, prio);
 				if (prio > maxprio) {
+					// we always enhance if it is the first building of this type
+					if (en_bo.total_count() == 0) prio += 500;
 					maxprio = prio;
 					enbld = (*x.current);
 				}
-			}
+			};
 		}
 	}
 
 	// Enhance if enhanced building is useful
 	// additional: we dont want to lose the old building
-	if (maxprio > 0 && site.bo->total_count() > 1) {
+	if (maxprio > 0 && (maxprio > 500 || site.bo->total_count() > 1)) {
 		game().send_player_enhance_building(*site.site, enbld);
 		changed = true;
 	}
@@ -1778,7 +1831,7 @@
 		// destruct the building and it's flag (via flag destruction)
 		// the destruction of the flag avoids that defaultAI will have too many
 		// unused roads - if needed the road will be rebuild directly.
-		game().send_player_bulldoze(site.site->base_flag());
+		game().send_player_dismantle(*(site.site));
 		return true;
 	}
 
@@ -1880,7 +1933,7 @@
 							(static_cast<int32_t>(ms->maxSoldierCapacity() * 4)
 							 <
 							 bf.military_influence)
-							game().send_player_bulldoze(ms->base_flag());
+							game().send_player_dismantle(*ms);
 
 						// Else consider enhancing the building (if possible)
 						else {
@@ -1995,7 +2048,7 @@
 		WareObserver & wo = wares.at(bo.outputs.at(k));
 		if (wo.consumers > 0) {
 			output_prio += wo.preciousness;
-			output_prio += wo.consumers * 2;
+			output_prio += wo.consumers * 3;
 			output_prio -= wo.producers * 2;
 			if (bo.total_count() == 0)
 				output_prio += 10; // add a big bonus
@@ -2006,12 +2059,18 @@
 			(ceil(output_prio / sqrt(static_cast<double>(bo.outputs.size()))));
 	prio += 2 * output_prio;
 
+	if (!bo.inputs.empty() && !bo.outputs.empty() && bo.current_stats >= 80) {
+		// extra bonus for high productivity
+		// this should compensate various output count of a production step
+		prio += 10;
+	}
+
 	// If building consumes some wares, multiply with current statistics of all
 	// other buildings of this type to avoid constructing buildings where already
 	// some are running on low resources.
 	// Else at least add a part of the stats t the calculation.
 	if (!bo.inputs.empty()) {
-		prio *= bo.current_stats;
+		prio = (prio + 50) * bo.current_stats;
 		prio /= 100;
 	} else
 		prio = ((prio * bo.current_stats) / 100) + (prio / 2);
@@ -2142,8 +2201,14 @@
 			militarysites.back().site = &ref_cast<MilitarySite, Building>(b);
 			militarysites.back().bo = &bo;
 			militarysites.back().checks = bo.desc->get_size();
-		} else if (bo.type == BuildingObserver::WAREHOUSE)
+		} else if (bo.type == BuildingObserver::WAREHOUSE) {
 			++numof_warehouses;
+			Ware_Index const nr_wares = tribe->get_nrwares();
+			for (Ware_Index i = Ware_Index::First(); i < nr_wares; ++i) {
+				// Warehouses are consumers of weapons
+				wares.at(i).consumers    += 2 * wares.at(i).fight_prio;
+			}
+		}
 	}
 }
 
@@ -2207,6 +2272,11 @@
 		} else if (bo.type == BuildingObserver::WAREHOUSE) {
 			assert(numof_warehouses > 0);
 			--numof_warehouses;
+			Ware_Index const nr_wares = tribe->get_nrwares();
+			for (Ware_Index i = Ware_Index::First(); i < nr_wares; ++i) {
+				// Warehouses are consumers of weapons
+				wares.at(i).consumers    -= 2 * wares.at(i).fight_prio;
+			}
 		}
 	}
 	m_buildable_changed = true;
@@ -2220,23 +2290,23 @@
 // TODO: It needs profiling and optimization.
 bool DefaultAI::check_supply(BuildingObserver const & bo)
 {
-	size_t supplied = 0;
 	container_iterate_const(std::vector<int16_t>, bo.inputs, i)
 		container_iterate_const(std::vector<BuildingObserver>, buildings, j)
 			if
 				(j.current->cnt_built &&
-				 std::find
-				 	(j.current->outputs.begin(), j.current->outputs.end(),
-				 	 *i.current)
-				 !=
-				 j.current->outputs.end()
-				 &&
-				 check_supply(*j.current))
+				std::find
+					(j.current->outputs.begin(), j.current->outputs.end(),
+					 *i.current)
+				!=
+				j.current->outputs.end()
+				&&
+				check_supply(*j.current))
 			{
-				++supplied;
 				break;
+			} else {
+				return false;
 			}
-	return supplied == bo.inputs.size();
+	return true;
 }
 
 
@@ -2277,12 +2347,12 @@
 				continue;
 			if (bld->canAttack()) {
 				int32_t ta = player->findAttackSoldiers(bld->base_flag());
-				if (type == NORMAL)
-					ta = ta * 2 / 3;
+				if (type != AGGRESSIVE)
+					ta = (ta * 2) / 3;
 				if (ta < 1)
 					continue;
 
-				int32_t const tc = ta - bld->presentSoldiers().size();
+				int32_t const tc = ta * 2 - bld->presentSoldiers().size() * 3;
 				if (tc > chance) {
 					target = bld;
 					chance = tc;
@@ -2313,7 +2383,7 @@
 
 	// Return if chance to win is too low
 	if (chance < 3) {
-		next_attack_consideration_due = gametime % 7 * 1000 + gametime;
+		next_attack_consideration_due = (2 + gametime % 7) * 1000 + gametime;
 		return false;
 	}
 
@@ -2326,6 +2396,6 @@
 		(target->base_flag(), pn, attackers, retreat);
 
 	//  Do not attack again too soon - returning soldiers must get healed first.
-	next_attack_consideration_due = (gametime % 51 + 10) * 1000 + gametime;
+	next_attack_consideration_due = (gametime % 51 + 20) * 1000 + gametime;
 	return true;
 }

=== modified file 'src/ui_basic/table.cc'
--- src/ui_basic/table.cc	2011-11-06 14:23:36 +0000
+++ src/ui_basic/table.cc	2011-11-13 00:10:30 +0000
@@ -429,6 +429,7 @@
 	return result;
 }
 
+
 /**
  * Scroll to the given position, in pixels.
 */