Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- """
- Comments:
- The program uses the principle that if a cell did not change this turn and none
- of its neighbors changed, then there it won't change this turn and can be
- skipped.
- grid = a boolean 2d array showing the status of all the cells
- gridNext = a boolean 2d array showing status of all cells in the next generation
- activeCell = list of tuples showing cells that either changed or had neighbors
- that changed this turn. This is the list that we iterate over every generation
- didChange = list of tuples of active cells which changed this turn
- didNotChange = list of tuples of active cells which changed this turn.
- if none of their neighbors changed either, remove it from the activeCell list
- so we won't bother looking at it in the next generation.
- #BASIC LOOP
- clear didNotChange()
- clear didChange()
- for each cell in activeCell()
- if alive
- if countOfNeighbors in (LIVERULE)
- add cell to group didNotChange()
- else
- kill
- add all neighbors to group activeCell()
- add cell to group didChange()
- else
- if countofNeighbors in (BIRTHRULE)
- live
- add cell to group didChange()
- add all neighbors to group activeCell()
- else
- add cell to group didNotChange()
- for each cell in didNotChange()
- if all neighbors of that cell did not change this turn:
- remove cell from group activeCell()
- for each cell in didChange()
- draw
- #-----------------
- controls:
- left mouse button: add cell
- s = start / stop
- c = clear board
- """
- # Dependencies ---------------------------------------------------------------
- import pygame, sys
- def mainloop():
- BLACK = ( 0, 0, 0)
- WHITE = ( 255, 255, 255)
- GREY = ( 245, 245, 245)
- FPS = 60
- SIZE = 5 #cell size, in pixels
- MARGIN = 1 #space between cells, in pixels
- # WIDTH, HEIGHT = 120, 80 #board width, height, in cells
- WIDTH, HEIGHT = 300, 200 #board width, height, in cells
- LIVERULE = (2, 3) #neighbors needed for a live cell to keep on living
- BIRTHRULE = (3,) #neighbors needed for a dead cell to become alive
- # cells are identified by their integer index, i.e. (r * WIDTH) + c
- # precompute the adjacencies of each cell by index, and the screen
- # rectangles of each cell by index
- adjacencies = [[((r + dx) % HEIGHT) * WIDTH + (c + dy) % WIDTH
- for dx, dy in ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))]
- for r in range(0, HEIGHT)
- for c in range(0, WIDTH)]
- cellrects = [(MARGIN + (SIZE + MARGIN) * (cell % WIDTH), MARGIN + (SIZE + MARGIN) * (cell / WIDTH), SIZE, SIZE)
- for cell in range(WIDTH * HEIGHT)]
- # the grid is a linear 1-D series of bytes (bytearray() is the mutable version of bytes())
- grid = bytearray([0] * WIDTH * HEIGHT)
- gridNext = bytearray([0] * WIDTH * HEIGHT)
- activeCells = set()
- didNotChange = set()
- didChange = set()
- pygame.init()
- screen = pygame.display.set_mode([((SIZE + MARGIN) * WIDTH - MARGIN),((SIZE + MARGIN) * HEIGHT - MARGIN)])
- pygame.display.set_caption("Game of Life")
- clock = pygame.time.Clock()
- drawrect = pygame.draw.rect
- # wipe the board clean. GREY is the margin color
- def clearBoard():
- screen.fill(GREY)
- for cell in range(HEIGHT * WIDTH):
- grid[cell] = 0
- drawrect(screen, BLACK if grid[cell] else WHITE, cellrects[cell])
- # this takes a (x, y) address as a convenience
- def changeCell(newstate, x, y):
- cell = y * WIDTH + x
- oldval = grid[cell]
- grid[cell] = newstate
- if oldval != newstate:
- didChange.add(cell)
- activeCells.update([cell], adjacencies[cell])
- clearBoard()
- if len(sys.argv) == 2 and sys.argv[1] == 'benchmark':
- benchmarking = True
- playing = True
- frametimes = []
- # place a F-pentamino in the center of the board
- centerx, centery = WIDTH / 2, HEIGHT / 2
- for x, y in ((0, 0), (0, -1), (0, 1), (-1, 0), (1, 1)):
- changeCell(1, x + centerx, y + centery)
- else:
- benchmarking = False
- playing = False
- mousedrag = False
- # Event Handler --------------------------------------------------------------
- while True:
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- pygame.quit()
- sys.exit()
- elif event.type == pygame.KEYDOWN:
- #start / stop game with 's'
- if event.key == ord('s'):
- playing = not playing
- #debug: clear board with 'c'
- elif event.key == ord('c'):
- clearBoard()
- elif event.type == pygame.MOUSEBUTTONDOWN or mousedrag and event.type == pygame.MOUSEMOTION:
- but = getattr(event, 'button', None) or event.buttons.index(1) + 1
- mousedrag = True
- changeCell(1 if but == 1 else 0, event.pos[0] // (SIZE + MARGIN), event.pos[1] // (SIZE + MARGIN))
- elif event.type == pygame.MOUSEBUTTONUP:
- mousedrag = False
- #start / stop game with 's'
- if playing:
- didNotChange.clear()
- didChange.clear()
- for cell in tuple(activeCells):
- neigh = sum(grid[x] for x in adjacencies[cell])
- if grid[cell]:
- if neigh in LIVERULE:
- gridNext[cell] = 1
- didNotChange.add(cell)
- else:
- gridNext[cell] = 0
- activeCells.update([cell], adjacencies[cell])
- didChange.add(cell)
- else:
- if neigh in BIRTHRULE:
- gridNext[cell] = 1
- activeCells.update([cell], adjacencies[cell])
- didChange.add(cell)
- else:
- gridNext[cell] = 0
- didNotChange.add(cell)
- #disable inactive cells (those that did not change this turn)
- for cell in didNotChange:
- if sum(grid[x] != gridNext[x] for x in adjacencies[cell]) == 0:
- activeCells.remove(cell)
- grid[:] = gridNext[:]
- for cell in didChange:
- drawrect(screen, BLACK if grid[cell] else WHITE, cellrects[cell])
- pygame.display.update()
- if not benchmarking:
- clock.tick(FPS)
- else:
- frametimes.append(clock.tick())
- if len(frametimes) >= 1500 and benchmarking:
- f = pygame.font.SysFont('verdana', size=18)
- msg = '{}x{}: {} frames min={}ms avg={}ms max={}ms total={}ms FPS={:.1f}'.format(
- WIDTH, HEIGHT, len(frametimes), min(frametimes), sum(frametimes) / len(frametimes),
- max(frametimes), sum(frametimes), len(frametimes) / (sum(frametimes) / 1000.))
- print(msg)
- message = f.render(msg, True, (0, 0, 255))
- screen.blit(message, ((WIDTH * (SIZE + MARGIN) + MARGIN - message.get_width()) / 2, 50))
- pygame.display.update()
- playing = False
- benchmarking = False
- mainloop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement