pacman.game.internal.PathsCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pacman-main Show documentation
Show all versions of pacman-main Show documentation
The main code for Ms. Pac-Man Vs Ghosts
package pacman.game.internal;
import pacman.game.Constants.MOVE;
import pacman.game.Game;
import java.util.*;
/*
* Pre-computes paths for more efficient execution of the game. It is a tradeoff between loading times, execution speed,
* and file sizes. It works as follows: the paths from any junction to any other junction are computed for all directions
* one can take at the junction. Then, for each node in the graph, the paths to the nearest one or two junctions are computed.
* A path is then found by
* (a) find the nearest junction to the source (in the case of the ghosts, just follow the path): distance d_1
* (b) find the shortest distance to the one or two junctions closest to the target. This depends also on distance (d_3) from target junction to target: d_2
* (c) combine the three paths: d_1+d_2+d_3
*
* In the case of Ms Pac-Man, it works as follows:
* (a) Looking at all combinations of 2-4 junctions, choosing the shortest path that also takes into account the path to get to either of them.
*
* If one only wants the distance instead of the path, a more efficient method has been implemented that does not need to copy arrays.
*/
public class PathsCache {
public HashMap junctionIndexConverter;
public DNode[] nodes;
public Junction[] junctions;
public Game game;
public PathsCache(int mazeIndex) {
junctionIndexConverter = new HashMap();
this.game = new Game(0, mazeIndex);
Maze m = game.getCurrentMaze();
int[] jctIndices = m.junctionIndices;
for (int i = 0; i < jctIndices.length; i++) {
junctionIndexConverter.put(jctIndices[i], i);
}
nodes = assignJunctionsToNodes(game);
junctions = junctionDistances(game);
for (int i = 0; i < junctions.length; i++) {
junctions[i].computeShortestPaths();
}
}
//for Ms Pac-Man
public int[] getPathFromA2B(int a, int b) {
//not going anywhere
if (a == b) {
return new int[]{};
}
//junctions near the source
ArrayList closestFromJunctions = nodes[a].closestJunctions;
//if target is on the way to junction, then we are done
for (int w = 0; w < closestFromJunctions.size(); w++) {
for (int i = 0; i < closestFromJunctions.get(w).path.length; i++) {
if (closestFromJunctions.get(w).path[i] == b) {
return Arrays.copyOf(closestFromJunctions.get(w).path, i + 1);
}
}
}
//junctions near the target
ArrayList closestToJunctions = nodes[b].closestJunctions;
int minFrom = -1;
int minTo = -1;
int minDistance = Integer.MAX_VALUE;
int[] shortestPath = null;
for (int i = 0; i < closestFromJunctions.size(); i++) {
for (int j = 0; j < closestToJunctions.size(); j++) {
//to the first junction
int distance = closestFromJunctions.get(i).path.length;
//junction to junction
int[] tmpPath = junctions[junctionIndexConverter.get(closestFromJunctions.get(i).nodeID)]
.paths[junctionIndexConverter.get(closestToJunctions.get(j).nodeID)].get(MOVE.NEUTRAL);
distance += tmpPath.length;
//to the second junction
distance += closestToJunctions.get(j).path.length;
if (distance < minDistance) {
minDistance = distance;
minFrom = i;
minTo = j;
shortestPath = tmpPath;
}
}
}
return concat(closestFromJunctions.get(minFrom).path, shortestPath, closestToJunctions.get(minTo).reversePath);
}
/////// ghosts //////////
//To be made more efficient shortly.
public int getPathDistanceFromA2B(int a, int b, MOVE lastMoveMade) {
return getPathFromA2B(a, b, lastMoveMade).length;
}
public int[] getPathFromA2B(int a, int b, MOVE lastMoveMade) {
//not going anywhere
if (a == b) {
return new int[]{};
}
//first, go to closest junction (there is only one since we can't reverse)
JunctionData fromJunction = nodes[a].getNearestJunction(lastMoveMade);
//if target is on the way to junction, then we are done
for (int i = 0; i < fromJunction.path.length; i++) {
if (fromJunction.path[i] == b) {
return Arrays.copyOf(fromJunction.path, i + 1);
}
}
//we have reached a junction, fromJunction, which we entered with moveEnteredJunction
int junctionFrom = fromJunction.nodeID;
int junctionFromId = junctionIndexConverter.get(junctionFrom);
MOVE moveEnteredJunction = fromJunction.lastMove.equals(MOVE.NEUTRAL) ? lastMoveMade : fromJunction.lastMove; //if we are at a junction, consider last move instead
//now we need to get the 1 or 2 target junctions that enclose the target point
ArrayList junctionsTo = nodes[b].closestJunctions;
int minDist = Integer.MAX_VALUE;
int[] shortestPath = null;
int closestJunction = -1;
boolean onTheWay = false;
for (int q = 0; q < junctionsTo.size(); q++) {
int junctionToId = junctionIndexConverter.get(junctionsTo.get(q).nodeID);
if (junctionFromId == junctionToId) {
if (!game.getMoveToMakeToReachDirectNeighbour(junctionFrom, junctionsTo.get(q).reversePath[0]).equals(moveEnteredJunction.opposite())) {
int[] reversepath = junctionsTo.get(q).reversePath;
int cutoff = -1;
for (int w = 0; w < reversepath.length; w++) {
if (reversepath[w] == b) {
cutoff = w;
}
}
shortestPath = Arrays.copyOf(reversepath, cutoff + 1);
minDist = shortestPath.length;
closestJunction = q;
onTheWay = true;
}
} else {
EnumMap paths = junctions[junctionFromId].paths[junctionToId];
Set set = paths.keySet();
for (MOVE move : set) {
if (!move.opposite().equals(moveEnteredJunction) && !move.equals(MOVE.NEUTRAL)) {
int[] path = paths.get(move);
if (path.length + junctionsTo.get(q).path.length < minDist)//need to take distance from toJunction to target into account
{
minDist = path.length + junctionsTo.get(q).path.length;
shortestPath = path;
closestJunction = q;
onTheWay = false;
}
}
}
}
}
if (!onTheWay) {
return concat(fromJunction.path, shortestPath, junctionsTo.get(closestJunction).reversePath);
} else {
return concat(fromJunction.path, shortestPath);
}
// return concat(fromJunction.path, junctionsTo.get(closestJunction).reversePath);
}
private Junction[] junctionDistances(Game game) {
Maze m = game.getCurrentMaze();
int[] indices = m.junctionIndices;
Junction[] junctions = new Junction[indices.length];
for (int q = 0; q < indices.length; q++)// from
{
MOVE[] possibleMoves = m.graph[indices[q]].allPossibleMoves.get(MOVE.NEUTRAL);// all possible moves
junctions[q] = new Junction(q, indices[q], indices.length);
for (int z = 0; z < indices.length; z++)// to (we need to include distance to itself)
{
for (int i = 0; i < possibleMoves.length; i++) {
int neighbour = game.getNeighbour(indices[q], possibleMoves[i]);
int[] p = m.astar.computePathsAStar(neighbour, indices[z], possibleMoves[i], game);
m.astar.resetGraph();
junctions[q].addPath(z, possibleMoves[i], p);
}
}
}
return junctions;
}
private DNode[] assignJunctionsToNodes(Game game) {
Maze m = game.getCurrentMaze();
int numNodes = m.graph.length;
DNode[] allNodes = new DNode[numNodes];
for (int i = 0; i < numNodes; i++) {
boolean isJunction = game.isJunction(i);
allNodes[i] = new DNode(i, isJunction);
if (!isJunction) {
MOVE[] possibleMoves = m.graph[i].allPossibleMoves.get(MOVE.NEUTRAL);
for (int j = 0; j < possibleMoves.length; j++) {
ArrayList path = new ArrayList();
MOVE lastMove = possibleMoves[j];
int currentNode = game.getNeighbour(i, lastMove);
path.add(currentNode);
while (!game.isJunction(currentNode)) {
MOVE[] newPossibleMoves = game.getPossibleMoves(currentNode);
for (int q = 0; q < newPossibleMoves.length; q++) {
if (newPossibleMoves[q].opposite() != lastMove) {
lastMove = newPossibleMoves[q];
break;
}
}
currentNode = game.getNeighbour(currentNode, lastMove);
path.add(currentNode);
}
int[] array = new int[path.size()];
for (int w = 0; w < path.size(); w++) {
array[w] = path.get(w);
}
allNodes[i].addPath(array[array.length - 1], possibleMoves[j], i, array, lastMove);
}
}
}
return allNodes;
}
private int[] concat(int[]... arrays) {
int totalLength = 0;
for (int i = 0; i < arrays.length; i++) {
totalLength += arrays[i].length;
}
int[] fullArray = new int[totalLength];
int index = 0;
for (int i = 0; i < arrays.length; i++) {
for (int j = 0; j < arrays[i].length; j++) {
fullArray[index++] = arrays[i][j];
}
}
return fullArray;
}
}
class JunctionData {
public int nodeID, nodeStartedFrom;
public MOVE firstMove, lastMove;
public int[] path, reversePath;
public JunctionData(int nodeID, MOVE firstMove, int nodeStartedFrom, int[] path, MOVE lastMove) {
this.nodeID = nodeID;
this.nodeStartedFrom = nodeStartedFrom;
this.firstMove = firstMove;
this.path = path;
this.lastMove = lastMove;
if (path.length > 0) {
this.reversePath = getReversePath(path);
} else {
reversePath = new int[]{};
}
}
public int[] getReversePath(int[] path) {
int[] reversePath = new int[path.length];
for (int i = 1; i < reversePath.length; i++) {
reversePath[i - 1] = path[path.length - 1 - i];
}
reversePath[reversePath.length - 1] = nodeStartedFrom;
return reversePath;
}
public String toString() {
return nodeID + "\t" + firstMove.toString() + "\t" + Arrays.toString(path);
}
}
class DNode {
public int nodeID;
public ArrayList closestJunctions;
public boolean isJunction;
public DNode(int nodeID, boolean isJunction) {
this.nodeID = nodeID;
this.isJunction = isJunction;
this.closestJunctions = new ArrayList();
if (isJunction) {
closestJunctions.add(new JunctionData(nodeID, MOVE.NEUTRAL, nodeID, new int[]{}, MOVE.NEUTRAL));
}
}
public int[] getPathToJunction(MOVE lastMoveMade) {
if (isJunction) {
return new int[]{};
}
for (int i = 0; i < closestJunctions.size(); i++) {
if (!closestJunctions.get(i).firstMove.equals(lastMoveMade.opposite())) {
return closestJunctions.get(i).path;
}
}
return null;
}
public JunctionData getNearestJunction(MOVE lastMoveMade) {
if (isJunction) {
return closestJunctions.get(0);
}
int minDist = Integer.MAX_VALUE;
int bestIndex = -1;
for (int i = 0; i < closestJunctions.size(); i++) {
if (!closestJunctions.get(i).firstMove.equals(lastMoveMade.opposite())) {
int newDist = closestJunctions.get(i).path.length;
if (newDist < minDist) {
minDist = newDist;
bestIndex = i;
}
}
}
if (bestIndex != -1) {
return closestJunctions.get(bestIndex);
} else {
return null;
}
}
public void addPath(int junctionID, MOVE firstMove, int nodeStartedFrom, int[] path, MOVE lastMove) {
closestJunctions.add(new JunctionData(junctionID, firstMove, nodeStartedFrom, path, lastMove));
}
public String toString() {
return "" + nodeID + "\t" + isJunction;
}
}
// for each junction, stores paths to all other junctions for all directions
class Junction {
public int jctId, nodeId;
public EnumMap[] paths;
@SuppressWarnings("unchecked")
public Junction(int jctId, int nodeId, int numJcts) {
this.jctId = jctId;
this.nodeId = nodeId;
paths = new EnumMap[numJcts];
for (int i = 0; i < paths.length; i++) {
paths[i] = new EnumMap(MOVE.class);
}
}
public void computeShortestPaths() {
MOVE[] moves = MOVE.values();
for (int i = 0; i < paths.length; i++) {
if (i == jctId) {
paths[i].put(MOVE.NEUTRAL, new int[]{});
} else {
int distance = Integer.MAX_VALUE;
int[] path = null;
for (int j = 0; j < moves.length; j++) {
if (paths[i].containsKey(moves[j])) {
int[] tmp = paths[i].get(moves[j]);
if (tmp.length < distance) {
distance = tmp.length;
path = tmp;
}
}
}
paths[i].put(MOVE.NEUTRAL, path);
}
}
}
// store the shortest path given the last move made
public void addPath(int toJunction, MOVE firstMoveMade, int[] path) {
paths[toJunction].put(firstMoveMade, path);
}
public String toString() {
return jctId + "\t" + nodeId;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy