UNPKG

maze-generation

Version:

A package to generate mazes using the depth first or hunt and kill algorithm. Mazes can be generated with seed values for reproducibility

412 lines (411 loc) 18.5 kB
import { Maze } from "../Maze"; /* eslint-env jest */ describe("Maze created with the correct dimension", () => { it("is create correctly when dimensions are 1 by 1", () => { const maze1 = new Maze(1, 1); expect(maze1.cells.length).toBe(1); expect(maze1.cells[0].length).toBe(1); }); it("is create correctly when dimensions are 5 by 5", () => { const maze2 = new Maze(5, 5); expect(maze2.cells.length).toBe(5); expect(maze2.cells[0].length).toBe(5); }); it("is create correctly when dimensions are 100 by 50", () => { const maze3 = new Maze(100, 50); expect(maze3.cells.length).toBe(50); expect(maze3.cells[0].length).toBe(100); }); }); describe("Removing cell walls", () => { let maze; beforeEach(() => { maze = new Maze(3, 3); }); it("Can remove cell wall", () => { maze.removeWall(0, 0, "up"); expect(maze.getWallStatus(0, 0, "up")).toBe(false); }); it("Removing the right wall of a cell removes the left of the next", () => { maze.removeWall(0, 1, "right"); expect(maze.getWallStatus(0, 1, "right")).toBe(false); expect(maze.getWallStatus(0, 2, "left")).toBe(false); }); it("Removing the right wall of an outer cell works successfully", () => { maze.removeWall(0, 2, "right"); expect(maze.getWallStatus(0, 2, "right")).toBe(false); }); it("Removing the left wall of a cell removes the right of the previous", () => { maze.removeWall(0, 1, "left"); expect(maze.getWallStatus(0, 1, "left")).toBe(false); expect(maze.getWallStatus(0, 0, "right")).toBe(false); }); it("Removing the left wall of an outer cell works successfully", () => { maze.removeWall(0, 0, "left"); expect(maze.getWallStatus(0, 0, "left")).toBe(false); }); it("Removing the top wall of a cell removes the bottom cell of the one above", () => { maze.removeWall(2, 1, "up"); expect(maze.getWallStatus(2, 1, "up")).toBe(false); expect(maze.getWallStatus(1, 1, "down")).toBe(false); }); it("Removing the top wall of an outer cell works successfully", () => { maze.removeWall(0, 2, "up"); expect(maze.getWallStatus(0, 2, "up")).toBe(false); }); it("Removing the bottom wall of a cell removes the top cell of the one below", () => { maze.removeWall(0, 2, "down"); expect(maze.getWallStatus(0, 2, "down")).toBe(false); expect(maze.getWallStatus(1, 2, "up")).toBe(false); }); it("Removing the bottom wall of an out cell works successfully", () => { maze.removeWall(2, 2, "down"); expect(maze.getWallStatus(2, 2, "down")).toBe(false); }); }); describe("Visiting cells", () => { it("Can visit cells", () => { const maze = new Maze(3, 3); expect(maze.getCellVisited(0, 0)).toBe(false); maze.visitCell(0, 0); expect(maze.getCellVisited(0, 0)).toBe(true); }); }); describe("String representaion", () => { let maze; beforeEach(() => { maze = new Maze(3, 3); }); it("Maze to string representation to be correct on creation", () => { expect(maze.toString()).toBe(" _ _ _\n|_|_|_|\n|_|_|_|\n|_|_|_|"); }); it("Maze to string representation to be correct when removing wall", () => { maze.removeWall(0, 0, "right"); expect(maze.toString()).toBe(" _ _ _\n|_ _|_|\n|_|_|_|\n|_|_|_|"); }); it("Maze to string representation to be correct when removing multiple walls", () => { maze.removeWall(0, 0, "right"); maze.removeWall(1, 1, "up"); maze.removeWall(1, 1, "down"); expect(maze.toString()).toBe(" _ _ _\n|_ |_|\n|_| |_|\n|_|_|_|"); }); test("Maze to string represenation is correct if top walls are removed", () => { maze.removeWall(0, 0, "up"); maze.removeWall(2, 2, "down"); expect(maze.toString()).toBe(" _ _\n|_|_|_|\n|_|_|_|\n|_|_| |"); }); }); describe("Cell neighbours", () => { let maze; beforeEach(() => { maze = new Maze(3, 3); }); describe("Get cell neighbour indicies", () => { it("Can get all neighbours indicies if in centre", () => { const actual = maze.getCellNeighbourIndices(1, 1); if (!actual.up || !actual.down || !actual.left || !actual.right) { throw Error("expected actual.right, actual.left, actual.up and actual.down to be defined"); } expect(actual.up.y).toBe(0); expect(actual.up.x).toBe(1); expect(actual.down.y).toBe(2); expect(actual.down.x).toBe(1); expect(actual.left.y).toBe(1); expect(actual.left.x).toBe(0); expect(actual.right.y).toBe(1); expect(actual.right.x).toBe(2); }); it("Right cell is undefined if cell furthest right", () => { const actual = maze.getCellNeighbourIndices(1, 2); if (!actual.left) { throw Error("expected actual defined"); } expect(actual.right).toBeUndefined(); expect(actual.left.y).toEqual(1); expect(actual.left.x).toEqual(1); }); it("Left cell is undefined if cell furthest Left", () => { const actual = maze.getCellNeighbourIndices(1, 0); if (!actual.right) { throw Error("expected actual.right to be defined"); } expect(actual.left).toBeUndefined(); expect(actual.right.y).toEqual(1); expect(actual.right.x).toEqual(1); }); it("Up cell is undefined if cell furthest up", () => { const actual = maze.getCellNeighbourIndices(0, 1); if (!actual.down) { throw Error("expected actual.down to be defined"); } expect(actual.up).toBeUndefined(); expect(actual.down.y).toEqual(1); expect(actual.down.x).toEqual(1); }); it("Down cell is undefined if cell furthest down", () => { const actual = maze.getCellNeighbourIndices(2, 1); if (!actual.up) { throw Error("expected actual.up to be defined"); } expect(actual.down).toBeUndefined(); expect(actual.up.y).toEqual(1); expect(actual.up.x).toEqual(1); }); }); }); describe("Unvisited Neighbour Indicies", () => { let maze; beforeEach(() => { maze = new Maze(3, 3); }); it("Centre cell gets 4 unvisited neighbours on construction", () => { const unvisitedNeighboursIndicies = maze.getUnvisitedNeigbourIndices(1, 1); expect(unvisitedNeighboursIndicies.length).toBe(4); }); it("Outer cell gets 3 unvisited neighbours on construction", () => { const unvisitedNeighboursIndicies = maze.getUnvisitedNeigbourIndices(0, 1); expect(unvisitedNeighboursIndicies.length).toBe(3); }); it("Corner cell gets 2 unvisited neighbours on construction", () => { const unvisitedNeighboursIndicies = maze.getUnvisitedNeigbourIndices(0, 0); expect(unvisitedNeighboursIndicies.length).toBe(2); }); it("Unvisited cell indicies length shortens as more neighbours are visited", () => { let unvisitedNeighboursIndicies = maze.getUnvisitedNeigbourIndices(1, 1); expect(unvisitedNeighboursIndicies.length).toBe(4); let nextCell = unvisitedNeighboursIndicies.pop(); if (!nextCell) { throw new Error("Expected nextCell to be defined"); } maze.visitCell(nextCell.y, nextCell.x); unvisitedNeighboursIndicies = maze.getUnvisitedNeigbourIndices(1, 1); expect(unvisitedNeighboursIndicies.length).toBe(3); nextCell = unvisitedNeighboursIndicies.pop(); if (!nextCell) { throw new Error("Expected nextCell to be defined"); } maze.visitCell(nextCell.y, nextCell.x); if (!nextCell) { throw new Error("Expected nextCell to be defined"); } unvisitedNeighboursIndicies = maze.getUnvisitedNeigbourIndices(1, 1); expect(unvisitedNeighboursIndicies.length).toBe(2); nextCell = unvisitedNeighboursIndicies.pop(); if (!nextCell) { throw new Error("Expected nextCell to be defined"); } maze.visitCell(nextCell.y, nextCell.x); unvisitedNeighboursIndicies = maze.getUnvisitedNeigbourIndices(1, 1); expect(unvisitedNeighboursIndicies.length).toBe(1); nextCell = unvisitedNeighboursIndicies.pop(); if (!nextCell) { throw new Error("Expected nextCell to be defined"); } maze.visitCell(nextCell.y, nextCell.x); unvisitedNeighboursIndicies = maze.getUnvisitedNeigbourIndices(1, 1); expect(unvisitedNeighboursIndicies.length).toBe(0); nextCell = unvisitedNeighboursIndicies.pop(); expect(nextCell).toBeUndefined(); }); }); describe("Visited Neighbour Indicies", () => { let maze; beforeEach(() => { maze = new Maze(3, 3); }); it("Centre cell gets 0 visited neighbours on construction", () => { const visitedNeighboursIndicies = maze.getVisitedNeigbourIndices(1, 1); expect(visitedNeighboursIndicies.length).toBe(0); }); it("Outer cell gets 0 visited neighbours on construction", () => { const visitedNeighboursIndicies = maze.getVisitedNeigbourIndices(0, 1); expect(visitedNeighboursIndicies.length).toBe(0); }); it("Corner cell gets 0 visited neighbours on construction", () => { const visitedNeighboursIndicies = maze.getVisitedNeigbourIndices(0, 0); expect(visitedNeighboursIndicies.length).toBe(0); }); it("Visited cell indicies length grows as more neighbours are visited", () => { let visitedNeighboursIndicies = maze.getVisitedNeigbourIndices(1, 1); expect(visitedNeighboursIndicies.length).toBe(0); const nextCell = visitedNeighboursIndicies.pop(); expect(nextCell).toBeUndefined(); maze.visitCell(0, 1); visitedNeighboursIndicies = maze.getVisitedNeigbourIndices(1, 1); expect(visitedNeighboursIndicies.length).toBe(1); maze.visitCell(2, 1); visitedNeighboursIndicies = maze.getVisitedNeigbourIndices(1, 1); expect(visitedNeighboursIndicies.length).toBe(2); maze.visitCell(1, 0); visitedNeighboursIndicies = maze.getVisitedNeigbourIndices(1, 1); expect(visitedNeighboursIndicies.length).toBe(3); maze.visitCell(1, 2); visitedNeighboursIndicies = maze.getVisitedNeigbourIndices(1, 1); expect(visitedNeighboursIndicies.length).toBe(4); }); }); describe("First cell with an unvisited neighbour", () => { let maze; const width = 3; const height = 3; beforeEach(() => { maze = new Maze(width, height); }); it("Should return false if no unvisited cell with visited neighbour exists", () => { const expected = false; const actual = maze.getFirstUnvisitedCellWithVisitedNeighbour(); expect(actual).toEqual(expected); }); it("Should return false if all cells have been visited", () => { for (let i = 0; i < height; i++) { for (let j = 0; j < width; j++) { maze.visitCell(j, i); } } const expected = false; const actual = maze.getFirstUnvisitedCellWithVisitedNeighbour(); expect(actual).toEqual(expected); }); it("Should return the top left cell and cell (1,0) if cell (1,0) has been visited", () => { maze.visitCell(0, 1); const firstUnivistedCellWithNeighbours = maze.getFirstUnvisitedCellWithVisitedNeighbour(); if (!firstUnivistedCellWithNeighbours) { throw new Error("Expected firstUnivistedCellWithNeighbours to not be false"); } const firstCellExpected = { x: 0, y: 0 }; const firstCellActual = firstUnivistedCellWithNeighbours.firstCell; expect(firstCellActual).toEqual(firstCellExpected); const neighbours = firstUnivistedCellWithNeighbours.neighbours; const expectedNeighboursLength = 1; const actualNeighboursLength = neighbours.length; expect(actualNeighboursLength).toEqual(expectedNeighboursLength); const expectedNeighbour = { direction: "right", x: 1, y: 0 }; const actualNeighbour = neighbours[0]; expect(actualNeighbour).toEqual(expectedNeighbour); }); it("Should return the centre cell and cells (1,0) & (0,1) if the top row, and left most cell on the second row has been visited", () => { maze.visitCell(0, 0); maze.visitCell(0, 1); maze.visitCell(0, 2); maze.visitCell(1, 0); const firstUnivistedCellWithNeighbours = maze.getFirstUnvisitedCellWithVisitedNeighbour(); if (!firstUnivistedCellWithNeighbours) { throw new Error("Expected firstUnivistedCellWithNeighbours to not be false"); } const firstCellExpected = { x: 1, y: 1 }; const firstCellActual = firstUnivistedCellWithNeighbours.firstCell; expect(firstCellActual).toEqual(firstCellExpected); const neighbours = firstUnivistedCellWithNeighbours.neighbours; const expectedNeighboursLength = 2; const actualNeighboursLength = neighbours.length; expect(actualNeighboursLength).toEqual(expectedNeighboursLength); const expectedNeighbourOne = { direction: "up", x: 1, y: 0 }; const expectedNeighbourTwo = { direction: "left", x: 0, y: 1 }; const actualNeighbourOne = neighbours[0]; const actualNeighbourTwo = neighbours[1]; expect(actualNeighbourOne).toEqual(expectedNeighbourOne); expect(actualNeighbourTwo).toEqual(expectedNeighbourTwo); }); }); describe("JSON representation", () => { let maze; beforeEach(() => { maze = new Maze(3, 3); }); it("Maze to JSON representation to be correct on creation", () => { maze = new Maze(3, 3); const testJSON = { rows: [ [ { left: true, right: true, up: true, down: true, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, ], [ { left: true, right: true, up: true, down: true, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, ], [ { left: true, right: true, up: true, down: true, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, ], ], }; expect(maze.toJSON()).toEqual(testJSON); }); it("Maze to JSON representation to be correct when removing wall", () => { maze = new Maze(3, 3); maze.removeWall(0, 0, "right"); const testJSON = { rows: [ [ { left: true, right: false, up: true, down: true, visited: false }, { left: false, right: true, up: true, down: true, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, ], [ { left: true, right: true, up: true, down: true, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, ], [ { left: true, right: true, up: true, down: true, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, ], ], }; expect(maze.toJSON()).toEqual(testJSON); }); it("Maze to JSON representation to be correct when removing multiple walls", () => { maze = new Maze(3, 3); maze.removeWall(0, 0, "right"); maze.removeWall(1, 1, "up"); maze.removeWall(1, 1, "down"); const testJSON = { rows: [ [ { left: true, right: false, up: true, down: true, visited: false }, { left: false, right: true, up: true, down: false, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, ], [ { left: true, right: true, up: true, down: true, visited: false }, { left: true, right: true, up: false, down: false, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, ], [ { left: true, right: true, up: true, down: true, visited: false }, { left: true, right: true, up: false, down: true, visited: false }, { left: true, right: true, up: true, down: true, visited: false }, ], ], }; expect(maze.toJSON()).toEqual(testJSON); }); }); describe("Number of unvisited cells", () => { let maze; const width = 3; const height = 3; beforeEach(() => { maze = new Maze(width, height); }); it("Should return the number of cells if all cells unvisited", () => { expect(maze.getTotalUnvisitedCells()).toEqual(width * height); }); it("Should return the number of cells - 1 when a single cell is visited", () => { maze.visitCell(0, 0); expect(maze.getTotalUnvisitedCells()).toEqual(width * height - 1); }); it("Should return 0 when all cells have been visited", () => { for (let i = 0; i < height; i++) { for (let j = 0; j < width; j++) { maze.visitCell(j, i); } } expect(maze.getTotalUnvisitedCells()).toEqual(0); }); });