Proposed micro_ai logic sanity check
Moderator: Forum Moderators
- Spannerbag
- Posts: 759
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Proposed micro_ai logic sanity check
Hello,
As a project I've decided to try and create a new micro_ai.
I did have quite a bit of the logic below working using WML but my implementation was messy and inelegant, hence an attempt at a purely lua implementation.
Below is a breakdown of the proposed logic.
All thoughts and comments (including "don't do this") greatly appreciated.
I already have a version of forest animals that allows units to wander randomly (ignores enemies) and if a child (tusklet) is attacked makes all units hostile (i.e. may attack on their turn). Me being me I decided to push the envelope but thought I'd best check here first to ensure the logic will work as I expect.
Here's an outline of the proposed behaviours:
Initial questions:
cheers!
-- Spannerbag
As a project I've decided to try and create a new micro_ai.
I did have quite a bit of the logic below working using WML but my implementation was messy and inelegant, hence an attempt at a purely lua implementation.
Below is a breakdown of the proposed logic.
All thoughts and comments (including "don't do this") greatly appreciated.
I already have a version of forest animals that allows units to wander randomly (ignores enemies) and if a child (tusklet) is attacked makes all units hostile (i.e. may attack on their turn). Me being me I decided to push the envelope but thought I'd best check here first to ensure the logic will work as I expect.
Here's an outline of the proposed behaviours:
Code: Select all
-- Ugly hack of forest animals mai to create wanderers mai (wmai).
--
-- Ignores enemies, if any, when moving units.
-- Units: parents and children.
--
-- Is really 3 micro-ais in one; parents only, children only or both.
-- Each have various behaviours as detailed below.
-- All subtags and filters detailed below are optional.
--
-- [filter_parent]
-- Unit filter for parent units.
-- If absent or no match: no parents.
-- As with forest animals mai parents will unconditionally attack any enemy adjacent to a child.
--
-- [filter_child]
-- Unit filter for child units.
-- If absent or no match: no children.
-- Child units never attack (so can only die if attacked).
--
-- [filter_location]
-- If present restricts units to specified locations.
-- If absent or no match: move over whole map.
--
-- Key "parent_attack" defines % chance (default 0%) of parent attacking if:
-- a) enemy unit adjacent to parent (but not also to child) at turn start, or
-- b) if parent moves adjacent to enemy unit.
--
-- If enemy attacks and hits:
-- Parent: attacked unit no longer controlled by mai but by default RCA ai
-- (next wmai turn only).
-- Child: *all* units controlled by default RCA ai
-- (all subsequent turns, i.e. wmai disabled).
--
-- Controlling ai is mediated by unit variable "maiw_status" with values:
-- i) =0 unit(s) controlled by mai (default/fallback value).
-- ii) =1 unit(s) controlled by RCA ai current/next turn only
-- (parents only, maiw_status cleared at turn end).
-- iii) =2 unit(s) always controlled by default RCA ai
-- (maiw_status not cleared at turn end).
--
-- Events (provisional)
-- 1: new side turn
-- If any parents begin with 1+ enemie(s) adjacent *and* no enemies adjacent to a child
-- then if maiw_status < 1 set maiw_status = 1 for this unit, prior to
-- checking which unit(s) should be controlled by wmai on current turn.
--
-- 2: moveto (enter_hex?)
-- If any unit moves adjacent to an enemy set moves=0.
-- If parent, set maiw_status=1 and attacks_left=1 (might not be necessary?).
--
-- 3: attacker hits
-- If second_unit.side = wmai side set maiw_status accordingly (depending on whether parent or child hit).
--
-- Proposed WML structure:
--
-- [micro_ai]
-- side=...
-- ai_type=wanderers
-- action=add / change / delete
-- [filter_parent]
-- Includes micro_ai side number above
-- Standard unit filter sub-tags and keys
-- [/filter_parent]
-- [filter_child]
-- Includes micro_ai side number above
-- Standard unit filter sub-tags and keys
-- [/filter_child]
-- [filter_location]
-- Standard location filter sub-tags and keys
-- [/filter_location]
-- parent_attack=%
- if a unit does blunder into an enemy, if I simply ensure the unit can attack will control automatically pass to the RCA ai when the micro_ai has finished or do I need to explicitly "deregister" it with the micro ai before the RCA ai can subsequently assume control of this unit?
- Is is preferable to implement the events above in lua or WML?
I would prefer lua as I hope to write the entire micro_ai as a single lua file.
However given the need to trap events on other side turns (attacker hits) I'm not certain this is possible? - Is it OK to invent/create arbitrary tags such as
[filter_parent]
or are only recognised WML tags allowed/recognised?
(If not I could fallback to using the existing type specification logic.) - Not sure whether to check that parents and children are distinct (and issue error if not) or trust the campaign designer.
(I.e. would such a sanity check be useful?) - Need to split moveto/enter_hex logic:
- Set moves=0 immediately (all units)
- Parent only: defer attack enemy until all moves completed then only attack adjacent enemy if no enemies adjacent to children within range (and parent_attack chance → attack).
Is this awkward to implement in lua?
- Would "nuisances" (or something else) be a better name than "wanderers"?
- Is my logic sane/optimal or are there better ways of doing this?
cheers!
-- Spannerbag
- Celtic_Minstrel
- Developer
- Posts: 2371
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: Proposed micro_ai logic sanity check
If your MicroAI does not move the unit nor clear its moves, other candidate actions will be given a chance to control it.Spannerbag wrote: ↑May 10th, 2025, 12:23 pm if a unit does blunder into an enemy, if I simply ensure the unit can attack will control automatically pass to the RCA ai when the micro_ai has finished or do I need to explicitly "deregister" it with the micro ai before the RCA ai can subsequently assume control of this unit?
It doesn't really make sense to have events in a MicroAI at all. Your events 1 and 2 probably don't need to be events – you can just check the current state of the map when the MicroAI runs. As for event 3, it doesn't make sense to me to put that logic in the MicroAI. I'd just define that logic in WML, separate from the MicroAI, and make the event actually disable the MicroAI. And, when it comes to detecting an attack on a parent, all you really need to do is store the parent's hitpoints in a unit variable on every turn. Then when the MicroAI runs, check if the variable is different from its current hitpoints and there is an adjacent enemy. If there is, skip that parent unit entirely.Spannerbag wrote: ↑May 10th, 2025, 12:23 pm Is is preferable to implement the events above in lua or WML?
I would prefer lua as I hope to write the entire micro_ai as a single lua file.
However given the need to trap events on other side turns (attacker hits) I'm not certain this is possible?
It's perfectly fine to make up tags. You can define the format of theSpannerbag wrote: ↑May 10th, 2025, 12:23 pmIs it OK to invent/create arbitrary tags such as[filter_parent]
or are only recognised WML tags allowed/recognised?
[micro_ai]
tag contents however you want.I'm not sure if that would be useful… I think I'd skip it for now.Spannerbag wrote: ↑May 10th, 2025, 12:23 pmNot sure whether to check that parents and children are distinct (and issue error if not) or trust the campaign designer.
(I.e. would such a sanity check be useful?)
Setting moves=0 seems wrong, pretty sure you said you want other candidate actions to take control in this case? The "defer attack" thing makes it even more clear that this probably shouldn't be an event, as you only care about the final state once it is the MicroAI's turn.Spannerbag wrote: ↑May 10th, 2025, 12:23 pm Need to split moveto/enter_hex logic:
- Set moves=0 immediately (all units)
- Parent only: defer attack enemy until all moves completed then only attack adjacent enemy if no enemies adjacent to children within range (and parent_attack chance → attack).
Is this awkward to implement in lua?
- Spannerbag
- Posts: 759
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Proposed micro_ai logic sanity check
Hi Celtic_Minstrel,
Thanks for the as ever useful and enlightening reply.
A couple of follow-ups (sorry, to keep the post size within tl;dr tolerances I omitted a few background items that I perhaps should've included
).
So the unit will have moved (and have moves=0 I assume) after encountering an enemy.
This is fine as I want to replicate normal behaviour so entering an enemy ZoC then stopping is fine (unless the designer chooses skirmishers... hadn't thought of that
).
Thus the unit can (unless a skirmisher) only attack adjacent enemies (and not move) which is also fine.
So what I should have asked is Once the wmai has finished, can the RCA mount an attack with these units (regardless of moves remaining)?
Also, for skirmishers with moves remaining, can the RCA also move these again?
Regarding a second move my guess is no, because "move" candidate action has already been used up by the wmai.


