actors/Actor.java


otherpackage pacman.actors;

import java.awt.Color;
import java.awt.Graphics2D;

import pacman.ai.AIManager;
import pacman.game.GameObject;
import pacman.map.Map;
import pacman.state.StateGame;
import pacman.util.Direction;
import pacman.util.RequestedDirectionBuffer;

/**
 * An actor is any object that has a degree of autonomy or intelligent input
 * (Human / AIManager) dictating the object's behavior ingame Subclass of
 * GameObject
 * 
 * @author Ramsey Kant
 */
public abstract class Actor extends GameObject {

    /** Whether the actor is alive or not. */
    protected boolean isDead;

    /** The x-position on the map where the actor gets spawned */
    protected int spawnX;
    
    /** The y-position on the map where the actor gets spawned */
    protected int spawnY;

    /** The direction in which the actor is currently oriented. */
    protected Direction currentMoveDir;
    
    /** The actor's direction requested by user input. Uses a buffer to
     *  remember requests over some steps to simplify timing for the user.
     *  Computer-controlled actors (ghosts) ignore this. */
    protected RequestedDirectionBuffer requestedMoveDirBuffer;
    
    /** The size of the direction request buffer. */
    private final static int DIRECTION_QUEUE_SIZE = 6;

    /** The current orientation angle of the actor. Ignored by actors that do
     *  not have to orient (aka ghosts). */
    protected int dirOrient;
    
    /** The x-position delta to the current map cell, caused by movement
     *  in pixels. */
    protected float deltaX;
    
    /** The y-position delta to the current map cell, caused by movement
     *  in pixels. */
    protected float deltaY;
    
    /** The movement speed of the actor */
    protected float speed;

    /**
     * Actor Class Constructor
     * 
     * @param type
     *            Object type that is an actor
     * @param color
     *            Base color of the actor
     * @param m
     *            Reference to the global map object
     * @param x
     *            X coordinate to spawn the actor at
     * @param y
     *            Y coordinate to spawn the actor at
     * @see GameObject
     */
    public Actor(int type, Color color, Map m, int x, int y) {
        super(type, color, m, x, y);

        isDead = false;

        // Movement
        spawnX = x;
        spawnY = y;
        currentMoveDir = Direction.none;
        requestedMoveDirBuffer = new RequestedDirectionBuffer(DIRECTION_QUEUE_SIZE);
        dirOrient = 0;
        deltaX = 0;
        deltaY = 0;
        speed = (float) (5d * map.SCALE);
    }

    // Getters and Setters

    /**
     * Returns the original X coordinate the Actor was given in the constructor
     * 
     * @return the X coordinate of the spawn point
     */
    public int getSpawnX() {
        return spawnX;
    }

    /**
     * Returns the original Y coordinate the Actor was given in the constructor
     * 
     * @return the Y coordinate of the spawn point
     */
    public int getSpawnY() {
        return spawnY;
    }

    /**
     * Set the death status of the actor. Used by StateGame and AIManager to
     * determine if the player / ghost has died
     */
    public void setDead(boolean s) {
        isDead = s;
    }

    /**
     * Get dead status
     * 
     * @return True if dead, false if alive
     * @see Actor#setDead(boolean)
     */
    public boolean isDead() {
        return isDead;
    }

    /**
     * Speed is the number of pixels an actor moves across the screen in a given
     * cycle. A full position change is the number of pixels defined in
     * Map.CELL_SIZE
     * 
     * @param s
     *            New Speed
     */
    public void setSpeed(float s) {
        speed = s;
    }

    /**
     * Get the current speed of the actor
     * 
     * @return Current speed
     * @see Actor#setSpeed(float)
     */
    public float getSpeed() {
        return speed;
    }

    /**
     * Set the direction actor should travel in. Player uses this to determine
     * the direction to "auto-move" to Ghosts ignore what is set by this
     * function because their direction is determined within act() based on the
     * path
     */
    public void setMoveDirection(Direction dir) {
        requestedMoveDirBuffer.setRequestedDirection(dir);
    }

    // Public Methods

    /**
     * Attempt to move the actor to the given x,y location. This method will
     * check if a coordinate is valid with the Map class method canMove(). It is
     * not necessary to call canMove() before this function
     * 
     * @param x
     *            A x coordinate to move to
     * @param y
     *            A y coordinate to move to
     * @return True if the move succeeded. False if otherwise
     * @see Map#canMove(Actor, int, int)
     */
    public boolean move(int x, int y) {
        final boolean res = map.canMove(this, x, y);
        if (res) {
            positionX = x;
            positionY = y;
        }
        return res;
    }

    /**
     * The primary logic function for actors. StateGame calls this for players
     * directly in logic() and the AIManager calls this for ghosts in process()
     * 
     * @see GameObject#act()
     * @see StateGame#logic()
     * @see AIManager#process()
     */
    @Override
    public abstract void act();

    /**
     * 
     * @see GameObject#paint(java.awt.Graphics2D)
     */
    @Override
    public abstract void paint(Graphics2D g);

}

actors/Ghost.java


otherpackage pacman.actors;

import java.awt.Color;
import java.awt.Graphics2D;

import pacman.ai.AIManager;
import pacman.game.GameObject;
import pacman.map.Map;
import pacman.map.Path;
import pacman.util.Direction;

/**
 * The Ghost class is the primary enemy in Pacman. Intelligent decisions of
 * ghosts are made by the AIManager class Ghost is a subclass of Actor
 * 
 * @author Ramsey Kant
 */
public class Ghost extends Actor {
    // Movement
    private Path path;
    private int nextStepIdx;
    private boolean needNewPath;

    // State
    private boolean trapped;
    private boolean inFear;
    private boolean debugDrawPath;

    /**
     * Class constructor
     * 
     * @param color
     *            Color of the ghost's 'body'
     * @param m
     *            Reference to the map
     * @param x
     *            X coordinate to spawn at
     * @param y
     *            Y coordinate to spawn at
     * @param trap
     *            Set trapped status
     */
    public Ghost(Color color, Map m, int x, int y, boolean trap) {
        super(GameObject.OBJECT_GHOST, color, m, x, y);
        needNewPath = true;
        inFear = false;
        trapped = trap;
        debugDrawPath = false;
    }

    /**
     * Return the fear status of the ghost
     * 
     * @return True if fearful
     */
    public boolean isInFear() {
        return inFear;
    }

    /**
     * Set fear status. AIManager interperates this setting for behavior
     * 
     * @param f
     *            Fear status, true if fearful
     */
    public void setFear(boolean f) {
        inFear = f;
    }

    /**
     * Get the current trapped status
     * 
     * @return True if the ghost is currently in the spawn-jail
     */
    public boolean isTrapped() {
        return trapped;
    }

    /**
     * Set the current trapped status
     * 
     * @param t
     *            Trye uf the ghost is in the spawn-jail
     */
    public void setTrapped(boolean t) {
        trapped = t;
    }

    /**
     * Flag that is set to true when the path reaches the last possible step
     * 
     * @return True if the AIManager needs to assign a new path
     */
    public boolean needsNewPath() {
        return needNewPath;
    }

    /**
     * Update the Path object for the ghost to follow'
     * 
     * @param p
     *            Path object generated in process() by the AIManager
     * @see AIManager#process()
     */
    public void updatePath(Path p) {
        nextStepIdx = 1;
        path = p;
        needNewPath = false;
    }

    /**
     * Direct's the paint() function to draw the current path of the ghost on
     * the map
     * 
     * @param d
     *            If true, debug is on and the path will be drawn
     * @see AIManager#setDebugEnabled
     */
    public void setDebugDrawPath(boolean d) {
        debugDrawPath = d;
    }

    /**
     * Run a think cycle for the AI. Major decisions are made by the AIManager
     * (pathing), this just determines movement and screen draw deltas
     * 
     * @see Actor#act()
     */
    @Override
    public void act() {
        // Move to the next step
        if (path != null && nextStepIdx < path.getLength()) {

            // Figure out the direction
            if ((path.getY(nextStepIdx) - positionY) < 0) {
                currentMoveDir = Direction.up;
            } else if ((path.getY(nextStepIdx) - positionY) > 0) {
                currentMoveDir = Direction.down;
            } else if ((path.getX(nextStepIdx) - positionX) > 0) {
                currentMoveDir = Direction.right;
            } else {
                currentMoveDir = Direction.left;
            }

            // Based on the direction, move the screen delta's and the X,Y
            // coordinates if the # of pixels for the cell have been surpassed
            switch (currentMoveDir) {
                case up:
                    deltaX = 0;
                    deltaY = deltaY - speed;
                    // If the movement delta has surpassed the number of pixels for
                    // the cell, set him to the map cell he has reached by his movement.
                    if (Math.abs(deltaY) >= map.CELL_SIZE) {
                        deltaY = 0;
                        move(positionX, positionY - 1);
                        nextStepIdx++;
                    }
                    break;
                case right:
                    deltaX = deltaX + speed;
                    deltaY = 0;
                    if (Math.abs(deltaX) >= map.CELL_SIZE) {
                        deltaX = 0;
                        move(positionX + 1, positionY);
                        nextStepIdx++;
                    }
                    break;
                case down:
                    deltaX = 0;
                    deltaY = deltaY + speed;
                    if (Math.abs(deltaY) >= map.CELL_SIZE) {
                        deltaY = 0;
                        move(positionX, positionY + 1);
                        nextStepIdx++;
                    }
                    break;
                case left:
                    deltaX = deltaX - speed;
                    deltaY = 0;
                    if (Math.abs(deltaX) >= map.CELL_SIZE) {
                        deltaX = 0;
                        move(positionX - 1, positionY);
                        nextStepIdx++;
                    }
                    break;
                case stop:
                case none:
                    // do not move
            }
        } else {
            needNewPath = true;
        }
    }

    /**
     * Draw the ghost
     * 
     * @see GameObject#paint(Graphics2D)
     */
    @Override
    public void paint(Graphics2D g) {
        // Change the position of pacman on screen by the offsets m_fDelta
        final int screenX = (int) ((map.CELL_SIZE * positionX) + deltaX);
        final int screenY = (int) ((map.CELL_SIZE * positionY) + deltaY);

        g.setColor(objColor);

        // Body
        if (inFear) {
            g.setColor(Color.WHITE);
        }
        g.fillArc(screenX, screenY, map.CELL_SIZE, map.CELL_SIZE, 0, 360);
        g.fillRect((int) ((map.CELL_SIZE * positionX) + deltaX), (int) ((map.CELL_SIZE * positionY)
                + (map.CELL_SIZE / 2) + deltaY), map.CELL_SIZE, map.CELL_SIZE / 2);

        // Eyes
        if (inFear) {
            g.setColor(Color.BLACK);
        } else {
            g.setColor(Color.WHITE);
        }
        g.fillOval((int) ((map.CELL_SIZE * positionX) + 4 + deltaX),
                (int) ((map.CELL_SIZE * positionY) + 3 + deltaY), 8, 10);
        g.fillOval((int) ((map.CELL_SIZE * positionX) + 12 + deltaX),
                (int) ((map.CELL_SIZE * positionY) + 3 + deltaY), 8, 10);

        // Eyeballs
        g.setColor(Color.BLUE);
        g.fillOval((int) ((map.CELL_SIZE * positionX) + 7 + deltaX),
                (int) ((map.CELL_SIZE * positionY) + 6 + deltaY), 4, 4);
        g.fillOval((int) ((map.CELL_SIZE * positionX) + 13 + deltaX),
                (int) ((map.CELL_SIZE * positionY) + 6 + deltaY), 4, 4);

        // Debug draw path
        if (debugDrawPath && path != null) {
            for (int i = 0; i < path.getLength(); i++) {
                final Path.Step s = path.getStep(i);
                g.setColor(objColor);
                g.drawLine(map.CELL_SIZE * s.getX(), map.CELL_SIZE * s.getY(),
                        (map.CELL_SIZE * s.getX()) + map.CELL_SIZE, (map.CELL_SIZE * s.getY())
                                + map.CELL_SIZE);
            }
        }
    }
}

actors/Player.java


otherpackage pacman.actors;

import java.awt.Color;
import java.awt.Graphics2D;

import pacman.game.GameObject;
import pacman.game.Item;
import pacman.map.Map;
import pacman.util.Direction;

/**
 * Player (pacman) is the object controlled by the human playing the game Player
 * is a subclass of Actor
 * 
 * @author Ramsey Kant
 */
public class Player extends Actor {
    // State
    private int m_iScore; // Current score - Only valid for the current life /
                          // level. StateGame will pull this on death or on
                          // level change
    private boolean isPowered; // Powered up
    private long poweredExpireTime;

    /**
     * Class Constructor for Player
     * 
     * @param m
     *            Reference to the map object
     * @param x
     *            X coordiante to spawn the player at
     * @param y
     *            Y coordinate to spawn the player at
     */
    public Player(Map m, int x, int y) {
        super(OBJECT_PLAYER, Color.yellow, m, x, y);

        // State
        m_iScore = 0;
        isPowered = false;
        poweredExpireTime = 0;
    }

    // Getters and Setters

    /**
     * Increment score by amount. The is the current level score, not the entire
     * session score This function is typically called inside an Item's use()
     * function when the player picks up an item like a dot
     * 
     * @param amt
     *            Amount to increment
     */
    public void incrementScore(int amt) {
        m_iScore += amt;
    }

    /**
     * Get the current level score of the player
     * 
     * @return the score
     */
    public int getScore() {
        return m_iScore;
    }

    /**
     * Returns the isPowered flag which determines whether or not the player is
     * powered up and invicible to ghosts
     * 
     * @return True if the player is powered up
     */
    public boolean isPoweredUp() {
        return isPowered;
    }

    /**
     * Set powered up state and start the expirtation time for when the powerup
     * wears off
     * 
     * @param x
     *            True if powered up, false if otherwise
     * @see Player#isPoweredUp()
     */
    public void setPowerUp(boolean x) {
        isPowered = x;
        // If powered up, start the timer and increase speed temporarily
        if (isPowered) {
            poweredExpireTime = System.currentTimeMillis() + 10000;
        }
    }

