From b34578de33b5a3f600e410973ec0499ef725c36f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Doblas=20Jim=C3=A9nez?= Date: Mon, 1 Aug 2022 18:30:43 +0200 Subject: [PATCH] Solution to problem 12 in Python --- src/Year_2018/P12.py | 203 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 src/Year_2018/P12.py diff --git a/src/Year_2018/P12.py b/src/Year_2018/P12.py new file mode 100644 index 0000000..9a04c84 --- /dev/null +++ b/src/Year_2018/P12.py @@ -0,0 +1,203 @@ +# --- Day 12: Subterranean Sustainability --- + +# The year 518 is significantly more underground than your history books +# implied. Either that, or you've arrived in a vast cavern network under the +# North Pole. + +# After exploring a little, you discover a long tunnel that contains a row of +# small pots as far as you can see to your left and right. A few of them +# contain plants - someone is trying to grow things in these +# geothermally-heated caves. + +# The pots are numbered, with 0 in front of you. To the left, the pots are +# numbered -1, -2, -3, and so on; to the right, 1, 2, 3.... Your puzzle input +# contains a list of pots from 0 to the right and whether they do (#) or do not +# (.) currently contain a plant, the initial state. (No other pots currently +# contain plants.) For example, an initial state of #..##.... indicates that +# pots 0, 3, and 4 currently contain plants. + +# Your puzzle input also contains some notes you find on a nearby table: +# someone has been trying to figure out how these plants spread to nearby pots. +# Based on the notes, for each generation of plants, a given pot has or does +# not have a plant based on whether that pot (and the two pots on either side +# of it) had a plant in the last generation. These are written as LLCRR => N, +# where L are pots to the left, C is the current pot being considered, R are +# the pots to the right, and N is whether the current pot will have a plant in +# the next generation. For example: + +# A note like ..#.. => . means that a pot that contains a plant but with no +# plants within two pots of it will not have a plant in it during the next +# generation. +# A note like ##.## => . means that an empty pot with two plants on each +# side of it will remain empty in the next generation. +# A note like .##.# => # means that a pot has a plant in a given generation +# if, in the previous generation, there were plants in that pot, the one +# immediately to the left, and the one two pots to the right, but not in the +# ones immediately to the right and two to the left. + +# It's not clear what these plants are for, but you're sure it's important, so +# you'd like to make sure the current configuration of plants is sustainable by +# determining what will happen after 20 generations. + +# For example, given the following input: + +# initial state: #..#.#..##......###...### + +# ...## => # +# ..#.. => # +# .#... => # +# .#.#. => # +# .#.## => # +# .##.. => # +# .#### => # +# #.#.# => # +# #.### => # +# ##.#. => # +# ##.## => # +# ###.. => # +# ###.# => # +# ####. => # + +# For brevity, in this example, only the combinations which do produce a plant +# are listed. (Your input includes all possible combinations.) Then, the next +# 20 generations will look like this: + +# 1 2 3 +# 0 0 0 0 +# 0: ...#..#.#..##......###...###........... +# 1: ...#...#....#.....#..#..#..#........... +# 2: ...##..##...##....#..#..#..##.......... +# 3: ..#.#...#..#.#....#..#..#...#.......... +# 4: ...#.#..#...#.#...#..#..##..##......... +# 5: ....#...##...#.#..#..#...#...#......... +# 6: ....##.#.#....#...#..##..##..##........ +# 7: ...#..###.#...##..#...#...#...#........ +# 8: ...#....##.#.#.#..##..##..##..##....... +# 9: ...##..#..#####....#...#...#...#....... +# 10: ..#.#..#...#.##....##..##..##..##...... +# 11: ...#...##...#.#...#.#...#...#...#...... +# 12: ...##.#.#....#.#...#.#..##..##..##..... +# 13: ..#..###.#....#.#...#....#...#...#..... +# 14: ..#....##.#....#.#..##...##..##..##.... +# 15: ..##..#..#.#....#....#..#.#...#...#.... +# 16: .#.#..#...#.#...##...#...#.#..##..##... +# 17: ..#...##...#.#.#.#...##...#....#...#... +# 18: ..##.#.#....#####.#.#.#...##...##..##.. +# 19: .#..###.#..#.#.#######.#.#.#..#.#...#.. +# 20: .#....##....#####...#######....#.#..##. + +# The generation is shown along the left, where 0 is the initial state. The pot +# numbers are shown along the top, where 0 labels the center pot, +# negative-numbered pots extend to the left, and positive pots extend toward +# the right. Remember, the initial state begins at pot 0, which is not the +# leftmost pot used in this example. + +# After one generation, only seven plants remain. The one in pot 0 matched the +# rule looking for ..#.., the one in pot 4 matched the rule looking for .#.#., +# pot 9 matched .##.., and so on. + +# In this example, after 20 generations, the pots shown as # contain plants, +# the furthest left of which is pot -2, and the furthest right of which is pot +# 34. Adding up all the numbers of plant-containing pots after the 20th +# generation produces 325. + +# After 20 generations, what is the sum of the numbers of all pots which +# contain a plant? + +with open("files/P12.txt") as f: + raw = [line for line in f.read().strip().split("\n")] + + +def parse_data() -> tuple[str, dict[str, str]]: + rules = {} + for line in raw: + if "initial state" in line: + initial_state = line[15:] + else: + rules[line[:5]] = line[9:10] + + return initial_state, rules + + +def part_1() -> None: + initial_state, rules = parse_data() + current = dict( + (idx, char) for idx, char in enumerate(initial_state) if char == "#" + ) + _sum, difference = 0, {} + for gen in range(20): + # notes are given for a given pot plus two on each side + left_side, right_side = min(current) - 2, max(current) + 2 + next_gen = {} + for char in range(left_side, right_side + 1): + pattern = "" + for idx in range(char - 2, char + 3): + if idx in current: + pattern += current[idx] + else: + pattern += "." + next_gen[char] = rules[pattern] + current = dict( + (idx, next_gen[idx]) for idx in next_gen if next_gen[idx] == "#" + ) + diff = sum(current) - _sum + if diff not in difference: + difference[diff] = 1 + else: + difference[diff] += 1 + _sum = sum(current) + print( + f"After 20 generations, there will be {sum(current)} pots containing plants" + ) + + +# --- Part Two --- + +# You realize that 20 generations aren't enough. After all, these plants will +# need to last another 1500 years to even reach your timeline, not to mention +# your future. + +# After fifty billion (50000000000) generations, what is the sum of the numbers +# of all pots which contain a plant? + + +def part_2() -> None: + generations = 50_000_000_000 + initial_state, rules = parse_data() + current = dict( + (idx, char) for idx, char in enumerate(initial_state) if char == "#" + ) + _sum, difference = 0, {} + for gen in range(generations): + # notes are given for a given pot plus two on each side + left_side, right_side = min(current) - 2, max(current) + 2 + next_gen = {} + for char in range(left_side, right_side + 1): + pattern = "" + for idx in range(char - 2, char + 3): + if idx in current: + pattern += current[idx] + else: + pattern += "." + next_gen[char] = rules[pattern] + current = dict( + (idx, next_gen[idx]) for idx in next_gen if next_gen[idx] == "#" + ) + diff = sum(current) - _sum + if diff not in difference: + difference[diff] = 1 + else: + difference[diff] += 1 + _sum = sum(current) + # it is a periodic result, so skip most of the calculations + if diff in difference and difference[diff] > 1000: + res = sum(current) + (generations - gen - 1) * diff + print( + f"After 5e10 generations, there will be {res} pots containing plants" + ) + break + + +if __name__ == "__main__": + part_1() + part_2()