Skip to content

Commit 5c88bf1

Browse files
committed
Added solutions for day 23
1 parent 7b944f1 commit 5c88bf1

File tree

3 files changed

+170
-1
lines changed

3 files changed

+170
-1
lines changed

advent2020/day23.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# MIT License
2+
#
3+
# Copyright (c) 2021 Andrew Krepps
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
23+
24+
from . import util
25+
26+
27+
class CircularListNode:
28+
def __init__(self, data):
29+
self.data = data
30+
self.previous = self
31+
self.next = self
32+
33+
def insert_after(self, existing_node):
34+
self.next = existing_node.next
35+
self.previous = existing_node
36+
existing_node.next = self
37+
self.next.previous = self
38+
39+
def detach(self):
40+
self.previous.next = self.next
41+
self.next.previous = self.previous
42+
self.previous = self
43+
self.next = self
44+
45+
46+
def parse_input(lines):
47+
assert len(lines) == 1
48+
head_node = None
49+
tail_node = None
50+
node_map = {}
51+
for c in lines[0].strip():
52+
data = int(c)
53+
node = CircularListNode(data)
54+
node_map[data] = node
55+
if head_node is None:
56+
head_node = node
57+
else:
58+
node.insert_after(tail_node)
59+
tail_node = node
60+
return head_node, node_map
61+
62+
63+
def play_one_round(current_cup, cup_map, cups_per_round):
64+
current_label = current_cup.data
65+
extracted_cups = []
66+
extracted_labels = set()
67+
for _ in range(cups_per_round):
68+
extracted_cup = current_cup.next
69+
extracted_cup.detach()
70+
extracted_cups.append(extracted_cup)
71+
extracted_labels.add(extracted_cup.data)
72+
searching = True
73+
destination_label = current_label
74+
while searching:
75+
destination_label -= 1
76+
if destination_label <= 0:
77+
destination_label = len(cup_map)
78+
searching = destination_label in extracted_labels
79+
destination_cup = cup_map[destination_label]
80+
for cup in extracted_cups:
81+
cup.insert_after(destination_cup)
82+
destination_cup = cup
83+
return current_cup.next
84+
85+
86+
def play_game(current_cup, cup_map, num_rounds):
87+
for _ in range(num_rounds):
88+
current_cup = play_one_round(current_cup, cup_map, cups_per_round=3)
89+
90+
91+
def get_labels_after(cup):
92+
marker_label = cup.data
93+
labels = []
94+
cup = cup.next
95+
while cup.data != marker_label:
96+
labels.append(cup.data)
97+
cup = cup.next
98+
return labels
99+
100+
101+
def get_part1_answer(lines):
102+
current_cup, cup_map = parse_input(lines)
103+
play_game(current_cup, cup_map, num_rounds=100)
104+
return ''.join(str(label) for label in get_labels_after(cup_map[1]))
105+
106+
107+
def get_part2_answer(lines):
108+
current_cup, cup_map = parse_input(lines)
109+
tail_node = current_cup.previous
110+
for label in range(len(cup_map) + 1, 1000001):
111+
cup = CircularListNode(label)
112+
cup.insert_after(tail_node)
113+
cup_map[label] = cup
114+
tail_node = cup
115+
play_game(current_cup, cup_map, num_rounds=10000000)
116+
current_cup = cup_map[1]
117+
product = 1
118+
for _ in range(2):
119+
current_cup = current_cup.next
120+
product *= current_cup.data
121+
return product
122+
123+
124+
def run():
125+
lines = util.get_input_file_lines("day23.txt")
126+
print(f"The answer to part 1 is {get_part1_answer(lines)}")
127+
print(f"The answer to part 2 is {get_part2_answer(lines)}")

main.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
from advent2020 import day20
4646
from advent2020 import day21
4747
from advent2020 import day22
48+
from advent2020 import day23
4849

4950

5051
day_runners = [
@@ -69,7 +70,8 @@
6970
lambda: day19.run(),
7071
lambda: day20.run(),
7172
lambda: day21.run(),
72-
lambda: day22.run()
73+
lambda: day22.run(),
74+
lambda: day23.run()
7375
]
7476

7577

test/test_day23.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# MIT License
2+
#
3+
# Copyright (c) 2021 Andrew Krepps
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
23+
24+
import unittest
25+
26+
from advent2020.day23 import get_part1_answer
27+
from advent2020.day23 import get_part2_answer
28+
from advent2020.util import get_input_data_lines
29+
30+
31+
data = """
32+
389125467
33+
"""
34+
35+
36+
class Day23Test(unittest.TestCase):
37+
def test_day23(self):
38+
lines = get_input_data_lines(data)
39+
self.assertEqual(get_part1_answer(lines), "67384529")
40+
self.assertEqual(get_part2_answer(lines), 149245887792)

0 commit comments

Comments
 (0)