Python Forum
conway's game of life
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
conway's game of life
#11
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
Reply
#12
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
Reply
#13
@ 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()
Reply
#14
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.
Reply
#15
(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.
Reply
#16
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.  Angry
Reply
#17
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.
Reply
#18
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()
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  While loop/half-life Miraclefruit 6 8,443 Mar-06-2017, 05:24 PM
Last Post: nilamo
  conway's game of life / Single Responsibility Principle hanscvanleeuwen 13 11,040 Dec-17-2016, 08:30 AM
Last Post: hanscvanleeuwen
  Game of life using IDLE Informatics109 4 5,080 Oct-29-2016, 01:39 PM
Last Post: ichabod801

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020