Python Forum
conway's game of life
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
conway's game of life
#1
Hi there ..first program I have made in python is conway's game of life (see wikipedia).

I used Python 3.5, tkinter using commands enclosed and overlapping.
Maybe a bit primitive for most here but it works ! and fun to see.
If anyone has time to have a look at it , I would appreciate suggestions where to improve on writing/shortening.
Here it is. Apologies for the length (150 lines).

#Any live cell with fewer than two live neighbours dies, as if caused by under-population.
#Any live cell with two or three live neighbours lives on to the next generation.
#Any live cell with more than three live neighbours dies, as if by over-population.
#Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
########################################################################
import time
from tkinter import *
tk = Tk()
canvas = Canvas(tk, width=1200, height=900,bd=0, highlightthickness=0)
canvas.pack()
##################### VARIABLES ########################################
keypressed = ""                 # no key pressed yet
sqident = 1                        # object number of SQuare and IDENTifier
dubblelist=[]                    # prevent object being placed twice at same position
############# GENERATE FIELD MANUALLY ######################
# ^ UP, V DOWN, < LEFT, > RIGHT, X = CREATE, f = FINISHED - START GAME

sq = canvas.create_rectangle(50, 50, 100, 100, fill="grey") #starting square identifier = 1

def movesquare(event): # detect movement keys
    if event.keysym == "Up":
        canvas.move(sqident, 0, -50) #identifier, x, y
    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)

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 dubblelist: # prevent creating twice
            sqident +=1 #new square identifier
            sq=canvas.create_rectangle((canvas.coords(sqident-1)), fill="grey") #new square created at previous position 
            dubblelist.append(canvas.coords(sqident-1))
while 1:
    canvas.bind_all("<KeyPress-Up>", movesquare) # snap niet verschil annotatie "Up"vs <KeyPress-Up> 
    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)
    if keypressed == "f":
        canvas.delete(sqident)
        break
    tk.update_idletasks()
    tk.update()
    
