Skip to content

Commit

Permalink
Convert to base class to faciliate inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
loociano committed Dec 15, 2024
1 parent 292ced7 commit beecb07
Showing 1 changed file with 66 additions and 49 deletions.
115 changes: 66 additions & 49 deletions aoc2024/src/day15/python/solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,24 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Sequence
from typing import Sequence, override

type WarehouseMap = Sequence[Sequence[str]]
type Position = tuple[int, int] # (x,y)
type Direction = tuple[int, int] # (x,y)
_ROBOT = '@'
_BOX = 'O'
_WALL = '#'
_EMPTY = '.'
_DIR_TO_OPPOSITE = {
(0, 1): (0, -1), # Right to left.
(0, -1): (0, 1), # Left to right.
(-1, 0): (1, 0), # Up to down.
(1, 0): (-1, 0), # Down to up.
}


class Warehouse:


class BaseWarehouse:
_ROBOT = '@'
_WALL = '#'
_EMPTY = '.'
_DIR_TO_OPPOSITE = {
(0, 1): (0, -1), # Right to left.
(0, -1): (0, 1), # Left to right.
(-1, 0): (1, 0), # Up to down.
(1, 0): (-1, 0), # Down to up.
}

def __init__(self, warehouse_map: WarehouseMap, robot_moves: Sequence[Position]):
self._warehouse_map: list[list[str]] = [list(line) for line in warehouse_map]
self._width = len(self._warehouse_map[0])
Expand All @@ -44,25 +44,23 @@ def simulate(self) -> None:
self._next_move_index += 1

def sum_all_boxes_gps_coordinates(self) -> int:
"""Returns the sum of all the boxes' GPS coordinates.
A box GPS coordinate is 100 times its distance from the top edge of the
warehouse plus its distance from the left edge of the map.
"""
result = 0
for y in range(self._height):
for x in range(self._width):
if self._charAt(pos=(x, y)) == _BOX:
result += 100 * y + x
return result
raise NotImplementedError()

def _find_robot_pos(self) -> Position:
"""Returns the position of the robot."""
for y in range(self._height):
for x in range(self._width):
if self._warehouse_map[y][x] == _ROBOT:
if self._warehouse_map[y][x] == self._ROBOT:
return x, y
raise ValueError('Robot not found!')

def _move_robot(self) -> None:
"""Attempts to move robot one step. If the robot cannot move, it is a NOP."""
raise NotImplementedError()

def _push(self, pos: Position, dir: Direction) -> None:
raise NotImplementedError()

def _charAt(self, pos: Position) -> str:
"""Returns what element is on the warehouse at a given position."""
return self._warehouse_map[pos[1]][pos[0]]
Expand All @@ -71,48 +69,67 @@ def _update(self, pos: Position, value: str) -> None:
"""Updates a warehouse position with a given element."""
self._warehouse_map[pos[1]][pos[0]] = value

def _within_bounds(self, pos: Position):
"""Returns true if a position is within warehouse bounds."""
return 0 <= pos[0] < self._width and 0 <= pos[1] < self._height


class Warehouse(BaseWarehouse):
_BOX = 'O'

@override
def sum_all_boxes_gps_coordinates(self) -> int:
"""Returns the sum of all the boxes' GPS coordinates.
A box GPS coordinate is 100 times its distance from the top edge of the
warehouse plus its distance from the left edge of the map.
"""
result = 0
for y in range(self._height):
for x in range(self._width):
if self._charAt(pos=(x, y)) == self._BOX:
result += 100 * y + x
return result

@override
def _move_robot(self) -> None:
"""Attempts to move robot one step. If the robot cannot move, it is a NOP."""
next_move = self._robot_moves[self._next_move_index]
next_pos = (self._robot_pos[0] + next_move[0], self._robot_pos[1] + next_move[1])
if self._charAt(next_pos) == _EMPTY:
self._update(self._robot_pos, _EMPTY)
if self._charAt(next_pos) == self._EMPTY:
self._update(self._robot_pos, self._EMPTY)
self._robot_pos = next_pos
self._update(self._robot_pos, _ROBOT)
elif self._charAt(next_pos) == _BOX:
self._update(self._robot_pos, self._ROBOT)
elif self._charAt(next_pos) == self._BOX:
self._push(pos=next_pos, dir=next_move)
if self._charAt(next_pos) == _EMPTY:
self._update(self._robot_pos, _EMPTY)
if self._charAt(next_pos) == self._EMPTY:
self._update(self._robot_pos, self._EMPTY)
self._robot_pos = next_pos
self._update(self._robot_pos, _ROBOT)
self._update(self._robot_pos, self._ROBOT)

def _within_bounds(self, pos: Position):
"""Returns true if a position is within warehouse bounds."""
return 0 <= pos[0] < self._width and 0 <= pos[1] < self._height
@override
def _push(self, pos: Position, dir: Direction) -> None:
"""Attempts to push boxes from given position towards given direction.
If there is no space to move boxes, it is a NOP."""
next_empty_pos = self._find_next_empty_pos(pos, dir)
if next_empty_pos is not None:
next_pos = next_empty_pos
while next_pos != pos:
self._update(next_pos, self._BOX)
opposite_dir = self._DIR_TO_OPPOSITE.get(dir)
next_pos = (next_pos[0] + opposite_dir[0], next_pos[1] + opposite_dir[1])
self._update(pos, self._EMPTY)

def _find_next_empty_pos(self, pos: Position, dir: Direction) -> Position | None:
"""Returns the next empty space from a position towards a given direction."""
while self._within_bounds(pos):
if self._charAt(pos) == _WALL:
if self._charAt(pos) == self._WALL:
return None # We hit a wall before an empty space.
if self._charAt(pos) == _EMPTY:
if self._charAt(pos) == self._EMPTY:
return pos
pos = (pos[0] + dir[0], pos[1] + dir[1])
# Empty space was not found.
return None

def _push(self, pos: Position, dir: Direction) -> None:
"""Attempts to push boxes from given position towards given direction.
If there is no space to move boxes, it is a NOP."""
next_empty_pos = self._find_next_empty_pos(pos, dir)
if next_empty_pos is not None:
next_pos = next_empty_pos
while next_pos != pos:
self._update(next_pos, _BOX)
opposite_dir = _DIR_TO_OPPOSITE.get(dir)
next_pos = (next_pos[0] + opposite_dir[0], next_pos[1] + opposite_dir[1])
self._update(pos, _EMPTY)


def _parse(input: Sequence[str]) -> tuple[WarehouseMap, Sequence[Position]]:
"""Parses the input and returns the warehouse map and sequence of robot moves."""
Expand Down

0 comments on commit beecb07

Please sign in to comment.