Exercises in Formula and Lua AI and AI-demos add-on feedback

Discussion of all aspects of the game engine, including development of new and existing features.

Moderator: Forum Moderators

Post Reply
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Exercises in Formula and Lua AI and AI-demos add-on feedback

Post by mattsc »

Hello All,

EDIT: See Practical Guide to Modifying AI Behavior for documentation on the issues discussed in this thread.

In a comment I made in the WML Workshop recently, it was suggested that I replace some WML unit events I am using in my latest campaign with FormulaAI code instead. I've been trying to get going on this by checking out the formula test scenario and the wiki pages (FormulaAI, AI Module, etc.). I think I understand the general principles now, but I am still somewhat in the dark on the details and am stuck at the very beginning. Probably just a stupid mistake of some sort, so hopefully it'll be easy to find/fix.

So here's a very simple test scenario I am using right now, in which I am trying to control the moves of Side 2 with FormulaAI

Code: Select all

#textdomain wesnoth-Grnk

[scenario]
    id=02_test
    name=_"02_test"
    next_scenario=null

    map_data="{~add-ons/AItest/maps/01_test.map}"

    {DEFAULT_SCHEDULE}
    turns=-1
    victory_when_enemies_defeated=no

    [side]
        side=1
        controller=human
        id=Grnk
        name=_"Grnk"
        gender=male
        unrenamable=yes
        type=Goblin Spearman

        team_name=Grnk
        user_team_name=_"Grnk"
        persistent=yes
        save_id=Grnk

        [modifications]
            {TRAIT_QUICK}
        [/modifications]

        gold=200
    [/side]

    [side]
        side=2
        #controller=ai
        #ai_algorithm=formula_ai
        id=Bad Orc
        type=Orcish Warlord
        team_name=Orcs
        user_team_name=_"Orcs"
        recruit=Orcish Grunt,Orcish Archer,Wolf Rider

        [unit]
            x,y=16,8
            type="Wolf Rider"
            generate_name=yes
            [ai]
                formula="move(me.loc, loc(me.loc.x, me.loc.y + 1))"
            [/ai]
        [/unit]

        [ai]
            version=10703
            [stage]
                engine=fai
                name=unit_formulas
            [/stage]
            [stage]
                engine=fai
                name=side_formulas
                move="recruit('Wolf Rider')"
            [/stage]

            [stage]
                engine=fai
                name=rca_formulas
                [register_candidate_move]
                    name=move_south
                    type=movement
                    action="move(me.loc, loc(me.loc.x+2, me.loc.y + 2))"
                    evaluation="1000"
                [/register_candidate_move]

 #               [register_candidate_move]
 #                   name=recruit
 #                   type=movement
 #                   action="recruit('Orcish Grunt')"
 #                   evaluation=2000
 #               [/register_candidate_move]
            [/stage]
        [/ai]

        gold=200
    [/side]
[/scenario]
What works: moving the Wolf Rider at 16,8 south one hex per turn with unit_formulas; and recruiting a keep of Wolf Riders on the first turn with side_formulas (although it tells me "error ai/engine/fai: ERROR #3006 while executing 'recruit' formula function", but I can figure out what that means later).

What does not work: getting the rca_formulas stage to do anything, or even be recognized. I get this error message:
"error ai/engine/fai: unknown type of formula_ai stage: [rca_formulas]"
[I'm not sure either if the content of [register_candidate_move] makes sense, but I think it doesn't even get to the point as it doesn't seem to recognize the rca_formulas stage. Right now, I don't care much what it does, I just want it to do something :P ]

I have tried to model this after the formula test scenario, but somehow I just can't get it to work. Could anybody give me a pointer? Thanks a lot! (And please also tell me if this is not the correct forum, in which case I apologize.)

I don't know if this is importanrt: I am doing this in BfW 1.9.9 (have also tried 1.9.5) on a MacBook Pro, OS 10.6.8 (Snow Leopard).

