# Game state management in Python (threads, generators, etc)

This topic is 4834 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

One of my current projects is a little turn-based strategy game, somewhat similar to X-Com. (It's actually closer to Lords of Chaos, one of X-Com's predecessors.) I handle states with a basic stack, where the game calls the 'run' method of the state at the top of the stack, then updates the graphics subsystem, pumps the event queue, etc. This is a very clean design for most things. However I now have the problem that during the AITurnState, when a computer player is making its moves, there are certain tasks that take a long time to complete and the system requires that the system returns from the 'run' method periodically (10-30 times a second) so that the main engine can perform housekeeping tasks. This is awkward because I want to be able to write clean linear code like this:
for creature in computerPlayer.creatures:
for possibleTactic in allAITactics:
evaluate(possibleTactic)
path = FindPathTo(self.chosenTactic.location)
for step in path:
move(step.x, step.y)


Yet during this code, which might take 10 seconds or so to complete normally, I'm going to want to return from inside each of those 3 for loops to allow the screen to update, to register the human player's mouse movement, etc. One option would be to write this as a generator, and add 'yield none' liberally through the routine. Then I'd call it like so:
class AITurnState:
def __init__():
# initialise the generator with the creature list, etc
self.ai = AIGenerator(creatures, world, otherContext)
def run():
"""this is called roughly 10-30 times a second"""
try:
# Do a bit more of the AI
self.ai.next()
# Not finished yet, so please call us again
return "state-continue"
except StopIteration:
# All done; pop us off the state-stack
return "state-finished"



##### Share on other sites
Yes, this is a very vexing problem. It's nice to see other people use python for games; it shows I'm not alone :) I personally like using more of a generator approach, since I don't like the idea of the rest of the system needing to know the internals of that one function; although, I don't like the use of exceptions to stop when the generator is done, either. That exception-checking code should be put into a lower-level routine (the AIGenerator class), so that it ends up looking like this:
class AITurnState:    def __init__():        self.ai = AIGenerator(creatures, world, otherContext)    def run():        """this is called roughly 10-30 times a second"""        action = self.ai.next()        if action == AIGenerator.CONTINUE:            return "state-continue"        elif action == AIGenerator.FINISHED:            return "state-finished"        else:            pass

##### Share on other sites
If you're instantiating the AI objects as real class objects, then you just put all the important bits of movement and thinking in their update sections. Then, you just make a list of them (in this case, in order of initiative or speed?) and increment through the list with like

for monster in list_of_monsters:
monster.update()

##### Share on other sites
My current game engine uses Lua, so some of the features are named differently, but I most of the concepts carry over. So:

There are really two types of operations that take longer than one update cycle, and they deserve to be handled differently. The first is synchronous updates, as embodied by your move-loop. Coroutines (generators in Python) are the best way I know of to code for this. State machines work too, but they lack the clarity and expressiveness of concurrent code, so I dislike them for this sort of thing. The second is asynchronous processing, which only takes multiple cycles because it requires a buttload of CPU time. Coroutines aren't really ideal here because there aren't discrete slices to execute per-frame. So I think this one deserves an asynchronous thread, given as much processing time as is available for the frame, and is tied to the scripting system by a simple loop that yields as long as the computation isn't complete.

##### Share on other sites
What I ended up implementing is something along those lines. Basically, the main thread loops, drawing the screen and checking the event queue, and each time it yields execution to the AI thread which runs up to a certain point, yields execution back, and the process repeats until the AI thread terminates.

The AI thread has discrete points at which it yields; specifically, every time one of its creatures moves (or performs some other action, once I implement them). The yielding mechanism in both threads is an awkward hack using 2 threading.Event() objects, although I expect something better could be done.

##### Share on other sites
Have you checked Stackless Python? It has explicit support for microthreads and things like that.

##### Share on other sites
Quote:
 Original post by Sneftel The second is asynchronous processing, which only takes multiple cycles because it requires a buttload of CPU time. Coroutines aren't really ideal here because there aren't discrete slices to execute per-frame. So I think this one deserves an asynchronous thread, given as much processing time as is available for the frame, and is tied to the scripting system by a simple loop that yields as long as the computation isn't complete.

Do you mean creating a different thread in the main code and then calling Lua from that thread? Would this work? (I'd be too afraid to do it :P).

I'd still prefer coroutines - all that is needed is to call a function that checks the time and yields if appropriate at various points in that computation.

##### Share on other sites
Quote:
 Original post by DiodorDo you mean creating a different thread in the main code and then calling Lua from that thread? Would this work? (I'd be too afraid to do it :P).
It is possible to do that. Lua has #defines you can use to trigger a mutex. But in the cases where a new thread is spawned, I don't have it call back into Lua; this is primarily because wherever CPU-intensive stuff is being done, I want to optimize it, and that means writing in C++.
Quote:
 I'd still prefer coroutines - all that is needed is to call a function that checks the time and yields if appropriate at various points in that computation.
Yeah, but that requires you to sprinkle extra calls around your code, slowing it down. The OS gives you thread scheduling (almost) for free; no reason not to use it.

##### Share on other sites
Quote:
 Original post by SneftelYeah, but that requires you to sprinkle extra calls around your code, slowing it down. The OS gives you thread scheduling (almost) for free; no reason not to use it.

Hey, I get lazy like that after a while, and can't be made to return to C unless at gunpoint :)

Anyway, I guess it all depends on specifics. A lua function _may_ do serious data crunching if most of the time it spends is in the C functions it calls.

##### Share on other sites
Quote:
 Original post by FrunyHave you checked Stackless Python? It has explicit support for microthreads and things like that.

I'm not sure I need anything like that, after all I'm only creating one additional thread, so the 'weight' of the thread is not an issue. The Stackless website is very poor for documentation anyway and I can't really see any benefits to using stackless Python from a quick glance at it.

##### Share on other sites
Using real thread is, on nowadays PC and console, a really specific operation that should be used only for really known thing...
That is a game can have thread for low level sound (DirectSound does for you ), or low level network ( you must have a thread that listen to what's happening on your ports ).

Other uses are begging for problems. Multi thread make programming much more comlpicated and bug proned. And for most of the code, solid code is the number one mandatory property.

It may change in near future, with the Xbox2, and the PS3, but it's no good news...

For what you want, generators seems just perfect...

Hope it helps,

##### Share on other sites
Quote:
 Original post by Anonymous PosterOther uses are begging for problems. Multi thread make programming much more comlpicated and bug proned.
Not with the advent of multicore processors. Intel has proclaimed (and I mean that with all the hype and fanfare that insinuates) that all of its future processors will be multicore, which means that you better get comfortable with MT code if you want decent performance.

##### Share on other sites
In the fact, there are only a few few people that have a multiprocessor computer...

there will be quite some time for it to become the standard, if it is to become the standard.

Threads are a nightmare to synchronized, but for some really simple places.

I can't imagine the win to have a complex code, bug prone, that run slower on most computer, just to win a few FPS on a few computer, that should the fastest...

Quake 3 was optimised for multithreading, and Carmack said it was a mistake... So much work, so little gain...

Creating a game is now a _huge_ work, so many things to do, so little time, so whenever you can avoid spending some time on an hazardous direction, do it...

##### Share on other sites

This topic is 4834 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Create an account

Register a new account

• ### Forum Statistics

• Total Topics
628721
• Total Posts
2984394

• 25
• 11
• 10
• 16
• 14