- # coding=UTF-8
- import sys, os
- import math
- import time
- import random
- import tkFileDialog
- import Tkinter
- import pyglet
- from pyglet.window import key, mouse
- from pyglet import clock
- import fileutils
- from lifepattern import LifePattern
- # CONSTANTS
- OFF_COLOR = (200, 200, 200)
- # VARIABLES
- root = Tkinter.Tk()
- window = pyglet.window.Window(width=800, height=600)
- window.set_caption('Conway\'s Game Of Life')
- # grid
- curX = 0
- curY = 0
- gridDims = {}
- # cells
- cellBatch = pyglet.graphics.Batch()
- cellVLists = []
- curOnCells = set()
- otherOnCells = set()
- cellDims = {}
- curCellDim = 15.0
- # input
- mVars = {}
- keys = key.KeyStateHandler()
- # text
- infoDoc = pyglet.text.document.UnformattedDocument('')
- infoDoc.set_style(None, None,
- {'color': (0, 0, 0, 255),
- 'background_color': (255, 255, 255, 255),
- 'font_name': 'Arial',
- 'font_size': 12})
- infoLayout = pyglet.text.layout.TextLayout(infoDoc)
- infoLayout.anchor_y = 'bottom'
- # misc
- isRunning = False
- curPattern = None
- # 0 = 0°
- # 1 = 90° clockwise
- # 2 = 180°
- # 3 = 270°
- rotation = 0
- # FUNCTIONS
- def configGrid(desiredCellDim):
- '''
- resets the graphical aspect of the grid, the number and size of cells.
- does not affect actual cell on/off data.
- desiredCellDim is the desired width/height of the cell in pixels.
- actual size will vary slightly to fit a whole number of rows and columns on screen.
- this should be called when zooming in/out and when window changes size.
- '''
- cellDims['w'] = window.width / (window.width // desiredCellDim)
- cellDims['h'] = window.height / (window.height // desiredCellDim)
- cellDims['borderW'] = 1.0 if cellDims > 1 else 0.0
- gridDims['wInCells'] = int(round(window.width / cellDims['w']))
- gridDims['hInCells'] = int(round(window.height / cellDims['h']))
- # clear vertex list
- del cellVLists[:]
- # no way to clear a Batch :(
- global cellBatch
- cellBatch = pyglet.graphics.Batch()
- for x in xrange(gridDims['wInCells']):
- cellVLists.append([])
- for y in xrange(gridDims['hInCells']):
- blitX, blitY = x * cellDims['w'], y * cellDims['h']
- cellVLists[-1].append(cellBatch.add(4, pyglet.gl.GL_QUADS, None,
- ('v2f', (blitX + cellDims['borderW'] / 2, blitY + cellDims['borderW'] / 2,
- blitX + cellDims['w'] - cellDims['borderW'] / 2, blitY + cellDims['borderW'] / 2,
- blitX + cellDims['w'] - cellDims['borderW'] / 2, blitY + cellDims['h'] - cellDims['borderW'] / 2,
- blitX + cellDims['borderW'] / 2, blitY + cellDims['h'] - cellDims['borderW'] / 2)),
- ('c3B', OFF_COLOR * 4)
- ))
- updateGrid()
- def resetMVars():
- mVars['curChange'] = None
- mVars['cellsPassed'] = set()
- def changeCell(x, y, which, updateOnCells = True):
- '''
- updates the display of cell.
- if updateOnCells is True, also changes curOnCells
- '''
- if which == 'on':
- desired = True
- elif which == 'off':
- desired = False
- else:
- raise ArgumentError, '3rd argument must be "on" or "off"'
- if updateOnCells:
- if desired:
- curOnCells.add((x, y))
- else:
- curOnCells.discard((x, y))
- x, y = x - curX, y - curY
- if (x < gridDims['wInCells'] and x >= 0 and
- y < gridDims['hInCells'] and y >= 0):
- if desired:
- cellVLists[x][y].colors = (0, 0, 0) * 4
- else:
- cellVLists[x][y].colors = OFF_COLOR * 4
- def updateGrid():
- for x in xrange(curX, curX + gridDims['wInCells']):
- for y in xrange(curY, curY + gridDims['hInCells']):
- if (x, y) in curOnCells:
- cellVLists[x - curX][y - curY].colors = (0, 0, 0) * 4
- else:
- cellVLists[x - curX][y - curY].colors = OFF_COLOR * 4
- _counts = {}
- def runOne():
- global curOnCells, otherOnCells
- _counts.clear()
- otherOnCells.clear()
- for cell in curOnCells:
- for x in xrange(cell[0] - 1, cell[0] + 2):
- for y in xrange(cell[1] - 1, cell[1] + 2):
- if (x, y) != cell:
- try:
- _counts[(x, y)] += 1
- except KeyError:
- _counts[(x, y)] = 1
- for cell in _counts:
- if _counts[cell] == 3 or (_counts[cell] == 2 and cell in curOnCells):
- otherOnCells.add(cell)
- for cell in curOnCells - otherOnCells:
- changeCell(cell[0], cell[1], 'off', updateOnCells = False)
- for cell in otherOnCells - curOnCells:
- changeCell(cell[0], cell[1], 'on', updateOnCells = False)
- curOnCells, otherOnCells = otherOnCells, curOnCells
- def insertPattern(x, y, pattern):
- unencoded = pattern.getUnencoded()
- for dy, line in enumerate(unencoded):
- for dx, cell in enumerate(line):
- if rotation == 0:
- changeCell(x + dx, y - dy, 'on' if cell else 'off')
- elif rotation == 1:
- changeCell(x + pattern.height - dy, y - dx, 'on' if cell else 'off')
- elif rotation == 2:
- changeCell(x + pattern.width - dx, y - pattern.height + dy, 'on' if cell else 'off')
- elif rotation == 3:
- changeCell(x + dy, y - pattern.width + dx, 'on' if cell else 'off')
- def setInfoText():
- infoDoc.text = (' Current pattern: %s' % str(curPattern)
- + (' ' if curPattern else ' - press enter to open a pattern')
- + u' || Rotation: %d° CW' % (rotation * 90))
- # EVENTS
- @window.event
- def on_draw():
- window.clear()
- if keys[key.UP] or keys[key.DOWN] or keys[key.RIGHT] or keys[key.LEFT]:
- global curX, curY
- curY += (keys[key.UP] - keys[key.DOWN]) * int(20 / cellDims['w'])
- curX += (keys[key.RIGHT] - keys[key.LEFT]) * int(20 / cellDims['w'])
- updateGrid()
- cellBatch.draw()
- infoLayout.draw()
- @window.event
- def on_mouse_press(x, y, button, modifiers):
- if button == mouse.LEFT:
- cellX, cellY = int(x // cellDims['w']) + curX, int(y // cellDims['h']) + curY
- if (cellX, cellY) in curOnCells:
- mVars['curChange'] = 'off'
- else:
- mVars['curChange'] = 'on'
- changeCell(cellX, cellY, mVars['curChange'])
- mVars['cellsPassed'].add((cellX, cellY))
- elif button == mouse.RIGHT:
- cellX, cellY = int(x // cellDims['w']) + curX, int(y // cellDims['h']) + curY
- if curPattern:
- insertPattern(cellX, cellY, curPattern)
- @window.event
- def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
- if buttons & mouse.LEFT:
- # draw through all cells in line dragged through
- factor = math.sqrt(x * x + y * y)
- dx, dy = dx / factor, dy / factor
- for i in xrange(int(factor)):
- cellX, cellY = int(x // cellDims['w']) + curX, int(y // cellDims['h']) + curY
- if ((cellX, cellY) not in mVars['cellsPassed'] and
- cellX >= curX and cellX < curX + gridDims['wInCells'] and
- cellY >= curY and cellY < curY + gridDims['wInCells']):
- if (cellX, cellY) in curOnCells and mVars['curChange'] == 'off':
- changeCell(cellX, cellY, mVars['curChange'])
- elif (cellX, cellY) not in curOnCells and mVars['curChange'] == 'on':
- changeCell(cellX, cellY, mVars['curChange'])
- mVars['cellsPassed'].add((cellX, cellY))
- x -= dx
- y -= dy
- @window.event
- def on_mouse_release(x, y, button, modifiers):
- resetMVars()
- @window.event
- def on_key_press(symbol, modifiers):
- if symbol == key.SPACE:
- global isRunning
- isRunning = not isRunning
- elif symbol == key.C:
- curOnCells.clear()
- updateGrid()
- elif symbol == key.PLUS:
- global curCellDim
- curCellDim += 1
- configGrid(curCellDim)
- elif symbol == key.MINUS:
- global curCellDim
- curCellDim -= 1
- configGrid(curCellDim)
- elif symbol == key.ENTER:
- global curPattern
- filename = tkFileDialog.askopenfilename()
- ## root.destroy()
- if filename:
- curPattern = LifePattern(filename)
- setInfoText()
- elif symbol == key.R:
- global rotation
- rotation = (rotation + 1) % 4
- setInfoText()
- # CLOCK
- def runPerFrame(dt):
- if isRunning:
- runOne()
- # INIT
- root.withdraw()
- resetMVars()
- window.push_handlers(keys)
- clock.schedule(runPerFrame)
- configGrid(curCellDim)
- setInfoText()
- pyglet.app.run()
life
Posted by Anonymous on Wed 5th Jan 2011 23:42
raw | new post
Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.