[Tutor] Old friend revisited - just for fun!

Gregor Lingl glingl@aon.at
Tue, 9 Jul 2002 23:23:20 +0200


This is a multi-part message in MIME format.

------=_NextPart_000_0015_01C2279F.9D06B120
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: 7bit

Hi!

I've attached a simple implementation of Conway's
life game. It can use VPython if available on 
your machine.

Try, for instance: python LifeGame.py 37 [torus]

Of course it's really useless, so perhaps Rob would like
to put it on his site.

Nevertheless, critical remarks and suggestions for 
improvement are welcome.
 
Gregor

P.S. I did it to show the spirit and some advantages
of OOP. Did I succeed in your opinion?


------=_NextPart_000_0015_01C2279F.9D06B120
Content-Type: text/plain;
	name="LifeGame.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="LifeGame.py"

"""A toy version of Conway's life game on a torus -
- based on a version found at: http://www.pythonapocrypha.com/
Outputdevice - selectable by optional command line argument:
canvas or torus.   ---   Just for fun.
                                        version: 1.0  - 2002-07-09"""
import sys, time
from Tkinter import *
from Canvas import Rectangle
try:
    from visual import *
    novisual =3D 0
except:
    print "Modul visual not available!!!"
    novisual =3D 1

class Torus:
    def __init__(self, NX, NY):
        if novisual: sys.exit(0)
        scene.forward =3D (-1.0, -1.0, -0.5)
        R, r =3D 5.0, 2.5
        self.NX, self.NY =3D NX, NY
        dfi, dtheta =3D 2*pi/NX, 2*pi/NY
        self.grid =3D {}
        for y in range(NY):
            for x in range(NX):
                fi1,    fi2    =3D x*dfi,    (x+1)*dfi
                theta1, theta2 =3D y*dtheta, (y+1)*dtheta
                r1, r2 =3D (R+r*cos(theta1)), (R+r*cos(theta2))
                c =3D convex( pos=3D[(r1*sin(fi1), r*sin(theta1), =
r1*cos(fi1)),
                                 (r1*sin(fi2), r*sin(theta1), =
r1*cos(fi2)),
                                 (r2*sin(fi2), r*sin(theta2), =
r2*cos(fi2)),
                                 (r2*sin(fi1), r*sin(theta2), =
r2*cos(fi1))],
                                 color =3D color.green )
                self.grid[(x,y)] =3D c
    def switch(self, cells):
        RATE =3D 20
        rate(RATE)   ### DOESN'T WORK PROPERLY WITH RATE =3D 40
                     ### (tested with N=3D3 on my 400MHz machine)
        for cell in cells:
            if self.grid[cell].green=3D=3D1.0:
                self.grid[cell].color=3Dcolor.red
            else:
                self.grid[cell].color=3Dcolor.green
    def getSize(self):
        return self.NX, self.NY

class TKCanvas:
    def __init__(self, NX, NY):
        squarewidth, cellwidth =3D 8, 10
        self.NX, self.NY =3D NX, NY
        root =3D Tk()
        w,h =3D NX*cellwidth, NY*cellwidth
        self.cv =3D Canvas(root, width=3Dw+2, height=3Dh+2, bg =3D =
'yellow')
        self.cv.pack()
        self.grid =3D {}
        for y in range(NY):
            for x in range(NX):
                r =3D Rectangle(self.cv, 2+x*cellwidth+1, =
2+y*cellwidth+1,
                              =
3+x*cellwidth+squarewidth,3+y*cellwidth+squarewidth,
                              fill=3D'yellow')
                self.grid[(x,y)] =3D r
        self.cv.update()
    def switch(self, cells):
        for cell in cells:
            if self.grid[cell]['fill']=3D=3D'yellow':
                self.grid[cell]['fill']=3D'red'
            else:
                self.grid[cell]['fill']=3D'yellow'
        self.cv.update()
        self.cv.after(2)
    def getSize(self):
        return self.NX, self.NY
           =20
STEADY_STATE  =3D "Steady state"
EVERYONE_DEAD =3D "Everyone dead"

class PlayField:
    def __init__(self, outputDevice):
        self._X, self._Y =3D outputDevice.getSize()
        self.out =3D outputDevice
        self.neighbors=3D{}
        self.reset()
        self.livingCells=3D[]
        self.changedCells=3D[]
    def reset(self):
        for Y in range(self._Y):
            for X in range(self._X):
                self.neighbors[(X,Y)]=3D0
    def setAlive(self,cells):
        self.livingCells=3Dcells
        self.out.switch(cells)
    def setNeighbors(self,X,Y):
        leftColumn=3D(X-1) % self._X
        rightColumn=3D(X+1) % self._X
        upRow=3D(Y-1) % self._Y
        downRow=3D(Y+1) % self._Y
        self.neighbors[(leftColumn,upRow)]+=3D1
        self.neighbors[(X,upRow)]+=3D1
        self.neighbors[(rightColumn,upRow)]+=3D1
        self.neighbors[(leftColumn,Y)]+=3D1
        self.neighbors[(rightColumn,Y)]+=3D1
        self.neighbors[(leftColumn,downRow)]+=3D1
        self.neighbors[(X,downRow)]+=3D1
        self.neighbors[(rightColumn,downRow)]+=3D1
    def nextGeneration(self):
        newGeneration=3D[]
        self.reset()
        for (X,Y) in self.livingCells:
            self.setNeighbors(X,Y)
        for cell in self.neighbors:
                n =3D self.neighbors[cell]
                if n =3D=3D 3 or n =3D=3D 2 and cell in =
self.livingCells:
                    newGeneration.append(cell)
        if not newGeneration: raise EVERYONE_DEAD
        if self.livingCells=3D=3DnewGeneration: raise STEADY_STATE
        self.changedCells=3D([c for c in self.livingCells if c not in =
newGeneration]+
                           [c for c in newGeneration if c not in =
self.livingCells])
        self.livingCells=3DnewGeneration
    def showGenerations(self,generationCount):
        try:
            for cycle in range(generationCount):
                self.out.switch(self.changedCells)
                self.nextGeneration()
        except EVERYONE_DEAD:
            self.out.switch(self.livingCells)
            print "\nAfter %s generations: the population is now =
dead."%cycle
        except STEADY_STATE:
            print "\nAfter %s generations: the population is no longer =
changing."%cycle

usage =3D """usage: python LifeGame.py  [n [g]] [canvas|torus]
optional input arguments:
n : number of cells in a line for start generation
    default value: n =3D 0 results in a glider
g : number of generations to compute,
    default value: g =3D 116    =20
canvas: causes life to be displayed on a 'toroidal' rectangle,
       actually a Tk Canvas
torus:  causes life to be displayed on a VPython torus"""
if (__name__=3D=3D"__main__"):
    #  Set size of the world:
    FIELD_X, FIELD_Y =3D 42, 17
    C_X, C_Y, C_g =3D 60,35,2000   # gcannon case
    g =3D 90    # default number of generations
    N =3D 0     # default population: glider
    args =3D sys.argv[1:]
    mode =3D 'canvas'   # default, may be changed to 'torus'
    if 'canvas' in args:
        mode =3D 'canvas'  =20
        args.remove('canvas')
    elif 'torus' in args:
        if novisual: print "Therefore no torus mode!"
        else:
            mode =3D 'torus'
            args.remove('torus')
    elif 'help' in args:
        print usage
        sys.exit(0)
    try:
        if args:
            N =3D min(int(args[0]),FIELD_X)
            if len(args) > 1: g =3D int(args[1])
    except:
        print usage
        sys.exit(0)
    if N =3D=3D -1:
        FIELD_X, FIELD_Y, g =3D C_X, C_Y, C_g
    if mode =3D=3D 'canvas':
        world =3D TKCanvas(FIELD_X, FIELD_Y)
    else:
        world =3D Torus(FIELD_X, FIELD_Y)
    life=3DPlayField(world)
    # Initialize population
    if N > 0:         # straight line of cells
        f =3D (mode=3D=3D'canvas')
        line =3D [((i+f*(FIELD_X-N)/2)%FIELD_X,0+f*FIELD_Y/2) for i in =
range(N)]
        life.setAlive(line)
    elif N =3D=3D 0:      # glider
        life.setAlive([(0,2),(1,2),(2,2),(2,3),(1,4)])
    else:             # e.g. N=3D-1, very special case
        gcannon =3D =
[(0,24),(0,25),(1,24),(1,25),(11,23),(11,24),(11,25),(12,22),(12,26),
            =
(13,21),(13,27),(14,22),(14,26),(15,23),(15,24),(15,25),(16,23),(16,24),
            =
(16,25),(21,25),(21,26),(21,27),(22,24),(22,25),(22,27),(22,28),(23,24),
            =
(23,25),(23,27),(23,28),(24,24),(24,25),(24,26),(24,27),(24,28),(25,23),
            =
(25,24),(25,28),(25,29),(30,24),(30,25),(34,26),(34,27),(35,26),(35,27)]
        if mode=3D=3D'torus': gcannon =3D [(x,(C_Y-y-8)%C_Y) for (x,y) =
in gcannon]
        life.setAlive(gcannon)
    a=3Dtime.time()
    life.showGenerations(g)
    print "\ntime:", time.time()-a
    raw_input()

------=_NextPart_000_0015_01C2279F.9D06B120--