Problem with Python AI in 2 modules

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

Moderators: Forum Moderators, Developers

Post Reply
SkeletonCrew
Developer
Posts: 787
Joined: March 31st, 2006, 6:55 am

Problem with Python AI in 2 modules

Post by SkeletonCrew » September 11th, 2006, 5:48 pm

At the moment I'm busy writing a Python AI and I just split my code in 2 modules. After this split the second module is only imported the first turn. Is this a bug or not, if it's a bug I'll file it on gna.

I did find a work-around by starting the first module with the following code

Code: Select all

if( sys.modules.has_key( 'module2' )):
    del sys.modules[ 'module2' ]

import module2

User avatar
allefant
Units Database Administrator
Posts: 516
Joined: May 6th, 2005, 3:04 pm

Post by allefant » September 11th, 2006, 7:36 pm

In short: It's not necessarily a bug - in Python, the code of a module is only run the first time it is imported. The Python import is very different from the C++ #include.

The "good" way to split code into modules is to have classes/functions in your other modules, and then call those from your main module which gets run every turn.


But, this still gets me thinking - maybe it would be better to actually re-initialize the whole Python interpreter at every turn. It might have a slight performance impact because of the re-initializing, but quite probably completely negligible. And it would make sure there can be no hidden bugs in a script when saving/reloading - since right now you can store global variables and they stay available between turns. Always starting with a new interpreter would prevent that (and also make your import working - but it still would be bad practice to use it like you tried :P).

SkeletonCrew
Developer
Posts: 787
Joined: March 31st, 2006, 6:55 am

Post by SkeletonCrew » September 11th, 2006, 8:09 pm

I import a module to call a class inside them, the problem occured when I found a bug in the imported module and the fix didn't seem te work at first. After restarting the game the fix worked... so then I found the work-around I described.

I'm new to Python but this is the way it should work isn't it??? Like good old Pascal.

I also recall to have read somewhere, that the Python interpreter was re-initialized every turn, but can't seem to find it :(

User avatar
zookeeper
WML Wizard
Posts: 9713
Joined: September 11th, 2004, 10:40 pm
Location: Finland

Post by zookeeper » September 11th, 2006, 8:14 pm

allefant wrote:But, this still gets me thinking - maybe it would be better to actually re-initialize the whole Python interpreter at every turn.
I must say that that sounds like fundamentally bad design to me. Of course I'm no Wesnoth (or C++/Python) coder, but still.

zaimoni
Posts: 281
Joined: January 27th, 2005, 7:00 am
Location: Linn Valley, KS U.S.A.
Contact:

Post by zaimoni » September 11th, 2006, 11:22 pm

I'm new to Python but this is the way it should work isn't it??? Like good old Pascal.
Eh...the way Python normally works is exactly like Pascal. To update the module, you normally have to stop the current instance of the program, Python, then restart it with the new code.

That is, unless you explicitly reload the module every turn:

Code: Select all

reload(my_module)
Allefont: I'd rather the Python AI didn't reinitialize every turn. That defeats any attempt to cache intermediate results across turns, and I'm likely to want to do that if I actually start coding one.

User avatar
allefant
Units Database Administrator
Posts: 516
Joined: May 6th, 2005, 3:04 pm

Post by allefant » September 12th, 2006, 11:21 am

I import a module to call a class inside them, the problem occured when I found a bug in the imported module and the fix didn't seem te work at first. After restarting the game the fix worked... so then I found the work-around I described.
That is how Python normally works, yes. But you could file a Feature Request to have a way to re-initialize Python (and therefore reload all modules). It would most likely be done with a debug function - since this is only useful for AI-developers, not for users.
I must say that that sounds like fundamentally bad design to me. Of course I'm no Wesnoth (or C++/Python) coder, but still.
Yes, so won't change it :)
Allefont: I'd rather the Python AI didn't reinitialize every turn. That defeats any attempt to cache intermediate results across turns, and I'm likely to want to do that if I actually start coding one.
The problem is, the user may save/load the game, so I can't see a way to have this working (except storing the complete Python state in savegames). You can still keep data across turns in a limited way, by using get_variable/set_variable, where you can assign strings to savegame variables, which are reloaded.

zaimoni
Posts: 281
Joined: January 27th, 2005, 7:00 am
Location: Linn Valley, KS U.S.A.
Contact:

Post by zaimoni » September 12th, 2006, 2:04 pm

The point of a cache is that it can be regenerated, it's just expensive to. No need to stuff it into the savefile. [The strategy I'm thinking of relies fairly heavily on accurate, memory-intensive modeling of ZoC.]

