Skip to content

Instantly share code, notes, and snippets.

@horstjens
Last active June 27, 2022 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save horstjens/68fc4ff2991b66b399d59a509a026dbc to your computer and use it in GitHub Desktop.
Save horstjens/68fc4ff2991b66b399d59a509a026dbc to your computer and use it in GitHub Desktop.
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