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