## Necessary Primitives/Structures:

### Lists — Lists

Lists can be constructed as a sequence of objects inside square brackets; e.g; [2,4,6]. The list function also converts other types of sequence data such as strings into lists.
Sequence operations such as indexing l[i], concatenation +, length len(l) and slicing apply to lists.
As opposed to strings, an element of a list can be mutated via assignment l[i] = x.
Lecture examples - None
More examples - Structure, True-False Quiz, Silly Words, Rainbow Canvas, Digital Numbers

### Points and vectors — Motion

A point in 2D is represented by a pair of Cartesian coordinates.
A vector in 2D is represented by a pair of numbers (its horizontal and vertical components).
Taking the difference of two points (componentwise) yields a vector.
Vectors can be added and scaled (componentwise). Points should not be added or scaled.
Adding a point and a vector (componentwise) yields a new point. This operation can be used to animate moving points.
Lecture examples - Timer Control, Formula Control
More examples - Drawing Vectors

### Distance computations — Collisions and reflections

The distance between two points p0 and p1 is
√(p0[0]-p1[0])2+(p0[1]-p1[1])2 .
The distance between a point and a circle and the distance between two circles follows from this formula.
The set of points [x,y] that satisfy the equation a*x + b*y + c == 0 is a line. The distance from this line to a point p is
(a*p[0] + b*p[1] + c)/ √a2 + b2
The distance from a circle to a line is the distance from the center of the circle to the line minus the radius.

### Reflections — Collisions and reflections

The direction of reflection for a ball bouncing off of a wall depends on the incoming velocity vector v and the normal vector to the wall at the point of contact.
The incoming velocity vector can be decomposed into v = vp + vn where vn is the component of v orthogonal to wall and vp is the component parallel to to the wall.
In this model, the reflected vector is v = vp - vn.
This model simplifies for horizontal and vertical walls. In particular, reflection simply negates one component of the velocity vector v.

### Keyboard events — Keyboard input

SimpleGUI suppports two event handlers for keyboard events.
The key down event handler is registered via set_keydown_handler.
The key up event handler is registered via set_keyup_handler.
The variable passed to each of these handlers is a number that can be compared at the constants in KEY_MAP to determine which key has been pressed.

### Positional control — Keyboard input

We can control the position of a point p directly using key board events.
The horizontal and vertical components of a point p's position can be increment/decrement via p[i] += c in response to key presses.

### Velocity control — Velocity control

Basic physics relates the position of a point p and its velocity v via the equation p += dt * v where dt is a small time step.
In this model, we can control the motion of p via keyup/keydown events that increment/decrement the two components of the velocity vector v via v[i] += c.

### Mutable vs. immutable data — Programming tips #4

Numbers, Booleans and strings are immutable and can not be modified. Only new copies of these kinds of data can be made.
On the other hand, parts of a list can be mutated via assignment to individual elements of the list.
Assignment of an entire list to a variable generates a reference that refers to the list. Subsequent assignment may generate multiple references to the same list.
Mutating a list with multiple references modifies all references to the list. This capability is very useful, but can generate subtle errors.

Code Implementation:

######################################
## Implementation of Pac-Man #########
##     Manuel Augusto Antao  #########
##     Python Class          #########
##     Rice University       #########
##     Extra-Project         #########
######################################

import simplegui
import math
import random
import user17_9jVc7rMrHiUCj84

# Images

# sounds
'''
Some or all of the sounds were obtained from the following sites:
http://www.playonloop.com/help-and-faqs/#faq5
http://www.noiseforfun.com/help-and-faqs/#faq5
http://www.sweetsoundeffects.com
'''

# direction_to_vel dictionary {direction: (forbidden_direction, velocity, angle)}
direction_to_vel = {"left" : ("right", (-1, 0), math.pi),
"right": ("left", (1, 0), 0),
"up"   : ("down", (0, -1), 3 * math.pi / 2),
"down" : ("up", (0, 1), math.pi / 2)}