############################## start animation #############################
numberofgenerations =0    
while numberofgenerations < 1000:
    tk.update()
    ##################### variables ########################################
    livelist = canvas.find_all()    # livelist         = list of al objectIDs on canvas
    leli = len(livelist)            # leli             = number of objects on canvas
    createlist=[]                    # createlist     = newly created squares stored this list (coordinates are stored!)
    killlist=[]                     # 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 killist
    for x in range(1,leli+1): 
        # look in livelist for object identifier at position x and retrieve its coordinates
        x1=(canvas.coords(livelist[x-1])[0]); y1=(canvas.coords(livelist[x-1])[1])
        x2=(canvas.coords(livelist[x-1])[2]); y2=(canvas.coords(livelist[x-1])[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))
            
    #########################################################################
    # Creating new lives. Store in createlist
    # look at coordinates NW,N,NE,W,E,SW,S,SE of object 
    # detect whether empty and count number of adjacent to empty field. If 3 create new live
    for x in range(1,leli+1): 
        x1=(canvas.coords(livelist[x-1])[0]); y1=(canvas.coords(livelist[x-1])[1])
        x2=(canvas.coords(livelist[x-1])[2]); y2=(canvas.coords(livelist[x-1])[3]) 
        
        NWcoordinates=[x1-50,y1-50,x2-50,y2-50]                        # North West coordinates
        NW=len(canvas.find_overlapping(x1-50,y1-50,x2-50,y2-50))    # Number of objects around NW 
        NWencl=len(canvas.find_enclosed(x1-60,y1-60,x2-40,y2-40))    # NW coordinates -> 0 is empty ; 1 is present

        Nocoordinates=[x1,y1-50,x2,y2-50]                #North
        No=len(canvas.find_overlapping(x1,y1-50,x2,y2-50))
        Noencl=len(canvas.find_enclosed(x1-10,y1-60,x2+10,y2-40))
        
        NEcoordinates=[x1+50,y1-50,x2+50,y2-50]            #North East
        NE=len(canvas.find_overlapping(x1+50,y1-50,x2+50,y2-50))
        NEencl=len(canvas.find_enclosed(x1+40,y1-60,x2+60,y2-40))
        
        Wecoordinates=[x1-50,y1,x2-50,y2]                #West
        We=len(canvas.find_overlapping(x1-50,y1,x2-50,y2))
        Weencl=len(canvas.find_enclosed(x1-60,y1-10,x2-40,y2+10))

        Eacoordinates=[x1+50,y1,x2+50,y2]                #East
        Ea=len(canvas.find_overlapping(x1+50,y1,x2+50,y2))
        Eaencl=len(canvas.find_enclosed(x1+40,y1-10,x2+60,y2+10))

        SWcoordinates=[x1-50,y1+50,x2-50,y2+50]            #South West
        SW=len(canvas.find_overlapping(x1-50,y1+50,x2-50,y2+50))
        SWencl=len(canvas.find_enclosed(x1-60,y1+40,x2-40,y2+60))

        Socoordinates=[x1,y1+50,x2,y2+50]                #South
        So=len(canvas.find_overlapping(x1,y1+50,x2,y2+50))
        Soencl=len(canvas.find_enclosed(x1-10,y1+40,x2+10,y2+60))

        SEcoordinates=[x1+50,y1+50,x2+50,y2+50]            #South East
        SE=len(canvas.find_overlapping(x1+50,y1+50,x2+50,y2+50))
        SEencl=len(canvas.find_enclosed(x1+40,y1+40,x2+60,y2+60))    
        
        if NWencl != 1: # NWencl (enclosed) not 1 thus is empty
            if NW == 3: # NW 3 (overlapping) neighbours thus new life
                if NWcoordinates not in createlist: # prevent creating twice
                    createlist.append (NWcoordinates)
        if Noencl != 1: 
            if No == 3: 
                if Nocoordinates not in createlist:
                    createlist.append (Nocoordinates)            
        if NEencl != 1: 
            if NE == 3: 
                if NEcoordinates not in createlist:
                    createlist.append (NEcoordinates)
        if Weencl != 1: 
            if We == 3: 
                if Wecoordinates not in createlist:
                    createlist.append (Wecoordinates)
        if Eaencl != 1: 
            if Ea == 3: 
                if Eacoordinates not in createlist:
                    createlist.append (Eacoordinates)
        if SWencl != 1: 
            if SW == 3: 
                if SWcoordinates not in createlist:
                    createlist.append (SWcoordinates)
        if Soencl != 1: 
            if So == 3: 
                if Socoordinates not in createlist:
                    createlist.append (Socoordinates)
        if SEencl != 1: 
            if SE == 3: 
                if SEcoordinates not in createlist:
                    createlist.append (SEcoordinates)
                    
    #########################################################################
    #start CReating
    lecl=len(createlist)    #number of objects to create (len)
    for CR in range(0,lecl):
        sq=canvas.create_rectangle(createlist[CR], fill="grey") #select from createlist coordinates    
        
    #########################################################################        
    #start KIlling
    leki=len(killlist)        #number of objects to kill (len)
    for KI in range(0,leki):
        kill=(killlist)[KI]    #select from killlist Object ID
        canvas.delete(kill)
    #########################################################################        
    # slow down
    for TI in range(0,2):
        time.sleep(0.2)

    numberofgenerations +=1