I don't mind making the user pay a speed hit for massive savescumming by forcing the AI to regenerate the cache on every reload.

SkeletonCrew
Developer
Posts: 787
Joined: March 31st, 2006, 6:55 am

Post by SkeletonCrew » September 12th, 2006, 4:40 pm

That is how Python normally works, yes. But you could file a Feature Request to have a way to re-initialize Python (and therefore reload all modules). It would most likely be done with a debug function - since this is only useful for AI-developers, not for users.
I won't place a feature request since this simply answers my question, thanks to zaimoni I managed to get my code a bit cleaner. I now use the following code

ai_main.py

Code: Select all

#!WPY

import wesnoth, ai_helper

if( True ):
    reload( ai_helper )

class AI:
    def __init__( self ):
        foo = ai_helper.bar()
AI()
ai_helper.py

Code: Select all

class bar:
    def __init__( self ):
        print 'Hello wrold'
After fixing the typo the second turn I get the proper text. Would it be oke to add this example to http://www.wesnoth.org/wiki/ReferencePythonAPI ?
But, this still gets me thinking - maybe it would be better to actually re-initialize the whole Python interpreter at every turn. It might have a slight performance impact because of the re-initializing, but quite probably completely negligible. And it would make sure there can be no hidden bugs in a script when saving/reloading - since right now you can store global variables and they stay available between turns. Always starting with a new interpreter would prevent that (and also make your import working - but it still would be bad practice to use it like you tried Razz).
Are you sure about that? I just tried the following code and only has foobar the second time also the second turn. (Please note that I'm pretty new to Python.)

Code: Select all

#!WPY 
    
import wesnoth

print dir()

foo_bar = "test"

print dir()

class AI:
    def __init__( self ):
        pass

AI()

User avatar
allefant
Units Database Administrator
Posts: 516
Joined: May 6th, 2005, 3:04 pm

Post by allefant » September 12th, 2006, 7:24 pm

After fixing the typo the second turn I get the proper text. Would it be oke to add this example to http://www.wesnoth.org/wiki/ReferencePythonAPI ?
Sure.. could have a page with different useful script fragments in the Wiki.
Are you sure about that? I just tried the following code and only has foobar the second time also the second turn. (Please note that I'm pretty new to Python.)
Yes, you're right - the main script does get reloaded each time, so all its globals get lost. (Globals inside a module would stay though.)

It would be easy to change this though. In fact, a much cleaner way for Python scripts would probably be to not just execute the contents of the script, but load it as a module, and execute a function inside it, e.g. "main" or "turn". That way, there would be no file with a special status.

So, you would have e.g. my_ai.py, and inside it have a function "turn". Then Wesnoth would do "import my_ai", and call the funtion "turn" inside it for each turn. The question is, can such a change be made for 1.2, or would there be lots of angry AI writers who would need to adjust their scripts :P

SkeletonCrew
Developer
Posts: 787
Joined: March 31st, 2006, 6:55 am

Post by SkeletonCrew » September 13th, 2006, 6:48 am

Sure.. could have a page with different useful script fragments in the Wiki.
I'll add something later to the wiki, but I'm pretty busy at the moment.
It would be easy to change this though. In fact, a much cleaner way for Python scripts would probably be to not just execute the contents of the script, but load it as a module, and execute a function inside it, e.g. "main" or "turn". That way, there would be no file with a special status.
I don't know about changing the AI interface, this change would also mean that the main AI script isn't loaded each time... That is one of the things I really like about the current interface. It would be trivial to store a cache as zaimoni mentioned in another module (and not reload the module every turn of course :wink: .)

I'm also thinking about using a cache like zaimoni montioned instead of storing a lot of data in the savefile. I'm only wondering about cache consistency. I'm playing a game and load (a totally different) game, will there be a new Python instance created or the old one used?
How is it with multiple AI's in an AI war, do they share the same Python instance? (If so would offer great ways for dirty warface :twisted: )

User avatar
allefant
Units Database Administrator
Posts: 516
Joined: May 6th, 2005, 3:04 pm

Post by allefant » September 13th, 2006, 1:08 pm

Yes, there's only one global Python interpreter. So caches really shouldn't be used for now.

SkeletonCrew
Developer
Posts: 787
Joined: March 31st, 2006, 6:55 am

Post by SkeletonCrew » September 17th, 2006, 8:26 am

I updated http://www.wesnoth.org/wiki/ReferencePythonAPI and added http://www.wesnoth.org/wiki/PythonAISampleCode

I looked for all the python samples on this forum and found only one :(

I've some pieces of code I'm working on, but those are not ready yet and some need a patch to the API. (Which I'll post on http://patches.wesnoth.org later.)

Post Reply