Many thanks for your reply it's given me a far better idea of what kind of logic suits/works inside a micro_ai and what doesn't (i.e. events).
I think before coding in earnest I'll rethink some wmai behaviours and probably simplify.
Will also try and ditch events entirely or, if I can't, embed the lua inside inside a WML wrapper so the micro_ai is still all contained inside a single file.
Thanks again, much appreciated!
Cheers!
-- Spannerbag
Thanks for the as ever useful and enlightening reply.
A couple of follow-ups (sorry, to keep the post size within tl;dr tolerances I omitted a few background items that I perhaps should've included

Ah, that might be a problem. The wmai side will have allies and enemies as usual and will move its units randomly and may run into an enemy (on the wmai turn, obviously) so, I assume, that will set moves=0 for the unit anyway (explicitly setting moves=0 is just paranoia plus a remnant of earlier versions).Celtic_Minstrel wrote: ↑May 10th, 2025, 3:07 pmIf your MicroAI does not move the unit nor clear its moves, other candidate actions will be given a chance to control it.
So the unit will have moved (and have moves=0 I assume) after encountering an enemy.
This is fine as I want to replicate normal behaviour so entering an enemy ZoC then stopping is fine (unless the designer chooses skirmishers... hadn't thought of that

Thus the unit can (unless a skirmisher) only attack adjacent enemies (and not move) which is also fine.
So what I should have asked is Once the wmai has finished, can the RCA mount an attack with these units (regardless of moves remaining)?
Also, for skirmishers with moves remaining, can the RCA also move these again?
Regarding a second move my guess is no, because "move" candidate action has already been used up by the wmai.
Excellent suggestions: I'll rethink my logic - and maybe simplify.Celtic_Minstrel wrote: ↑May 10th, 2025, 3:07 pm [It doesn't really make sense to have events in a MicroAI at all. Your events 1 and 2 probably don't need to be events – you can just check the current state of the map when the MicroAI runs. As for event 3, it doesn't make sense to me to put that logic in the MicroAI. I'd just define that logic in WML, separate from the MicroAI, and make the event actually disable the MicroAI. And, when it comes to detecting an attack on a parent, all you really need to do is store the parent's hitpoints in a unit variable on every turn. Then when the MicroAI runs, check if the variable is different from its current hitpoints and there is an adjacent enemy. If there is, skip that parent unit entirely.
Great - hoped that'd be the case.Celtic_Minstrel wrote: ↑May 10th, 2025, 3:07 pmIt's perfectly fine to make up tags. You can define the format of theSpannerbag wrote: ↑May 10th, 2025, 12:23 pmIs it OK to invent/create arbitrary tags such as[filter_parent]
or are only recognised WML tags allowed/recognised?[micro_ai]
tag contents however you want.

Thanks. Less work for me.Celtic_Minstrel wrote: ↑May 10th, 2025, 3:07 pmI'm not sure if that would be useful… I think I'd skip it for now.Spannerbag wrote: ↑May 10th, 2025, 12:23 pmNot sure whether to check that parents and children are distinct (and issue error if not) or trust the campaign designer.
(I.e. would such a sanity check be useful?)

Yeah, need to rethink this.Celtic_Minstrel wrote: ↑May 10th, 2025, 3:07 pmSetting moves=0 seems wrong, pretty sure you said you want other candidate actions to take control in this case? The "defer attack" thing makes it even more clear that this probably shouldn't be an event, as you only care about the final state once it is the MicroAI's turn.
Many thanks for your reply it's given me a far better idea of what kind of logic suits/works inside a micro_ai and what doesn't (i.e. events).
I think before coding in earnest I'll rethink some wmai behaviours and probably simplify.
Will also try and ditch events entirely or, if I can't, embed the lua inside inside a WML wrapper so the micro_ai is still all contained inside a single file.
Thanks again, much appreciated!

Cheers!
-- Spannerbag
- Celtic_Minstrel
- Developer
- Posts: 2371
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: Proposed micro_ai logic sanity check
I'm afraid you're wrong – that's not what you should have asked. The problem with your question is that "Once the wmai is finished" appears to demonstrate an assumption of linearity: "The game will run my MicroAI, and then it will run the RCA." That's not even close to the truth. It's entirely possible that actions from your MicroAI and actions from the default AI will end up interleaved. The MicroAI simply inserts additional candidate actions into the RCA, which the RCA will then evaluate on each loop just like the default candidate actions.Spannerbag wrote: ↑May 11th, 2025, 10:22 amz
Thus the unit can (unless a skirmisher) only attack adjacent enemies (and not move) which is also fine.
So what I should have asked is Once the wmai has finished, can the RCA mount an attack with these units (regardless of moves remaining)?
Also, for skirmishers with moves remaining, can the RCA also move these again?
Regarding a second move my guess is no, because "move" candidate action has already been used up by the wmai.
I will nevertheless try to answer the question you wanted to ask.
If a unit has moves remaining, the default move candidate action in the RCA will move it, although "move it" can include the possibility of deciding that there are no good moves and setting its moves to 0.
If a unit has attacks remaining and is in a position to attack, the default attack candidate action in the RCA will attack with it if it deems it to be worthwhile. I believe it will set the unit's attacks to 0 if it decides it's not worth attacking even though it can.
The candidate actions of your custom MicroAI would normally be set up so that the RCA prefers to choose them instead of the default move or attack candidate action for the units you want the MicroAI to control. The evaluation phase of the custom candidate action needs to inform the RCA when it decides there is nothing for the MicroAI to do.
Since the MicroAI will therefore run before the default move and attack candidate actions (but not necessarily before the entire default AI), assuming everything is set up correctly, that means the MicroAI gets the first chance to decide what those units will do. It also gets the opportunity to prevent further candidate actions from controlling the unit, by setting its moves and/or attacks to 0.
In short: if the MicroAI leaves a unit with unused moves or attacks, then the default CAs can then make it move and attack however they want. If on the other hand it leaves a unit with 0 moves or attacks, then the default CAs cannot make the unit do anything else.
- Spannerbag
- Posts: 759
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Proposed micro_ai logic sanity check
Wow, a really helpful reply that explains how unit moves/attacks were set to zero even when I was sure the micro_ai hadn't done anything!
My understanding of what's going on is now much clearer - many thanks!
I've still got a fair amount of research to do, e.g. not currently sure how to implement:
E.g. at first glance my understanding of the code below is:
If units subject to this micro_ai with moves>0 exist, movement will be considered to have taken place; return ca_score as specified in custom micro_ai otherwise return 0 (thereby reducing this CA's priority below the default CA score?).
Again, thanks for the detailed reply, very much appreciated.
Cheers!
-- Spannerbag
My understanding of what's going on is now much clearer - many thanks!

I've still got a fair amount of research to do, e.g. not currently sure how to implement:
However with a better grasp of the fundamentals I'm sure when I look for examples I'll be able to work out how to do that.Celtic_Minstrel wrote: ↑May 11th, 2025, 11:01 pm ...The evaluation phase of the custom candidate action needs to inform the RCA when it decides there is nothing for the MicroAI to do...
E.g. at first glance my understanding of the code below is:
If units subject to this micro_ai with moves>0 exist, movement will be considered to have taken place; return ca_score as specified in custom micro_ai otherwise return 0 (thereby reducing this CA's priority below the default CA score?).
Code: Select all
function ca_forest_animals_move:evaluation(cfg)
if get_forest_animals(cfg)[1] then return cfg.ca_score end
return 0
end
Cheers!
-- Spannerbag
- Celtic_Minstrel
- Developer
- Posts: 2371
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: Proposed micro_ai logic sanity check
It informs the RCA by returning a score of 0. This also allows it to be called again if, after a move from the default move CA, there is something new that the MicroAI can do. If it returns a non-zero score but then doesn't do anything, the RCA won't call it again until the next turn.Spannerbag wrote: ↑May 12th, 2025, 9:49 am I've still got a fair amount of research to do, e.g. not currently sure how to implement:[/code]Celtic_Minstrel wrote: ↑May 11th, 2025, 11:01 pm ...The evaluation phase of the custom candidate action needs to inform the RCA when it decides there is nothing for the MicroAI to do...
Pretty much, except that If units subject to this micro_ai with moves>0 exist can be whatever condition you want. If you want the units to only ever be controlled by the MicroAI, then that condition is fine. If you want the units to be controlled by the default AI in certain circumstances, your condition would need to be such that it returns 0 in that circumstance.Spannerbag wrote: ↑May 12th, 2025, 9:49 am However with a better grasp of the fundamentals I'm sure when I look for examples I'll be able to work out how to do that.
E.g. at first glance my understanding of the code below is:
If units subject to this micro_ai with moves>0 exist, movement will be considered to have taken place; return ca_score as specified in custom micro_ai otherwise return 0 (thereby reducing this CA's priority below the default CA score?).
This works, assumingSpannerbag wrote: ↑May 12th, 2025, 9:49 amCode: Select all
function ca_forest_animals_move:evaluation(cfg) if get_forest_animals(cfg)[1] then return cfg.ca_score end return 0 end
get_forest_animals
filters out units that cannot move.- Spannerbag
- Posts: 759
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Proposed micro_ai logic sanity check
Hi Celtic_Minstrel,
As always, many thanks for your help.
Not had chance to look at the code any further (rl... tsk, always getting in the way), hopefully I won't have fogotten it all by the time I revisit this!
Am mulling over behavioural changes to my custom micro_ai to make implementation easier.
Your time and patience are very much appreciated; you've made me slightly less clueless (quite an achievement).
Cheers!
-- Spannerbag
As always, many thanks for your help.
Not had chance to look at the code any further (rl... tsk, always getting in the way), hopefully I won't have fogotten it all by the time I revisit this!
Am mulling over behavioural changes to my custom micro_ai to make implementation easier.
Your time and patience are very much appreciated; you've made me slightly less clueless (quite an achievement).

Cheers!
-- Spannerbag
- Spannerbag
- Posts: 759
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Proposed micro_ai logic sanity check - syntax query
Hi,
Bit puzzled about how lua actually gets to execute a chunk of code?
Assigning ca values seems to be:
Looking at one of these:
I understand that
I notice that the lua file name always matches a table creation, here (within the file
As I'm writing a custom mai I'd like to have everything in a single file, so will remove
However, do I need to put a function call/table definition/something else in place of
E.g. would this simply suffice?
structure?
I haven't found anything helpful in online references so suspect I'm asking the wrong question but could spend a lot of time getting nowhere fast hence this post.
I'd also appreciate an explanation of how
knows what to do with
Any help greatly appreciated.
Cheers!
-- Spannerbag
Bit puzzled about how lua actually gets to execute a chunk of code?
Assigning ca values seems to be:
Code: Select all
function wesnoth.micro_ais.forest_animals(cfg)
local optional_keys = { rabbit_type = 'string', rabbit_number = 'integer',
rabbit_enemy_distance = 'integer', rabbit_hole_img = 'string', tusker_type = 'string',
tusklet_type = 'string', deer_type = 'string', filter_location = 'tag'
}
local score = cfg.ca_score or 300000
local CA_parms = {
ai_id = 'mai_forest_animals',
{ ca_id = "new_rabbit", location = 'ca_forest_animals_new_rabbit.lua', score = score },
{ ca_id = "tusker_attack", location = 'ca_forest_animals_tusker_attack.lua', score = score - 1 },
{ ca_id = "move", location = 'ca_forest_animals_move.lua', score = score - 2 },
{ ca_id = "tusklet_move", location = 'ca_forest_animals_tusklet_move.lua', score = score - 3 }
}
...
{ ca_id = "move", location = 'ca_forest_animals_move.lua', score = score - 2 },
I understand that
location
defines the filepath to the relevant code however I don't seem able to find where this code is actually called/executed?I notice that the lua file name always matches a table creation, here (within the file
ca_forest_animals_move.lua
) in this case:local ca_forest_animals_move = {}
As I'm writing a custom mai I'd like to have everything in a single file, so will remove
location =
and simply embed the same code within the mai definition file that calls the various ca assignments.However, do I need to put a function call/table definition/something else in place of
location
?E.g. would this simply suffice?
- Add line
local ca_forest_animals_move = {}
before the code below. - Replace
location
{ ca_id = "move", ca_forest_animals_move, score = score - 2 },
Code: Select all
local CA_parms = {
ai_id = 'mai_forest_animals',
{ ca_id = "new_rabbit", location = 'ca_forest_animals_new_rabbit.lua', score = score },
{ ca_id = "tusker_attack", location = 'ca_forest_animals_tusker_attack.lua', score = score - 1 },
{ ca_id = "move", location = 'ca_forest_animals_move.lua', score = score - 2 },
{ ca_id = "tusklet_move", location = 'ca_forest_animals_tusklet_move.lua', score = score - 3 }
}
...
I haven't found anything helpful in online references so suspect I'm asking the wrong question but could spend a lot of time getting nowhere fast hence this post.
I'd also appreciate an explanation of how
{ ca_id = "move", location = 'ca_forest_animals_move.lua', score = score - 2 },
knows what to do with
ca_forest_animals_move.lua
- or is the code executed elsewhere and I'd missed it?Any help greatly appreciated.
Cheers!
-- Spannerbag
Re: Proposed micro_ai logic sanity check
I don't think that's really possible?Spannerbag wrote: ↑May 18th, 2025, 9:33 am As I'm writing a custom mai I'd like to have everything in a single file, so will removelocation =
and simply embed the same code within the mai definition file that calls the various ca assignments.
location
is a required field, and it is defined in the Micro AIs documentation as the "Path to the Lua file that defines the candidate action". So I think it has to be there and it has to be a file.Keep in mind, if you really don't like the way Micro AIs are structured, you don't actually have to use them. There are other ways to customize the AI's behavior - i.e., you could use the
[ai]
tag instead of the [micro_ai]
tag. Note that you would still need to have a separate .lua file for each CA. But if you have only one CA, you would just need that one .lua file.- Spannerbag
- Posts: 759
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Proposed micro_ai logic sanity check
- Celtic_Minstrel
- Developer
- Posts: 2371
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: Proposed micro_ai logic sanity check - syntax query
It's called from inside the engine, so you won't find "the place it's called" anywhere in WML or Lua.Spannerbag wrote: ↑May 18th, 2025, 9:33 am I understand thatlocation
defines the filepath to the relevant code however I don't seem able to find where this code is actually called/executed?
I notice that the lua file name always matches a table creation, here (within the fileca_forest_animals_move.lua
) in this case:
Hmm. There might actually be a way to make it work if there's only one CA – you might be able to put the MicroAI definition in the CA file and have the location refer to the same file. I don't know if it would work, but if you really want to keep things in one file, it might be worth a try.Spannerbag wrote: ↑May 18th, 2025, 9:33 am As I'm writing a custom mai I'd like to have everything in a single file, so will removelocation =
and simply embed the same code within the mai definition file that calls the various ca assignments.
However, do I need to put a function call/table definition/something else in place oflocation
?
- Spannerbag
- Posts: 759
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Proposed micro_ai logic sanity check
Gnombat kindly pointed out this link:
Unfortunately as gnombat said, it still expects a
Otherwise they'll have to clutter up
I've reworked the logic a little to be more amenable to (my present understanding of) wmai implementation.
I'd hoped to begin incremental test/refine this weekend but stuff happens...
Re.:
This wouldn't be an issue because only the first successful attack needs to be detected so I can simply compare current hitpoints vs
As always, thanks for your time and patience.
Cheers!
-- Spannerbag
This page has hardly changed for years but I'm hoping it's still valid as it further dispelled my ignorance.
Unfortunately as gnombat said, it still expects a
location
and I think I'll need 3 CAs (stolen/adapted from forest animals mai):- Move units randomly ignoring enemies.
- "Protective parents" attack (defend child).
- Child move (follow parent).
lua
files will be created, preferably in a wmai
sub-folder of lua
, assuming the add-on server can create sub-folders.Otherwise they'll have to clutter up
lua
.I've reworked the logic a little to be more amenable to (my present understanding of) wmai implementation.
I'd hoped to begin incremental test/refine this weekend but stuff happens...

Re.:
I'm assuming that - if implemented as part of a mai - the code to record hitpoints would execute every mai turn start?Celtic_Minstrel wrote: ↑May 10th, 2025, 3:07 pm ...when it comes to detecting an attack on a parent, all you really need to do is store the parent's hitpoints in a unit variable on every turn. Then when the MicroAI runs, check if the variable is different from its current hitpoints and there is an adjacent enemy. If there is, skip that parent unit entirely.
This wouldn't be an issue because only the first successful attack needs to be detected so I can simply compare current hitpoints vs
max_hitpoints
*.
* Of course this assumes that units are added with full hitpoints.
Given that the mai doesn't have to be running when the scenario starts and that units can be added any time (and not necessarily on full hitpoints) I'm not clear if it is possible to "capture starting hitpoints" whenever a unit is added to the mai in the general case.
Sanity check: is my understanding correct or - as is highly likely - have I missed or misunderstood something again?Given that the mai doesn't have to be running when the scenario starts and that units can be added any time (and not necessarily on full hitpoints) I'm not clear if it is possible to "capture starting hitpoints" whenever a unit is added to the mai in the general case.
As always, thanks for your time and patience.
Cheers!
-- Spannerbag
- Celtic_Minstrel
- Developer
- Posts: 2371
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: Proposed micro_ai logic sanity check
There is a way to make CAs without a
You can find more info on this alternate (and older) method here. That page says you need an
If you want the MicroAI to only control undamaged parent units, then just add
location=
parameter. You would instead provide evaluation=
and execution=
parameters, which contain the content of the respective functions in the CA file. You would need to also add a line at the top to convert ...
to the proper arguments (just take the actual argument list of the function declared in the file, without parentheses, put local
in front, and add = ...
to the end).You can find more info on this alternate (and older) method here. That page says you need an
[engine]
tag in the AI definition to use the alternate form, but I'm not sure if that's actually true. Personally, I don't really recommend trying to consolidate the whole thing into one file like this, but it is at least theoretically possible to achieve.What? The add-on server doesn't create folders. It just takes something like a zip file and stores it.Spannerbag wrote: ↑May 24th, 2025, 12:07 pm So multiplelua
files will be created, preferably in awmai
sub-folder oflua
, assuming the add-on server can create sub-folders.
Otherwise they'll have to clutter uplua
.
I don't quite get what you're saying.Spannerbag wrote: ↑May 24th, 2025, 12:07 pmI'm assuming that - if implemented as part of a mai - the code to record hitpoints would execute every mai turn start?Celtic_Minstrel wrote: ↑May 10th, 2025, 3:07 pm ...when it comes to detecting an attack on a parent, all you really need to do is store the parent's hitpoints in a unit variable on every turn. Then when the MicroAI runs, check if the variable is different from its current hitpoints and there is an adjacent enemy. If there is, skip that parent unit entirely.
This wouldn't be an issue because only the first successful attack needs to be detected so I can simply compare current hitpoints vsmax_hitpoints
*.* Of course this assumes that units are added with full hitpoints.Sanity check: is my understanding correct or - as is highly likely - have I missed or misunderstood something again?
Given that the mai doesn't have to be running when the scenario starts and that units can be added any time (and not necessarily on full hitpoints) I'm not clear if it is possible to "capture starting hitpoints" whenever a unit is added to the mai in the general case.
If you want the MicroAI to only control undamaged parent units, then just add
formula="hitpoints < max_hitpoints"
to the filter for parent units. But I thought you were asking for something that directly reacts to being damaged. That would probably require some events.
Last edited by Celtic_Minstrel on May 27th, 2025, 5:54 am, edited 1 time in total.
Re: Proposed micro_ai logic sanity check
The usual convention appears to be to put these in a top-level folder namedSpannerbag wrote: ↑May 24th, 2025, 12:07 pm So multiplelua
files will be created, preferably in awmai
sub-folder oflua
, assuming the add-on server can create sub-folders.
Otherwise they'll have to clutter uplua
.
ai
, not in the lua
folder at all.Eastern_Invasion/ai
Son_Of_The_Black_Eye/ai
The_Rise_Of_Wesnoth/ai
Two_Brothers/ai
(I believe this is just a convention, though - I don't think it is required or enforced.)
- Spannerbag
- Posts: 759
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Proposed micro_ai logic sanity check
Celtic_Minstrel wrote: ↑May 25th, 2025, 9:31 pm There is a way to make CAs without alocation=
parameter...
Personally, I don't really recommend trying to consolidate the whole thing into one file like this, but it is at least theoretically possible to achieve.
Thanks for the insights, useful to know.
On reflection will use standard (multiple file) implementation with all lua files in
ai
folder.As I learn more my ideas keeps changing - hopefully this is a good thing and will lead somewhere productive...
Well that's saved me an experiment, thanks!

Heh, I'm not surprised since I'd changed my idea of how the mai would work but didn't mention the changes here.Celtic_Minstrel wrote: ↑May 25th, 2025, 9:31 pmI don't quite get what you're saying.Spannerbag wrote: ↑May 24th, 2025, 12:07 pmI'm assuming that - if implemented as part of a mai - the code to record hitpoints would execute every mai turn start?Celtic_Minstrel wrote: ↑May 10th, 2025, 3:07 pm ...when it comes to detecting an attack on a parent, all you really need to do is store the parent's hitpoints in a unit variable on every turn. Then when the MicroAI runs, check if the variable is different from its current hitpoints and there is an adjacent enemy. If there is, skip that parent unit entirely.
This wouldn't be an issue because only the first successful attack needs to be detected so I can simply compare current hitpoints vsmax_hitpoints
*.* Of course this assumes that units are added with full hitpoints.Sanity check: is my understanding correct or - as is highly likely - have I missed or misunderstood something again?
Given that the mai doesn't have to be running when the scenario starts and that units can be added any time (and not necessarily on full hitpoints) I'm not clear if it is possible to "capture starting hitpoints" whenever a unit is added to the mai in the general case.

But don't worry about my witterings about starting hitpoints - my ideas have moved on.
FWIW my concern was over how to detect the first successful attack upon a unit (i.e. has taken damage) via hitpoint reduction given that in the general case it's possible to put an already wounded unit under mai control.
In such cases initial hitpoints seen by the mai (when compared to
Further, (possibly wounded) units can be added to the mai at any time so "was attacked?" would need to be tested every time the mai executes.
I'm probably trying to do this the wrong way or using the wrong tools.In such cases initial hitpoints seen by the mai (when compared to
[max_hitpoints]
) would indicate the unit had been previously attacked, which may or may not be so.Further, (possibly wounded) units can be added to the mai at any time so "was attacked?" would need to be tested every time the mai executes.
More of this later in this post.
Not reached a firm conclusion yet but on reflection I think as little as possible needs to be put into the mai but allow scenario designers great flexibility in how to deploy or change the mai's behaviour via WML?
- Using a dynamic unit selection/inclusion method (e.g.
[filter]
) rather than a static one (e.g.type=
) would hopefully allow units to be selectively placed under mai control every turn by using unit specific markers such asrole
or a unit variable.
(This is actually the same underlying logic as my very first attempt which involved tortuous WML because that version of my custom mai wasn't suited to doing this.) - Use a few boolean keys to switch some inbuilt behaviours on or off.
(Here I'm thinking primarily of the "protective parents*" behaviour.) - Other behaviours would be always enforced because removing them disables the behaviours that define this mai, leaving the designer with an mai that doesn't do anything special.
(These include always ignore enemies when moving and children follow parents*.) - Presently dithering about
[filter_location]
and/or[avoid]
for the mai.
Other considerations such as whether or not to allow parents to attack adjacent enemies on their turn can be implemented in WML.
E.g. if the designer wants to allow, say, a difficulty dependent chance that parents might attack an adjacent enemy on their turn, this could be done with WML.
I haven't throught this through yet but something like:
- For units starting their turn adjacent to an enemy unit, adding a
[filter_adjacent]→is_enemy→count=0
clause to the parent filter should exclude those units from mai control, allowing the RCA ai to determine what to do with these units depending on aggression/caution etc. and/or specific WML?
(Moves could also be set to 0 to force attacks vs adjacent units only if desired.) - For units that blunder into enemies,
enter_hex
could trap that so that hopefully simply settingattacks_left=1
(if needed) would suffice.
Current thinking: mai always sets moves and attacks =0 (ai.stopunit_all
) before finishing with a unit regardless of whether it moves or not and this mai never attacks offensively - but this might change...
Also please see end of post for question regarding this point.
attack
/attacker hits
(as previously advised) would do the job.As I say my ideas and thoughts are still developing as time and energy permit.
Question:
It would he helpful to know for certain about the relative timings of events and mai execution, as mentioned previously in this post, i.e.:
for all units, do all (RCA and/or micro) ai processes concerned with movement always complete before movement related events (WML) can fire?
Cheers!
-- Spannerbag