Lua error: cannot find ai.move_full (but finds & calls other func in 'ai')

Discussion of Lua and LuaWML support, development, and ideas.

Moderator: Forum Moderators

Post Reply
denispir
Posts: 184
Joined: March 14th, 2013, 12:26 am

Lua error: cannot find ai.move_full (but finds & calls other func in 'ai')

Post by denispir »

Hello,

I just had a Lua error, think (and hope) it may be a stupid bug by me, but who know?
"attempt to call a nil value (field 'move_full')" in ai_helper.lua at line 218 (if/else block below). This is in function 'checked_move_core':

Code: Select all

function ai_helper.checked_move_core(ai, unit, x, y, move_type)
    local check = ai.check_move(unit, x, y)

    if (not check.ok) then
        if (not ai_helper.is_incomplete_or_empty_move(check)) then
            ai.stopunit_moves(unit)
            ai_helper.checked_action_error(move_type .. ' from ' .. unit.x .. ',' .. unit.y .. ' to ' .. x .. ',' .. y, check.status .. ' (' .. check.result .. ')')
            return check
        end
    end

    if (move_type == 'ai.move_full') then
        return ai.move_full(unit, x, y)         -- *** line 218 ***
    else
        return ai.move(unit, x, y)
    end
end
'checked_move_core' is itself called by 'checked_move_full' which full code is:

Code: Select all

function ai_helper.checked_move_full(ai, unit, x, y)
    return ai_helper.checked_move_core(ai, unit, x, y, 'ai.move_full')
end
Fortunately, I have not made many changes before running a test. Among them, for consistency with other "import" names like AH, I set in my CA code at module top level:

Code: Select all

local W = wesnoth
local AI = ai
I guessed this should be a factor of the error, especially because I had read a few days ago that 'ai' is "now available as a "scoped global variable" anywhere in AI code" at wiki page https://wiki.wesnoth.org/Creating_Custom_AIs. Would someone explain what this actually means? (I searched the Lua ref because my knowledge of Lua is old, but found nothing, so this may be proper to Wesnoth Lua.)

Anyway, removing these lines alone did not help. I mention that just in case they still may be a 'cofactor' of the error (and to have an explanation as asked above).

In the function 'checked_move_core' (code above), a call to 'ai.check_move' is done in any case, so we can be sure that Lua properly finds 'ai' and its available, callable fields. Also, noting the if/else branching (end of func), I modified my own code to provoke a call to 'ai.move' instead of 'ai.move_full' (by calling AH.checked_move).
This provokes (as half expected) the same error except for a change to "field 'move'" and to line 220...

I am blocked because I cannot find the table (pseudo module) 'ai'. Thus, in addition to help about the bug, I take the opportunity to ask for the code defining 'ai' and also 'wesnoth'. (Present breve descriptions are not enough, especially around path finding; for other modules like AH I did have to read the code to actually understand purpose and usage).

Right time for a pause and doing something move moving ;) !
thank you, diniz
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Lua error: cannot find ai.move_full (but finds & calls other func in 'ai')

Post by mattsc »

Hey,

The information you provide isn't really enough to say what is going on. As you suspect yourself, the problem is almost certainly in the code that makes the 'ai' table available, but I don't remember ever experiencing a case where 'ai.check_move()' is available and 'ai.move()' is not, so I have no idea what might be happening. If you post your entire code, I could do some checking.
denispir wrote: January 22nd, 2019, 10:00 am I guessed this should be a factor of the error, especially because I had read a few days ago that 'ai' is "now available as a "scoped global variable" anywhere in AI code" at wiki page https://wiki.wesnoth.org/Creating_Custom_AIs. Would someone explain what this actually means?
I actually don't know what the "scoped" means either (I did not write this), only the consequence that it is not necessary (and in fact does not work) any more if you pass the 'ai' table as an argument to a candidate action eval/exec function.
denispir wrote: January 22nd, 2019, 10:00 am I am blocked because I cannot find the table (pseudo module) 'ai'. Thus, in addition to help about the bug, I take the opportunity to ask for the code defining 'ai' and also 'wesnoth'.
The AI Lua functions are defined in 'src/ai/lua/core.cpp' and the 'wesnoth' table in 'src/scripting/game_lua_kernel.cpp' (or similar functions in the same directories). I don't actually always remember this off the top of my head, so I 'grep' for, for example, 'check_move_full' in the 'src/' directory.

Also, FYI, I find the 'dbms()' function of the Wesnoth Lua Pack extremely useful when working with Wesnoth Lua in order to see the structure of a table (although that does not always work with the 'ai' table, if I remember correctly, as its functions are hidden, at least in some cases). I'm not sure if the WLP is still maintained, but it should still be available on the add-ons server. Alternatively, there's a slightly modified local version in the AI-demos add-on.

Hope this helps.
denispir
Posts: 184
Joined: March 14th, 2013, 12:26 am

Re: Lua error: cannot find ai.move_full (but finds & calls other func in 'ai')

Post by denispir »

Hello @mattsc,

Thank you for your reply. In the meantime I have passed to something else to explore; and right now cannot give much time to this issue. Anyway, I am learning how to do micro AIs, albeit still at a very early stage with a single & simple CA added to the mainloop. I took inspiration from your CA_move_type in AI_Demos. I ended up rewriting it from scratch and found some interesting side problems which led to that "something else to explore" (another way to find next stop on the path toward a dest). The bug comes up when using this rewritten version.

Apart from what I'm telling above, I did not make any other change (between the time when it worked and the bug) than creating 3 free data fields in the CA table itself (to keep lists of controlled units, ones not moved yet, and their number), and setting them from either the eval or the exec funcs.

I now guess that the "scoped global variable" may just mean that now CA files run into a redefined _ENV table into which 'ai' is injected: 'ai' would thus be a module-level var as through an import. I now now that 'ai' is defined in C, thank you, which I suspected. I will have a look at the C code whenever I can, hoping that the '++' functionalities are not too heavily used (but since Lua is supposed to be 100% standard C I can hope ;)).

Tell you more when I have some free time to dedicate to this issue (and other fun problems).
diniz

PS: Why isn't there an AI forum (and also a map forum).
User avatar
Celtic_Minstrel
Developer
Posts: 2166
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: Lua error: cannot find ai.move_full (but finds & calls other func in 'ai')

Post by Celtic_Minstrel »

I'm guessing you're running into the fact that the gamestate-changing functions are removed from the ai table while running a candidate action evaluation function, as those functions are not supposed to change the gamestate. If you absolutely need to change the gamestate anyway during the evaluation phase, you can execute ai.read_only = false (possibly without the underscore, I can't quite remember) and then those functions will become available, but I recommend instead moving your code to the candidate action execution function. This might involve changing the WML that defines your AI.

The meaning of "scoped global variable" is just that if for some reason you create a global variable named ai in non-AI code, it won't be overwritten by the ai module (though it will be inaccessible from AI code). To put it another way: when running the AI code, the engine does something like this (pseudo-code):

Code: Select all

local save_ai = ai
ai = wesnoth.debug_ai(wesnoth.current.side).ai
local result = your_ai_function()
ai = save_ai
return result
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
denispir
Posts: 184
Joined: March 14th, 2013, 12:26 am

Re: Lua error: cannot find ai.move_full (but finds & calls other func in 'ai')

Post by denispir »

First, sorry for reacting so late (!), I had not seen your answer: the forum engine just does not notify of replies, even to our own theads, unless we are explicitely quoted (I know about subscribing).
Celtic_Minstrel wrote: February 19th, 2019, 4:09 am I'm guessing you're running into the fact that the gamestate-changing functions are removed from the ai table while running a candidate action evaluation function, as those functions are not supposed to change the gamestate. If you absolutely need to change the gamestate anyway during the evaluation phase, you can execute ai.read_only = false (possibly without the underscore, I can't quite remember) and then those functions will become available, but I recommend instead moving your code to the candidate action execution function. This might involve changing the WML that defines your AI.
All right, thank you! It makes sense indeed, pretty sure this the source of my problem. Unfortunately, I cannot try right now because I have abandoned AI study for while, forgotten most of it (veeery bad memory), and I am totally occupied by a campaign revamp. But whenever I can test, I will try and think at posting the result here (and quote you again so that you know).

The reason why I did all in an evaluation func is simply that I do not need 2 functions. They actually make no sense for me, even in general: if eval determines that I should do something, let us do it right now; if instead it determines we should do nothing, then finished. (This also avoids double computation of various things like a microAI's group of units, pathes, units in range... I know we can store all that in module-level vars, but why do it if unnneded?). In the same vein, apart from combat proper, I guess we should process all units controlled by a microAI at once, instead of one per (mainloop) cycle. That's what I did and it worked for my case, which is plain group moving toward a target, until they reach a zone of potential combat/threat.
All this is just my present vision, and I may well be wrong, totally or in some cases.

Celtic_Minstrel wrote: February 19th, 2019, 4:09 am The meaning of "scoped global variable" is just that if for some reason you create a global variable named ai in non-AI code, it won't be overwritten by the ai module (though it will be inaccessible from AI code). To put it another way: when running the AI code, the engine does something like this (pseudo-code):

Code: Select all

local save_ai = ai
ai = wesnoth.debug_ai(wesnoth.current.side).ai
local result = your_ai_function()
ai = save_ai
return result
Clear explanation, thank you again! If we had a better organised and named LuaAI kind of domain-specific language, then such things would not be necessary. No more than for builtin names in any dynamic language. An alternative, again in an imaginary world, may be to warn the user when re-defining a predefined var (possible in principle in Lua for non-local vars, since it keeps their names, while locals are replaced by array indexes IIRC).

PS: For this, stopping the general confusion between creating and changing a var, which some rightly call def and redef, would be totally appropriate:

Code: Select all

var1 : val1    # define var1
var1 : val2    # error: var1 is already defined
var1 :: val2   # right
var2 :: val2   # error: var2 is not defined
Both cases are equally considered as assignment or "setting" in nearly all languages, all mainstream for sure.
User avatar
Celtic_Minstrel
Developer
Posts: 2166
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: Lua error: cannot find ai.move_full (but finds & calls other func in 'ai')

Post by Celtic_Minstrel »

denispir wrote: November 3rd, 2019, 3:27 pm The reason why I did all in an evaluation func is simply that I do not need 2 functions. They actually make no sense for me, even in general: if eval determines that I should do something, let us do it right now; if instead it determines we should do nothing, then finished.
I believe the purpose of the separate evaluation and execution is so that the AI engine can determine which actions are available, then select one of them based on their priorities. So… the evaluation function determines an action's priority (which if I recall correctly is zero when the action is unavailable), but then the AI might see that a different action should be taken first and execute that one instead.

If you want, you could certainly make the evaluation return a fixed priority and then write all the logic in the execution function. However, that may also potentially cause problems, because if the AI engine calls your execution function but finds that it didn't do anything, it blacklists it so it won't be run again. I'm not quite sure what blacklisting entails, though – if it's only for the rest of the turn, that might be what you want anyway.
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
denispir
Posts: 184
Joined: March 14th, 2013, 12:26 am

Re: Lua error: cannot find ai.move_full (but finds & calls other func in 'ai')

Post by denispir »

Celtic_Minstrel wrote: November 3rd, 2019, 3:45 pm I believe the purpose of the separate evaluation and execution is so that the AI engine can determine which actions are available, then select one of them based on their priorities. So… the evaluation function determines an action's priority (which if I recall correctly is zero when the action is unavailable), but then the AI might see that a different action should be taken first and execute that one instead.

If you want, you could certainly make the evaluation return a fixed priority and then write all the logic in the execution function. However, that may also potentially cause problems, because if the AI engine calls your execution function but finds that it didn't do anything, it blacklists it so it won't be run again. I'm not quite sure what blacklisting entails, though – if it's only for the rest of the turn, that might be what you want anyway.
IIUC the AI framework, or rather the unsaid logic behind its structure, especially the mainloop cycle, then all what you say above is right.

But:
  1. IIRC, the CAs or microAIs are already checked in order of default priority score (that' the only point a default score?). So that if the present one microAI, it (the AI) knows that it is one remaining with highest priority. Thus, if its eval func says "yes, let's do sth", then it can do it right now.
  2. Changing the priority makes no sense. It is a strategic or tactical decision that an AI cannot do sensibly see below.
also @mattsc
My prevent views:
When game authors design a scenario, they determine several key things, plus many more, which are concur to define, and allow or forbid, vaious potential tactics:
* the map indeed
* objective(s) and "anti-objectives
* the forces in presence
* difficulty levels (intended)
* events (!)
I let you think & find the various ways, each of these points may have a dramatic effect on tactical choices; and how they combine as well...

Just an example: in the HttT scenario where Konrad and his gang rush toward the drawven doors, the obvious ennemy tactic is also to rush toward there; or, if they areclose or fast enough, somewhere in-between on Konrad's path. Instead, they rush after Konrad and his ganstars, or they did so when I played. just because there is no right predefined CAs "block-a-path" or "interception" (but the author could use a "moveto x,y" CA indeed!). Instead they seem to use "moveto unit".

Now, the interesting is that a 1-year-old baby does the right when intercepting someone moving, for instance to give or show sth. It's pure good concrete intelligence. Baby animals also do the right thing by the way. But for an AI, even an Sci-Fi one of year 2222, it would be impossible, at least if it is an abstract one (where software/control is disconnented from machine/action: note that in a cell or any organism, the sftware is the hardware). We may think otherwise, and even code something that would work very well in that case. But is would miserably in most similar case, however you define "similar". (Change any of factors above.). It would be mechanical and abstract, instead of concretely intelligent, and fail in most cases. Worse: whenever it succeded, it would by chance, or more exactly by accident (but its designer may interpet that differently indeed ;-)).

The consequence is that we should not even think at designing an AI, less so a general, but instead let game authors define it (or several), for each real scenario case (and maybe diff-level etc...). We should tactical modules that combine into sensible AIs --of which again we are not the designer. This is the way I interpret Mattsc's microAIs, and why I'm so interested in his approach of the big AI problem.

But the present AI framework is useless, worse a burden and straitjacket. We can do it despite it. I'm thinking at an alternative: simply using events (the game author) to define/change/replace microAIs in action; and also events to launch them, following which each microAI in action does all its job at once.
User avatar
Celtic_Minstrel
Developer
Posts: 2166
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: Lua error: cannot find ai.move_full (but finds & calls other func in 'ai')

Post by Celtic_Minstrel »

If you want to get away from the candidate action framework with separate evaluation and execution, I think what I would suggest is to define a Lua stage instead of a custom candidate action. Then you have full control of the AI logic. You can even let it fall back to the default AI, if you want, by defining a second stage using the C++ engine.

The MicroAIs build on the candidate action system though, so if you do that you won't be able to use them with your custom stage.
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
mattsc
Inactive Developer
Posts: 1217
Joined: October 13th, 2010, 6:14 pm

Re: Lua error: cannot find ai.move_full (but finds & calls other func in 'ai')

Post by mattsc »

Just a couple additional comments on Celtic_Minstrel's last two post. First: yes, that's why the eval/exec separation is done that way. Once you are inside the execution function, it has to execute an action otherwise it gets blacklisted. That is primarily set up in this way to prevent an AI getting stuck in an infinite loop, but it can also be used on purpose for some things. So if you want an evaluation of different CAs against each other to see which one to execute first, that has to be done in the evaluation functions. Of course there are other ways to do this and whether you like this is up to you. I do like it because it provides a lot of versatility.

As for celmin's last post, that should work. And another way would be to put everything into one single CA and do the ranking of actions inside that. That way you might be able to bind in existing CAs more easily. Oh, that's what celmin wrote in the previous post. And yes, blacklisting is only until the end of the current turn.
Post Reply