EDIT: Changed the title since this is now going beyond the original newbie question I had.
Last edited by mattsc on November 25th, 2011, 9:04 pm, edited 4 times in total.
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

FormulaAI: Swamp Lurker moves

Post by mattsc »

I still haven't figured out what is going on with that first question I had, but it doesn't really matter for now since I only need the unit_formulas for what I was trying to do anyway.

Sooo... The reason why I looked into this: in my most recent campaign I have units called Swamp Lurkers. They have the 'swamp_submerge' ability, which is the same as the 'submerge' ability that some units (skeletons etc.) have in deep water, just in swamps. Besides that, they are really dumb monsters of some sort (nobody's ever seen anything but their eyes!). Their moves look like this:
- They can move across most terrains, but they only come to a halt on swamp (incl. quagmire) terrain
- If there are enemy units next to a swamp hex they can reach, they go to and attack the enemy with the smallest number of HP - one by one, unconditionally and without strategy (did I mention that they are rather stupid?)
- if no enemy is within attack range, they randomly go to one of the swamp hexes they can reach (again, no strategy)

Since I didn't know any better until recently, I programmed all of that using WML events. If anybody is interested, here's how I do that:
Spoiler:
Now, in a thread about something different, I commented on having these units and Sapient suggested to look at FormulaAI for this instead. So that's what I've done over the last few days, and I have to say that I like it. I programmed the same behavior using FAI instead (well, it's almost the same, there are subtle differences, but I actually like it a bit better this way):

EDIT: This is the original version of the code. A newer version might be available here.
Spoiler:
I am posting this for 2 reasons:
1. Somebody might actually be interested in it. Wishful thinking, I know ...
2. If there are any FAI gurus out there who don't mind having a look at this, please let me know if there's something stupid in there; something that could be done much more easily or that I really shouldn't be doing in this way for some reason I don't understand (yet). Thanks!
I hope this doesn't count as spam. :)

I have to say, I like FormulaAI! It's obviously much more powerful than WML for these kind of things (and the code's much shorter, too!). If anybody wants me to post the entire scenario (it's really very simple) and the Lurker unit files to experiment with it yourself a little, let me know.

PS: Thanks for pointing me in this direction, Sapient! That was a fun little problem.

PS2: The one thing I haven't done is make this work for both 'swamp_water' and 'quagmire' terrain as it does in WML, but since that's a rather trivial addition, I didn't really care. For now ...
Last edited by mattsc on November 27th, 2011, 11:29 pm, edited 1 time in total.
User avatar
Crab
Inactive Developer
Posts: 200
Joined: March 18th, 2009, 9:42 pm

Re: FormulaAI: Swamp Lurker moves

Post by Crab »

You can use loc(x,y) to construct a location instead of getting it from a map filtering on x and y, that should be faster.
User avatar
Sapient
Inactive Developer
Posts: 4453
Joined: November 26th, 2005, 7:41 am
Contact:

Re: FormulaAI: Swamp Lurker moves

Post by Sapient »

But how would you access the terrain type id from loc(xx,yy) ?

Or did you mean to filter(map.terrain, loc=loc(xx,yy)) ?

As a side note, it does seem a bit odd to me that (built-in) map.terrain is not a map (data-type). Otherwise we could just write map.terrain[loc(xx,yy)].id
http://www.wesnoth.org/wiki/User:Sapient... "Looks like your skills saved us again. Uh, well at least, they saved Soarin's apple pie."
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: FormulaAI: Swamp Lurker moves

Post by mattsc »

Thanks, Crab. I've been staring at my code and end up with the same question as Sapient. I am going through the map because I need the terrain type and I don't know a different way how to do that. If there's a better method, I'd be interested to hear about it.
Sapient wrote:Or did you mean to filter(map.terrain, loc=loc(xx,yy)) ?
That would still be filtering the map, just by 'loc', rather than by 'x' and 'y'. I don't know if using one variable would save much time over two.
Sapient wrote:As a side note, it does seem a bit odd to me that (built-in) map.terrain is not a map (data-type). Otherwise we could just write map.terrain[loc(xx,yy)].id
Oh, hey, that gives me an idea. (I'm slow sometimes!) No, we cannot do it that way, but since the map is ordered in x and y, this is possible:

Code: Select all

map.terrain[ (xx-1)*map.h+(yy-1) ] 
I've tested that and it works, and that should indeed be much faster than filtering the map each time. Thank you to both of you.
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Exercises in FormulaAI

Post by mattsc »

I've continued putting a few minutes here and there into learning how to manipulate the AI behavior. I've learned in general how things work, how to change and replace candidate actions (CA) with my own FAI code, etc., and I've put together a few toy examples. I am now ready to try out something more serious and I have hit sort of an impasse. Here's what I'd like to do:

I want to "mess around" with the combat CA (for the time being, this is an academic exercise more than anything, but maybe something useful will come out of it in the long run). As a first step, I would like to take the list of attacks that I get from the 'attacks' or 'my_attacks' variables, eliminate some of them based on certain criteria (I know how to do that part), and then select the best of the remaining attacks in the same way the default AI would do it. I can see several ways of how this could be done theoretically, but in practice I don't know how to do any of them:

1. Somehow turn the move back to the standard combat algorithm, but with a reduced set of possible attacks. I have no idea if that is possible.

2. Pick out the specific attack that the AI would choose from the list of possible attacks. I looked through the 'attacks' and 'my_attacks' data structures and there is a lot of useful information in there (target_value, chance_to_kill, avg_damage_taken etc.), but as far as I can tell, the choice of attack comes from a combination of all of that, rather than from one specific element of this structure. I also checked out whether the attacks are maybe sorted by "priority" (or whatever it would be called), but that doesn't seem to be the case either. As in, when I turn control over to the AI, it does not necessarily execute the attack that appears first in the list.

3. Use an evaluation equivalent to that the normal AI uses on the remaining attacks. Is there anything built-in that I could use in either FAI or somewhere else? I've briefly looked over some of the C++ source code such as ai/default/attack.cpp and could probably reproduce the evaluation algorithm used in there, but I'd rather not unless I have to.

Thanks much in advance for any information and/or ideas!


PS: Whoever put together this FAI framework, thank you very much! I think it's a great set of tools that should be used much more than it is (or at least more than I am aware of that it is used).
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Exercises in FormulaAI

Post by mattsc »

In trying to advance this, I've come across a typo in two of the example files that come with Wesnoth. The last line of function get_best_defense_loc in ai/formula/level_up_attack_eval.fai and ai/formula/poisoner_attack.fai is

Code: Select all

defense_on(attacker.loc, self));
but should be

Code: Select all

defense_on(attacker, self));
It's a minor typo, but it results in the function not doing what it's supposed to do. Maybe we can spare future FAIers small headaches by fixing this.
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Exercises in FormulaAI

Post by mattsc »

Sooo... I have a dumb question. Based on searching the forums, I get the impression that FAI is not used a lot. If true, is that because there is no interest, because there are only a handful of people out there who know how to use it, or because the consensus is that it's not a good idea?

I've been playing with FAI on and off over the last weeks and I am having fun with it. In the process, I've also learned quite a bit about it and would be happy to share that knowledge - if there's any interest, that is. For example, I just came across this link and could update that document (or put it on the wiki). I've also come across a number of typos in the wiki that I could fix or add to it. But I don't want to step on anybody's toes by doing that.

Or, I could make some examples and/or macros of modified AI behavior available to others. For example, I'm working on a slightly altered combat action that, while still in its early stages, can stand up to the default AI reasonably well on certain terrains (but would get slaughtered on other terrains). I am also thinking about modifying other behavior that I've sometimes wished I could influence while writing scenarios (the way how targeting works, or how the AI leader just sits and waits things out, etc.). If nobody cares whether this gets done, that's fine, I'll just keep having fun with it for myself. But if there's a reason why this shouldn't be done, or at least not with FAI, let me know.

PS: I understand why the things I mention are done the way they are in default AI and I am not suggesting they get changed, but I have come across situations where I would have liked the AI to behave differently. My guess is that others feel that way too, but that few want to dive into altering AI behavior themselves.
User avatar
Crab
Inactive Developer
Posts: 200
Joined: March 18th, 2009, 9:42 pm

Re: Exercises in FormulaAI

Post by Crab »

Sooo... I have a dumb question. Based on searching the forums, I get the impression that FAI is not used a lot. If true, is that because there is no interest, because there are only a handful of people out there who know how to use it, or because the consensus is that it's not a good idea?
FAI is hard to use for the 'average' person. When lua stuff was added to the game, it turned out that lua is easier to understand and use..
So, Lua AI project was started to allow AI to be controlled by lua. I expect that it would be good and working by 1.10 - it's current state is 'works but there should be annoying bugs, which are easy to fix' - so, if you sit over a svn version, are ready to compile new changes and bug me constantly about blocking things, so I'd fix them, it's certainly possible to code AI in lua - I believe it'd take only a few days to root them out.
I am not going to remove any FAI stuff, but I, personally, consider it to be 'feature frozen because noone works on it' (both it's original creators and maintainers are not around anymore, and, although I know it, I would prefer to concentrate on LuaAI )
But I don't want to step on anybody's toes by doing that.
You are welcome to improve FAI, nothing wrong with that :)))

PS: I understand why the things I mention are done the way they are in default AI and I am not suggesting they get changed, but I have come across situations where I would have liked the AI to behave differently. My guess is that others feel that way too, but that few want to dive into altering AI behavior themselves.
Actually, it's ok to suggest, if you know exactly what behavior you want and how it is possible to 'compute' it. RCA AI is made from pieces, and it's easy to add a new piece to the code without affecting 'default' RCA AI.
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Exercises in FormulaAI

Post by mattsc »

Cool, thanks! I started with FAI instead of LuaAI because I got the impression that it was more advanced at this time. There seem to be a lot of very convenient AI-specific functions available in FAI that I haven't seen for LuaAI - maybe simply because I haven't looked in the right place yet. I also assume that all the information that's so easily available as part of the standard variables (for example, target_value, avg_damage_taken, ... in the attacks variable) is also available in LuaAI? Well, I'll look into it...

If FAI development has stopped, that's not a problem for me. The fun for me is mostly in figuring out how to make the AI behave certain ways. What language I use for that is secondary to me, I figure I can switch anytime. The question is when best to make that switch, maybe now's the time. It does sound, however, that putting a lot of effort into documenting FAI for others is somewhat of a waste of time at this stage and those efforts should better be put into LuaAI.
Crab wrote:Actually, it's ok to suggest, if you know exactly what behavior you want and how it is possible to 'compute' it. RCA AI is made from pieces, and it's easy to add a new piece to the code without affecting 'default' RCA AI.
I 'll keep that in mind, but for now my ideas are still too vague for that. And don't worry, I won't suggest having the AI take away allies' villages - although I might program it for myself as an exercise at some point. :)