mainloop()
Reply
#2
i did my life game program in assembly.  mine did some fancy low level tricks to achieve 4 cells looked up in parallel for speed.  it ran directly in video memory and achieved 22 cycles (frames) per second for the whole screen in 320x200 mode.  blink and you miss things so i had to add a single step (one frame) mode.
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#3
No clue how many cycles per second mine runs. I included a time loop at the end in order to slow it down.
The whole program takes 120 lines, which is shorter than I expected when I began writing.
I had some comments and variables in Dutch (where I'm from) now replaced in English. Plus some small improvements. Program again below.
^ = UP, V = DOWN, < = LEFT, > = RIGHT, x = CREATE, f = FINISHED  (START GAME)
Any suggestions how I could add random colors to squares ? Idea was to make a list ["red", "yellow", "blue" etc] and random pick the color from the list.
Hans

import time
from tkinter import *
tk = Tk()
canvas = Canvas(tk, width=1200, height=900,bd=0, highlightthickness=0)
canvas.pack()
##################### VARIABLES ############################
keypressed = ""                 # no key pressed yet
sqident = 1                        # object number of SQuare and IDENTifier
doublelist=[]                    # prevent object being placed twice at same position
############# GENERATE FIELD MANUALLY ! #####################
# ^ UP, V DOWN, < LEFT, > RIGHT, X = CREATE, f = FINISHED - START GAME

sq = canvas.create_rectangle(50, 50, 100, 100, fill="grey") #starting square (identifier = 1)

def movesquare(event): # detect movement keys
    if event.keysym == "Up":
        canvas.move(sqident, 0, -50) #identifier, x, y
    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)

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:
    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)
    if keypressed == "f":
        canvas.delete(sqident)
        break
    tk.update_idletasks()
    tk.update()
    
