Skip to content

Commit

Permalink
Remove aoc.Board in favor of Map
Browse files Browse the repository at this point in the history
  • Loading branch information
IsaacG committed Dec 13, 2024
1 parent 28786ff commit f90d49b
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 110 deletions.
2 changes: 1 addition & 1 deletion 2020/d11.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def __init__(self, block: List[str]):
self.stable = False

def __eq__(self, other):
"""Board equality."""
"""Seating equality."""
return isinstance(other, type(self)) and self.board == other.board

@classmethod
Expand Down
48 changes: 22 additions & 26 deletions 2021/d15.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from lib import aoc

SAMPLE = ["""\
SAMPLE = """\
1163751742
1381373672
2136511328
Expand All @@ -14,60 +14,60 @@
3125421639
1293138521
2311944581
"""]
InputType = dict[complex, int]
"""


class Day15(aoc.Challenge):
"""Navigate through a maze of chiton, minimizing damage/cost."""

TESTS = (
aoc.TestCase(inputs=SAMPLE[0], part=1, want=40),
aoc.TestCase(inputs=SAMPLE[0], part=2, want=315),
aoc.TestCase(inputs=SAMPLE, part=1, want=40),
aoc.TestCase(inputs=SAMPLE, part=2, want=315),
)
TIMEOUT = 90

def part1(self, puzzle_input: InputType) -> int:
def part1(self, puzzle_input: aoc.Map) -> int:
"""Return the lowest cost path from start to end."""
return self.solve(puzzle_input)

def part2(self, puzzle_input: InputType) -> int:
def part2(self, puzzle_input: aoc.Map) -> int:
"""Return the lowest cost path from start to end ... with larger input."""
graph = puzzle_input
width = graph.width
height = graph.height

# Dictionary comprehension is hard to read.
# full_graph = {
# (x * width + point.real) + (y * height + point.imag) * 1j: (
# ((val + x + y - 1) % 9) + 1
# )
# for x in range(5) for y in range(5) for point, val in graph.items()
# }

full_graph = aoc.Board(diagonal=False)
full_graph = {}
for x in range(5):
for y in range(5):
for point, val in graph.items():
repeat_offset = x + y
for point, val in graph.chars.items():
# Scale the point by x, y
point = (x * width + point.real) + (y * height + point.imag) * 1j
# Add +1 to the value for each shift. Modulus 9 so that 10 wraps around to 1.
val = ((val + x + y - 1) % 9) + 1
val = ((val + repeat_offset - 1) % 9) + 1
# Insert the point.
full_graph[point] = val

return self.solve(full_graph)
rows = [
"".join(
str(full_graph[complex(x, y)])
for x in range(graph.width * 5)
)
for y in range(graph.height * 5)
]
graph = aoc.CoordinatesParser().parse("\n".join(rows))
return self.solve(graph)

@staticmethod
def solve(node_weights: InputType) -> int:
def solve(node_weights: aoc.Map) -> int:
"""Return the lowest cost path from start to end with Djiksta's."""
starting_point = complex(0)
end_point = node_weights.max_point
end_point = complex(node_weights.max_x, node_weights.max_y)

# Use Djiksta's to compute the cost from start to every node.
cost = {starting_point: 0}
visited = set()
todo = set([starting_point])
todo = {starting_point}
while todo:
# Pop the lowest cost node.
current = sorted(todo, key=lambda x: cost[x])[0]
Expand All @@ -84,7 +84,3 @@ def solve(node_weights: InputType) -> int:
# Add unvisited neighbors to the todo list.
todo.add(neighbor)
return cost[end_point]

def input_parser(self, puzzle_input: str) -> InputType:
"""Parse the input data."""
return aoc.Board.from_int_block(puzzle_input, diagonal=False)
11 changes: 4 additions & 7 deletions 2022/d08.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
35390"""
]

InputType = aoc.Map


class Day08(aoc.Challenge):
"""Day 8: Treetop Tree House."""
Expand All @@ -22,15 +20,14 @@ class Day08(aoc.Challenge):
aoc.TestCase(inputs=SAMPLE[0], part=1, want=21),
aoc.TestCase(inputs=SAMPLE[0], part=2, want=8),
]
INPUT_PARSER = aoc.ParseOneWord(aoc.Board.from_int_block)

def part1(self, puzzle_input: InputType) -> int:
def part1(self, puzzle_input: aoc.Map) -> int:
"""Return how many trees are visible from outside.
Walk the perimeter and peer into the forest.
Record how many trees we can see along every line.
"""
board = puzzle_input
board = puzzle_input.chars

visible: set[complex] = set()

Expand All @@ -57,9 +54,9 @@ def part1(self, puzzle_input: InputType) -> int:

return len(visible)

def part2(self, puzzle_input: InputType) -> int:
def part2(self, puzzle_input: aoc.Map) -> int:
"""Return the highest scenic score in the forest."""
board = puzzle_input
board = puzzle_input.chars
scores = []

for tree in board:
Expand Down
75 changes: 0 additions & 75 deletions pylib/aoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,81 +256,6 @@ def point_set_to_lists(points: set[complex]) -> list[list[bool]]:
return rows


class Board(dict):
"""A Board, represented as a set of complex points."""

def __init__(self, *args, diagonal=False, **kwargs):
super().__init__(*args, **kwargs)
self.diagonal = diagonal
assert all(isinstance(point, complex) for point in self.keys())

@property
def width(self):
"""Return the width of the board."""
return int(max(point.real for point in self)) + 1

@property
def height(self):
"""Return the height of the board."""
return int(max(point.imag for point in self)) + 1

@property
def max_point(self) -> complex:
"""Return the max point in the board."""
return complex(self.width - 1, self.height - 1)

@property
def corners(self) -> tuple[complex, complex, complex, complex]:
return (
0,
complex(0, self.height - 1),
complex(self.width - 1, 0),
complex(self.width - 1, self.height - 1)
)

def neighbors(self, point: complex) -> dict[complex, Any]:
"""Return the 4/8 neighbors of a point."""
neighbors = {}
for i in range(4):
if (candidate := point + -1j ** i) in self:
neighbors[candidate] = self[candidate]
if self.diagonal:
for i in range(4):
if (candidate := point + (1 + 1j) * -1j ** i) in self:
neighbors[candidate] = self[candidate]
return neighbors

def edges(self) -> set[complex]:
"""Return the edges of a fully populated board."""
edges: set[complex] = set()
cur = complex(0, 0)
for direction in FOUR_DIRECTIONS:
while cur + direction in self:
cur += direction
edges.add(cur)
return edges

@classmethod
def from_block_map(
cls,
block: str,
transform: Callable[[str], Any],
diagonal: bool = False,
) -> Board:
"""Return a Board from a block mapped with transform()."""
raw_dict = {
x + y * 1j: transform(char)
for y, line in enumerate(block.splitlines())
for x, char in enumerate(line)
}
return cls(raw_dict, diagonal=diagonal)

@classmethod
def from_int_block(cls, block: str, diagonal: bool = False) -> Board:
"""Return a Board from a block of ints."""
return cls.from_block_map(block, int, diagonal)


@dataclasses.dataclass(frozen=True)
class Point:
"""A cartesian point."""
Expand Down
2 changes: 1 addition & 1 deletion pylib/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,6 @@ def corners(self) -> tuple[complex, complex, complex, complex]:

def neighbors(self, point: complex, directions: collections.abc.Sequence[complex] = STRAIGHT_NEIGHBORS) -> dict[complex, T]:
"""Return neighboring points and values which are in the map."""
return [(n, self.chars[n]) for n in neighbors(directions) if n in self.all_coords]
return {n: self.chars[n] for n in neighbors(point, directions) if n in self.all_coords}


0 comments on commit f90d49b

Please sign in to comment.