sp24-proj3-g121 / proj3 / src / core / World.java
World.java
Raw
package core;

import edu.princeton.cs.algs4.StdDraw;
import tileengine.TERenderer;
import tileengine.TETile;
import tileengine.Tileset;

import javax.sound.sampled.*;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Random;

public class World {

    private static final int DEFAULT_WIDTH = 90;
    private static final int DEFAULT_HEIGHT = 51;

    private static final int MIN_ROOM_WIDTH = 6;
    private static final int MIN_ROOM_HEIGHT = 6;
    private static final int MAX_ROOM_WIDTH = 12;
    private static final int MAX_ROOM_HEIGHT = 12;
    private TETile[][] world;
    private TERenderer ter = new TERenderer();
    private long realSeed;
    private Random random;
    private final int numberOfRoomsRequired = 100; /* caps at around 40-50 rooms */
    private final int numberOfTriesPerRoom = 800;
    private ArrayList<Room> roomList;
    private final double MAX_EMPTY_TILES = 0.58;

    private int xAvatar;
    private int yAvatar;

    private File savedWorld;

    private int countsOfRandom;
    private StringBuilder userInput = new StringBuilder();

    private int countMovements;

    private int flowerCount;

    public void generateWorld() {
        world = new TETile[DEFAULT_WIDTH][DEFAULT_HEIGHT];
        loadStartScreen();

        Menu menu = new Menu();
        String seed = "";
        int seedStart = 0;
        int seedEnd = 0;
        while (true) {
            char c = menu.getNextKey();
            if (c == 'N') {
                userInput.append(c);
                c = menu.getNextKey();
                while (c != 'S') {
                    userInput.append(c);
                    c = menu.getNextKey();
                }
                userInput.append(c);
                while (userInput.charAt(seedStart) != 'N') {
                    seedStart++;
                }
                while (userInput.charAt(seedEnd) != 'S') {
                    seedEnd++;
                }
                for (int i = seedStart + 1; i < seedEnd; i++) {
                    seed = seed + userInput.charAt(i);
                }

                realSeed = Long.valueOf(seed);
                random = new Random(realSeed);
                ter.initialize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
                fillWithTiles();
                spawnAvatar();
                hud();
                avatarDisplayer(menu);
            }
            if (c == 'L') {
                loadWorld();
                hud();
                avatarDisplayer(menu);
            }
            if (c == 'M') {
                random = new Random();
                ter.initialize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
                fillWithTiles();
                placeFlower();
                spawnAvatar();
                hud();
                avatarMover(menu);
            }

            if (c == 'Q') {
                System.exit(0);
            }
        }
    }

    private void loadStartScreen() {
        Font font = new Font("Arial", 0, 5 * 5);
        StdDraw.setFont(font);
        StdDraw.text(0.5, (double) (5 * ((5 * 2) + 3)) / (5 * 5 * 4), "CS61B: THE GAME");
        StdDraw.text(0.5, (double) (5 * 9) / (5 * 5 * 4), "New Game (N)");
        StdDraw.text(0.5, (double) (5 * 4 * 2) / (5 * 5 * 4), "Load Game (L)");
        StdDraw.text(0.5, (double) (5 * 7) / (5 * 5 * 4), "Mini Game (M)");
        StdDraw.text(0.5, (double) (5 * 6) / (5 * 5 * 4), "Quit (Q)");
    }

    // same as avatar Displayer but doesn't save world, since its mini-game.
    private void avatarMover(Menu menu) {
        while (true) {
            hud();
            while (!StdDraw.hasNextKeyTyped()) {
                hud();
            }
            char c = menu.getNextKey();
            char capitalizedC = Character.toUpperCase(c);
            if (capitalizedC == 'W') {
                moveForwardMG();
            }
            if (capitalizedC == 'A') {
                moveLeftMG();
            }
            if (capitalizedC == 'S') {
                moveBackMG();
            }
            if (capitalizedC == 'D') {
                moveRightMG();
            }
            if (capitalizedC == ':') {
                c = menu.getNextKey();
                capitalizedC = Character.toUpperCase(c);
                if (capitalizedC == 'Q') {
                    System.exit(0);
                } else {
                    if (capitalizedC == 'W') {
                        moveForwardMG();
                    }
                    if (capitalizedC == 'A') {
                        moveLeftMG();
                    }
                    if (capitalizedC == 'S') {
                        moveBackMG();
                    }
                    if (capitalizedC == 'D') {
                        moveRightMG();
                    }
                }
            }
        }
    }

