Untitled

 avatar
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