User:Danf/TurtleGraphics
Jump to navigation
Jump to search
python turtle module -> Conway's Game of Life ... and stuff.
experimental work in progress, needs to be cleaned up and refactored a bit ...
# turtlife.py - Artistic License w/ Attribution -> "(evil) Dan of MOISEBRIDGE"
# note: press 'n' to advance frame, 'r' to run, 'p' to pause, 'x' to re-randomize
from turtle import Screen, Turtle, mainloop
from itertools import islice, product, repeat, starmap
from random import randint
from time import sleep
class Cell(object):
def __init__(self, colony, row, col):
self.colony = colony
self.row = row
self.col = col
self.val = 0
self.extra = 0
self.rules = colony.rules
self._neighbors = None
def neighbors(self):
if self._neighbors is None:
self._neighbors = list(self.colony.neighbormap(self, self.colony.cells))
return self._neighbors
def neighborsum(self):
return sum(o.val for o in self.neighbors())
def destiny(self):
n = self.neighborsum()
if self.rules == 'prime':
return ((self.val if (n == 5 or n == 7) else 1 if (n == 2 or n == 3) else 0), n)
elif self.rules == 'life':
return ((self.val if (n == 2) else 1 if (n == 3) else 0), n)
def value(self, val=None, extra=None):
if val is not None:
self.val = val
if extra is not None:
self.extra = extra
return self.val
def valchar(self):
return (' ', 'o')[self.val]
class Raster(object):
def __init__(self, rules, displaymode, rows, cols):
self.rules = rules
self.displaymode = displaymode
self.rows = rows
self.cols = cols
self.cells = list(starmap(
lambda x, y: Cell(self, x, y),
product(range(rows), range(cols)) ))
self.turtles = None
def rowslice(self, r):
i = r * self.cols
return islice(self.cells, i, i + self.cols)
def neighborhood(self, row, col):
up = row - 1 if row else self.rows - 1
down = row + 1 if row < self.rows - 1 else 0
left = col - 1 if col else self.cols - 1
right = col + 1 if col < self.cols - 1 else 0
return ( (up, left), (up, col), (up, right),
(row, left), (row, right),
(down, left), (down, col), (down, right) )
def neighbormap(self, o, sq):
return starmap(
lambda x, y: sq[x * self.cols + y],
self.neighborhood(o.row, o.col) )
def turtledisplay(self):
if self.turtles is None:
self.turtles = list(CellularTurtle(self, row, col) for row in range(self.rows) for col in range(self.cols))
for c, t in zip(self.cells, self.turtles):
if c.val:
if c.extra == 2:
t.rgb = list(t.colors['blue'])
elif c.extra == 3:
t.rgb = list(t.colors['green'])
elif c.extra == 5:
t.rgb = list(t.colors['yellow'])
elif c.extra == 7:
t.rgb = list(t.colors['red'])
else:
if self.displaymode == 'ambient':
t.rgb = list(t.ambience())
elif self.displaymode == 'fade':
for i in range(3):
t.rgb[i] *= 0.618
else:
t.rgb = list(t.colors['black'])
t.color(t.rgb)
def textdisplay(self):
for r in range(self.rows):
print(.join(map(lambda x: x.valchar(), self.rowslice(r))))
def display(self):
self.turtledisplay()
self.textdisplay()
class CellularTurtle(Turtle):
def __init__(self, colony, row, col):
Turtle.__init__(self)
self.colony = colony
self.row = row
self.col = col
self.speed(0)
# self.hideturtle()
self.shape("circle")
# self.settiltangle(90)
self.resizemode("user")
self.shapesize(2, 2, 0)
self.pu()
self.setx(col)
self.sety(row)
self.colors = dict( (
( 'black', (0.0, 0.0, 0.0) ),
( 'grey50', (0.5, 0.5, 0.5) ),
( 'white', (1.0, 1.0, 1.0) ),
( 'red', (0.7, 0.0, 0.0) ),
( 'yellow', (0.7, 0.7, 0.0) ),
( 'green', (0.0, 0.7, 0.0) ),
( 'blue', (0.0, 0.0, 0.7) ) ) )
self.rgb = list(self.colors['black'])
self.color(self.rgb)
self._neighbors = None
def neighbors(self):
if self._neighbors is None:
self._neighbors = list(self.colony.neighbormap(self, self.colony.turtles))
return self._neighbors
def avg_rgb(self, turtles):
rgb = [0.0, 0.0, 0.0]
n = len(turtles)
for t in turtles:
for i in range(3):
rgb[i] += t.rgb[i]
return map(lambda x: x/n, rgb)
def ambience(self):
return self.avg_rgb(self.neighbors())
class CellRunner(object):
def __init__(self, rules, displaymode, rows, cols):
self.raster = Raster(rules, displaymode, rows, cols)
self.randomize()
def randomize(self):
list(map(lambda x: x.value(randint(0, 1)), self.raster.cells))
def update(self, sync=True):
dst = map(lambda x: x.destiny(), self.raster.cells)
if sync:
dst = list(dst)
list(starmap(
lambda x, y: x.value(*y),
zip(self.raster.cells, dst) ))
self.raster.display()
def run(self, n=0, delay=0.1):
f = (lambda: repeat(1, n)) if n else (lambda: repeat(1))
for x in f():
try:
self.update(sync=True)
if(delay):
sleep(delay)
except:
break
class ScreenRunner(object):
def __init__(self, rules='prime', displaymode='ambient', rows=16, cols=32):
self.screen = self.initscreen(rows, cols)
self.cellrunner = CellRunner(rules, displaymode, rows, cols)
self.running = False
self.next()
def initscreen(self, rows, cols):
screen = Screen()
screen.delay(0)
offset = map(lambda x: x - 0.3, (0, rows, cols, 0))
screen.setworldcoordinates(*offset)
screen.bgcolor(0.0, 0.0, 0.0)
screen.tracer(n=rows*cols)
self.bindkeys(screen)
return screen
def bindkeys(self, screen):
screen.onkey(self.randomize, 'x')
screen.onkey(self.next, 'n')
screen.onkey(self.run, 'r')
screen.onkey(self.save, 's')
screen.onkey(self.pause, 'p')
screen.onkey(self.quit, 'q')
screen.listen()
def randomize(self):
self.cellrunner.randomize()
self.next()
def next(self):
self.cellrunner.run(1, 0)
def run(self):
self.running = True
self.timer()
def save(self):
self.pause(r)
s = .join(str(o.val) for o in self.cellrunner.raster.cells)
print(s)
def pause(self):
self.running = False
def quit(self):
exit()
def timer(self, delay=100):
if self.running:
self.next()
self.screen.ontimer(lambda: self.timer(delay), delay)
def main():
sr = ScreenRunner(rules='life', displaymode='fade', rows=7, cols=11)
return "EVENTLOOP"
if __name__ == "__main__":
msg = main()
print(msg)
mainloop()