Untitled
unknown
plain_text
a year ago
16 kB
8
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