User:Danf/TurtleGraphics: Difference between revisions

From Noisebridge
Jump to navigation Jump to search
(better demo-size rows & columns setting)
('x' to re-randomize ... lulz)
Line 5: Line 5:
<pre>
<pre>
# turtlife.py - Artistic License w/ Attribution -> "(evil) Dan of MOISEBRIDGE"
# turtlife.py - Artistic License w/ Attribution -> "(evil) Dan of MOISEBRIDGE"
# note: press 'n' to advance frame, 'r' to run, 'p' to pause
# note: press 'n' to advance frame, 'r' to run, 'p' to pause, 'x' to re-randomize


from turtle import Screen, Turtle, mainloop
from turtle import Screen, Turtle, mainloop

Revision as of 21:08, 20 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, '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()