from collections import namedtuple
Explorer = namedtuple('Explorer', 'row,col,direction')
movement = { '>': (0,1), '<': (0,-1), '^': (-1,0), 'V': (1,0) }
turns = {'>': ('V', '<', '^'), 'V': ('<', '^', '>'),
'<': ('^', '>', 'V'), '^': ('>', 'V', '<')}
def turn_right(direction):
return turns[direction][0]
def turn_around(direction):
return turns[direction][1]
def turn_left(direction):
return turns[direction][2]
def move(row, col, direction, spaces=1):
r = row + movement[direction][0]*spaces
c = col + movement[direction][1]*spaces
return r, c
def navigate(maze):
maze = maze.splitlines()
# Find explorer
for r, row in enumerate(maze):
for c, cell in enumerate(row):
if cell in '><^V':
explorer = Explorer(r, c, cell)
break
states = {explorer}
r, c = explorer.row, explorer.col
d = explorer.direction
while True:
# Keep moving according to the rules until we reach
# the exit or a repeated state
for i in range(6):
if maze[r][c] == 'E':
return True
next_r, next_c = move(r,c,d)
if maze[next_r][next_c] == '#':
# If a wall is in my way I turn to the right
next_r, next_c = move(r,c,turn_right(d))
if maze[next_r][next_c] == '#':
# if that not possible I turn to the left
next_r, next_c = move(r, c, turn_left(d))
if maze[next_r][next_c] == '#':
# if that is not possible I turn back from where I came
d = turn_around(d)
next_r, next_c = move(r, c, d)
else:
d = turn_left(d)
else:
d = turn_right(d)
r, c = next_r, next_c
d = turn_right(turn_right(d))
if Explorer(r, c, d) in states:
# We've seen this before. The maze can't be solved.
return False
states.add(Explorer(r, c, d))
mazes = [
'''#######
#> E#
#######''',
'''#####E#
#< #
#######''',
'''##########
#> E#
##########''',
'''#####E#
##### #
#> #
##### #
#######''',
'''#####E#
##### #
##### #
##### #
##### #
#> #
##### #
#######''']
challenges = ['''#########
#E ######
## #
##### # #
#> ###
##### ###
##### ###
##### ###
##### ###
##### ###
##### ###
#########''',
'''#########
#E ######
## #
## ## # #
##### # #
#> ###
##### ###
##### ###
##### ###
##### ###
##### ###
##### ###
#########''']
for maze in mazes:
print(navigate(maze))
for maze in challenges:
print(navigate(maze))
ZnJvbSBjb2xsZWN0aW9ucyBpbXBvcnQgbmFtZWR0dXBsZQoKRXhwbG9yZXIgPSBuYW1lZHR1cGxlKCdFeHBsb3JlcicsICdyb3csY29sLGRpcmVjdGlvbicpCgptb3ZlbWVudCA9IHsgJz4nOiAoMCwxKSwgJzwnOiAoMCwtMSksICdeJzogKC0xLDApLCAnVic6ICgxLDApIH0KCnR1cm5zID0geyc+JzogKCdWJywgJzwnLCAnXicpLCAnVic6ICgnPCcsICdeJywgJz4nKSwKICAgICAgICAgJzwnOiAoJ14nLCAnPicsICdWJyksICdeJzogKCc+JywgJ1YnLCAnPCcpfQoKZGVmIHR1cm5fcmlnaHQoZGlyZWN0aW9uKToKICAgIHJldHVybiB0dXJuc1tkaXJlY3Rpb25dWzBdCgpkZWYgdHVybl9hcm91bmQoZGlyZWN0aW9uKToKICAgIHJldHVybiB0dXJuc1tkaXJlY3Rpb25dWzFdCgpkZWYgdHVybl9sZWZ0KGRpcmVjdGlvbik6CiAgICByZXR1cm4gdHVybnNbZGlyZWN0aW9uXVsyXQoKZGVmIG1vdmUocm93LCBjb2wsIGRpcmVjdGlvbiwgc3BhY2VzPTEpOgogICAgciA9IHJvdyArIG1vdmVtZW50W2RpcmVjdGlvbl1bMF0qc3BhY2VzCiAgICBjID0gY29sICsgbW92ZW1lbnRbZGlyZWN0aW9uXVsxXSpzcGFjZXMKICAgIHJldHVybiByLCBjCgpkZWYgbmF2aWdhdGUobWF6ZSk6CiAgICBtYXplID0gbWF6ZS5zcGxpdGxpbmVzKCkKICAgICMgRmluZCBleHBsb3JlcgogICAgZm9yIHIsIHJvdyBpbiBlbnVtZXJhdGUobWF6ZSk6CiAgICAgICAgZm9yIGMsIGNlbGwgaW4gZW51bWVyYXRlKHJvdyk6CiAgICAgICAgICAgIGlmIGNlbGwgaW4gJz48XlYnOgogICAgICAgICAgICAgICAgZXhwbG9yZXIgPSBFeHBsb3JlcihyLCBjLCBjZWxsKQogICAgICAgICAgICAgICAgYnJlYWsKICAgIHN0YXRlcyA9IHtleHBsb3Jlcn0KCiAgICByLCBjID0gZXhwbG9yZXIucm93LCBleHBsb3Jlci5jb2wKICAgIGQgPSBleHBsb3Jlci5kaXJlY3Rpb24KICAgIHdoaWxlIFRydWU6CiAgICAgICAgIyBLZWVwIG1vdmluZyBhY2NvcmRpbmcgdG8gdGhlIHJ1bGVzIHVudGlsIHdlIHJlYWNoCiAgICAgICAgIyB0aGUgZXhpdCBvciBhIHJlcGVhdGVkIHN0YXRlCiAgICAgICAgZm9yIGkgaW4gcmFuZ2UoNik6CiAgICAgICAgICAgIGlmIG1hemVbcl1bY10gPT0gJ0UnOgogICAgICAgICAgICAgICAgcmV0dXJuIFRydWUKICAgICAgICAgICAgbmV4dF9yLCBuZXh0X2MgPSBtb3ZlKHIsYyxkKQogICAgICAgICAgICBpZiBtYXplW25leHRfcl1bbmV4dF9jXSA9PSAnIyc6CiAgICAgICAgICAgICAgICAjIElmIGEgd2FsbCBpcyBpbiBteSB3YXkgSSB0dXJuIHRvIHRoZSByaWdodAogICAgICAgICAgICAgICAgbmV4dF9yLCBuZXh0X2MgPSBtb3ZlKHIsYyx0dXJuX3JpZ2h0KGQpKQogICAgICAgICAgICAgICAgaWYgbWF6ZVtuZXh0X3JdW25leHRfY10gPT0gJyMnOgogICAgICAgICAgICAgICAgICAgICMgaWYgdGhhdCBub3QgcG9zc2libGUgSSB0dXJuIHRvIHRoZSBsZWZ0CiAgICAgICAgICAgICAgICAgICAgbmV4dF9yLCBuZXh0X2MgPSBtb3ZlKHIsIGMsIHR1cm5fbGVmdChkKSkKICAgICAgICAgICAgICAgICAgICBpZiBtYXplW25leHRfcl1bbmV4dF9jXSA9PSAnIyc6CiAgICAgICAgICAgICAgICAgICAgICAgICMgaWYgdGhhdCBpcyBub3QgcG9zc2libGUgSSB0dXJuIGJhY2sgZnJvbSB3aGVyZSBJIGNhbWUKICAgICAgICAgICAgICAgICAgICAgICAgZCA9IHR1cm5fYXJvdW5kKGQpCiAgICAgICAgICAgICAgICAgICAgICAgIG5leHRfciwgbmV4dF9jID0gbW92ZShyLCBjLCBkKQogICAgICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgICAgIGQgPSB0dXJuX2xlZnQoZCkKICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgZCA9IHR1cm5fcmlnaHQoZCkKICAgICAgICAgICAgciwgYyA9IG5leHRfciwgbmV4dF9jCiAgICAgICAgZCA9IHR1cm5fcmlnaHQodHVybl9yaWdodChkKSkKICAgICAgICBpZiBFeHBsb3JlcihyLCBjLCBkKSBpbiBzdGF0ZXM6CiAgICAgICAgICAgICMgV2UndmUgc2VlbiB0aGlzIGJlZm9yZS4gVGhlIG1hemUgY2FuJ3QgYmUgc29sdmVkLgogICAgICAgICAgICByZXR1cm4gRmFsc2UKICAgICAgICBzdGF0ZXMuYWRkKEV4cGxvcmVyKHIsIGMsIGQpKQoKCm1hemVzID0gWwonJycjIyMjIyMjCiM+ICAgRSMKIyMjIyMjIycnJywKCicnJyMjIyMjRSMKIzwgICAgIwojIyMjIyMjJycnLAoKJycnIyMjIyMjIyMjIwojPiAgICAgIEUjCiMjIyMjIyMjIyMnJycsCgonJycjIyMjI0UjCiMjIyMjICMKIz4gICAgIwojIyMjIyAjCiMjIyMjIyMnJycsCgonJycjIyMjI0UjCiMjIyMjICMKIyMjIyMgIwojIyMjIyAjCiMjIyMjICMKIz4gICAgIwojIyMjIyAjCiMjIyMjIyMnJyddCgpjaGFsbGVuZ2VzID0gWycnJyMjIyMjIyMjIwojRSAjIyMjIyMKIyMgICAgICAjCiMjIyMjICMgIwojPiAgICAjIyMKIyMjIyMgIyMjCiMjIyMjICMjIwojIyMjIyAjIyMKIyMjIyMgIyMjCiMjIyMjICMjIwojIyMjIyAjIyMKIyMjIyMjIyMjJycnLAoKJycnIyMjIyMjIyMjCiNFICMjIyMjIwojIyAgICAgICMKIyMgIyMgIyAjCiMjIyMjICMgIwojPiAgICAjIyMKIyMjIyMgIyMjCiMjIyMjICMjIwojIyMjIyAjIyMKIyMjIyMgIyMjCiMjIyMjICMjIwojIyMjIyAjIyMKIyMjIyMjIyMjJycnXQoKZm9yIG1hemUgaW4gbWF6ZXM6CiAgICBwcmludChuYXZpZ2F0ZShtYXplKSkKZm9yIG1hemUgaW4gY2hhbGxlbmdlczoKICAgIHByaW50KG5hdmlnYXRlKG1hemUpKQ==