680 lines
27 KiB
Python
680 lines
27 KiB
Python
|
# graphicsDisplay.py
|
||
|
# ------------------
|
||
|
# Licensing Information: You are free to use or extend these projects for
|
||
|
# educational purposes provided that (1) you do not distribute or publish
|
||
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||
|
#
|
||
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||
|
# The core projects and autograders were primarily created by John DeNero
|
||
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||
|
# Student side autograding was added by Brad Miller, Nick Hay, and
|
||
|
# Pieter Abbeel (pabbeel@cs.berkeley.edu).
|
||
|
|
||
|
|
||
|
from graphicsUtils import *
|
||
|
import math, time
|
||
|
from game import Directions
|
||
|
|
||
|
###########################
|
||
|
# GRAPHICS DISPLAY CODE #
|
||
|
###########################
|
||
|
|
||
|
# Most code by Dan Klein and John Denero written or rewritten for cs188, UC Berkeley.
|
||
|
# Some code from a Pacman implementation by LiveWires, and used / modified with permission.
|
||
|
|
||
|
DEFAULT_GRID_SIZE = 30.0
|
||
|
INFO_PANE_HEIGHT = 35
|
||
|
BACKGROUND_COLOR = formatColor(0,0,0)
|
||
|
WALL_COLOR = formatColor(0.0/255.0, 51.0/255.0, 255.0/255.0)
|
||
|
INFO_PANE_COLOR = formatColor(.4,.4,0)
|
||
|
SCORE_COLOR = formatColor(.9, .9, .9)
|
||
|
PACMAN_OUTLINE_WIDTH = 2
|
||
|
PACMAN_CAPTURE_OUTLINE_WIDTH = 4
|
||
|
|
||
|
GHOST_COLORS = []
|
||
|
GHOST_COLORS.append(formatColor(.9,0,0)) # Red
|
||
|
GHOST_COLORS.append(formatColor(0,.3,.9)) # Blue
|
||
|
GHOST_COLORS.append(formatColor(.98,.41,.07)) # Orange
|
||
|
GHOST_COLORS.append(formatColor(.1,.75,.7)) # Green
|
||
|
GHOST_COLORS.append(formatColor(1.0,0.6,0.0)) # Yellow
|
||
|
GHOST_COLORS.append(formatColor(.4,0.13,0.91)) # Purple
|
||
|
|
||
|
TEAM_COLORS = GHOST_COLORS[:2]
|
||
|
|
||
|
GHOST_SHAPE = [
|
||
|
( 0, 0.3 ),
|
||
|
( 0.25, 0.75 ),
|
||
|
( 0.5, 0.3 ),
|
||
|
( 0.75, 0.75 ),
|
||
|
( 0.75, -0.5 ),
|
||
|
( 0.5, -0.75 ),
|
||
|
(-0.5, -0.75 ),
|
||
|
(-0.75, -0.5 ),
|
||
|
(-0.75, 0.75 ),
|
||
|
(-0.5, 0.3 ),
|
||
|
(-0.25, 0.75 )
|
||
|
]
|
||
|
GHOST_SIZE = 0.65
|
||
|
SCARED_COLOR = formatColor(1,1,1)
|
||
|
|
||
|
GHOST_VEC_COLORS = map(colorToVector, GHOST_COLORS)
|
||
|
|
||
|
PACMAN_COLOR = formatColor(255.0/255.0,255.0/255.0,61.0/255)
|
||
|
PACMAN_SCALE = 0.5
|
||
|
#pacman_speed = 0.25
|
||
|
|
||
|
# Food
|
||
|
FOOD_COLOR = formatColor(1,1,1)
|
||
|
FOOD_SIZE = 0.1
|
||
|
|
||
|
# Laser
|
||
|
LASER_COLOR = formatColor(1,0,0)
|
||
|
LASER_SIZE = 0.02
|
||
|
|
||
|
# Capsule graphics
|
||
|
CAPSULE_COLOR = formatColor(1,1,1)
|
||
|
CAPSULE_SIZE = 0.25
|
||
|
|
||
|
# Drawing walls
|
||
|
WALL_RADIUS = 0.15
|
||
|
|
||
|
class InfoPane:
|
||
|
def __init__(self, layout, gridSize):
|
||
|
self.gridSize = gridSize
|
||
|
self.width = (layout.width) * gridSize
|
||
|
self.base = (layout.height + 1) * gridSize
|
||
|
self.height = INFO_PANE_HEIGHT
|
||
|
self.fontSize = 24
|
||
|
self.textColor = PACMAN_COLOR
|
||
|
self.drawPane()
|
||
|
|
||
|
def toScreen(self, pos, y = None):
|
||
|
"""
|
||
|
Translates a point relative from the bottom left of the info pane.
|
||
|
"""
|
||
|
if y == None:
|
||
|
x,y = pos
|
||
|
else:
|
||
|
x = pos
|
||
|
|
||
|
x = self.gridSize + x # Margin
|
||
|
y = self.base + y
|
||
|
return x,y
|
||
|
|
||
|
def drawPane(self):
|
||
|
self.scoreText = text( self.toScreen(0, 0 ), self.textColor, "SCORE: 0", "Times", self.fontSize, "bold")
|
||
|
|
||
|
def initializeGhostDistances(self, distances):
|
||
|
self.ghostDistanceText = []
|
||
|
|
||
|
size = 20
|
||
|
if self.width < 240:
|
||
|
size = 12
|
||
|
if self.width < 160:
|
||
|
size = 10
|
||
|
|
||
|
for i, d in enumerate(distances):
|
||
|
t = text( self.toScreen(self.width/2 + self.width/8 * i, 0), GHOST_COLORS[i+1], d, "Times", size, "bold")
|
||
|
self.ghostDistanceText.append(t)
|
||
|
|
||
|
def updateScore(self, score):
|
||
|
changeText(self.scoreText, "SCORE: % 4d" % score)
|
||
|
|
||
|
def setTeam(self, isBlue):
|
||
|
text = "RED TEAM"
|
||
|
if isBlue: text = "BLUE TEAM"
|
||
|
self.teamText = text( self.toScreen(300, 0 ), self.textColor, text, "Times", self.fontSize, "bold")
|
||
|
|
||
|
def updateGhostDistances(self, distances):
|
||
|
if len(distances) == 0: return
|
||
|
if 'ghostDistanceText' not in dir(self): self.initializeGhostDistances(distances)
|
||
|
else:
|
||
|
for i, d in enumerate(distances):
|
||
|
changeText(self.ghostDistanceText[i], d)
|
||
|
|
||
|
def drawGhost(self):
|
||
|
pass
|
||
|
|
||
|
def drawPacman(self):
|
||
|
pass
|
||
|
|
||
|
def drawWarning(self):
|
||
|
pass
|
||
|
|
||
|
def clearIcon(self):
|
||
|
pass
|
||
|
|
||
|
def updateMessage(self, message):
|
||
|
pass
|
||
|
|
||
|
def clearMessage(self):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class PacmanGraphics:
|
||
|
def __init__(self, zoom=1.0, frameTime=0.0, capture=False):
|
||
|
self.have_window = 0
|
||
|
self.currentGhostImages = {}
|
||
|
self.pacmanImage = None
|
||
|
self.zoom = zoom
|
||
|
self.gridSize = DEFAULT_GRID_SIZE * zoom
|
||
|
self.capture = capture
|
||
|
self.frameTime = frameTime
|
||
|
|
||
|
def checkNullDisplay(self):
|
||
|
return False
|
||
|
|
||
|
def initialize(self, state, isBlue = False):
|
||
|
self.isBlue = isBlue
|
||
|
self.startGraphics(state)
|
||
|
|
||
|
# self.drawDistributions(state)
|
||
|
self.distributionImages = None # Initialized lazily
|
||
|
self.drawStaticObjects(state)
|
||
|
self.drawAgentObjects(state)
|
||
|
|
||
|
# Information
|
||
|
self.previousState = state
|
||
|
|
||
|
def startGraphics(self, state):
|
||
|
self.layout = state.layout
|
||
|
layout = self.layout
|
||
|
self.width = layout.width
|
||
|
self.height = layout.height
|
||
|
self.make_window(self.width, self.height)
|
||
|
self.infoPane = InfoPane(layout, self.gridSize)
|
||
|
self.currentState = layout
|
||
|
|
||
|
def drawDistributions(self, state):
|
||
|
walls = state.layout.walls
|
||
|
dist = []
|
||
|
for x in range(walls.width):
|
||
|
distx = []
|
||
|
dist.append(distx)
|
||
|
for y in range(walls.height):
|
||
|
( screen_x, screen_y ) = self.to_screen( (x, y) )
|
||
|
block = square( (screen_x, screen_y),
|
||
|
0.5 * self.gridSize,
|
||
|
color = BACKGROUND_COLOR,
|
||
|
filled = 1, behind=2)
|
||
|
distx.append(block)
|
||
|
self.distributionImages = dist
|
||
|
|
||
|
def drawStaticObjects(self, state):
|
||
|
layout = self.layout
|
||
|
self.drawWalls(layout.walls)
|
||
|
self.food = self.drawFood(layout.food)
|
||
|
self.capsules = self.drawCapsules(layout.capsules)
|
||
|
refresh()
|
||
|
|
||
|
def drawAgentObjects(self, state):
|
||
|
self.agentImages = [] # (agentState, image)
|
||
|
for index, agent in enumerate(state.agentStates):
|
||
|
if agent.isPacman:
|
||
|
image = self.drawPacman(agent, index)
|
||
|
self.agentImages.append( (agent, image) )
|
||
|
else:
|
||
|
image = self.drawGhost(agent, index)
|
||
|
self.agentImages.append( (agent, image) )
|
||
|
refresh()
|
||
|
|
||
|
def swapImages(self, agentIndex, newState):
|
||
|
"""
|
||
|
Changes an image from a ghost to a pacman or vis versa (for capture)
|
||
|
"""
|
||
|
prevState, prevImage = self.agentImages[agentIndex]
|
||
|
for item in prevImage: remove_from_screen(item)
|
||
|
if newState.isPacman:
|
||
|
image = self.drawPacman(newState, agentIndex)
|
||
|
self.agentImages[agentIndex] = (newState, image )
|
||
|
else:
|
||
|
image = self.drawGhost(newState, agentIndex)
|
||
|
self.agentImages[agentIndex] = (newState, image )
|
||
|
refresh()
|
||
|
|
||
|
def update(self, newState):
|
||
|
agentIndex = newState._agentMoved
|
||
|
agentState = newState.agentStates[agentIndex]
|
||
|
|
||
|
if self.agentImages[agentIndex][0].isPacman != agentState.isPacman: self.swapImages(agentIndex, agentState)
|
||
|
prevState, prevImage = self.agentImages[agentIndex]
|
||
|
if agentState.isPacman:
|
||
|
self.animatePacman(agentState, prevState, prevImage)
|
||
|
else:
|
||
|
self.moveGhost(agentState, agentIndex, prevState, prevImage)
|
||
|
self.agentImages[agentIndex] = (agentState, prevImage)
|
||
|
|
||
|
if newState._foodEaten != None:
|
||
|
self.removeFood(newState._foodEaten, self.food)
|
||
|
if newState._capsuleEaten != None:
|
||
|
self.removeCapsule(newState._capsuleEaten, self.capsules)
|
||
|
self.infoPane.updateScore(newState.score)
|
||
|
if 'ghostDistances' in dir(newState):
|
||
|
self.infoPane.updateGhostDistances(newState.ghostDistances)
|
||
|
|
||
|
def make_window(self, width, height):
|
||
|
grid_width = (width-1) * self.gridSize
|
||
|
grid_height = (height-1) * self.gridSize
|
||
|
screen_width = 2*self.gridSize + grid_width
|
||
|
screen_height = 2*self.gridSize + grid_height + INFO_PANE_HEIGHT
|
||
|
|
||
|
begin_graphics(screen_width,
|
||
|
screen_height,
|
||
|
BACKGROUND_COLOR,
|
||
|
"CS188 Pacman")
|
||
|
|
||
|
def drawPacman(self, pacman, index):
|
||
|
position = self.getPosition(pacman)
|
||
|
screen_point = self.to_screen(position)
|
||
|
endpoints = self.getEndpoints(self.getDirection(pacman))
|
||
|
|
||
|
width = PACMAN_OUTLINE_WIDTH
|
||
|
outlineColor = PACMAN_COLOR
|
||
|
fillColor = PACMAN_COLOR
|
||
|
|
||
|
if self.capture:
|
||
|
outlineColor = TEAM_COLORS[index % 2]
|
||
|
fillColor = GHOST_COLORS[index]
|
||
|
width = PACMAN_CAPTURE_OUTLINE_WIDTH
|
||
|
|
||
|
return [circle(screen_point, PACMAN_SCALE * self.gridSize,
|
||
|
fillColor = fillColor, outlineColor = outlineColor,
|
||
|
endpoints = endpoints,
|
||
|
width = width)]
|
||
|
|
||
|
def getEndpoints(self, direction, position=(0,0)):
|
||
|
x, y = position
|
||
|
pos = x - int(x) + y - int(y)
|
||
|
width = 30 + 80 * math.sin(math.pi* pos)
|
||
|
|
||
|
delta = width / 2
|
||
|
if (direction == 'West'):
|
||
|
endpoints = (180+delta, 180-delta)
|
||
|
elif (direction == 'North'):
|
||
|
endpoints = (90+delta, 90-delta)
|
||
|
elif (direction == 'South'):
|
||
|
endpoints = (270+delta, 270-delta)
|
||
|
else:
|
||
|
endpoints = (0+delta, 0-delta)
|
||
|
return endpoints
|
||
|
|
||
|
def movePacman(self, position, direction, image):
|
||
|
screenPosition = self.to_screen(position)
|
||
|
endpoints = self.getEndpoints( direction, position )
|
||
|
r = PACMAN_SCALE * self.gridSize
|
||
|
moveCircle(image[0], screenPosition, r, endpoints)
|
||
|
refresh()
|
||
|
|
||
|
def animatePacman(self, pacman, prevPacman, image):
|
||
|
if self.frameTime < 0:
|
||
|
print 'Press any key to step forward, "q" to play'
|
||
|
keys = wait_for_keys()
|
||
|
if 'q' in keys:
|
||
|
self.frameTime = 0.1
|
||
|
if self.frameTime > 0.01 or self.frameTime < 0:
|
||
|
start = time.time()
|
||
|
fx, fy = self.getPosition(prevPacman)
|
||
|
px, py = self.getPosition(pacman)
|
||
|
frames = 4.0
|
||
|
for i in range(1,int(frames) + 1):
|
||
|
pos = px*i/frames + fx*(frames-i)/frames, py*i/frames + fy*(frames-i)/frames
|
||
|
self.movePacman(pos, self.getDirection(pacman), image)
|
||
|
refresh()
|
||
|
sleep(abs(self.frameTime) / frames)
|
||
|
else:
|
||
|
self.movePacman(self.getPosition(pacman), self.getDirection(pacman), image)
|
||
|
refresh()
|
||
|
|
||
|
def getGhostColor(self, ghost, ghostIndex):
|
||
|
if ghost.scaredTimer > 0:
|
||
|
return SCARED_COLOR
|
||
|
else:
|
||
|
return GHOST_COLORS[ghostIndex]
|
||
|
|
||
|
def drawGhost(self, ghost, agentIndex):
|
||
|
pos = self.getPosition(ghost)
|
||
|
dir = self.getDirection(ghost)
|
||
|
(screen_x, screen_y) = (self.to_screen(pos) )
|
||
|
coords = []
|
||
|
for (x, y) in GHOST_SHAPE:
|
||
|
coords.append((x*self.gridSize*GHOST_SIZE + screen_x, y*self.gridSize*GHOST_SIZE + screen_y))
|
||
|
|
||
|
colour = self.getGhostColor(ghost, agentIndex)
|
||
|
body = polygon(coords, colour, filled = 1)
|
||
|
WHITE = formatColor(1.0, 1.0, 1.0)
|
||
|
BLACK = formatColor(0.0, 0.0, 0.0)
|
||
|
|
||
|
dx = 0
|
||
|
dy = 0
|
||
|
if dir == 'North':
|
||
|
dy = -0.2
|
||
|
if dir == 'South':
|
||
|
dy = 0.2
|
||
|
if dir == 'East':
|
||
|
dx = 0.2
|
||
|
if dir == 'West':
|
||
|
dx = -0.2
|
||
|
leftEye = circle((screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx/1.5), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2, WHITE, WHITE)
|
||
|
rightEye = circle((screen_x+self.gridSize*GHOST_SIZE*(0.3+dx/1.5), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2, WHITE, WHITE)
|
||
|
leftPupil = circle((screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08, BLACK, BLACK)
|
||
|
rightPupil = circle((screen_x+self.gridSize*GHOST_SIZE*(0.3+dx), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08, BLACK, BLACK)
|
||
|
ghostImageParts = []
|
||
|
ghostImageParts.append(body)
|
||
|
ghostImageParts.append(leftEye)
|
||
|
ghostImageParts.append(rightEye)
|
||
|
ghostImageParts.append(leftPupil)
|
||
|
ghostImageParts.append(rightPupil)
|
||
|
|
||
|
return ghostImageParts
|
||
|
|
||
|
def moveEyes(self, pos, dir, eyes):
|
||
|
(screen_x, screen_y) = (self.to_screen(pos) )
|
||
|
dx = 0
|
||
|
dy = 0
|
||
|
if dir == 'North':
|
||
|
dy = -0.2
|
||
|
if dir == 'South':
|
||
|
dy = 0.2
|
||
|
if dir == 'East':
|
||
|
dx = 0.2
|
||
|
if dir == 'West':
|
||
|
dx = -0.2
|
||
|
moveCircle(eyes[0],(screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx/1.5), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2)
|
||
|
moveCircle(eyes[1],(screen_x+self.gridSize*GHOST_SIZE*(0.3+dx/1.5), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2)
|
||
|
moveCircle(eyes[2],(screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08)
|
||
|
moveCircle(eyes[3],(screen_x+self.gridSize*GHOST_SIZE*(0.3+dx), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08)
|
||
|
|
||
|
def moveGhost(self, ghost, ghostIndex, prevGhost, ghostImageParts):
|
||
|
old_x, old_y = self.to_screen(self.getPosition(prevGhost))
|
||
|
new_x, new_y = self.to_screen(self.getPosition(ghost))
|
||
|
delta = new_x - old_x, new_y - old_y
|
||
|
|
||
|
for ghostImagePart in ghostImageParts:
|
||
|
move_by(ghostImagePart, delta)
|
||
|
refresh()
|
||
|
|
||
|
if ghost.scaredTimer > 0:
|
||
|
color = SCARED_COLOR
|
||
|
else:
|
||
|
color = GHOST_COLORS[ghostIndex]
|
||
|
edit(ghostImageParts[0], ('fill', color), ('outline', color))
|
||
|
self.moveEyes(self.getPosition(ghost), self.getDirection(ghost), ghostImageParts[-4:])
|
||
|
refresh()
|
||
|
|
||
|
def getPosition(self, agentState):
|
||
|
if agentState.configuration == None: return (-1000, -1000)
|
||
|
return agentState.getPosition()
|
||
|
|
||
|
def getDirection(self, agentState):
|
||
|
if agentState.configuration == None: return Directions.STOP
|
||
|
return agentState.configuration.getDirection()
|
||
|
|
||
|
def finish(self):
|
||
|
end_graphics()
|
||
|
|
||
|
def to_screen(self, point):
|
||
|
( x, y ) = point
|
||
|
#y = self.height - y
|
||
|
x = (x + 1)*self.gridSize
|
||
|
y = (self.height - y)*self.gridSize
|
||
|
return ( x, y )
|
||
|
|
||
|
# Fixes some TK issue with off-center circles
|
||
|
def to_screen2(self, point):
|
||
|
( x, y ) = point
|
||
|
#y = self.height - y
|
||
|
x = (x + 1)*self.gridSize
|
||
|
y = (self.height - y)*self.gridSize
|
||
|
return ( x, y )
|
||
|
|
||
|
def drawWalls(self, wallMatrix):
|
||
|
wallColor = WALL_COLOR
|
||
|
for xNum, x in enumerate(wallMatrix):
|
||
|
if self.capture and (xNum * 2) < wallMatrix.width: wallColor = TEAM_COLORS[0]
|
||
|
if self.capture and (xNum * 2) >= wallMatrix.width: wallColor = TEAM_COLORS[1]
|
||
|
|
||
|
for yNum, cell in enumerate(x):
|
||
|
if cell: # There's a wall here
|
||
|
pos = (xNum, yNum)
|
||
|
screen = self.to_screen(pos)
|
||
|
screen2 = self.to_screen2(pos)
|
||
|
|
||
|
# draw each quadrant of the square based on adjacent walls
|
||
|
wIsWall = self.isWall(xNum-1, yNum, wallMatrix)
|
||
|
eIsWall = self.isWall(xNum+1, yNum, wallMatrix)
|
||
|
nIsWall = self.isWall(xNum, yNum+1, wallMatrix)
|
||
|
sIsWall = self.isWall(xNum, yNum-1, wallMatrix)
|
||
|
nwIsWall = self.isWall(xNum-1, yNum+1, wallMatrix)
|
||
|
swIsWall = self.isWall(xNum-1, yNum-1, wallMatrix)
|
||
|
neIsWall = self.isWall(xNum+1, yNum+1, wallMatrix)
|
||
|
seIsWall = self.isWall(xNum+1, yNum-1, wallMatrix)
|
||
|
|
||
|
# NE quadrant
|
||
|
if (not nIsWall) and (not eIsWall):
|
||
|
# inner circle
|
||
|
circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (0,91), 'arc')
|
||
|
if (nIsWall) and (not eIsWall):
|
||
|
# vertical line
|
||
|
line(add(screen, (self.gridSize*WALL_RADIUS, 0)), add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(-0.5)-1)), wallColor)
|
||
|
if (not nIsWall) and (eIsWall):
|
||
|
# horizontal line
|
||
|
line(add(screen, (0, self.gridSize*(-1)*WALL_RADIUS)), add(screen, (self.gridSize*0.5+1, self.gridSize*(-1)*WALL_RADIUS)), wallColor)
|
||
|
if (nIsWall) and (eIsWall) and (not neIsWall):
|
||
|
# outer circle
|
||
|
circle(add(screen2, (self.gridSize*2*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS)), WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (180,271), 'arc')
|
||
|
line(add(screen, (self.gridSize*2*WALL_RADIUS-1, self.gridSize*(-1)*WALL_RADIUS)), add(screen, (self.gridSize*0.5+1, self.gridSize*(-1)*WALL_RADIUS)), wallColor)
|
||
|
line(add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS+1)), add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(-0.5))), wallColor)
|
||
|
|
||
|
# NW quadrant
|
||
|
if (not nIsWall) and (not wIsWall):
|
||
|
# inner circle
|
||
|
circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (90,181), 'arc')
|
||
|
if (nIsWall) and (not wIsWall):
|
||
|
# vertical line
|
||
|
line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, 0)), add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(-0.5)-1)), wallColor)
|
||
|
if (not nIsWall) and (wIsWall):
|
||
|
# horizontal line
|
||
|
line(add(screen, (0, self.gridSize*(-1)*WALL_RADIUS)), add(screen, (self.gridSize*(-0.5)-1, self.gridSize*(-1)*WALL_RADIUS)), wallColor)
|
||
|
if (nIsWall) and (wIsWall) and (not nwIsWall):
|
||
|
# outer circle
|
||
|
circle(add(screen2, (self.gridSize*(-2)*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS)), WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (270,361), 'arc')
|
||
|
line(add(screen, (self.gridSize*(-2)*WALL_RADIUS+1, self.gridSize*(-1)*WALL_RADIUS)), add(screen, (self.gridSize*(-0.5), self.gridSize*(-1)*WALL_RADIUS)), wallColor)
|
||
|
line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS+1)), add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(-0.5))), wallColor)
|
||
|
|
||
|
# SE quadrant
|
||
|
if (not sIsWall) and (not eIsWall):
|
||
|
# inner circle
|
||
|
circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (270,361), 'arc')
|
||
|
if (sIsWall) and (not eIsWall):
|
||
|
# vertical line
|
||
|
line(add(screen, (self.gridSize*WALL_RADIUS, 0)), add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(0.5)+1)), wallColor)
|
||
|
if (not sIsWall) and (eIsWall):
|
||
|
# horizontal line
|
||
|
line(add(screen, (0, self.gridSize*(1)*WALL_RADIUS)), add(screen, (self.gridSize*0.5+1, self.gridSize*(1)*WALL_RADIUS)), wallColor)
|
||
|
if (sIsWall) and (eIsWall) and (not seIsWall):
|
||
|
# outer circle
|
||
|
circle(add(screen2, (self.gridSize*2*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS)), WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (90,181), 'arc')
|
||
|
line(add(screen, (self.gridSize*2*WALL_RADIUS-1, self.gridSize*(1)*WALL_RADIUS)), add(screen, (self.gridSize*0.5, self.gridSize*(1)*WALL_RADIUS)), wallColor)
|
||
|
line(add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS-1)), add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(0.5))), wallColor)
|
||
|
|
||
|
# SW quadrant
|
||
|
if (not sIsWall) and (not wIsWall):
|
||
|
# inner circle
|
||
|
circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (180,271), 'arc')
|
||
|
if (sIsWall) and (not wIsWall):
|
||
|
# vertical line
|
||
|
line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, 0)), add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(0.5)+1)), wallColor)
|
||
|
if (not sIsWall) and (wIsWall):
|
||
|
# horizontal line
|
||
|
line(add(screen, (0, self.gridSize*(1)*WALL_RADIUS)), add(screen, (self.gridSize*(-0.5)-1, self.gridSize*(1)*WALL_RADIUS)), wallColor)
|
||
|
if (sIsWall) and (wIsWall) and (not swIsWall):
|
||
|
# outer circle
|
||
|
circle(add(screen2, (self.gridSize*(-2)*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS)), WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (0,91), 'arc')
|
||
|
line(add(screen, (self.gridSize*(-2)*WALL_RADIUS+1, self.gridSize*(1)*WALL_RADIUS)), add(screen, (self.gridSize*(-0.5), self.gridSize*(1)*WALL_RADIUS)), wallColor)
|
||
|
line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS-1)), add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(0.5))), wallColor)
|
||
|
|
||
|
def isWall(self, x, y, walls):
|
||
|
if x < 0 or y < 0:
|
||
|
return False
|
||
|
if x >= walls.width or y >= walls.height:
|
||
|
return False
|
||
|
return walls[x][y]
|
||
|
|
||
|
def drawFood(self, foodMatrix ):
|
||
|
foodImages = []
|
||
|
color = FOOD_COLOR
|
||
|
for xNum, x in enumerate(foodMatrix):
|
||
|
if self.capture and (xNum * 2) <= foodMatrix.width: color = TEAM_COLORS[0]
|
||
|
if self.capture and (xNum * 2) > foodMatrix.width: color = TEAM_COLORS[1]
|
||
|
imageRow = []
|
||
|
foodImages.append(imageRow)
|
||
|
for yNum, cell in enumerate(x):
|
||
|
if cell: # There's food here
|
||
|
screen = self.to_screen((xNum, yNum ))
|
||
|
dot = circle( screen,
|
||
|
FOOD_SIZE * self.gridSize,
|
||
|
outlineColor = color, fillColor = color,
|
||
|
width = 1)
|
||
|
imageRow.append(dot)
|
||
|
else:
|
||
|
imageRow.append(None)
|
||
|
return foodImages
|
||
|
|
||
|
def drawCapsules(self, capsules ):
|
||
|
capsuleImages = {}
|
||
|
for capsule in capsules:
|
||
|
( screen_x, screen_y ) = self.to_screen(capsule)
|
||
|
dot = circle( (screen_x, screen_y),
|
||
|
CAPSULE_SIZE * self.gridSize,
|
||
|
outlineColor = CAPSULE_COLOR,
|
||
|
fillColor = CAPSULE_COLOR,
|
||
|
width = 1)
|
||
|
capsuleImages[capsule] = dot
|
||
|
return capsuleImages
|
||
|
|
||
|
def removeFood(self, cell, foodImages ):
|
||
|
x, y = cell
|
||
|
remove_from_screen(foodImages[x][y])
|
||
|
|
||
|
def removeCapsule(self, cell, capsuleImages ):
|
||
|
x, y = cell
|
||
|
remove_from_screen(capsuleImages[(x, y)])
|
||
|
|
||
|
def drawExpandedCells(self, cells):
|
||
|
"""
|
||
|
Draws an overlay of expanded grid positions for search agents
|
||
|
"""
|
||
|
n = float(len(cells))
|
||
|
baseColor = [1.0, 0.0, 0.0]
|
||
|
self.clearExpandedCells()
|
||
|
self.expandedCells = []
|
||
|
for k, cell in enumerate(cells):
|
||
|
screenPos = self.to_screen( cell)
|
||
|
cellColor = formatColor(*[(n-k) * c * .5 / n + .25 for c in baseColor])
|
||
|
block = square(screenPos,
|
||
|
0.5 * self.gridSize,
|
||
|
color = cellColor,
|
||
|
filled = 1, behind=2)
|
||
|
self.expandedCells.append(block)
|
||
|
if self.frameTime < 0:
|
||
|
refresh()
|
||
|
|
||
|
def clearExpandedCells(self):
|
||
|
if 'expandedCells' in dir(self) and len(self.expandedCells) > 0:
|
||
|
for cell in self.expandedCells:
|
||
|
remove_from_screen(cell)
|
||
|
|
||
|
|
||
|
def updateDistributions(self, distributions):
|
||
|
"Draws an agent's belief distributions"
|
||
|
# copy all distributions so we don't change their state
|
||
|
distributions = map(lambda x: x.copy(), distributions)
|
||
|
if self.distributionImages == None:
|
||
|
self.drawDistributions(self.previousState)
|
||
|
for x in range(len(self.distributionImages)):
|
||
|
for y in range(len(self.distributionImages[0])):
|
||
|
image = self.distributionImages[x][y]
|
||
|
weights = [dist[ (x,y) ] for dist in distributions]
|
||
|
|
||
|
if sum(weights) != 0:
|
||
|
pass
|
||
|
# Fog of war
|
||
|
color = [0.0,0.0,0.0]
|
||
|
colors = GHOST_VEC_COLORS[1:] # With Pacman
|
||
|
if self.capture: colors = GHOST_VEC_COLORS
|
||
|
for weight, gcolor in zip(weights, colors):
|
||
|
color = [min(1.0, c + 0.95 * g * weight ** .3) for c,g in zip(color, gcolor)]
|
||
|
changeColor(image, formatColor(*color))
|
||
|
refresh()
|
||
|
|
||
|
class FirstPersonPacmanGraphics(PacmanGraphics):
|
||
|
def __init__(self, zoom = 1.0, showGhosts = True, capture = False, frameTime=0):
|
||
|
PacmanGraphics.__init__(self, zoom, frameTime=frameTime)
|
||
|
self.showGhosts = showGhosts
|
||
|
self.capture = capture
|
||
|
|
||
|
def initialize(self, state, isBlue = False):
|
||
|
|
||
|
self.isBlue = isBlue
|
||
|
PacmanGraphics.startGraphics(self, state)
|
||
|
# Initialize distribution images
|
||
|
walls = state.layout.walls
|
||
|
dist = []
|
||
|
self.layout = state.layout
|
||
|
|
||
|
# Draw the rest
|
||
|
self.distributionImages = None # initialize lazily
|
||
|
self.drawStaticObjects(state)
|
||
|
self.drawAgentObjects(state)
|
||
|
|
||
|
# Information
|
||
|
self.previousState = state
|
||
|
|
||
|
def lookAhead(self, config, state):
|
||
|
if config.getDirection() == 'Stop':
|
||
|
return
|
||
|
else:
|
||
|
pass
|
||
|
# Draw relevant ghosts
|
||
|
allGhosts = state.getGhostStates()
|
||
|
visibleGhosts = state.getVisibleGhosts()
|
||
|
for i, ghost in enumerate(allGhosts):
|
||
|
if ghost in visibleGhosts:
|
||
|
self.drawGhost(ghost, i)
|
||
|
else:
|
||
|
self.currentGhostImages[i] = None
|
||
|
|
||
|
def getGhostColor(self, ghost, ghostIndex):
|
||
|
return GHOST_COLORS[ghostIndex]
|
||
|
|
||
|
def getPosition(self, ghostState):
|
||
|
if not self.showGhosts and not ghostState.isPacman and ghostState.getPosition()[1] > 1:
|
||
|
return (-1000, -1000)
|
||
|
else:
|
||
|
return PacmanGraphics.getPosition(self, ghostState)
|
||
|
|
||
|
def add(x, y):
|
||
|
return (x[0] + y[0], x[1] + y[1])
|
||
|
|
||
|
|
||
|
# Saving graphical output
|
||
|
# -----------------------
|
||
|
# Note: to make an animated gif from this postscript output, try the command:
|
||
|
# convert -delay 7 -loop 1 -compress lzw -layers optimize frame* out.gif
|
||
|
# convert is part of imagemagick (freeware)
|
||
|
|
||
|
SAVE_POSTSCRIPT = False
|
||
|
POSTSCRIPT_OUTPUT_DIR = 'frames'
|
||
|
FRAME_NUMBER = 0
|
||
|
import os
|
||
|
|
||
|
def saveFrame():
|
||
|
"Saves the current graphical output as a postscript file"
|
||
|
global SAVE_POSTSCRIPT, FRAME_NUMBER, POSTSCRIPT_OUTPUT_DIR
|
||
|
if not SAVE_POSTSCRIPT: return
|
||
|
if not os.path.exists(POSTSCRIPT_OUTPUT_DIR): os.mkdir(POSTSCRIPT_OUTPUT_DIR)
|
||
|
name = os.path.join(POSTSCRIPT_OUTPUT_DIR, 'frame_%08d.ps' % FRAME_NUMBER)
|
||
|
FRAME_NUMBER += 1
|
||
|
writePostscript(name) # writes the current canvas
|