    /**
     * Player act() method This should evaluate if there is: - a collission with
     * a ghost and how to handle that interaction - a dot or cherry being eaten
     * (call use() on the item) - a next movement
     */
    @Override
    public void act() {
        // If there is a ghost at the players location, this player is dead
        // unless the player is powered up (then the ghost dies)
        final Actor a = map.getActor(positionX, positionY, true);
        if (a != null && a.getType() == GameObject.OBJECT_GHOST) {
            // Notify the State of the loss if pacman isn't powered up
            if (!isPowered) {
                setDead(true);
                return;
            } else {
                a.setDead(true);
            }
        }

        // Check for powerup expire
        if (System.currentTimeMillis() > poweredExpireTime) {
            setPowerUp(false);
        }

        // Use item at current location
        boolean itemDestroy = false;
        final Item item = map.getItem(positionX, positionY);
        if (item != null) {
            itemDestroy = item.use(this);
        }

        // Update the item's state in the map (remove if itemDestroy is true)
        if (itemDestroy) {
            map.removeItem(positionX, positionY);
        }

        // Check if a change in direction was requested and if we can move
        // there. If yes that will be the new direction, if no, keep
        // the old direction.
        final Direction requestedDir = requestedMoveDirBuffer.getRequestedDirection();
        if (requestedDir != Direction.none) {
            if (map.canMove(this, requestedDir)) {
                currentMoveDir = requestedDir;
            }
        }

        // Based on the direction, increment the movement delta and set the
        // appropriate orientation
        // The delta's represent the screen position (in pixels) since the last
        // official change in position on the grid
        // When a delta in a certain direction passes the CELL_SIZE, the object
        // can change position in the map grid. This makes for smooth
        // transitions between tiles
        switch (currentMoveDir) {
            case up:
                // Move in the direction only if the next map cell in this
                // direction is reachable (not occupied by a wall)
                if (map.canMove(this, positionX, positionY - 1)) {
                    deltaX = 0;
                    deltaY = deltaY - speed;
                    // If the movement delta has surpassed the number of pixels for
                    // the cell, set him to the map cell he has reached by his movement
                    if (Math.abs(deltaY) >= map.CELL_SIZE) {
                        deltaY = 0;
                        move(positionX, positionY - 1);
                    }
                }
                dirOrient = 90;
                break;
            case right:
                if (map.canMove(this, positionX + 1, positionY)) {
                    deltaX = deltaX + speed;
                    deltaY = 0;
                    if (Math.abs(deltaX) >= map.CELL_SIZE) {
                        deltaX = 0;
                        move(positionX + 1, positionY);
                    }
                }
                dirOrient = 0;
                break;
            case down:
                if (map.canMove(this, positionX, positionY + 1)) {
                    deltaX = 0;
                    deltaY = deltaY + speed;
                    if (Math.abs(deltaY) >= map.CELL_SIZE) {
                        deltaY = 0;
                        move(positionX, positionY + 1);
                    }
                }
                dirOrient = -90;
                break;
            case left:
                if (map.canMove(this, positionX - 1, positionY)) {
                    deltaX = deltaX - speed;
                    deltaY = 0;
                    if (Math.abs(deltaX) >= map.CELL_SIZE) {
                        deltaX = 0;
                        move(positionX - 1, positionY);
                    }
                }
                dirOrient = 180;
                break;
            case stop:
            case none:
                // do not move
        }
    }

    /**
     * Draw & animate pacman
     * 
     * @param g
     *            The graphics context
     * @see Actor#act()
     */
    @Override
    public void paint(Graphics2D g) {
        // Change the position of pacman on screen by the offsets m_fDelta
        final int screenX = (int) ((map.CELL_SIZE * positionX) + deltaX);
        final int screenY = (int) ((map.CELL_SIZE * positionY) + deltaY);

        g.setColor(objColor);

        // Animate Pacman's mouth
        // When the player is half-way through a tile, close the flap. Open it
        // back up when the flap clears a tile.
        // This essentially creates an eating animation
        if ((Math.abs(deltaX) >= map.CELL_SIZE / 2) || Math.abs(deltaY) >= map.CELL_SIZE / 2) {
            g.fillArc(screenX, screenY, map.CELL_SIZE, map.CELL_SIZE, 0 + dirOrient, 360); // flap
                                                                                           // closed
        } else {
            g.fillArc(screenX, screenY, map.CELL_SIZE, map.CELL_SIZE, 35 + dirOrient, 270);
        }
    }
}

ai/AIManager.java


otherpackage pacman.ai;

import java.util.ArrayList;

import pacman.actors.Actor;
import pacman.actors.Ghost;
import pacman.actors.Player;
import pacman.game.GameObject;
import pacman.map.Map;
import pacman.map.Path;
import pacman.map.PathFinder;

/**
 * Strategy management behind the AI (Ghost objects)
 * 
 * @author Ramsey Kant
 */
public class AIManager {
    // References
    private Map map;
    private Player player;

    // Logic
    private boolean debugEnabled;
    private PathFinder finder;
    private final ArrayList<Ghost> ghosts;
    private long nextReleaseTime;

    /**
     * Class Constructor
     * 
     * @param m
     *            Reference to the map object being used by the game
     * @param pl
     *            Reference to the player
     * @param debug
     *            Set the debug flag allowing the AI manager to direct ghosts to
     *            exibit diagnostic behavior
     */
    public AIManager(Map m, Player pl, boolean debug) {
        // Set vars
        ghosts = new ArrayList<Ghost>();
        setReferences(m, pl);
        nextReleaseTime = System.currentTimeMillis() + 10000;
        debugEnabled = debug;
    }

    // Getters and Setters

    /**
     * Direct ghosts to display diagnostic information
     * 
     * @param d
     *            If true, ghosts will enter debug mode
     * @see Ghost#setDebugDrawPath
     */
    public void setDebugEnabled(boolean d) {
        debugEnabled = d;
    }

    /**
     * Set the global map and player references. Ghosts being tracked (in the
     * 'ghosts' ArrayList) will be updated
     * 
     * @param m
     *            Reference to the map
     * @param pl
     *            Reference to the player object
     */
    public void setReferences(Map m, Player pl) {
        ghosts.clear();
        map = m;
        player = pl;
        finder = new PathFinder(m, 500, false);

        // Get a list of all AI on the map
        final int nActors = map.getNumActors();
        for (int i = 0; i < nActors; i++) {
            final Actor a = map.getActor(i);
            if (a.getType() == GameObject.OBJECT_GHOST) {
                ghosts.add((Ghost) a);
            }
        }
    }

    /**
     * Run all logic required for AI operation; fear, ghost release, path
     * updates. Ghost act() functions are called here
     */
    public void process() {
        // Make sure the game is still running and there is a map
        if (map == null) {
            return;
        }

        // Determine if ghosts are fearful of the player
        boolean fear = false;
        if (map.getPlayer().isPoweredUp()) {
            fear = true;
        }

        // Release the next ghost
        if (System.currentTimeMillis() > nextReleaseTime) {
            for (final Ghost g : ghosts) {
                if (g.isTrapped()) {
                    g.setTrapped(false);
                    g.move(13, 11);
                    nextReleaseTime = System.currentTimeMillis() + 8000;
                    break;
                }
            }
        }

        // Go through a list of all AI on the map
        for (final Ghost ghost : ghosts) {
            // If a ghost just died, send them to jail
            if (ghost.isDead()) {
                // Find an empty spot in the jail
                final int x = 11;
                final int y = 13;
                int z = 0;
                while (!map.isEmpty(x + z, y)) {
                    z++;
                    if (z > 4) {
                        break;
                    }
                }

                // Clear path and move to jail
                ghost.updatePath(null);
                ghost.move(x, y);
                ghost.setTrapped(true);
                ghost.setDead(false);
            }

            // Any ghost not trapped is given the current fear status
            if (!ghost.isTrapped()) {
                // If fear switches from false to true for this ghost, abandon
                // their current (and likely) chase path
                if (!ghost.isInFear() && fear) {
                    ghost.updatePath(null);
                }
                ghost.setFear(fear);
            } else {
                ghost.setFear(false);
            }

            // Develop path for ghost
            if (!ghost.isTrapped() && ghost.needsNewPath()) {
                int randx = player.getX();
                int randy = player.getY();
                // 45% chance of randomizing a destination, or if they are
                // fearful
                if (fear || Math.random() < 0.45) {
                    randx = (int) (Math.random() * map.getWidth());
                    randy = (int) (Math.random() * map.getHeight());
                }
                final Path p = finder.findPath(ghost, ghost.getX(), ghost.getY(), randx, randy);
                ghost.updatePath(p);
            }

            // Run an act()
            ghost.act();

            // If debug is enabled, force ghost to draw it's path
            ghost.setDebugDrawPath(debugEnabled);
        }
    }
}

ai/AStarHeuristic.java


otherpackage pacman.ai;
import pacman.actors.Actor;
import pacman.map.Map;

/**
 * A heuristic that uses the tile that is closest to the target as the next best
 * tile.
 * 
 * @author Kevin Glass
 */
public class AStarHeuristic {

    /**
     * Get the additional heuristic cost of the given tile. This controls the
     * order in which tiles are searched while attempting to find a path to the
     * target location. The lower the cost the more likely the tile will be
     * searched.
     * 
     * @param map
     *            The map on which the path is being found
     * @param mover
     *            The entity that is moving along the path
     * @param x
     *            The x coordinate of the tile being evaluated
     * @param y
     *            The y coordinate of the tile being evaluated
     * @param tx
     *            The x coordinate of the target location
     * @param ty
     *            Teh y coordinate of the target location
     * @return The cost associated with the given tile
     */
    public float getCost(Map map, Actor mover, int x, int y, int tx, int ty) {
        final float dx = tx - x;
        final float dy = ty - y;

        final float result = (float) (Math.sqrt((dx * dx) + (dy * dy)));

        return result;
    }

}

editor/EditorFrame.java


otherpackage pacman.editor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.WindowConstants;

import pacman.game.GameObject;
import pacman.state.State;
import pacman.state.StateEditor;

/**
 * This code was edited or generated using CloudGarden's Jigloo SWT/Swing GUI
 * Builder, which is free for non-commercial use. If Jigloo is being used
 * commercially (ie, by a corporation, company or business for any purpose
 * whatever) then you should purchase a license for each developer using Jigloo.
 * Please visit www.cloudgarden.com for details. Use of Jigloo implies
 * acceptance of these licensing terms. A COMMERCIAL LICENSE HAS NOT BEEN
 * PURCHASED FOR THIS MACHINE, SO JIGLOO OR THIS CODE CANNOT BE USED LEGALLY FOR
 * ANY CORPORATE OR COMMERCIAL PURPOSE.
 */
public class EditorFrame extends javax.swing.JFrame {

    private static final long serialVersionUID = 1L;
    private final StateEditor editor;
    private JMenuItem jItemSaveAs;
    private JTextArea txtTeleportY;
    private JTextField txtTeleportX;
    private JLabel lblTeleportY;
    private JLabel lblTeleportX;
    private JLabel lblTeleportSettings;
    private JButton btnTeleport;
    private JButton btnNew;
    private JTextField txtFilename;
    private JButton btnLoad;
    private JButton btnSave;
    private JComboBox jWallTypeCombo;
    private JButton btnGhost;
    private JComboBox comboGhost;
    private JCheckBox chkGhostTrapped;
    private JLabel lblGhosts;
    private JButton btnPowerup;
    private JLabel jWallTypeLabel;
    private JMenuItem jItemExit;
    private JSeparator jSeperatorFile;
    private JMenuItem jItemSave;
    private JMenuItem jItemLoad;
    private JMenu jMenuFile;
    private JMenuBar jMenuBar1;
    private JLabel lblPlaceableObjs;
    private JSeparator jSeparator1;
    private JButton btnPacman;
    private JButton btnDot;
    private JButton btnWall;

    public EditorFrame(StateEditor e) {
        super();
        editor = e;
        initGUI();
    }

