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
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()