fork(1) download
  1. # life.py
  2. # Glenn A. Richard
  3. # Mechanical MOOC MIT OCW 6.189
  4. # Project 2 Conway's Game of Life
  5. # Currently set to create a Gosper Glider Gun
  6. # Use Python 2.7
  7. # July 24, 2013
  8. # Modified July 30, 2013
  9. ## Code template written by Sarina Canelake & Kelly Casteel, August 2010
  10. ## Revised January 2011
  11. from graphics import *
  12. import random
  13.  
  14. ############################################################
  15. # GLOBAL VARIABLES
  16. ############################################################
  17.  
  18. BLOCK_SIZE = 16
  19. BLOCK_OUTLINE_WIDTH = 2
  20. BOARD_WIDTH = 50
  21. BOARD_HEIGHT = 50
  22. COLOR = "red" # I added this.
  23. DELAY = 200
  24.  
  25. neighbor_test_blocklist = [(0,0), (1,1)]
  26. blinker_blocklist = [(2, 2), (2, 3), (2, 4)]
  27. toad_blocklist = [(4,4), (3,5), (3,6), (5,7), (6,5), (6,6)]
  28. beacon_blocklist = [(2,3), (2,4), (3,3), (3,4), (4,5), (4,6), (5,5), (5,6)]
  29. glider_blocklist = [(1,2), (2,3), (3,1), (3,2), (3,3)]
  30. pulsar_blocklist = [(2,4), (2,5), (2,6), (4,2), (4,7), (5,2), (5,7),
  31. (6,2), (6,7), (7,4), (7,5), (7,6)]
  32. gosper_glider_gun_blocklist = [(1,5), (1,6), (2,5), (2,6), (11,5), (11,6), (11,7), (12,4), (12,8), (13,3), (13,9), (14,3), (14,9), (15,6), (16,4), (16,8), (17,5), (17,6), (17,7), (18,6), (21,3), (21,4), (21,5), (22,3), (22,4), (22,5), (23,2), (23,6), (25,1), (25,2), (25,6), (25,7), (35,3), (35,4), (36,3), (36,4)]
  33. # for diehard, make board at least 25x25, might need to change block size
  34. diehard_blocklist = [(5,7), (6,7), (6,8), (10,8), (11,8), (12,8), (11,6)]
  35. # list of blocklists
  36. blocklist_list = [glider_blocklist, gosper_glider_gun_blocklist]
  37.  
  38. # blocklist_list = [toad_blocklist, beacon_blocklist, glider_blocklist, pulsar_blocklist, diehard_blocklist, gosper_glider_gun_blocklist]
  39. ############################################################
  40. # TEST CODE (don't worry about understanding this section)
  41. ############################################################
  42.  
  43. def test_neighbors(board):
  44. '''
  45. Code to test the board.get_block_neighbor function
  46. '''
  47. for block in board.block_list.values():
  48. neighbors = board.get_block_neighbors(block)
  49. ncoords = [neighbor.get_coords() for neighbor in neighbors]
  50. if block.get_coords() == (0,0):
  51. zeroneighs = [(0,1), (1,1), (1,0)]
  52. for n in ncoords:
  53. if n not in zeroneighs:
  54. print "Testing block at (0,0)"
  55. print "Got", ncoords
  56. print "Expected", zeroneighs
  57. return False
  58.  
  59. for neighbor in neighbors:
  60. if neighbor.get_coords() == (1, 1):
  61. if neighbor.is_live() == False:
  62. print "Testing block at (0, 0)..."
  63. print "My neighbor at (1, 1) should be live; it is not."
  64. print "Did you return my actual neighbors, or create new copies of them?"
  65. print "FAIL: get_block_neighbors() should NOT return new Blocks!"
  66. return False
  67.  
  68. elif block.get_coords() == (1,1):
  69. oneneighs = [(0,0), (0,1), (0,2), (1,0), (1,2), (2,0), (2,1),(2,2)]
  70. for n in ncoords:
  71. if n not in oneneighs:
  72. print "Testing block at (1,1)"
  73. print "Got", ncoords
  74. print "Expected", oneneighs
  75. return False
  76. for n in oneneighs:
  77. if n not in ncoords:
  78. print "Testing block at (1,1)"
  79. print "Got", ncoords
  80. print "Expected", oneneighs
  81. return False
  82. print "Passed neighbor test"
  83. return True
  84.  
  85.  
  86. ############################################################
  87. # BLOCK CLASS (Read through and understand this part!)
  88. ############################################################
  89.  
  90. class Block(Rectangle):
  91. ''' Block class:
  92. Implement a block for a tetris piece
  93. Attributes: x - type: int
  94. y - type: int
  95. specify the position on the board
  96. in terms of the square grid
  97. '''
  98.  
  99. def __init__(self, pos, color):
  100. '''
  101. pos: a Point object specifing the (x, y) square of the Block (NOT in pixels!)
  102. color: a string specifing the color of the block (eg 'blue' or 'purple')
  103. '''
  104. self.x = pos.x
  105. self.y = pos.y
  106.  
  107. p1 = Point(pos.x*BLOCK_SIZE,
  108. pos.y*BLOCK_SIZE)
  109. p2 = Point(p1.x + BLOCK_SIZE, p1.y + BLOCK_SIZE)
  110.  
  111. Rectangle.__init__(self, p1, p2)
  112. self.setWidth(BLOCK_OUTLINE_WIDTH)
  113. self.setFill(color)
  114. self.status = 'dead'
  115. self.new_status = 'None'
  116.  
  117. def get_coords(self):
  118. return (self.x, self.y)
  119.  
  120. def set_live(self, canvas):
  121. '''
  122. Sets the block status to 'live' and draws it on the grid.
  123. Be sure to do this on the canvas!
  124. '''
  125. if self.status=='dead':
  126. self.status = 'live'
  127. self.draw(canvas)
  128.  
  129. def set_dead(self):
  130. '''
  131. Sets the block status to 'dead' and undraws it from the grid.
  132. '''
  133. if self.status=='live':
  134. self.status = 'dead'
  135. self.undraw()
  136.  
  137. def is_live(self):
  138. '''
  139. Returns True if the block is currently 'live'. Returns False otherwise.
  140. '''
  141. if self.status == 'live':
  142. return True
  143. return False
  144.  
  145. def reset_status(self, canvas):
  146. '''
  147. Sets the new_status to be the current status
  148. '''
  149. if self.new_status=='dead':
  150. self.set_dead()
  151. elif self.new_status=='live':
  152. self.set_live(canvas)
  153.  
  154.  
  155. ###########################################################
  156. # BOARD CLASS (Read through and understand this part!)
  157. # Print out and turn in this section.
  158. # Name:
  159. # Recitation:
  160. ###########################################################
  161.  
  162. class Board(object):
  163. ''' Board class: it represents the Game of Life board
  164.  
  165. Attributes: width - type:int - width of the board in squares
  166. height - type:int - height of the board in squares
  167. canvas - type:CanvasFrame - where the blocks will be drawn
  168. block_list - type:Dictionary - stores the blocks for a given position
  169. '''
  170.  
  171. def __init__(self, win, width, height):
  172. self.width = width
  173. self.height = height
  174. self.win = win
  175. # self.delay is the number of ms between each simulation. Change to be
  176. # shorter or longer if you wish!
  177. self.delay = DELAY
  178.  
  179. # create a canvas to draw the blocks on
  180. self.canvas = CanvasFrame(win, self.width * BLOCK_SIZE,
  181. self.height * BLOCK_SIZE)
  182. self.canvas.setBackground('white')
  183.  
  184. # initialize grid lines
  185. for x in range(1,self.width):
  186. self.draw_gridline(Point(x, 0), Point(x, self.height))
  187.  
  188. for y in range(1,self.height):
  189. self.draw_gridline(Point(0, y), Point(self.width, y))
  190.  
  191. # For each square on the board, we need to initialize
  192. # a block and store that block in a data structure. A
  193. # dictionary (self.block_list) that has key:value pairs of
  194. # (x,y):Block will be useful here.
  195. self.block_list = {}
  196.  
  197. ####### YOUR CODE HERE ######
  198. ## BLOCK_SIZE = 40
  199. ## BLOCK_OUTLINE_WIDTH = 2
  200. ## BOARD_WIDTH = 12
  201. ## BOARD_HEIGHT = 12
  202. ## # raise Exception("__init__ not implemented")
  203. for x in range(BOARD_WIDTH):
  204. for y in range(BOARD_HEIGHT):
  205. self.block_list[x, y] = Block(Point(x, y), COLOR)
  206.  
  207.  
  208. def draw_gridline(self, startp, endp):
  209. ''' Parameters: startp - a Point of where to start the gridline
  210. endp - a Point of where to end the gridline
  211. Draws two straight 1 pixel lines next to each other, to create
  212. a nice looking grid on the canvas.
  213. '''
  214. line = Line(Point(startp.x*BLOCK_SIZE, startp.y*BLOCK_SIZE), \
  215. Point(endp.x*BLOCK_SIZE, endp.y*BLOCK_SIZE))
  216. line.draw(self.canvas)
  217.  
  218. line = Line(Point(startp.x*BLOCK_SIZE-1, startp.y*BLOCK_SIZE-1), \
  219. Point(endp.x*BLOCK_SIZE-1, endp.y*BLOCK_SIZE-1))
  220. line.draw(self.canvas)
  221.  
  222.  
  223. def random_seed(self, percentage):
  224. ''' Parameters: percentage - a number between 0 and 1 representing the
  225. percentage of the board to be filled with
  226. blocks
  227. This method activates the specified percentage of blocks randomly.
  228. '''
  229. for block in self.block_list.values():
  230. if random.random() < percentage:
  231. block.set_live(self.canvas)
  232.  
  233. def seed(self, block_coords, offset_x_y = (0, 0)):
  234. '''
  235. Seeds the board with a certain configuration.
  236. Takes in a list of (x, y) tuples representing block coordinates,
  237. and activates the blocks corresponding to those coordinates.
  238. '''
  239.  
  240. #### YOUR CODE HERE #####
  241. # raise Exception("seed not implemented")
  242. for coord in block_coords:
  243. coord = (coord[0] + offset_x_y[0], coord[1] + offset_x_y[1])
  244. self.block_list[coord].set_live(self.canvas)
  245.  
  246.  
  247. def get_block_neighbors(self, block):
  248. '''
  249. Given a Block object, returns a list of neighboring blocks.
  250. Should not return itself in the list.
  251. '''
  252. #### YOUR CODE HERE #####
  253. #### Think about edge conditions!
  254. # raise Exception("get_block_neighbors not implemented")
  255. x = block.x
  256. y = block.y
  257. neighborCoordList = []
  258. for col in range(x - 1, x + 2):
  259. for row in range(y - 1, y + 2):
  260. if 0 <= col < BOARD_WIDTH and 0 <= row < BOARD_HEIGHT:
  261. neighborCoordList.append((col, row))
  262. neighborCoordList.remove((x, y))
  263. neighborList = []
  264. for coords in neighborCoordList:
  265. neighborList.append(self.block_list[coords])
  266. return neighborList
  267.  
  268. def simulate(self):
  269. '''
  270. Executes one turn of Conways Game of Life using the rules
  271. listed in the handout. Best approached in a two-step strategy:
  272.  
  273. 1. Calculate the new_status of each block by looking at the
  274. status of its neighbors.
  275.  
  276. 2. Set blocks to 'live' if their new_status is 'live' and their
  277. status is 'dead'. Similarly, set blocks to 'dead' if their
  278. new_status is 'dead' and their status is 'live'. Then, remember
  279. to call reset_status(self.canvas) on each block.
  280. '''
  281.  
  282. #### YOUR CODE HERE #####
  283. for col in range(BOARD_WIDTH):
  284. for row in range(BOARD_HEIGHT):
  285. block = self.block_list[col, row]
  286. n = self.count_live_neighbors(block)
  287. if block.is_live():
  288. if 2 <= n <= 3:
  289. block.new_status = "live"
  290. else:
  291. block.new_status = "dead"
  292. else:
  293. if n == 3:
  294. block.new_status = "live"
  295. else:
  296. block.new_status = "dead"
  297.  
  298. for col in range(BOARD_WIDTH):
  299. for row in range(BOARD_HEIGHT):
  300. block = self.block_list[col, row]
  301. block.reset_status(self.canvas)
  302. # raise Exception("simulate not implemented")
  303. # My helper methods below
  304. def count_live_neighbors(self, block):
  305. count = 0
  306. neighbors = self.get_block_neighbors(block)
  307. for n in neighbors:
  308. if n.is_live():
  309. count += 1
  310. return count
  311. # My helper methods above
  312.  
  313.  
  314.  
  315. def animate(self):
  316. '''
  317. Animates the Game of Life, calling "simulate"
  318. once every second
  319. '''
  320. self.simulate()
  321. self.win.after(self.delay, self.animate)
  322.  
  323.  
  324.  
  325. ################################################################
  326. # RUNNING THE SIMULATION
  327. ################################################################
  328.  
  329. if __name__ == '__main__':
  330. # Initalize board
  331. win = Window("Conway's Game of Life")
  332. board = Board(win, BOARD_WIDTH, BOARD_HEIGHT)
  333.  
  334. ## PART 1: Make sure that the board __init__ method works
  335. # board.random_seed(.15)
  336.  
  337. ## PART 2: Make sure board.seed works. Comment random_seed above and uncomment
  338. ## one of the seed methods below
  339. # board.seed(toad_blocklist)
  340.  
  341. ## PART 3: Test that neighbors work by commenting the above and uncommenting
  342. ## the following two lines:
  343. # board.seed(neighbor_test_blocklist)
  344. # test_neighbors(board)
  345.  
  346.  
  347. ## PART 4: Test that simulate() works by uncommenting the next two lines:
  348.  
  349. # board.seed(blocklist_list[random.randint(0, len(blocklist_list) - 1)])
  350. board.seed(gosper_glider_gun_blocklist)
  351.  
  352. # win.after(2000, board.simulate)
  353.  
  354. ## PART 5: Try animating! Comment out win.after(2000, board.simulate) above, and
  355. ## uncomment win.after below.
  356. win.after(2000, board.animate)
  357.  
  358. ## Yay, you're done! Try seeding with different blocklists (a few are provided at the top of this file!)
  359.  
  360. win.mainloop()
  361.  
  362.  
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty