refactored out Position

This commit is contained in:
Ruediger Ludwig 2023-01-08 14:02:48 +01:00
parent 923e967056
commit b83bb6b37a
12 changed files with 252 additions and 216 deletions

View file

@ -5,6 +5,8 @@ from queue import PriorityQueue
from typing import Iterator
from advent.common.position import UNIT_NEG_X, UNIT_NEG_Y, UNIT_X, UNIT_Y, Position
day_num = 24
@ -32,7 +34,6 @@ def lcm(num1: int, num2: int) -> int:
return num1 * num2 // gcd(num1, num2)
Position = tuple[int, int]
BlizList = list[Position]
BlizTuple = tuple[BlizList, BlizList, BlizList, BlizList]
@ -54,10 +55,10 @@ class Direction(IntEnum):
def position(self) -> Position:
match self:
case Direction.East: return 1, 0
case Direction.North: return 0, -1
case Direction.West: return -1, 0
case Direction.South: return 0, 1
case Direction.East: return UNIT_X
case Direction.North: return UNIT_NEG_Y
case Direction.West: return UNIT_NEG_X
case Direction.South: return UNIT_Y
@property
def char(self) -> str:
@ -77,10 +78,10 @@ class Weather:
def print(self, time: int) -> list[str]:
current = self.blizzards[self.normal_time(time)]
lines: list[str] = []
for row in range(self.extent[1]):
for row in range(self.extent.y):
line = ""
for col in range(self.extent[0]):
if (char := current.get((col, row))) is not None:
for col in range(self.extent.x):
if (char := current.get(Position(col, row))) is not None:
line += char
else:
line += '.'
@ -95,7 +96,7 @@ class Weather:
@classmethod
def predict_weather(cls, blizzards: BlizTuple, extent: Position) -> Weather:
repeat = lcm(extent[0], extent[1])
repeat = lcm(extent.x, extent.y)
weather: list[dict[Position, str]] = [Weather.create_dict(blizzards)]
for _ in range(repeat - 1):
blizzards = Weather.progress_blizzards(blizzards, extent)
@ -108,9 +109,9 @@ class Weather:
add = Direction.East.position()
result: BlizList = []
for pos in blizzards:
next_pos = pos[0] + add[0], pos[1] + add[1]
if next_pos[0] >= extent[0]:
next_pos = 0, next_pos[1]
next_pos = pos + add
if next_pos.x >= extent.x:
next_pos = next_pos.set_x(0)
result.append(next_pos)
return result
@ -120,9 +121,9 @@ class Weather:
add = Direction.West.position()
result: BlizList = []
for pos in blizzards:
next_pos = pos[0] + add[0], pos[1] + add[1]
if next_pos[0] < 0:
next_pos = extent[0] - 1, next_pos[1]
next_pos = pos + add
if next_pos.x < 0:
next_pos = next_pos.set_x(extent.x - 1)
result.append(next_pos)
return result
@ -132,9 +133,9 @@ class Weather:
add = Direction.South.position()
result: BlizList = []
for pos in blizzards:
next_pos = pos[0] + add[0], pos[1] + add[1]
if next_pos[1] >= extent[1]:
next_pos = next_pos[0], 0
next_pos = pos + add
if next_pos.y >= extent.y:
next_pos = next_pos.set_y(0)
result.append(next_pos)
return result
@ -144,9 +145,9 @@ class Weather:
add = Direction.North.position()
result: BlizList = []
for pos in blizzards:
next_pos = pos[0] + add[0], pos[1] + add[1]
if next_pos[1] < 0:
next_pos = next_pos[0], extent[1] - 1
next_pos = pos + add
if next_pos.y < 0:
next_pos = next_pos.set_y(extent.y - 1)
result.append(next_pos)
return result
@ -206,7 +207,7 @@ class Valley:
case '#':
return blizzards
case '>' | '^' | '<' | 'v':
blizzards = Valley.append(blizzards, Direction.create(char), (col, row))
blizzards = Valley.append(blizzards, Direction.create(char), Position(col, row))
case '.':
pass
case _:
@ -223,9 +224,9 @@ class Valley:
for row, line in enumerate(lines):
if line.startswith("##"):
end_col = Valley._get_wallbreak(line)
extent = width, row
extent = Position(width, row)
return Valley(Weather.predict_weather(blizzards, extent), extent,
(start_col, -1), (end_col, row))
Position(start_col, -1), Position(end_col, row))
else:
blizzards = Valley.parse_line(blizzards, line, row)
assert False, "Unreachable"
@ -234,8 +235,8 @@ class Valley:
return '\n'.join(self.print(0))
def print(self, time: int) -> list[str]:
first = '#' + ('#' * self.start[0]) + '.' + ('#' * (self.extent[0] - self.start[0]))
last = '#' + ('#' * self.exit[0]) + '.' + ('#' * (self.extent[0] - self.exit[0]))
first = '#' + ('#' * self.start.x) + '.' + ('#' * (self.extent.x - self.start.x))
last = '#' + ('#' * self.exit.x) + '.' + ('#' * (self.extent.x - self.exit.x))
lines = self.weather.print(time)
lines = [first] + ['#' + line + '#' for line in lines] + [last]
return lines
@ -284,9 +285,9 @@ class Step:
def print(self) -> list[str]:
lines = self.valley.print(self.time)
line = lines[self.position[1] + 1]
lines[self.position[1] + 1] = line[:self.position[0] + 1] + \
'E' + line[self.position[0] + 2:]
line = lines[self.position.y + 1]
lines[self.position.y + 1] = line[:self.position.x + 1] + \
'E' + line[self.position.x + 2:]
return lines
@ -326,11 +327,11 @@ class Step:
for dir in Direction:
add = dir.position()
next_position = self.position[0] + add[0], self.position[1] + add[1]
next_position = self.position + add
if next_position == self.target:
yield self.reach_target()
elif (0 <= next_position[0] < self.valley.extent[0]
and 0 <= next_position[1] < self.valley.extent[1]):
elif (0 <= next_position.x < self.valley.extent.x
and 0 <= next_position.y < self.valley.extent.y):
if next_position not in impassable:
yield self.move(next_position)

View file

@ -1,4 +1,5 @@
from advent.common import input
from advent.common.position import Position
from .solution import BlizTuple, Valley, day_num, part1, part2
@ -20,9 +21,9 @@ def test_part2():
def test_parse_line():
input = "#>>.<^<#"
expected: BlizTuple = (
[(0, 0), (1, 0)],
[(4, 0)],
[(3, 0), (5, 0)],
[Position(0, 0), Position(1, 0)],
[Position(4, 0)],
[Position(3, 0), Position(5, 0)],
[])
result = Valley.parse_line(([], [], [], []), input, 0)
assert result == expected