/* * Implementation of "Ms Pac-Man" for the "Ms Pac-Man versus Ghost Team Competition", brought * to you by Philipp Rohlfshagen, David Robles and Simon Lucas of the University of Essex. * * www.pacman-vs-ghosts.net * * Code written by Philipp Rohlfshagen, based on earlier implementations of the game by * Simon Lucas and David Robles. * * You may use and distribute this code freely for non-commercial purposes. This notice * needs to be included in all distributions. Deviations from the original should be * clearly documented. We welcome any comments and suggestions regarding the code. */ package game.core; import game.entries.ghosts.MyGhosts; import java.io.BufferedReader; //import java.io.File; //import java.io.FileInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; //import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; /* * Simple implementation of Ms Pac-Man. The class Game contains all code relating to the * game; the class GameView displays the game. Controllers must implement PacManController * and GhostController respectively. The game may be executed using Exec. */ public class G implements Game { //Static stuff (mazes are immutable - hence static) protected static Maze[] mazes=new Maze[NUM_MAZES]; //Variables (game state): protected BitSet pills,powerPills; //level-specific protected int curMaze,totLevel,levelTime,totalTime,score,ghostEatMultiplier; protected boolean gameOver; //pac-man-specific protected int curPacManLoc,lastPacManDir,livesRemaining; protected boolean extraLife; //ghosts-specific protected int[] curGhostLocs,lastGhostDirs,edibleTimes,lairTimes; ///////////////////////////////////////////////////////////////////////////// ///////////////// Constructors and Initialisers ////////////////////////// ///////////////////////////////////////////////////////////////////////////// //Constructor protected G(){} //loads the mazes and store them protected void init() { for(int i=0;i<mazes.length;i++) if(mazes[i]==null) mazes[i]=new Maze(i); } //Creates an exact copy of the game public Game copy() { G copy=new G(); copy.pills=(BitSet)pills.clone(); copy.powerPills=(BitSet)powerPills.clone(); copy.curMaze=curMaze; copy.totLevel=totLevel; copy.levelTime=levelTime; copy.totalTime=totalTime; copy.score=score; copy.ghostEatMultiplier=ghostEatMultiplier; copy.gameOver=gameOver; copy.curPacManLoc=curPacManLoc; copy.lastPacManDir=lastPacManDir; copy.livesRemaining=livesRemaining; copy.extraLife=extraLife; copy.curGhostLocs=Arrays.copyOf(curGhostLocs,curGhostLocs.length); copy.lastGhostDirs=Arrays.copyOf(lastGhostDirs,lastGhostDirs.length); copy.edibleTimes=Arrays.copyOf(edibleTimes,edibleTimes.length); copy.lairTimes=Arrays.copyOf(lairTimes,lairTimes.length); return copy; } //If pac-man has been eaten or a new level has been reached protected void reset(boolean newLevel) { if(newLevel) { curMaze=(curMaze+1)%G.NUM_MAZES; totLevel++; levelTime=0; pills=new BitSet(getNumberPills()); pills.set(0,getNumberPills()); powerPills=new BitSet(getNumberPowerPills()); powerPills.set(0,getNumberPowerPills()); } curPacManLoc=getInitialPacPosition(); lastPacManDir=G.INITIAL_PAC_DIR; Arrays.fill(curGhostLocs,mazes[curMaze].lairPosition); lastGhostDirs=Arrays.copyOf(G.INITIAL_GHOST_DIRS,G.INITIAL_GHOST_DIRS.length); Arrays.fill(edibleTimes,0); ghostEatMultiplier=1; for(int i=0;i<lairTimes.length;i++) lairTimes[i]=(int)(G.LAIR_TIMES[i]*(Math.pow(LAIR_REDUCTION,totLevel))); } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////// Game Play ////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// //Central method that advances the game state public int[] advanceGame(int pacManDir,int[] ghostDirs) { updatePacMan(pacManDir); //move pac-man eatPill(); //eat a pill boolean reverse=eatPowerPill(); //eat a power pill updateGhosts(ghostDirs,reverse); //move ghosts //This is primarily done for the replays as reset (as possibly called by feast()) sets the //last directions to the initial ones, not the ones taken int[] actionsTakens={lastPacManDir,lastGhostDirs[0],lastGhostDirs[1],lastGhostDirs[2],lastGhostDirs[3]}; feast(); //ghosts eat pac-man or vice versa for(int i=0;i<lairTimes.length;i++) if(lairTimes[i]>0) { lairTimes[i]--; if(lairTimes[i]==0) curGhostLocs[i]=mazes[curMaze].initialGhostsPosition; } if(!extraLife && score>=EXTRA_LIFE_SCORE) //award 1 extra life at 10000 points { extraLife=true; livesRemaining++; } totalTime++; levelTime++; checkLevelState(); //check if level/game is over return actionsTakens; } //Updates the location of Ms Pac-Man protected void updatePacMan(int direction) { direction=checkPacManDir(direction); lastPacManDir=direction; curPacManLoc=getNeighbour(curPacManLoc,direction); } //Checks the direction supplied by the controller and substitutes for a legal one if necessary protected int checkPacManDir(int direction) { int[] neighbours=getPacManNeighbours(); if((direction>3 || direction<0 || neighbours[direction]==-1) && (lastPacManDir>3 || lastPacManDir<0 || neighbours[lastPacManDir]==-1)) return 4; if(direction<0 || direction>3) direction=lastPacManDir; if(neighbours[direction]==-1) if(neighbours[lastPacManDir]!=-1) direction=lastPacManDir; else { int[] options=getPossiblePacManDirs(true); direction=options[G.rnd.nextInt(options.length)]; } return direction; } //Updates the locations of the ghosts protected void updateGhosts(int[] directions,boolean reverse) { if(directions==null) directions=Arrays.copyOf(lastGhostDirs,lastGhostDirs.length); for(int i=0;i<directions.length;i++) { if(lairTimes[i]==0) { if(reverse) { lastGhostDirs[i]=getReverse(lastGhostDirs[i]); curGhostLocs[i]=getNeighbour(curGhostLocs[i],lastGhostDirs[i]); } else if(edibleTimes[i]==0 || edibleTimes[i]%GHOST_SPEED_REDUCTION!=0) { directions[i]=checkGhostDir(i,directions[i]); lastGhostDirs[i]=directions[i]; curGhostLocs[i]=getNeighbour(curGhostLocs[i],directions[i]); } } } } //Checks the directions supplied by the controller and substitutes for a legal ones if necessary protected int checkGhostDir(int whichGhost,int direction) { if(direction<0 || direction>3) direction=lastGhostDirs[whichGhost]; int[] neighbours=getGhostNeighbours(whichGhost); if(neighbours[direction]==-1) { if(neighbours[lastGhostDirs[whichGhost]]!=-1) direction=lastGhostDirs[whichGhost]; else { int[] options=getPossibleGhostDirs(whichGhost); direction=options[G.rnd.nextInt(options.length)]; } } return direction; } //Eats a pill protected void eatPill() { int pillIndex=getPillIndex(curPacManLoc); if(pillIndex>=0 && pills.get(pillIndex)) { score+=G.PILL; pills.clear(pillIndex); } } //Eats a power pill - turns ghosts edible (blue) protected boolean eatPowerPill() { boolean reverse=false; int powerPillIndex=getPowerPillIndex(curPacManLoc); if(powerPillIndex>=0 && powerPills.get(powerPillIndex)) { score+=G.POWER_PILL; ghostEatMultiplier=1; powerPills.clear(powerPillIndex); //This ensures that only ghosts outside the lair (i.e., inside the maze) turn edible int newEdibleTime=(int)(G.EDIBLE_TIME*(Math.pow(G.EDIBLE_TIME_REDUCTION,totLevel))); for(int i=0;i<NUM_GHOSTS;i++) if(lairTimes[i]==0) edibleTimes[i]=newEdibleTime; else edibleTimes[i]=0; //This turns all ghosts edible, independent on whether they are in the lair or not // Arrays.fill(edibleTimes,(int)(G.EDIBLE_TIME*(Math.pow(G.EDIBLE_TIME_REDUCTION,totLevel)))); reverse=true; } else if(levelTime>1 && Math.random()<MyGhosts.GHOST_REVERSAL) //random ghost reversal reverse=true; return reverse; } //This is where the characters of the game eat one another if possible protected void feast() { for(int i=0;i<curGhostLocs.length;i++) { int distance=getPathDistance(curPacManLoc,curGhostLocs[i]); if(distance<=G.EAT_DISTANCE && distance!=-1) { if(edibleTimes[i]>0) //pac-man eats ghost { score+=G.GHOST_EAT_SCORE*ghostEatMultiplier; ghostEatMultiplier*=2; edibleTimes[i]=0; lairTimes[i]=(int)(G.COMMON_LAIR_TIME*(Math.pow(G.LAIR_REDUCTION,totLevel))); curGhostLocs[i]=mazes[curMaze].lairPosition; lastGhostDirs[i]=G.INITIAL_GHOST_DIRS[i]; } else //ghost eats pac-man { livesRemaining--; if(livesRemaining<=0) { gameOver=true; return; } else reset(false); } } } for(int i=0;i<edibleTimes.length;i++) if(edibleTimes[i]>0) edibleTimes[i]--; } //Checks the state of the level/game and advances to the next level or terminates the game protected void checkLevelState() { //if all pills have been eaten or the time is up... if((pills.isEmpty() && powerPills.isEmpty()) || levelTime>=LEVEL_LIMIT) { //award any remaining pills to Ms Pac-Man score+=G.PILL*pills.cardinality()+G.POWER_PILL*powerPills.cardinality(); //put a cap on the total number of levels played if(totLevel+1==G.MAX_LEVELS) { gameOver=true; return; } else reset(true); } } ///////////////////////////////////////////////////////////////////////////// /////////////////////////// Getter Methods //////////////////////////////// ///////////////////////////////////////////////////////////////////////////// //Returns the reverse of the direction supplied public int getReverse(int direction) { switch(direction) { case 0: return 2; case 1: return 3; case 2: return 0; case 3: return 1; } return 4; } //Whether the game is over or not public boolean gameOver() { return gameOver; } //Whether the pill specified is still there public boolean checkPill(int nodeIndex) { return pills.get(nodeIndex); } //Whether the power pill specified is still there public boolean checkPowerPill(int nodeIndex) { return powerPills.get(nodeIndex); } //Returns the neighbours of the node at which Ms Pac-Man currently resides public int[] getPacManNeighbours() { return Arrays.copyOf(mazes[curMaze].graph[curPacManLoc].neighbours,mazes[curMaze].graph[curPacManLoc].neighbours.length); } //Returns the neighbours of the node at which the specified ghost currently resides. NOTE: since ghosts are not allowed to reverse, that //neighbour is filtered out. Alternatively use: getNeighbour(), given curGhostLoc[-] for all directions public int[] getGhostNeighbours(int whichGhost) { int[] neighbours=Arrays.copyOf(mazes[curMaze].graph[curGhostLocs[whichGhost]].neighbours,mazes[curMaze].graph[curGhostLocs[whichGhost]].neighbours.length); neighbours[getReverse(lastGhostDirs[whichGhost])]=-1; return neighbours; } //The current level public int getCurLevel() { return totLevel; } //The current maze (1-4) public int getCurMaze() { return curMaze; } //Current node index of Ms Pac-Man public int getCurPacManLoc() { return curPacManLoc; } //Current node index of Ms Pac-Man public int getCurPacManDir() { return lastPacManDir; } //Lives that remain for Ms Pac-Man public int getLivesRemaining() { return livesRemaining; } //Current node at which the specified ghost resides public int getCurGhostLoc(int whichGhost) { return curGhostLocs[whichGhost]; } //Current direction of the specified ghost public int getCurGhostDir(int whichGhost) { return lastGhostDirs[whichGhost]; } //Returns the edible time for the specified ghost public int getEdibleTime(int whichGhost) { return edibleTimes[whichGhost]; } //Simpler check to see if a ghost is edible public boolean isEdible(int whichGhost) { return edibleTimes[whichGhost]>0; } //Returns the score of the game public int getScore() { return score; } //Returns the time of the current level (important with respect to LEVEL_LIMIT) public int getLevelTime() { return levelTime; } //Total time the game has been played for (at most LEVEL_LIMIT*MAX_LEVELS) public int getTotalTime() { return totalTime; } //Total number of pills in the maze public int getNumberPills() { return mazes[curMaze].pillIndices.length; } //Total number of power pills in the maze public int getNumberPowerPills() { return mazes[curMaze].powerPillIndices.length; } //Time left that the specified ghost will spend in the lair public int getLairTime(int whichGhost) { return lairTimes[whichGhost]; } //If in lair (getLairTime(-)>0) or if not at junction public boolean ghostRequiresAction(int whichGhost) { return (isJunction(curGhostLocs[whichGhost]) && (edibleTimes[whichGhost]==0 || edibleTimes[whichGhost]%GHOST_SPEED_REDUCTION!=0)); } //Returns name of maze: A, B, C, D public String getName() { return mazes[curMaze].name; } //Returns the starting position of Ms PacMan public int getInitialPacPosition() { return mazes[curMaze].initialPacPosition; } //Returns the starting position of the ghosts (i.e., first node AFTER leaving the lair) public int getInitialGhostsPosition() { return mazes[curMaze].initialGhostsPosition; } //Total number of nodes in the graph (i.e., those with pills, power pills and those that are empty) public int getNumberOfNodes() { return mazes[curMaze].graph.length; } //Returns the x coordinate of the specified node public int getX(int index) { return mazes[curMaze].graph[index].x; } //Returns the y coordinate of the specified node public int getY(int index) { return mazes[curMaze].graph[index].y; } //Returns the pill index of the node. If it is -1, the node has no pill. Otherwise one can //use the bitset to check whether the pill has already been eaten public int getPillIndex(int nodeIndex) { return mazes[curMaze].graph[nodeIndex].pillIndex; } //Returns the power pill index of the node. If it is -1, the node has no pill. Otherwise one //can use the bitset to check whether the pill has already been eaten public int getPowerPillIndex(int nodeIndex) { return mazes[curMaze].graph[nodeIndex].powerPillIndex; } //Returns the neighbour of node index that corresponds to direction. In the case of neutral, the //same node index is returned public int getNeighbour(int nodeIndex,int direction) { if(direction<0 || direction>3)//this takes care of "neutral" return nodeIndex; else return mazes[curMaze].graph[nodeIndex].neighbours[direction]; } //Returns the indices to all the nodes that have pills public int[] getPillIndices() { return Arrays.copyOf(mazes[curMaze].pillIndices,mazes[curMaze].pillIndices.length); } //Returns the indices to all the nodes that have power pills public int[] getPowerPillIndices() { return Arrays.copyOf(mazes[curMaze].powerPillIndices,mazes[curMaze].powerPillIndices.length); } //Returns the indices to all the nodes that are junctions public int[] getJunctionIndices() { return Arrays.copyOf(mazes[curMaze].junctionIndices,mazes[curMaze].junctionIndices.length); } //Checks of a node is a junction public boolean isJunction(int nodeIndex) { return mazes[curMaze].graph[nodeIndex].numNeighbours>2; } //returns the score awarded for the next ghost to be eaten public int getNextEdibleGhostScore() { return G.GHOST_EAT_SCORE*ghostEatMultiplier; } //returns the number of pills still in the maze public int getNumActivePills() { return pills.cardinality(); } //returns the number of power pills still in the maze public int getNumActivePowerPills() { return powerPills.cardinality(); } //returns the indices of all active pills in the maze public int[] getPillIndicesActive() { int[] indices=new int[pills.cardinality()]; int index=0; for(int i=0;i<mazes[curMaze].pillIndices.length;i++) if(pills.get(i)) indices[index++]=mazes[curMaze].pillIndices[i]; return indices; } //returns the indices of all active power pills in the maze public int[] getPowerPillIndicesActive() { int[] indices=new int[powerPills.cardinality()]; int index=0; for(int i=0;i<mazes[curMaze].powerPillIndices.length;i++) if(powerPills.get(i)) indices[index++]=mazes[curMaze].powerPillIndices[i]; return indices; } //Returns the number of neighbours of a node: 2, 3 or 4. Exception: lair, which has no neighbours public int getNumNeighbours(int nodeIndex) { return mazes[curMaze].graph[nodeIndex].numNeighbours; } //Returns the actual directions Ms Pac-Man can take public int[] getPossiblePacManDirs(boolean includeReverse) { return getPossibleDirs(curPacManLoc,lastPacManDir,includeReverse); } //Returns the actual directions the specified ghost can take public int[] getPossibleGhostDirs(int whichGhost) { return getPossibleDirs(curGhostLocs[whichGhost],lastGhostDirs[whichGhost],false); } //Computes the directions to be taken given the current location private int[] getPossibleDirs(int curLoc,int curDir,boolean includeReverse) { int numNeighbours=mazes[curMaze].graph[curLoc].numNeighbours; if(numNeighbours==0) return new int[0]; int[] nodes=mazes[curMaze].graph[curLoc].neighbours; int[] directions; if(includeReverse || (curDir<0 || curDir>3)) directions=new int[numNeighbours]; else directions=new int[numNeighbours-1]; int index=0; for(int i=0;i<nodes.length;i++) if(nodes[i]!=-1) { if(includeReverse || (curDir<0 || curDir>3)) directions[index++]=i; else if(i!=getReverse(curDir)) directions[index++]=i; } return directions; } //Returns the direction Pac-Man should take to approach/retreat a target (to) given some distance //measure public int getNextPacManDir(int to,boolean closer,DM measure) { return getNextDir(mazes[curMaze].graph[curPacManLoc].neighbours,to,closer,measure); } //Returns the direction the ghost should take to approach/retreat a target (to) given some distance //measure. Reversals are filtered. public int getNextGhostDir(int whichGhost,int to,boolean closer,Game.DM measure) { return getNextDir(getGhostNeighbours(whichGhost),to,closer,measure); } //This method returns the direction to take given some options (usually corresponding to the //neighbours of the node in question), moving either towards or away (closer in {true, false}) //using one of the three distance measures. private int getNextDir(int[] from,int to,boolean closer,Game.DM measure) { int dir=-1; double min=Integer.MAX_VALUE; double max=-Integer.MAX_VALUE; for(int i=0;i<from.length;i++) { if(from[i]!=-1) { double dist=0; switch(measure) { case PATH: dist=getPathDistance(from[i],to); break; case EUCLID: dist=getEuclideanDistance(from[i],to); break; case MANHATTEN: dist=getManhattenDistance(from[i],to); break; } if(closer && dist<min) { min=dist; dir=i; } if(!closer && dist>max) { max=dist; dir=i; } } } return dir; } //Returns the PATH distance from any node to any other node public int getPathDistance(int from,int to) { if(from==to) return 0; else if(from<to) return mazes[curMaze].distances[((to*(to+1))/2)+from]; else return mazes[curMaze].distances[((from*(from+1))/2)+to]; } //Returns the EUCLEDIAN distance between two nodes in the current maze. public double getEuclideanDistance(int from,int to) { return Math.sqrt(Math.pow(mazes[curMaze].graph[from].x-mazes[curMaze].graph[to].x,2)+Math.pow(mazes[curMaze].graph[from].y-mazes[curMaze].graph[to].y,2)); } //Returns the MANHATTEN distance between two nodes in the current maze. public int getManhattenDistance(int from,int to) { return (int)(Math.abs(mazes[curMaze].graph[from].x-mazes[curMaze].graph[to].x)+Math.abs(mazes[curMaze].graph[from].y-mazes[curMaze].graph[to].y)); } //Returns the path of adjacent nodes from one node to another, including these nodes //E.g., path from a to c might be [a,f,r,t,c] public int[] getPath(int from,int to) { int currentNode=from; ArrayList<Integer> path=new ArrayList<Integer>(); int lastDir; while(currentNode!=to) { path.add(currentNode); int[] neighbours=mazes[curMaze].graph[currentNode].neighbours; lastDir=getNextDir(neighbours,to,true,G.DM.PATH); currentNode=neighbours[lastDir]; } int[] arrayPath=new int[path.size()]; for(int i=0;i<arrayPath.length;i++) arrayPath[i]=path.get(i); return arrayPath; } //Similar to getPath(-) but takes into consideration the fact that ghosts may not reverse. Hence the path to be taken //may be significantly longer than the shortest available path public int[] getGhostPath(int whichGhost,int to) { if(mazes[curMaze].graph[curGhostLocs[whichGhost]].numNeighbours==0) return new int[0]; int currentNode=curGhostLocs[whichGhost]; ArrayList<Integer> path=new ArrayList<Integer>(); int lastDir=lastGhostDirs[whichGhost]; while(currentNode!=to) { path.add(currentNode); int[] neighbours=getGhostNeighbours(currentNode,lastDir); lastDir=getNextDir(neighbours,to,true,G.DM.PATH); currentNode=neighbours[lastDir]; } int[] arrayPath=new int[path.size()]; for(int i=0;i<arrayPath.length;i++) arrayPath[i]=path.get(i); return arrayPath; } //Returns the node from 'targets' that is closest/farthest from the node 'from' given the distance measure specified public int getTarget(int from,int[] targets,boolean nearest,Game.DM measure) { int target=-1; double min=Integer.MAX_VALUE; double max=-Integer.MAX_VALUE; for(int i=0;i<targets.length;i++) { double dist=0; switch(measure) { case PATH: dist=getPathDistance(targets[i],from); break; case EUCLID: dist=getEuclideanDistance(targets[i],from); break; case MANHATTEN: dist=getManhattenDistance(targets[i],from); break; } if(nearest && dist<min) { min=dist; target=targets[i]; } if(!nearest && dist>max) { max=dist; target=targets[i]; } } return target; } //Returns the target closes from the position of the ghost, considering that reversals are not allowed public int getGhostTarget(int whichGhost,int[] targets,boolean nearest) { int target=-1; double min=Integer.MAX_VALUE; double max=-Integer.MAX_VALUE; for(int i=0;i<targets.length;i++) { double dist=getGhostPathDistance(whichGhost,targets[i]); if(nearest && dist<min) { min=dist; target=targets[i]; } if(!nearest && dist>max) { max=dist; target=targets[i]; } } return target; } //Returns the path distance for a particular ghost: takes into account the fact that ghosts may not reverse public int getGhostPathDistance(int whichGhost,int to) { return getGhostPath(whichGhost,to).length; } //Returns the neighbours of a node with the one correspodining to the reverse of direction being deleted (i.e., =-1) private int[] getGhostNeighbours(int node,int lastDirection) { int[] neighbours=Arrays.copyOf(mazes[curMaze].graph[node].neighbours,mazes[curMaze].graph[node].neighbours.length); neighbours[getReverse(lastDirection)]=-1; return neighbours; } /* * Stores the actual mazes, each of which is simply a connected graph. The differences between the mazes are the connectivity * and the x,y coordinates (used for drawing or to compute the Euclidean distance. There are 3 built-in distance functions in * total: Euclidean, Manhatten and Dijkstra's shortest path distance. The latter is pre-computed and loaded, the others are * computed on the fly whenever getNextDir(-) is called. */ protected final class Maze { private String pathMazes="data"; private String[] nodeNames={"a","b","c","d"}; private String[] distNames={"da","db","dc","dd"}; protected int[] distances,pillIndices,powerPillIndices,junctionIndices; //Information for the controllers protected Node[] graph; //The actual maze, stored as a graph (set of nodes) protected int initialPacPosition,lairPosition,initialGhostsPosition,width,height; //Maze-specific information protected String name; //Name of the Maze /* * Each maze is stored as a (connected) graph: all nodes have neighbours, stored in an array of length 4. The * index of the array associates the direction the neighbour is located at: '[up,right,down,left]'. * For instance, if node '9' has neighbours '[-1,12,-1,6]', you can reach node '12' by going right, and node * 6 by going left. The directions returned by the controllers should thus be in {0,1,2,3} and can be used * directly to determine the next node to go to. */ protected Maze(int index) { loadNodes(nodeNames[index]); loadDistances(distNames[index]); } //Loads all the nodes from files and initialises all maze-specific information. private void loadNodes(String fileName) { try { //APPLET // BufferedReader br=new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("/data/"+fileName))); //APPLICATION BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(pathMazes+System.getProperty("file.separator")+fileName))); String input=br.readLine(); //preamble String[] pr=input.split("\t"); this.name=pr[0]; this.initialPacPosition=Integer.parseInt(pr[1]); this.lairPosition=Integer.parseInt(pr[2]); this.initialGhostsPosition=Integer.parseInt(pr[3]); this.graph=new Node[Integer.parseInt(pr[4])]; this.pillIndices=new int[Integer.parseInt(pr[5])]; this.powerPillIndices=new int[Integer.parseInt(pr[6])]; this.junctionIndices=new int[Integer.parseInt(pr[7])]; this.width=Integer.parseInt(pr[8]); this.height=Integer.parseInt(pr[9]); input=br.readLine(); int nodeIndex=0; int pillIndex=0; int powerPillIndex=0; int junctionIndex=0; while(input!=null) { String[] nd=input.split("\t"); Node node=new Node(nd[0],nd[1],nd[2],nd[7],nd[8],new String[]{nd[3],nd[4],nd[5],nd[6]}); graph[nodeIndex++]=node; if(node.pillIndex>=0) pillIndices[pillIndex++]=node.nodeIndex; else if(node.powerPillIndex>=0) powerPillIndices[powerPillIndex++]=node.nodeIndex; if(node.numNeighbours>2) junctionIndices[junctionIndex++]=node.nodeIndex; input=br.readLine(); } } catch(IOException ioe) { ioe.printStackTrace(); } } /* * Loads the shortest path distances which have been pre-computed. The data contains the shortest distance from * any node in the maze to any other node. Since the graph is symmetric, the symmetries have been removed to preserve * memory and all distances are stored in a 1D array; they are looked-up using getDistance(-). */ private void loadDistances(String fileName) { this.distances=new int[((graph.length*(graph.length-1))/2)+graph.length]; try { //APPLET // BufferedReader br=new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("/data/"+fileName))); //APPLICATION BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(pathMazes+System.getProperty("file.separator")+fileName))); String input=br.readLine(); int index=0; while(input!=null) { distances[index++]=Integer.parseInt(input); input=br.readLine(); } } catch(IOException ioe) { ioe.printStackTrace(); } } } /* * Stores all information relating to a node in the graph, including all the indices required * to check and update the current state of the game. */ protected final class Node { protected int x,y,nodeIndex,pillIndex,powerPillIndex,numNeighbours; protected int[] neighbours; protected Node(String nodeIndex,String x,String y,String pillIndex,String powerPillIndex,String[] neighbours) { this.nodeIndex=Integer.parseInt(nodeIndex); this.x=Integer.parseInt(x); this.y=Integer.parseInt(y); this.pillIndex=Integer.parseInt(pillIndex); this.powerPillIndex=Integer.parseInt(powerPillIndex); this.neighbours=new int[neighbours.length]; for(int i=0;i<neighbours.length;i++) { this.neighbours[i]=Integer.parseInt(neighbours[i]); if(this.neighbours[i]!=-1) numNeighbours++; } } } }