Strategy AI - Histidine91/Nexerelin GitHub Wiki

(stub, to be fleshed out later maybe)

Overview

Strategy AI tries to make factions take actions (in particular warfare, diplomacy, operative actions, and some construction) in a more 'goal-directed' manner.

Strategy-related details can be seen on the faction intels in the Strat. AI tab, or the intel of the specific action if applicable.

Old-ass dev screenshot:

Mods can define their own concerns and actions in data/config/exerelin/strategicAIconfig.json.

How it works

  • Every so often the AI generates a bunch of 'strategic concerns', which are things like 'we can make a profit by producing more of this commodity' or 'this other faction holds a market that rightfully belongs to us' or 'our war weariness is high'.
  • Each concern has a priority score, with the priority and the broader choice of concerns varying between factions based on their alignments and traits.
  • The faction then picks the top 3 concerns by priority, and for each one generates a bunch of possible 'actions' to address the concern, and tries to do the action with the highest priority score.
  • Typical actions are raids/invasions, diplomacy actions, operative actions, and industry construction.

For modders

(work in progress)

Writing a concern

First make a copy of strategicAIconfig.json in your mod.

Now in the original file we see an example of a concern:

	"concerns":{
		"revanchism":{
			"name":"Revanchism",
			"desc":"We must take back our rightful territory of $market (size $size) from the vile $faction!",
			#"icon":"",	# if blank or unspecified, will default to faction icon (more generally, it uses whatever icon the code says to use)
			"classPath":"exerelin.campaign.ai.concern.RevanchismConcern",
			"tags":["military", "market", "canInvade", "canDeclareWar", "trait_irredentist"],
			"module":MILITARY	# which AI module handles this concern, mostly for organizational purposes
		},

The concerns table contains a list of concerns by their ID. Each concern is a table with name, description, icon, class path, tags and module.

Tags

Some of the tags are quite important, as they control the concern's priority and what actions can be used for them. For instance, military tag gives a higher priority for factions with a high militarist alignment in their Nexerelin faction .json, while trait_irredentist increases priority for factions with that trait in the faction .json.

Module

Each AI has MILITARY, ECONOMIC and DIPLOMATIC modules, primarily to help organize them in the intel GUI. A fourth EXECUTIVE module handles the actions afterwards.

Concern's Java class

Your concern should extend BaseStrategicConcern or one of its existing subclasses. Look at the Nexerelin API for examples.

Some important methods in the strategic concern class that your implementation should override:

  • generate: Called when an AI module wants to create a new concern. Check if we can/should actually generate this concern; if so, set all relevant internal variables for this concern, and return true. Return false if we should not create this concern.
  • isValid: Called at every strategy meeting after the one where the concern was generated. See if we should keep it around or cancel the concern, returning true or false accordingly.
  • update: Called at every strategy meeting after the one where the concern was generated, after isValid. See whether we should change the priority, etc. If need be, we can terminate the concern here as well.
  • isSameAs: Used to check whether this concern is the same as a concern we already have (e.g. if we have two revanchism concerns targeting the same market).
  • createTooltipDesc: What it says on the tin.

Some others that might be of interest:

  • applyPriorityModifiers: If you want custom logic for modifying the priority of a concern, do it here.

Writing an action

Also in strategicAIConfig.json:

	# for an action to be usable for a concern, they need to have at least one matching tag
	# the economy/military/diplomacy tags are also used for prioritization of concerns and actions based on the faction's alignment
	"actions":{
		"invasion":{
			"name":"Invasion",
			"classPath":"exerelin.campaign.ai.action.fleet.InvasionAction",
			"cooldown":45,			# how long after action completion before a new action can be taken for the concern; default 30
			"antiRepetition":30,	# reduction in action priority if recently used elsewhere, decays over time; default 25
			"chance":0.85,			# if the roll fails, skip the action before even checking its priority
			# wartime actions can only be used when at war
			"tags":["military", "market", "canInvade", "unfriendly", "aggressive", "wartime", "useIfSurplusShips"],
		},

Tags work much the same as described for concerns.

Action's java class

Your concern should extend BaseStrategicAction or one of its existing subclasses. Look at the Nexerelin API for examples.

Actions work by creating another Java object that implements StrategicActionDelegate. For instance, InvasionAction creates an invasion fleet event, which reports back to InvasionAction on its current status or outcome, and can be terminated if we decide to abort the action.

  • Your action can be its own delegate if needed, e.g. if you don't have or want to implement a separate class for it.

Some important methods in the strategic action class that your implementation should override:

  • generate: Much like the concern version; check if we can/should actually create and run this action; if so, set all relevant internal variables for this concern, initiate the action and return true. Return false if we should not create this action.
  • canUse: Preliminary checks for whether we should actually use this action for the specified concern.
  • applyPriorityModifiers: If you want custom logic for modifying the priority of an action, do it here.

Action delegates

An intel class that is used as part of a strategic action may implement the StrategicActionDelegate interface. For example, InvasionIntel is used as a delegate for InvasionAction. The interface methods allow the delegate to report its current progress to the action and receive abort commands if needed.

Every action needs a delegate, but in a pinch this can be the action itself (in which case the action should call delegate = this; in its generate() method).