Difference between revisions of "User:Danf/TurtleGraphics"

From Noisebridge
Jump to: navigation, search
(python turtle module -> Conway's Game of Life ... and stuff.)
 
(better demo-size rows & columns setting)
Line 205: Line 205:
  
 
def main():
 
def main():
   sr = ScreenRunner(rules='life', displaymode='fade', rows=17, cols=29)
+
   sr = ScreenRunner(rules='life', displaymode='fade', rows=7, cols=11)
 
   return "EVENTLOOP"
 
   return "EVENTLOOP"
  

Revision as of 04:06, 21 January 2015

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