1. #!/usr/bin/python
  2.  
  3. print """A minimal implementation of Conway's Game of Life.
  4. Each cell's survival depends on the number of occupied nearest and
  5. next-nearest neighbours (calculated in Grid::step). A living cell dies
  6. of overcrowding or loneliness if it has more than three or fewer than
  7. two neighbours; a dead cell is brought to life if it has exactly three
  8. neighbours (determined in Cell::setNextState).
  9. Iain Haslam, June 2005.
  10. """
  11.  
  12. print """For larger scale demo,
  13. see http://www.youtube.com/watch?v=JRQGeVxfYmg
  14. """
  15.  
  16. from Tkinter import *
  17. root = Tk()
  18. class Cell(Label):
  19. DEAD = 0
  20. LIVE = 1
  21. def __init__(self,parent):
  22. Label.__init__(self,parent,relief="raised",width=2,borderwidth=1)
  23. self.bind("<Button-1>", self.toggle)
  24. self.displayState(Cell.DEAD)
  25. def toggle(self,event):
  26. self.displayState(1-self.state)
  27. def setNextState(self,numNeighbours):
  28. """Work out whether this cell will be alive at the next iteration."""
  29. if self.state==Cell.LIVE and \
  30. (numNeighbours>3 or numNeighbours<2):
  31. self.nextState = Cell.DEAD
  32. elif self.state==Cell.DEAD and numNeighbours==3:
  33. self.nextState = Cell.LIVE
  34. else:
  35. self.nextState = self.state
  36. def stepToNextState(self):
  37. self.displayState(self.nextState)
  38. def displayState(self,newstate):
  39. self.state = newstate
  40. if self.state==Cell.LIVE:
  41. self["bg"] = "black"
  42. else:
  43. self["bg"] = "white"
  44. class Grid:
  45. def __init__(self,parent,sizex,sizey):
  46. self.sizex = sizex
  47. self.sizey = sizey
  48. #numpy.zeros(sizex,sizey) is a better choice,
  49. #but an additional dependency might be rude...
  50. self.cells = []
  51. for a in range(0,self.sizex):
  52. rowcells = []
  53. for b in range(0,self.sizey):
  54. c = Cell(parent)
  55. c.grid(row=b, column=a)
  56. rowcells.append(c)
  57. self.cells.append(rowcells)
  58. if sizex>10 and sizey>10:
  59. #Start with a glider
  60. self.cells[10][9].displayState(Cell.LIVE)
  61. self.cells[11][10].displayState(Cell.LIVE)
  62. self.cells[9][11].displayState(Cell.LIVE)
  63. self.cells[10][11].displayState(Cell.LIVE)
  64. self.cells[11][11].displayState(Cell.LIVE)
  65. def step(self):
  66. """Calculate then display the next iteration of the game of life.
  67. This function uses wraparound boundary conditions.
  68. """
  69. cells = self.cells
  70. for x in range(0,self.sizex):
  71. if x==0: x_down = self.sizex-1
  72. else: x_down = x-1
  73. if x==self.sizex-1: x_up = 0
  74. else: x_up = x+1
  75. for y in range(0,self.sizey):
  76. if y==0: y_down = self.sizey-1
  77. else: y_down = y-1
  78. if y==self.sizey-1: y_up = 0
  79. else: y_up = y+1
  80. sum = cells[x_down][y].state + cells[x_up][y].state + \
  81. cells[x][y_down].state + cells[x][y_up].state + \
  82. cells[x_down][y_down].state + cells[x_up][y_up].state + \
  83. cells[x_down][y_up].state + cells[x_up][y_down].state
  84. cells[x][y].setNextState(sum)
  85. for row in cells:
  86. for cell in row:
  87. cell.stepToNextState()
  88. def clear(self):
  89. for row in self.cells:
  90. for cell in row:
  91. cell.displayState(Cell.DEAD)
  92. if __name__ == "__main__":
  93. frame = Frame(root)
  94. frame.pack()
  95. grid = Grid(frame,25,25)
  96. bottomFrame = Frame(root)
  97. bottomFrame.pack(side=BOTTOM)
  98. buttonStep = Button(bottomFrame,text="Step",command=grid.step)
  99. buttonStep.pack(side=LEFT)
  100. buttonClear = Button(bottomFrame,text="Clear",command=grid.clear)
  101. buttonClear.pack(side=LEFT,after=buttonStep)
  102. root.mainloop()