Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Conway's Game of Life with Python and PySimpleGUI
import PySimpleGUI as sg
import random
import time
#from matplotlib.colors import hex2color
RESOLUTIONS = ["100x100", "200x200", "320x200", "400x400", "600x600", "640x400","800x600","1024x800", "1024x960","1280x1024"]
WIDTH, HEIGHT = 400,400
MIN_TILES = 10
MAX_TILES = 50
TX = 40
TY = 40
# -------- first gui: theme cooser --------------------------
sg.theme(random.choice(sg.theme_list()))
# ---------------------- theme selector layout -------------
layout1 = [
[
sg.Text("Select theme:"),
sg.Combo(
values=sg.theme_list(),
default_value=sg.theme(),
enable_events=True,
key="selector",
),
],
[sg.Text("delay in [ms]:"),
sg.Slider(range=(0,500), default_value=100,orientation="h", key="delay_ms", enable_events=False, size=(10,8)),],
[sg.Text("# x-tiles:"),sg.Slider(range=(MIN_TILES,MAX_TILES), default_value=10,orientation="h", key="x_slider", enable_events=True,)],
[sg.Text("# y-tiles:"),sg.Slider(range=(MIN_TILES,MAX_TILES), default_value=10,orientation="h", key="y_slider", enable_events=True)],
[sg.Text("10 x 10 = 100 tiles", key="tiles")],
[sg.Text("canvas resolution in Pixel:"),
sg.Combo(values=RESOLUTIONS, key="resolution", default_value="400x400", enable_events=True)],
#[sg.Text("Pause in [ms]:"),sg.Slider(range=(0,1000), default_value=500,orientation="h", key="pause_slider")],
[sg.Text("tile size in pixel:"), sg.Text("40 x 40",key="tile_size")],
[sg.Button("play the game")],
]
window1 = sg.Window("theme selector", layout1)
# ---------------- main loop for first layout ---------------
while True:
event1, values1 = window1.read()
if event1 == "play the game" or event1 == sg.WIN_CLOSED:
# sg.theme("DarkBlue3")
break
if event1 == "selector":
old = sg.theme()
sg.theme(values1["selector"])
if sg.PopupYesNo("do you like this Theme?") == "Yes":
break
else:
sg.theme(old)
if event1 in ("x_slider", "y_slider"):
window1["tiles"].update(f'{int(values1["x_slider"])} x {int(values1["y_slider"])} = {int(values1["x_slider"])*int(values1["y_slider"])} tiles')
if event1 in ("x_slider", "y_slider", "resolution"):
WIDTH, HEIGHT = [int(v) for v in values1["resolution"].split("x")]
TX = WIDTH / int(values1["x_slider"])
TY = HEIGHT / int(values1["y_slider"])
tile_size_string = ""
if TX == int(TX):
tile_size_string += f"{TX:.0f} x "
else:
tile_size_string += f"{TX:.1f} x "
if TY == int(TY):
tile_size_string += f"{TY:.0f}"
else:
tile_size_string += f"{TY:.1f}"
window1["tile_size"].update(tile_size_string)
WIDTH, HEIGHT = [int(v) for v in values1["resolution"].split("x")]
X_TILES = int(values1["x_slider"])
Y_TILES = int(values1["y_slider"])
DELAY = values1["delay_ms"]
window1.close()
# -----------------end of theme chooser -------------------
# --------------- start of main program ------------------
class Game:
number = 0
figures = []
lines = []
lines2 = []
bgcolor = "#FFFFFF"
fgcolor = "#FF00FF"
playing = False
next_calculation = 0
REBORN_MIN = 3
REBORN_MAX = 3
SURVIVE_MIN = 2
SURVIVE_MAX = 3
HELPTEXT = ""
turn = 0
def get_neighbors(x,y):
n = {} # empty dict
for dy in (-1,0,1):
for dx in (-1,0,1):
if dx==dy==0:
continue
my = y+dy
mx = x+dx
if not values2["wrap"]:
if (mx < 0) or (mx == X_TILES) or (my < 0) or (my == Y_TILES):
continue
else:
if mx == X_TILES:
mx = 0
if my == Y_TILES:
my = 0
#print("mx,my:",mx, my)
color = Game.lines[my][mx]
if color is None:
continue
how_often = n.get(color, 0) # if color is not in dictionary n, return 0
#print("how_often", color, how_often)
how_often += 1
n[color] = how_often
#print(n)
return n
def count_the_colors():
"""returns a dict with color_name and occurence for each color"""
d = {}
for line in Game.lines:
for color in line:
if color is None:
continue
how_often = d.get(color,0)
how_often += 1
d[color] = how_often
return d
def is_free(x,y):
"""check if there is a None in Game.lines[y][x]"""
if x < 0 or y < 0:
return False
try:
result = Game.lines[y][x]
except IndexError:
return False
if Game.lines[y][x] is None:
return True
return False
def create_headers():
"""write row/column number into the two canvases 'headrow' and 'headcol'."""
for y in range(0, Y_TILES):
window2["headcol"].draw_text(
text=f"{y:02}",
location=(10,y+0.5),
color = "white",
font = ("Arial", 8 ),
angle = 0,
text_location = sg.TEXT_LOCATION_CENTER)
for x in range(0, X_TILES):
window2["headrow"].draw_text(
text=f"{x:02}",
location=(x+0.5,10),
color = "white",
font = ("Arial", 8 ),
angle = 0,
text_location = sg.TEXT_LOCATION_CENTER)
def show_grid():
gridcolor = "#646464"
for y in range(0, Y_TILES):
Game.grid.append(c.draw_line(point_from=(0,y),
point_to=(X_TILES, y),
color=gridcolor,
width=1))
for x in range(0, X_TILES):
Game.grid.append(c.draw_line(point_from=(x,0),
point_to=(x, Y_TILES),
color=gridcolor,
width=1))
def hide_grid():
for f in Game.grid:
c.delete_figure(f)
Game.grid = []
def create_blocks():
"""create random numbers in array Game.lines[]"""
#step = 256*256*25*//values2["randomcolors"]
step = 32
colors = []
for r in range(0,256, step):
for g in range(0,256, step):
for b in range(0,256,step):
colors.append(f"#{r:02X}{g:02X}{b:02X}")
random.shuffle(colors)
colors = colors[0:values2["randomcolors"]]
for y, line in enumerate(Game.lines):
for x, number in enumerate(line):
if random.random() < values2["noblock"]:
new_value = None
continue
else:
new_value = random.choice(colors)
#print(x,y,"old value:", lines[y][x], "new value:", new_value)
Game.lines[y][x]= new_value
def delete_old_figures():
"""delete all figures from canvas (except player and cursor)"""
for f in Game.figures:
c.delete_figure(f)
Game.figures = []
def update_gui():
"""run this after changing blocks/cells"""
window2["figures_count"].update(f'# of figures: {len(Game.figures)}')
if len([Game.figures]) > 0:
d = count_the_colors()
window2["cellcounter"].update(f"turn: {Game.turn}\n# of cells:\n"+"\n".join([str(line) for line in d.items()]))
def delete_old_blocks():
"""delete all old blocks, fill Game.lines with [None, None,...] """
Game.lines = [[None for x in range(X_TILES)] for y in range(Y_TILES)]
#for f in Game.figures:
# c.delete_figure(f)
#Game.figures = []
def calculate_new_cells():
# ---- calculate new cells ----
Game.turn += 1
Game.lines2 = [[None for x in range(X_TILES)] for y in range(Y_TILES)]
for y, line in enumerate(Game.lines):
for x, color in enumerate(line):
result=[]
if color is None:
# reborn ?
crit_min = Game.REBORN_MIN
crit_max = Game.REBORN_MAX
else:
# survive?
crit_min = Game.SURVIVE_MIN
crit_max = Game.SURVIVE_MAX
# get the number of neighbors
ndict = get_neighbors(x,y)
#print("ndict:", ndict)
for ncolor, namount in ndict.items():
if crit_min <= namount <= crit_max:
result.append(ncolor)
if len(result)>0:
Game.lines2[y][x] = random.choice(result)
# make new generation
delete_old_blocks()
delete_old_figures()
for y, line in enumerate(Game.lines2):
for x, color in enumerate(line):
Game.lines[y][x] = color
paint_blocks()
def update_helptext():
Game.REBORN_MIN = values2["reborn_min"]
Game.REBORN_MAX = values2["reborn_max"]
Game.SURVIVE_MIN = values2["survive_min"]
Game.SURVIVE_MAX = values2["survive_max"]
Game.HELPTEXT = f"""Game of life rules:
A living cell survive if it has between {Game.SURVIVE_MIN} and {Game.SURVIVE_MAX} neighbors.
A dead cell is reborn if it has between {Game.REBORN_MIN} and {Game.REBORN_MAX} neighbors.
"""
def paint_blocks():
"""create colorful blocks, depending on the value in lines
adds the Tkinter figure into the list figures"""
#print(lines)
for y, line in enumerate(Game.lines):
for x, number in enumerate(line):
if number is None:
#color = Game.bgcolor
continue
# do not paint black, just leave it
color = Game.lines[y][x]
Game.figures.append(c.draw_rectangle(top_left=(x,y),
bottom_right=(x+1,y+1),
fill_color = color))
layout2 = [
[ sg.Text("Cursor (x,y):"),
sg.Text("mouse xy",key="mousepos"),
sg.Text("Color:"),
sg.Text("#??????", key="mousecolor", size=(8,1)),
sg.Checkbox("show grid", default=True, key="grid", enable_events=True),
sg.Text("# of figures: ???", key="figures_count" ),
sg.Text("# of neighbors:"),
sg.Text("?", size=(20,1), key="neighbors"),
],
[#sg.Text("show:"),
#sg.Radio("instructions", group_id="showgroup", default=True, enable_events=True, key="instructions"),
#sg.Radio("lines", group_id="showgroup", default=False,enable_events=True, key="lines"),
#sg.Radio("figures", group_id="showgroup", default=False, enable_events=True, key="figures"),
sg.Text("toggle with SPACE:"),
sg.Radio("paint", group_id="modus", default=True, key="paint"),
sg.Radio("erase", group_id="modus", default=False,key="erase", ),
sg.Button("fg color:", key="button_fg", button_color="#FF00FF"),
sg.Text("#FF00FF", key="fg_color", size=(8,1)),
sg.Button("bg color:", key="button_bg", button_color="#FFFFFF"),
sg.Text("#FFFFFF", key="bg_color", size=(8,1), ),
],
[sg.Text("Game of life rules:"),
sg.Checkbox("wrap-around", default=True, key="wrap", enable_events=False),
sg.Button("Help")],
[sg.Text("For [new blocks]: # of random colors:"),
sg.Combo([1,2,3,4,8,16,32,64,128,256,512], default_value=1, key="randomcolors"),
sg.Text("Chance for No Block (1=100%):"),
sg.Combo(values=[0,0.1,0.125,0.2,0.25,0.3,0.33,0.4,0.5,0.6,0.75,0.8,0.85,0.9,0.95], default_value=0.8, key="noblock")],
[sg.Text("# of neighbors for surviving: minimal:"),
#sg.Slider(range=(0,8), default_value=2,
# orientation="h", key="survive_min", enable_events=True, size=(10,8)),
sg.Combo(values=[0,1,2,3,4,5,6,7,8], default_value=2, enable_events=True, key="survive_min"),
sg.Text("maximal:", ),
sg.Combo(values=[0,1,2,3,4,5,6,7,8], default_value=3, key="survive_max", enable_events=True, )],
[sg.Text("# of neighbors for reborn: minimal:"),
sg.Combo(values=[0,1,2,3,4,5,6,7,8], default_value=3, key="reborn_min", enable_events=True,),
sg.Text("maximal:"),
sg.Combo(values=[0,1,2,3,4,5,6,7,8], default_value=3, key="reborn_max", enable_events=True,)],
# sg.Checkbox("show grid", default=False, change_submits=True, key="show_grid")],
#[sg.Multiline(default_text=HELPTEXT, size=(90,6), disabled=True, key="multi"),],
[sg.Button("new blocks"),sg.Button("delete all"), sg.Button("lines"), sg.Button("one step"), sg.Button("start/stop", key="play", size=(18,0)),sg.Button("Exit"), ],
[sg.Sizer(30),
sg.Graph(canvas_size=(WIDTH, 20), graph_bottom_left=(0,20), graph_top_right=(X_TILES, 0), key="headrow",
background_color = "#333333"), sg.Text("\u2192 x"),],
[sg.Graph(canvas_size=(20, HEIGHT), graph_bottom_left=(0, Y_TILES), graph_top_right=(20,0), key="headcol",
background_color="#333333"),
sg.Graph(canvas_size=(WIDTH,HEIGHT),
key="canvas", graph_bottom_left=(0, Y_TILES),
graph_top_right=(X_TILES,0 ),
background_color="#FFFFFF",
enable_events=True,
drag_submits=True,
motion_events=True,),
sg.Multiline(default_text="number of cells:\n\n\n", key="cellcounter", disabled=True, size=(20, HEIGHT/15),),
# sg.Graph(canvas_size=(X_TILES, Y_TILES), graph_bottom_left=(0,Y_TILES), graph_top_right=(X_TILES,0),
# key="preview", background_color = "#333333", ),
],
[sg.Text("\u2193\ny", size=(1,2))],
]
window2 = sg.Window("enjoy the game", layout2, return_keyboard_events=True)
window2.finalize() # necessary to be able to draw on canvas
#update_helptext()
c = window2["canvas"]
# ----- create labyrinth -----
# fill labyrinth with zeros
Game.lines = [[None for x in range(X_TILES)] for y in range(Y_TILES)]
Game.figures = [] # for Tkinter
Game.grid = []
Game.HELPTEXT = """Game of life rules:
A living cell survive if it has between 2 and 2 neighbors.
A dead cell is reborn if it has between 3 and 3 neighbors."""
show_grid()
create_headers()
#delete_old_blocks()
#create_blocks()
#paint_blocks()
# ------------- main loop -----------
#duration = 60
#player_pos_x = 0
#player_pos_y = 0
cursor_pos_x = 0
cursor_pos_y = 0
#figures.append(make_trail())
#player = c.draw_rectangle(top_left=(0,0), bottom_right=(1,1), fill_color="#CCCCCC")
cursor = c.draw_rectangle(top_left=(0,0), bottom_right=(1,1), line_width=5, line_color="#D1D1D1", fill_color=None)
#goal = c.draw_rectangle(top_left=(X_TILES-1,Y_TILES-1), bottom_right=(X_TILES, Y_TILES), fill_color="yellow")
#end_time = time.time() + duration
while True:
event2, values2 = window2.read(timeout=DELAY)
#print(event2, values2)
# print all values except the multiline field
print("Event:",event2, "Values:", {k:v for k,v in values2.items()})
# ---- window close event or Exit button ----
if event2 in ("Exit", sg.WINDOW_CLOSED ):
break
if event2.startswith("space"): #space:65
if values2["erase"]:
window2["erase"].update(False)
window2["paint"].update(True)
else:
window2["erase"].update(True)
window2["paint"].update(False)
# --- toggle grid ---
if event2 == "grid":
if values2["grid"]:
show_grid()
else:
hide_grid()
# ----- play ----
if event2 == "play":
Game.playing = not Game.playing
if Game.playing:
window2["play"].update("stop simulation")
#Game.next_calculation = time.time() + values2["delay_ms"]/1000
else:
window2["play"].update("start simulation")
#------- timeout event ---------
if event2 == sg.TIMEOUT_EVENT:
now = time.time()
if Game.playing:
#print(now, Game.next_calculation)
#if now > Game.next_calculation:
# Game.next_calculation = now + values2["delay_ms"]/1000
calculate_new_cells()
update_gui()
if event2=="one step":
calculate_new_cells()
update_gui()
if event2.startswith("survive") or event2.startswith("reborn"):
update_helptext()
if event2 == "Help":
sg.PopupScrolled(Game.HELPTEXT, title="Game of life:")
# ------- show lines -----
if event2 == "lines":
sg.PopupScrolled("\n".join([str(line) for line in Game.lines]))
#----- color choosen ----
if event2 == "button_fg":
# askcolor gives back a tuple like: ((140, 0, 255), '#8c00ff')
new_color = sg.askcolor(Game.fgcolor)[1]
if new_color is None:
continue
new_color = new_color.upper()
#print("new color=", new_color)
window2["button_fg"].update(button_color=new_color)
window2["fg_color"].update(new_color)
Game.fgcolor = new_color
if event2 == "button_bg":
new_color = sg.askcolor(Game.fgcolor)[1]
if new_color is None:
continue
new_color = new_color.upper()
#print("new color=", new_color)
window2["button_bg"].update(button_color=new_color)
window2["bg_color"].update(new_color)
Game.bgcolor = new_color
window2["canvas"].update(background_color=new_color)
if event2 == "new blocks":
#delete_old_blocks()
delete_old_blocks()
delete_old_figures()
create_blocks()
paint_blocks()
#c.bring_figure_to_front(player)
Game.turns = 0
update_gui()
if event2 == "delete all":
delete_old_blocks()
delete_old_figures()
Game.turns = 0
update_gui()
# mouse movement event
if event2.endswith("+MOVE"):
mousepos = values2["canvas"]
window2["mousepos"].update(mousepos)
#print("mousepos:", mousepos)
cursor_pos_x, cursor_pos_y = mousepos
window2["mousecolor"].update(str(Game.lines[cursor_pos_y][cursor_pos_x]))
c.relocate_figure(cursor, cursor_pos_x, cursor_pos_y)
c.bring_figure_to_front(cursor)
# get neighbors
window2["neighbors"].update(get_neighbors(cursor_pos_x, cursor_pos_y))
# mouse click down event
if event2 == "canvas":
mousepos = values2["canvas"]
window2["mousepos"].update(mousepos)
#print("mousepos:", mousepos)
cursor_pos_x, cursor_pos_y = mousepos
if not ( 0 <= cursor_pos_x < X_TILES):
continue
if not ( 0 <= cursor_pos_y < Y_TILES):
continue
print("cursor:", cursor_pos_x, cursor_pos_y, X_TILES, Y_TILES)
window2["mousecolor"].update(str(Game.lines[cursor_pos_y][cursor_pos_x]))
c.relocate_figure(cursor, cursor_pos_x, cursor_pos_y)
c.bring_figure_to_front(cursor)
print("mouseclick down at: ", mousepos)
actual_color = Game.lines[cursor_pos_y][cursor_pos_x]
#print("paint, erase", values2["paint"], values2["erase"])
if values2["paint"]:
if actual_color != Game.fgcolor:
Game.lines[cursor_pos_y][cursor_pos_x] = Game.fgcolor
#delete_old_blocks()
delete_old_figures()
paint_blocks()
if values2["erase"]:
if actual_color is not None:
Game.lines[cursor_pos_y][cursor_pos_x] = None
delete_old_figures()
paint_blocks()
update_gui()
# mouse click up event
if event2 == "canvas+UP":
mousepos = values2["canvas"]
print("mouseclick up at: ", mousepos)
##if event2.endswith('+RIGHT+'): # Righ click
## print("mouseclick right at ", values2["canvas"])
window2.close()
print("bye")
import PySimpleGUI as sg
import random
import time
from PIL import Image
import numpy
#from matplotlib.colors import hex2color
RESOLUTIONS = ["100x100", "200x200", "320x200", "400x400", "600x600", "640x400","800x600","1024x800", "1024x960","1280x1024"]
WIDTH, HEIGHT = 400,400
MIN_TILES = 10
MAX_TILES = 50
TX = 40
TY = 40
# -------- first gui: theme cooser --------------------------
sg.theme(random.choice(sg.theme_list()))
# ---------------------- theme selector layout -------------
layout1 = [
[
sg.Text("Select theme:"),
sg.Combo(
values=sg.theme_list(),
default_value=sg.theme(),
enable_events=True,
key="selector",
),
],
[sg.Text("delay in [ms]:"),
sg.Slider(range=(0,500), default_value=100,orientation="h", key="delay_ms", enable_events=False, size=(10,8)),],
[sg.Text("# x-tiles:"),sg.Slider(range=(MIN_TILES,MAX_TILES), default_value=10,orientation="h", key="x_slider", enable_events=True,)],
[sg.Text("# y-tiles:"),sg.Slider(range=(MIN_TILES,MAX_TILES), default_value=10,orientation="h", key="y_slider", enable_events=True)],
[sg.Text("10 x 10 = 100 tiles", key="tiles")],
[sg.Text("canvas resolution in Pixel:"),
sg.Combo(values=RESOLUTIONS, key="resolution", default_value="400x400", enable_events=True)],
#[sg.Text("Pause in [ms]:"),sg.Slider(range=(0,1000), default_value=500,orientation="h", key="pause_slider")],
[sg.Text("tile size in pixel:"), sg.Text("40 x 40",key="tile_size")],
[sg.Button("play the game")],
]
window1 = sg.Window("theme selector", layout1)
# ---------------- main loop for first layout ---------------
while True:
event1, values1 = window1.read()
if event1 == "play the game" or event1 == sg.WIN_CLOSED:
# sg.theme("DarkBlue3")
break
if event1 == "selector":
old = sg.theme()
sg.theme(values1["selector"])
if sg.PopupYesNo("do you like this Theme?") == "Yes":
break
else:
sg.theme(old)
if event1 in ("x_slider", "y_slider"):
window1["tiles"].update(f'{int(values1["x_slider"])} x {int(values1["y_slider"])} = {int(values1["x_slider"])*int(values1["y_slider"])} tiles')
if event1 in ("x_slider", "y_slider", "resolution"):
WIDTH, HEIGHT = [int(v) for v in values1["resolution"].split("x")]
TX = WIDTH / int(values1["x_slider"])
TY = HEIGHT / int(values1["y_slider"])
tile_size_string = ""
if TX == int(TX):
tile_size_string += f"{TX:.0f} x "
else:
tile_size_string += f"{TX:.1f} x "
if TY == int(TY):
tile_size_string += f"{TY:.0f}"
else:
tile_size_string += f"{TY:.1f}"
window1["tile_size"].update(tile_size_string)
WIDTH, HEIGHT = [int(v) for v in values1["resolution"].split("x")]
X_TILES = int(values1["x_slider"])
Y_TILES = int(values1["y_slider"])
DELAY = values1["delay_ms"]
window1.close()
# -----------------end of theme chooser -------------------
# --------------- start of main program ------------------
class Game:
number = 0
figures = []
lines = []
lines2 = []
bgcolor = "#FFFFFF"
fgcolor = "#FF00FF"
playing = False
next_calculation = 0
REBORN_MIN = 3
REBORN_MAX = 3
SURVIVE_MIN = 2
SURVIVE_MAX = 3
HELPTEXT = ""
turn = 0
def get_neighbors(x,y):
n = {} # empty dict
for dy in (-1,0,1):
for dx in (-1,0,1):
if dx==dy==0:
continue
my = y+dy
mx = x+dx
if not values2["wrap"]:
if (mx < 0) or (mx == X_TILES) or (my < 0) or (my == Y_TILES):
continue
else:
if mx == X_TILES:
mx = 0
if my == Y_TILES:
my = 0
#print("mx,my:",mx, my)
color = Game.lines[my][mx]
if color is None:
continue
how_often = n.get(color, 0) # if color is not in dictionary n, return 0
#print("how_often", color, how_often)
how_often += 1
n[color] = how_often
#print(n)
return n
def count_the_colors():
"""returns a dict with color_name and occurence for each color"""
d = {}
for line in Game.lines:
for color in line:
if color is None:
continue
how_often = d.get(color,0)
how_often += 1
d[color] = how_often
return d
def is_free(x,y):
"""check if there is a None in Game.lines[y][x]"""
if x < 0 or y < 0:
return False
try:
result = Game.lines[y][x]
except IndexError:
return False
if Game.lines[y][x] is None:
return True
return False
def create_headers():
"""write row/column number into the two canvases 'headrow' and 'headcol'."""
for y in range(0, Y_TILES):
window2["headcol"].draw_text(
text=f"{y:02}",
location=(10,y+0.5),
color = "white",
font = ("Arial", 8 ),
angle = 0,
text_location = sg.TEXT_LOCATION_CENTER)
for x in range(0, X_TILES):
window2["headrow"].draw_text(
text=f"{x:02}",
location=(x+0.5,10),
color = "white",
font = ("Arial", 8 ),
angle = 0,
text_location = sg.TEXT_LOCATION_CENTER)
def show_grid():
gridcolor = "#646464"
for y in range(0, Y_TILES):
Game.grid.append(c.draw_line(point_from=(0,y),
point_to=(X_TILES, y),
color=gridcolor,
width=1))
for x in range(0, X_TILES):
Game.grid.append(c.draw_line(point_from=(x,0),
point_to=(x, Y_TILES),
color=gridcolor,
width=1))
def hide_grid():
for f in Game.grid:
c.delete_figure(f)
Game.grid = []
def create_blocks():
"""create random numbers in array Game.lines[]"""
#step = 256*256*25*//values2["randomcolors"]
step = 32
colors = []
for r in range(0,256, step):
for g in range(0,256, step):
for b in range(0,256,step):
colors.append(f"#{r:02X}{g:02X}{b:02X}")
random.shuffle(colors)
colors = colors[0:values2["randomcolors"]]
for y, line in enumerate(Game.lines):
for x, number in enumerate(line):
if random.random() < values2["noblock"]:
new_value = None
continue
else:
new_value = random.choice(colors)
#print(x,y,"old value:", lines[y][x], "new value:", new_value)
Game.lines[y][x]= new_value
def delete_old_figures():
"""delete all figures from canvas (except player and cursor)"""
for f in Game.figures:
c.delete_figure(f)
Game.figures = []
def update_gui():
"""run this after changing blocks/cells"""
window2["figures_count"].update(f'# of figures: {len(Game.figures)}')
if len([Game.figures]) > 0:
d = count_the_colors()
window2["cellcounter"].update(f"turn: {Game.turn}\n# of cells:\n"+"\n".join([str(line) for line in d.items()]))
def delete_old_blocks():
"""delete all old blocks, fill Game.lines with [None, None,...] """
Game.lines = [[None for x in range(X_TILES)] for y in range(Y_TILES)]
#for f in Game.figures:
# c.delete_figure(f)
#Game.figures = []
def calculate_new_cells():
# ---- calculate new cells ----
Game.turn += 1
Game.lines2 = [[None for x in range(X_TILES)] for y in range(Y_TILES)]
for y, line in enumerate(Game.lines):
for x, color in enumerate(line):
result=[]
if color is None:
# reborn ?
crit_min = Game.REBORN_MIN
crit_max = Game.REBORN_MAX
else:
# survive?
crit_min = Game.SURVIVE_MIN
crit_max = Game.SURVIVE_MAX
# get the number of neighbors
ndict = get_neighbors(x,y)
#print("ndict:", ndict)
for ncolor, namount in ndict.items():
if crit_min <= namount <= crit_max:
result.append(ncolor)
if len(result)>0:
Game.lines2[y][x] = random.choice(result)
# make new generation
delete_old_blocks()
delete_old_figures()
for y, line in enumerate(Game.lines2):
for x, color in enumerate(line):
Game.lines[y][x] = color
paint_blocks()
def update_helptext():
Game.REBORN_MIN = values2["reborn_min"]
Game.REBORN_MAX = values2["reborn_max"]
Game.SURVIVE_MIN = values2["survive_min"]
Game.SURVIVE_MAX = values2["survive_max"]
Game.HELPTEXT = f"""Game of life rules:
A living cell survive if it has between {Game.SURVIVE_MIN} and {Game.SURVIVE_MAX} neighbors.
A dead cell is reborn if it has between {Game.REBORN_MIN} and {Game.REBORN_MAX} neighbors.
"""
def paint_blocks():
"""create colorful blocks, depending on the value in lines
adds the Tkinter figure into the list figures"""
#print(lines)
for y, line in enumerate(Game.lines):
for x, number in enumerate(line):
if number is None:
#color = Game.bgcolor
continue
# do not paint black, just leave it
color = Game.lines[y][x]
Game.figures.append(c.draw_rectangle(top_left=(x,y),
bottom_right=(x+1,y+1),
fill_color = color))
layout2 = [
[ sg.Text("Cursor (x,y):"),
sg.Text("mouse xy",key="mousepos"),
sg.Text("Color:"),
sg.Text("#??????", key="mousecolor", size=(8,1)),
sg.Checkbox("show grid", default=True, key="grid", enable_events=True),
sg.Text("# of figures: ???", key="figures_count" ),
sg.Text("# of neighbors:"),
sg.Text("?", size=(20,1), key="neighbors"),
],
[#sg.Text("show:"),
#sg.Radio("instructions", group_id="showgroup", default=True, enable_events=True, key="instructions"),
#sg.Radio("lines", group_id="showgroup", default=False,enable_events=True, key="lines"),
#sg.Radio("figures", group_id="showgroup", default=False, enable_events=True, key="figures"),
sg.Text("toggle with SPACE:"),
sg.Radio("paint", group_id="modus", default=True, key="paint"),
sg.Radio("erase", group_id="modus", default=False,key="erase", ),
sg.Button("fg color:", key="button_fg", button_color="#FF00FF"),
sg.Text("#FF00FF", key="fg_color", size=(8,1)),
sg.Button("bg color:", key="button_bg", button_color="#FFFFFF"),
sg.Text("#FFFFFF", key="bg_color", size=(8,1), ),
],
[sg.Text("Game of life rules:"),
sg.Checkbox("wrap-around", default=True, key="wrap", enable_events=False),
sg.Button("Help"), sg.Text("Image:"),sg.Button("save")],
[sg.Text("For [new blocks]: # of random colors:"),
sg.Combo([1,2,3,4,8,16,32,64,128,256,512], default_value=1, key="randomcolors"),
sg.Text("Chance for No Block (1=100%):"),
sg.Combo(values=[0,0.1,0.125,0.2,0.25,0.3,0.33,0.4,0.5,0.6,0.75,0.8,0.85,0.9,0.95], default_value=0.8, key="noblock")],
[sg.Text("# of neighbors for surviving: minimal:"),
#sg.Slider(range=(0,8), default_value=2,
# orientation="h", key="survive_min", enable_events=True, size=(10,8)),
sg.Combo(values=[0,1,2,3,4,5,6,7,8], default_value=2, enable_events=True, key="survive_min"),
sg.Text("maximal:", ),
sg.Combo(values=[0,1,2,3,4,5,6,7,8], default_value=3, key="survive_max", enable_events=True, )],
[sg.Text("# of neighbors for reborn: minimal:"),
sg.Combo(values=[0,1,2,3,4,5,6,7,8], default_value=3, key="reborn_min", enable_events=True,),
sg.Text("maximal:"),
sg.Combo(values=[0,1,2,3,4,5,6,7,8], default_value=3, key="reborn_max", enable_events=True,)],
# sg.Checkbox("show grid", default=False, change_submits=True, key="show_grid")],
#[sg.Multiline(default_text=HELPTEXT, size=(90,6), disabled=True, key="multi"),],
[sg.Button("new blocks"),sg.Button("delete all"), sg.Button("lines"), sg.Button("one step"), sg.Button("start/stop", key="play", size=(18,0)),sg.Button("Exit"), ],
[sg.Sizer(30),
sg.Graph(canvas_size=(WIDTH, 20), graph_bottom_left=(0,20), graph_top_right=(X_TILES, 0), key="headrow",
background_color = "#333333"), sg.Text("\u2192 x"),],
[sg.Graph(canvas_size=(20, HEIGHT), graph_bottom_left=(0, Y_TILES), graph_top_right=(20,0), key="headcol",
background_color="#333333"),
sg.Graph(canvas_size=(WIDTH,HEIGHT),
key="canvas", graph_bottom_left=(0, Y_TILES),
graph_top_right=(X_TILES,0 ),
background_color="#FFFFFF",
enable_events=True,
drag_submits=True,
motion_events=True,),
sg.Multiline(default_text="number of cells:\n\n\n", key="cellcounter", disabled=True, size=(20, HEIGHT/15),),
# sg.Graph(canvas_size=(X_TILES, Y_TILES), graph_bottom_left=(0,Y_TILES), graph_top_right=(X_TILES,0),
# key="preview", background_color = "#333333", ),
],
[sg.Text("\u2193\ny", size=(1,2))],
]
window2 = sg.Window("enjoy the game", layout2, return_keyboard_events=True)
window2.finalize() # necessary to be able to draw on canvas
#update_helptext()
c = window2["canvas"]
# ----- create labyrinth -----
# fill labyrinth with zeros
Game.lines = [[None for x in range(X_TILES)] for y in range(Y_TILES)]
Game.figures = [] # for Tkinter
Game.grid = []
Game.HELPTEXT = """Game of life rules:
A living cell survive if it has between 2 and 2 neighbors.
A dead cell is reborn if it has between 3 and 3 neighbors."""
show_grid()
create_headers()
#delete_old_blocks()
#create_blocks()
#paint_blocks()
# ------------- main loop -----------
#duration = 60
#player_pos_x = 0
#player_pos_y = 0
cursor_pos_x = 0
cursor_pos_y = 0
#figures.append(make_trail())
#player = c.draw_rectangle(top_left=(0,0), bottom_right=(1,1), fill_color="#CCCCCC")
cursor = c.draw_rectangle(top_left=(0,0), bottom_right=(1,1), line_width=5, line_color="#D1D1D1", fill_color=None)
#goal = c.draw_rectangle(top_left=(X_TILES-1,Y_TILES-1), bottom_right=(X_TILES, Y_TILES), fill_color="yellow")
#end_time = time.time() + duration
while True:
event2, values2 = window2.read(timeout=DELAY)
#print(event2, values2)
# print all values except the multiline field
print("Event:",event2, "Values:", {k:v for k,v in values2.items()})
# ---- window close event or Exit button ----
if event2 in ("Exit", sg.WINDOW_CLOSED ):
break
if event2.startswith("space"): #space:65
if values2["erase"]:
window2["erase"].update(False)
window2["paint"].update(True)
else:
window2["erase"].update(True)
window2["paint"].update(False)
# --- save image ---
if event2 == "save":
print("lines:",Game.lines)
# convert #RRGGBB into R,G,B
image_array = numpy.zeros((X_TILES, Y_TILES, 3), dtype=numpy.uint8)
for y, line in enumerate(Game.lines):
new_line = []
for x, hexcolor in enumerate(line):
if hexcolor is None:
color = (0,0,0)
else:
color = [int(hexcolor[1:3], base=16),
int(hexcolor[3:5], base=16),
int(hexcolor[5:7], base=16),]
#new_line.append(color)
image_array[y,x] = color
img = Image.fromarray(image_array,"RGB")
img.save("testbild.png")
print("image_array done", image_array)
break
# --- toggle grid ---
if event2 == "grid":
if values2["grid"]:
show_grid()
else:
hide_grid()
# ----- play ----
if event2 == "play":
Game.playing = not Game.playing
if Game.playing:
window2["play"].update("stop simulation")
#Game.next_calculation = time.time() + values2["delay_ms"]/1000
else:
window2["play"].update("start simulation")
#------- timeout event ---------
if event2 == sg.TIMEOUT_EVENT:
now = time.time()
if Game.playing:
#print(now, Game.next_calculation)
#if now > Game.next_calculation:
# Game.next_calculation = now + values2["delay_ms"]/1000
calculate_new_cells()
update_gui()
if event2=="one step":
calculate_new_cells()
update_gui()
if event2.startswith("survive") or event2.startswith("reborn"):
update_helptext()
if event2 == "Help":
sg.PopupScrolled(Game.HELPTEXT, title="Game of life:")
# ------- show lines -----
if event2 == "lines":
sg.PopupScrolled("\n".join([str(line) for line in Game.lines]))
#----- color choosen ----
if event2 == "button_fg":
# askcolor gives back a tuple like: ((140, 0, 255), '#8c00ff')
new_color = sg.askcolor(Game.fgcolor)[1]
if new_color is None:
continue
new_color = new_color.upper()
#print("new color=", new_color)
window2["button_fg"].update(button_color=new_color)
window2["fg_color"].update(new_color)
Game.fgcolor = new_color
if event2 == "button_bg":
new_color = sg.askcolor(Game.fgcolor)[1]
if new_color is None:
continue
new_color = new_color.upper()
#print("new color=", new_color)
window2["button_bg"].update(button_color=new_color)
window2["bg_color"].update(new_color)
Game.bgcolor = new_color
window2["canvas"].update(background_color=new_color)
if event2 == "new blocks":
#delete_old_blocks()
delete_old_blocks()
delete_old_figures()
create_blocks()
paint_blocks()
#c.bring_figure_to_front(player)
Game.turns = 0
update_gui()
if event2 == "delete all":
delete_old_blocks()
delete_old_figures()
Game.turns = 0
update_gui()
# mouse movement event
if event2.endswith("+MOVE"):
mousepos = values2["canvas"]
window2["mousepos"].update(mousepos)
#print("mousepos:", mousepos)
cursor_pos_x, cursor_pos_y = mousepos
window2["mousecolor"].update(str(Game.lines[cursor_pos_y][cursor_pos_x]))
c.relocate_figure(cursor, cursor_pos_x, cursor_pos_y)
c.bring_figure_to_front(cursor)
# get neighbors
window2["neighbors"].update(get_neighbors(cursor_pos_x, cursor_pos_y))
# mouse click down event
if event2 == "canvas":
mousepos = values2["canvas"]
window2["mousepos"].update(mousepos)
#print("mousepos:", mousepos)
cursor_pos_x, cursor_pos_y = mousepos
if not ( 0 <= cursor_pos_x < X_TILES):
continue
if not ( 0 <= cursor_pos_y < Y_TILES):
continue
print("cursor:", cursor_pos_x, cursor_pos_y, X_TILES, Y_TILES)
window2["mousecolor"].update(str(Game.lines[cursor_pos_y][cursor_pos_x]))
c.relocate_figure(cursor, cursor_pos_x, cursor_pos_y)
c.bring_figure_to_front(cursor)
print("mouseclick down at: ", mousepos)
actual_color = Game.lines[cursor_pos_y][cursor_pos_x]
#print("paint, erase", values2["paint"], values2["erase"])
if values2["paint"]:
if actual_color != Game.fgcolor:
Game.lines[cursor_pos_y][cursor_pos_x] = Game.fgcolor
#delete_old_blocks()
delete_old_figures()
paint_blocks()
if values2["erase"]:
if actual_color is not None:
Game.lines[cursor_pos_y][cursor_pos_x] = None
delete_old_figures()
paint_blocks()
update_gui()
# mouse click up event
if event2 == "canvas+UP":
mousepos = values2["canvas"]
print("mouseclick up at: ", mousepos)
##if event2.endswith('+RIGHT+'): # Righ click
## print("mouseclick right at ", values2["canvas"])
window2.close()
print("bye")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment