[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--