    private void initGUI() {
        try {
            setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            getContentPane().setLayout(null);
            this.setTitle("Pacman Map Editor - Ramsey Kant");
            this.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosed(WindowEvent evt) {
                    editor.getGame().requestChangeState(State.STATE_EXITING);
                }
            });
            {
                jMenuBar1 = new JMenuBar();
                setJMenuBar(jMenuBar1);
                {
                    jMenuFile = new JMenu();
                    jMenuBar1.add(jMenuFile);
                    jMenuFile.setText("File");
                    {
                        jItemLoad = new JMenuItem();
                        jMenuFile.add(jItemLoad);
                        jItemLoad.setText("Load");
                    }
                    {
                        jItemSave = new JMenuItem();
                        jMenuFile.add(jItemSave);
                        jItemSave.setText("Save");
                    }
                    {
                        jItemSaveAs = new JMenuItem();
                        jMenuFile.add(jItemSaveAs);
                        jItemSaveAs.setText("Save As..");
                    }
                    {
                        jSeperatorFile = new JSeparator();
                        jMenuFile.add(jSeperatorFile);
                    }
                    {
                        jItemExit = new JMenuItem();
                        jMenuFile.add(jItemExit);
                        jItemExit.setText("Exit");
                    }
                }
            }
            {
                btnWall = new JButton();
                getContentPane().add(btnWall);
                btnWall.setText("Wall");
                btnWall.setBounds(12, 218, 59, 23);
                btnWall.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent evt) {
                        editor.setMarkerObjectType(GameObject.OBJECT_WALL);
                    }
                });
            }
            {
                btnDot = new JButton();
                getContentPane().add(btnDot);
                btnDot.setText("Dot");
                btnDot.setBounds(12, 36, 59, 23);
                btnDot.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent evt) {
                        editor.setMarkerObjectType(GameObject.OBJECT_DOT);
                    }
                });
            }
            {
                btnPacman = new JButton();
                getContentPane().add(btnPacman);
                btnPacman.setText("Pacman");
                btnPacman.setBounds(136, 36, 110, 23);
                btnPacman.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent evt) {
                        editor.setMarkerObjectType(GameObject.OBJECT_PLAYER);
                    }
                });
            }
            {
                jSeparator1 = new JSeparator();
                getContentPane().add(jSeparator1);
                jSeparator1.setBounds(12, 301, 360, 10);
            }
            {
                lblPlaceableObjs = new JLabel();
                getContentPane().add(lblPlaceableObjs);
                lblPlaceableObjs.setText("Placeable Objects");
                lblPlaceableObjs.setBounds(12, 12, 129, 16);
            }
            {
                jWallTypeLabel = new JLabel();
                getContentPane().add(jWallTypeLabel);
                jWallTypeLabel.setText("Wall Type");
                jWallTypeLabel.setBounds(12, 196, 82, 16);
            }
            {
                final ComboBoxModel jWallTypeComboModel = new DefaultComboBoxModel(new String[] {
                        "Vertical", "Horizontal", "Top Left", "Top Right", "Bottom Left",
                        "Bottom Right", "Ghost Barrier" });
                jWallTypeCombo = new JComboBox();
                getContentPane().add(jWallTypeCombo);
                jWallTypeCombo.setModel(jWallTypeComboModel);
                jWallTypeCombo.setBounds(12, 246, 153, 23);
                jWallTypeCombo.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent evt) {
                        final String sType = (String) jWallTypeCombo.getSelectedItem();
                        if (sType.equals("Vertical")) {
                            editor.setMarkerWallType(GameObject.WALL_VERTICAL);
                        } else if (sType.equals("Horizontal")) {
                            editor.setMarkerWallType(GameObject.WALL_HORIZONTAL);
                        } else if (sType.equals("Top Left")) {
                            editor.setMarkerWallType(GameObject.WALL_TOPLEFT);
                        } else if (sType.equals("Top Right")) {
                            editor.setMarkerWallType(GameObject.WALL_TOPRIGHT);
                        } else if (sType.equals("Bottom Left")) {
                            editor.setMarkerWallType(GameObject.WALL_BOTTOMLEFT);
                        } else if (sType.equals("Bottom Right")) {
                            editor.setMarkerWallType(GameObject.WALL_BOTTOMRIGHT);
                        } else if (sType.equals("Ghost Barrier")) {
                            editor.setMarkerWallType(GameObject.WALL_GHOSTBARRIER);
                        } else {
                            editor.setMarkerWallType(GameObject.WALL_HORIZONTAL);
                        }
                    }
                });
            }
            {
                btnSave = new JButton();
                getContentPane().add(btnSave);
                btnSave.setText("Save");
                btnSave.setBounds(12, 317, 70, 23);
                btnSave.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent evt) {
                        editor.saveMap(txtFilename.getText());
                    }
                });
            }
            {
                btnLoad = new JButton();
                getContentPane().add(btnLoad);
                btnLoad.setText("Load");
                btnLoad.setBounds(87, 317, 68, 23);
                btnLoad.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent evt) {
                        editor.loadMap(txtFilename.getText());
                    }
                });
            }
            {
                txtFilename = new JTextField();
                getContentPane().add(txtFilename);
                txtFilename.setBounds(12, 345, 225, 23);
                txtFilename.setText("test.map");
            }
            {
                btnNew = new JButton();
                getContentPane().add(btnNew);
                btnNew.setText("New");
                btnNew.setBounds(160, 317, 71, 23);
                btnNew.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent evt) {
                        editor.newMap(28, 31);
                    }
                });
            }
            {
                btnTeleport = new JButton();
                getContentPane().add(btnTeleport);
                btnTeleport.setText("Teleport");
                btnTeleport.setBounds(237, 218, 110, 23);
                btnTeleport.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent evt) {
                        editor.setMarkerObjectType(GameObject.OBJECT_TELEPORT);
                        editor.setMarkerTeleport(Integer.parseInt(txtTeleportX.getText()),
                                Integer.parseInt(txtTeleportY.getText()));
                    }
                });
            }
            {
                lblTeleportSettings = new JLabel();
                getContentPane().add(lblTeleportSettings);
                lblTeleportSettings.setText("Teleport Settings");
                lblTeleportSettings.setBounds(237, 196, 123, 16);
            }
            {
                lblTeleportX = new JLabel();
                getContentPane().add(lblTeleportX);
                lblTeleportX.setText("Dest X:");
                lblTeleportX.setBounds(237, 249, 60, 16);
            }
            {
                lblTeleportY = new JLabel();
                getContentPane().add(lblTeleportY);
                lblTeleportY.setText("Dest Y: ");
                lblTeleportY.setBounds(235, 279, 52, 16);
            }
            {
                txtTeleportX = new JTextField();
                getContentPane().add(txtTeleportX);
                txtTeleportX.setText("13");
                txtTeleportX.setBounds(280, 246, 85, 23);
            }
            {
                txtTeleportY = new JTextArea();
                getContentPane().add(txtTeleportY);
                txtTeleportY.setText("17");
                txtTeleportY.setBounds(280, 275, 82, 20);
            }
            {
                btnPowerup = new JButton();
                getContentPane().add(btnPowerup);
                btnPowerup.setText("Powerup");
                btnPowerup.setBounds(12, 65, 102, 23);
                btnPowerup.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent evt) {
                        editor.setMarkerObjectType(GameObject.OBJECT_POWERUP);
                    }
                });
            }
            {
                lblGhosts = new JLabel();
                getContentPane().add(lblGhosts);
                lblGhosts.setText("Ghost Settings");
                lblGhosts.setBounds(272, 12, 76, 16);
            }
            {
                chkGhostTrapped = new JCheckBox();
                getContentPane().add(chkGhostTrapped);
                chkGhostTrapped.setText("Trapped");
                chkGhostTrapped.setBounds(360, 10, 100, 20);
                chkGhostTrapped.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent evt) {
                        editor.setMarkerGhostTrapped(!editor.getMarkerGhostTrapped());
                        System.out.println(editor.getMarkerGhostTrapped());
                    }
                });
            }
            {
                final ComboBoxModel comboGhostModel = new DefaultComboBoxModel(new String[] {
                        "Blinky", "Pinky", "Inky", "Clyde" });
                comboGhost = new JComboBox();
                getContentPane().add(comboGhost);
                comboGhost.setModel(comboGhostModel);
                comboGhost.setBounds(272, 65, 146, 23);
                comboGhost.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent evt) {
                        final String sType = (String) comboGhost.getSelectedItem();
                        editor.setMarkerGhostType(sType);
                    }
                });
            }
            {
                btnGhost = new JButton();
                getContentPane().add(btnGhost);
                btnGhost.setText("Add Ghost");
                btnGhost.setBounds(272, 36, 146, 23);
                btnGhost.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent evt) {
                        editor.setMarkerObjectType(GameObject.OBJECT_GHOST);
                    }
                });
            }
            pack();
            this.setSize(451, 547);
        } catch (final Exception e) {
            // add your error handling code here
            e.printStackTrace();
        }
    }

}

editor/EditorMarker.java


otherpackage pacman.editor;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;

import pacman.game.GameObject;
import pacman.map.Map;
import pacman.state.StateEditor;

/**
 * The EditorMarker is used by the StateEditor for navigation and selecting
 * tiles on the map EditorMarker is NOT tracked inside the Map EditorMarker is a
 * subclass of GameObject
 * 
 * @author Ramsey Kant
 */
public class EditorMarker extends GameObject {

    /**
     * Class constructor for EditorMarker
     * 
     * @param color
     *            Color of the marker
     * @param m
     *            Reference to the map object
     * @param x
     *            X coordinate to initially place the marker
     * @param y
     *            Y coordinate to initially place the marker
     */
    public EditorMarker(Color color, Map m, int x, int y) {
        super(GameObject.OBJECT_MARKER, color, m, x, y);
    }

    // Public Methods

    /**
     * Change tile is the EditorMarker's version of Actor's move() method.
     * Called by keyPressed in StateEditor Moves the Marker on the screen
     * 
     * @param dx
     *            Amount to change the current X coordinate by
     * @param dy
     *            Amount to change the current Y coordinate by
     * @see StateEditor#keyPressed(KeyEvent)
     */
    public void changeTile(int dx, int dy) {
        // Check bounds
        if (positionX + dx < 0 || positionY + dy < 0 || positionX + dx >= map.getWidth()
                || positionY + dy >= map.getHeight()) {
            return;
        }

        positionX += dx;
        positionY += dy;
    }

    /**
     * EditorMarker has a blank act() method
     * 
     * @see GameObject#act()
     */
    @Override
    public void act() {
        // do nothing
    }

    /**
     * EditorMarker appears as a circle around the tile being edited. The color
     * is set in the constructor
     * 
     * @see GameObject#paint(Graphics2D)
     */
    @Override
    public void paint(Graphics2D g) {
        final int screenX = (map.CELL_SIZE * positionX);
        final int screenY = (map.CELL_SIZE * positionY);

        g.setColor(objColor);

        g.drawOval(screenX, screenY, map.CELL_SIZE, map.CELL_SIZE);
    }

}

game/Game.java


otherpackage pacman.game;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;

import pacman.state.State;
import pacman.state.StateEditor;
import pacman.state.StateGame;
import pacman.state.StateMenu;
import pacman.state.StateScoreboard;

/**
 * The Game Supervisor. This class implements that core program logic, state
 * management, and graphics.
 * 
 * @author Ramsey Kant
 */
public class Game extends Canvas {
    private static final long serialVersionUID = 1L;

    // Debug vars
    private boolean debugEnabled;

    // Threading
    private boolean runMainThread;

    // Graphics variables
    private Frame frame;
    public final int RES_X;
    public final int RES_Y;
    private BufferStrategy m_gBuffer;

    // State
    private int stateId;
    private State currentState;
    private boolean changeStateRequested;
    private int requestedState;
    private String startMap;

    /**
     * Class Constructor Set's up graphics and put's game logic into a startup
     * state by calling init()
     * 
     * @param x
     *            Resolution X
     * @param y
     *            Resolution Y
     * @see Game#init()
     */
    public Game(int x, int y) {
        // Set resolution settings
        RES_X = x;
        RES_Y = y;

        // Init game
        init();
    }

    /**
     * Startup functionality for the program called by the constructor
     */
    private void init() {
        // Debug vars
        debugEnabled = false;

        startMap = "test.map";
        changeStateRequested = false;

        // Setup the game frame
        frame = new Frame("Pacman");
        frame.setLayout(null);
        setBounds(0, 0, RES_X, RES_Y);
        frame.add(this);
        frame.setSize(RES_X, RES_Y);
        frame.setResizable(false);
        frame.setVisible(true);

        // Set the exit handler with an anonymous class
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                // Exit main thread
                runMainThread = false;
            }
        });

        // Setup double buffering
        setIgnoreRepaint(true); // We'll handle repainting
        createBufferStrategy(2);
        m_gBuffer = getBufferStrategy();

        runMainThread = true;
    }

    // Getter and Setter methods

    /**
     * Get the Frame object encapsulating the program
     * 
     * @return The frame
     */
    public Frame getFrame() {
        return frame;
    }

    /**
     * Get a 'handle' of the current graphics buffer for drawing
     * 
     * @return The Graphics2D buffer
     */
    public Graphics2D getGraphicsContext() {
        return (Graphics2D) m_gBuffer.getDrawGraphics();
    }

    /**
     * Get the name of the map to be loaded in StateGame
     * 
     * @return Map name (with .map extension)
     */
    public String getStartMap() {
        return startMap;
    }

    /**
     * Set the default starting map (set by menu)
     * 
     * @param m
     *            The name of the map to load (with the .map extension)
     */
    public void setStartMap(String m) {
        startMap = m;
    }

    /**
     * Return the current debug setting
     * 
     * @return True if debug setting is on
     * @see Game#toggleDebug()
     */
    public boolean isDebugEnabled() {
        return debugEnabled;
    }

    /**
     * Toggle debugging. Facilities like AIManager use this flag to display
     * diagnostic information like AI paths
     */
    public void toggleDebug() {
        debugEnabled = !debugEnabled;
    }

    // Public Methods

    /**
     * Called by other states to safely change currentState. This is done so the
     * currentState's logic can finish
     * 
     * @see Game#mainThreadLoop()
     */
    public void requestChangeState(int state) {
        requestedState = state;
        changeStateRequested = true;
    }

    /**
     * The main game loop that handles graphics and game state determination
     */
    public void mainThreadLoop() {
    	final long loopTime = 20;
    	long lastLoopStart = 0;
    	
        while (runMainThread) {
            
            if ((lastLoopStart + loopTime) > System.currentTimeMillis()){
                continue;
            }
            
            lastLoopStart = System.currentTimeMillis();
        	
            // If a state change was requested, execute it now
            if (changeStateRequested) {
                changeStateRequested = false;
                changeState(requestedState);
                continue;
            }

            final Graphics2D g = getGraphicsContext();

            // Wipe the screen
            g.setColor(Color.black);
            g.fillRect(0, 0, RES_X, RES_Y);

            // Run the logic of the current game state here
            currentState.logic();
            
            // Show the new buffer
            g.dispose();
            m_gBuffer.show();
            
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }

    // Private Methods

    /**
     * Change the state of the game. Called in mainThreadLogic()
     * 
     * @param state
     *            The state to set. Must match the static vars above
     * @see Game#requestChangeState(int)
     * @see Game#mainThreadLoop()
     */
    private void changeState(int state) {
        // Cleanup for the outgoing state
        if (currentState != null) {
            frame.removeKeyListener(currentState);
            removeKeyListener(currentState);
            currentState.end();
        }

        // Set the new state type
        stateId = state;

        // Instance the new state (reset() is called in the construtor)
        switch (stateId) {
            case State.STATE_GAME:
                currentState = new StateGame(this);
                break;
            case State.STATE_SCOREBOARD:
                currentState = new StateScoreboard(this);
                /*
                 * StateGame sb = new StateScoreboard(); int newScore = 0;
                 * 
                 * // If the previous state was STATE_GAME, pull the session
                 * score and pass it to the scoreboard if(currentState
                 * instanceof StateGame)
                 * sb.addScore((int)((StateGame)currentState
                 * ).getSessionScore()));
                 * 
                 * currentState = sb;
                 */
                break;
            case State.STATE_EDITOR:
                currentState = new StateEditor(this);
                break;
            case State.STATE_MENU:
                currentState = new StateMenu(this);
                break;
            case State.STATE_EXITING:
                currentState = null;
                runMainThread = false;
                break;
            default:
                break;
        }

        // Setup input handler and reset()
        if (currentState != null) {
            frame.addKeyListener(currentState);
            addKeyListener(currentState);
        }
    }
}

game/GameObject.java


otherpackage pacman.game;

import java.awt.Color;
import java.awt.Graphics2D;

import pacman.actors.Actor;
import pacman.map.Map;
import pacman.state.StateGame;

/**
 * A game object is anything on the pacman grid (wall, cherry, ghost, player).
 * GameObject is the base class of almost everything within the Map
 * 
 * @author Ramsey Kant
 */
public abstract class GameObject {
    // Static type vars
    public static final int OBJECT_DOT = 1;
    public static final int OBJECT_POWERUP = 2;
    public static final int OBJECT_CHERRY = 4;
    public static final int OBJECT_PLAYER = 8;
    public static final int OBJECT_GHOST = 16;
    public static final int OBJECT_MARKER = 32; // Virtual
    public static final int OBJECT_WALL = 64; // Virtual
    public static final int OBJECT_TELEPORT = 128;