    private void placeFlower() {
        int xFlowerPlace = random.nextInt(DEFAULT_WIDTH - 1);
        int yFlowerPlace = random.nextInt(DEFAULT_HEIGHT - 1);
        while (flowerCount < (5 * 2)) {
            while (world[xFlowerPlace][yFlowerPlace] != Tileset.FLOOR) {
                xFlowerPlace = random.nextInt(DEFAULT_WIDTH - 1);
                yFlowerPlace = random.nextInt(DEFAULT_HEIGHT - 1);
            }
            flowerCount++;
            world[xFlowerPlace][yFlowerPlace] = Tileset.FLOWER;
        }
    }

    private void fillWithTiles() {
        roomList = new ArrayList<>();

        for (int i = 0; i < DEFAULT_WIDTH; i++) {
            for (int j = 0; j < DEFAULT_HEIGHT; j++) {
                world[i][j] = Tileset.NOTHING;
            }
        }
        createRoomFirst();
        for (int i = 0; i < (5 * 5 * 4); i++) {
            for (int j = 0; j < (5 * 5 * 4 * 4 * 2); j++) {
                Room randomRoom = createRoom();
                int randomIndex;
                int realIndex = 0;
                if (roomList.size() == 1) {
                    randomIndex = 0;
                } else if (roomList.size() == 2) {
                    randomIndex = 1;
                } else {
                    randomIndex = roomList.size() - 1;
                    realIndex = random.nextInt(roomList.size() - 1);
                    countsOfRandom++;
                }
                if (connectableViaHallways(roomList.get(randomIndex), randomRoom)
                        && connectableViaHallways(roomList.get(realIndex), randomRoom)
                        && roomsAreInProximity(roomList.get(roomList.size() - 1), randomRoom)
                        && tilesToBeUsedAreEmpty(randomRoom.getBottomLeft().getxPos(),
                        randomRoom.getBottomLeft().getyPos(),
                        randomRoom.getBottomRight().getxPos(),
                        randomRoom.getTopLeft().getyPos())) {
                    writeRandomRoomToWorld(randomRoom);
                    connectViaHallways(randomRoom, roomList.get(randomIndex));
                    connectViaHallways(randomRoom, roomList.get(realIndex));
                    roomList.add(randomRoom);
                    break;
                }
            }
        }
        double percentOfEmptyTiles = emptyTilesCount();
        if (percentOfEmptyTiles > MAX_EMPTY_TILES) {
            clearWorld();
            fillWithTiles();
        } else {
            ter.renderFrame(world);
        }
    }

    private void writeRandomRoomToWorld(Room randomRoom) {
        for (int i = randomRoom.getBottomLeft().getxPos(); i < randomRoom.getBottomRight().getxPos(); i++) {
            for (int j = randomRoom.getBottomLeft().getyPos(); j < randomRoom.getTopRight().getyPos(); j++) {
                world[i][j] = Tileset.FLOOR;
            }
        }
        for (int i = randomRoom.getBottomLeft().getxPos(); i <= randomRoom.getBottomRight().getxPos(); i++) {
            world[i][randomRoom.getBottomLeft().getyPos()] = Tileset.WALL;
            world[i][randomRoom.getTopLeft().getyPos()] = Tileset.WALL;
        }
        for (int j = randomRoom.getBottomLeft().getyPos(); j <= randomRoom.getTopLeft().getyPos(); j++) {
            world[randomRoom.getBottomLeft().getxPos()][j] = Tileset.WALL;
            world[randomRoom.getBottomRight().getxPos()][j] = Tileset.WALL;
        }
    }

    private void createRoomFirst() {
        int minRoomWidth = MIN_ROOM_WIDTH;
        int maxRoomWidth = MAX_ROOM_WIDTH;
        int minRoomHeight = MIN_ROOM_HEIGHT;
        int maxRoomHeight = MAX_ROOM_HEIGHT;
        int roomWidth = random.nextInt(maxRoomWidth - minRoomWidth + 1) + minRoomWidth;
        countsOfRandom++;
        int roomHeight = random.nextInt(maxRoomHeight - minRoomHeight + 1) + minRoomHeight;
        countsOfRandom++;
        ArrayList<Coordinates> retrievedCoordinates = retrieveCoordinates(roomWidth, roomHeight);
        Coordinates topLeft = retrievedCoordinates.get(0);
        Coordinates topRight = retrievedCoordinates.get(1);
        Coordinates bottomLeft = retrievedCoordinates.get(2);
        Coordinates bottomRight = retrievedCoordinates.get(3);

        Room roomFirst = new Room(roomWidth, roomHeight, topLeft, topRight, bottomLeft, bottomRight);
        roomList.add(roomFirst);

        /* set tiles of room to floors */
        for (int i = roomFirst.getBottomLeft().getxPos(); i <= roomFirst.getBottomRight().getxPos(); i++) {
            for (int j = roomFirst.getBottomLeft().getyPos(); j <= roomFirst.getTopLeft().getyPos(); j++) {
                world[i][j] = Tileset.FLOOR;
            }
        }

        /* set edges of room to walls */
        int yBottom = roomFirst.getBottomLeft().getyPos();
        int yTop = roomFirst.getTopLeft().getyPos();
        for (int i = roomFirst.getBottomLeft().getxPos(); i <= roomFirst.getBottomRight().getxPos(); i++) {
            world[i][yBottom] = Tileset.WALL;
            world[i][yTop] = Tileset.WALL;
        }

        int xLeft = roomFirst.getBottomLeft().getxPos();
        int xRight = roomFirst.getBottomRight().getxPos();
        for (int j = roomFirst.getBottomLeft().getyPos(); j <= roomFirst.getTopLeft().getyPos(); j++) {
            world[xLeft][j] = Tileset.WALL;
            world[xRight][j] = Tileset.WALL;
        }
    }

    private ArrayList<Coordinates> retrieveCoordinates(int widthOfRoom, int heightOfRoom) {
        ArrayList<Coordinates> coordinatesArrayList = new ArrayList<>();
        Coordinates topLeft;
        Coordinates topRight;
        Coordinates bottomLeft;
        Coordinates bottomRight;
        int randomPlace = random.nextInt(4);
        countsOfRandom++;
        /* room goes to any of the following positions p0, p1, p2, or p3
        p2            p3


        p0            p1
        */
        if (randomPlace == 0) {
            bottomLeft = new Coordinates(1, 1);
            bottomRight = new Coordinates(1 + widthOfRoom, 1);
            topLeft = new Coordinates(1, 1 + heightOfRoom);
            topRight = new Coordinates(1 + widthOfRoom, 1 + heightOfRoom);
        } else if (randomPlace == 1) {
            bottomLeft = new Coordinates(DEFAULT_WIDTH - widthOfRoom - 1, 1);
            bottomRight = new Coordinates(DEFAULT_WIDTH - 1, 1);
            topLeft = new Coordinates(DEFAULT_WIDTH - widthOfRoom - 1, 1 + heightOfRoom);
            topRight = new Coordinates(DEFAULT_WIDTH - 1, 1 + heightOfRoom);
        } else if (randomPlace == 2) {
            bottomLeft = new Coordinates(1, DEFAULT_HEIGHT - heightOfRoom - 1);
            bottomRight = new Coordinates(1 + widthOfRoom, DEFAULT_HEIGHT - widthOfRoom - 1);
            topLeft = new Coordinates(1, DEFAULT_HEIGHT - 1);
            topRight = new Coordinates(1 + widthOfRoom, DEFAULT_HEIGHT - 1);
        } else {
            bottomLeft = new Coordinates(DEFAULT_WIDTH - widthOfRoom - 1, DEFAULT_HEIGHT - heightOfRoom - 1);
            bottomRight = new Coordinates(DEFAULT_WIDTH - 1, DEFAULT_HEIGHT - heightOfRoom - 1);
            topLeft = new Coordinates(DEFAULT_WIDTH - widthOfRoom - 1, DEFAULT_HEIGHT - 1);
            topRight = new Coordinates(DEFAULT_WIDTH - 1, DEFAULT_HEIGHT - 1);
        }
        coordinatesArrayList.add(topLeft);
        coordinatesArrayList.add(topRight);
        coordinatesArrayList.add(bottomLeft);
        coordinatesArrayList.add(bottomRight);
        return coordinatesArrayList;
    }

    private Room createRoom() {
        int minRoomWidth = MIN_ROOM_WIDTH;
        int maxRoomWidth = MAX_ROOM_WIDTH;
        int minRoomHeight = MIN_ROOM_HEIGHT;
        int maxRoomHeight = MAX_ROOM_HEIGHT;
        int roomWidth = random.nextInt(maxRoomWidth - minRoomWidth + 1) + minRoomWidth;
        countsOfRandom++;
        int roomHeight = random.nextInt(maxRoomHeight - minRoomHeight + 1) + minRoomHeight;
        countsOfRandom++;

        int xBottomLeft = 0;
        int yBottomLeft = 0;
        while (xBottomLeft < 1 || xBottomLeft >= DEFAULT_WIDTH - roomWidth - 1) {
            xBottomLeft = random.nextInt(DEFAULT_WIDTH - roomWidth - 1);
            countsOfRandom++;
        }
        while (yBottomLeft < 1 || yBottomLeft >= DEFAULT_HEIGHT - roomHeight - 1) {
            yBottomLeft = random.nextInt(DEFAULT_WIDTH - roomWidth - 1);
            countsOfRandom++;
        }
        int xTopLeft = xBottomLeft;
        int yBottomRight = yBottomLeft;
        int xBottomRight = xBottomLeft + roomWidth;
        int xTopRight = xBottomRight;
        int yTopLeft = yBottomLeft + roomHeight;
        int yTopRight = yTopLeft;
        Coordinates topLeft = new Coordinates(xTopLeft, yTopLeft);
        Coordinates topRight = new Coordinates(xTopRight, yTopRight);
        Coordinates bottomLeft = new Coordinates(xBottomLeft, yBottomLeft);
        Coordinates bottomRight = new Coordinates(xBottomRight, yBottomRight);

        Room newRoom = new Room(roomWidth, roomHeight, topLeft, topRight, bottomLeft, bottomRight);
        return newRoom;
    }

    private boolean tilesToBeUsedAreEmpty(int xBLeft, int yBLeft, int xBRight, int yTLeft) {
        for (int i = xBLeft - 1; i <= xBRight + 1; i++) {
            for (int j = yBLeft - 1; j <= yTLeft + 1; j++) {
                if (world[i][j] != Tileset.NOTHING) {
                    return false;
                }
            }
        }
        return true;
    }

    private boolean connectableViaHallways(Room r1, Room r2) {
        Coordinates r1Center = new Coordinates(getCenterX(r1), getCenterY(r1));
        Coordinates r2Center = new Coordinates(getCenterX(r2), getCenterY(r2));

        int pathLengthX = Math.abs(r1Center.getxPos() - r2Center.getxPos());
        int pathLengthY = Math.abs(r1Center.getyPos() - r2Center.getyPos());

        Room begin = findBegin(r1, r2).get(0);
        Room end = findBegin(r1, r2).get(1);

        if (pathLengthY != 0 && pathLengthX != 0) {
            if (getCenterY(end) < getCenterY(begin)) {
                for (int x = getCenterX(begin) + begin.getWidth() / 2 + 1; x < getCenterX(end); x++) {
                    if (world[x][getCenterY(begin)] != Tileset.NOTHING
                            || world[x][getCenterY(begin) - 1] != Tileset.NOTHING
                            || world[x][getCenterY(begin) + 1] != Tileset.NOTHING) {
                        return false;
                    }
                }
                for (int y = getCenterY(begin); y <= getCenterY(end) - end.getHeight() / 2 - 1; y++) {
                    if (world[getCenterX(begin) - 1][y] != Tileset.NOTHING
                            || world[getCenterX(begin) + 1][y] != Tileset.NOTHING
                            || world[getCenterX(begin)][y] != Tileset.NOTHING) {
                        return false;
                    }
                }
                if (world[getCenterX(end)][getCenterY(begin) - 1] != Tileset.NOTHING) {
                    return false;
                }
                if (world[getCenterX(end) + 1][getCenterY(begin) - 1] != Tileset.NOTHING) {
                    return false;
                }
                return true;
            } else {
                for (int x = getCenterX(begin) + begin.getWidth() / 2 + 1; x < getCenterX(end); x++) {
                    if (world[x][getCenterY(begin)] != Tileset.NOTHING
                            || world[x][getCenterY(begin) - 1] != Tileset.NOTHING
                            || world[x][getCenterY(begin) + 1] != Tileset.NOTHING) {
                        return false;
                    }
                }
                for (int y = getCenterY(end) + end.getHeight() / 2 + 1; y <= getCenterY(begin); y++) {
                    if (world[getCenterX(end) - 1][y] != Tileset.NOTHING
                            || world[getCenterX(end) + 1][y] != Tileset.NOTHING
                            || world[getCenterX(end)][y] != Tileset.NOTHING) {
                        return false;
                    }
                }
                if (world[getCenterX(end)][getCenterY(begin) + 1] != Tileset.NOTHING) {
                    return false;
                }
                if (world[getCenterX(end) + 1][getCenterY(begin) + 1] != Tileset.NOTHING) {
                    return false;
                }
                return true;
            }
        } else if (pathLengthX == 0 && pathLengthY != 0) {
            for (int y = getCenterY(end) + end.getHeight() / 2 + 1;
                 y < getCenterY(begin) - begin.getHeight() / 2; y++) {
                if (world[pathLengthX - 1][y] != Tileset.NOTHING
                        || world[pathLengthX + 1][y] != Tileset.NOTHING
                        || world[pathLengthX][y] != Tileset.NOTHING) {
                    return false;
                }
            }
            return true;
        } else {
            for (int x = getCenterX(begin) + begin.getWidth() / 2 + 1;
                 x < getCenterX(end) - end.getWidth() / 2; x++) {
                if (world[x][getCenterY(end)] != Tileset.NOTHING
                        || world[x][getCenterY(end) - 1] != Tileset.NOTHING
                        || world[x][getCenterY(end) + 1] != Tileset.NOTHING) {
                    return false;
                }
            }
            return true;
        }
    }

    private void connectViaHallways(Room r1, Room r2) {
        Coordinates r1Center = new Coordinates(getCenterX(r1), getCenterY(r1));
        Coordinates r2Center = new Coordinates(getCenterX(r2), getCenterY(r2));

        int pathLengthX = Math.abs(r1Center.getxPos() - r2Center.getxPos());
        int pathLengthY = Math.abs(r1Center.getyPos() - r2Center.getyPos());

        Room begin = findBegin(r1, r2).get(0);
        Room end = findBegin(r1, r2).get(1);

        if (pathLengthX != 0 && pathLengthY != 0) {
            if (getCenterY(end) > getCenterY(begin)) {
                markTileForWorld(begin, end);
                markSecondTileForWorld(begin, end);

                world[getCenterX(end) - 1][getCenterY(begin)] = Tileset.FLOOR;
                if (world[getCenterX(end) + 1][getCenterY(begin) - 1] != Tileset.FLOOR) {
                    world[getCenterX(end) + 1][getCenterY(begin) - 1] = Tileset.WALL;
                }
                if (world[getCenterX(end)][getCenterY(begin) - 1] != Tileset.FLOOR) {
                    world[getCenterX(end)][getCenterY(begin) - 1] = Tileset.WALL;
                }
            } else {
                for (int x = getCenterX(begin) + begin.getWidth() / 2; x < getCenterX(end); x++) {
                    world[x][getCenterY(begin)] = Tileset.FLOOR;
                    if (world[x][getCenterY(begin) - 1] != Tileset.FLOOR) {
                        world[x][getCenterY(begin) - 1] = Tileset.WALL;
                    }
                    if (world[x][getCenterY(begin) + 1] != Tileset.FLOOR) {
                        world[x][getCenterY(begin) + 1] = Tileset.WALL;
                    }
                }
                for (int y = getCenterY(end) + end.getHeight() / 2; y <= getCenterY(begin); y++) {
                    world[getCenterX(end)][y] = Tileset.FLOOR;
                    if (world[getCenterX(end) - 1][y] != Tileset.FLOOR) {
                        world[getCenterX(end) - 1][y] = Tileset.WALL;
                    }
                    if (world[getCenterX(end) + 1][y] != Tileset.FLOOR) {
                        world[getCenterX(end) + 1][y] = Tileset.WALL;
                    }
                }
                world[getCenterX(end) - 1][getCenterY(begin)] = Tileset.FLOOR;
                if (world[getCenterX(end) + 1][getCenterY(begin) + 1] != Tileset.FLOOR) {
                    world[getCenterX(end) + 1][getCenterY(begin) + 1] = Tileset.WALL;
                }
                if (world[getCenterX(end)][getCenterY(begin) + 1] != Tileset.FLOOR) {
                    world[getCenterX(end)][getCenterY(begin) + 1] = Tileset.WALL;
                }
            }
        } else if (pathLengthX == 0 && pathLengthY != 0) {
            for (int y = getCenterY(end) + end.getHeight() / 2; y <= getCenterY(begin) - begin.getHeight() / 2; y++) {
                world[getCenterX(r1)][y] = Tileset.FLOOR;
                if (world[getCenterX(r1) - 1][y] != Tileset.FLOOR) {
                    world[getCenterX(r1) - 1][y] = Tileset.WALL;
                }
                if (world[getCenterX(r1) + 1][y] != Tileset.FLOOR) {
                    world[getCenterX(r1) + 1][y] = Tileset.WALL;
                }
            }
        } else {
            for (int x = getCenterX(begin) + begin.getWidth() / 2;
                 x <= getCenterX(end) - end.getWidth() / 2; x++) {
                world[x][getCenterY(r2)] = Tileset.FLOOR;
                if (world[x][getCenterY(r2) - 1] != Tileset.FLOOR) {
                    world[x][getCenterY(r2) - 1] = Tileset.WALL;
                }
                if (world[x][getCenterY(r2) + 1] != Tileset.FLOOR) {
                    world[x][getCenterY(r2) + 1] = Tileset.WALL;
                }
            }
        }
    }

    private void markSecondTileForWorld(Room begin, Room end) {
        for (int y = getCenterY(begin); y <= getCenterY(end) - end.getHeight() / 2; y++) {
            world[getCenterX(end)][y] = Tileset.FLOOR;
            if (world[getCenterX(end) - 1][y] != Tileset.FLOOR) {
                world[getCenterX(end) - 1][y] = Tileset.WALL;
            }
            if (world[getCenterX(end) + 1][y] != Tileset.FLOOR) {
                world[getCenterX(end) + 1][y] = Tileset.WALL;
            }
        }
    }

    private void markTileForWorld(Room begin, Room end) {
        for (int x = getCenterX(begin) + begin.getWidth() / 2; x < getCenterX(end); x++) {
            world[x][getCenterY(begin)] = Tileset.FLOOR;
            if (world[x][getCenterY(begin) - 1] != Tileset.FLOOR) {
                world[x][getCenterY(begin) - 1] = Tileset.WALL;
            }
            if (world[x][getCenterY(begin) + 1] != Tileset.FLOOR) {
                world[x][getCenterY(begin) + 1] = Tileset.WALL;
            }
        }
    }

    private boolean roomsAreInProximity(Room r1, Room r2) {
        int r1CenterX = getCenterX(r1);
        int r1CenterY = getCenterY(r1);

        int r2CenterX = getCenterX(r2);
        int r2CenterY = getCenterY(r2);

        // @Source CK-12 distance between 2 points formula
        double shortestDistance = Math.sqrt(Math.pow(r2CenterX - r1CenterX, 2) + Math.pow(r2CenterY - r1CenterY, 2));

        return (shortestDistance < (DEFAULT_WIDTH / 6));
    }

    private int getCenterX(Room r) {
        return r.getBottomLeft().getxPos() + (Math.floorDiv(r.getWidth(), 2));
    }

    private int getCenterY(Room r) {
        return r.getBottomLeft().getyPos() + (Math.floorDiv(r.getHeight(), 2));
    }

    private double emptyTilesCount() {
        double emptyTiles = 0;
        double totalTiles = 0;
        for (int i = 0; i < DEFAULT_WIDTH; i++) {
            for (int j = 0; j < DEFAULT_HEIGHT; j++) {
                totalTiles++;
                if (world[i][j] == Tileset.NOTHING) {
                    emptyTiles++;
                }
            }
        }
        return emptyTiles / totalTiles;
    }

    private void clearWorld() {
        for (int i = 0; i < DEFAULT_WIDTH; i++) {
            for (int j = 0; j < DEFAULT_HEIGHT; j++) {
                world[i][j] = Tileset.NOTHING;
            }
        }
    }

    private ArrayList<Room> findBegin(Room r1, Room r2) {
        ArrayList<Room> outputRooms = new ArrayList<>();
        Room begin;
        Room end;
        if (getCenterX(r1) < getCenterX(r2)) {
            begin = r1;
            end = r2;
        } else if (getCenterX(r1) > getCenterX(r2)) {
            begin = r2;
            end = r1;
        } else {
            if (getCenterY(r1) < getCenterY(r2)) {
                begin = r1;
                end = r2;
            } else {
                begin = r2;
                end = r1;
            }
        }
        outputRooms.add(begin);
        outputRooms.add(end);
        return outputRooms;
    }

    private void spawnAvatar() {
        int x = random.nextInt(DEFAULT_WIDTH - 1);
        countsOfRandom++;
        int y = random.nextInt(DEFAULT_HEIGHT - 1);
        countsOfRandom++;

        while (world[x][y] != Tileset.FLOOR) {
            x = random.nextInt(DEFAULT_WIDTH - 1);
            countsOfRandom++;
            y = random.nextInt(DEFAULT_HEIGHT - 1);
            countsOfRandom++;
        }
        xAvatar = x;
        yAvatar = y;
        world[x][y] = Tileset.AVATAR;
        ter.renderFrame(world);
    }

