Posts: 687
Threads: 37
Joined: Sep 2016
This one;
def movesquare(event): # detect movement keys
if event.keysym == "Up":
canvas.move(SQident, 0, -50)
elif event.keysym == "Down":
canvas.move(SQident, 0, 50)
elif event.keysym == "Left":
canvas.move(SQident, -50, 0)
elif event.keysym == "Right":
canvas.move(SQident, 50, 0) is begging for a dictionary:
moves={'Up':(0, -50),'Down':(0,50), 'Left':(-50,0), 'Right':(50,0)}
def movesquare(event): # detect movement keys
x,y=moves[event.keysym]
canvas.move(SQident,x,y) There is even a way to replace the whole function by a one-liner but this can make the code a bit less readable.
Unless noted otherwise, code in my posts should be understood as "coding suggestions", and its use may require more neurones than the two necessary for Ctrl-C/Ctrl-V.
Your one-stop place for all your GIMP needs: gimp-forum.net
Posts: 591
Threads: 26
Joined: Sep 2016
Quote:moves={'Up':(0, -50),'Down':(0,50), 'Left':(-50,0), 'Right':(50,0)}
def movesquare(event): # detect movement keys
x,y=moves[event.keysym]
canvas.move(SQident,x,y)
I use this technique a lot. My recomendation is to go one step further and make the dict from unit vectors.
moves = {'Up':(0, -1),'Down':(0,1), 'Left':(-1,0), 'Right':(1,0)} Then you can independently define a speed and multiply it by the move components.
Same kind of technique used here:
https://github.com/Mekire/pygame-samples...ir_move.py
Posts: 17
Threads: 2
Joined: Nov 2016
@ Ofnuts..thanks for the clever idea… I would never have thought of that (and probably never will ;) as no textbook will teach you that.
@ Mekire…I guess I could implement that but the +/- 50 also corresponds to the size of the square (not speed). Next development might be asking for the size of the square first and use your idea to adjust these.
Question.. could I combine the bind_all commands in lines 37-40 (or 41-43) in a similar way?
Finally, I added a random generator of squares (press r), as I got lazy of creating them myself.
(Lines 47-60). Fun to watch
Hans
#Any live cell with fewer than two live neighbours dies.
#Any live cell with two or three live neighbours lives.
#Any live cell with more than three live neighbours dies.
#Any dead cell with exactly three live neighbours becomes a live cell.
import time
import random
from tkinter import *
window = Tk()
window.title("Conway's Game of Life")
canvas = Canvas(window, width=1200, height=900,bd=0, highlightthickness=0)
canvas.pack()
text_ = Text(window, height=3, width=30)
text_.pack()
text_.insert(END, "To MOVE use arrow keys. Press x to seed. r for random seeds. f to finish")
##################### VARIABLES ############################
keypressed = ""; SQident = 1; doublelist=[]
Neigh=([-50,-50,-50,-50],[0,-50,0,-50],[+50,-50,+50,-50],[-50,0,-50,0],[+50,0,+50,0],[-50,+50,-50,+50],[0,+50,0,+50],[+50,+50,+50,+50])
#Neigh contains coordinates, NW,N,NW,W,E,SW,S,SE of square
sq = canvas.create_rectangle(50, 50, 100, 100, fill="grey")
moves={"Up":(0, -50),"Down":(0,50), "Left":(-50,0), "Right":(50,0)}
def movesquare(event): # detect movement keys
x,y=moves[event.keysym]
canvas.move(SQident,x,y)
def otherkey(event): #detect other keys pressed
global SQident; global keypressed
keypressed = event.keysym
if keypressed == "x": # make new square
if (canvas.coords(SQident)) not in doublelist: # prevent creating twice
sq=canvas.create_rectangle((canvas.coords(SQident)), fill="grey") #new square created at previous position
doublelist.append(canvas.coords(SQident))
SQident +=1 #new square identifier
while 1:
window.update()
window.update_idletasks()
canvas.bind_all("<KeyPress-Up>", movesquare)
canvas.bind_all("<KeyPress-Down>", movesquare)
canvas.bind_all("<KeyPress-Left>", movesquare)
canvas.bind_all("<KeyPress-Right>", movesquare)
canvas.bind_all("x", otherkey)
canvas.bind_all("f", otherkey)
canvas.bind_all("r", otherkey)
if keypressed == "f": #finshed
canvas.delete(SQident)
break
if keypressed == "r": #add random
canvas.delete(SQident)
for y in range(10):
x1=(random.randint(0,13)+random.randint(0,13))*50 # between 0 and 24 * 50 = 0 - 1200 is width
y1=(random.randint(0,9)+random.randint(0,9))*50 # between 0 and 18 * 50 = 0 - 900 is height
x2=x1+50; y2=y1+50
sq = canvas.create_rectangle(x1, y1, x2, y2, fill="grey")
for x in range(0,3):
ra=random.randint(0,7)
x1ne=(Neigh[ra][0]);y1ne=(Neigh[ra][1]);x2ne=(Neigh[ra][2]);y2ne=(Neigh[ra][3]) # create 3 random neighbors
if (x1+x1ne,y1+y1ne,x2+x2ne,y2+y2ne) not in doublelist: # prevent creating twice
sq = canvas.create_rectangle((x1+x1ne,y1+y1ne,x2+x2ne,y2+y2ne), fill="pink")
doublelist.append((x1+x1ne,y1+y1ne,x2+x2ne,y2+y2ne))
break
############################## start animation #############################
numberofgenerations =0 ; color=0
colsel=["Light Pink","Pale Violet Red","Deep Pink","Medium Violet Red","Orchid","Medium Orchid","Medium Purple","Dark Violet","Blue Violet","Purple"]
while numberofgenerations < 1000:
window.update()
window.update_idletasks()
color +=1
if color == 10:
color = 1
##################### variables ########################################
livelist = canvas.find_all() #= list of al objectIDs on canvas
createlist=[] #= newly created squares stored this list (coordinates are stored!)
killlist=[] #= to be killed squares stored in this list (objectIDs are stored!)
# Staying ALIVE OR DIE using Object IDs. Detecting number of neighbours. Store in killlist
for live in livelist:
# look in livelist for object identifier at position x and retrieve its coordinates
x1=(canvas.coords(live)[0]); y1=(canvas.coords(live)[1])
x2=(canvas.coords(live)[2]); y2=(canvas.coords(live)[3])
# Count number of Neighbours (CN) by using overlap function (-1 as it includes object itself)
CN=(len(canvas.find_overlapping(x1,y1,x2,y2))-1)
if CN < 2: #dead too few neighbours
killlist.append (canvas.find_enclosed(x1-10,y1-10,x2+10,y2+10))
# enclosed function gives Object ID square within enclosed. Enclosed needs wider coordinates
# do nothing if CN==2 OR CN==3 (survive)
if CN > 3: #dead too many neighbours
killlist.append (canvas.find_enclosed(x1-10,y1-10,x2+10,y2+10))
# Detect wether neighbours empty (via enclosed/Encl) and count number of adjacent(via overlapping/Overl).
for Ne in Neigh:
Coo=[x1+Ne[0],y1+Ne[1],x2+Ne[2],y2+Ne[3]] # surrounding coordinates
Overl=len(canvas.find_overlapping(x1+Ne[0],y1+Ne[1],x2+Ne[2],y2+Ne[3])) # Number of objects surrounding
Encl=len(canvas.find_enclosed(x1+Ne[0]-10,y1+Ne[1]-10,x2+Ne[2]+10,y2+Ne[3]+10)) # enclosed coordinates:0 is empty; 1 is present
if Encl != 1: # enclosed not 1 thus is field is empty. Possible new life
if Overl == 3: # has 3 (overlapping) neighbours thus new life
if Coo not in createlist: # prevent creating twice
createlist.append (Coo)
#Start creating
for create in createlist:
sq=canvas.create_rectangle(create, fill=(colsel[color])) #select coordinates from createlist
#Start killlling
for kill in killlist:
canvas.delete(kill) # delete ObjectID
#Slow down
for TI in range(0,2):
time.sleep(0.1)
numberofgenerations +=1
mainloop()
Posts: 591
Threads: 26
Joined: Sep 2016
Quote:I guess I could implement that but the +/- 50 also corresponds to the size of the square (not speed).
It was more of a generic concept related to reducing magic numbers in code.
Define speed in one place and the dict never changes. If you need to change speed you change it in one place.
Something to keep in mind in the future.
As to your question, I don't use tkinter much, but yes.
You could do something like this:
bindings = {"<KeyPress-Up>" : movesquare, "<KeyPress-Down>": movesquare,
"<KeyPress-Left>": movesquare, "<KeyPress-Right>": movesquare,
"x": otherkey, "f": otherkey, "r": otherkey}
for binding in bindings:
canvas.bind_all(binding, bindings[binding]) Also, I question whether or not you need to rebind those every time through the loop.
Once up front should be fine I would think.
Posts: 17
Threads: 2
Joined: Nov 2016
(Dec-09-2016, 11:03 AM)Mekire Wrote: Also, I question whether or not you need to rebind those every time through the loop.
Once up front should be fine I would think.
You are right ..doesnt have to be in the loop!
Could/should have come up with the answer to my question myself. Identical to Ofnuts solution.
Posts: 17
Threads: 2
Joined: Nov 2016
Meanwhile..I introduced something whereby the game doesn't work as it supposed to do..some square stay alive which shouldn't. Time for debugging.
Posts: 591
Threads: 26
Joined: Sep 2016
You really need to break your program logic up into more functions.
Single Responsibility Principle
Every function should have a single job. This makes debugging much much easier.
You should have, for instance, one clean function that takes in the current generation and returns the next (or modifies the current if you must maintain instances for aging etc.).
Ideally you should have no code in your global namespace other than constants.
Also, time to implement adding/killing cells with the mouse me'thinks.
Posts: 17
Threads: 2
Joined: Nov 2016
Hi Mekire,
Im not sure I get the idea about “single responsibility principle” ..
I have to admit that I have problems understanding programming Python Classes / modules.
This program I wrote linearly with one line at a time, while thinking what next ;)
My first challenge thus will be rewriting “no code in my global namespace other than constants.”
(give me some time/hints!).
The challenge with the mouse is much easier I think (looked at other examples with tkinter+mouse).
p.s. I did get the small bug out (random generated squares would be on top of own creation and therefore hold two squares while you could see only one).
The correct program below (lines 43, 45).. with thousand apologies for lack of logic functions.
( its my first program since the ZX81 ;o )
import time ; import random
from tkinter import *
window = Tk()
window.title("Conway's Game of Life")
canvas = Canvas(window, width=1200, height=900,bd=0, highlightthickness=0);canvas.pack()
text_ = Text(window, height=3, width=30); text_.pack()
text_.insert(END, "To MOVE use arrow keys. Press x to seed. r for random seeds. f to finish")
##################### VARIABLES ############################
keypressed = ""; SQident = 1; doublelist=[]
Neigh=([-50,-50,-50,-50],[0,-50,0,-50],[+50,-50,+50,-50],[-50,0,-50,0],[+50,0,+50,0],[-50,+50,-50,+50],[0,+50,0,+50],[+50,+50,+50,+50])
moves={"Up":(0, -50),"Down":(0,50), "Left":(-50,0), "Right":(50,0)}
sq = canvas.create_rectangle(50, 50, 100, 100, fill="grey")
##################### create field ##########################
def movesquare(event): # detect movement keys
x,y=moves[event.keysym]
canvas.move(SQident,x,y)
def otherkey(event): #detect other keys pressed
global SQident; global keypressed
keypressed = event.keysym
if keypressed == "x": # make new square
if (canvas.coords(SQident)) not in doublelist: # prevent creating twice
sq=canvas.create_rectangle((canvas.coords(SQident)), fill="grey") #new square created
doublelist.append([canvas.coords(SQident)])
SQident +=1 #new square identifier
bindings = {"<KeyPress-Up>" : movesquare, "<KeyPress-Down>": movesquare,
"<KeyPress-Left>": movesquare, "<KeyPress-Right>": movesquare,
"x": otherkey, "f": otherkey, "r": otherkey}
for binding in bindings:
canvas.bind_all(binding, bindings[binding])
while 1:
window.update();window.update_idletasks()
if keypressed == "f": #finshed
canvas.delete(SQident)
break
if keypressed == "r": #add random
canvas.delete(SQident)
for y in range(10):
x1=(random.randint(0,13)+random.randint(0,13))*50 # between 0 and 24 * 50 = 0 - 1200 is width
y1=(random.randint(0,9)+random.randint(0,9))*50 # between 0 and 18 * 50 = 0 - 900 is height
x2=x1+50; y2=y1+50
if ([x1,y1,x2,y2]) not in doublelist: # DEBUGGED
sq = canvas.create_rectangle(x1, y1, x2, y2, fill="grey")
doublelist.append([x1,y1,x2,y2]) # DEBUGGED
for x in range(0,3):
ra=random.randint(0,7)
x1ne=(Neigh[ra][0]);y1ne=(Neigh[ra][1]);x2ne=(Neigh[ra][2]);y2ne=(Neigh[ra][3])
if ([x1+x1ne,y1+y1ne,x2+x2ne,y2+y2ne]) not in doublelist:
sq = canvas.create_rectangle((x1+x1ne,y1+y1ne,x2+x2ne,y2+y2ne), fill="pink")
doublelist.append([x1+x1ne,y1+y1ne,x2+x2ne,y2+y2ne])
break
############################## start animation #############################
numberofgenerations=0; color=0
colsel=["Light Pink","Pale Violet Red","Deep Pink","Medium Violet Red","Orchid","Medium Orchid","Medium Purple","Dark Violet","Blue Violet","Purple"]
while numberofgenerations < 1000:
window.update();window.update_idletasks()
livelist = canvas.find_all() #list of al objectIDs on canvas
createlist=[];killlist=[] # createlist stores coordinates; killlist stores objectIDs.
if color == 10:
color = 1
for live in livelist:
# look in livelist for object identifier at position x and retrieve its coordinates
x1=(canvas.coords(live)[0]); y1=(canvas.coords(live)[1]); x2=(canvas.coords(live)[2]); y2=(canvas.coords(live)[3])
CN=(len(canvas.find_overlapping(x1,y1,x2,y2))-1) # count Neighbours CN
if (CN < 2) or (CN > 3): #dead too few/many neighbours
killlist.append (canvas.find_enclosed(x1-10,y1-10,x2+10,y2+10))
for Ne in Neigh:
Coo=([x1+Ne[0],y1+Ne[1],x2+Ne[2],y2+Ne[3]]) # surrounding coordinates
Overl=len(canvas.find_overlapping(x1+Ne[0],y1+Ne[1],x2+Ne[2],y2+Ne[3])) # Number of objects surrounding
Encl=len(canvas.find_enclosed(x1+Ne[0]-10,y1+Ne[1]-10,x2+Ne[2]+10,y2+Ne[3]+10)) # enclosed coordinates:0 is empty; 1 is present
if Encl == 0: # enclosed is 0 thus is field is empty. Possible new life
if Overl == 3: # has 3 (overlapping) neighbours thus new life
if Coo not in createlist: # prevent creating twice
createlist.append (Coo)
for create in createlist: #Start creating
sq=canvas.create_rectangle(create, fill=(colsel[color]))
for kill in killlist: #Start killling
canvas.delete(kill)
#Slow down:
for TI in range(0,2):
time.sleep(0.1)
numberofgenerations +=1;color +=1
mainloop()
|