[Tutor] engines

Gregor Lingl glingl@aon.at
Thu, 18 Jul 2002 19:53:55 +0200


This is a multi-part message in MIME format.
--------------020609000405030103040107
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Hi Cameron, hi Pythonistas!

By accident I'm just working on a tiny (and well known) game
(see attachment!) It has an example of a game-engine built in -
or at least what I could imagine this could be.
It's designed to serve for some educational meterial I'm
going to prepare and it's not finished yet - so it's not well
documented.I'm learning more than I'm programming, at the moment.  

Nevertheless it may serve as an example, Cameron was searching for
in his posting.

Of course I'm interested to know, if you all consider it a
useful example.

Specifically I use a pattern for implementing a keyboard-controlled
animation, which I would like to know if it's the standard way doing
things like this with Tkinter or if there are better ways to do it.

It goes like this:

1.)  a function like

eventlist = []
def taste(event):
    key = event.keysym
    if key in goodkeys:
        self.eventlist.append(key)

is bound to the GUI and maintains a list of
events, which serve to control the

2.) animation, which runs in some canvas:

def run():
    taste = None
    # 1. suck one command from the eventlist
    if self.eventlist:
         taste = self.eventlist[0]
         self.eventlist = self.eventlist[1:]
    # 2. then process some sort of finite state machine:
    if state == "start":
         if taste == ...:
              * do this *
         elif taste == ...:
              * do that *
         < if necessary change state >
    elif state = "running":
         <  do other stuff depending on commands
            waiting in eventlist >
    ....

    elif state = "over":
          <etc..   >

    # 3.  finally
    canvas.update()
    canvas.after(100, run)

Have fun studying it ( and perhaps even playing it ;;;-) ).
Critical comments and suggestions for improvement of the code
are VERY welcome.

With kind regards

Gregor

Cameron Stoner schrieb:

> Hi all,
>  
> How does a program engine work?  How do you develope one and how do 
> you use it?
>  
> Thanks,
>  
> Cameron Stoner




--------------020609000405030103040107
Content-Type: text/plain;
 name="snake.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="snake.py"

""" snake - OOP training   (V. OOP12)
    author: gregor lingl, vienna.    email:  glingl@aon.at   """
from Tkinter import *
from Canvas import Rectangle
import sys, random

class Spielbrett(Canvas):
    def __init__(self, root, NX, NY, feldbreite=12, **kwargs):
        Canvas.__init__(self, root, kwargs)
        w,h = NX*feldbreite, NY*feldbreite
        self.config(width=w+2, height=h+2, bg = 'yellow')
        self.grid = {}
        for zeile in range(NY):
            for spalte in range(NX):
                x0 = 2 + spalte * feldbreite
                y0 = 2 + zeile  * feldbreite
                x1 = x0 + feldbreite
                y1 = y0 + feldbreite
                r = Rectangle(self, x0, y0, x1, y1,
                              fill='yellow', outline='yellow' )
                self.grid[(spalte,zeile)] = r
    def set(self,feld,farbe):
        self.grid[feld]['fill'] = farbe

class Snake:
    def __init__(self, size):
        self.N = size
        self.futter = None
        self.reset()
    def reset(self):
        self.schlange = [(self.N/2,self.N/2)]
        selfFutter = None
        self.setzeFutter()
        self.richtung = None
        self.alive = 1         
    def setzeFutter(self):
        while not self.futter or self.futter in self.schlange:
            self.futter = (random.randrange(self.N), random.randrange(self.N))
    def getscore(self):
        return "Score: " + str(max(0,len(self.schlange)-4))        
    def schwanzAb(self):
        self.schlange = self.schlange[1:]
    def kopfDran(self,feld):
        self.schlange.append(feld)
    def neuesFeld(self):
        x,y = self.schlange[-1]
        if self.richtung == 'Up':
            y=y-1
        elif self.richtung == 'Down':
            y=y+1
        elif self.richtung == 'Left':
            x=x-1
        elif self.richtung == 'Right':
            x=x+1
        else:
            return None
        return (x,y)
    def amBrett(self, neu):
        x,y=neu
        return 0 <= x < self.N and 0 <= y < self.N
    def schritt(self, neueRichtung):
        changed = {}
        if neueRichtung:
            if (neueRichtung,self.richtung) not in (('Up','Down'),('Down','Up'),
                                                ('Right','Left'),('Left','Right')):
                self.richtung = neueRichtung
        neu = self.neuesFeld()
        # Fall 1: angestossen
        if not self.amBrett(neu) or neu in self.schlange:
            self.alive = 0
            for glied in self.schlange:
                changed[glied]='black'
        # Fall 2: Futter gefunden
        elif neu == self.futter:
            self.kopfDran(neu)
            changed[neu]='red'
            self.setzeFutter()
            changed[self.futter]='blue'
        # Fall 3: normaler Schritt
        else:
            if len(self.schlange) > 3:
                changed[self.schlange[0]]='yellow'
                self.schwanzAb()
            self.kopfDran(neu)
            changed[neu]='red'
        return changed

class SnakeEngine:
    def __init__(self, N):
        self.N = N
        root = Tk()
        root.title("SNAKE")
        root.bind('<KeyPress>', self.taste)
        self.brett = Spielbrett(root,N,N)
        self.brett.pack()
        self.scorelabel = Label(root,width=22,text="",font=("Courier",14,"bold"),fg='red')
        self.hintlabel  = Label(root,width=22,text="",font=("Courier",14,"normal"))
        self.scorelabel.pack()
        self.hintlabel.pack()
        self.schlange = Snake(N)
        self.reset()
    def taste(self,event):
        key = event.keysym
        if key == 'Return':
            sys.exit(0)
        if key in ['Up','Down','Left','Right','space']:
                self.eventlist.append(key)
    def reset(self):
        for feld in self.brett.grid:
            self.brett.set(feld,'yellow')
        self.eventlist = []
        self.state = "waiting"
        self.schlange.reset()
        self.brett.set(self.schlange.schlange[0],'red')
        self.brett.set(self.schlange.futter,'blue')
        self.brett.update()
        self.hintlabel.config(text="Steuerung: Pfeiltasten")
        self.scorelabel.config(text=self.schlange.getscore())
    def run(self):
        changed = {}
        taste = None
        if self.eventlist:
            taste = self.eventlist[0]
            self.eventlist = self.eventlist[1:]
        if self.state == "waiting":
            if taste in ['Up','Down','Right','Left']:
                self.state = "running"
        if self.state == "running":
            if taste in ['Up','Down','Right','Left', None]:
                changed = self.schlange.schritt(taste)
                self.scorelabel.config(text=self.schlange.getscore())
            if not self.schlange.alive:
                self.state="over"
                self.hintlabel.config(text="Neues Spiel: Leertaste")
        elif self.state == "over":
            if taste == 'space':
                self.reset()
        for feld in changed:
            self.brett.set(feld, changed[feld])
        self.brett.update()
        self.brett.after(100, self.run)
       
SnakeEngine(21).run()
mainloop()
--------------020609000405030103040107--