### Author: Sam Delaney
### Description: Game of Life
### License: MIT
### Modified by Sebastius for the SHA2017 Badge. Fixed by polyfloyd.
### Modified by Renze for the ESP32 platform firmware

import system, buttons, display, urandom

# Attach all buttons to a reboot callback
def reboot(pressed):
	if pressed:
		system.launcher()

buttons.attach(buttons.BTN_RIGHT,  reboot)
buttons.attach(buttons.BTN_LEFT,   reboot)
buttons.attach(buttons.BTN_UP,     reboot)
buttons.attach(buttons.BTN_DOWN,   reboot)
buttons.attach(buttons.BTN_A,      reboot)
buttons.attach(buttons.BTN_B,      reboot)
buttons.attach(buttons.BTN_SELECT, reboot)
buttons.attach(buttons.BTN_START,  reboot)

# Clear the screen
display.drawFill(0xFFFFFF)
display.flush(display.FLAG_LUT_NORMAL)

width  = int(display.width()/8)
height = int(display.height()/8)
cell_width = 8
cell_height = 8

grid = [[False] * height for _ in range(width)]

def seed():
	global grid, width, height, cell_width, cell_height
	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 updateDisplay():
	global grid, width, height, cell_width, cell_height
	for x in range(0, width):
		for y in range(0, height):
			if grid[x][y]:
				display.drawRect(x * cell_width, y * cell_height, cell_width, cell_height, True, 0x000000)
			else:
				display.drawRect(x * cell_width, y * cell_height, cell_width, cell_height, True, 0xFFFFFF)
	display.flush(display.FLAG_LUT_NORMAL)

def alive_neighbours(x, y, wrap):
	global grid, width, height, cell_width, cell_height
	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, width, height, cell_width, cell_height
	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 True:
	generation += 1
	updateDisplay()
	if not step() or generation > 50:
		seed()
		generation = 0