    // Wall types (Walls aren't instanced GameObject's)
    public static final byte WALL_VERTICAL = 1;
    public static final byte WALL_HORIZONTAL = 2;
    public static final byte WALL_TOPLEFT = 3;
    public static final byte WALL_TOPRIGHT = 4;
    public static final byte WALL_BOTTOMLEFT = 5;
    public static final byte WALL_BOTTOMRIGHT = 6;
    public static final byte WALL_GHOSTBARRIER = 7;

    // Generic object attributes
    protected int objType;
    protected Color objColor;
    protected int positionX;
    protected int positionY;

    // Outside refereneces
    protected final Map map; // Can only be set once. Object only exists within
                             // the map. If the map changes, new objects are
                             // created

    // Getters and Setters

    /**
     * Return the type of the object set in the constructor. See static types
     * defined in GameObject
     * 
     * @return type of object
     */
    public int getType() {
        return objType;
    }

    /**
     * Grab the current java.awt.Color (base color) the object is being rendered
     * in
     * 
     * @return Base Color of the object
     */
    public Color getColor() {
        return objColor;
    }

    /**
     * Set the current base color used when rendering the object
     * 
     * @param c
     *            java.awt.Color Color of object
     */
    public void setColor(Color c) {
        objColor = c;
    }

    /**
     * Grab the current X coordinate of the object on the map. This property is
     * frequently modified by the Map class and move() method
     * 
     * @see Actor#move(int, int)
     */
    public int getX() {
        return positionX;
    }

    /**
     * Grab the current Y coordinate of the object on the map. This property is
     * frequently modified by the Map class and move() method
     * 
     * @see Actor#move(int, int)
     */
    public int getY() {
        return positionY;
    }

    // Public & Protected Abstract methods

    /**
     * Class Constructor for a game object
     * 
     * @param type
     *            Type of game object (see static types above)
     * @param color
     *            Standard java Color
     * @param m
     *            Reference to the global Map
     * @param x
     *            Initial x coordinate
     * @param y
     *            Initial y coordinate
     */
    public GameObject(int type, Color color, Map m, int x, int y) {
        objType = type;
        objColor = color;
        map = m;
        positionX = x;
        positionY = y;
    }

    /**
     * Perform a "Think" cycle for the Object This includes things like self
     * maintenance and movement
     */
    public abstract void act();

    /**
     * Draw the object. Subclasses should define how they are to be drawn. This
     * is called in StateGame's logic()
     * 
     * @param g
     *            The graphics context
     * @see StateGame#logic()
     */
    public abstract void paint(Graphics2D g);
}

game/Item.java


otherpackage pacman.game;

import java.awt.Color;
import java.awt.Graphics2D;

import pacman.actors.Player;
import pacman.map.Map;

/**
 * Item objects are GameObject's that can be manipulated by the Player on the
 * map (teleports, dots, powerups, fruit) Item is a subclass of GameObject
 * 
 * @author Ramsey Kant
 */
public class Item extends GameObject {
    // Teleportation vars
    private int teleportDestX;
    private int teleportDestY;

    /**
     * Class constructor for Item
     * 
     * @param type
     *            Object type
     * @param color
     *            Base color of the item
     * @param m
     *            Reference to the map object
     * @param x
     *            X coordinate the item will occupy on the map
     * @param y
     *            Y coordinate the item with occupy on the map
     * @see GameObject
     */
    public Item(int type, Color color, Map m, int x, int y) {
        super(type, color, m, x, y);

        teleportDestX = 13;
        teleportDestY = 17;
    }

    /**
     * Set the destination coordinates for teleportation. This isn't useful to
     * any item other than a teleport
     * 
     * @param x
     *            X destination coordinate
     * @param y
     *            Y destination coordinate
     */
    public void setTeleport(int x, int y) {
        teleportDestX = x;
        teleportDestY = y;
    }

    /**
     * Retrieve the teleport destination X coordinate
     * 
     * @return X destination coordinate
     * @see Item#setTeleport(int, int)
     */
    public int getTeleportX() {
        return teleportDestX;
    }

    /**
     * Retrieve the teleport destination Y coordinate
     * 
     * @return Y destination coordinate
     * @see Item#setTeleport(int, int)
     */
    public int getTeleportY() {
        return teleportDestY;
    }

    /**
     * Called when the item is picked up / used by the player (in the player's
     * act() function) Add point values or trigger powerup modifiers here (using
     * the pl object)
     * 
     * @param pl
     *            Player that uses the item
     * @return True->Destroy the item. False->Keep the item on the map
     * @see Player#act()
     */
    public boolean use(Player pl) {
        boolean destroy = false;

        // Perform action based on type
        switch (objType) {
            case OBJECT_DOT:
                pl.incrementScore(10);
                destroy = true;
                break;
            case OBJECT_POWERUP:
                pl.incrementScore(50);
                pl.setPowerUp(true);
                destroy = true;
                break;
            case OBJECT_TELEPORT:
                pl.move(teleportDestX, teleportDestY);
                break;
            default:
                break;
        }

        return destroy;
    }

    /**
     * Item's have no "think" process. Blank method
     * 
     * @see GameObject#act()
     */
    @Override
    public void act() {
        // do nothing
    }

    /**
     * Draw the item based on it's type
     * 
     * @see GameObject#paint(java.awt.Graphics2D)
     */
    @Override
    public void paint(Graphics2D g) {
        g.setColor(objColor);

        // Item's are placed in the center of a cell
        final int center_x = (positionX * map.CELL_SIZE) + map.CELL_SIZE / 2;
        final int center_y = (positionY * map.CELL_SIZE) + map.CELL_SIZE / 2;

        // Render item based on type
        switch (objType) {
            case OBJECT_DOT:
                g.fillArc(center_x - 4, center_y - 4, 8, 8, 0, 360);
                break;
            case OBJECT_POWERUP:
                g.fillArc(center_x - 8, center_y - 8, 16, 16, 0, 360);
                break;
            case OBJECT_TELEPORT:
                g.fillOval(center_x - 6, center_y - 8, 12, 16);
                break;
            default:
                break;
        }
    }

}

game/JPacmanGame.java


otherpackage pacman.game;
import pacman.state.State;

/**
 * The entry point of the program
 * 
 * @author Ramsey Kant
 */
public class JPacmanGame {
    public static void main(String[] arg) {
        final Game g = new Game(1024, 768);
        g.requestChangeState(State.STATE_MENU);
        g.mainThreadLoop();
        System.exit(0);
    }
}

map/Map.java


otherpackage pacman.map;

import java.awt.Color;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

import pacman.actors.Actor;
import pacman.actors.Ghost;
import pacman.actors.Player;
import pacman.game.GameObject;
import pacman.game.Item;
import pacman.util.Direction;

/**
 * Map class keeps track objects on the playing grid, helper methods to make
 * movement decisions, and export/import methods for the editor
 * 
 * @author Ramsey Kant
 */
public class Map {
    // Map parameters (width & height represent # of cells)
    private int mapWidth;
    private int mapHeight;
    public final int CELL_SIZE;
    public final int WALL_THICKNESS;
    public final int WALL_OVERLAP;
    public final double SCALE;

    // Instance vars
    private byte collideMap[][];
    private Item itemMap[][];
    private ArrayList<Actor> actorList;
    private int dotsRemaining;

    /**
     * Class constructor, inits a blank map based on a width, height, and cell
     * size Used in the editor
     * 
     * @param w
     *            Width of the map
     * @param h
     *            Height of the map
     * @param cs
     *            Size of individual cells in pixels
     */
    public Map(int w, int h, double scale) {
        // Set map parameters
        mapWidth = w;
        mapHeight = h;
        SCALE = scale;
        CELL_SIZE = (int) (32 * scale);
        WALL_THICKNESS = (int) (12 * scale);
        WALL_OVERLAP = (int) (10 * scale);
        dotsRemaining = 0;

        // Initialize collideMap, a 2D array that contains all static collidable
        // GameObjects
        // We use this for fast lookup during collision detection and AI
        // movement paths
        collideMap = new byte[mapWidth][mapHeight];

        // Initialize itemMap, a 2D array that contains items (dots, powerups,
        // cherry) on the map
        itemMap = new Item[mapWidth][mapHeight];

        // Create m_objects, an arraylist with all actorList
        actorList = new ArrayList<Actor>();
    }

    /**
     * Class Constructor that reads the map data from filename
     * 
     * @param filename
     *            The file name of the map to read contents from
     * @param cs
     *            Size of individual cells in pixels. This is something that
     *            should be deteremined by graphics, not the mapfile
     */
    public Map(String filename, double scale) {
        // Set the cell size
        SCALE = scale;
        CELL_SIZE = (int) (32 * scale);
        WALL_THICKNESS = (int) (12 * scale);
        WALL_OVERLAP = (int) (10 * scale);

        // Read contents of the map file
        read(filename);
    }

    /**
     * The width of the map originally set in the constructor
     * 
     * @return The width of the map
     */
    public int getWidth() {
        return mapWidth;
    }

    /**
     * The height of the map originally set in the constructor
     * 
     * @return The height of the map
     */
    public int getHeight() {
        return mapHeight;
    }

    /**
     * Get the number of actorList on the map (the size of the actorList
     * ArrayList)
     * 
     * @return Number of actorList
     */
    public int getNumActors() {
        return actorList.size();
    }

    /**
     * Return the collidable map (a 2d array of bytes which correspond to the
     * collidable types defined in GameObject)
     * 
     * @return collidable map (collideMap)
     */
    public byte[][] getCollidableMap() {
        return collideMap;
    }

    /**
     * Return the item map (a 2D array of Item objects)
     * 
     * @return item map (itemMap)
     */
    public Item[][] getItemMap() {
        return itemMap;
    }

    /**
     * Return the number of dots remaining on the map. This is tracked by the
     * dotsRemaining local var (not a loop and count in itemMap)
     * 
     * @return dots remaining
     */
    public int getDotsRemaining() {
        return dotsRemaining;
    }

    /**
     * Add a collidable (by type) to the collideMap
     * 
     * @param x
     *            X coordinate
     * @param y
     *            Y coordinate
     * @param t
     *            Type of collidable
     * @return True if successful
     */
    public boolean addCollidable(int x, int y, byte t) {
        // Check bounds
        if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) {
            return false;
        }

        // Check if theres already something there
        if (collideMap[x][y] > 0) {
            return false;
        }