############################## start animation #############################
numberofgenerations =0    
while numberofgenerations < 1000:
    tk.update()
    ##################### variables ########################################
    livelist = canvas.find_all()    # livelist         = list of al objectIDs on canvas
    leli = len(livelist)            # leli             = number of objects on canvas
    createlist=[]                    # createlist     = newly created squares stored this list (coordinates are stored!)
    killlist=[]                     # 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 killist
    for x in range(0,leli): 
        # look in livelist for object identifier at position x and retrieve its coordinates
        x1=(canvas.coords(livelist[x])[0]); y1=(canvas.coords(livelist[x])[1])
        x2=(canvas.coords(livelist[x])[2]); y2=(canvas.coords(livelist[x])[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))
            
    #########################################################################
    # Creating NEW lives. Store in createlist. 
    # look at coordinates NW,N,NE,W,E,SW,S,SE of squares 
    # detect whether empty (enclosed) and count number of adjacent(overlapping) to empty field. If 3 create new live
    for x in range(0,leli): 
        x1=(canvas.coords(livelist[x])[0]); y1=(canvas.coords(livelist[x])[1])
        x2=(canvas.coords(livelist[x])[2]); y2=(canvas.coords(livelist[x])[3]) 
        
        NWcoordinates=[x1-50,y1-50,x2-50,y2-50]                        # North West coordinates
        NW=len(canvas.find_overlapping(x1-50,y1-50,x2-50,y2-50))    # Number of objects around NW 
        NWencl=len(canvas.find_enclosed(x1-60,y1-60,x2-40,y2-40))    # enclosed NW coordinates:0 is empty; 1 is present

        Nocoordinates=[x1,y1-50,x2,y2-50]                #North
        No=len(canvas.find_overlapping(x1,y1-50,x2,y2-50))
        Noencl=len(canvas.find_enclosed(x1-10,y1-60,x2+10,y2-40))
        
        NEcoordinates=[x1+50,y1-50,x2+50,y2-50]            #North East
        NE=len(canvas.find_overlapping(x1+50,y1-50,x2+50,y2-50))
        NEencl=len(canvas.find_enclosed(x1+40,y1-60,x2+60,y2-40))
        
        Wecoordinates=[x1-50,y1,x2-50,y2]                #West
        We=len(canvas.find_overlapping(x1-50,y1,x2-50,y2))
        Weencl=len(canvas.find_enclosed(x1-60,y1-10,x2-40,y2+10))

        Eacoordinates=[x1+50,y1,x2+50,y2]                #East
        Ea=len(canvas.find_overlapping(x1+50,y1,x2+50,y2))
        Eaencl=len(canvas.find_enclosed(x1+40,y1-10,x2+60,y2+10))

        SWcoordinates=[x1-50,y1+50,x2-50,y2+50]            #South West
        SW=len(canvas.find_overlapping(x1-50,y1+50,x2-50,y2+50))
        SWencl=len(canvas.find_enclosed(x1-60,y1+40,x2-40,y2+60))

        Socoordinates=[x1,y1+50,x2,y2+50]                #South
        So=len(canvas.find_overlapping(x1,y1+50,x2,y2+50))
        Soencl=len(canvas.find_enclosed(x1-10,y1+40,x2+10,y2+60))

        SEcoordinates=[x1+50,y1+50,x2+50,y2+50]            #South East
        SE=len(canvas.find_overlapping(x1+50,y1+50,x2+50,y2+50))
        SEencl=len(canvas.find_enclosed(x1+40,y1+40,x2+60,y2+60))    
        
        if NWencl != 1: # NWencl (enclosed) not 1 thus is empty. Possible new life
            if NW == 3: # NW has 3 (overlapping) neighbours thus new life
                if NWcoordinates not in createlist: # prevent creating twice
                    createlist.append (NWcoordinates)
        if Noencl != 1: 
            if No == 3: 
                if Nocoordinates not in createlist:
                    createlist.append (Nocoordinates)            
        if NEencl != 1: 
            if NE == 3: 
                if NEcoordinates not in createlist:
                    createlist.append (NEcoordinates)
        if Weencl != 1: 
            if We == 3: 
                if Wecoordinates not in createlist:
                    createlist.append (Wecoordinates)
        if Eaencl != 1: 
            if Ea == 3: 
                if Eacoordinates not in createlist:
                    createlist.append (Eacoordinates)
        if SWencl != 1: 
            if SW == 3: 
                if SWcoordinates not in createlist:
                    createlist.append (SWcoordinates)
        if Soencl != 1: 
            if So == 3: 
                if Socoordinates not in createlist:
                    createlist.append (Socoordinates)
        if SEencl != 1: 
            if SE == 3: 
                if SEcoordinates not in createlist:
                    createlist.append (SEcoordinates)
                    
    #Start CReating
    lecl=len(createlist)    #number of objects to create (len)
    for CR in range(0,lecl):
        sq=canvas.create_rectangle(createlist[CR], fill="blue") #select coordinates from createlist    
        
    #Start KIlling
    leki=len(killlist)        #number of objects to kill (len)
    for KI in range(0,leki):
        kill=(killlist)[KI]    #select from killlist Object ID
        canvas.delete(kill) #delete ObjectID
        
    #Slow down
    for TI in range(0,2):
        time.sleep(0.1)

    numberofgenerations +=1

mainloop()

Surprisingly some figures run off the canvas and come back later! Thus canvas has no limits.
Originally I had planned something to screen the canvas in x and y directions and calculate each position directly.
By chosing to keep track of each square by object ID it can run off the screen freely.!!
Reply
#4
Pretty cool is to change color every generation so I did this:
(in last file)
After line 48
color=0
colsel=["Hot Pink","Deep Pink","Pink","Light Pink","Pale Violet Red","Maroon","Medium Violet Red","Violet Red","Violet","Plum","Orchid","Medium Orchid","Dark Orchid","Dark Violet","Blue Violet","Purple","Medium Purple","Thistle"]
 
After line 50
                color +=1
                if color == 18:
                               color = 1
replace line 147
sq=canvas.create_rectangle(createlist[CR], fill="blue")
with
sq=canvas.create_rectangle(createlist[CR], fill=(colsel[color]))

You'll see a new color every generation!
Reply
#5
i did a color version, much slower in C. i used color bits (RGB) to form a type of DNA and let new cells inherit color bits to see how things would spread. old cells just kept the same colors. empty spaces were black.  i had many other ideas i never implemented.  today i might try making the cells show up as smilie faces Cry Sick
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#6
Here is a minimal pygame version with aging:
https://github.com/Mekire/Conway-User-Interaction

