commit 08e1a707644c3e90cfbcc230a38b6ec8bae4de3a
parent ae50a2184f97659c33b8f5fdf8c09fa3616a6dde
Author: Eamon Caddigan <eamon.caddigan@gmail.com>
Date: Wed, 22 Dec 2021 15:42:56 -0500
Solution to day 22, part 1
Diffstat:
A | day22_part1.py | | | 100 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 100 insertions(+), 0 deletions(-)
diff --git a/day22_part1.py b/day22_part1.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+"""Advent of Code 2021, day 22 (part 1): Reactor Reboot
+Apply on/off instructions to a 3D grid of cuboids"""
+
+# Ugh, the way this problem is written you just KNOW that Part 2 is going to
+# require an entirely different approach, but yesterday really burned me on
+# overthinking situations that didn't come up, so I'm going to be chill today.
+
+from typing import Tuple, List, NewType
+import re
+from functools import reduce
+
+# For part 1 we'll just represent everything in the -50..50 bounds explicitly.
+# I suspect part 2 will require a different approach!
+import numpy as np
+
+from utils import get_puzzle_input
+
+EXAMPLE_INPUT = \
+"""on x=-20..26,y=-36..17,z=-47..7
+on x=-20..33,y=-21..23,z=-26..28
+on x=-22..28,y=-29..23,z=-38..16
+on x=-46..7,y=-6..46,z=-50..-1
+on x=-49..1,y=-3..46,z=-24..28
+on x=2..47,y=-22..22,z=-23..27
+on x=-27..23,y=-28..26,z=-21..29
+on x=-39..5,y=-6..47,z=-3..44
+on x=-30..21,y=-8..43,z=-13..34
+on x=-22..26,y=-27..20,z=-29..19
+off x=-48..-32,y=26..41,z=-47..-37
+on x=-12..35,y=6..50,z=-50..-2
+off x=-48..-32,y=-32..-16,z=-15..-5
+on x=-18..26,y=-33..15,z=-7..46
+off x=-40..-22,y=-38..-28,z=23..41
+on x=-16..35,y=-41..10,z=-47..6
+off x=-32..-23,y=11..30,z=-14..3
+on x=-49..-5,y=-3..45,z=-29..18
+off x=18..30,y=-20..-8,z=-3..13
+on x=-41..9,y=-7..43,z=-33..15
+on x=-54112..-39298,y=-85059..-49293,z=-27449..7877
+on x=967..23432,y=45373..81175,z=27513..53682
+"""
+
+Instruction = NewType(
+ 'Instruction',
+ Tuple[bool, Tuple[int, int], Tuple[int, int], Tuple[int, int]]
+)
+
+def parse_input(input_string: str) -> List[Instruction]:
+ """Given the puzzle input, return a list of "on" (True) or "off" (False)
+ instructions and their x, y, and z bounds"""
+ range_pattern = r"(-?\d+)\.\.(-?\d+)"
+ groups = [
+ match.groups()
+ for l in input_string.rstrip('\n').split('\n')
+ if (match := re.match("^(on|off) x=" + range_pattern
+ + ",y=" + range_pattern
+ + ",z=" + range_pattern, l)) is not None
+ ]
+ return [Instruction((
+ {'on': True, 'off': False}[x[0]],
+ (int(x[1]), int(x[2])),
+ (int(x[3]), int(x[4])),
+ (int(x[5]), int(x[6]))
+ )) for x in groups]
+
+def inside_initialization_area(instruction: Instruction) -> bool:
+ """Returns True iff the instruction is fully inside the area -50..50 along
+ all thre axes"""
+ return all(-50 <= i <= 50 for p in instruction[1:4] for i in p)
+
+def apply_step(reactor: np.ndarray, instruction: Instruction) -> np.ndarray:
+ """Apply the given step to the reactor, and return a new reactor state"""
+ reactor_update = reactor.copy()
+ reactor_update[
+ (instruction[1][0]+50):(instruction[1][1]+51),
+ (instruction[2][0]+50):(instruction[2][1]+51),
+ (instruction[3][0]+50):(instruction[3][1]+51)
+ ] = instruction[0]
+ return reactor_update
+
+def reboot_reactor(instructions: List[Instruction]) -> np.ndarray:
+ """Starting with a reactor that's turned off, apply all the steps to
+ initialize the reactor"""
+ return reduce(apply_step,
+ instructions,
+ np.zeros((101, 101, 101), dtype=bool))
+
+def solve_puzzle(input_string: str) -> int:
+ """Return the numeric solution to the puzzle"""
+ return int(reboot_reactor(parse_input(input_string)).sum())
+
+def main() -> None:
+ """Run when the file is called as a script"""
+ assert solve_puzzle(EXAMPLE_INPUT) == 590784
+ print("Number of cubes turned on:",
+ solve_puzzle(get_puzzle_input(22)))
+
+if __name__ == "__main__":
+ main()