Пример #1
0
 def testGetNeighbourOfOutOfRangeTooLowCell(self):
     maze = Maze(4, 4)
     with self.assertRaises(
             expected_exception=IndexError,
             msg="`cellIndex` out of range (-5 not between 0 and 15).",
     ):
         _ = maze.getNeighboursOfCell(-5)
Пример #2
0
 def testGetOutOfRangeCellTooHighIndexFromCoordinate(self):
     maze = Maze(1, 1)
     with self.assertRaises(
             ValueError,
             msg="Coordinate (72, 0) is not valid.",
     ):
         _ = maze.getIndexFromCoordinates(XY(72, 0))
Пример #3
0
 def testAddWallFromNonExistentCell(self):
     maze = Maze(2, 2)
     with self.assertRaises(
             IndexError,
             msg="`cellIndex` out of range (55 not between 0 and 3).",
     ):
         maze.addWallBetween(55, 2)
Пример #4
0
 def testRemoveWallToNonExistentCell(self):
     maze = Maze(2, 2)
     with self.assertRaises(
             IndexError,
             msg="`cellIndex` out of range (55 not between 0 and 3).",
     ):
         maze.removeWallBetween(2, 55)
Пример #5
0
 def testGetInvalidCellCoordinatesFromIndexInNonSquareMaze(self):
     maze = Maze(12, 1)
     with self.assertRaises(
             IndexError,
             msg=
             "`cellIndex` out of range (761263748 not between 0 and 11).",
     ):
         _ = maze.getCoordinatesFromIndex(761263748)
Пример #6
0
 def testGetOutOfRangeTooHighCellCoordinatesFromIndex(self):
     maze = Maze(1, 1)
     # get too high cell index coordinates
     with self.assertRaises(
             IndexError,
             msg="`cellIndex` out of range (55 not between 0 and 0).",
     ):
         _ = maze.getCoordinatesFromIndex(55)
Пример #7
0
 def testRemoveWallBetweenNonAdjacentCells(self):
     maze = Maze(3, 3)
     # remove invalid wall between top left and bottom right cells
     with self.assertRaises(
             ValueError,
             msg="Cell at index 0 is not adjacent to cell at 8.",
     ):
         maze.removeWallBetween(0, 8)
Пример #8
0
 def testRemoveWallBetweenSelf(self):
     maze = Maze(2, 2)
     # remove a wall between `x` and `x`, i.e., to itself
     with self.assertRaises(
             ValueError,
             msg="Cell at index 0 is not adjacent to cell at 0.",
     ):
         maze.removeWallBetween(0, 0)
Пример #9
0
 def testRemoveNonExistentWall(self):
     maze = Maze(3, 3, False)
     # remove a wall that doesn't exist
     with self.assertRaises(
             Exception,
             msg="Node index '1' already exists in node 0's connections.",
     ):
         maze.removeWallBetween(0, 1)
Пример #10
0
 def testAddWallBetweenSelf(self):
     maze = Maze(2, 2)
     # add a wall between `x` and `x`, i.e., to itself
     with self.assertRaises(
             Exception,
             msg=
             "Node index 0 already does not exist in node at index 0's connections.",
     ):
         maze.addWallBetween(0, 0)
Пример #11
0
 def testAddExistentWall(self):
     maze = Maze(3, 3, True)
     # add a wall that already exists
     with self.assertRaises(
             Exception,
             msg=
             "Node index 1 already does not exist in node at index 0's connections.",
     ):
         maze.addWallBetween(0, 1)
Пример #12
0
    def __init__(self, size: XY) -> None:
        # check size is valid
        self._testSizeIsValidWithException(size)

        self.__size = size
        # initialize a Maze full of walls
        self.__maze = Maze(self.__size.x, self.__size.y, walls=True)

        # get the last cell index of the maze, so we can initialise __visitedOrNotCells to a list of False (nothing visited yet)
        lastIndex = self.__maze.getIndexFromCoordinates(
            XY(self.__size.x - 1, self.__size.y - 1))
        # init __visitedOrNotCells to a list of False with the length of the max index of cells
        self.__visitedOrNotCells = [False] * (lastIndex + 1)
Пример #13
0
 def testRemoveWall(self):
     maze = Maze(4, 4, True)
     # remove a wall from first cell
     maze.removeWallBetween(0, 1)
     self.assertCountEqual(maze.getConnectionsOfCellAtIndex(0), [1])
     # remove another wall from first cell
     maze.removeWallBetween(0, 4)
     self.assertCountEqual(maze.getConnectionsOfCellAtIndex(0), [1, 4])
Пример #14
0
 def testAddWallAgain(self):
     maze = Maze(4, 4, False)
     # add a wall from first cell
     maze.addWallBetween(0, 1)
     self.assertCountEqual(maze.getConnectionsOfCellAtIndex(0), [4])
     # add another wall from first cell
     maze.addWallBetween(0, 4)
     self.assertCountEqual(maze.getConnectionsOfCellAtIndex(0), [])
Пример #15
0
    def __init__(self, size: XY) -> None:
        # check size is valid
        self._testSizeIsValidWithException(size)

        self.__size = size
        # initialize a Maze full of walls
        self.__maze = Maze(self.__size.x, self.__size.y, walls=True)
    def testMazeIsSaved(self):
        FILEPATH = self.FILE_PATH_PREFIX + "test_save.maze"

        fileHandler = MazeFileHandler(FILEPATH)
        maze = Maze(4, 4, True)

        with self.assertLogs(level="DEBUG"):
            fileHandler.save(maze)
    def testMazeIsLoadedCorrectly(self):
        FILEPATH = self.FILE_PATH_PREFIX + "test_save.maze"

        size = XY(13, 3)

        # save the maze
        fileHandler = MazeFileHandler(FILEPATH)
        fileHandler.save(Maze(size.x, size.y, False))

        # try to load the maze
        newMaze = fileHandler.load()
        self.assertEqual(newMaze.size, size)
Пример #18
0
 def testGetLastCellIndexFromCoordinates(self):
     maze = Maze(3, 3)
     index = maze.getIndexFromCoordinates(XY(2, 2))
     self.assertEqual(index, 8)
Пример #19
0
 def testGetLastCellCoordinatesFromIndexInNonSquareMaze(self):
     maze = Maze(37, 17)
     c = maze.getCoordinatesFromIndex(628).toTuple()
     self.assertEqual(c, (36, 16))
Пример #20
0
class RecursiveBacktracker(MazeGenerator):
    """Recursive Backtracker maze generation algorithm implementation.

    |    Here’s the mile-high view of recursive backtracking:
    |
    |    • Choose a starting point in the field.
    |    • Randomly choose a wall at that point and carve a passage through to the adjacent cell, but only if the adjacent cell has not been visited yet.
    |           This becomes the new current cell.
    |    • If all adjacent cells have been visited, back up to the last cell that has uncarved walls and repeat.
    |    • The algorithm ends when the process has backed all the way up to the starting point.
    > Source - http://weblog.jamisbuck.org/2010/12/27/maze-generation-recursive-backtracking
    """

    __maze: MazeProtocol
    __visitedOrNotCells: List[
        bool]  # the cell index is the position in the list
    __size: XY
    # the stack of positions
    __positionsStack: Stack[int]

    def __init__(self, size: XY) -> None:
        # check size is valid
        self._testSizeIsValidWithException(size)

        self.__size = size
        # initialize a Maze full of walls
        self.__maze = Maze(self.__size.x, self.__size.y, walls=True)

        # get the last cell index of the maze, so we can initialise __visitedOrNotCells to a list of False (nothing visited yet)
        lastIndex = self.__maze.getIndexFromCoordinates(
            XY(self.__size.x - 1, self.__size.y - 1))
        # init __visitedOrNotCells to a list of False with the length of the max index of cells
        self.__visitedOrNotCells = [False] * (lastIndex + 1)

    def generate(self) -> MazeProtocol:
        # start our maze generator in the top left of the maze
        start = XY(0, 0)

        # init positionsStack to a new empty stack of type int
        self.__positionsStack = Stack[int]()

        # Convert the `start` position to the same cell's index in the maze, and push to the positions stack
        self.__positionsStack.push(
            # get the index of the `start` posotion
            self.__maze.getIndexFromCoordinates(start))

        # set the starting cell as visited
        self.__visitedOrNotCells[self.__positionsStack.peek()] = True

        # while the positions stack is not empty, so we automatically exit the loop when visited all cells and back at start
        while not self.__positionsStack.isEmpty():
            randomCellChoice: Optional[int] = None
            # this loop tries to find a random cell to visit out of the unvisited neighbours of the current cell
            while randomCellChoice is None:
                # if we've ran out of positions to backtrack to, and therefore made the entire maze
                if self.__positionsStack.isEmpty():
                    break

                # get a list of unvisited adjacent cells
                neighbourCells = self.__maze.getNeighboursOfCell(
                    # get the current position by peeking the stack.
                    # don't pop because we want the item to remain on the stack,
                    # so we can backtrach through it.
                    self.__positionsStack.peek())

                # Filter the neighbourCells by whether they've been visited or not
                # create a lambda to return whether or not a cell at index has been visited, but return the inverse because we are _filtering_ the cells!
                checkIsVisited: Callable[
                    [int],
                    bool] = lambda cellIndex: not self.__visitedOrNotCells[
                        cellIndex]
                # …and filter the neighbourCells by this lambda, and put it into the list `unvisitedWalls`
                unvisitedWalls = list(filter(checkIsVisited, neighbourCells))

                # check that there are unvisited walls
                if len(unvisitedWalls) > 0:
                    # choose a random wall
                    randomCellChoice = randomChoice(unvisitedWalls)
                    # set the next cell to visited
                    self.__visitedOrNotCells[randomCellChoice] = True
                else:
                    # all the cells here have been visited
                    # so back up to the last cell, by popping the positionsStack
                    self.__positionsStack.pop()

            # if the cell hasn't been chosen, and therefore we've explored the whole maze
            if randomCellChoice is None:
                # break so we can return the completed maze
                break

            # carve a passage through to the adjacent cell
            self.__maze.removeWallBetween(
                cellAIndex=self.__positionsStack.peek(),
                cellBIndex=randomCellChoice,
            )
            # push the choice to the positionsStack so it is our next one
            self.__positionsStack.push(randomCellChoice)

        return self.__maze
