Getting AI to recall?
Moderator: Forum Moderators
Forum rules
- Please use [code] BBCode tags in your posts for embedding WML snippets.
- To keep your code readable so that others can easily help you, make sure to indent it following our conventions.
- Ken_Oh
- Moderator Emeritus
- Posts: 2178
- Joined: February 6th, 2006, 4:03 am
- Location: Baltimore, Maryland, USA
Getting AI to recall?
I've got this little campaign where I'd like to have some persistence to the enemy. So far I've been able to provide the same enemy leader (unit and xp) with the same recruit list for different scenarios, but can't get AI to recall.
zookeeper suggested I check to see if AI can recall by providing the player side with a recall list, deleting every unit from the recruit list and then :droid'ing myself to see if the AI recalls. Sure enough, the leader unit just goes around capping villages.
I was wondering if anyone has any thoughts on this. Am I maybe missing something? I'm also not 100% sure how to store recall lists for AI sides.
There's just one idea I have that might work, but it's far from simple.
Upon recruit a check is first done for gold. If there isn't at least enough gold past the recruit cost to make it 20 (unless the cost is already above 20) then forget it. Then a check is done of the availability of units in the recall list that are leveled up versions of that unit or the same units but with more xp. Some values are assigned to some things that give this choice a score (the less xp left to level, especially for level 1, the higher the score; the more gold the AI side has, the higher the score;etc.)...........
OK, I think I just hurt my brain thinking about the details just now. But, anyway, you get the point that things get scored, they get a chance of happening assigned, then there's a random number generated that makes the decision if it wants to substitute the unit and with which unit to put in.
Just wondering if anyone has any better ideas.
zookeeper suggested I check to see if AI can recall by providing the player side with a recall list, deleting every unit from the recruit list and then :droid'ing myself to see if the AI recalls. Sure enough, the leader unit just goes around capping villages.
I was wondering if anyone has any thoughts on this. Am I maybe missing something? I'm also not 100% sure how to store recall lists for AI sides.
There's just one idea I have that might work, but it's far from simple.
Upon recruit a check is first done for gold. If there isn't at least enough gold past the recruit cost to make it 20 (unless the cost is already above 20) then forget it. Then a check is done of the availability of units in the recall list that are leveled up versions of that unit or the same units but with more xp. Some values are assigned to some things that give this choice a score (the less xp left to level, especially for level 1, the higher the score; the more gold the AI side has, the higher the score;etc.)...........
OK, I think I just hurt my brain thinking about the details just now. But, anyway, you get the point that things get scored, they get a chance of happening assigned, then there's a random number generated that makes the decision if it wants to substitute the unit and with which unit to put in.
Just wondering if anyone has any better ideas.
-
- Retired Developer
- Posts: 2633
- Joined: March 22nd, 2004, 11:22 pm
- Location: An Earl's Roadstead
The current AI can not recall units. It is something I intend to fix in my AI, however for now you will need to use some deep WML tricks.
"you can already do that with WML"
Fight Creeeping Biggerism!
http://www.wesnoth.org/forum/viewtopic. ... 760#131760
http://www.wesnoth.org/forum/viewtopic. ... 1358#11358
-
- Posts: 199
- Joined: November 27th, 2006, 2:29 pm
- Location: Behind you
Another quick fly-by answer...
At the end of each scenario, [store] all (surviving) enemy units. Then find some way of separating different types of unit into separate arrays.
When, in the next scenario, the AI recruits a unit, check to see if there is a stored unit of that type. If so, [kill] the newly-recruited unit and replace it with a randomly-selected unit from the array of that type. Then remove that unit from the array, and correct the AI's gold (if you really care that much)
At the end of each scenario, [store] all (surviving) enemy units. Then find some way of separating different types of unit into separate arrays.
When, in the next scenario, the AI recruits a unit, check to see if there is a stored unit of that type. If so, [kill] the newly-recruited unit and replace it with a randomly-selected unit from the array of that type. Then remove that unit from the array, and correct the AI's gold (if you really care that much)
I don't exist when you don't see me...
I don't exist when you're not here.
You bought a mask - I put it on.
You never thought to ask me if I wear it when you're gone.
I don't exist when you're not here.
You bought a mask - I put it on.
You never thought to ask me if I wear it when you're gone.
- Ken_Oh
- Moderator Emeritus
- Posts: 2178
- Joined: February 6th, 2006, 4:03 am
- Location: Baltimore, Maryland, USA
Yeah, I already had the storing and putting to recall list made (that's basically what I've been doing to put dead units to the recall list).
I'd rather not make the recalling random, but intelligent to the point that the AI only recalls higher level units or the same unit type with a somewhat high xp (unless it's a unit with a recruit cost of >= 20).
I'm trying to think how to sort units in the recall list by lowest to highest max_experience minus experience and pick lowest one, but am not coming up with anything yet.
If anyone has a clue, please chime in.
EDIT: Talked it over in irc and it seems that [recall] already takes unit that is closest to leveling up. Case solved.
EDIT #2
EDIT #3 - Nevermind. I think I have it all figured out.
I'd rather not make the recalling random, but intelligent to the point that the AI only recalls higher level units or the same unit type with a somewhat high xp (unless it's a unit with a recruit cost of >= 20).
I'm trying to think how to sort units in the recall list by lowest to highest max_experience minus experience and pick lowest one, but am not coming up with anything yet.
If anyone has a clue, please chime in.
EDIT: Talked it over in irc and it seems that [recall] already takes unit that is closest to leveling up. Case solved.
EDIT #2
EDIT #3 - Nevermind. I think I have it all figured out.
- Ken_Oh
- Moderator Emeritus
- Posts: 2178
- Joined: February 6th, 2006, 4:03 am
- Location: Baltimore, Maryland, USA
Bump for attention. My recall code basically comes down to this for every unit, which I include in the second scenario:
It kind of works, as I've had the AI recruit xp'ed and leveled up Bowmen, Cavalrymen and Fencers. But it kind of doesn't. I put in a counter for it:
What happens seems random for each recall, and none of it makes any sense. It'll stay on the same x,y but the count increment goes up to anywhere between 4-8 before going to the next unit. Plus, for each x,y it sometimes displays both "have" and "not have." When it displays "have" (regardless of if it displays "have not"), then it seems to work. But other times it'll just say "have not" and then ends up with a recruit instead of a recall.
There's got to be something I'm missing here. It seem as if recruit events have a delay of some sort. The same with [recall].
Code: Select all
#define AI_RECALL_SPEARMAN
[event]
name=recruit
first_time_only=no
[filter]
side=2
type=Spearman
[/filter]
[store_unit]
variable=SPEARMAN_ai_recall
[filter]
x,y=$x1,$y1
[/filter]
kill=yes
[/store_unit]
[recall]
type=Spearman,Swordsman,Pikeman,Javelineer,Royal Guard,Halbardier
x,y=$x1,$y1
[/recall]
[if]
[have_unit]
x,y=$x1,$y1
[/have_unit]
[then]
[gold]
side=2
amount=-6
[/gold]
[/then]
[else]
[unstore_unit]
variable=SPEARMAN_ai_recall
[/unstore_unit]
[/else]
[/if]
{CLEAR_VARIABLE SPEARMAN_ai_recall}
[/event]
#enddef
Code: Select all
#define AI_RECALL_FENCER
[event]
name=recruit
first_time_only=no
[filter]
side=2
type=Fencer
[/filter]
{VARIABLE_OP count add 1}
{DEBUG_MSG "fencer xy $x1| $y1| count $count|" }
[store_unit]
variable=FENCER_ai_recall
[filter]
x,y=$x1,$y1
[/filter]
kill=yes
[/store_unit]
[recall]
type=Master at Arms,Duelist,Fencer
x,y=$x1,$y1
[/recall]
[if]
[have_unit]
x,y=$x1,$y1
[/have_unit]
[then]
{VARIABLE_OP count add 1}
{DEBUG_MSG "fencer have xy $x1| $y1| count $count|"}
[gold]
side=2
amount=-4
[/gold]
[/then]
[else]
{VARIABLE_OP count add 1}
{DEBUG_MSG "fencer not have xy $x1| $y1| count $count|"}
[unstore_unit]
variable=FENCER_ai_recall
[/unstore_unit]
[/else]
[/if]
{VARIABLE count 0}
{CLEAR_VARIABLE FENCER_ai_recall}
[/event]
#enddef
There's got to be something I'm missing here. It seem as if recruit events have a delay of some sort. The same with [recall].
-
- Retired Developer
- Posts: 2633
- Joined: March 22nd, 2004, 11:22 pm
- Location: An Earl's Roadstead
Without looking at the C++ code, it wouldn't surprise me if recalling a unit is triggering a recruit event which could be leading to a little circular logic until it exhausts the recall list. If that is the case, what is needed is some tag that lets you define whether the recruit event should include both recall/recruit events, or only one or the other.
"you can already do that with WML"
Fight Creeeping Biggerism!
http://www.wesnoth.org/forum/viewtopic. ... 760#131760
http://www.wesnoth.org/forum/viewtopic. ... 1358#11358
- Ken_Oh
- Moderator Emeritus
- Posts: 2178
- Joined: February 6th, 2006, 4:03 am
- Location: Baltimore, Maryland, USA
Thanks. I think that might be it since it doesn't seem to have much problem recalling level 2 units (a Duelist wouldn't trigger the filter for Fencer).
Do you think this would do it?
If not, I don't how to delay an event until just -after- a recruit, but not before the next one.
I'm having trouble telling if stuff works now that it kind of works.
Do you think this would do it?
Code: Select all
#define AI_RECALL_TEST
[event]
name=prerecruit
first_time_only=no
[filter]
side=2
[/filter]
[store_unit]
variable=TEST_ai_recall
[filter]
x,y=$x1,$y1
[/filter]
kill=no
[/store_unit]
[/event]
#enddef
#define AI_RECALL_DRAKE_BURNER
[event]
name=recruit
first_time_only=no
[filter]
side=2
type=Drake Burner
[variable]
name=TEST_ai_recall.xp
less_than=1
[/variable]
[/filter]
etc...
I'm having trouble telling if stuff works now that it kind of works.