# levels (waves)
levels = [[7, 27, 34, 54, 59, 79, 84],
[7, 27, 34, 54, 59, 1092, 1093],
[5, 25, 30, 50, 55, 1092, 1093]]

######################
# initialize globals #
######################
board = user17_9jVc7rMrHiUCj84.board
WIDTH = 448
HEIGHT = 576
PAC_INIT_POS = (14*16, 26.5*16)
pac_time = 0
cur_direction = "left"
global_timer = 0
global_dot_counter = 0
score = 0
eaten_pellets = []
level = 1
frightened_timer = 0
previous_mode = "scatter"
eaten_ghosts = []
lives = 3
flicker_timer = 0
release_ghost_timer = 0
edible_exists = False
edible_timer = 0
eatable_spawn_list = [70, 170]
started = False
popups = set()
game_timer = 0
one_up = False
eaten_fruits = [-1, -1, -1, -1, -1, -1, -1]
fruit_count = 0

###########################
# define helper functions #
###########################
def reset():
"""resets everything"""
global board, pacman, blinky, pinky, inky, clyde, ghost_list, ghosts_in_house, edible, popups, global_timer, global_dot_counter, eaten_pellets, frightened_timer, previous_mode, eaten_ghosts, flicker_timer, release_ghost_timer, edible_exists, edible_timer, eatable_spawn_list, game_timer, cur_direction

for tile in eaten_pellets:
board[tile[0]][tile[1]] = 2
board[6][1] = 3
board[6][26] = 3
board[26][1] = 3
board[26][26] = 3
pacman = Pacman()
pinky = Pinky()
inky = Inky()
clyde = Clyde()
ghost_list = [blinky, pinky, inky, clyde]
ghosts_in_house = [pinky, inky, clyde]
edible = Edible()
popups = set()

global_timer = 0
global_dot_counter = 0
eaten_pellets = []
frightened_timer = 0
previous_mode = "scatter"
flicker_timer = 0
eatable_spawn_list = [70, 170]
eaten_ghosts = []
release_ghost_timer = 0
edible_exists = False
edible_timer = 0
game_timer = 0
cur_direction = "left"
Ghost.set_mode("scatter")

def process_group(group, canvas):
"""Processes and draws a group"""
for obj in group:
obj.draw(canvas)