Btw, I am not a developer. I've never programmed in C++, but I know C and enough other languages that I can generally understand what the code does - which is how I have figured out how the default AI does its ratings etc (my current combat stage is still very simple but goes roughly 50/50 against the default AI. :D Only on the right kind of terrain though, it'd fail miserably in other situations). Compiling the latest code etc. is not a problem though, I've done plenty of that in my Linux days. I'll see if I can get into a position where I can "bug you constantly"...
User avatar
Crab
Inactive Developer
Posts: 200
Joined: March 18th, 2009, 9:42 pm

Re: Exercises in FormulaAI

Post by Crab »

also assume that all the information that's so easily available as part of the standard variables (for example, target_value, avg_damage_taken, ... in the attacks variable) is also available in LuaAI?
that's what I meant by 'some minor fixes are required' - it's trivial to add this stuff in, since the infrastructure is already there, but most likely it wasn't added yet.
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Exercises in FormulaAI

Post by mattsc »

Crab wrote:
also assume that all the information that's so easily available as part of the standard variables (for example, target_value, avg_damage_taken, ... in the attacks variable) is also available in LuaAI?
that's what I meant by 'some minor fixes are required' - it's trivial to add this stuff in, since the infrastructure is already there, but most likely it wasn't added yet.
Ok. I didn't know if this counted as one of the minor things. If that's the kind of "bugging" you want from me, I can do that. Give me a little time to catch up on Lua Ai and I will get back in contact with you.
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Exercises in Lua AI

