123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- import argparse
- import enum
- import logging
-
- from sys import stdout
- from typing import List, Tuple
-
- logger = logging.Logger(__name__)
- logger_2 = logging.Logger(f"{__name__}_2")
- formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
- sh = logging.StreamHandler(stdout)
- sh.setLevel(logging.INFO)
- sh.setFormatter(formatter)
- fh = logging.FileHandler("./day02-2.log", mode="w", encoding="utf-8")
- fh_2 = logging.FileHandler("./day02-2_round2.log", mode="w", encoding="utf-8")
- fh.setLevel(logging.DEBUG)
- fh.setFormatter(formatter)
- fh_2.setLevel(logging.DEBUG)
- fh_2.setFormatter(formatter)
- logger.addHandler(sh)
- logger.addHandler(fh)
- logger_2.addHandler(fh_2)
-
- class Direction(enum.Enum):
- UP = 0
- RIGHT = 1
- DOWN = 2
- LEFT = 3
-
- class Guard:
- def __init__(self, grid: List[List[str]], initial_x: int = None, initial_y: int = None, initial_dir: int = None, test_mode: bool = False) -> object:
- self.grid = grid
- self.height = len(self.grid)
- self.width = len(self.grid[0])
- logger.info(f"Received a grid with {self.height} lines and {self.width} characters per line.")
- self.initial_x = initial_x
- self.x = self.initial_x
- self.initial_y = initial_y
- self.y = self.initial_y
- self.initial_dir = initial_dir
- self.dir = self.initial_dir
- self.test_mode = test_mode
- self.visited = set()
- self.traveled = 1
-
- def whereami(self) -> Tuple[int,int]:
- # Identify the guard's initial position and direction in the grid
- breakout = False
- x,y,dir = None,None,None
- for j, line in enumerate(self.grid):
- if breakout:
- break
- for i, char in enumerate(line):
- if char in ["^",">","<","v"]:
- x, y = i, j
- match char:
- case "^":
- dir = Direction.UP
- case ">":
- dir = Direction.RIGHT
- case "v":
- dir = Direction.DOWN
- case "<":
- dir = Direction.LEFT
- case "_":
- raise ValueError(f"char must be one of '^','>','v','<', received {char}")
- breakout = True
- break
-
- self.initial_x = x
- self.x = self.initial_x
- self.initial_y = y
- self.y = self.initial_y
- self.initial_dir = dir
- self.dir = self.initial_dir
-
- return (x,y)
-
- def proceed(self):
- if self.dir is None:
- logger.error("You can't move until you have a direction set.")
- raise ValueError
- logger.info(f"Proceeding {self.dir}")
- match self.dir:
- case Direction.UP:
- return self.go_north()
- case Direction.RIGHT:
- return self.go_east()
- case Direction.DOWN:
- return self.go_south()
- case Direction.LEFT:
- return self.go_west()
- case _:
- logger.error(f"Unknown direction! {self.dir}")
- raise ValueError
-
- def go_north(self) -> bool:
- path = [self.grid[y][self.x] for y in range(self.y-1, -1, -1)]
- logger.info(f"Path created from {self.x},{self.y} to {self.x},{0}: {path}.")
- exited = "#" not in path
- if exited:
- blocked = 999999
- else:
- blocked = path.index("#")
- if self.test_mode:
- logger.info(f"Found a blocker at ({self.x}, {self.y-blocked-1}).")
- for i, char in enumerate(path[:blocked+1]):
- j = i + 1 if exited else i
- if self.test_mode:
- logger.info(f"Walked to ({self.x}, {self.y-j}).")
- self.visited.add((self.x, self.y-j))
- self.traveled += 1
- new_y = self.y - (blocked)
- self.y = new_y
- self.dir = Direction.RIGHT
- return exited
-
- def go_east(self) -> bool:
- path = [self.grid[self.y][x] for x in range(self.x+1, self.width)]
- logger.info(f"Path created from {self.x},{self.y} to {self.width-1},{self.y}: {path}.")
- exited = "#" not in path
- if exited:
- blocked = 999999
- else:
- blocked = path.index("#")
- if self.test_mode:
- logger.info(f"Found a blocker at ({self.x+blocked+1}, {self.y}).")
- for i, char in enumerate(path[:blocked+1]):
- j = i + 1 if exited else i
- if self.test_mode:
- logger.info(f"Walked to ({self.x+j}, {self.y}).")
- self.visited.add((self.x+j, self.y))
- self.traveled += 1
- new_x = self.x + (blocked)
- self.x = new_x
- self.dir = Direction.DOWN
- return exited
-
- def go_south(self) -> bool:
- path = [self.grid[y][self.x] for y in range(self.y+1, self.height)]
- logger.info(f"Path created from {self.x},{self.y} to {self.x},{self.height}: {path}.")
- exited = "#" not in path
- if exited:
- blocked = 999999
- else:
- blocked = path.index("#")
- if self.test_mode:
- logger.info(f"Found a blocker at ({self.x}, {self.y+blocked+1}).")
- for i, char in enumerate(path[:blocked+1]):
- j = i + 1 if exited else i
- if self.test_mode:
- logger.info(f"Walked to ({self.x}, {self.y+j}).")
- self.visited.add((self.x, self.y+j))
- self.traveled += 1
- new_y = self.y + (blocked)
- self.y = new_y
- self.dir = Direction.LEFT
- return exited
-
- def go_west(self) -> bool:
- path = [self.grid[self.y][x] for x in range(self.x-1, -1, -1)]
- logger.info(f"Path created from {self.x},{self.y} to {0},{self.y}: {path}.")
- exited = "#" not in path
- if exited:
- blocked = 999999
- else:
- blocked = path.index("#")
- if self.test_mode:
- logger.info(f"Found a blocker at ({self.x-blocked-1}, {self.y}).")
- for i, char in enumerate(path[:blocked+1]):
- j = i + 1 if exited else i
- if self.test_mode:
- logger.info(f"Walked to ({self.x-j}, {self.y}).")
- self.visited.add((self.x-j, self.y))
- self.traveled += 1
- new_x = self.x - (blocked)
- self.x = new_x
- self.dir = Direction.UP
- return exited
-
- def pathfind(self):
- exited = False
- while not exited:
- exited = self.proceed()
- logger.info(f"Found an exit after {self.traveled} steps!")
-
-
- parser = argparse.ArgumentParser()
- parser.add_argument("--test", action="store_true", help="Do a test run instead of the full puzzle")
-
- def main061():
- args = parser.parse_args()
- if args.test:
- input_grid = """....#.....
- .........#
- ..........
- ..#.......
- .......#..
- ..........
- .#..^.....
- ........#.
- #.........
- ......#..."""
- grid = [list(l) for l in input_grid.split("\n")]
- print("\n".join(["".join(line) for line in grid]))
- else:
- with open("input06.txt", "r", encoding="utf-8") as f:
- grid = [list(l) for l in f.readlines()]
-
- guard = Guard(grid, test_mode=args.test)
- logger.info(f"Guard has been created: x:{guard.x}, y:{guard.y}, dir:{guard.dir}, test_mode:{guard.test_mode}, width:{guard.width}, height:{guard.height}.")
- guard_position = guard.whereami()
- logger.info(f"Starting guard's walk at {guard_position}.")
- guard.pathfind()
- logger.info(f"The guard visited {len(guard.visited)} unique positions.")
-
-
- if __name__ == "__main__":
- main061()
|