Skip to content

Instantly share code, notes, and snippets.

@lvidarte
Last active April 26, 2020 21:08
Show Gist options
  • Save lvidarte/f513bc94b31a76fb4f476ad4dfc134dd to your computer and use it in GitHub Desktop.
Save lvidarte/f513bc94b31a76fb4f476ad4dfc134dd to your computer and use it in GitHub Desktop.
#!/usr/bin/python3
"""
Author: Leo Vidarte <http://nerdlabs.com.ar>
This is free software,
you can redistribute it and/or modify it
under the terms of the GPL version 3
as published by the Free Software Foundation.
"""
"""
Las transiciones dependen del número de células vecinas vivas:
* Una célula muerta con exactamente 3 células vecinas vivas "nace"
(al turno siguiente estará viva).
* Una célula viva con 2 ó 3 células vecinas vivas sigue viva.
* En otro caso muere o permanece muerta (por "soledad" o "superpoblación").
"""
import tkinter as tk
class Life(tk.Frame):
def __init__(self, board, cell_size=20):
super(Life, self).__init__(tk.Tk())
self.master.title("Game of Life")
self.grid()
self.board = board
self.cell_size = cell_size
self.create_widgets()
self.draw_grid()
self.cells_alive = {}
self.create_events()
self.mainloop()
def create_widgets(self):
width, height = self.board.size()
kwargs = {
'width' : width * self.cell_size,
'height': height * self.cell_size,
'bg' : 'white',
}
self.canvas = tk.Canvas(self, **kwargs)
self.canvas.grid()
def draw_grid(self):
self.draw_vertical_lines()
self.draw_horizontal_lines()
def draw_vertical_lines(self, color='gray'):
width, height = self.board.size()
for i in range(width - 1):
x = (self.cell_size * i) + self.cell_size
y0 = 0
y1 = self.cell_size * height
self.canvas.create_line(x, y0, x, y1, fill=color)
def draw_horizontal_lines(self, color='gray'):
width, height = self.board.size()
for i in range(height - 1):
x0 = 0
x1 = self.cell_size * width
y = (self.cell_size * i) + self.cell_size
self.canvas.create_line(x0, y, x1, y, fill=color)
def draw_cell(self, x, y, color='black'):
x0 = x * self.cell_size
y0 = y * self.cell_size
x1 = x0 + self.cell_size
y1 = y0 + self.cell_size
args = (x0, y0, x1, y1)
_id = self.canvas.create_rectangle(*args, width=0, fill=color)
self.cells_alive[(x, y)] = _id
self.board.set_alive(x, y)
def del_cell(self, x, y):
self.canvas.delete(self.cells_alive[(x, y)])
del self.cells_alive[(x, y)]
self.board.set_dead(x, y)
def toggle_cell(self, event):
x = event.x // self.cell_size
y = event.y // self.cell_size
if (x, y) in self.cells_alive:
self.del_cell(x, y)
else:
self.draw_cell(x, y)
def create_events(self):
self.canvas.bind_all('<Button 1>', self.toggle_cell)
self.canvas.bind_all('<space>', self.tick)
def tick(self, event):
self.board.tick()
self.draw()
def draw(self):
self.clear()
for cell_pos in self.board.get_cells_alive():
self.draw_cell(*cell_pos)
def clear(self):
for _id in self.cells_alive.values():
self.canvas.delete(_id)
self.cells_alive = {}
class Board:
DEAD = 0
ALIVE = 1
def __init__(self, width=8, height=8):
self._width = width
self._height = height
self.set(self._get_empty_board_list())
def set(self, board_list):
self._board_list = [row[:] for row in board_list] # deep copy
self._width = len(board_list[0])
self._height = len(board_list)
def _get_empty_board_list(self):
return [[self.DEAD] * self._width for _ in range(self._height)]
def get(self):
return self._board_list
def size(self):
return (self._width, self._height)
def count_neighbors_alive(self, x, y):
x_center, y_center = x, y
x_left = x_center-1 if x_center-1 >= 0 else self._width-1
x_right = x_center+1 if x_center+1 < self._width else 0
y_up = y_center-1 if y_center-1 >= 0 else self._height-1
y_down = y_center+1 if y_center+1 < self._height else 0
return (self._board_list[y_up][x_left],
self._board_list[y_up][x_center],
self._board_list[y_up][x_right],
self._board_list[y_center][x_left],
self._board_list[y_center][x_right],
self._board_list[y_down][x_left],
self._board_list[y_down][x_center],
self._board_list[y_down][x_right]).count(self.ALIVE)
def get_next_cell_state(self, cell_state, neighbors_alive):
if (cell_state == self.DEAD and neighbors_alive == 3) or \
(cell_state == self.ALIVE and neighbors_alive in (2, 3)):
return self.ALIVE
else:
return self.DEAD
def tick(self):
board_list = self._get_empty_board_list()
for x in range(self._width):
for y in range(self._height):
cell_state = self._board_list[y][x]
neighbors_alive = self.count_neighbors_alive(x, y)
args = (cell_state, neighbors_alive)
board_list[y][x] = self.get_next_cell_state(*args)
self._board_list = board_list
def set_alive(self, x, y):
self._board_list[y][x] = self.ALIVE
def set_dead(self, x, y):
self._board_list[y][x] = self.DEAD
def get_cells_alive(self):
cells = []
for x in range(self._width):
for y in range(self._height):
if self._board_list[y][x] == self.ALIVE:
cells.append((x, y))
return cells
if __name__ == '__main__':
board = Board(width=20, height=20)
life = Life(board, cell_size=30)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment