'''
Conway's Game of Life
Hayden Walker, 22 August 2019

1. Live cell < 2 neighbours         --> dies
2. Live cell with 2-3 neighbours    --> lives to next generation
3. Live cell > 3 neighbours         --> dies from overcrowding
4. Dead cell with 3 live neighbours --> Becomes live (reproduction)
'''
import pygame
import random
import math


class Space:
	'''
	A space, which can be either living or dead
	'''
	def __init__(self, living, x, y):
		self.living = living
		self.x = x
		self.y = y
		self.neighbours = 0

	def draw(self):
		if self.living:
			pygame.draw.rect(win, (255, 255, 0), (self.x, self.y, 10, 10))
		else:
			pygame.draw.rect(win, (0, 0, 0), (self.x, self.y, 10, 10))

def startwin():
	'''
	Create the window based on specified size, and define the edges
	'''
	global redge
	global tedge
	global bedge
	global ledge
	global win

	# Use h/w to find # of creatures, # of rows/cols, and creatures per row
	creatures = (wh // 10) ** 2
	rowscols = int(math.sqrt(creatures))
	cperrow = wh // 10

	# Initialize the pygame window
	pygame.init()
	win = pygame.display.set_mode((wh, wh))
	pygame.display.set_caption("Life")

	# Define edges of screen
	redge = list(range(rowscols - 1, creatures, cperrow))
	ledge = list(range(0, creatures - cperrow - 1, cperrow))
	tedge = list(range(0, cperrow))
	bedge = list(range(creatures - cperrow - 1, creatures))

def genchars():
	'''
	Randomly generate living/dead squares
	'''
	global chars
	
	chars = list()

	for y in range(0, wh, 10):
		for x in range(0, wh, 10):
			chars.append(Space(random.randint(0, 1), x, y))

def drawchars():
	'''
	Draw all squares
	'''
	win.fill((255, 255, 255))
	for i in chars:
		i.draw()
	pygame.display.update()

def checkneighbours():
	'''
	Check how many living neighbours a square has
	'''
	for i in chars:
		neighbours = 0
		spot = chars.index(i)
		# To the left
		if (not (spot in ledge)) and (chars[spot - 1].living):
			neighbours += 1
		# To the right
		if (not (spot in redge)) and (chars[spot + 1].living):
			neighbours += 1
		# Below
		if (not (spot in bedge)) and (chars[spot + (wh // 10)].living):
			neighbours += 1
		# Above
		if (not(spot in tedge)) and (chars[spot - (wh // 10)].living):
			neighbours += 1
		# Diag. upper left
		if (not (spot in tedge)) and (not (spot in ledge)) and (chars[spot - ((wh // 10) + 1)].living):
			neighbours += 1
		# Diag. bottom right
		if (not (spot in bedge)) and (not (spot in redge)) and (chars[spot + ((wh // 10) + 1)].living):
			neighbours += 1
		# Diag. upper right
		if (not (spot in tedge)) and (not (spot in redge)) and (chars[spot - ((wh // 10) - 1)].living):
			neighbours += 1
		# Diag. bottom left
		if (not (spot in bedge)) and (not (spot in ledge)) and (chars[spot + ((wh // 10) - 1)].living):
			neighbours += 1

		i.neighbours = neighbours

def evolve():
	'''
	Either kill or generate living squares based on neighbours
	'''
	for i in chars:
		if i.living:
			if (i.neighbours < 2) or (i.neighbours > 3):
				i.living = False
		else:
			if i.neighbours == 3:
				i.living = True

def mainloop():
	'''
	The main loop
	'''
	while True:
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				quit()

		drawchars()
		checkneighbours()
		evolve()

		pygame.time.delay(100) # Time between generations

wh = 500 # Window width and height
startwin() # Initiate the window
genchars() # Generate the initial characters
mainloop() # Start the game loop