1. Pac-Man in JR
ECS 140B Final Project
Neil Flowers, Eric Chu, Benjamin Beasley
2. For our final project we chose to implement the game PacMan in JR.
The game contains a single cursor (PacMan) which the user must navigate
with the arrow keys to eat all of the food on the map while avoiding four AI-
controlled ghosts. Each piece of food grants the player a small point value.
The ghosts will chase PacMan around the map, attempting to surround and
collide with him. If the player collides with a ghost, he or she will lose one
life and be moved back to the original spawn point with the items remaining
in their current state (in other words, food that has been consumed will
remain gone). There are also special power-up items on the map which,
when activated, temporarily give the user the ability to consume and reset
ghosts upon collision, also granting additional points. If the player consumes
all of the food on the map, it will immediately be reset along with the ghosts
and level two will commence all levels use the same map, but the score
will continue to accumulate until the player dies. If the player has no lives
remaining, the map will reset, the current high score will be displayed, and
the he or she may choose to play again if desired.
When implementing this project we chose to go with a very object-
oriented design, similar to that of the LL interpreter of project 1. The map is
defined in Map.jr. It is split up into square blocks that can contain up to one
item each. The number of blocks on the screen vertically and horizontally
are defined by height and width, respectively. This class houses all of the
data on the map at any given moment. All items and walls are stored in a
two-dimensional array of Items, which we chose to use because their positions
1
3. on the map never change; this is in contrast to the User and Ghost objects,
which are not stored in the array. Rather, references to these objects are
also stored (separately) in the map class, and each one stores its own current
position.
Any type of object present on the map, including PacMan, ghosts, items,
and even walls, extend the abstract class MapObject.jr. MapObject defines
important member variables common to all objects on the map, such as
position and color, as well as get methods for these values. Its abstract
definition is for paint(), which is implemented by all subclasses and is the
means by which an object knows how to draw itself on the map. MapObject
has two subclasses: Item and GamePiece.
Item.jr contains the definition for all items and edges on the map. Items
are di↵erentiated by their type member variable, which corresponds to the
public static constants defined in the file. Item’s paint() method contains
the logic for rendering the edges and items on the map according to their
position, and its use() method contains the logic for using up an item (which
will never be called for edges).
GamePiece is another abstract class that serves as the superclass of User
and Ghost, the moving objects on the map. It contains important member
variables for the moving objects: original spawn points, current o↵sets for X
and Y (used in order to move them between blocks incrementally rather than
all at once), current direction, and speed. Constants for direction are also
defined here, and correspond to angle o↵sets used in AWT painting methods
2
4. to make painting PacMan in the correct direction seamless. It continues the
abstract definition of paint(), but does not contain any further abstract
definitions.
User extends GamePiece and contains the definition for the user’s cursor
PacMan. Basic user information, including life count and current score,
are stored here. The key listener is actually implemented in Game.jr as a
constantly running JR process. Arrow key pressed by the user are forwarded
to this class using an op, defined as move(int), which the Game class retrieves
using getMoveOp(). PacMan’s movements are implemented as a process as
well, which repeatedly listens for movement commands on the move op. If a
movement key is pressed, the process receives that key and changes PacMan’s
direction accordingly; if no key is pending in the op queue, PacMan continues
moving forward in his current direction. PacMan moves incrementally across
a cell and, upon crossing into a new one, uses the item in the cell if present.
Similar to User, Ghost extends GamePiece and contains the logic for each
ghost on the map. Each of the ghosts are actually initialized in the Ghosts
class, which serves as an easy container for references to them and directs
their movements. Each ghost is controlled by a separate AI module, which is
initialized in its constructor in a new VM on a di↵erent machine (as directed
by Ghosts.jr). The path member variable consists of an ArrayList of AWT
Points corresponding to the last path determined by the AI. The algorithm
used to implement the AI is the A* shortest path algorithm, and is defined
in AStarAI.jr. When it has exhausted its current path, each ghost sends
3
5. a request to its AI object to create the shortest path based on its current
position and the user’s current position.
Finally, the class that glues all of the previously mentioned classes to-
gether and makes them work is Game.jr. In addition to containing the
KeyListener process, this class creates the visualization by extending JPanel
and receiving the Graphics object in paintComponent(). Its job is to direct
the flow of the game, from beginning (title screen) to end (user exiting).
The crux of the program resides in the process mainLoop, which continues
through its entire lifespan and directs the flow of movement by calling ap-
propriate ghost and AI methods. It also controls the painting by calling
repaint(), which results in the execution of paintComponent() and all of
the object rendering logic therein.
We chose to design the program in this way for a number of reasons.
First, the hierarchy is intuitive and straightforward: each type of object on
the map (and the map itself) correspond to a specific class. Moreover, we
observed that many objects on the map share common characteristics: for
example, all objects must have a position on the map and a color, so it made
sense to define these attributes in the superclass. Taking this approach also
made rendering in Game.jr very straightforward: simply call each object’s
paint() method in turn and the work is done.
After working on this project, we can say we learned more about JR and
how concurrent pro-gramming works. Throughout the program we had many
send and receive statements going to dif-ferent classes as processes had to
4
6. pass operations along to let other processes run. We also learned a little of
how it to write a program and show animation with Javas AWT package.
Furthermore, not on-ly did we learn about how concurrent programming
works but also learned first-hand why it is e↵ec-tive. When we first built up
the program, the code was primarily written without using JR features, and
thus when we tried to run the program we noticed the game was choppy as
the animation of Pac-Man and the ghosts werent smooth and crisp. We were
doing a lot of busy wait in our earlier implementa-tion (i.e. checking each
main loop iteration over whether the level was complete, whether the user
touched a ghost, etc.), and by re-writing things in JR (i.e. having these checks
as processes that wait until they receive an op), we noticed the performance
of the game considerably improved.
Due to time constraints, there were features that we had hoped to imple-
ment into the game but were ultimately left out. One of these features we
would like to add is a level system. Currently, after completing a level by eat-
ing all the pellets, the game would restart with the users score carrying over
to the next game, which plays out exactly as the previous. We would have
liked to make the game more challenging as the user completes each level by
increasing the di cult (improving the AI of the ghosts, making them faster,
or producing more ghosts in general) and rewarding the user as they play
through the higher levels (make the pellets worth more points). Adding on
to that, we would have liked to tweak the AI to make the game more chal-
lenging and sporadic in general. Currently, the AI use the A* algorithm to
5
7. decide on the closest path to take to get to the user, which makes the AI a bit
predictable as they will generally end up being behind the user and chasing
them. Another idea we would have ide-ally implemented is having multiple
maps for di↵erent levels and even letting users create their own maps. Our
current implementation assumes a lot of things about the hard-coded map,
and thus the map system would have to be rewritten, but a new map system
would keep the game di↵erent and fresh to play.
6
8. Figure 1: UML Sequence Diagram of Process Interaction
7