        // Add to the collideMap
        collideMap[x][y] = t;
        return true;
    }

    /**
     * Put a new item to the item map
     * 
     * @param item
     *            Item
     * @return True if successful
     */
    public boolean addItem(Item item) {
        if (item == null) {
            return false;
        }

        // Check bounds
        final int x = item.getX();
        final int y = item.getY();
        if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) {
            return false;
        }

        // Add to the itemMap
        if (item.getType() == GameObject.OBJECT_DOT) {
            dotsRemaining++;
        }
        itemMap[x][y] = item;
        return true;
    }

    /**
     * Put a new actor in the map (actorList ArrayList)
     * 
     * @param act
     *            Actor
     * @return True if successful
     */
    public boolean addActor(Actor act) {
        if (act == null) {
            return false;
        }

        // Check bounds
        final int x = act.getX();
        final int y = act.getY();
        if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) {
            return false;
        }

        // Add to the array list
        actorList.add(act);
        return true;
    }

    /**
     * Return a value at (x,y) in the collision map
     * 
     * @param x
     *            X Coordinate
     * @param y
     *            Y Coordinate
     * @return Integer that represents the collision object
     */
    public byte getCollidable(int x, int y) {
        // Check bounds
        if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) {
            return -1;
        }

        return collideMap[x][y];
    }

    /**
     * Return an item at coordinate (x,y) from within the item map (itemMap)
     * 
     * @param x
     *            X Coordinate
     * @param y
     *            Y Coordinate
     * @return Item the item that is found at (x,y)
     */
    public Item getItem(int x, int y) {
        // Check bounds
        if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) {
            return null;
        }

        return itemMap[x][y];
    }

    /**
     * Return an actor at index in the actorList ArrayList
     * 
     * @param idx
     *            Index in actorList
     * @return Actor (null if non-existant)
     */
    public Actor getActor(int idx) {
        Actor act = null;
        try {
            act = actorList.get(idx);
        } catch (final IndexOutOfBoundsException e) {
            e.printStackTrace();
        }
        return act;
    }

    /**
     * Find and return the player object within the local actorList ArrayList
     * 
     * @return The player object. null if not found
     */
    public Player getPlayer() {
        // Get from the object map
        for (final Actor g : actorList) {
            if (g.getType() == GameObject.OBJECT_PLAYER) {
                return (Player) g;
            }
        }

        return null;
    }

    /**
     * Return an actor at coordinate (x,y)
     * 
     * @param x
     *            X Coordinate
     * @param y
     *            Y Coordinate
     * @param notPlayer
     *            If true, ignore a "Player" actor at (x,y)
     * @return Actor (null if an actor doesn't exist at the position)
     */
    public Actor getActor(int x, int y, boolean notPlayer) {
        // Check bounds
        if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) {
            return null;
        }

        // Get from the object map
        for (final Actor g : actorList) {
            if (notPlayer && g.getType() == GameObject.OBJECT_PLAYER) {
                continue;
            }

            if (g.getX() == x && g.getY() == y) {
                return g;
            }
        }

        return null;
    }

    /**
     * Remove an actor from actorList based on index. Be careful when using
     * this! Just because an actor isn't in the map doesn't mean it's not
     * 'alive' This is primarily for the editor
     * 
     * @param idx
     *            Index of the actor
     */
    public void removeActor(int idx) {
        actorList.remove(idx);
    }

    /**
     * Remove an item from the item array by coordinate (x, y)
     * 
     * @param x
     *            X coordinate of the item
     * @param y
     *            Y coordinate of the item
     */
    public void removeItem(int x, int y) {
        // Check bounds
        if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) {
            return;
        }

        if (itemMap[x][y].getType() == GameObject.OBJECT_DOT) {
            dotsRemaining--;
        }

        itemMap[x][y] = null;
    }

    /**
     * Remove everything at coordiante (x,y) Used by the editor only
     * 
     * @param x
     *            X coordinate
     * @param y
     *            Y coordinate
     * @return boolean True if something was removed, false if otherwise
     */
    public boolean removeAnyAt(int x, int y) {
        boolean rm = false;

        // Check bounds
        if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) {
            return false;
        }

        // Remove any collidable
        if (collideMap[x][y] != 0) {
            collideMap[x][y] = 0;
            rm = true;
        }

        // Remove any item
        if (itemMap[x][y] != null) {
            itemMap[x][y] = null;
            rm = true;
        }

        // Remove any actor
        for (int i = 0; i < actorList.size(); i++) {
            Actor a = actorList.get(i);
            if (a.getX() == x && a.getY() == y) {
                actorList.remove(i);
                a = null;
                i--;
                rm = true;
            }
        }

        return rm;
    }

    /**
     * Find the distance (Manhattan) between two objects
     * 
     * @param start
     *            GameObject at the initial position
     * @param end
     *            GameObject at the end position
     * @return Distance (integer)
     */
    public int findDistance(GameObject start, GameObject end) {
        return (int) Math.sqrt(Math.pow(Math.abs(start.getX() - end.getX()), 2)
                + Math.pow(Math.abs(start.getY() - end.getY()), 2));
    }

    /**
     * Check if a coordinate is completely empty (void of actorList, items, and
     * collissions) Used by the editor
     * 
     * @param x
     *            A x coordinate to move to
     * @param y
     *            A y coordinate to move to
     * @return True if empty. False if otherwise
     */
    public boolean isEmpty(int x, int y) {
        // Check bounds
        if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) {
            return false;
        }

        // Check if the Object is hitting something on the collideMap
        if (getCollidable(x, y) != 0) {
            return false;
        }

        // Check if object is hitting something on the itemMap
        if (getItem(x, y) != null) {
            return false;
        }

        // Actor collission
        if (getActor(x, y, false) != null) {
            return false;
        }

        return true;
    }

    /**
     * Move attempt method. Changes the position the map of the game object if
     * there are no obstructions
     * 
     * @param act
     *            The actor object trying to move
     * @param x
     *            A x coordinate to move to
     * @param y
     *            A y coordinate to move to
     * @return True if the move succeeded. False if otherwise
     */
    public boolean canMove(Actor act, int x, int y) {
        if (act == null) {
            return false;
        }

        // Check bounds
        if (!isInBounds(x, y)) {
            return false;
        }

        // Check if the Object is hitting something on the collideMap
        if (getCollidable(x, y) != 0) {
            return false;
        }

        // Allow the Actor to move
        return true;
    }

    public boolean canMove(Actor act, Direction dir) {
        int x = act.getX();
        int y = act.getY();
        switch (dir) {
            case up:
                y--;
                break;
            case right:
                x++;
                break;
            case down:
                y++;
                break;
            case left:
                x--;
                break;
            case none:
                return true;
        }

        return canMove(act, x, y);
    }

    private boolean isInBounds(int x, int y) {
        return x > 0 && y > 0 && x <= mapWidth && y <= mapHeight;
    }

    /**
     * Get the cost of moving through the given tile. This can be used to make
     * certain areas more desirable. A simple and valid implementation of this
     * method would be to return 1 in all cases.
     * 
     * @param mover
     *            The mover that is trying to move across the tile
     * @param sx
     *            The x coordinate of the tile we're moving from
     * @param sy
     *            The y coordinate of the tile we're moving from
     * @param tx
     *            The x coordinate of the tile we're moving to
     * @param ty
     *            The y coordinate of the tile we're moving to
     * @return The relative cost of moving across the given tile
     */
    public float getCost(Actor mover, int sx, int sy, int tx, int ty) {
        return 1;
    }

    /**
     * Write the contents of this map to a file in the correct format
     * 
     * @param filename
     *            File name of the map
     */
    public void write(String filename) {
        FileOutputStream fout;
        DataOutputStream data;

        try {
            fout = new FileOutputStream(filename);
            data = new DataOutputStream(fout);

            // Write the map file magic
            data.writeUTF("RKPACMAP");

            // Write map width & height
            data.writeInt(mapWidth);
            data.writeInt(mapHeight);

            // Write the collision map
            for (int x = 0; x < mapWidth; x++) {
                for (int y = 0; y < mapHeight; y++) {
                    data.write(collideMap[x][y]);
                }
            }

            // Write the item map
            Item item = null;
            for (int x = 0; x < mapWidth; x++) {
                for (int y = 0; y < mapHeight; y++) {
                    item = itemMap[x][y];

                    // If an item doesnt exist at (x,y), write 'false' for
                    // nonexistant and continue
                    if (item == null) {
                        data.writeBoolean(false);
                        continue;
                    }
                    data.writeBoolean(true);

                    // Write properties of the item
                    data.writeInt(item.getType());
                    data.writeInt(item.getX());
                    data.writeInt(item.getY());
                    data.writeInt(item.getColor().getRGB());
                    if (item.getType() == GameObject.OBJECT_TELEPORT) {
                        data.writeInt(item.getTeleportX());
                        data.writeInt(item.getTeleportY());
                    }
                }
            }

            // Write the number of actorList, then all actor data
            data.writeInt(actorList.size());
            for (final Actor a : actorList) {
                data.writeInt(a.getType());
                data.writeInt(a.getX());
                data.writeInt(a.getY());
                data.writeInt(a.getColor().getRGB());
                if (a.getType() == GameObject.OBJECT_GHOST) {
                    data.writeBoolean(((Ghost) a).isTrapped());
                }
            }

            data.close();
            fout.close();
        } catch (final IOException e) {
            System.out.println("Failed to write map file: " + e.getMessage());
        }
    }

    /**
     * Read a file with map contents and set the properties in this map Called
     * by the constructor.
     * 
     * @param filename
     *            File name of the map
     */
    private void read(String filename) {

        FileInputStream fin;
        DataInputStream data;

        try {
            fin = new FileInputStream(filename);
            data = new DataInputStream(fin);

            // Check for the magic
            if (!data.readUTF().equals("RKPACMAP")) {
                System.out.println("Not a map file!");
                return;
            }

            // Read map width & height
            mapWidth = data.readInt();
            mapHeight = data.readInt();
            dotsRemaining = 0;

            // Initialize collideMap, a 2D array that contains all static
            // collidable GameObjects
            // We use this for fast lookup during collision detection and AI
            // movement paths
            collideMap = new byte[mapWidth][mapHeight];

            // Initialize itemMap, a 2D array that contains items (dots,
            // powerups, cherry) on the map
            itemMap = new Item[mapWidth][mapHeight];

            // Create m_objects, an arraylist with all actorList
            actorList = new ArrayList<Actor>();

            // Read the collision map
            for (int x = 0; x < mapWidth; x++) {
                for (int y = 0; y < mapHeight; y++) {
                    addCollidable(x, y, data.readByte());
                }
            }

            // Read the item map
            for (int x = 0; x < mapWidth; x++) {
                for (int y = 0; y < mapHeight; y++) {
                    // If an item doesnt exist at (x,y), continue
                    if (!data.readBoolean()) {
                        continue;
                    }

                    // Read and set properties of the item
                    final int t = data.readInt();
                    final int ix = data.readInt();
                    final int iy = data.readInt();
                    final Color c = new Color(data.readInt());
                    addItem(new Item(t, c, this, ix, iy));
                    if (t == GameObject.OBJECT_TELEPORT) {
                        final int teleX = data.readInt();
                        final int teleY = data.readInt();
                        itemMap[ix][iy].setTeleport(teleX, teleY);
                    }
                }
            }

            // Read the number of actorList, then all actor data
            final int nActorsSize = data.readInt();
            for (int i = 0; i < nActorsSize; i++) {
                final int t = data.readInt();
                final int ix = data.readInt();
                final int iy = data.readInt();
                final Color c = new Color(data.readInt());
                if (t == GameObject.OBJECT_PLAYER) {
                    addActor(new Player(this, ix, iy));
                } else if (t == GameObject.OBJECT_GHOST) {
                    final boolean trap = data.readBoolean();
                    addActor(new Ghost(c, this, ix, iy, trap));
                } /*
                   * else { addActor(new Actor(t, c, this, ix, iy)); }
                   */
            }

            data.close();
            fin.close();
        } catch (final IOException e) {
            System.out.println("Failed to read map file: " + e.getMessage());
        }
    }

}

map/Path.java


otherpackage pacman.map;
import java.util.ArrayList;

/**
 * A path determined by some path finding algorithm. A series of steps from the
 * starting location to the target location. This includes a step for the
 * initial location.
 * 
 * @author Kevin Glass
 */
public class Path {
    /** The list of steps building up this path */
    private final ArrayList<Step> steps;

    /**
     * Create an empty path
     */
    public Path() {
        steps = new ArrayList<Step>();
    }

    /**
     * Get the length of the path, i.e. the number of steps
     * 
     * @return The number of steps in this path
     */
    public int getLength() {
        return steps.size();
    }

    /**
     * Get the step at a given index in the path
     * 
     * @param index
     *            The index of the step to retrieve. Note this should be >= 0
     *            and < getLength();
     * @return The step information, the position on the map.
     */
    public Step getStep(int index) {
        return steps.get(index);
    }

    /**
     * Get the x coordinate for the step at the given index
     * 
     * @param index
     *            The index of the step whose x coordinate should be retrieved
     * @return The x coordinate at the step
     */
    public int getX(int index) {
        return getStep(index).x;
    }

    /**
     * Get the y coordinate for the step at the given index
     * 
     * @param index
     *            The index of the step whose y coordinate should be retrieved
     * @return The y coordinate at the step
     */
    public int getY(int index) {
        return getStep(index).y;
    }

    /**
     * Append a step to the path.
     * 
     * @param x
     *            The x coordinate of the new step
     * @param y
     *            The y coordinate of the new step
     */
    public void appendStep(int x, int y) {
        steps.add(new Step(x, y));
    }

    /**
     * Prepend a step to the path.
     * 
     * @param x
     *            The x coordinate of the new step
     * @param y
     *            The y coordinate of the new step
     */
    public void prependStep(int x, int y) {
        steps.add(0, new Step(x, y));
    }

    /**
     * Check if this path contains the given step
     * 
     * @param x
     *            The x coordinate of the step to check for
     * @param y
     *            The y coordinate of the step to check for
     * @return True if the path contains the given step
     */
    public boolean contains(int x, int y) {
        return steps.contains(new Step(x, y));
    }

    /**
     * A single step within the path
     * 
     * @author Kevin Glass
     */
    public class Step {
        /** The x coordinate at the given step */
        private final int x;
        /** The y coordinate at the given step */
        private final int y;

        /**
         * Create a new step
         * 
         * @param x
         *            The x coordinate of the new step
         * @param y
         *            The y coordinate of the new step
         */
        public Step(int x, int y) {
            this.x = x;
            this.y = y;
        }

        /**
         * Get the x coordinate of the new step
         * 
         * @return The x coodindate of the new step
         */
        public int getX() {
            return x;
        }

        /**
         * Get the y coordinate of the new step
         * 
         * @return The y coodindate of the new step
         */
        public int getY() {
            return y;
        }

        /**
         * @see Object#hashCode()
         */
        @Override
        public int hashCode() {
            return x * y;
        }

        /**
         * @see Object#equals(Object)
         */
        @Override
        public boolean equals(Object other) {
            if (other instanceof Step) {
                final Step o = (Step) other;

                return (o.x == x) && (o.y == y);
            }

            return false;
        }
    }
}

map/PathFinder.java


otherpackage pacman.map;
import java.util.ArrayList;
import java.util.Collections;

import pacman.actors.Actor;
import pacman.ai.AStarHeuristic;

/**
 * A path finder implementation that uses the AStar heuristic based algorithm to
 * determine a path.
 * 
 * @author Kevin Glass
 */
public class PathFinder {
    /** The set of nodes that have been searched through */
    private final ArrayList<Node> closed;
    /** The set of nodes that we do not yet consider fully searched */
    private final SortedNodeList open = new SortedNodeList();

    /** The map being searched */
    private final Map map;
    /** The maximum depth of search we're willing to accept before giving up */
    private final int maxSearchDistance;

    /** The complete set of nodes across the map */
    private final Node[][] nodes;
    /** True if we allow diaganol movement */
    private final boolean allowDiagMovement;
    /** The heuristic we're applying to determine which nodes to search first */
    private final AStarHeuristic heuristic;

    /**
     * Create a path finder with the default heuristic - closest to target.
     * 
     * @param map
     *            The map to be searched
     * @param maxSearchDistance
     *            The maximum depth we'll search before giving up
     * @param allowDiagMovement
     *            True if the search should try diaganol movement
     */
    public PathFinder(Map map, int maxSearchDistance, boolean allowDiagMovement) {
        this(map, maxSearchDistance, allowDiagMovement, new AStarHeuristic());
    }

    /**
     * Create a path finder
     * 
     * @param heuristic
     *            The heuristic used to determine the search order of the map
     * @param map
     *            The map to be searched
     * @param maxSearchDistance
     *            The maximum depth we'll search before giving up
     * @param allowDiagMovement
     *            True if the search should try diaganol movement
     */
    public PathFinder(Map map, int maxSearchDistance, boolean allowDiagMovement,
            AStarHeuristic heuristic) {
        this.heuristic = heuristic;
        this.map = map;
        this.maxSearchDistance = maxSearchDistance;
        this.allowDiagMovement = allowDiagMovement;

        closed = new ArrayList<Node>();
        nodes = new Node[map.getWidth()][map.getHeight()];

        for (int x = 0; x < map.getWidth(); x++) {
            for (int y = 0; y < map.getHeight(); y++) {
                nodes[x][y] = new Node(x, y);
            }
        }
    }

    /**
     * Find a path from the starting location provided (sx,sy) to the target
     * location (tx,ty) avoiding blockages and attempting to honour costs
     * provided by the tile map.
     * 
     * @param mover
     *            The entity that will be moving along the path. This provides a
     *            place to pass context information about the game entity doing
     *            the moving, e.g. can it fly? can it swim etc.
     * 
     * @param sx
     *            The x coordinate of the start location
     * @param sy
     *            The y coordinate of the start location
     * @param tx
     *            The x coordinate of the target location
     * @param ty
     *            Teh y coordinate of the target location
     * @return The path found from start to end, or null if no path can be
     *         found.
     */

    public Path findPath(Actor mover, int sx, int sy, int tx, int ty) {
        // easy first check, if the destination is blocked, we can't get there

        if (!map.canMove(mover, tx, ty)) {
            return null;
        }

        // initial state for A*. The closed group is empty. Only the starting

        // tile is in the open list and it'e're already there
        nodes[sx][sy].cost = 0;
        nodes[sx][sy].depth = 0;
        closed.clear();
        open.clear();
        open.add(nodes[sx][sy]);

        nodes[tx][ty].parent = null;

        // while we haven'n't exceeded our max search depth
        int maxDepth = 0;
        while ((maxDepth < maxSearchDistance) && (open.size() != 0)) {
            // pull out the first node in our open list, this is determined to

            // be the most likely to be the next step based on our heuristic

            final Node current = getFirstInOpen();
            if (current == nodes[tx][ty]) {
                break;
            }

            removeFromOpen(current);
            addToClosed(current);

            // search through all the neighbours of the current node evaluating

            // them as next steps

            for (int x = -1; x < 2; x++) {
                for (int y = -1; y < 2; y++) {
                    // not a neighbour, its the current tile

                    if ((x == 0) && (y == 0)) {
                        continue;
                    }

                    // if we're not allowing diaganol movement then only

                    // one of x or y can be set

                    if (!allowDiagMovement) {
                        if ((x != 0) && (y != 0)) {
                            continue;
                        }
                    }

                    // determine the location of the neighbour and evaluate it

                    final int xp = x + current.x;
                    final int yp = y + current.y;

                    if (isValidLocation(mover, sx, sy, xp, yp)) {
                        // the cost to get to this node is cost the current plus
                        // the movement

                        // cost to reach this node. Note that the heursitic
                        // value is only used

                        // in the sorted open list

                        final float nextStepCost = current.cost
                                + getMovementCost(mover, current.x, current.y, xp, yp);
                        final Node neighbour = nodes[xp][yp];
                        // map.pathFinderVisited(xp, yp);

                        // if the new cost we've determined for this node is
                        // lower than

                        // it has been previously makes sure the node hasn'e've
                        // determined that there might have been a better path
                        // to get to

                        // this node so it needs to be re-evaluated

                        if (nextStepCost < neighbour.cost) {
                            if (inOpenList(neighbour)) {
                                removeFromOpen(neighbour);
                            }
                            if (inClosedList(neighbour)) {
                                removeFromClosed(neighbour);
                            }
                        }

                        // if the node hasn't already been processed and
                        // discarded then

                        // reset it's cost to our current cost and add it as a
                        // next possible

                        // step (i.e. to the open list)

                        if (!inOpenList(neighbour) && !(inClosedList(neighbour))) {
                            neighbour.cost = nextStepCost;
                            neighbour.nodeHeuristic = getHeuristicCost(mover, xp, yp, tx, ty);
                            maxDepth = Math.max(maxDepth, neighbour.setParent(current));
                            addToOpen(neighbour);
                        }
                    }
                }
            }
        }

        // since we'e've run out of search
        // there was no path. Just return null

        if (nodes[tx][ty].parent == null) {
            return null;
        }

        // At this point we've definitely found a path so we can uses the parent

        // references of the nodes to find out way from the target location back

        // to the start recording the nodes on the way.

        final Path path = new Path();
        Node target = nodes[tx][ty];
        while (target != nodes[sx][sy]) {
            path.prependStep(target.x, target.y);
            target = target.parent;
        }
        path.prependStep(sx, sy);

        // thats it, we have our path

        return path;
    }

    /**
     * Get the first element from the open list. This is the next one to be
     * searched.
     * 
     * @return The first element in the open list
     */
    protected Node getFirstInOpen() {
        return (Node) open.first();
    }

    /**
     * Add a node to the open list
     * 
     * @param node
     *            The node to be added to the open list
     */
    protected void addToOpen(Node node) {
        open.add(node);
    }

    /**
     * Check if a node is in the open list
     * 
     * @param node
     *            The node to check for
     * @return True if the node given is in the open list
     */
    protected boolean inOpenList(Node node) {
        return open.contains(node);
    }

    /**
     * Remove a node from the open list
     * 
     * @param node
     *            The node to remove from the open list
     */
    protected void removeFromOpen(Node node) {
        open.remove(node);
    }

    /**
     * Add a node to the closed list
     * 
     * @param node
     *            The node to add to the closed list
     */
    protected void addToClosed(Node node) {
        closed.add(node);
    }

    /**
     * Check if the node supplied is in the closed list
     * 
     * @param node
     *            The node to search for
     * @return True if the node specified is in the closed list
     */
    protected boolean inClosedList(Node node) {
        return closed.contains(node);
    }

    /**
     * Remove a node from the closed list
     * 
     * @param node
     *            The node to remove from the closed list
     */
    protected void removeFromClosed(Node node) {
        closed.remove(node);
    }

    /**
     * Check if a given location is valid for the supplied mover
     * 
     * @param mover
     *            The mover that would hold a given location
     * @param sx
     *            The starting x coordinate
     * @param sy
     *            The starting y coordinate
     * @param x
     *            The x coordinate of the location to check
     * @param y
     *            The y coordinate of the location to check
     * @return True if the location is valid for the given mover
     */
    protected boolean isValidLocation(Actor mover, int sx, int sy, int x, int y) {
        boolean invalid = (x < 0) || (y < 0) || (x >= map.getWidth()) || (y >= map.getHeight());

        if ((!invalid) && ((sx != x) || (sy != y))) {
            invalid = map.canMove(mover, x, y) == false;
        }

        return !invalid;
    }

    /**
     * Get the cost to move through a given location
     * 
     * @param mover
     *            The entity that is being moved
     * @param sx
     *            The x coordinate of the tile whose cost is being determined
     * @param sy
     *            The y coordiante of the tile whose cost is being determined
     * @param tx
     *            The x coordinate of the target location
     * @param ty
     *            The y coordinate of the target location
     * @return The cost of movement through the given tile
     */
    public float getMovementCost(Actor mover, int sx, int sy, int tx, int ty) {
        return map.getCost(mover, sx, sy, tx, ty);
    }

    /**
     * Get the heuristic cost for the given location. This determines in which
     * order the locations are processed.
     * 
     * @param mover
     *            The entity that is being moved
     * @param x
     *            The x coordinate of the tile whose cost is being determined
     * @param y
     *            The y coordiante of the tile whose cost is being determined
     * @param tx
     *            The x coordinate of the target location
     * @param ty
     *            The y coordinate of the target location
     * @return The heuristic cost assigned to the tile
     */
    public float getHeuristicCost(Actor mover, int x, int y, int tx, int ty) {
        return heuristic.getCost(map, mover, x, y, tx, ty);
    }

    /**
     * A simple sorted list
     * 
     * @author kevin
     */
    private class SortedNodeList {
        /** The list of elements */
        private final ArrayList<Node> list = new ArrayList<Node>();

        /**
         * Retrieve the first element from the list
         * 
         * @return The first element from the list
         */
        public Object first() {
            return list.get(0);
        }

        /**
         * Empty the list
         */
        public void clear() {
            list.clear();
        }

        /**
         * Add an element to the list - causes sorting
         * 
         * @param o
         *            The element to add
         */
        public void add(Node o) {
            list.add(o);
            Collections.sort(list);
        }

        /**
         * Remove an element from the list
         * 
         * @param o
         *            The element to remove
         */
        public void remove(Object o) {
            list.remove(o);
        }

        /**
         * Get the number of elements in the list
         * 
         * @return The number of element in the list
         */
        public int size() {
            return list.size();
        }

        /**
         * Check if an element is in the list
         * 
         * @param o
         *            The element to search for
         * @return True if the element is in the list
         */
        public boolean contains(Object o) {
            return list.contains(o);
        }
    }

    /**
     * A single node in the search graph
     */
    private class Node implements Comparable<Object> {
        /** The x coordinate of the node */
        private final int x;
        /** The y coordinate of the node */
        private final int y;
        /** The path cost for this node */
        private float cost;
        /** The parent of this node, how we reached it in the search */
        private Node parent;
        /** The heuristic cost of this node */
        private float nodeHeuristic;
        /** The search depth of this node */
        private int depth;

        /**
         * Create a new node
         * 
         * @param x
         *            The x coordinate of the node
         * @param y
         *            The y coordinate of the node
         */
        public Node(int x, int y) {
            this.x = x;
            this.y = y;
        }

        /**
         * Set the parent of this node
         * 
         * @param parent
         *            The parent node which lead us to this node
         * @return The depth we have no reached in searching
         */
        public int setParent(Node parent) {
            depth = parent.depth + 1;
            this.parent = parent;

            return depth;
        }

        /**
         * @see Comparable#compareTo(Object)
         */
        @Override
        public int compareTo(Object other) {
            final Node o = (Node) other;

            final float f = nodeHeuristic + cost;
            final float of = o.nodeHeuristic + o.cost;

            if (f < of) {
                return -1;
            } else if (f > of) {
                return 1;
            } else {
                return 0;
            }
        }
    }
}

state/State.java


otherpackage pacman.state;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import pacman.game.Game;

/**
 * A State is a mode of the program where input and functionality are radically
 * different from others portions of the program. We can then effectively
 * separate different logical facilities into their own State subclasses
 * 
 * @author Ramsey Kant
 */
public abstract class State implements KeyListener {
    // Game States
    public static final int STATE_MENU = 1;
    public static final int STATE_SCOREBOARD = 2;
    public static final int STATE_GAME = 4;
    public static final int STATE_DEAD = 8;
    // public static final int STATE_GAMEOVER = 16;
    public static final int STATE_EDITOR = 32;
    public static final int STATE_EXITING = 64;

    protected Game game;

    /**
     * Class Constructor
     * 
     * @param g
     *            Reference to the game
     */
    public State(Game g) {
        game = g;
        reset();
    }

    /**
     * Return the reference to the game object
     * 
     * @return Reference to the game object
     */
    public Game getGame() {
        return game;
    }

    /**
     * Start or reset the state
     * 
     * Can be called either by the Supervisor or the state itself
     */
    public abstract void reset();

    /**
     * Primary logic function called in the mainThreadLoop
     * 
     * Called only by the Supervisor
     */
    public abstract void logic();

    /**
     * Signals the state to terminate. Any final updates should be performed
     * here THIS IS ONLY CALLED INSIDE CHANGESTATE() - DO NOT CALL THIS ANYWHERE
     * ELSE
     */
    public abstract void end();

    /*
     * Human Input default
     */

    @Override
    public void keyReleased(KeyEvent e) {
        // do nothing
    }

    @Override
    public void keyTyped(KeyEvent e) {
        // Esc
        switch (e.getKeyChar()) {
            case 27:
                game.requestChangeState(STATE_EXITING);
                break;
            default:
                break;
        }
    }
}

state/StateEditor.java


otherpackage pacman.state;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;

import pacman.actors.Actor;
import pacman.actors.Ghost;
import pacman.actors.Player;
import pacman.editor.EditorFrame;
import pacman.editor.EditorMarker;
import pacman.game.Game;
import pacman.game.GameObject;
import pacman.game.Item;
import pacman.map.Map;

/**
 * The StateEditor is a mode of the program that allows the user to create and
 * modify map files that can be played in StateGame. StateEditor is a subclass
 * of State
 * 
 * @author Ramsey Kant
 */
public class StateEditor extends State {

    // Logic object references
    private final EditorFrame editorFrame;
    private EditorMarker marker;
    private boolean gameView;
    private Map map;

    // Placement variables
    private int markerObjectType;
    private byte markerWallType;
    private String markerGhostType;
    private boolean markerGhostTrapped;
    private int markerTeleportX;
    private int markerTeleportY;

    // Map vars. Store them as class member vars to eliminate function call
    // overhead for getHeight/getWidth
    private int mapWidth;
    private int mapHeight;

    public StateEditor(Game g) {
        super(g);

        // If true, remove all editor helpers like grid lines
        gameView = false;

        // Create the editor toolpane
        game.getFrame().setSize(1024, game.RES_Y);
        editorFrame = new EditorFrame(this);
        editorFrame.setVisible(true);

        // Defaults
        markerObjectType = GameObject.OBJECT_WALL;
        markerWallType = GameObject.WALL_VERTICAL;
        markerGhostType = "Blinky";
        markerGhostTrapped = false;
        markerTeleportX = 13;
        markerTeleportY = 17;
    }

    // Getters and Setters

    /**
     * Set the type of object to be placed by the marker Called by the
     * EditorFrame (Dot, Powerup, Teleport buttons)
     * 
     * @param t
     *            Type of object (from GameObject statics)
     */
    public void setMarkerObjectType(int t) {
        markerObjectType = t;
    }

    /**
     * Set the type of wall to be placed by the marker Called by the EditorFrame
     * Wall button
     * 
     * @param t
     *            Type of wall (from GameObject statics)
     */
    public void setMarkerWallType(byte t) {
        markerWallType = t;
    }

    /**
     * Set the type of wall to be placed by the marker Called by the EditorFrame
     * Add Ghost button
     * 
     * @param t
     *            Type of wall (from GameObject statics)
     */
    public void setMarkerGhostType(String t) {
        markerGhostType = t;
    }

    /**
     * Toggle the trapped status of the next ghost to be added
     * 
     * @param t
     *            True if trapped in the spawn-jail
     */
    public void setMarkerGhostTrapped(boolean t) {
        markerGhostTrapped = t;
    }

    /**
     * Get the current trapped status
     * 
     * @return True if new ghosts will be created as trapped
     */
    public boolean getMarkerGhostTrapped() {
        return markerGhostTrapped;
    }

    /**
     * Set the teleport destination, used to aid a teleport drop Called in
     * EditorFrame by the Teleport add button
     * 
     * @param x
     *            destination coordinate X of the next teleport
     * @param y
     *            destination coordinate Y of the next teleport
     */
    public void setMarkerTeleport(int x, int y) {
        markerTeleportX = x;
        markerTeleportY = y;
    }

    /**
     * Reset the StateEditor objects like the Marker
     * 
     * @see State#reset()
     */
    @Override
    public void reset() {
        // Force previous references out of scope
        marker = null;
        map = null;

        markerObjectType = GameObject.OBJECT_DOT;
    }

    /**
     * Setup and render a new blank map
     * 
     * @param width
     *            The width of the map to be created
     * @param height
     *            The height of the map to be created
     */
    public void newMap(int width, int height) {
        // Setup the game map
        game.getGraphicsContext().setBackground(Color.BLACK);
        mapWidth = width;
        mapHeight = height;
        map = new Map(28, 31, 32);

        // Create the marker (but don't put it "in" the map)
        marker = new EditorMarker(Color.GREEN, map, 0, 0);
    }

    /**
     * Save the map
     * 
     * @param filename
     */
    public void saveMap(String filename) {
        map.write(System.getProperty("user.dir") + "\\" + filename);
    }

    /**
     * Setup and render a map loaded from the file system
     * 
     * @param filename
     */
    public void loadMap(String filename) {
        // Setup the game map
        game.getGraphicsContext().setBackground(Color.BLACK);
        map = new Map(System.getProperty("user.dir") + "\\" + filename, 32);
        mapWidth = map.getWidth();
        mapHeight = map.getHeight();

        // Create the marker (but don't put it "in" the map)
        marker = new EditorMarker(Color.GREEN, map, 0, 0);
    }

    /**
     * Logic of the editor processed here: Rendering, input, and object
     * placement. Called in the mainThreadLoop
     * 
     * @see State#logic()
     */
    @Override
    public void logic() {
        if (map == null) {
            return;
        }

        final Graphics2D g = game.getGraphicsContext();

        // Offset the buffer so object's arent clipped by the window borders
        g.translate(10, 30);

        // Now run render logic
        // Paint boundaries and items
        Item item = null;
        for (int x = 0; x < mapWidth; x++) {
            for (int y = 0; y < mapHeight; y++) {
                // Get and paint the static object here
                final byte sType = map.getCollidable(x, y);

                // Switch based on wall type and paint
                g.setColor(Color.BLUE);
                switch (sType) {
                    case 0:
                        // Nothing
                        break;
                    case GameObject.WALL_VERTICAL:
                        // Vertical wall, no edges
                        g.fillRoundRect(x * map.CELL_SIZE + 10, y * map.CELL_SIZE, 12,
                                map.CELL_SIZE, 0, 0); // 2x+12 = map.CELL_SIZE.
                                                      // x = 10
                        break;
                    case GameObject.WALL_HORIZONTAL:
                        // Horizontal wall, no edges
                        g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + 10, map.CELL_SIZE,
                                12, 0, 0);
                        break;
                    case GameObject.WALL_TOPLEFT:
                        // g.fillArc(x*map.CELL_SIZE+10, y*map.CELL_SIZE,
                        // map.CELL_SIZE, map.CELL_SIZE, 90, 90);
                        g.fillRoundRect(x * map.CELL_SIZE + (map.CELL_SIZE / 2), y * map.CELL_SIZE
                                + 10, map.CELL_SIZE / 2, 12, 0, 0);
                        g.fillRoundRect(x * map.CELL_SIZE + 10, y * map.CELL_SIZE
                                + (map.CELL_SIZE / 2), 12, map.CELL_SIZE / 2, 0, 0);
                        break;
                    case GameObject.WALL_TOPRIGHT:
                        g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + 10,
                                map.CELL_SIZE / 2, 12, 0, 0);
                        g.fillRoundRect(x * map.CELL_SIZE + 10, y * map.CELL_SIZE
                                + (map.CELL_SIZE / 2), 12, map.CELL_SIZE / 2, 0, 0);
                        break;
                    case GameObject.WALL_BOTTOMLEFT:
                        g.fillRoundRect(x * map.CELL_SIZE + (map.CELL_SIZE / 2), y * map.CELL_SIZE
                                + 10, map.CELL_SIZE / 2, 12, 0, 0); // hori
                        g.fillRoundRect(x * map.CELL_SIZE + 10, y * map.CELL_SIZE, 12,
                                map.CELL_SIZE / 2, 0, 0); // vert
                        break;
                    case GameObject.WALL_BOTTOMRIGHT:
                        g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + 10,
                                map.CELL_SIZE / 2, 12, 0, 0); // hori
                        g.fillRoundRect(x * map.CELL_SIZE + 10, y * map.CELL_SIZE, 12,
                                map.CELL_SIZE / 2, 0, 0); // vert
                        break;
                    case GameObject.WALL_GHOSTBARRIER:
                        g.setColor(Color.PINK);
                        g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + 10, map.CELL_SIZE,
                                6, 0, 0);
                        break;
                    default:
                        break;
                }

                // Paint any of the items here
                item = map.getItem(x, y);
                if (item != null) {
                    item.paint(g);
                }
            }
        }

        // Paint actors ontop
        final int nActors = map.getNumActors();
        for (int i = 0; i < nActors; i++) {
            final Actor a = map.getActor(i);
            if (a != null) {
                a.paint(g);
            }
        }

        // Paint the marker
        marker.paint(g);

        // Paint gridline overlay if in editor view
        if (!gameView) {
            g.setColor(Color.RED);
            for (int i = 0; i < mapWidth; i++) {
                g.drawLine(i * map.CELL_SIZE, 0, i * map.CELL_SIZE, mapHeight * map.CELL_SIZE);
            }
            for (int i = 0; i < mapHeight; i++) {
                g.drawLine(0, i * map.CELL_SIZE, mapWidth * map.CELL_SIZE, i * map.CELL_SIZE);
            }

            // Player X,Y coordinates bottom right
            g.drawString("X: " + marker.getX() + ", Y: " + marker.getY(), 900, 700);
        }
    }

    /**
     * Termination of the StateEdtior. Set references stored by the StateEditor
     * as null
     * 
     * @see State#end()
     */
    @Override
    public void end() {
        // Cleanup
        marker = null;
        map = null;
    }

    /**
     * Input processing for the Editor
     * 
     * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
     */
    @Override
    public void keyPressed(KeyEvent e) {
        switch (e.getKeyCode()) {
            case KeyEvent.VK_UP:
                marker.changeTile(0, -1);
                break;
            case KeyEvent.VK_RIGHT:
                marker.changeTile(1, 0);
                break;
            case KeyEvent.VK_DOWN:
                marker.changeTile(0, +1);
                break;
            case KeyEvent.VK_LEFT:
                marker.changeTile(-1, 0);
                break;
            case KeyEvent.VK_ENTER:
                if (marker == null) {
                    return;
                }

                // If not empty, bail
                if (!map.isEmpty(marker.getX(), marker.getY())) {
                    return;
                }

                switch (markerObjectType) {
                    case GameObject.OBJECT_WALL:
                        map.addCollidable(marker.getX(), marker.getY(), markerWallType);
                        break;
                    case GameObject.OBJECT_DOT:
                        map.addItem(new Item(GameObject.OBJECT_DOT, Color.WHITE, map,
                                marker.getX(), marker.getY()));
                        break;
                    case GameObject.OBJECT_POWERUP:
                        map.addItem(new Item(GameObject.OBJECT_POWERUP, Color.WHITE, map, marker
                                .getX(), marker.getY()));
                        break;
                    case GameObject.OBJECT_GHOST:
                        if (markerGhostType.equals("Blinky")) {
                            map.addActor(new Ghost(Color.RED, map, marker.getX(), marker.getY(),
                                    markerGhostTrapped));
                        } else if (markerGhostType.equals("Pinky")) {
                            map.addActor(new Ghost(Color.PINK, map, marker.getX(), marker.getY(),
                                    markerGhostTrapped));
                        } else if (markerGhostType.equals("Inky")) {
                            map.addActor(new Ghost(Color.CYAN, map, marker.getX(), marker.getY(),
                                    markerGhostTrapped));
                        } else {
                            map.addActor(new Ghost(Color.ORANGE, map, marker.getX(), marker.getY(),
                                    markerGhostTrapped));
                        }
                        break;
                    case GameObject.OBJECT_PLAYER:
                        // If there is already a player in the actors list,
                        // remove it
                        for (int i = 0; i < map.getNumActors(); i++) {
                            if (map.getActor(i).getType() == GameObject.OBJECT_PLAYER) {
                                map.removeActor(i);
                                i--;
                            }
                        }

                        // Add the new player
                        map.addActor(new Player(map, marker.getX(), marker.getY()));
                        break;
                    case GameObject.OBJECT_TELEPORT:
                        final Item tel = new Item(GameObject.OBJECT_TELEPORT, Color.LIGHT_GRAY,
                                map, marker.getX(), marker.getY());
                        tel.setTeleport(markerTeleportX, markerTeleportY);
                        map.addItem(tel);
                        break;

                    default:
                        break;
                }
                break;
            case KeyEvent.VK_DELETE:
                // Delete a placed object. Will reduce excessive memory
                // consumption if the user cant just replace a tile with a new
                // object

                // If empty, bail
                if (map.isEmpty(marker.getX(), marker.getY())) {
                    return;
                }

                // Remove anything (collidable, actor, or item) at (x,y)
                map.removeAnyAt(marker.getX(), marker.getY());
                break;
            case KeyEvent.VK_V:
                gameView = !gameView;
                break;
            case KeyEvent.VK_0:
                // editorFrame.setEnabled(false);
                // game.changeState(STATE_MENU);
                break;
            default:
                break;
        }
    }

}

state/StateGame.java


otherpackage pacman.state;


import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;

import pacman.actors.Actor;
import pacman.actors.Ghost;
import pacman.actors.Player;
import pacman.ai.AIManager;
import pacman.game.Game;
import pacman.game.GameObject;
import pacman.game.Item;
import pacman.map.Map;
import pacman.util.Direction;

/**
 * StateGame is a mode of the program where the user can play the game of Pacman
 * various maps StateGame is a subclass of State
 * 
 * @author Ramsey Kant
 */
public class StateGame extends State {

    // Logic object references
    private Player player;
    private Map map;
    private AIManager ai;

    // Game vars
    private String mapName;
    private int currentLevel;
    private int sessionScore; // Overall score for the game session. The player
                              // object score is only the score for that life /
                              // level
    private int livesRemaining;
    private boolean gamePaused;
    private long pauseTime;

    // Map vars. Store them as class member vars to eliminate function call
    // overhead for getHeight/getWidth
    private int mapWidth;
    private int mapHeight;

    /**
     * StateGame Constructor
     * 
     * @param g
     *            Reference to the game supervisor
     */
    public StateGame(Game g) {
        super(g);
    }

    /**
     * Get the current session score. A session score is the total score of the
     * entire 'game' contained by the limited number of lives This is compounded
     * at game over and in win()
     * 
     * @see StateGame#win()
     * @see StateGame#lose()
     */
    public int getSessionScore() {
        return sessionScore;
    }

    // Public Methods

    /**
     * Reset the state of the game entirely (level 1)
     * 
     * @see State#reset()
     */
    @Override
    public void reset() {
        // Set game vars
        mapName = game.getStartMap();
        currentLevel = 0;
        sessionScore = 0;
        livesRemaining = 99;
        pauseTime = 0;

        // Respawn (start level 1)
        respawn(true);
    }

    /**
     * Respawns the player after a death or win. Similar to reset() except
     * running vars are saved
     * 
     * @param nextLevel
     *            Moves to the next level and corresponding map
     */
    public void respawn(boolean nextLevel) {
        gamePaused = true;
        pauseTime = System.currentTimeMillis() + 3000;

        // If we're jumping to the next level, reset everything
        if (nextLevel) {
            currentLevel++;

            // Force previous references out of scope
            player = null;
            map = null;
            ai = null;

            // Setup the game map
            game.getGraphicsContext().setBackground(Color.BLACK);
            map = new Map(mapName, 0.75);
            mapWidth = map.getWidth();
            mapHeight = map.getHeight();

            // Spawn the player
            player = map.getPlayer();

            // Setup AI
            ai = new AIManager(map, player, game.isDebugEnabled());

            // Slighly increase the game speed

        } else { // Player died, reset the map
            // Move all actors back to their spawn positions
            final int nActors = map.getNumActors();
            for (int i = 0; i < nActors; i++) {
                final Actor a = map.getActor(i);
                if (a != null) {
                    a.move(a.getSpawnX(), a.getSpawnY());
                    a.setDead(false);
                    if (a.getType() == GameObject.OBJECT_GHOST) {
                        ((Ghost) a).updatePath(null);
                    }
                }
            }
        }
    }

    /**
     * Main game logic for rendering and processing. Called by mainThreadLoop
     * 
     * @see Game#mainThreadLoop()
     * @see State#logic()
     */
    @Override
    public void logic() {
        if (map == null) {
            return;
        }

        final Graphics2D g = game.getGraphicsContext();

        // Offset the buffer so object's arent clipped by the window borders
        g.translate(10, 30);

        // Paint right UI with lives remaining, score, highscore etc
        g.setColor(Color.WHITE);
        g.setFont(new Font("Comic Sans MS", Font.BOLD, 24));
        g.drawString("PACMAN by Ramsey Kant", 680, 50);
        g.drawString("Score: " + player.getScore(), 750, 100);
        g.drawString("Total: " + sessionScore, 750, 150);
        g.drawString("Lives: " + livesRemaining, 750, 200);
        g.drawString("Level: " + currentLevel, 750, 250);

        // Execute game logic for all entites on the map
        if (!gamePaused) {
            ai.process();
            player.act();
        }

        // Check for player death. End the round if the player is dead
        if (player.isDead()) {
            lose();
            return;
        }

        // Check for a win (all dots collected)
        if (map.getDotsRemaining() <= 0) {
            win();
            return;
        }

        // Now run render logic
        // Paint boundaries and items
        Item item = null;
        for (int x = 0; x < mapWidth; x++) {
            for (int y = 0; y < mapHeight; y++) {
                // Get and paint the static object here
                final byte sType = map.getCollidable(x, y);

                // Switch based on wall type and paint
                g.setColor(Color.BLUE);
                switch (sType) {
                    case 0:
                        // Nothing
                        break;
                    case GameObject.WALL_VERTICAL:
                        // Vertical wall, no edges
                        g.fillRoundRect(x * map.CELL_SIZE + map.WALL_OVERLAP,
                                y * map.CELL_SIZE,
                                map.WALL_THICKNESS,
                                map.CELL_SIZE, 0, 0); // 2x+12 = map.CELL_SIZE.
                                                      // x = 10
                        break;
                    case GameObject.WALL_HORIZONTAL:
                        // Horizontal wall, no edges
                        g.fillRoundRect(x * map.CELL_SIZE,
                                y * map.CELL_SIZE + map.WALL_OVERLAP,
                                map.CELL_SIZE,
                                map.WALL_THICKNESS, 0, 0);
                        break;
                    case GameObject.WALL_TOPLEFT:
                        g.fillRoundRect(x * map.CELL_SIZE + (map.CELL_SIZE / 2),
                                y * map.CELL_SIZE + map.WALL_OVERLAP,
                                map.CELL_SIZE / 2,
                                map.WALL_THICKNESS, 0, 0);
                        g.fillRoundRect(x * map.CELL_SIZE + map.WALL_OVERLAP,
                                y * map.CELL_SIZE + (map.CELL_SIZE / 2),
                                map.WALL_THICKNESS,
                                map.CELL_SIZE / 2, 0, 0);
                        break;
                    case GameObject.WALL_TOPRIGHT:
                        g.fillRoundRect(x * map.CELL_SIZE,
                                y * map.CELL_SIZE + map.WALL_OVERLAP,
                                map.CELL_SIZE / 2,
                                map.WALL_THICKNESS, 0, 0);
                        g.fillRoundRect(x * map.CELL_SIZE + map.WALL_OVERLAP,
                                y * map.CELL_SIZE
                                + (map.CELL_SIZE / 2),
                                map.WALL_THICKNESS, map.CELL_SIZE / 2, 0, 0);
                        break;
                    case GameObject.WALL_BOTTOMLEFT:
                        g.fillRoundRect(x * map.CELL_SIZE + (map.CELL_SIZE / 2), y * map.CELL_SIZE
                                + map.WALL_OVERLAP, map.CELL_SIZE / 2, map.WALL_THICKNESS, 0, 0); // hori
                        g.fillRoundRect(x * map.CELL_SIZE + map.WALL_OVERLAP, y * map.CELL_SIZE, 12,
                                map.CELL_SIZE / 2, 0, 0); // vert
                        break;
                    case GameObject.WALL_BOTTOMRIGHT:
                        g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + map.WALL_OVERLAP,
                                map.CELL_SIZE / 2, map.WALL_THICKNESS, 0, 0); // hori
                        g.fillRoundRect(x * map.CELL_SIZE + map.WALL_OVERLAP, y * map.CELL_SIZE, map.WALL_THICKNESS,
                                map.CELL_SIZE / 2, 0, 0); // vert
                        break;
                    case GameObject.WALL_GHOSTBARRIER:
                        g.setColor(Color.PINK);
                        g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + map.WALL_OVERLAP, map.CELL_SIZE,
                                map.WALL_THICKNESS / 2, 0, 0);
                        break;
                    default:
                        break;
                }

                // Paint any of the items here
                item = map.getItem(x, y);
                if (item != null) {
                    item.paint(g);
                }
            }
        }

        // Paint actors ontop
        final int nActors = map.getNumActors();
        for (int i = 0; i < nActors; i++) {
            final Actor a = map.getActor(i);
            if (a != null) {
                a.paint(g);
            }
        }

        // Debug
        if (game.isDebugEnabled()) {
            g.setColor(Color.RED);
            g.drawString("DEBUG ON", 750, 650);
            /*
             * // Paint gridline overlay for(int i = 0; i < mapWidth; i++)
             * g.drawLine(i*map.CELL_SIZE, 0, i*map.CELL_SIZE,
             * mapHeight*map.CELL_SIZE); for(int i = 0; i < mapHeight; i++)
             * g.drawLine(0, i*map.CELL_SIZE, mapWidth*map.CELL_SIZE,
             * i*map.CELL_SIZE);
             */

            // Player X,Y coordinates bottom right
            g.drawString("positionX: " + player.getX(), 750, 675);
            g.drawString("positionY: " + player.getY(), 750, 700);
        }

        // Check for game pause and print pause status
        if (gamePaused) {
            g.setColor(Color.RED);
            g.setFont(new Font("Comic Sans MS", Font.BOLD, 24));
            g.drawString("PAUSED", 750, 500);
            if (pauseTime > System.currentTimeMillis()) {
                g.drawString(
                        "Pause ends in..." + ((pauseTime - System.currentTimeMillis()) / 1000),
                        750, 550);
            }
            if (pauseTime != 0 && System.currentTimeMillis() > pauseTime) {
                pauseTime = 0;
                gamePaused = false;
            }
            return;
        }
    }

    /**
     * Player has died or the Supervisor has decided to change the state
     * abruptly
     * 
     * @see State#end()
     */
    @Override
    public void end() {
        // Cleanup
        player = null;
        map = null;
    }

    /**
     * Player has won, move to the next level Called by logic()
     * 
     * @see StateGame#logic()
     */
    public void win() {
        sessionScore += player.getScore();

        respawn(true);
    }

    /**
     * Player has died, reset() if lives remain. Otherwise, request a state
     * change thereby end()ing this state Called by logic()
     * 
     * @see StateGame#logic()
     */
    public void lose() {
        livesRemaining--;

        if (livesRemaining > 0) {
            respawn(false);
        } else {
            if (currentLevel == 1) {
                sessionScore = player.getScore(); // win() never called, so
                                                  // score is the 1st level
                                                  // score
            }
            game.requestChangeState(State.STATE_SCOREBOARD);
        }
    }

    /**
     * Start automove in certain direction
     * 
     * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
     */
    @Override
    public void keyPressed(KeyEvent e) {
        if (player == null) {
            return;
        }

        switch (e.getKeyCode()) {
            case KeyEvent.VK_UP:
                player.setMoveDirection(Direction.up);
                break;
            case KeyEvent.VK_RIGHT:
                player.setMoveDirection(Direction.right);
                break;
            case KeyEvent.VK_DOWN:
                player.setMoveDirection(Direction.down);
                break;
            case KeyEvent.VK_LEFT:
                player.setMoveDirection(Direction.left);
                break;
            case KeyEvent.VK_SPACE:
                player.setMoveDirection(Direction.stop);
                break;
            case KeyEvent.VK_P:
                // Don't interupt system pauses
                if (pauseTime < System.currentTimeMillis()) {
                    gamePaused = !gamePaused;
                }
                break;
            case KeyEvent.VK_V:
                game.toggleDebug();
                // AI debug
                ai.setDebugEnabled(game.isDebugEnabled());
                break;
            case KeyEvent.VK_0:
                // game.changeState(STATE_MENU);
                break;
            default:
                break;
        }
    }
}

state/StateMenu.java


otherpackage pacman.state;


import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FilenameFilter;

import pacman.game.Game;

/**
 * StateMenu is a graphical representation that allows users to switch into
 * other States of the program like the Game and editor
 * 
 * @author Ramsey Kant
 */
public class StateMenu extends State {

    // Private instance
    private int cursorX;
    private int cursorY;
    private byte currentOption;
    private byte currentMapOption; // Corresponds to the index in mapList
    private String[] mapList;

    public StateMenu(Game g) {
        super(g);
    }

    @Override
    public void reset() {
        // Set cursor & menu position
        cursorX = 380;
        cursorY = 310;
        currentOption = 0;
        currentMapOption = 0;

        // Load the map list
        final File dir = new File(System.getProperty("user.dir"));

        // It is also possible to filter the list of returned files.
        // This example does not return any files that start with `.'.
        final FilenameFilter filter = new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(".map");
            }
        };

        // Apply the filter
        mapList = dir.list(filter);

        if (mapList == null) {
            System.out.println("No maps exist!");
            game.requestChangeState(STATE_EXITING);
            return;
        }
    }

    /**
     * Cleanup Menu objects
     * 
     * @see State#end()
     */
    @Override
    public void end() {
        // do nothing
    }

    /**
     * Logic processing for the Menu. Rendering, Input, screen pointer
     * manipulation
     * 
     * @see State#logic()
     */
    @Override
    public void logic() {
        final Graphics2D g = game.getGraphicsContext();

        // Draw title
        g.setColor(Color.YELLOW);
        g.setFont(new Font("Comic Sans MS", Font.BOLD, 50));
        g.fillArc(56, 92, 100, 100, 35, 270); // First pacman
        g.drawString("PACMAN", 350, 180);
        g.fillArc(780, 92, 100, 100, 35, 270);

        // Draw menu options
        g.setFont(new Font("Comic Sans MS", Font.BOLD, 24));
        g.drawString("Play Game", 380, 300);
        //g.drawString("Map Editor", 525, 340);
        g.drawString("Scoreboard", 380, 340);
        g.drawString("Exit", 380, 380);
        if (mapList.length > 0) {
            g.drawString("Current Map: " + mapList[currentMapOption], 380, 600);
        } else {
            g.drawString(
                    "No maps detected. Have you placed the maps file in the same directory as the program?",
                    100, 600);
        }

        // Draw underline cursor
        g.setColor(Color.RED);
        g.fillRect(cursorX, cursorY, 150, 5);
    }

    @Override
    public void keyPressed(KeyEvent e) {
        switch (e.getKeyCode()) {
            case KeyEvent.VK_RIGHT:
                if (currentMapOption >= 0 && currentMapOption < (mapList.length - 1)) {
                    currentMapOption++;
                }
                break;
            case KeyEvent.VK_LEFT:
                if (currentMapOption > 0 && currentMapOption <= (mapList.length - 1)) {
                    currentMapOption--;
                }
                break;
            case KeyEvent.VK_DOWN:
                if (currentOption >= 0 && currentOption < 2) {
                    currentOption++;
                    cursorY += 38;
                }
                break;
            case KeyEvent.VK_UP:
                if (currentOption > 0 && currentOption <= 2) {
                    currentOption--;
                    cursorY -= 38;
                }
                break;
            case KeyEvent.VK_ENTER:
                // Execute the appropriate state change
                switch (currentOption) {
                    case 0:
                        // Play game
                        if (mapList.length > 0) {
                            game.setStartMap(mapList[currentMapOption]);
                            game.requestChangeState(STATE_GAME);
                        }
                        break;
                    case 1:
                        // Scoreboard
                        game.requestChangeState(STATE_SCOREBOARD);
                        break;
                    case 2:
                        // Exit
                        game.requestChangeState(STATE_EXITING);
                        break;
                    default:
                        break;
                }
                break;
            default:
                break;
        }
    }

}

state/StateScoreboard.java


otherpackage pacman.state;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import pacman.game.Game;

/**
 * StateScoreboard is a mode of the program that allows the user to view (and
 * sometimes modify) scores set in StateGame StateScoreboard is a subclass of
 * State
 * 
 * @author Ramsey Kant
 */
public class StateScoreboard extends State {

    private String[] names;
    private int[] scores;
    private int numScores;

    /**
     * Class Constructor
     * 
     * @param g
     *            Reference to the Game class
     */
    public StateScoreboard(Game g) {
        super(g);
    }

    // Public Functions

    /**
     * Setup the scoreboard by loading the current score file
     * 
     * @see State#reset()
     */
    @Override
    public void reset() {
        // Only the top 10 scores will be displayed
        names = new String[10];
        scores = new int[10];
        numScores = 0;

        // Read in the scores
        // readScores();
    }

    /**
     * Cleanup objects and write back the scores
     * 
     * @see State#end()
     */
    @Override
    public void end() {
        // saveScores();
    }

    /**
     * Render the Scoreboard and perform any updates
     */
    @Override
    public void logic() {
        final Graphics2D g = game.getGraphicsContext();

        // Draw title
        g.setColor(Color.YELLOW);
        g.setFont(new Font("Comic Sans MS", Font.BOLD, 72));
        g.fillArc(156, 92, 100, 100, 35, 270); // First pacman
        g.drawString("Scores", 450, 180);
        g.fillArc(960, 92, 100, 100, 35, 270);
        g.fillRect(150, 200, 910, 5);

        g.setFont(new Font("Comic Sans MS", Font.BOLD, 24));

        // Draw scores
        for (int i = 0; i < names.length; i++) {
            if (names[i] == null) {
                continue;
            }

            g.drawString(names[i], 150, 210);
            g.drawString(scores[i] + " ", 960, 210);
        }
    }

    /**
     * Output names and corresponding scores to the pacman.scores file
     */
    public void saveScores() {
        FileOutputStream fout;
        DataOutputStream data;

        try {
            fout = new FileOutputStream("pacman.scores");
            data = new DataOutputStream(fout);

            // Write the score file magic
            data.writeUTF("RKPACSCORES");

            // Write # of scores in the file, then the actual scores
            data.writeInt(numScores);
            for (int i = 0; i < numScores; i++) {
                if (names[i] == null) {
                    break;
                }
                data.writeUTF(names[i]);
                data.writeInt(scores[i]);
            }

            data.close();
            fout.close();
        } catch (final IOException e) {
            System.out.println("Failed to write score file: " + e.getMessage());
        }
    }

    /**
     * Populate names and scores from the pacman.scores file
     */
    public void readScores() {
        FileInputStream fin;
        DataInputStream data;

        try {
            fin = new FileInputStream("pacman.scores");
            data = new DataInputStream(fin);

            // Check for the magic
            if (!data.readUTF().equals("RKPACSCORES")) {
                System.out.println("Not a score file!");
                return;
            }

            // Read in scores
            numScores = data.readInt();
            if (numScores > 10) {
                numScores = 10;
            }
            for (int i = 0; i < numScores; i++) {
                names[i] = data.readUTF();
                scores[i] = data.readInt();
            }

            data.close();
            fin.close();
        } catch (final IOException e) {
            System.out.println("Failed to read score file: " + e.getMessage());
        }
    }

    // Input functions

    /**
     * Process input on the scoreboard (exit)
     * 
     * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
     */
    @Override
    public void keyPressed(KeyEvent e) {
        switch (e.getKeyCode()) {
            case KeyEvent.VK_0:
                game.requestChangeState(STATE_MENU);
                break;
            default:
                break;
        }
    }

}

util/RequestedDirectionBuffer.java


otherpackage pacman.util;
import java.util.LinkedList;

/**
 * This buffer is used to remember direction changes requested by the user for
 * some game turns. If a direction change was requested too early (move not
 * possible yet, because the crossing has not been reached) the direction change
 * can be retired in the next steps. The size parameter of the constructor
 * contols how long the user input will be remembered. If another direction is
 * set, all previous directions will be forgotten.
 * 
 * By default the buffer will return the neutral Direction.none direction. If
 * another direction is set, the buffer will return this direction for the next
 * n calls to getRequestedDirection(), where n = size. When a new direction is
 * set, all previously set directions are forgotten.
 * 
 */
public class RequestedDirectionBuffer {

    private final int size;
    private final LinkedList<Direction> queue;

    public RequestedDirectionBuffer(int size) {
        super();
        this.size = size;
        queue = new LinkedList<Direction>();
        fill(Direction.none);
    }

    /**
     * Get the currently requested direction.
     * 
     * @return the currently requested direction.
     */
    public Direction getRequestedDirection() {
        final Direction dir = queue.poll();
        queue.add(Direction.none);
        return dir;
    }

    /**
     * Set the requested direction
     * 
     * @param dir
     *            the requested direction
     */
    public void setRequestedDirection(Direction dir) {
        fill(dir);
    }

    /**
     * Fill the queue with a direction
     * 
     * @param dir
     *            the direction.
     */
    private void fill(Direction dir) {
        queue.clear();
        for (int i = 0; i < size; i++) {
            queue.add(dir);
        }
    }
}