At 04:12 PM 11/18/03 -0800, Wade Brainerd wrote: >Hello, I'm working on a game engine using Python as the scripting language >and have a question about generators. >I'm using what I guess are called 'microthreads' as my basic script >building block, and I'd like to know if there is some kind of syntax that >could make them clearer, either something in Python already or something >that could be added. > >Here's an example script that illustrates the problem. > >from jthe import * > >def darlene_ai(self): > while True: > for x in wait_until_near(player.po.w,self.po.w): yield None > > begin_cutscene(self) > > for x in wait_face_each_other(player.po,self.po): yield None > > if not player.inventory.has_key("papers"): > for x in say("Hi, I'm Darlene! I found these papers,\ndid you > lose them?"): yield None > else: > for x in say("Hey, I'm new to this town, wanna go out > sometime?"): yield None > > end_cutscene(self) > > if not player.inventory.has_key("papers"): > spawn(give_item("papers")) > > for x in wait(2.5): yield None > >Now in our in-house script language the above code would look very >similar, only without the > >for x in <call>: yield None > >constructs. Instead, subroutines with a wait_ prefix execute yield >statements which are automatically propogated up the call stack all the >way to the thread manager. >Is there anything to be done about this in Python? I can see it >implemented three ways: Since you don't seem to be using the values yielded, how about doing this instead: while True: yield wait_until_near(...) begin_cutscene(self) yield wait_face_each_other(player.po,self.po) ... All you need to do is change your microthread scheduler so that when a microthread yields a generator-iterator, you push the current microthread onto a stack, and replace it with the yielded generator. Whenever a generator raises StopIteration, you pop the stack it's associated with and resume that generator. This will produce the desired behavior without any language changes. Your scheduler might look like: class Scheduler: def __init__(self): self.threads = [] def spawn(self,thread): stack = [thread] threads.append(stack) def __iter__(self): while True: for thread in self.threads: current = thread[-1] try: step = current.next() except StopIteration: # Current generator is finished, remove it # and give the next thread a chance thread.pop() if not thread: self.threads.remove(thread) yield None continue try: # Is the yielded result iterable? new = iter(step) except TypeError: # No, skip it yield None continue # Yes, push it on the thread's call stack thread.append(new) So, to use this, you would do, e.g: scheduler = Scheduler() runOnce = iter(scheduler).next scheduler.spawn( whatever.darlene_ai() ) while True: runOnce() # do between-quanta activities All this is untested, so use at your own risk.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4