    private void avatarDisplayer(Menu menu) {
        while (true) {
            hud();
            while (!StdDraw.hasNextKeyTyped()) {
                hud();
            }
            char c = menu.getNextKey();
            char capitalizedC = Character.toUpperCase(c);
            if (capitalizedC == 'W') {
                moveForward();
            }
            if (capitalizedC == 'A') {
                moveLeft();
            }
            if (capitalizedC == 'S') {
                moveBack();
            }
            if (capitalizedC == 'D') {
                moveRight();
            }
            if (capitalizedC == ':') {
                c = menu.getNextKey();
                capitalizedC = Character.toUpperCase(c);
                if (capitalizedC == 'Q') {
                    saveWorld();
                    System.exit(0);
                } else {
                    if (capitalizedC == 'W') {
                        moveForward();
                    }
                    if (capitalizedC == 'A') {
                        moveLeft();
                    }
                    if (capitalizedC == 'S') {
                        moveBack();
                    }
                    if (capitalizedC == 'D') {
                        moveRight();
                    }
                }
            }
        }
    }

    private void saveWorld() {
        savedWorld = new File("save.txt");
        try {
            FileWriter fw = new FileWriter(savedWorld);
            BufferedWriter bw = new BufferedWriter(fw);
            bw.write(String.valueOf(realSeed));
            bw.write("\n");
            // 0 = WALL, 1 = FLOOR, 2 = AVATAR
            for (int y = DEFAULT_HEIGHT - 1; y >= 0; y--) {
                StringBuilder line = getStringBuilder(y);
                bw.write(String.valueOf(line));
                bw.write("\n");
            }
            bw.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private StringBuilder getStringBuilder(int y) {
        StringBuilder line = new StringBuilder();
        for (int x = 0; x < DEFAULT_WIDTH; x++) {
            if (world[x][y] == Tileset.WALL) {
                line.append("0");
            } else if (world[x][y] == Tileset.FLOOR) {
                line.append("1");
            } else if (world[x][y] == Tileset.AVATAR) {
                line.append("2");
            } else {
                line.append("3");
            }
        }
        return line;
    }

    private void loadWorld() {
        File worldFile = new File("save.txt");
        if (worldFile.exists()) {
            try {
                ter.initialize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
                FileReader fr = new FileReader(worldFile);
                BufferedReader br = new BufferedReader(fr);
                realSeed = Long.parseLong(br.readLine());
                random = new Random(realSeed);

                for (int i = 0; i <= countsOfRandom; i++) {
                    random.nextInt();
                }

                int aX = 0;
                int aY = 0;
                for (int j = DEFAULT_HEIGHT - 1; j >= 0; j--) {
                    String line = br.readLine();

                    for (int i = 0; i < line.length(); i++) {
                        if (line.charAt(i) == '0') {
                            world[i][j] = Tileset.WALL;
                        } else if (line.charAt(i) == '1') {
                            world[i][j] = Tileset.FLOOR;
                        } else if (line.charAt(i) == '2') {
                            world[i][j] = Tileset.AVATAR;
                            aX = i;
                            aY = j;
                        } else {
                            world[i][j] = Tileset.NOTHING;
                        }
                    }
                }
                xAvatar = aX;
                yAvatar = aY;
                ter.renderFrame(world);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            System.exit(0);
        }
    }

    private void moveForward() {
        if (world[xAvatar][yAvatar + 1] == Tileset.FLOOR) {
            countMovements++;
            world[xAvatar][yAvatar + 1] = Tileset.AVATAR;
            world[xAvatar][yAvatar] = Tileset.FLOOR;
            yAvatar++;
            ter.renderFrame(world);
            if (countMovements % (5 * 2) == 0) {
                playSound();
            }
        }
    }

    private void moveLeft() {
        if (world[xAvatar - 1][yAvatar] == Tileset.FLOOR) {
            countMovements++;
            world[xAvatar - 1][yAvatar] = Tileset.AVATAR;
            world[xAvatar][yAvatar] = Tileset.FLOOR;
            xAvatar--;
            ter.renderFrame(world);
            if (countMovements % (5 * 2) == 0) {
                playSound();
            }
        }
    }

    private void moveBack() {
        if (world[xAvatar][yAvatar - 1] == Tileset.FLOOR) {
            countMovements++;
            world[xAvatar][yAvatar - 1] = Tileset.AVATAR;
            world[xAvatar][yAvatar] = Tileset.FLOOR;
            yAvatar--;
            ter.renderFrame(world);
            if (countMovements % (5 * 2) == 0) {
                playSound();
            }
        }
    }

    private void moveRight() {
        if (world[xAvatar + 1][yAvatar] == Tileset.FLOOR) {
            countMovements++;
            world[xAvatar + 1][yAvatar] = Tileset.AVATAR;
            world[xAvatar][yAvatar] = Tileset.FLOOR;
            xAvatar++;
            ter.renderFrame(world);
            if (countMovements % (5 * 2) == 0) {
                playSound();
            }
        }
    }

    private void hud() {
        int mouseX = (int) StdDraw.mouseX();
        int mouseY = (int) StdDraw.mouseY();
        StdDraw.setPenColor(StdDraw.BLACK);
        StdDraw.filledRectangle(2, DEFAULT_HEIGHT - 1, 3, 1);

        StdDraw.setPenColor(Color.WHITE);
        if (mouseY < DEFAULT_HEIGHT - 1) {
            StdDraw.text(2, DEFAULT_HEIGHT - 1, world[mouseX][mouseY].description());
        } else {
            StdDraw.text(3, DEFAULT_HEIGHT - 1, "nothing");
        }
        StdDraw.show();
    }

    public static void playSound() {
        try {
            File soundPath = new File("/Users/ayush/Desktop/cs61b/sp24-proj3-g121/proj3/src/core/hog-rider.wav");
            AudioInputStream audioInput = AudioSystem.getAudioInputStream(soundPath);
            Clip clip = AudioSystem.getClip();
            clip.open(audioInput);
            clip.start();

        } catch (UnsupportedAudioFileException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (LineUnavailableException e) {
            throw new RuntimeException(e);
        }
    }

    private void moveForwardMG() {
        if (world[xAvatar][yAvatar + 1] == Tileset.FLOOR) {
            world[xAvatar][yAvatar + 1] = Tileset.AVATAR;
            world[xAvatar][yAvatar] = Tileset.FLOOR;
            yAvatar++;
            ter.renderFrame(world);
        } else if (world[xAvatar][yAvatar + 1] == Tileset.FLOWER) {
            world[xAvatar][yAvatar + 1] = Tileset.AVATAR;
            world[xAvatar][yAvatar] = Tileset.FLOOR;
            yAvatar++;
            flowerCount--;
            ter.renderFrame(world);
            if (flowerCount <= 0) {
                miniGameOver();
            }
        }
    }

    private void moveLeftMG() {
        if (world[xAvatar - 1][yAvatar] == Tileset.FLOOR) {
            world[xAvatar - 1][yAvatar] = Tileset.AVATAR;
            world[xAvatar][yAvatar] = Tileset.FLOOR;
            xAvatar--;
            ter.renderFrame(world);
        } else if (world[xAvatar - 1][yAvatar] == Tileset.FLOWER) {
            world[xAvatar - 1][yAvatar] = Tileset.AVATAR;
            world[xAvatar][yAvatar] = Tileset.FLOOR;
            xAvatar--;
            flowerCount--;
            ter.renderFrame(world);
            if (flowerCount <= 0) {
                miniGameOver();
            }
        }
    }

    private void moveBackMG() {
        if (world[xAvatar][yAvatar - 1] == Tileset.FLOOR) {
            world[xAvatar][yAvatar - 1] = Tileset.AVATAR;
            world[xAvatar][yAvatar] = Tileset.FLOOR;
            yAvatar--;
            ter.renderFrame(world);
        } else if (world[xAvatar][yAvatar - 1] == Tileset.FLOWER) {
            world[xAvatar][yAvatar - 1] = Tileset.AVATAR;
            world[xAvatar][yAvatar] = Tileset.FLOOR;
            yAvatar--;
            flowerCount--;
            ter.renderFrame(world);
            if (flowerCount <= 0) {
                miniGameOver();
            }
        }
    }

    private void moveRightMG() {
        if (world[xAvatar + 1][yAvatar] == Tileset.FLOOR) {
            world[xAvatar + 1][yAvatar] = Tileset.AVATAR;
            world[xAvatar][yAvatar] = Tileset.FLOOR;
            xAvatar++;
            ter.renderFrame(world);
        } else if (world[xAvatar + 1][yAvatar] == Tileset.FLOWER) {
            world[xAvatar + 1][yAvatar] = Tileset.AVATAR;
            world[xAvatar][yAvatar] = Tileset.FLOOR;
            xAvatar++;
            flowerCount--;
            ter.renderFrame(world);
            if (flowerCount <= 0) {
                miniGameOver();
            }
        }
    }

    private void miniGameOver() {
        JOptionPane.showMessageDialog(null, "You Win!");
        StdDraw.pause((5 * 2) * (5 * 2) * (5 * 2));
        System.exit(0);
    }
}