Post by mattsc »

[This is a Lua question more so than a Lua AI question, so let me know if I should post this over at "Lua Labs" instead. It's eventual purpose is for Lua AI work though.]

In making my switch from Formula AI to Lua AI, I am coding a few of the FAI convenience functions in Lua. For the moment, this is for my own getting familiar with Lua more so than because they are really needed, although I might put a library of them together at some point if there's an interest in that. FAI has a 'filter' function that can be used like this:

Code: Select all

selected_units = filter(my_units, hitpoints < max_hitpoints)
If my_units is a list (the FAI equivalent to a Lua table) of units, it returns a new list with all units that don't have full hitpoints. Note that the input (my_units) is an implicit variable inside the function, meaning that the condition does not need to use 'my_units.hitpoints' or 'self.hitpoints'.

I have written that same function in Lua:
Spoiler:
[If you don't have Wesnoth_Lua_pack installed, just comment out the 'wesnoth.require' line and the very last line.]

In brief, I set up a table 'my_moves', which contains pairs of source-destination (src, dst) information. I then filter out the ones with field dst.y==3. The function call is this:

Code: Select all

    local filtered_data = 
        filter(my_moves, 
            function(self) return self.dst.y == 3 end
        )
This works just fine, but I am lazy, so I would like to make the function call even easier. Either like this:

Code: Select all

local filtered_data = filter(my_moves, self.dst.y == 3)
or ideally even like this (at which point it would be identical to FAI):

Code: Select all

local filtered_data = filter(my_moves, dst.y == 3)
So my question is, is that possible in Lua with reasonable effort? I'm still very new to Lua, looked into it for real for the first time a few days ago, so any pointers would be much appreciated. Thanks!
User avatar
Sapient
Inactive Developer
Posts: 4453
Joined: November 26th, 2005, 7:41 am
Contact:

Re: Exercises in Lua AI

Post by Sapient »

I'm not a Lua expert, but the closest thing would probably be "self.dst.y == 3" combined with loadstring(). However, I'm not sure if loadstring is allowed, and the performance hit would probably be unacceptable for AI general library purposes anyway.

function(self) ... end -- this isn't so bad is it?

Also, you may want to use
filtered_table=v
instead of
table.insert(filtered_table, v)

(depending on the type of tables that might be passed)
http://www.wesnoth.org/wiki/User:Sapient... "Looks like your skills saved us again. Uh, well at least, they saved Soarin's apple pie."
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: Exercises in Formula and Lua AI

Post by Anonymissimus »

Sapient wrote:function(self) ... end -- this isn't so bad is it?
Yup, passing a function containing the condition and calling it in the child function should work. No need to pass a string and parse it.

You should probably not use "self" as the name of a variable or parameter, since self is a special keyword in lua, it is very much like the "this" in object-oriented java and C++, see the usage in data/lua/location_set.lua. I have no clue of FAI but it seems to be something else there (a unit).

Unfortunately, it's not clear enough to me what you want and what the FAI code does for I could post some untested code.
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
Post Reply