2015, day 16-25 (first completed calendar!) (#11)

* Solve 2015:16 "Aunt Sue"

* Make 2023:08 future compatible

Code used to work with another version of python.

* Solve 2015:17 "No such Thing as Too much"

* Solve 2015:18 "Like a GIF For Your Yard"

Also solve 2015:06 just in case, was just a ref
in the end.

* Solve 2015:19 "Medicine for Rudolph"

* Solve 2015:20 "Infinite Elves and Infinite Houses"

* Solve 2023:21 "RPG Simulator 20XX"

* Solve 2015:22 "Wizard Simulator 20XX"

* Solve 2015:23 "Opening the Turing Lock"

* Solve 2015:25 "Let it Snow"

Wrote p2rc and rc2p just for academic purposes.
Puzzles could be solved anyway.

* Solve 2015:24 "Hangs in the Balance"

---------

Co-authored-by: Anders Englöf Ytterström <anders@playmaker.ai>
This commit is contained in:
Anders Englöf Ytterström 2023-12-19 14:24:28 +01:00 committed by GitHub
parent b6e048e4a7
commit 5a5e843129
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 857 additions and 53 deletions

View file

@ -26,65 +26,29 @@ class Solution(BaseSolution):
def __str__(self):
return "Day {day}: {name}"
def solve(self, pi):
return pi
def solve_again(self, pi):
return pi
def parse_input(self, data):
return data.strip()
def solve(self, puzzle_input):
return True
def solve_again(self, puzzle_input):
return True
if __name__ == "__main__":
solution = Solution()
solution.show_results()
""".strip().format(
day=day_no, day_no=day_no.zfill(2), name=name
)
# solution.show_results()
dummy = \"\"\"
replace me
\"\"\".strip()
solution.solve(dummy)
# solution.solve_again(dummy)
""".strip().format(day=day_no, day_no=day_no.zfill(2), name=name)
+ "\n"
)
print(f"- creating tests/test_day_{day_no.zfill(2)}.py")
with open("tests/test_day_{}.py".format(day_no.zfill(2)), "w") as t:
t.write(
"""
import unittest
from solutions.day_{day_no} import Solution
class Day{day_no}TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
self.puzzle_input = self.solution.parse_input(
\"\"\"
<REPLACE ME>
\"\"\"
)
def test_parse_puzzle_input(self):
data = \"\"\"
<REPLACE ME>
\"\"\"
assert self.solution.parse_input(data) == "<REPLACE ME>"
# def test_solve_first_part(self):
# assert self.solution.solve(self.puzzle_input) == True
# def test_solve_second_part(self):
# assert self.solution.solve_again(self.puzzle_input) == True
if __name__ == "__main__":
unittest.main()
""".strip().format(
day_no=day_no.zfill(2)
)
+ "\n"
)
print(f"- creating empty inputs/{day_no.zfill(2)}.txt")
with open("inputs/{}.txt".format(day_no.zfill(2)), "w") as i:
i.write("")
print(
f"""

View file

@ -0,0 +1,50 @@
import re
from solutions import BaseSolution
from collections import defaultdict
class Solution(BaseSolution):
input_file = "06.txt"
def __str__(self):
return "Day 6: Probably a Fire Hazard"
def parse_input(self, data):
return data.strip()
def solve(self, puzzle_input):
m = defaultdict(bool)
for ase in puzzle_input.splitlines():
p = re.match(r"^(.+) (\d+),(\d+)\D+(\d+),(\d+)", ase).groups()
a, sx, sy, ex, ey = p
for r in range(int(sy), int(ey) + 1):
for c in range(int(sx), int(ex) + 1):
match a:
case "toggle":
m[(r, c)] = not m[(r, c)]
case "turn on":
m[(r, c)] = True
case "turn off":
m[(r, c)] = False
return sum(m.values())
def solve_again(self, puzzle_input):
m = defaultdict(int)
for ase in puzzle_input.splitlines():
p = re.match(r"^(.+) (\d+),(\d+)\D+(\d+),(\d+)", ase).groups()
a, sx, sy, ex, ey = p
for r in range(int(sy), int(ey) + 1):
for c in range(int(sx), int(ex) + 1):
match a:
case "toggle":
m[(r, c)] += 2
case "turn on":
m[(r, c)] += 1
case "turn off":
m[(r, c)] = max(m[(r, c)] - 1, 0)
return sum(m.values())
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -1,4 +1,5 @@
import re
from solutions import BaseSolution
@ -20,8 +21,8 @@ class Solution(BaseSolution):
def solve_again(self, puzzle_input):
def lendiff(line):
encoded = f'"{line.replace("\\", "\\\\").replace('"', '\\"')}"'
return len(encoded) - len(line)
encoded = line.replace("\\", "\\\\").replace('"', '\\"')
return len(encoded) + 2 - len(line)
return sum(map(lendiff, puzzle_input))

View file

@ -0,0 +1,56 @@
import re
from collections import defaultdict
from solutions import BaseSolution
S = {
"children": 3,
"cats": 7,
"samoyeds": 2,
"pomeranians": 3,
"akitas": 0,
"vizslas": 0,
"goldfish": 5,
"trees": 3,
"cars": 2,
"perfumes": 1,
}
class Solution(BaseSolution):
input_file = "16.txt"
def __str__(self):
return "Day 16: Aunt Sue"
def parse_input(self, data):
return data.strip()
def solve(self, puzzle_input):
s = defaultdict(int)
for i, l in enumerate(puzzle_input.splitlines(), 1):
for kv in re.findall(r"\w+: \d+", l):
k, v = kv.split(": ")
v = int(v)
s[i] += 1 if k in S and S[k] == v else 0
return max(s.items(), key=lambda x: x[1])[0]
def solve_again(self, puzzle_input):
s = defaultdict(int)
for i, l in enumerate(puzzle_input.splitlines(), 1):
for kv in re.findall(r"\w+: \d+", l):
k, v = kv.split(": ")
v = int(v)
match k:
case "cats" | "trees":
s[i] += 1 if k in S and S[k] < v else 0
case "pomeranians" | "goldfish":
s[i] += 1 if k in S and S[k] > v else 0
case _:
s[i] += 1 if k in S and S[k] == v else 0
return max(s.items(), key=lambda x: x[1])[0]
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,36 @@
from collections import defaultdict
from itertools import combinations
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = "17.txt"
def __str__(self):
return "Day 17: No Such Thing as Too Much"
def parse_input(self, data):
return data.strip()
def solve(self, puzzle_input, g=150):
return self._solve(puzzle_input, g)[0]
def solve_again(self, puzzle_input, g=150):
return self._solve(puzzle_input, g)[1]
def _solve(self, puzzle_input, g=150):
buckets = sorted(list(map(int, puzzle_input.split())), reverse=True)
t = 0
r = defaultdict(int)
for i in range(len(buckets)):
for c in combinations(buckets, i):
if sum(c) == g:
t += 1
r[i] += 1
return t, min(r.items(), key=lambda kv: kv[0])[1]
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,79 @@
from collections import defaultdict
from solutions import BaseSolution
N = [
(-1, -1),
(-1, 0),
(-1, 1),
(0, -1),
(0, 1),
(1, -1),
(1, 0),
(1, 1),
]
class Solution(BaseSolution):
input_file = "18.txt"
def __str__(self):
return "Day 18: Like a GIF For Your Yard"
def parse_input(self, data):
return data.strip()
def solve(self, data):
m = defaultdict(bool)
rows = data.split()
h = len(rows)
w = len(rows[0])
for r in range(h):
for c in range(w):
m[(r, c)] = 1 if rows[r][c] == "#" else 0
for _ in range(100):
nm = defaultdict(bool)
for r in range(h):
for c in range(w):
n = sum(m[(r + nr, c + nc)] for nr, nc in N)
match m[(r, c)]:
case 1:
nm[(r, c)] = n in (2, 3)
case 0:
nm[(r, c)] = n == 3
m = nm
return sum(m.values())
def solve_again(self, data):
m = defaultdict(bool)
rows = data.split()
h = len(rows)
w = len(rows[0])
for r in range(h):
for c in range(w):
m[(r, c)] = 1 if rows[r][c] == "#" else 0
m[(0, 0)] = True
m[(99, 0)] = True
m[(99, 99)] = True
m[(0, 99)] = True
for _ in range(100):
nm = defaultdict(bool)
for r in range(h):
for c in range(w):
if (r, c) in [(0, 0), (99, 0), (99, 99), (0, 99)]:
nm[(r, c)] = True
continue
n = sum(m[(r + nr, c + nc)] for nr, nc in N)
match m[(r, c)]:
case 1:
nm[(r, c)] = n in (2, 3)
case 0:
nm[(r, c)] = n == 3
m = nm
return sum(m.values())
return True
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,51 @@
from collections import defaultdict
from solutions import BaseSolution
import re
from random import shuffle, choice
class Solution(BaseSolution):
input_file = "19.txt"
def __str__(self):
return "Day 19: Medicine for Rudolph"
def solve(self, pi):
ml, s = pi.split("\n\n")
o = set()
for l in ml.splitlines():
k, v = l.split(" => ")
for mo in re.finditer(k, s):
a, b = mo.span()
o.add(s[:a] + v + s[b:])
return len(o)
def solve_again(self, pi):
m = {}
ml, e = pi.split("\n\n")
for l in ml.splitlines():
k, v = l.split(" => ")
m[v] = k
p2 = 0
while p2 == 0:
s = e
c = 0
os = ""
keys = list(m.keys())
shuffle(keys)
while os != s:
os = s
for k in keys:
while k in s:
c += s.count(k)
s = s.replace(k, m[k])
p2 = int(s == "e") * c
return p2
def parse_input(self, data):
return data.strip()
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,51 @@
from collections import defaultdict
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = "20.txt"
def __str__(self):
return "Day 20: Infinite Elves and Infinite Houses"
def solve(self, pi):
S = 36_000_000
# hn = 831601
# x1 = defaultdict(int)
# for i in range(1, hn):
# for n in range(0, hn, i):
# if n == 0:
# continue
# x1[n] += i * 10
# for k, v in x1.items():
# if v >= S:
# print(k, v)
hn = 885000
hc = 50
x2 = defaultdict(int)
for i in range(1, hn):
for n in range(0, hc * i + 1, i):
if n == 0:
continue
x2[n] += i * 11
# p1 = max(x1.items(), key=lambda y: y[1])[0]
p2 = min(filter(lambda x: x[1] >= S, x2.items()), key=lambda y: y[0])[0]
return 0, p2
def solve_again(self, pi):
return pi
def parse_input(self, data):
return data.strip()
if __name__ == "__main__":
solution = Solution()
# solution.show_results()
dummy = """
replace me
""".strip()
print(solution.solve(dummy))
# solution.solve_again(dummy)

View file

@ -0,0 +1,100 @@
import re
from itertools import product
from math import inf
from solutions import BaseSolution
SHOP = [
[
("Dagger", 8, 4, 0),
("Shortsword", 10, 5, 0),
("Warhammer", 25, 6, 0),
("Longsword", 40, 7, 0),
("Greataxe", 74, 8, 0),
],
[
("No armor", 0, 0, 0),
("Leather", 13, 0, 1),
("Chainmail", 31, 0, 2),
("Splintmail", 53, 0, 3),
("Bandedmail", 75, 0, 4),
("Platemail", 102, 0, 5),
],
[
("No ring", 0, 0, 0),
("Damage +1", 25, 1, 0),
("Damage +2", 50, 2, 0),
("Damage +3", 100, 3, 0),
("Defense +1", 20, 0, 1),
("Defense +2", 40, 0, 2),
("Defense +3", 80, 0, 3),
("Damage +1 & Damage +2", 75, 3, 0),
("Damage +1 & Damage +3", 125, 4, 0),
("Damage +1 & Defense +1", 45, 1, 1),
("Damage +1 & Defense +2", 65, 1, 2),
("Damage +1 & Defense +3", 105, 1, 3),
("Damage +2 & Damage +3", 150, 5, 0),
("Damage +2 & Defense +1", 70, 2, 1),
("Damage +2 & Defense +2", 90, 2, 2),
("Damage +2 & Defense +3", 130, 2, 3),
("Damage +3 & Defense +1", 120, 3, 1),
("Damage +3 & Defense +2", 140, 3, 2),
("Damage +3 & Defense +3", 180, 3, 3),
("Defense +1 & Defense +2", 60, 0, 3),
("Defense +1 & Defense +3", 100, 0, 4),
("Defense +2 & Defense +3", 120, 0, 5),
],
]
class Solution(BaseSolution):
input_file = "21.txt"
def __str__(self):
return "Day 21: RPG Simulator 20XX"
def solve(self, pi):
o = self._solve(pi)
return o[0]
def solve_again(self, pi):
o = self._solve(pi)
return o[1]
def parse_input(self, data):
return data.strip()
def _solve(self, pi):
boss = re.findall(r"\d+", pi)
l = inf
m = 0
for ph, pd, pa, c in self._gearup():
bh, bd, ba = [int(v) for v in boss]
while bh > 0 and ph > 0:
bh -= max(1, pd - ba)
if bh <= 0:
break
ph -= max(1, bd - pa)
if ph > 0:
l = min(l, c)
else:
m = max(m, c)
return l, m
def _gearup(self):
g = []
for w, a, r in product(*SHOP):
g.append(
(
100,
sum([w[2], a[2], r[2]]),
sum([w[3], a[3], r[3]]),
sum([w[1], a[1], r[1]]),
)
)
return g
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,151 @@
import random
import re
from math import inf
from solutions import BaseSolution
C = {
"M": 53,
"D": 73,
"S": 113,
"P": 173,
"R": 229,
}
D = {
"S": 6,
"P": 6,
"R": 5,
}
E = {
"P": 3,
"R": 101,
"S": 7,
}
class Solution(BaseSolution):
input_file = "22.txt"
def __str__(self):
return "Day 22: Wizard Simulator 20XX"
def solve(self, pi):
o = self._solve(pi)
return o[0]
def solve_again(self, pi):
o = self._solve(pi)
return o[1]
def parse_input(self, data):
return data.strip()
def _solve(self, data):
p12 = []
boss = re.findall(r"\d+", data)
shp, smp = 50, 500
for hd in [False, True]:
x = 100_000
l = inf
for _i in range(x):
bhp, bd = [int(v) for v in boss]
t, ms = 0, 0
hp, mp = shp, smp
e = {"S": 0, "P": 0, "R": 0}
while hp > 0 and bhp > 0 and mp > 0:
if hd and t % 2 == 0:
hp -= 1
if hp == 0:
break
b = {"d": bd, "e": 0}
p = {"e": 0}
if e["P"] > 0:
e["P"] -= 1
bhp -= E["P"]
if bhp <= 0:
break
if e["R"] > 0:
e["R"] -= 1
mp += E["R"]
if e["S"] > 0:
e["S"] -= 1
p["e"] += E["S"]
if t % 2 == 0:
a = self._guess(mp, e)
if a in "RSP":
e[a] = D[a]
dp, db = self._turn(t, p, b, a)
mp -= C[a]
ms += C[a]
else:
dp, db = self._turn(t, p, b)
hp += dp
bhp += db
t += 1
if bhp <= 0 and mp >= 0:
l = min(l, ms)
p12.append(l)
assert p12[0] == 953
assert p12[1] == 1289
return p12
@staticmethod
def _guess(mp, e):
v = False
while not v:
a = random.choice("MDPRS")
match a:
case "S" | "P" | "R":
v = e[a] == 0
case _:
v = True
return a
@staticmethod
def _turn(t, p, b, a=None):
"""Take turns
>>> Solution._turn(1, {}, {"d": 5})
(-5, 0)
>>> Solution._turn(1, {}, {"d": 5, "e": -2})
(-5, -2)
>>> Solution._turn(1, {"e": 7}, {"d": 5})
(-1, 0)
>>> Solution._turn(2, {}, {}, "M")
(0, -4)
>>> Solution._turn(2, {}, {}, "D")
(2, -2)
>>> Solution._turn(2, {}, {}, "S")
(0, 0)
>>> Solution._turn(2, {}, {}, "P")
(0, 0)
>>> Solution._turn(2, {}, {}, "R")
(0, 0)
"""
ped = p.get("e", 0)
bed = b.get("e", 0)
if t % 2 == 1:
return min(ped - b["d"], -1), 0 + bed
else:
match a:
case "D":
return 2, -2
case "M":
return 0, -4
case "S":
return 0, 0
case "R":
return 0, 0
case "P":
return 0, 0
if __name__ == "__main__":
import doctest
doctest.testmod()
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,65 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = "23.txt"
def __str__(self):
return "Day 23: Opening the Turing Lock"
def _solve(self, pi):
p12 = []
for a in [0, 1]:
m = {
"a": a,
"b": 0,
}
p = [l.split() for l in pi.splitlines()]
i = 0
while i < len(p):
v, rv, *a = p[i]
rv = rv.split(",")[0]
match v:
case "hlf":
m[rv] //= 2
i += 1
case "tpl":
m[rv] *= 3
i += 1
case "inc":
m[rv] += 1
i += 1
case "jmp":
i += int(rv)
case "jie":
i += int(a[0]) if int(m[rv]) % 2 == 0 else 1
case "jio":
i += int(a[0]) if int(m[rv]) == 1 else 1
p12.append(m["b"])
return p12
def solve(self, pi):
o = self._solve(pi)
return o[0]
def solve_again(self, pi):
o = self._solve(pi)
return o[1]
def parse_input(self, data):
return data.strip()
if __name__ == "__main__":
solution = Solution()
solution.show_results()
dummy = """
inc a
jio a, +2
tpl a
inc a
""".strip()
# solution.solve(dummy)
# solution.solve_again(dummy)

View file

@ -0,0 +1,50 @@
import re
from itertools import combinations
from math import prod
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = "24.txt"
def __str__(self):
return "Day 24: Hangs in the Balance"
def solve(self, pi):
o = self._solve(pi)
return o[0]
def solve_again(self, pi):
o = self._solve(pi)
return o[1]
def _solve(self, pi):
p12 = []
for g in [3, 4]:
w = [int(s) for s in re.findall(r"\d+", pi)]
gs = sum(w) // g
c = []
for r in range(len(w)):
for n in combinations(w, r=r):
if sum(n) == gs:
c.append(n)
if c:
break
p12.append(min(map(prod, c)))
return p12
def parse_input(self, data):
return data.strip()
if __name__ == "__main__":
solution = Solution()
solution.show_results()
dummy = """
1,2,3,4,5,7,8,9,10,11
""".strip()
solution.solve(dummy)
# solution.solve_again(dummy)

View file

@ -0,0 +1,150 @@
import re
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = "25.txt"
def __str__(self):
return "Day 25: Let It Snow"
def solve(self, pi):
r, c = [int(s) - 1 for s in re.findall(r"\d+", pi)]
v = 20151125
seen = set()
e = rc2p(r, c)
t = 0
while v not in seen:
seen.add(v)
v = v * 252533 % 33554393
t += 1
for _ in range(e % t - 1):
v = v * 252533 % 33554393
assert v == 9132360
return v
def solve_again(self, pi):
return "God jul!"
def parse_input(self, data):
return data.strip()
def p2rc(p):
"""
Get row and column for a storage position
>>> p2rc(1)
(0, 0)
>>> p2rc(2)
(1, 0)
>>> p2rc(3)
(0, 1)
>>> p2rc(4)
(2, 0)
>>> p2rc(5)
(1, 1)
>>> p2rc(6)
(0, 2)
>>> p2rc(7)
(3, 0)
>>> p2rc(8)
(2, 1)
>>> p2rc(9)
(1, 2)
>>> p2rc(10)
(0, 3)
>>> p2rc(11)
(4, 0)
>>> p2rc(12)
(3, 1)
>>> p2rc(13)
(2, 2)
>>> p2rc(14)
(1, 3)
>>> p2rc(15)
(0, 4)
>>> p2rc(16)
(5, 0)
>>> p2rc(17)
(4, 1)
>>> p2rc(18)
(3, 2)
>>> p2rc(19)
(2, 3)
>>> p2rc(20)
(1, 4)
>>> p2rc(21)
(0, 5)
"""
v = 0
i = 0
while v < p:
i += 1
v = sum(range(i + 1))
r, c = 0, i - 1
for _ in range(v - p):
r += 1
c -= 1
return (r, c)
def rc2p(r, c):
"""
Get storage position for coordinate row R, column C
>>> rc2p(0, 0)
1
>>> rc2p(1, 0)
2
>>> rc2p(0, 1)
3
>>> rc2p(2, 0)
4
>>> rc2p(1, 1)
5
>>> rc2p(0, 2)
6
>>> rc2p(3, 0)
7
>>> rc2p(2, 1)
8
>>> rc2p(1, 2)
9
>>> rc2p(0, 3)
10
>>> rc2p(4, 0)
11
>>> rc2p(3, 1)
12
>>> rc2p(2, 2)
13
>>> rc2p(1, 3)
14
>>> rc2p(0, 4)
15
>>> rc2p(5, 0)
16
>>> rc2p(4, 1)
17
>>> rc2p(3, 2)
18
>>> rc2p(2, 3)
19
>>> rc2p(1, 4)
20
>>> rc2p(0, 5)
21
"""
return sum(range(r + c + 1)) + c + 1
if __name__ == "__main__":
import doctest
doctest.testmod()
solution = Solution()
solution.show_results()