Пример #21
0
            forward.success,
            f"Turned {movementDirection} and attempted to move forward",
            solver._state,
        )

        command = MazeSolverCommand(
            "Move randomly",
            MazeSolverCommandType.movement,
            result,
        )

        return (command, result)


if __name__ == "__main__":
    # Test out the random mouse maze solver
    from modules.data_structures.maze.maze import Maze

    maze = Maze(10, 10, False)
    rm = RandomMouse(maze, XY(0, 0), XY(9, 9))

    FORMAT = "%(asctime)s - %(name)-20s - %(levelname)-5s - %(message)s"
    LEVEL = 0
    logging.basicConfig(format=FORMAT, level=LEVEL)
    logging.getLogger().setLevel(LEVEL)
    log = logging.getLogger()

    while True:
        rm.advance()
        print(rm.getCurrentState().currentCell)
Пример #22
0
 def testGetCoordinateOfFirstCell(self):
     coordinate = Maze(5, 5).getCoordinatesFromIndex(0).toTuple()
     self.assertEqual(coordinate, (0, 0))
Пример #23
0
 def testInitWithZeroSize(self):
     maze = Maze(0, 0)
     self.assertIsInstance(maze, Maze)
Пример #24
0
    def testGetNeighbourOfSurroundedCell(self):
        neighbours = Maze(20, 20).getNeighboursOfCell(189)
        actualNeighbours = [169, 209, 188, 190]

        self.assertCountEqual(neighbours, actualNeighbours)
Пример #25
0
 def testGetFirstCellIndexFromCoordinates(self):
     maze = Maze(2, 2)
     index = maze.getIndexFromCoordinates(XY(0, 0))
     self.assertEqual(index, 0)
Пример #26
0
    def testGetNeighbourOfWallCell(self):
        neighbours = Maze(20, 20).getNeighboursOfCell(1)
        actualNeighbours = [0, 2, 21]

        self.assertCountEqual(neighbours, actualNeighbours)
Пример #27
0
    def testGetNeighbourOfCornerCell(self):
        neighbours = Maze(20, 20).getNeighboursOfCell(0)
        actualNeighbours = [1, 20]

        self.assertCountEqual(neighbours, actualNeighbours)
Пример #28
0
 def testInitWithInvalidYSize(self):
     with self.assertRaises(
             ValueError,
             msg="Invalid size `(4, -66)` given.",
     ):
         _ = Maze(4, -66)
Пример #29
0
 def testInitWithInvalidXSize(self):
     with self.assertRaises(
             ValueError,
             msg="Invalid size `(-21, 7)` given.",
     ):
         _ = Maze(-21, 7)
Пример #30
0
 def testGetCentreCellIndexFromCoordinates(self):
     maze = Maze(3, 3)
     index = maze.getIndexFromCoordinates(XY(1, 1))
     self.assertEqual(index, 4)