And also, a prime example of terrible terrible code. But here is a ridiculously full featured version I did years ago:
https://github.com/Mekire/life-as-a-bit

Includes patterns you can stamp; cutting and pasting; zooming; etc.
Don't look at the code; just don't.
Reply
#7
Nice. Sorry, I had to look at some of the code.. only way to learn.
I might copy your approach for adjacents.
ADJACENTS = {(-1, 1), (0, 1), (1, 1), (-1, 0),
(1, 0), (-1, -1), (0,-1), (1,-1)}
I put that in 8 separate lines.
Reply
#8
You could improve the while-loop:

numberofgenerations =0    
while numberofgenerations < 1000:
to:

While True:
    ...
    if user press stop-key:
        break;
    if no changes any more:
         break;

You can shorten for example:

    leki=len(killlist)       
    for KI in range(0,leki):
        kill=(killlist)[KI]    #select from killlist Object ID
        canvas.delete(kill)
to

 
    for kill in killlist:
        canvas.delete(kill)
Reply
#9
Thanks Heiner,

Shows you what a novice I am. I didn't know that you could use the - FOR . . IN ..- command that way.
( my solution .. getting length first and using range (0, length) must look stupid)
I did FOR . . IN for the creatlist as well.
Working on the stop-key now!
Reply
#10
Homework finished .. less than 80 lines (assigment) .colors extra . thanks to you guys

import time
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=2, width=30)
text_.pack() 
text_.insert(END, "To MOVE use arrow keys. Press x to seed.  f to finish")
##################### VARIABLES ############################
keypressed = ""; SQident = 1; double=[]    
sq = canvas.create_rectangle(50, 50, 100, 100, fill="grey") # starting square,identifier = 1
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)        
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 double: # prevent creating twice
            sq=canvas.create_rectangle((canvas.coords(SQident)), fill="grey") #new square created  
            double.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)
    if keypressed == "f":
        canvas.delete(SQident)
        break    
############################## start creation #############################
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"]
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, SW,SE of square
while numberofgenerations < 1000:
    window.update()
    window.update_idletasks()
    color +=1
    if color == 10:
        color = 1
    livelist = canvas.find_all()    #= retrieves list of al objectIDs on canvas
    createlist=[]                    #= Coordinates of newly created squares stored this list.
    killlist=[]                     #= objectIDs to be killed squares stored in this list.
# Staying ALIVE OR DIE
    for live in livelist:  # look in livelist for object identifier 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 number of neighbours (CN) by using overlap function (-1 as it includes object itself)
        if CN < 2: #dead too few neighbours
            killlist.append (canvas.find_enclosed(x1-10,y1-10,x2+10,y2+10)) 
            # enclosed gives Object ID square within enclosed (needs wider coordinates-10,-10,+10,+10)
        if CN > 3: #dead too many neighbours
            killlist.append (canvas.find_enclosed(x1-10,y1-10,x2+10,y2+10))
            
# Detect whether Neighbours empty (via Encl/enclosed) and count number of adjacent(via Overl/overlapping).
        for Ne in Neigh: 
            Coo=[x1+Ne[0],y1+Ne[1],x2+Ne[2],y2+Ne[3]]    # Coo = 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 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])) #select coordinates from createlist            
    for kill in killlist:        #Start killing
        canvas.delete(kill)     #Delete ObjectID        
    for TI in range(0,2):        #Slow down
        time.sleep(0.1)
    numberofgenerations +=1
mainloop()
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  While loop/half-life Miraclefruit 6 8,457 Mar-06-2017, 05:24 PM
Last Post: nilamo
  conway's game of life / Single Responsibility Principle hanscvanleeuwen 13 11,071 Dec-17-2016, 08:30 AM
Last Post: hanscvanleeuwen
  Game of life using IDLE Informatics109 4 5,090 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