Help a mouse in a maze find where the cheese is. 2 functions are given:
The goal is to write a function find that will move the mouse from origin to the cheese cell and return True if possible and return False otherwise.
No code is provided during the interview. The functions move and hasCheese do not need to be implemented neither the structure of the maze. More or less the structure of the find function should be structure free (the link with the structure being made by the move and hasCheese functions).
# The example given during the interview is more or less like this
'''
| |c|
| |_| _|
| __ | |
|m| |
'''
The ideas of the algorithm is:
To apply this strategy we need:
from collections import deque
class MouseMaze:
def __init__(self, maze, origin_i, origin_j, objective_i=None, objective_j=None):
self.maze = maze
self.i, self.j = origin_i, origin_j
self.objective_i, self.objective_j = objective_i, objective_j
# Moves to one of the 4 directions and returns false if you can't move and true if you can.
def move(self, direction):
# we do not care about border as the maze is well defined (example; for cells on top border can't go up)
if direction in self.maze[self.i][self.j]:
match direction:
case 'up':
self.i -= 1 # bottom is N so up is -1
case 'down':
self.i += 1 # top is 0 so down is +1
case 'left':
self.j -= 1
case 'right':
self.j += 1
return(True)
return(False)
# Returns true if there is a cheese in the current cell.
def hasCheese(self):
return(self.i==self.objective_i and self.j==self.objective_j)
# Return True and move to cheese position if mouse can find a cheese; otherwise return False and do not move
def find(self):
# dict to inverse last move when we need backtrack: if last move was up last cell was down from this one
dict_inverse_move = {"up": "down", "down": "up", "left": "right", "right": "left"}
# stack_moves is a stack of past moves + past tried moves (to backtrack if mouse reach a dead end)
stack_moves = deque()
# already_tried_moves is a set of moves already tried for this position
def dfs(stack_moves, already_tried_moves):
if self.hasCheese():
return(True)
directions_not_tested = {"up", "down", "left", "right"} - already_tried_moves
# check every possible move that has not already been tested
for direction in directions_not_tested:
already_tried_moves.add(direction) # add the current tried direction to the set
if self.move(direction):
stack_moves.append((direction, already_tried_moves)) # if move is ok store it in stack
# recursion from next cell; already_tried_moves is set to the opposite of the move done
return(dfs(stack_moves, {dict_inverse_move[direction]}))
# if we reach a dead end ie no already tried possibilities of move then backtrack
if stack_moves:
last_move, last_tried_moves = stack_moves.pop() # pop last_move and info about last cell
_ = self.move(dict_inverse_move[last_move]) # go back to this previous cell
return(dfs(stack_moves, last_tried_moves)) # try again from this cell with another move
else:
return(False)
return(dfs(stack_moves, set()))
# maze is a list of cells; each cell has two informations if the cell contains cheese and the reachable cells
maze = [[{"right"}, {"down", "left", "right"}, {"down", "left"}, {"down"}],
[{"down", "right"}, {"up", "left"}, {"up", "down"}, {"up", "down"}],
[{"up", "down"}, {"down"}, {"up", "down"}, {"up", "down"}],
[{"up", "down"}, {"up", "down"}, {"up", "down"}, {"up", "down"}],
[{"up"}, {"up", "right"}, {"up", "left", "right"}, {"up", "left"}]]
origin_i, origin_j = 4, 0
objective_i, objective_j = 0, 3
mouse_in_maze = MouseMaze(maze, origin_i, origin_j, objective_i, objective_j)
print("Initial position is: {}, {}".format(mouse_in_maze.i, mouse_in_maze.j))
print("Has the mouse reach the cheese?", mouse_in_maze.find())
print("Final position is: {}, {}".format(mouse_in_maze.i, mouse_in_maze.j))
Initial position is: 4, 0 Has the mouse reach the cheese? True Final position is: 0, 3
# maze is a list of cells; each cell has two informations if the cell contains cheese and the reachable cells
maze = [[{"right"}, {"down", "left", "right"}, {"down", "left"}, {"down"}],
[{"down", "right"}, {"up", "left"}, {"up", "down"}, {"up"}],
[{"up", "down"}, {"down"}, {"up", "down"}, {"down"}],
[{"up", "down"}, {"up", "down"}, {"up", "down"}, {"up", "down"}],
[{"up"}, {"up", "right"}, {"up", "left", "right"}, {"up", "left"}]]
origin_i, origin_j = 4, 0
objective_i, objective_j = 0, 3
mouse_in_maze = MouseMaze(maze, origin_i, origin_j, objective_i, objective_j)
print("Initial position is: {}, {}".format(mouse_in_maze.i, mouse_in_maze.j))
print("Has the mouse reach the cheese?", mouse_in_maze.find())
print("Final position is: {}, {}".format(mouse_in_maze.i, mouse_in_maze.j))
Initial position is: 4, 0 Has the mouse reach the cheese? False Final position is: 4, 0