-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
417 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import typer | ||
import re | ||
import functools | ||
from rich.logging import RichHandler | ||
import logging | ||
from rich.progress import track, Progress | ||
|
||
from multiprocessing import Pool, freeze_support | ||
|
||
FORMAT = "%(message)s" | ||
logging.basicConfig( | ||
level="DEBUG", format=FORMAT, datefmt="[%X]", handlers=[RichHandler()] | ||
) | ||
log = logging.getLogger("rich") | ||
|
||
|
||
def clac_hash(string: str) -> int: | ||
current_value = 0 | ||
for c in string: | ||
current_value += ord(c) | ||
current_value *= 17 | ||
current_value %= 256 | ||
log.debug(f"{string} -> {current_value}") | ||
return current_value | ||
|
||
|
||
def calc_focussing_power(boxes: list) -> int: | ||
fp_sum = 0 | ||
for i, box in enumerate(boxes): | ||
if not box: | ||
continue | ||
for j, (lens, focal_length) in enumerate(box.items()): | ||
fp = 1 + i | ||
fp *= j + 1 | ||
fp *= focal_length | ||
fp_sum += fp | ||
return fp_sum | ||
|
||
|
||
def print_boxes(boxes: list): | ||
for i, box in enumerate(boxes): | ||
if not box: | ||
continue | ||
log.debug(f"Box {i}:") | ||
for j, (lens, focal_length) in enumerate(box.items()): | ||
log.debug(f" slot {j}: {lens} {focal_length}") | ||
|
||
|
||
def main( | ||
input_file: typer.FileText, | ||
log_level: str = "INFO", | ||
part1: bool = True, | ||
part2: bool = True, | ||
): | ||
log.setLevel(log_level) | ||
line = input_file.read().strip().replace("\n", "").replace("\r", "") | ||
|
||
if part1: | ||
current_value = 0 | ||
for step in track(line.split(",")): | ||
current_value += clac_hash(step) | ||
log.info(f"Part1: {current_value}") | ||
|
||
if part2: | ||
boxes = [None] * 256 | ||
for step in track(line.split(",")): | ||
if "-" in step: | ||
lens = step.split("-")[0] | ||
box = clac_hash(lens) | ||
if boxes[box] and lens in boxes[box]: | ||
boxes[box].pop(lens) | ||
log.debug(f"Step: {step} -> Box {box}: {boxes[box]}") | ||
elif "=" in step: | ||
lens = step.split("=")[0] | ||
strength = int(step.split("=")[1]) | ||
box = clac_hash(lens) | ||
if not boxes[box]: | ||
boxes[box] = {} | ||
boxes[box][lens] = strength | ||
log.debug(f"Step: {step} -> Box {box}: {boxes[box]}") | ||
else: | ||
raise ValueError(f"Unknown step: {step}") | ||
print_boxes(boxes) | ||
fp = calc_focussing_power(boxes) | ||
log.info(f"Part2: {fp}") | ||
|
||
|
||
if __name__ == "__main__": | ||
freeze_support() | ||
typer.run(main) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
import typer | ||
import re | ||
import functools | ||
from rich.logging import RichHandler | ||
import logging | ||
from rich.progress import track, Progress | ||
import math | ||
from enum import Enum | ||
|
||
from multiprocessing import Pool, freeze_support | ||
|
||
FORMAT = "%(message)s" | ||
logging.basicConfig( | ||
level="DEBUG", format=FORMAT, datefmt="[%X]", handlers=[RichHandler()] | ||
) | ||
log = logging.getLogger("rich") | ||
|
||
|
||
class Heading(Enum): | ||
NORTH = 0 | ||
EAST = 1 | ||
SOUTH = 2 | ||
WEST = 3 | ||
|
||
|
||
def new_pos(pos: tuple[int], heading: Heading) -> tuple[int]: | ||
if heading == Heading.NORTH: | ||
return (pos[0] - 1, pos[1]) | ||
elif heading == Heading.EAST: | ||
return (pos[0], pos[1] + 1) | ||
elif heading == Heading.SOUTH: | ||
return (pos[0] + 1, pos[1]) | ||
elif heading == Heading.WEST: | ||
return (pos[0], pos[1] - 1) | ||
|
||
|
||
def print_energized(layout: list[str], positions: set[tuple]): | ||
for i, row in enumerate(layout): | ||
for j, col in enumerate(row): | ||
if (i, j) in positions: | ||
print("#", end="") | ||
else: | ||
print(".", end="") | ||
print() | ||
|
||
|
||
def num_tiles_traversed( | ||
layout: list[str], | ||
pos: tuple[int] = (0, -1), | ||
heading: Heading = Heading.EAST, | ||
positions_traversed: set[tuple] | None = None, | ||
) -> set[tuple[int]]: | ||
width = len(layout[0]) | ||
height = len(layout) | ||
|
||
if positions_traversed is None: | ||
positions_traversed = set() | ||
|
||
while True: | ||
pos = new_pos(pos, heading) | ||
if pos[0] < 0 or pos[0] >= height or pos[1] < 0 or pos[1] >= width: | ||
log.debug( | ||
f"Out of bounds, positions traversed: {len(positions_traversed)}" | ||
) | ||
break | ||
if (pos, heading) in positions_traversed: | ||
log.debug( | ||
f"Loop detected, positions traversed: {len(positions_traversed)}" | ||
) | ||
break | ||
log.debug(f"New pos: {pos} ({heading} {layout[pos[0]][pos[1]]})") | ||
positions_traversed.add((pos, heading)) | ||
if heading == Heading.NORTH: | ||
if layout[pos[0]][pos[1]] == "-": | ||
positions_traversed.update( | ||
num_tiles_traversed( | ||
layout, pos, Heading.WEST, positions_traversed | ||
) | ||
) | ||
heading = Heading.EAST | ||
elif layout[pos[0]][pos[1]] == "/": | ||
heading = Heading.EAST | ||
elif layout[pos[0]][pos[1]] == "\\": | ||
heading = Heading.WEST | ||
elif heading == Heading.EAST: | ||
if layout[pos[0]][pos[1]] == "|": | ||
positions_traversed.update( | ||
num_tiles_traversed( | ||
layout, pos, Heading.NORTH, positions_traversed | ||
) | ||
) | ||
heading = Heading.SOUTH | ||
elif layout[pos[0]][pos[1]] == "/": | ||
heading = Heading.NORTH | ||
elif layout[pos[0]][pos[1]] == "\\": | ||
heading = Heading.SOUTH | ||
elif heading == Heading.SOUTH: | ||
if layout[pos[0]][pos[1]] == "-": | ||
positions_traversed.update( | ||
num_tiles_traversed( | ||
layout, pos, Heading.WEST, positions_traversed | ||
) | ||
) | ||
heading = Heading.EAST | ||
elif layout[pos[0]][pos[1]] == "/": | ||
heading = Heading.WEST | ||
elif layout[pos[0]][pos[1]] == "\\": | ||
heading = Heading.EAST | ||
elif heading == Heading.WEST: | ||
if layout[pos[0]][pos[1]] == "|": | ||
positions_traversed.update( | ||
num_tiles_traversed( | ||
layout, pos, Heading.NORTH, positions_traversed | ||
) | ||
) | ||
heading = Heading.SOUTH | ||
elif layout[pos[0]][pos[1]] == "/": | ||
heading = Heading.SOUTH | ||
elif layout[pos[0]][pos[1]] == "\\": | ||
heading = Heading.NORTH | ||
|
||
return positions_traversed | ||
|
||
|
||
def main( | ||
input_file: typer.FileText, | ||
log_level: str = "INFO", | ||
part1: bool = True, | ||
part2: bool = True, | ||
): | ||
log.setLevel(log_level) | ||
layout = input_file.read().strip().split("\n") | ||
|
||
if part1: | ||
positions_traversed = num_tiles_traversed(layout) | ||
positions_traversed = {pos for pos, heading in positions_traversed} | ||
print_energized(layout, positions_traversed) | ||
num_tiles = len(positions_traversed) | ||
log.info(f"Part 1: {num_tiles}") | ||
|
||
if part2: | ||
positions_traversed = set() | ||
width = len(layout[0]) | ||
height = len(layout) | ||
pt_max = 0 | ||
for y in track( | ||
range(height), | ||
description="Traversing with light from top to bottom", | ||
): | ||
positions_traversed = num_tiles_traversed( | ||
layout, (y, -1), Heading.EAST | ||
) | ||
pt_max = max( | ||
pt_max, len(set({pos for pos, heading in positions_traversed})) | ||
) | ||
positions_traversed = num_tiles_traversed( | ||
layout, (y, width), Heading.WEST | ||
) | ||
pt_max = max( | ||
pt_max, len(set({pos for pos, heading in positions_traversed})) | ||
) | ||
for x in track( | ||
range(width), | ||
description="Traversing with light from left to right", | ||
): | ||
positions_traversed = num_tiles_traversed( | ||
layout, (-1, x), Heading.SOUTH | ||
) | ||
pt_max = max( | ||
pt_max, len(set({pos for pos, heading in positions_traversed})) | ||
) | ||
positions_traversed = num_tiles_traversed( | ||
layout, (height, x), Heading.NORTH | ||
) | ||
pt_max = max( | ||
pt_max, len(set({pos for pos, heading in positions_traversed})) | ||
) | ||
# num_tiles = len(positions_traversed) | ||
log.info(f"Part 2: {pt_max}") | ||
|
||
|
||
if __name__ == "__main__": | ||
freeze_support() | ||
typer.run(main) |
Oops, something went wrong.