def dist(point1, point2):
"""return the distance between two points"""
return math.sqrt((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2)

def kill_pacman():
"""handle pacman's death"""
global lives, global_timer, temp, global_dot_counter
# decrement lives
lives -= 1
# play gotcha_sound
gotcha_sound.play()
# stop pacman where it is
pacman.set_vel([0, 0])
# remove ghosts from the field
for ghost in list(ghost_list):
ghost_list.remove(ghost)
# reset Ghost.mode
Ghost.set_mode("scatter")
# kill pacman :[
pacman.set_alive(False)
temp = global_timer
# reset global_dot_counter
global_dot_counter = 1
##################
# define classes #
##################
class Pacman:
def __init__(self):
self.pos = list(PAC_INIT_POS)
self.direction = "left"
self.vel = [direction_to_vel[self.direction][1][0], direction_to_vel[self.direction][1][1]]
self.orientation = direction_to_vel[self.direction][2]
self.sprite_index = 0
self.alive = True
def draw(self, canvas):
"""draw Pacman on the board"""
canvas.draw_image(pacman_image, (13 + 26 * self.sprite_index, 13), (26, 26), self.pos, (26, 26), self.orientation)

# update
if self.alive:
self.update()

def update(self):
global pac_time, score, release_ghost_timer
"""Update Pacman position"""
# move
self.pos[0] = (self.pos[0] + 2 * self.vel[0]) % 448
self.pos[1] += 2 * self.vel[1]
# animate
if self.alive:
pac_time = (pac_time + 1) % 100
if pac_time % 5 == 0:
self.sprite_index = (self.sprite_index + 1) % 2

if self.pos[0] % 16 == 8 and self.pos[1] % 16 == 8:
cur_block = [self.pos[1] // 16, self.pos[0] // 16]
# stop moving if pacman is against a wall
if board[cur_block[0] + self.vel[1]][(cur_block[1] + self.vel[0]) % 28] == 0:
self.vel = [0, 0]
# eat pellets
if board[cur_block[0]][cur_block[1]] in (2, 3):
eaten_pellets.append(cur_block)
# update score
if board[cur_block[0]][cur_block[1]] == 2:
score += 10
else:
score += 50
Ghost.set_mode("frightened")
# reset release_ghost_timer
release_ghost_timer = 0
# play pellet_sound
pellet_sound.play()

def get_pos(self):
"""return position"""
return self.pos

def get_vel(self):
"""return velocity"""
return self.vel

def get_orientation(self):
"""return orientation"""
return self.orientation

def get_direction(self):
"""return direction"""
return self.direction

def get_sprite_index(self):
"""return sprite index"""
return self.sprite_index

def is_alive(self):
"""check if pacman is alive"""
return pacman.alive

def set_pos(self, pos):
"""set position"""
self.pos = pos

def set_vel(self, vel):
"""set velocity"""
self.vel = vel

def set_orientation(self, angle):
"""set orientation"""
self.orientation = angle

def set_direction(self, direction):
"""set direction"""
self.direction = direction

def set_sprite_index(self, index):
"""set sprite index"""
self.sprite_index = index

def set_alive(self, boolean):
"""set pacman's status"""
self.alive = boolean

class Ghost:
mode = "scatter"
default_vel = 2
def __init__(self):
self.sprite_index = 0
self.direction = "left"
self.vel = direction_to_vel[self.direction][1]
self.vel_mag = Ghost.default_vel
self.dot_count = 0
self.has_turned = False

def draw(self, canvas):
"""draw ghost on the canvas"""
canvas.draw_image(ghost_image, [13 + 26 * self.sprite_index, 13 + 26 * self.ghost_index], [26, 26], self.pos, [26, 26])

# update ghost if not in house
if self not in ghosts_in_house:
self.update()
#canvas.draw_circle([self.tile[1] * 16, self.tile[0] * 16], 3, 1, "Red", "Red")
def update(self):
"""update ghost"""
global eaten_ghosts_count, score, lives, frightened_timer, flicker_timer
# turn into disembodied eyes when eaten by pacman
if self in list(eaten_ghosts):
# resurrect if eaten ghost reaches house
if self.in_house:
eaten_ghosts.remove(self)
self.sprite_index = 0
else:
self.sprite_index = 3
if self.pos[0] % 16 == 8 and self.pos[1] % 16 == 8:
# if getting out of house, set target tile to the tile just outside the gate
if self.in_house == True:
self.tile = [14, 13]
# else, if eaten by pacman, rush to house
elif self in eaten_ghosts:
self.tile = [17, 13]
# else, if scatter mode, set scatter tile as the target tile
elif Ghost.mode == "scatter":
self.tile = self.scatter_tile
# otherwise, update target tile
else:
self.tile_update()

# handle movement
cur_block = [self.pos[1] // 16, self.pos[0] // 16]
available_directions = []
# check if passing the gates
if board[cur_block[0]][cur_block[1]] == 4:
self.in_house = not self.in_house

# make a list of forbidden blocks
forbidden_blocks = [0]
if not self.in_house and self not in eaten_ghosts:
forbidden_blocks.append(4)
# check all possible directions
for direction in direction_to_vel.keys():
# except the direction the ghost is coming from
if direction != direction_to_vel[self.direction][0]:
if board[cur_block[0] + direction_to_vel[direction][1][1]][(cur_block[1] + direction_to_vel[direction][1][0]) % 28] not in forbidden_blocks:
available_directions.append(direction)

# frightened mode
if Ghost.mode == "frightened":
# :)
# make it move randomly
if self not in eaten_ghosts:
chosen_direction = random.choice(available_directions)
self.direction = chosen_direction
# else, make it move to the house
else:
# move to house if not in house
if not self.in_house:
min = 10000 #a large number
for direction in available_directions:
if dist([(cur_block[0] + direction_to_vel[direction][1][1]), (cur_block[1] + direction_to_vel[direction][1][0])], self.tile) < min:
min = dist([(cur_block[0] + direction_to_vel[direction][1][1]), (cur_block[1] + direction_to_vel[direction][1][0])], self.tile)
chosen_direction = direction
self.direction = chosen_direction
# increase velocity
self.vel_mag = 4

# chase or scatter mode
else:
# make it turn
if not self.has_turned:
self.turn()
# make it chase
min = 10000 #a large number
for direction in available_directions:
if dist([(cur_block[0] + direction_to_vel[direction][1][1]), (cur_block[1] + direction_to_vel[direction][1][0])], self.tile) < min:
min = dist([(cur_block[0] + direction_to_vel[direction][1][1]), (cur_block[1] + direction_to_vel[direction][1][0])], self.tile)
chosen_direction = direction
self.direction = chosen_direction

# slow down ghosts in the tunnel
if self not in eaten_ghosts:
if board[cur_block[0]][cur_block[1]] == 5:
self.vel_mag = 1
else:
self.vel_mag = Ghost.default_vel

# check if eaten by pacman
if [pacman.get_pos()[1] // 16, pacman.get_pos()[0] // 16] == [self.pos[1] // 16, self.pos[0] // 16]:
if Ghost.mode == "frightened":
if self not in eaten_ghosts:
eaten_ghosts.append(self)
eaten_ghosts_count += 1
popup = Popup(list(pacman.get_pos()), ((2**eaten_ghosts_count) * 100))
eating_ghost_sound.play()
score += (2**eaten_ghosts_count) * 100
else:
if self not in eaten_ghosts:
if pacman.is_alive():
kill_pacman()

self.pos[0] = (self.pos[0] + self.vel_mag * direction_to_vel[self.direction][1][0]) % 448
self.pos[1] += self.vel_mag * direction_to_vel[self.direction][1][1]

def turn(self):
"""turn the ghost in the direction it is coming from"""
self.has_turned = True
self.direction = direction_to_vel[self.direction][0]

def get_pos(self):
"""return position"""
return self.pos

def get_direction(self):
"""return direction"""
return self.direction

def get_index(self):
"""return ghost index"""
return self.ghost_index

def get_dot_count(self):
"""return dot count"""
return self.dot_count

def get_dot_limit(self):
"""return dot limit"""
return self.dot_limit

def get_tile(self):
"""return target tile"""
return self.tile

def get_mode():
"""return mode"""
return Ghost.mode

def set_pos(self, pos):
"""set position"""
self.pos = pos

def set_direction(self, direction):
"""set direction"""
self.direction = direction

def set_dot_count(self, dot_count):
"""set dot count"""
self.dot_count = dot_count

def set_tile(self, tile):
"""set target tile"""
self.tile = tile

def set_sprite_index(self, index):
"""set sprite index"""
self.sprite_index = index

def set_mode(mode):
"""set ghost mode"""
global previous_mode, frightened_timer, eaten_ghosts_count, flicker_timer
# stop frightened_sound
frightened_sound.rewind()
# turn on mode change
if not Ghost.mode == "frightened":
for ghost in ghost_list:
ghost.has_turned = False
ghost.turn()

# handle frightened mode #
if mode == "frightened":
# play frightened_sound
frightened_sound.play()
# reset eaten ghosts count
eaten_ghosts_count = 0
# reset flicker_timer
flicker_timer = 0
if Ghost.mode != "frightened":
previous_mode = Ghost.mode
for ghost in ghost_list:
if ghost not in eaten_ghosts:
# 1. turn ghosts purple
ghost.sprite_index = 1
# 2. slow down the ghosts
Ghost.default_vel = 1
# 3. Reset frightened_timer
frightened_timer = 0
else:
for ghost in ghost_list:
ghost.sprite_index = 0
Ghost.default_vel = 2

# change mode
Ghost.mode = mode

def __init__(self):
Ghost.__init__(self)
self.ghost_index = 0
self.start_pos = [14 * 16, 14.5 * 16]
self.pos = self.start_pos
self.scatter_tile = [2, 25]
self.tile = [0, 0]
self.in_house = False
self.dot_limit = 0

def tile_update(self):
# update target tile
self.tile = [pacman.get_pos()[1] // 16, pacman.get_pos()[0] // 16]

class Pinky(Ghost):
def __init__(self):
Ghost.__init__(self)
self.ghost_index = 1
self.start_pos = [14 * 16, 17.5 * 16]
self.pos = self.start_pos
self.scatter_tile = [2, 2]
self.tile = [0, 0]
self.in_house = True
self.dot_limit = 0
def tile_update(self):
# update target tile
self.tile = [(pacman.get_pos()[1] // 16 + 4 * direction_to_vel[pacman.get_direction()][1][1]), (pacman.get_pos()[0] // 16 + 4 * direction_to_vel[pacman.get_direction()][1][0])]

class Inky(Ghost):
def __init__(self):
Ghost.__init__(self)
self.ghost_index = 2
self.start_pos = [12 * 16, 17.5 * 16]
self.pos = self.start_pos
self.scatter_tile = [34, 27]
self.tile = [0, 0]
self.in_house = True
if level == 1:
self.dot_limit = 30
else:
self.dot_limit = 0
def tile_update(self):
# update target tile
intermediate_block = [pacman.get_pos()[1] // 16 + 2 * direction_to_vel[pacman.get_direction()][1][1], pacman.get_pos()[0] // 16 + 2 * direction_to_vel[pacman.get_direction()][1][0]]
self.tile = [intermediate_block[0] + difference[0], intermediate_block[1] + difference[1]]

class Clyde(Ghost):
def __init__(self):
Ghost.__init__(self)
self.ghost_index = 3
self.start_pos = [16 * 16, 17.5 * 16]
self.pos = self.start_pos
self.scatter_tile = [34, 0]
self.tile = [0, 0]
self.in_house = True
if level == 1:
self.dot_limit = 60
elif level == 2:
self.dot_limit = 50
else:
self.dot_limit = 0
def tile_update(self):
# update target tile
cur_block = [self.pos[1] // 16, self.pos[0] // 16]
pacman_block = [pacman.get_pos()[1] // 16, pacman.get_pos()[0] // 16]
distance = dist(cur_block, pacman_block)
if distance > 8:
self.tile = pacman_block
else:
self.tile = self.scatter_tile

class Popup:
def __init__(self, pos, value):
self.pos = pos
self.value = value
self.size = 12
self.color = "White"
self.init_pos = pos[1]
self.vel = 1

def draw(self, canvas):
global popups
canvas.draw_text(str(self.value), self.pos, self.size, self.color)
self.pos[1] -= self.vel
if self.pos[1] <= self.init_pos - 30:
popups.remove(self)

class Edible:
global level
def __init__(self):
self.pos = (14 * 16, 20.5 * 16)
self.value = level * 100
if level <= 7:
self.sprite_index = (level - 1)
else:
self.sprite_index = 6

def draw(self, canvas):
"""draw edible on the canvas"""
canvas.draw_image(edible_image, (14 + (self.sprite_index * 28), 14), (28, 28), self.pos, (28, 28))

def get_value(self):
"""return edible's value"""
return self.value

def get_sprite_index(self):
"""return sprite index"""
return self.sprite_index

#########################
# define event handlers #
#########################
def draw(canvas):
global started, pac_time, cur_direction, score, lives, frightened_timer, flicker_timer, global_dot_counter, release_ghost_timer, edible_exists, edible_timer, level, one_up, fruit_count
# draw background
canvas.draw_image(board_image, (224, 288), (448, 576), (WIDTH / 2, HEIGHT / 2), (WIDTH, HEIGHT))

# blackout eaten pellets
for i in range(4, 33):
blackout_range = 0
for j in range(1, 28):
if board[i][j] == 1 :
blackout_range += 1
else:
if blackout_range!=0:
canvas.draw_polygon([((j - blackout_range) * 16, i * 16), ((j - blackout_range) * 16, (i * 16) + 16),
(j * 16, (i * 16) + 16), (j * 16, i * 16)], 1, "Black", "Black")
blackout_range = 0

if started:
# draw Pacman
pacman.draw(canvas)

# play kill animation
if not pacman.is_alive():
flicker_timer = (flicker_timer + 1) % 10
if flicker_timer % 10 == 0:
pacman.set_sprite_index(pacman.get_sprite_index() + 1)

## update pacman direction ##
# check if pacman is in the center of a grid point
if pacman.get_pos()[0] % 16 == 8 and pacman.get_pos()[1] % 16 == 8:
# determine pacman's current block
cur_block = [pacman.get_pos()[1] // 16, pacman.get_pos()[0] // 16]
# determine if pacman can move in that direction
if board[cur_block[0] + direction_to_vel[cur_direction][1][1]][(cur_block[1] + direction_to_vel[cur_direction][1][0]) % 28] not in [0, 4]:
pacman.set_vel(direction_to_vel[cur_direction][1])
pacman.set_direction(cur_direction)
pacman.set_orientation(direction_to_vel[cur_direction][2])
#############################

# draw Ghosts
process_group(ghost_list, canvas)

# make ghosts flicker
if Ghost.get_mode() == "frightened":
if frightened_timer > 4:
flicker_timer = (flicker_timer + 1) % 50
if flicker_timer < 25:
for ghost in ghost_list:
if ghost not in eaten_ghosts:
ghost.set_sprite_index(2)
else:
for ghost in ghost_list:
if ghost not in eaten_ghosts:
ghost.set_sprite_index(1)

## release ghosts from house ##
# 1. determine preferred ghost
min = 4
for ghost in ghosts_in_house:
if ghost.get_index() < min:
min = ghost.get_index()
preferred_ghost = ghost

# 2. update counter whenever pacman eats a dot
# 2.1 check if there is a ghost in house
if ghosts_in_house:
# 2.1. check if pacman is in the middle of a tile
if pacman.get_pos()[0] % 16 == 8 and pacman.get_pos()[1] % 16 == 8:
# 2.2. check if pacman's tile has a pellet in it
cur_block = [pacman.get_pos()[1] // 16, pacman.get_pos()[0] // 16]
if board[cur_block[0]][cur_block[1]] == 2:
# 3. Release ghost
# if global_dot_counter is enabled, use it.
if global_dot_counter:
global_dot_counter += 1
if global_dot_counter in [8, 18, 33]:
ghosts_in_house.remove(preferred_ghost)
# else, use individual dot counters
else:
preferred_ghost.set_dot_count(preferred_ghost.get_dot_count() + 1)
if preferred_ghost.get_dot_count() >= preferred_ghost.get_dot_limit():
ghosts_in_house.remove(preferred_ghost)
preferred_ghost.set_dot_count(0)
# Alternate, timer based way of releasing ghosts
if release_ghost_timer >= 4:
ghosts_in_house.remove(preferred_ghost)
release_ghost_timer = 0

# else, display "click to start!"
else:
canvas.draw_text("Click to start!", (11*16, 21*16), 18, "Red")

# remove eaten pellet
for tile in eaten_pellets:
board[tile[0]][tile[1]] = 1

# draw popups
process_group(popups, canvas)

# draw edibles
if len(eaten_pellets) in eatable_spawn_list:
eatable_spawn_list.remove(len(eaten_pellets))
edible_exists = True
if edible_exists:
edible.draw(canvas)
if [pacman.get_pos()[1] // 16, pacman.get_pos()[0] // 16] in [[20,13], [20,14]]:
score += edible.get_value()
popup = Popup(list(pacman.get_pos()), edible.get_value())
if edible.get_sprite_index() not in eaten_fruits:
eaten_fruits[fruit_count] = edible.get_sprite_index()
fruit_count += 1
edible_sound.play()
edible_exists = False
edible_timer = 0

# display score and level
canvas.draw_text("Score: " + str(score), (304, 16), 16, "White")
canvas.draw_text("Level: " + str(level), (304, 32), 16, "White")

# display lives
for i in range(lives):
canvas.draw_image(pacman_image, (13, 13), (26, 26), (32 + (i * 30), 560), (26, 26))

# update level
if len(eaten_pellets) == 244:
level += 1
reset()

# award one_up when player's score reaches 10000
if score == 10000 and not one_up:
lives += 1
one_up = True
one_up_sound.play()

# draw eaten fruits
for i in range(7):
if eaten_fruits[i] != -1:
canvas.draw_image(edible_image, (14 + (eaten_fruits[i] * 28), 14), (28, 28), (16 * 14 + (i * 33), 16 * 35), (28, 28))

def move(key):
global cur_direction
if key == simplegui.KEY_MAP["left"]:
cur_direction = "left"
elif key == simplegui.KEY_MAP["right"]:
cur_direction = "right"
if key == simplegui.KEY_MAP["up"]:
cur_direction = "up"
if key == simplegui.KEY_MAP["down"]:
cur_direction = "down"

def tick():
global started, game_timer, global_timer, level, frightened_timer, previous_mode, pacman, temp, ghost_list, ghosts_in_house, cur_direction, blinky, pinky, inky, clyde, release_ghost_timer, edible_exists, edible_timer

if started:
# resurrect pacman and the ghosts
if not pacman.is_alive():
# wait for the kill animation to stop
if global_timer - temp > 2:
# check game over condition
if lives == 0:
started = False
# reset ghosts and pacman
else:
pinky = Pinky()
inky = Inky()
clyde = Clyde()
pacman = Pacman()
cur_direction = "left"
ghost_list = [blinky, pinky, inky, clyde]
ghosts_in_house = [pinky, inky, clyde]
Ghost.set_mode("scatter")
global_timer = 0
release_ghost_timer = 0

if not Ghost.get_mode() == "frightened":
global_timer += 1
if level == 1:
wave_set = 0
elif level in range(2, 5):
wave_set = 1
else:
wave_set = 2

if global_timer == levels[wave_set][0]:
Ghost.set_mode("chase")
elif global_timer == levels[wave_set][1]:
Ghost.set_mode("scatter")
elif global_timer == levels[wave_set][2]:
Ghost.set_mode("chase")
elif global_timer == levels[wave_set][3]:
Ghost.set_mode("scatter")
elif global_timer == levels[wave_set][4]:
Ghost.set_mode("chase")
elif global_timer == levels[wave_set][5]:
Ghost.set_mode("scatter")
elif global_timer == levels[wave_set][6]:
Ghost.set_mode("chase")

else:
frightened_timer += 1
if frightened_timer == 7:
Ghost.set_mode(previous_mode)

# update release_ghost_timer
release_ghost_timer += 1

# edible timer
if edible_exists:
edible_timer += 1
if edible_timer >= 10:
edible_exists = False
edible_timer = 0

# update game_timer
if game_timer:
game_timer += 1
if game_timer == 5:
reset()
started = True
game_timer = 0

def start_game(pos):
global started, game_timer, lives, score, level, one_up, eaten_fruits, fruit_count
# start the game if it hasn't already started
if not started:
# reset lives, score and level
lives = 3
score = 0
level = 1
one_up = False
eaten_fruits = [-1, -1, -1, -1, -1, -1, -1]
fruit_count = 0
# play intro music
intro_sound.play()
# set game_timer
game_timer = 1

################
# create frame #
################
frame = simplegui.create_frame("Pac-Man - Manuel Augusto Antao - 2013-07-09", WIDTH, HEIGHT)

###########################
# register event handlers #
###########################
frame.set_draw_handler(draw)
frame.set_keydown_handler(move)
frame.set_mouseclick_handler(start_game)
main_timer = simplegui.create_timer(1000, tick)

######################
# set things rolling #
######################
frame.start()
main_timer.start()