Untitled
unknown
plain_text
4 months ago
16 kB
5
Indexable
package pacman; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import java.util.*; public class Ghost implements Collidable { private Rectangle square; private Pane gamePane; private Direction currentDirection; private MazeSquare[][] mapArray; public Ghost(int x, int y, Pane gamePane, Color color, MazeSquare[][] mapArray) { this.square = new Rectangle(x, y, Constants.SQUARE_WIDTH, Constants.SQUARE_WIDTH); this.square.setFill(color); this.gamePane = gamePane; this.currentDirection = this.getRandomDirection(); this.mapArray = mapArray; this.addGhost(); } public void addGhost() { this.gamePane.getChildren().add(this.square); } /** * Setter method for the color of a square */ public void setColor(Color color) { this.square.setFill(color); } private void backToPen(MazeSquare[][] maze) { maze[this.getRow()][this.getCol()].removeObject(this); // Update the ghost's graphical position this.setLocation(11 * Constants.SQUARE_WIDTH, 10 * Constants.SQUARE_WIDTH); // Add the ghost to the new MazeSquare (pen location) maze[11][10].addObject(this); } @Override public void collide(Game game) { System.out.println("collided"); switch (game.getGameMode()) { case 0: case 1: game.decreaseLives(1); game.resetGame(); break; case 2: //used getMaze because collide is a method in the collidable interface and adding a parameter //means all other classes implementing the interface also has to add that parameter this.backToPen(game.getMaze()); game.setGhostLeaveCounter(0); game.addToPenQueue(this); game.increasePoints(200); break; default: break; } } public void setLocation(int x, int y) { this.square.setX(x); this.square.setY(y); } private Direction getRandomDirection() { Direction[] directions = Direction.values(); Random random = new Random(); return directions[random.nextInt(directions.length)]; } //can make this whole thing into helper method to get validDirections private List<Direction> getValidDirections(MazeSquare[][] maze) { List<Direction> validDirections = new ArrayList<>(); if (this.checkMoveValidity(-1, 0, maze)) { // UP validDirections.add(Direction.UP); } if (this.checkMoveValidity(1, 0, maze)) { // DOWN validDirections.add(Direction.DOWN); } if (this.checkMoveValidity(0, -1, maze)) { // LEFT validDirections.add(Direction.LEFT); } if (this.checkMoveValidity(0, 1, maze)) { // RIGHT validDirections.add(Direction.RIGHT); } validDirections.remove(this.getOppositeDirection(this.currentDirection)); return validDirections; } public void moveRandomly(MazeSquare[][] maze) {//remember to add that whenever the ghost moves, move it logically too! //use helper method to get all valid directions List<Direction> validDirections = this.getValidDirections(maze); //change getRandomDirection to helper method that takes in 1 parameter? Random random = new Random(); this.currentDirection = validDirections.get(random.nextInt(validDirections.size())); switch (this.currentDirection) { case UP: this.moveUp(maze); break; case DOWN: this.moveDown(maze); break; case LEFT: this.moveLeft(maze); break; case RIGHT: this.moveRight(maze); break; default: break; } } public void scatter(MazeSquare[][] maze, BoardCoordinate target) { //BoardCoordinate currentPosition = new BoardCoordinate(this.getRow(), this.getCol(), false); // Use BFS to find the next direction toward the target corner Direction nextDirection = this.BFS(target, maze); // Move the ghost in the determined direction if (nextDirection != null) { this.currentDirection = nextDirection; // Update the current direction switch (this.currentDirection) { case UP: this.moveUp(maze); break; case DOWN: this.moveDown(maze); break; case LEFT: this.moveLeft(maze); break; case RIGHT: this.moveRight(maze); break; } } } public void chase(MazeSquare[][] maze, BoardCoordinate pacmanPosition) { // Get the ghost's current position //BoardCoordinate currentPosition = new BoardCoordinate(this.getRow(), this.getCol(), false); // Perform BFS to get the next direction Direction nextDirection = this.BFS(pacmanPosition, maze); // Move the ghost based on the direction if (nextDirection != null) { this.currentDirection = nextDirection; switch (this.currentDirection) { case UP: this.moveUp(maze); break; case DOWN: this.moveDown(maze); break; case LEFT: this.moveLeft(maze); break; case RIGHT: this.moveRight(maze); break; } } } public Direction BFS(BoardCoordinate target, MazeSquare[][] maze) { Queue<BoardCoordinate> queue = new LinkedList<>(); boolean[][] visited = new boolean[23][23]; // Assuming a 23x23 board Direction[][] directions = new Direction[23][23]; // Tracks the first direction to each cell //directions[this.getRow()][this.getCol()] = this.currentDirection; int currentRow = this.getRow(); int currentCol = this.getCol(); // Mark the current square as visited to prevent it from being re-queued visited[currentRow][currentCol] = true; //directions[currentRow][currentCol] = currentDirection; // Initialize tracking of the closest position BoardCoordinate closestSquare = new BoardCoordinate(this.getRow(), this.getCol(), false); //Direction initialDirection = this.currentDirection; for (Direction initialDirection : Direction.values()) { if (initialDirection != this.getOppositeDirection(this.currentDirection)) { BoardCoordinate neighbor = this.getNextSquare(this.getRow(), this.getCol(), initialDirection, maze); //new BoardCoordinate(this.getRow()+getRowOffset(initialDirection), this.getCol()+getColOffset(initialDirection), true); if (this.isValidNeighbor(neighbor, visited,maze)) { queue.add(neighbor); visited[neighbor.getRow()][neighbor.getColumn()] = true; directions[neighbor.getRow()][neighbor.getColumn()] = initialDirection; } } } // Process BFS while (!queue.isEmpty()) { BoardCoordinate current = queue.remove(); double currentDistance = this.calculateDistance(current, target); double closestDistance = calculateDistance(closestSquare, target); if (currentDistance < closestDistance) { closestSquare = current; } // Enqueue neighbors for (Direction dir : Direction.values()) { BoardCoordinate neighbor = this.getNextSquare(current.getRow(), current.getColumn(), dir, maze); if (isValidNeighbor(neighbor, visited, maze) && directions[neighbor.getRow()][neighbor.getColumn()] == null) { queue.add(neighbor); visited[neighbor.getRow()][neighbor.getColumn()] = true; directions[neighbor.getRow()][neighbor.getColumn()] = directions[current.getRow()][current.getColumn()]; } } } // If the ghost is already on the closest square and no better square is found if((closestSquare.getRow() == currentRow && closestSquare.getColumn() == currentCol)) { // Move to any valid neighbor to avoid getting stuck List<Direction> validDirections = getValidDirections(maze); if (!validDirections.isEmpty()) { return validDirections.get(0); // Choose the first valid direction } } this.printMap(directions); // Return the initial direction recorded for the closest position //System.out.println(directions[closestSquare.getRow()][closestSquare.getColumn()]); return directions[closestSquare.getRow()][closestSquare.getColumn()]; } private void printMap(Direction[][] searchMap){ for (int i = 0; i < 23; i++) { for (int j = 0; j < 23; j++) { if (searchMap[i][j] == null) { System.out.print("\t"); } else { System.out.print(searchMap[i][j].toString().charAt(0) + "\t"); } } System.out.println(); } } public boolean checkMoveValidity(int rowsMoved, int colsMoved, MazeSquare[][] maze) { //checks where each square of the piece is moving to int currRow = this.getRow(); int currCol = this.getCol(); // Calculate target row and column int targetRow = currRow + rowsMoved; int targetCol = currCol + colsMoved; //check if target square is in the board and the wrapping tunnel row // Handle wrapping in the tunnel (row 11) if (currRow == 11) { if (targetCol < 0 || targetCol > 22) { return true; // Allow wrapping if on the tunnel row } } // Check if the target position is out of bounds (non-tunnel rows) if (targetRow < 0 || targetRow >= maze.length || targetCol < 0 || targetCol >= maze[0].length) { System.out.println("target square out of bounds"); return false; // Out of bounds } if (maze[currRow + rowsMoved][currCol + colsMoved].getColor() != Color.BLACK) { return false; } return true; } public int getRow() { return (int) this.square.getY() / Constants.SQUARE_WIDTH; } /** * Getter method for the column index of a square on the tetris board */ public int getCol() { return (int) this.square.getX() / Constants.SQUARE_WIDTH; } public void moveLeft(MazeSquare[][] maze) { maze[this.getRow()][this.getCol()].removeObject(this); // Handle wrapping visually if (this.square.getX() <= 0) { this.square.toFront(); this.square.setX((maze[0].length - 1) * Constants.SQUARE_WIDTH); // Wrap to the right } else { this.square.toFront(); this.square.setX(this.square.getX() - Constants.SQUARE_WIDTH); } maze[this.getRow()][this.getCol()].addObject(this); } public void moveRight(MazeSquare[][] maze) { maze[this.getRow()][this.getCol()].removeObject(this); // Handle wrapping visually if (this.square.getX() >= (maze[0].length - 1) * Constants.SQUARE_WIDTH) { this.square.toFront(); this.square.setX(0); // Wrap to the left } else { this.square.toFront(); this.square.setX(this.square.getX() + Constants.SQUARE_WIDTH); } maze[this.getRow()][this.getCol()].addObject(this); } /** * Moves rectangle down by 1 square (30 pixels) */ public void moveDown(MazeSquare[][] maze) { maze[this.getRow()][this.getCol()].removeObject(this); this.square.toFront(); this.square.setY(this.square.getY() + Constants.SQUARE_WIDTH); maze[this.getRow()][this.getCol()].addObject(this); } /** * Moves rectangle up by 1 square (30 pixels) */ public void moveUp(MazeSquare[][] maze) { maze[this.getRow()][this.getCol()].removeObject(this); this.square.toFront(); this.square.setY(this.square.getY() - Constants.SQUARE_WIDTH); maze[this.getRow()][this.getCol()].addObject(this); } @Override public boolean isCollectible() { return false; // Energizers and dots are collectible } // Helper methods private int getRowOffset(Direction dir) { switch (dir) { case UP: return -1; case DOWN: return 1; default: return 0; } } private Direction getOppositeDirection(Direction dir) { switch (dir) { case UP: return Direction.DOWN; case DOWN: return Direction.UP; case LEFT: return Direction.RIGHT; case RIGHT: return Direction.LEFT; default: return null; } } private int getColOffset(Direction dir) { switch (dir) { case LEFT: return -1; case RIGHT: return 1; default: return 0; } } private double calculateDistance(BoardCoordinate a, BoardCoordinate b) { int dRow = a.getRow() - b.getRow(); int dCol = a.getColumn() - b.getColumn(); return Math.hypot(dCol, dRow); } public void leavePen(MazeSquare[][] maze, int exitRow, int exitCol) { maze[this.getRow()][this.getCol()].removeObject(this); this.setLocation(exitCol * Constants.SQUARE_WIDTH, exitRow * Constants.SQUARE_WIDTH); maze[exitRow][exitCol].addObject(this); } // Determine if a neighbor is valid private boolean isValidNeighbor(BoardCoordinate neighbor, boolean[][] visited, MazeSquare[][] maze) { int row = neighbor.getRow(); int col = neighbor.getColumn(); int sizeRow = maze.length; int sizeCol = maze[0].length; col = (col + sizeCol) % sizeCol; return row >= 0 && row < sizeRow && maze[row][col].getColor() == (Color.BLACK) // Only valid if not a wall && !visited[row][col]; } // Find the next square in the given direction private BoardCoordinate getNextSquare(int row, int col, Direction direction, MazeSquare[][] maze) { int numRows = maze.length; int numCols = maze[0].length; int nextRow = (row + getRowOffset(direction) + numRows) % numRows; // Ensure positive wrapping int nextCol = (col + getColOffset(direction) + numCols) % numCols; // Ensure positive wrapping // if (nextRow < 0) nextRow += numRows; // if (nextCol < 0) nextCol += numCols; return new BoardCoordinate(nextRow, nextCol, true); } }
Editor is loading...
Leave a Comment