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
Boucman
Inactive Developer
Posts: 2119
Joined: March 31st, 2004, 1:04 pm

Re: Exercises in Formula and Lua AI

Post by Boucman »

mattsc wrote:
Boucman wrote:how possible would it be to add some info in the map itself ?
That's trivial and makes writing the AI a lot easier. For example, the 'Pass Defense' scenario uses hard-coded coordinates for the best positions to place defenders, healers and leaders. My recent efforts have been toward getting away from that, but for specific maps it is definitely a possibility.
well, I'm thinking mainline integration here.

I think it would be possible to have such tags as long as

* the AI can work without them (that is : doesn't break entirely or refuse to start)
* they are standard and well documented in the wiki

with these conditions, I don't see why we couldn't have a standard set of hints for mapmakers to use as strategicall/scenaristic hints.
Fight key loggers: write some perl using vim
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Exercises in Formula and Lua AI

Post by mattsc »

TickleMe: Thanks for the replay and all the additional comments. And no worries about the "delay". I am way overextended at the moment and won't be able to look into this right now anyway. As for the incorrect healer behavior, that might both be a bug or simply a "feature" (unintended, of course) of the yet-incomplete healer algorithm. I have noticed that both healers and leaders do stupid things at times, but I have been concentrating on the defensive-line-formation part of the algorithm so far. As for giving you the option to play the scenario as a defender, that can be done easily (once I have fixed a couple recent "optimizations" that have broken this scenario, and most others).

Boucman: Ah, I think I understand now. All of that should be possible. I can write a new tag in lua that the mapmakers can use to set hints. The AI then uses those if they are set, or goes to default behavior when not. So as long as setting them in WML code with a tag is acceptable, that should not be hard to code at all. [If you meant giving the map designer an option to do that in the map editor, then somebody else would have to do that, but I don't see anything wrong with that WML tag approach.] The hard part is defining what kind of hints would be useful and how the AI should behave if they are set.
Boucman
Inactive Developer
Posts: 2119
Joined: March 31st, 2004, 1:04 pm

Re: Exercises in Formula and Lua AI

Post by Boucman »

yup, but that's a new tool you have in your arsenal :) scenario specific human hints (just like we have unit specific human hints in their role)
Fight key loggers: write some perl using vim
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Exercises in Formula and Lua AI

Post by mattsc »

UnwiseOwl, Rigor: Thank you very much for the comments. Let me start with this:
UnwiseOwl wrote:Ha! I measure my time for wesnoth changes in months, trying to fit it around a life, work and family that aren't optimised for computer time, if it takes days or even weeks then you're exceeding expectations by a country mile.
I really only meant getting any MP AI set up, using already existing code, not writing a fully functioning AI. :) Having said that:

Well, I have done that, using a mix from 'Protect the Wizard' and 'Prune Cart' to place healers behind units on the front line, preferably next to those already injured, while, more importantly, keeping them out of harms way. This AI controls only the healers, everything else is done by the default AI, and it currently never attacks with the healers.

Unfortunately, I have to report that I was right: doing that without also adjusting other parts of the AI, makes the AI play worse on MP maps. Have a look, if you want (instructions below) and let me know what you think can be improved. There is clearly room for improvement, but to be honest, I don't think that just controlling the healers will do the job. A 'Prune Cart'-like AI might have a better chance, but that one's not ready yet. So maybe we should try something like the orcish grunt strategy Rigor suggested first instead. I'll look into that sometime in the next few days...

If you want to test this:
  • Download v0.9.2 of 'AI Demos'
  • There's a hidden (very boring) scenario demonstrating the AI. Go into debug mode, type ':choose_level' and select scenario 'healer'. That shows how the AI works when there are "no distractions".
  • To use it in multiplayer mode, launch Wesnoth from a terminal:

    Code: Select all

    path_to_wesnoth/Wesnoth -m --scenario multiplayer_Sablestone_Delta --controller 1:ai --controller 2:ai --side 1:Rebels --side 2:Rebels --parm 1:gold:400 --parm 2:gold:400 --ai-config 1:~add-ons/AI-demos/ais/ai_healer.cfg
    
    The path/executable needs to be adjusted to your system, of course. I'm using map Sablestone_Delta because it is pretty narrow, and give each side 400 gold (to try to avoid small-number luck), but still the side playing the RCA AI has won every single time I watched it. Both sides use the Rebel factions, so that Elvish Shamans are available as healers. The one thing that might help a bit is restricting the side to recruiting only 2 shamans or so, but, well, see my disclaimer above. I don't think that that by itself will do the job.
Well, that's how far I got. The good thing is that I now know how to set this up and that we can start testing things on MP maps. Any suggestions on what to try next would be very welcome.

EDIT:
mattsc wrote:A 'Prune Cart'-like AI might have a better chance
I take that back (I actually did set it up for MP use and tried it) for one simple reason: villages. The Prune Cart AI is set up to sit in a spot and defend a unit. On a map with lots of villages, it will lose simply because of all the income the enemy gets. When I set the village gold to zero, it does better (but its unfinishedness still is pretty obvious).
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Exercises in Formula and Lua AI

Post by mattsc »

Rigor: I've put together a first version of a 'Grunts Rush on Freelands' following your suggestion. It's still pretty simple and has its problems and I have some ideas, but I would be interested in hearing what you and others think would be the most important improvements. To test it:
  • You need v0.9.3 of 'AI Demos'
  • At the command-line type:

    Code: Select all

    path-to-wesnoth/wesnoth -d -m --controller 1:ai --controller 2:ai --side 1:Northerners --side 2:Northerners --parm 1:gold:200 --parm 2:gold:200 --ai-config 1:~add-ons/AI-demos/ais/ai_orcs.cfg
    
Again, path-to-wesnoth/wesnoth obviously has to be adjusted for your system. As for the algorithms used, I have added two candidate actions to the default AI (both of which have higher priority than the default ones):

Recruit grunts: If the side has less than 60% grunts, recruit grunts until >60% is reached. The rest of recruitment is left to the standard algorithm.

Grunts hold villages: A grunt will either hold or go to a village using the following rating system:
  • Only villages that can be reached by enemies on the next turn are considered ('can be reached' in this case ignores AI units that might be in the way and being able to get on an adjacent hex counts). The more enemy units can reach the village, the higher its priority.
  • Also, only villages that are not occupied by the AI (other than the grunt itself) are considered.
  • Unowned villages get a bonus
  • Enemy-owned villages get a larger bonus
  • All else being equal, the orc with the most hitpoints moves to the village (as the village is within enemy reach)
User avatar
FAAB
Inactive Developer
Posts: 52
Joined: November 15th, 2008, 12:15 pm

Re: Exercises in Formula and Lua AI

Post by FAAB »

mattsc wrote:I've put together a first version of a 'Grunts Rush on Freelands' following your suggestion.

Recruit grunts: If the side has less than 60% grunts, recruit grunts until >60% is reached. The rest of recruitment is left to the standard algorithm.

Grunts hold villages: A grunt will either hold or go to a village using the following rating system:
  • Only villages that can be reached by enemies on the next turn are considered ('can be reached' in this case ignores AI units that might be in the way and being able to get on an adjacent hex counts). The more enemy units can reach the village, the higher its priority.
  • Also, only villages that are not occupied by the AI (other than the grunt itself) are considered.
  • Unowned villages get a bonus
  • Enemy-owned villages get a larger bonus
  • All else being equal, the orc with the most hitpoints moves to the village (as the village is within enemy reach)
Hi mattsc, you are bringing in quite exciting results. You have put all the code in place to achieve a good grunt spam.

Having run it several times, here are my observations:
1. grunts are sitting on village as long as it is in reach from a wolfrider
2. three grunts are sticking to their villages while a 1-HP wolfrider in "'threatening" them.
3. grunts are protecting villages that are zoced-out from the enemy

I would suggest the following rule changes:
- move the village protection lower than the attack (to avoid situation 2.)
- however, keep the enemy village stealing higher than attack (*)
- extend the village protection code to all units
- given that all attack are done before village protection, most of your unit have moved and use ignore_units=false in path finding (removing village protection on unreachable villages [case 3.]).

All in all, here are the changes in parameter I suggest (**):

Code: Select all

diff -r backup//lua/orcs_engine.lua AI-demos//lua/orcs_engine.lua
28c28
<             if (#grunts / (#all_units+1) > 0.6) then return 0 end
---
>             if (#grunts / #all_units > 0.6) then return 0 end
55c55
<             local grunts = wesnoth.get_units { side = wesnoth.current.side, 
---
>             local grunts = wesnoth.get_units { side = wesnoth.current.side, type = 'Orcish Grunt',
88c88
<                             local path_e, cost_e = wesnoth.find_path(e, v[1], v[2], { ignore_units = false })
---
>                             local path_e, cost_e = wesnoth.find_path(e, v[1], v[2], { ignore_units = true })
123,125c123
<                 local owner = wesnoth.get_village_owner(best_village[1], best_village[2])
<                 if (owner ~= wesnoth.current.side) then return 290000 end
<                 return 90000
---
>                 return 290000
Having it run, it looks more like a real grunt spam as we find them on the MP server. From my testing it is also more efficient than the initial parameters.

A remaining improvement to me would to activate the grunt recruitment only on turn 2 (to get more scouts on turn 1 for village grabbing)

(*) actually a potential unit kill is usually better than a village steal
(**) with a change the logic on 60% grunt recruit to insure getting one more grunt when we have 3 out of 5 units (60%)
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Exercises in Formula and Lua AI

Post by mattsc »

Hi FAAB. Thank you very much, this is great! The AI plays much better after the changes you made! I have some comments and questions back:
FAAB wrote: - move the village protection lower than the attack (to avoid situation 2.)
- however, keep the enemy village stealing higher than attack (*)
Maybe I misunderstand you, but I thought one of the main points of what Rigor suggested was to have grunts sit on villages so that the enemy wastes a lot of hitpoints trying to get rid of them. If I only make villages stealing higher priority than attacks, we're (almost) back to the default behavior (after reachable villages have been taken). I have tried this and I think it makes the custom AI play worse, so I took it out again, but left the code for it in the file. If you want to try it, comment out the line 'return 290000' and uncomment the one following it.

However, what you said made me do another change to that part of the code. Previously, I had number of enemy units that can reach a village set as higher priority than whether the village is owned by the AI. I have now switched that, which I think is better.
FAAB wrote:A remaining improvement to me would to activate the grunt recruitment only on turn 2 (to get more scouts on turn 1 for village grabbing)
Done. An alternative would be to force recruiting of a couple (2, 3?) scouts on Turn 1, before starting the grunt recruiting. By simply starting on Turn 2, the AI recruits whatever it wants, not necessarily scouts, on Turn 1.
FAAB wrote:(*) actually a potential unit kill is usually better than a village steal
Agreed. So the way I have done that: if any of the units with moves left has a >50% chance of making a kill, then the village grabbing/holding action for all units has lower priority than attack, otherwise it's the other way around. Does that sound right?
FAAB wrote:(**) with a change the logic on 60% grunt recruit to insure getting one more grunt when we have 3 out of 5 units (60%)
I have nothing against the new version at all, but wasn't that already done by using '>' rather than '>='. But then, that 60% was pretty much a random choice anyway, so if you have another preference, just let me know.

One other thing I have noticed: most often when the custom AI loses, it has more units (and villages) than the enemy, but some enemy units managed to sneak around the defenses and take out the leader while some grunts sit on close villages and watch. I could add something that does priority targeting of those enemies. E.g. 2 AI units for any enemy that is closer to the AI's leader than any AI unit; or for any enemy unit within attack range of the leader. Or something. Suggestions?

Also: in having the AI's play against each other (on Freelands), there seems to be a huge difference depending on who goes first (this is with 200 gold). With the old version, the custom AI almost always lost when it was playing Side 2. With the new version, it wins most of the time no matter which side it plays. (If you just want the outcome, you can add '--nogui' to the command-line and just get the result very quickly.)

Thank you very much again for the detailed comments. This is great! If you're interested,
here's the diff:

Code: Select all

13,15d12
<             -- Start this at Turn 2
<             if (wesnoth.current.turn < 2) then return 0 end
< 
31c28,29
<              if (#grunts / (#all_units+1) > 0.6) then return 0 end
---
>             if (#grunts / #all_units > 0.6) then return 0 end
> 
57c55
<             local grunts = wesnoth.get_units { side = wesnoth.current.side, 
---
>             local grunts = wesnoth.get_units { side = wesnoth.current.side, type = 'Orcish Grunt',
72,81d69
<             -- First check attacks that are possible for this unit
<             local return_value = 290000
<             local attacks = AH.get_attacks(grunts)
<             -- If one with > 50% chance of kill is possible, set return_value for lower combat CA
<             for i,a in ipairs(attacks) do
<                 if (a.def_stats.hp_chance[0] > 0.5) then
<                     return_value = 90000
<                 end
<             end
< 
100c88
<                             local path_e, cost_e = wesnoth.find_path(e, v[1], v[2])
---
>                             local path_e, cost_e = wesnoth.find_path(e, v[1], v[2], { ignore_units = true })
103c91
<                                 rating = rating + 10
---
>                                 rating = rating + 100
113c101
<                                 rating = rating + 1000
---
>                                 rating = rating + 10
115c103
<                                 if (owner ~= wesnoth.current.side) then rating = rating + 2000 end
---
>                                 if (owner ~= wesnoth.current.side) then rating = rating + 20 end
136d123
<                 --if (max_rating >= 1000) then return 290000 else return 90000 end
The new orcs_engine.lua is attached. Or, if you give me a few minutes, you can download it in v0.9.5 from the add-ons server. (Edit: more than one file was changed, so I deleted the attachment. Download the latest version instead.)


Finally, something I noticed that is not directly related to this AI, but that caused "mystery crashes" of my code several times. If several sides in a MP game use the same faction, their leaders have the same id. So checking for a unit's id is not enough in some functions, you also need to check for the side of the unit. (This is just a note for other people interested in writing their own AI code, not directly related to this discussion.)
Last edited by mattsc on June 26th, 2012, 2:52 pm, edited 1 time in total.
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: Exercises in Formula and Lua AI

Post by Anonymissimus »

mattsc wrote:Finally, something I noticed that is not directly related to this AI, but that caused "mystery crashes" of my code several times. If several sides in a MP game use the same faction, their leaders have the same id. So checking for a unit's id is not enough in some functions, you also need to check for the side of the unit. (This is just a note for other people interested in writing their own AI code, not directly related to this discussion.)
You mean the wml id from unit.id where unit is the lua proxy unit of the leader ?
That is very unfortunate I think. You could make an almost-a-bug feature request to give them unique ids.
The engine doesn't (should not) make use of the wml ids, but it is a (perhaps implicit) wml rule that unit ids should be unique, and the engine also checks for it at some spots.

EDIT
I just checked this in some local MP game and cannot reproduce. Perhaps some additional conditions ?
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
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Exercises in Formula and Lua AI

Post by mattsc »

Anonymissimus wrote:You mean the wml id from unit.id where unit is the lua proxy unit of the leader ?
EDIT
I just checked this in some local MP game and cannot reproduce. Perhaps some additional conditions ?
Yeah, I just tried that too. It appears to be the case only when Wesnoth is launched from the command line (this is with 1.10.3 on a Mac). Then, the side leader id seems to set to the faction name.
Anonymissimus wrote:That is very unfortunate I think. You could make an almost-a-bug feature request to give them unique ids.
Ok.
Anonymissimus wrote:The engine doesn't (should not) make use of the wml ids, but it is a (perhaps implicit) wml rule that unit ids should be unique, and the engine also checks for it at some spots.
I agree. I'd prefer to use underlying_id, but that's only available through .__cfg in Lua which is really slow. So maybe I should make that a feature request? Having underlying_id available directly for Lua proxy units. Or is there a suggested better way to test whether two units stored in two proxy tables are identical? Just comparing the id's is obviously not always unambiguous.
User avatar
Sapient
Inactive Developer
Posts: 4453
Joined: November 26th, 2005, 7:41 am
Contact:

Re: Exercises in Formula and Lua AI

Post by Sapient »

it should always be unique so thats a bug.

originally my plan was to allow clones with the same id to be placed on the map at the same time, as a convession to careless wml authors, with the caveat that undefined behaviors could result..



However since then other coders have made it a hard requirement
.. unless my memory is mistaken

either way the scenarios in core shouldnt have duplicate unit ids
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: Exercises in Formula and Lua AI

Post by mattsc »

Sapient: Thanks for the information. I'll file a bug report tonight.
User avatar
Rigor
Posts: 941
Joined: September 27th, 2007, 1:40 am

Re: Exercises in Formula and Lua AI

Post by Rigor »

u know what would be even better as demonstration? uploading savegames! :mrgreen: the grut rush does work best if you recruit e.g. 4 grunts, 2 assassins or exchange one assassin vs a wolf/archer etc - no trolls usually, they arrive way too slow at the scene and wont help with their little damage per hex. dont be fooled by the villages, the typical grunt goes forward on the right side of the map while two units are sufficient to grab 4 villages on the left. optimally, they arrive during dusk on a spot where they can perfectly attack the village on the right, always. its abotu being p1 too, im sure you could code "when p1 use 2/3 of the army here and 1/3 there". if ur p2 this doesnt work at all of course because when everything is free on the right and ur troops are on the left, then p1 just reinforces his left side with more units while ALSO holding the right, so ... bad.
Last edited by Rigor on June 25th, 2012, 9:25 pm, edited 1 time in total.
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Exercises in Formula and Lua AI

Post by mattsc »

Rigor wrote:u know what would be even better as demonstration? uploading savegames! :mrgreen:
Well, yes and no. The problem with savegames is that I need to choose the ones that best demonstrate certain strengths or weaknesses. As I said, I'm not an MP player (and barely a Wesnoth player at all atm), so using my strategy to test the AI would be ... sub-optimal. I just let the custom AI play against the default AI (which does not produce savegames) and see which one seems to do better. I'm not qualified to test how this AI would do against a decent human player.
Rigor wrote: ... the typical grunt goes forward on the right side of the map while two units are sufficient to grab 4 villages on the left. optimally, they arrive during dusk on a spot where they can perfectly attack the village on the right, always. its abotu being p1 too, im sure you could code "when p1 use 2/3 of the army here and 1/3 there". if ur p2 this doesnt work at all of course because when everything is free on the right and ur troops are on the left, then p1 just reinforces his left side with more units while ALSO holding the right, so ... bad.
Question about that: this applies specifically to the Freelands map, right? So yes, that could be coded, but it would only work on this map. Is that something people are interested in at this time? So far I'd assumed I'd write stuff that is applicable to other maps as well (whether the strategy makes sense there or not being a different question). It's fine with me, but it's not what I have been doing so far. If that is the case, I'll need detailed map-specific (and faction-specific) information. See my disclaimer above. ;) If a Freelands specific AI for Northerners is what you're more interested in than the more general approach, sure I can look into the right vs. left strategy thing. And sorry for the misunderstanding in that case. :D

Also: does this strategy depend on how much starting gold the sides have? I've been testing with 200 gold so far to reduce some of the small-number statistics variations.
User avatar
Rigor
Posts: 941
Joined: September 27th, 2007, 1:40 am

Re: Exercises in Formula and Lua AI

Post by Rigor »

for your pleasure i did a recruit that would do the AI some good. 4 grunts stand where they should (at dusk) and there are enough supporters to maintain their initial advantage for the day to come. for your question: i suggest you do a map and faction specific AI behaviour and when done playing around with it you can go for the generic one. i think this would be better than trying to do the more difficult generic approach just like that! did you say i can play those kind of games online so ppl can observe and comment on the ais behaviour?
Attachments
2p_—_The_Freelands_replay.gz
grunt R
(22.68 KiB) Downloaded 163 times
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Exercises in Formula and Lua AI

Post by mattsc »

Rigor wrote:for your pleasure i did a recruit that would do the AI some good. 4 grunts stand where they should (at dusk) and there are enough supporters to maintain their initial advantage for the day to come.
Thanks much! I'll study this and figure out how to tell the AI how to do (some of) that. (And in the process I might learn something for myself as well!)
Rigor wrote:for your question: i suggest you do a map and faction specific AI behaviour and when done playing around with it you can go for the generic one. i think this would be better than trying to do the more difficult generic approach just like that!
That makes sense. It is certainly easier to code that way.
Rigor wrote:did you say i can play those kind of games online so ppl can observe and comment on the ais behaviour?
Umm... I didn't say because I don't know where the game takes its AI code from when on the server (as in, whether it needs to be a file in an official distribution or not). My guess is that it can be done, I just don't know how much of a hack it involves and whether the observers need to have the same on their local versions. I'll find out and report back.

Edit: Yes, it can be done and that was much easier than I thought. I should have checked this out before replying...
So, all you need is to put the AI file into the directory in which the MP game looks for available AIs. That means, copy file ~add-ons/AI-demos/ais/ai_orcs.cfg into directory ai/dev/ in your data directory (or create a link to it). After that, you can select it as one of the available AI's in MP games. (Note that several other files from AI-Demos are also needed, but this is the only file that needs to be copied into a core folder.)

Edit2: Very interesting! Apparently I have a lot to learn if I ever want to become a multiplayer. Well, wanting is not the issue...
Post Reply