### Sebastius and polyfloyd
### Special for CZ19 Badge
### It's not really stable at the moment
### Missing a button to reset back to menu
### Missing beautifull colors
### Display way to blinky :)

import rgb
import urandom
import buttons, defines, system

direction = 0
brightness = rgb.getbrightness()
rgb.clear()

UP, DOWN, LEFT, RIGHT = defines.BTN_UP, defines.BTN_DOWN, defines.BTN_LEFT, defines.BTN_RIGHT
A, B = defines.BTN_A, defines.BTN_B

def input_up(pressed):
    global direction
    direction = UP

def input_down(pressed):
    global direction
    direction = DOWN

def input_left(pressed):
    global direction
    direction = LEFT

def input_right(pressed):
    global direction
    direction = RIGHT

def input_B(pressed):
    global direction
    direction = B
	
def input_A(pressed):
    global direction
    direction = A

buttons.register(UP, input_up)
buttons.register(DOWN, input_down)
buttons.register(LEFT, input_left)
buttons.register(RIGHT, input_right)
buttons.register(B, input_B)
buttons.register(A, input_B)

def game_of_life():
    global direction
    global brightness
    rgb.clear()
    width = rgb.PANEL_WIDTH
    height = rgb.PANEL_HEIGHT
    global grid
    grid = [[False] * height for _ in range(width)]

    def seed():
        global grid
        for x in range(0, width-1):
            for y in range(0, height-1):
                if urandom.getrandbits(30) % 2 == 1:
                    grid[x][y] = True
                else:
                    grid[x][y] = False

    def display():
        rgb.clear()
        for x in range(0, width):
            for y in range(0, height):
                if grid[x][y]:
                    rgb.pixel(color=(0,255,255),pos=(x,y))

    def alive_neighbours(x, y, wrap):
        if wrap:
            range_func = lambda dim, size: range(dim - 1, dim + 2)
        else:
            range_func = lambda dim, size: range(max(0, dim - 1), min(size, dim + 2))
        n = 0
        for nx in range_func(x, width):
            for ny in range_func(y, height):
                if grid[nx % width][ny % height]:
                    if nx != x or ny != y:
                        n += 1
        return n

    def step():
        global grid
        changed = False
        new_grid = [[False] * height for _ in range(width)]
        for x in range(width):
            for y in range(height):
                neighbours = alive_neighbours(x, y, True)
                if neighbours < 2: # Starvation
                    new_grid[x][y] = False
                    changed = True
                elif neighbours > 3: # Overpopulation
                    new_grid[x][y] = False
                    changed = True
                elif not grid[x][y] and neighbours == 3: # Birth
                    new_grid[x][y] = True
                    changed = True
                else: # Survival
                    new_grid[x][y] = grid[x][y]
                    pass
        grid = new_grid
        return changed

    seed()
    generation = 0
    while direction != B:
      if direction == LEFT:
        if brightness > 3:
          brightness=brightness-1
          rgb.brightness(brightness)
          print(brightness)
          direction = 0
      if direction == RIGHT:
        if brightness < 32:
          brightness=brightness+1
          rgb.brightness(brightness)
          print(brightness)
          direction = 0
      generation += 1
      display()
      if not step() or generation > 50:
        seed()
        generation = 0
    system.reboot()
game_of_life()