advent_of_code_2021

My attempts to work through the 2021 Advent of Code problems.
git clone https://git.eamoncaddigan.net/advent_of_code_2021.git
Log | Files | Refs | README | LICENSE

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:
Aday22_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()