This is a problem that became one after switching laptops. It occurred on WSL 2, probably Ubuntu 20.04 LTS, Python 3.10.12.
148 lines
3.4 KiB
Python
148 lines
3.4 KiB
Python
import numpy as np
|
|
from enum import Enum
|
|
|
|
|
|
verbose = False
|
|
|
|
class Map:
|
|
directions = ["^", ">", "v", "<"]
|
|
#step_per_direction = [(0, -1), (1, 0), (0, 1), (-1, 0)]
|
|
step_per_direction = [(-1, 0), (0, 1), (1, 0), (0, -1)]
|
|
|
|
class Tiles(Enum):
|
|
BORDER = "+"
|
|
FREE = "."
|
|
OBSTACLE = "#"
|
|
|
|
def __init__(self):
|
|
self.trace = []
|
|
self.pos = None
|
|
self.direction = None
|
|
|
|
def next_pos(self):
|
|
"""
|
|
Returns next position given the state of this Map.
|
|
"""
|
|
|
|
next_pos = tuple([self.pos[i] + self.step_per_direction[self.directions.index(self.direction)][i] for i in range(len(self.pos))])
|
|
if verbose:
|
|
print("next_pos", next_pos)
|
|
return next_pos
|
|
|
|
def pos_in_map(self, pos):
|
|
"""
|
|
Returns whether pos is in the map.
|
|
"""
|
|
|
|
if verbose:
|
|
print("pos_in_map", pos)
|
|
return pos[0] >= 0 and pos[0] < self.map.shape[0] \
|
|
and pos[1] >= 0 and pos[0] < self.map.shape[1]
|
|
|
|
def look_ahead(self):
|
|
next_step = self.next_pos()
|
|
if verbose:
|
|
print("look_ahead.next_step", next_step)
|
|
|
|
if not self.pos_in_map(next_step):
|
|
return self.Tiles.BORDER
|
|
if self.map[next_step] == "#":
|
|
return self.Tiles.OBSTACLE
|
|
if self.map[next_step] == ".":
|
|
return self.Tiles.FREE
|
|
|
|
def next_direction(self, direction: str):
|
|
"""
|
|
Return the direction turning right given direction.
|
|
"""
|
|
|
|
assert direction in self.directions, "Invalid direction given"
|
|
|
|
if self.directions[-1] == direction:
|
|
return self.directions[0]
|
|
else:
|
|
return self.directions[self.directions.index(direction) + 1]
|
|
|
|
def seek_guard_in_map(self):
|
|
"""
|
|
Looks for the guard in the map and returns its position.
|
|
|
|
Will raise an exception if multiple or none are found.
|
|
"""
|
|
|
|
guard_locations = []
|
|
for direction in self.directions:
|
|
guard_locations.extend(list(zip(*np.where(m.map == direction))))
|
|
return guard_locations
|
|
|
|
def load_map(self, filename="input"):
|
|
"""
|
|
Reads a map from a file without verifying its contents.
|
|
"""
|
|
|
|
with open(filename, "r") as fp:
|
|
data = fp.read().splitlines()
|
|
chararray = np.array(data, dtype=str)\
|
|
.view("U1")\
|
|
.reshape((len(data), -1))
|
|
self.map = chararray
|
|
|
|
# Find the location of the guard in the map.
|
|
guards = self.seek_guard_in_map()
|
|
|
|
if verbose:
|
|
print(guards)
|
|
assert len(guards) == 1, "There should only be one guard in the map."
|
|
|
|
self.pos = guards[0]
|
|
self.trace.append(self.pos)
|
|
self.direction = m.map[self.pos]
|
|
# TODO: Use enum.
|
|
#self.map[self.pos] = self.Tiles.FREE
|
|
self.map[self.pos] = "."
|
|
|
|
def step(self):
|
|
"""
|
|
Take a step, turn or stop.
|
|
"""
|
|
|
|
next_tile = self.look_ahead()
|
|
if verbose:
|
|
print("step.next_tile", next_tile)
|
|
|
|
if next_tile == self.Tiles.FREE:
|
|
# TODO: Really take a step! Do not only turn.
|
|
self.pos = self.next_pos()
|
|
self.trace.append(self.pos)
|
|
return True
|
|
elif next_tile == self.Tiles.OBSTACLE:
|
|
self.direction = self.next_direction(self.direction)
|
|
return True
|
|
elif next_tile == self.Tiles.BORDER:
|
|
return False
|
|
|
|
assert False, "Should not come here :')"
|
|
|
|
def show(self):
|
|
"""Prints the current map to stdout."""
|
|
|
|
for i in range(len(self.map)):
|
|
for j in range(len(self.map[0])):
|
|
if self.pos == (i, j):
|
|
print(self.direction, end="")
|
|
else:
|
|
print(self.map[i][j], end="")
|
|
print()
|
|
|
|
if __name__ == "__main__":
|
|
m = Map()
|
|
#m.load_map("testinput")
|
|
m.load_map("input")
|
|
|
|
while m.step():
|
|
if verbose:
|
|
print(m.pos, m.direction)
|
|
#m.show()
|
|
#print(len(m.trace), m.trace)
|
|
print(len(set(m.trace)))
|