Strategic AI – Code and notes

In the last AI update I presented an overview of the strategic AI including its pseudocode. I also discussed a bit about the strengths of an ‘utility-based AI’ (read more about different AI systems in Dave Marks article ‘AI Architectures: A Culinary Guide’).

Today we’ll have a look at the actual code for the strategic AI (method planner). This is its first version. Thoughts, suggestions and ideas are welcome! :)

Part 1 – Identifying free resources:

// Identify what free resources the planner can work with
// --> Free meaning not claimed by an objective and no company production
// --> Settlement must also have military capacity (major) or a population above 2.500 civilians (minor)
LogWriter.outputAI(ai.getEmpire(), String.format("<b>Searching for free resources</b>"));
List freeSettlements = new ArrayList();
List freeMajorSettlements = new ArrayList();
List freeMinorSettlements = new ArrayList();
for ( SettlementAI sai:ai.getGovernors() ) {
	if ( !ObjectiveMethods.isSettlementClaimed(ai, sai) ) {
		// We need to make at least two lists; major (military capacity) and minor free settlements
		if ( sai.isMilitaryCapacity() &amp;&amp; sai.getSettlement().getCompanyProduction() == null ) {
			LogWriter.outputAI(ai.getEmpire(), String.format("Free major settlement: %s (id: %d, unique id: %d).", sai.getSettlement().getName(), sai.getSettlement().getId(), sai.getSettlement().getUniqueId()));
		else if ( sai.getSettlement().getCompanyProduction() == null &amp;&amp; SettlementMethods.getTotalPopulation(sai.getSettlement()) &gt; 2500 ) {
			LogWriter.outputAI(ai.getEmpire(), String.format("Free minor settlement: %s (id: %d, unique id: %d).", sai.getSettlement().getName(), sai.getSettlement().getId(), sai.getSettlement().getUniqueId()));

// If there isn't any resources we end method. No new objectives can be started.
// TODO: In crisis we must continue the method as resources can be taken from already existing objectives.
if ( freeSettlements.size() == 0 ) {
	LogWriter.outputAI(ai.getEmpire(), String.format("Planner has no free resorces to work with."));

As can be seen I split all free settlements into two lists. Major settlements are those with capacity to build new armies and has enough population to send population to old (or new) settlements that need it. Minor settlements are those that can be asked to do small stuff, like building a new scout, but they are unable to complete larger objectives.

Part 2 – Creating map with alternatives:

// We have resources. Create map with all alternatives and give them start values depending on requests
LogWriter.outputAI(ai.getEmpire(), String.format("<b>Creating map of alternatives</b>"));
Map map = new HashMap();
// In alphabetical order
map.put(buildScout, 0);
map.put(garrison, 0);
map.put(increasePop, 0);
map.put(newSettlement, 0);
map.put(standingArmy, 0);

Part 3 – Add basic values from requests:

// Loop through all request that needs to considered by the planner and add them to map
for ( Request r:ai.getRequests() ) {
	if ( r.getRequest() == Request.REQUEST_SCOUTS ) {
		map.put(buildScout, map.get(buildScout) + basicValue * r.getPriority() );
	else if ( r.getRequest() == Request.REQUEST_GARRISON ) {
		map.put(garrison, map.get(garrison) + basicValue * r.getPriority() );
	else if ( r.getRequest() == Request.REQUEST_POPULATION ) {
		map.put(increasePop, map.get(increasePop) + basicValue * r.getPriority() );
	else if ( r.getRequest() == Request.REQUEST_NEW_SETTLEMENT ) {
		map.put(newSettlement, map.get(newSettlement) + basicValue * r.getPriority() );
	else if ( r.getRequest() == Request.REQUEST_STANDING_ARMY ) {
		map.put(standingArmy, map.get(standingArmy) + basicValue * r.getPriority() );

The variable basicValue will have to be exchanged for multiple variables as the strategic AI can’t give the same priority to all types of requests. Certain military requests, for example, must be preferred when the empire is in conflict with another empire and so on…

Part 4 – Adding personality values (not implemented yet) and logging final values of all alternatives:

// Add values from Personality to the above alternatives
// --&gt; It depends on the alternative and it's current value if we add personality values to it.
// For example, we only want to consider buildings scouts if there is a request for it.

// TODO: We need to add Personality values to all alternatives
// TODO: We need at modify the values depending on the diplomatic status (peace/war)

// Log alternatives
for ( Map.Entry e:map.entrySet() ) {
	LogWriter.outputAI(ai.getEmpire(), String.format("Alternative: %s, value: %d", e.getKey(), e.getValue()));

Not much to say about the above. This will first be implemented when we got enough actions for the AI to select between. At the momement we’re not that focused on making the AI prioritize the right stuff, we only want to see that everything works. ;)