This code is an implementation of "Ms Pac-Man" for the "Ms Pac-Man vs Ghosts Competition". This competition is organised by Philipp Rohlfshagen, David Robles and Simon Lucas of the University of Essex.

The code is written by Philipp Rohlfshagen based on earlier implementations of the game by Simon Lucas and David Robles.

Competition website: www.pacman-vs-ghosts.net

Competition organisers: Philipp Rohlfshagen, David Robles and Simon Lucas (University of Essex)

Quick Starter Guide

Run software

  1. Download: Download the software and unzip it in a convenient location on your computer.
  2. Create a Project: Open Eclipse and create a new (empty) Java project.
  3. Import files:
    1. Click on File, then Import ... and choose General, then File System.
    2. In the 'From directory' dialog, choose the folder where you unzipped the software.
    3. In the 'Into folder' dialog, choose the project you just created.
  4. Click Finished.
  5. Run the Code

You can execute the code by running the class Executor.java This class contains a main method with numerous options for execution. Just uncomment the one you wish to run.

Start coding

  1. Inspect the Files The files you need to edit are:

    1. pacman.entries.pacman.MyPacMan.java
    2. pacman.entries.ghosts.MyGhosts.java

    These files extend Controller.java. You will need to provide code for the getMove(Game game, long timeDue) method.

  2. The getMove() Method

    In order to create a controller, you need to provide code for the method: getMove(Game game, long timeDue) in MyPacMan.java or MyGhosts.java. These files are already included in the code.

    In the case of Ms Pac-Man, this method returns an element of the MOVE enumeration found in Constants.java. The elements are:

    • MOVE.UP
    • MOVE.RIGHT
    • MOVE.DOWN
    • MOVE.LEFT
    • MOVE.NEUTRAL

    In the case of the ghosts, this method returns an EnumMap that maps every element from the enumeration GHOST to MOVE:

    • GHOST.BLINKY
    • GHOST.PINKY
    • GHOST.INKY
    • GHOST.SUE

    To calculate a good move, you can query the game object to access information about the game state. The long timeDue indicates the system time the next move is due. You have 40ms per game tick to provide a response.

  3. The Game object Every game tick, your controller receives a copy of the current game state via the getMove() method. The game object contains all the information required to make an informed decision. The class Game.java is the only class you need to be concerned with (you may also need to know about Constants.java, which holds all the game's enumerations and constants, and GameView in case you would like to use visual aids. The game is represented as a graph: you move from one node to another throughout the game. Pills and powerpills are located on specific nodes throughout the maze. Nodes are generally referred to by their indices.

  4. Putting it together Whenever the getMove(-) method gets called, you receive a new copy of the game and the time the move is due. Query the game state using its many methods to obtain information about the pills, powerpills, positions of Pac-Man and the Ghosts etc. The game also provides numerous additional helper methods to obtains things like shortest paths or moves to take to approach a specific target.

Tutorials

Timings

The game is played asynchronously in real time: every discrete game tick, the game uses the moves supplied by the controllers to update the gamestate. Then, the game waits for 40ms for new actions to come in, after which the game updates again. At each game tick, the controllers have 40 ms to respond. The controllers are given a copy of the current game and the time the move is due. Each controller can then query the game using its many methods to compute an appropriate response. It is the responsibility of the controller to respond in a timely manner (this is one of the challenges of the competition).

If a controller returns a move on time, the game will use that move to advance the game. If the move happens to be illegal, the game tries to replay the previous move or chooses a new legal move randomly. If a controller does not replay on time, the game tries to play the previous move or, if that is not possible, chooses a legal move randomly.

The controller will receive a new copy of the game state for the remaining time of that game tick to compute a new move. In other words, if a controller takes 60ms to respond, it will have roughly (2x40)-60=20ms in the next game tick. If a controller does not reply over several game ticks, the game keeps replaying previous actions.

Software

The Executor runs a game by instantiating two controllers (one for Ms. Pac-Man, one for the Ghosts) and creating a new game object. It then supplies each controller with a copy of the game object and then waits for 40ms for each controllers to compute their action. After the time is up, the actions computed by the controllers are used to advance the game state. This is continued until the end of the game (or the limit has been reached).

Each controller extends the abstract class Controller. The only method that needs implementation is

getMove(Game game, long timeDue)

The game object is a copy of the current game state and timeDue signals the point in time the game advances; a controller should return an action before the time is up. Your code must be in either

pacman.entries.pacman.MyPacMan.java pacman.entries.ghosts.MyGhosts.java

providing an implementation of the getMove(-) method. These files are already included in the software.

The controllers can access all the information about the game by using the many methods provided in Game. In fact, game is almost the only class you need to worry about. The only other two classes are

The moves of the game are implemented as an enumeration. The options are:

Ms. Pac-Man controllers need to return one of these moves at every time step. Ms Pac-Man returns a single MOVE every time step. If the controller returns null or an illegal move (e.g., trying to go up even though there is a wall), the game engine will try to play the last move if possible or else choose a legal move randomly.

The ghosts controller needs to return one move for each of the 4 ghosts. The ghosts are encoded using another enumeration called GHOST:

The reply from a ghosts controller is an EnumMap that maps from each type of ghost to a move. For instance, the following shows how to return a left move for Blinky:

EnumMap<GHOST, MOVE> myMoves = new EnumMap<GHOST, MOVE>(GHOST.class)

myMoves.put(GHOST.BLINKY, MOVE.LEFT);

A ghost controller can return moves for 0 or more ghosts. Any missing moves will be substituted by the game engine (including the case where the controller returns null).

Once the time is up, the game will advance using the moves computed by the controllers. If no move was returned, the game engine tries to repeat the previous move played or chooses a legal move uniformly at random. If a controller returns a move after the time limit is up, the controller will be given the remaining time in the current time step to compute a new move.

To make an informed decision, the controllers may query the game state to obtains all relevant information. This is done via the many methods provided (see tutorial on Game.java). The game consists of 4 mazes that are encountered in succession. Each maze is modelled as a graph: Ms Pac-Man and each of the ghosts move from node to node along this graph. Pills and powerpills are also located on nodes. Each node has a set of neighbours that may be reached: in a corridor, for instance, a node has two neighbours, at junctions 3 or 4 neighbours. The only exception to this is the lair (where the ghosts start and return to once eaten) which does not have any neighbours at all.

One of the most useful information is the distance between any two nodes in the maze. For reasons of efficiency, the shortest path distances from any node to any other node have been precomputed (using Dijstra) and are loaded from file when the game starts. The shortest path distances may be used for Ms Pac-Man to determine the distance between selected nodes. However, since ghosts are not allowed to reverse, distances may be longer (as a ghost may be forced to take a longer route to avoid reversal). There are two ways to get the shortest path distance for a ghost:

Below is a list of all the classes:

The other files contains in pacman.controllers.examples are example controllers that serve as examples and which you may use to test and evaluate your own code.

The class Executor contains a main method and a choice of static methods which contain the different modes of operation. You can execute the game in 3 ways:

Visuals

The methods for adding visuals to the game view are all static member of GameView. All the methods required to add visuals are static so can be called without having to have an instance of GameView. You can do two things:

Any highlights added to the game view are active for a single game tick only so you need to add them at every game tick (in your getMove(-) method).

Below is an example that highlights the path from the current location of Ms Pac-Man to the nearest power pill:

GameView.addPoints(game,Color.CYAN,game.getShortestPath(game.getPacmanCurrentNodeIndex(),nearestActivePowerPillIndex))

where nearestActivePowerPillIndex is the index of the nearest power pill that has not yet been eaten.

The code snippet below draws a line from the current position of Ms Pac-Man to all remaining power pills:

int[] activePowerPills=game.getActivePowerPillsIndices();

for(int i=0;i<activePowerPills.length;i++)
  GameView.addLines(game,Color.CYAN,game.getPacmanCurrentNodeIndex(),activePowerPills[i]);

Rules of the game

General rules

The game consists of four mazes (A, B, C and D), which are played in order; when maze D is cleared (i.e., all pills have been eaten by Ms Pac-Man), the game continues with maze A and so on until the game is over. Each maze contains a different layout with pills and power pills placed at specific locations. Ms Pac-Man starts the game with three lives; an additional life is awarded at 10,000 points.

The goal of Ms Pac-Man is to obtain the highest possible score by eating all the pills and power pills in the maze (and thus advancing to the next stage). Each pill eaten scores 10 points, each power pill is worth 50 points. Ms Pac-Man's quest is opposed by the four ghosts: Blinky (red), Pinky (pink), Inky (green) and Sue (brown). At the start of each level, the ghosts start in the lair in the middle of the maze and, after some idle time, enter the maze in their pursuit of Ms Pac-Man. Their goal is to eat Ms Pac-Man and each time this happens, a life is lost and Ms Pac-Man and the ghosts return to their initial positions; the time spent by the ghosts in the lair decreases as the player progresses to higher levels.

There are four power pills in each of the four mazes, which, when eaten, reverse the direction of the ghosts and turn them blue; they may now be eaten for extra points. The score for eating each ghost in succession immediately after a power pill has been consumed starts at 200 points and doubles each time, for a total of 200+400+800+1600=3000 additional points. Any ghost that has been eaten re-appears in the lair and emerges soon after, once again chasing Ms Pac-Man. If a second power pill is consumed while some ghosts remain edible, the ghost score is reset to 200; if the level is cleared while the ghosts remain edible, play continuous immediately to the next level.

The more advanced the level, the shorter the ghost edible times become, making the levels progressively more difficult; at an advanced stage, the ghosts do not turn blue at all (however, they still change direction). When the edible period runs out, the ghosts start flashing blue and white. The player (or agent) needs to be careful at this stage to avoid losing lives. When all the pills and power pills have been cleared, the game moves on to the next maze.

Goals

The goal of a Ms Pac-Man controller is to maximise the score of the game. In the competition, it is the average score against multiple ghost teams that counts and the winning controller is the one which obtains the highest total average score.

The goal of a ghost-team controller is to minimise the average score obtained agains t it by the different Ms Pac-Man controllers. The winning ghost team will be team with the lowest average score against it.

Deviations from the Original

A few changes were made to simplify the game.

  1. The speed of Ms Pac-Man and the ghosts are always identical, except when ghosts are edible (in which case the ghosts reduce their speed by half).
  2. Bonus fruits are omitted.
  3. Each maze is played once before moving on to the next one (in the original, each maze was played twice in succession).

Ghost-Team-Specific Rules

There are no restrictions regarding the actions of Ms Pac-Man: movement in any direction not blocked by a wall is allowed at all times. For the ghost team, on the other hand, three restrictions apply:

  1. Ghosts may not reverse direction. Subsequently, a ghost may only choose its direction at a junction, choosing any of the paths available except the one the ghost used to approach the junction.
  2. Occasionally there is a global reversal event when all the ghosts suddenly change direction. This may happen in any game tick with a small probability of 0.0015.
  3. To prevent the ghosts from spoiling the game by constantly blocking the power pills (in which case the game would result in a stalemate), each level finishes after 3000 time steps; half the score of all remaining pills are awarded to Ms Pac-Man.