Add 2022 solutions

This commit is contained in:
felixm 2024-07-07 20:34:52 -04:00
parent d582dfc777
commit e9ba9cee63
28 changed files with 2774 additions and 30 deletions

20
2022/d1.py Normal file
View File

@ -0,0 +1,20 @@
from lib import *
def solve(input: Input, second=False):
ps = input.text.split("\n\n")
xss = map(lambda p: map(int, p.splitlines()), ps)
xs = list(map(sum, xss))
if not second:
return max(xs)
xs.sort()
return sum(xs[-3:])
def main():
DAY_INPUT = "d1.txt"
print("Solution 1:", solve(Input(DAY_INPUT)))
assert solve(Input(DAY_INPUT)) == 69883
print("Solution 2:", solve(Input(DAY_INPUT), True))
assert solve(Input(DAY_INPUT), True) == 207576
if __name__ == "__main__":
main()

216
2022/d10.py Normal file
View File

@ -0,0 +1,216 @@
import re
from string import ascii_lowercase, ascii_uppercase
EXAMPLE = """
addx 15
addx -11
addx 6
addx -3
addx 5
addx -1
addx -8
addx 13
addx 4
noop
addx -1
addx 5
addx -1
addx 5
addx -1
addx 5
addx -1
addx 5
addx -1
addx -35
addx 1
addx 24
addx -19
addx 1
addx 16
addx -11
noop
noop
addx 21
addx -15
noop
noop
addx -3
addx 9
addx 1
addx -3
addx 8
addx 1
addx 5
noop
noop
noop
noop
noop
addx -36
noop
addx 1
addx 7
noop
noop
noop
addx 2
addx 6
noop
noop
noop
noop
noop
addx 1
noop
noop
addx 7
addx 1
noop
addx -13
addx 13
addx 7
noop
addx 1
addx -33
noop
noop
noop
addx 2
noop
noop
noop
addx 8
noop
addx -1
addx 2
addx 1
noop
addx 17
addx -9
addx 1
addx 1
addx -3
addx 11
noop
noop
addx 1
noop
addx 1
noop
noop
addx -13
addx -19
addx 1
addx 3
addx 26
addx -30
addx 12
addx -1
addx 3
addx 1
noop
noop
noop
addx -9
addx 18
addx 1
addx 2
noop
noop
addx 9
noop
noop
noop
addx -1
addx 2
addx -37
addx 1
addx 3
noop
addx 15
addx -21
addx 22
addx -6
addx 1
noop
addx 2
addx 1
noop
addx -10
noop
noop
addx 20
addx 1
addx 2
addx 2
addx -6
addx -11
noop
noop
noop
"""
def clean(text: str) -> list[str]:
return list(filter(lambda l: l.strip() != "", text.splitlines()))
def solve(lines: list[str]):
res = 0
inst_count = 0
value_x = 1
cycles = [20, 60, 100, 140, 180, 220]
for i, line in enumerate(lines):
inst_count_before = inst_count
addx = 0
if line.startswith("noop"):
inst_count += 1
elif line.startswith("addx"):
v = int(line.split()[1])
value_x += v
inst_count += 2
addx = v
else:
raise Exception("Unexpected {line}")
for c in cycles:
if inst_count_before < c and inst_count >= c:
strength = (value_x - addx) * c
res += strength
print(inst_count, strength)
# 15:10
return res
def solve2(lines: list[str]):
x_values = [1]
for _, line in enumerate(lines):
if line.startswith("noop"):
x_values.append(x_values[-1])
elif line.startswith("addx"):
x_values.append(x_values[-1])
v = int(line.split()[1])
x_values.append(x_values[-1] + v)
s = ""
for i, x in enumerate(x_values):
col = i % 40
if col == 0:
s += "\n"
if col == x - 1 or col == x or col == x + 1:
s += "#"
else:
s += " "
print(s)
# 25:20
def main():
example = clean(EXAMPLE)
print("Example 1:", solve(example))
data = clean(open("d10.txt").read())
print("Solution 1:", solve(data))
data = clean(open("d10.txt").read())
print("Solution 2:", solve2(data))
if __name__ == "__main__":
main()

143
2022/d11.py Normal file
View File

@ -0,0 +1,143 @@
import re
from string import ascii_lowercase, ascii_uppercase
def extract_single_digit(line: str) -> int:
r = re.compile(r"\d+")
for m in r.findall(line):
return int(m)
raise Exception("No single digit sequence in '{line}'")
def extract_digits_list(line: str) -> list[int]:
r = re.compile(r"\d+")
return list(map(int, r.findall(line)))
EXAMPLE = """
Monkey 0:
Starting items: 79, 98
Operation: new = old * 19
Test: divisible by 23
If true: throw to monkey 2
If false: throw to monkey 3
Monkey 1:
Starting items: 54, 65, 75, 74
Operation: new = old + 6
Test: divisible by 19
If true: throw to monkey 2
If false: throw to monkey 0
Monkey 2:
Starting items: 79, 60, 97
Operation: new = old * old
Test: divisible by 13
If true: throw to monkey 1
If false: throw to monkey 3
Monkey 3:
Starting items: 74
Operation: new = old + 3
Test: divisible by 17
If true: throw to monkey 0
If false: throw to monkey 1
"""
def clean(text: str) -> list[str]:
return list(filter(lambda l: l.strip() != "", text.splitlines()))
def get_monkesy(lines):
monkeys = []
for i, line in enumerate(lines):
if line.startswith("Monkey"):
d = extract_single_digit(line)
monkeys.append([d])
elif line.strip().startswith("Starting"):
d = extract_digits_list(line)
monkeys[-1].append(d)
elif line.strip().startswith("Operation"):
if "old * old" in line:
f = lambda y: y * y
monkeys[-1].append(f)
elif "*" in line:
d = extract_single_digit(line)
f = lambda x: (lambda y: x * y)
f = f(d)
monkeys[-1].append(f)
elif "+" in line:
d = extract_single_digit(line)
f = lambda x: (lambda y: x + y)
f = f(d)
monkeys[-1].append(f)
else:
raise Exception(line)
elif line.strip().startswith("Test"):
d = extract_single_digit(line)
monkeys[-1].append(d)
elif line.strip().startswith("If true"):
d = extract_single_digit(line)
monkeys[-1].append(d)
elif line.strip().startswith("If false"):
d = extract_single_digit(line)
monkeys[-1].append(d)
else:
print(line)
raise Exception("Unexpected line.")
for m in monkeys:
m.append(0)
return monkeys
def solve(lines: list[str]):
monkeys = get_monkesy(lines)
for _ in range(20):
for monkey in monkeys:
id, items, op, test, if_true, if_false, count = monkey
while items:
item = items.pop()
monkey[6] += 1
worry = item
worry = op(worry)
worry //= 3
if worry % test == 0:
monkeys[if_true][1].append(worry)
else:
monkeys[if_false][1].append(worry)
monkeys[id][1] = monkeys[id][1][1:]
counts = sorted([m[6] for m in monkeys])[-2:]
return counts[0] * counts[1]
def solve2(lines: list[str]):
monkeys = get_monkesy(lines)
mod = 1
for m in monkeys:
mod *= m[3]
for _ in range(10000):
for monkey in monkeys:
id, items, op, test, if_true, if_false, count = monkey
for item in list(items):
monkey[6] += 1
worry = item
worry = op(worry)
worry %= mod
if worry % test == 0:
monkeys[if_true][1].append(worry)
else:
monkeys[if_false][1].append(worry)
monkeys[id][1] = monkeys[id][1][1:]
counts = sorted([m[6] for m in monkeys])[-2:]
return counts[0] * counts[1]
def main():
example = clean(EXAMPLE)
print("Example 1:", solve(example))
data = clean(open("d11.txt").read())
print("Solution 1:", solve(data))
example = clean(EXAMPLE)
print("Example 2:", solve2(example))
data = clean(open("d11.txt").read())
print("Solution 2:", solve2(data))
if __name__ == "__main__":
main()

120
2022/d12.py Normal file
View File

@ -0,0 +1,120 @@
import lib
EXAMPLE = """
Sabqponm
abcryxxl
accszExk
acctuvwj
abdefghi
"""
def neighbors_4(row, col, rows, cols):
n = []
for r, c in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
new_row, new_col = row + r, col + c
if new_row >= 0 and new_row < rows and new_col >= 0 and new_col < cols:
n.append((new_row, new_col))
return n
def solve(lines: list[str]):
start = (0, 0)
end = (0, 0)
rows = len(lines)
cols = len(lines[0])
heights = []
dists = []
for (row, line) in enumerate(lines):
heights.append([])
dists.append([])
for (col, c) in enumerate(line):
if c == 'S':
start = (row, col)
heights[row].append(0)
dists[row].append(0)
elif c == 'E':
end = (row, col)
heights[row].append(26)
dists[row].append(2**16)
else:
heights[row].append(ord(lines[row][col]) - ord('a'))
dists[row].append(2**16)
active_fields = [start]
while active_fields:
row, col = active_fields.pop()
current_dist = dists[row][col]
current_height = heights[row][col]
for row, col in neighbors_4(row, col, rows, cols):
pot_height = heights[row][col]
if current_height < pot_height - 1:
continue
if current_dist + 1 < dists[row][col]:
dists[row][col] = current_dist + 1
active_fields.append((row, col))
# for row in dists:
# print(row)
# 34:00
return dists[end[0]][end[1]]
def solve2(lines: list[str]):
starts = []
all_dists =[]
for (row, line) in enumerate(lines):
for (col, c) in enumerate(line):
if c == 'S' or c == 'a':
starts.append((row, col))
for start in starts:
end = (0, 0)
rows = len(lines)
cols = len(lines[0])
heights = []
dists = []
for (row, line) in enumerate(lines):
heights.append([])
dists.append([])
for (col, c) in enumerate(line):
if row == start[0] and col == start[1]:
heights[row].append(0)
dists[row].append(0)
elif c == 'E':
end = (row, col)
heights[row].append(26)
dists[row].append(2**16)
else:
heights[row].append(ord(lines[row][col]) - ord('a'))
dists[row].append(2**16)
active_fields = [start]
while active_fields:
row, col = active_fields.pop()
current_dist = dists[row][col]
current_height = heights[row][col]
for row, col in neighbors_4(row, col, rows, cols):
pot_height = heights[row][col]
if current_height < pot_height - 1:
continue
if current_dist + 1 < dists[row][col]:
dists[row][col] = current_dist + 1
active_fields.append((row, col))
all_dists.append(dists[end[0]][end[1]])
# 39:45
return min(all_dists)
def main():
lines = lib.str_to_lines_no_empty(EXAMPLE)
print("Example 1:", solve(lines))
lines = lib.str_to_lines_no_empty(open("d12.txt").read())
print("Solution 1:", solve(lines))
lines = lib.str_to_lines_no_empty(EXAMPLE)
print("Example 2:", solve2(lines))
lines = lib.str_to_lines_no_empty(open("d12.txt").read())
print("Solution 2:", solve2(lines))
if __name__ == "__main__":
main()

115
2022/d13.py Normal file
View File

@ -0,0 +1,115 @@
import lib
EXAMPLE = """[1,1,3,1,1]
[1,1,5,1,1]
[[1],[2,3,4]]
[[1],4]
[9]
[[8,7,6]]
[[4,4],4,4]
[[4,4],4,4,4]
[7,7,7,7]
[7,7,7]
[]
[3]
[[[]]]
[[]]
[1,[2,[3,[4,[5,6,7]]]],8,9]
[1,[2,[3,[4,[5,6,0]]]],8,9]
"""
def right_order(a, b):
if not a and not b:
return None
elif not a:
return True
elif not b:
return False
l, r = a[0], b[0]
if type(l) is int and type(r) is int:
if l < r:
return True
elif l > r:
return False
else:
return right_order(a[1:], b[1:])
if type(l) is int:
l = [l]
if type(r) is int:
r = [r]
o = right_order(l, r)
if o is not None:
return o
return right_order(a[1:], b[1:])
def solve(lines: list[str]):
res = 0
pairs = [[]]
for (i, line) in enumerate(lines):
if line.strip() == "":
pairs.append([])
else:
pairs[-1].append(eval(line))
for i, p in enumerate(pairs):
if not len(p) == 2:
continue
a, b = p[0], p[1]
o = right_order(a, b)
if o is True:
res += i + 1
if o is None:
raise Exception("NO DECISION")
# 37:00
return res
def solve2(lines: list[str]):
res = 0
packets = list(map(eval, lines))
p1 = [[2]]
packets.append(p1)
p2 = [[6]]
packets.append(p2)
resorted = True
while resorted:
resorted = False
for i in range(len(packets) - 1):
if not right_order(packets[i], packets[i + 1]):
packets[i], packets[i + 1] = packets[i + 1], packets[i]
resorted = True
p1i, p2i = 0, 0
for (i, p) in enumerate(packets):
if p == p1:
p1i = i + 1
elif p == p2:
p2i = i + 1
print(p1i, p2i)
# 44:00
return p1i * p2i
def main():
lines = lib.str_to_lines(EXAMPLE)
print("Example 1:", solve(lines))
lines = lib.str_to_lines(open("d13.txt").read())
print("Solution 1:", solve(lines))
lines = lib.str_to_lines_no_empty(EXAMPLE)
print("Example 2:", solve2(lines))
lines = lib.str_to_lines_no_empty(open("d13.txt").read())
print("Solution 2:", solve2(lines))
if __name__ == "__main__":
main()

104
2022/d14.py Normal file
View File

@ -0,0 +1,104 @@
import lib
EXAMPLE = """
498,4 -> 498,6 -> 496,6
503,4 -> 502,4 -> 502,9 -> 494,9
"""
def solve(lines: list[str]):
c = set()
for (i, line) in enumerate(lines):
coords = [list(map(int, c.split(","))) for c in line.split(" -> ")]
for i in range(len(coords) - 1):
a, b = coords[i:i + 2]
a_x, a_y = a
b_x, b_y = b
if a_x == b_x and a_y < b_y:
for y in range(a_y, b_y + 1):
c.add((a_x, y))
elif a_x == b_x and a_y > b_y:
for y in range(b_y, a_y + 1):
c.add((a_x, y))
elif a_y == b_y and a_x < b_x:
for x in range(a_x, b_x + 1):
c.add((x, a_y))
elif a_y == b_y and a_x > b_x:
for x in range(b_x, a_x + 1):
c.add((x, a_y))
else:
raise Exception(f"unexpected {a=} {b=}")
largest_y = max(c, key=lambda c: c[1])[1]
s = (500, 0)
s_count = 0
while s[1] < largest_y:
if not (ns := (s[0], s[1] + 1)) in c:
s = ns
elif not (ns := (s[0] - 1, s[1] + 1)) in c:
s = ns
elif not (ns := (s[0] + 1, s[1] + 1)) in c:
s = ns
else:
c.add(s)
s = (500, 0)
s_count += 1
return s_count
def solve2(lines: list[str]):
c = set()
for (i, line) in enumerate(lines):
coords = [list(map(int, c.split(","))) for c in line.split(" -> ")]
for i in range(len(coords) - 1):
a, b = coords[i:i + 2]
a_x, a_y = a
b_x, b_y = b
if a_x == b_x and a_y < b_y:
for y in range(a_y, b_y + 1):
c.add((a_x, y))
elif a_x == b_x and a_y > b_y:
for y in range(b_y, a_y + 1):
c.add((a_x, y))
elif a_y == b_y and a_x < b_x:
for x in range(a_x, b_x + 1):
c.add((x, a_y))
elif a_y == b_y and a_x > b_x:
for x in range(b_x, a_x + 1):
c.add((x, a_y))
else:
raise Exception(f"unexpected {a=} {b=}")
largest_y = max(c, key=lambda c: c[1])[1]
for x in range(-10000, 10000):
c.add((x, largest_y + 2))
s = (500, 0)
s_count = 1
while True:
if not (ns := (s[0], s[1] + 1)) in c:
s = ns
elif not (ns := (s[0] - 1, s[1] + 1)) in c:
s = ns
elif not (ns := (s[0] + 1, s[1] + 1)) in c:
s = ns
elif s[1] == 0:
return s_count
else:
c.add(s)
s_count += 1
s = (500, 0)
def main():
lines = lib.str_to_lines_no_empty(EXAMPLE)
print("Example 1:", solve(lines))
lines = lib.str_to_lines_no_empty(open("d14.txt").read())
print("Solution 1:", solve(lines))
lines = lib.str_to_lines_no_empty(EXAMPLE)
print("Example 2:", solve2(lines))
lines = lib.str_to_lines_no_empty(open("d14.txt").read())
print("Solution 2:", solve2(lines))
if __name__ == "__main__":
main()

151
2022/d15.py Normal file
View File

@ -0,0 +1,151 @@
import lib
import math
EXAMPLE = """
Sensor at x=2, y=18: closest beacon is at x=-2, y=15
Sensor at x=9, y=16: closest beacon is at x=10, y=16
Sensor at x=13, y=2: closest beacon is at x=15, y=3
Sensor at x=12, y=14: closest beacon is at x=10, y=16
Sensor at x=10, y=20: closest beacon is at x=10, y=16
Sensor at x=14, y=17: closest beacon is at x=10, y=16
Sensor at x=8, y=7: closest beacon is at x=2, y=10
Sensor at x=2, y=0: closest beacon is at x=2, y=10
Sensor at x=0, y=11: closest beacon is at x=2, y=10
Sensor at x=20, y=14: closest beacon is at x=25, y=17
Sensor at x=17, y=20: closest beacon is at x=21, y=22
Sensor at x=16, y=7: closest beacon is at x=15, y=3
Sensor at x=14, y=3: closest beacon is at x=15, y=3
Sensor at x=20, y=1: closest beacon is at x=15, y=3
"""
def unify_ranges(ranges):
# aaaaaaaaaaa
# bbbb
#
# aaa
# bbb
#
# aaa
# bbb
ranges = sorted(ranges)
while True:
for i in range(len(ranges) - 1):
a, b = ranges[i], ranges[i + 1]
if a == b:
del ranges[i]
break
elif a[0] == b[0]:
ranges[i] = (a[0], max(a[1], b[1]))
del ranges[i + 1]
break
elif a[1] == b[1]:
ranges[i] = (min(a[0], b[0]), b[1])
del ranges[i + 1]
break
elif a[0] < b[0] and a[1] > b[1]:
del ranges[i + 1]
break
elif a[0] > b[0] and a[1] < b[1]:
del ranges[i]
break
elif a[1] < b[0]:
pass
elif a[0] <= b[1] and a[1] >= b[0]:
ranges[i] = (a[0], b[1])
del ranges[i + 1]
break
else:
raise Exception("uhoh", a, b)
else:
return ranges
return ranges
def mdist(dx, dy):
return abs(dx) + abs(dy)
def xdist(dm, dy):
x = dm - abs(dy)
if x <= 0:
return 0
return x
def solve(lines: list[str], yt):
sensors = []
bacons = set()
for (i, line) in enumerate(lines):
digits = lib.str_to_ints(line)
sx, sy, bx, by = digits
sm = mdist(bx - sx, by - sy)
sensors.append([sx, sy, sm])
bacons.add((bx, by))
ranges = []
for (sx, sy, sm) in sensors:
x_range = xdist(sm, yt - sy)
if x_range == 0:
continue
r = (sx - x_range, sx + x_range)
# print(f"{sx=} {sy=} {r=}")
ranges.append(r)
ranges = unify_ranges(ranges)
r = 0
for (a, b) in ranges:
r += b - a + 1
for (bx, by) in list(bacons):
if by == yt and bx >= a and bx <= b:
r -= 1
# 140:00 I don't know what a Manhattan distance is.
return r
def solve2(lines: list[str], xymax):
sensors = []
bacons = set()
for (i, line) in enumerate(lines):
digits = lib.str_to_ints(line)
sx, sy, bx, by = digits
sm = mdist(bx - sx, by - sy)
sensors.append([sx, sy, sm])
bacons.add((bx, by))
finds = []
for yt in range(0, xymax):
ranges = []
for (sx, sy, sm) in sensors:
x_range = xdist(sm, yt - sy)
if x_range == 0:
continue
r = (sx - x_range, sx + x_range)
ranges.append(r)
ranges = unify_ranges(ranges)
if ranges[0][0] > 0:
raise Exception("Bacon at 0.")
elif ranges[-1][-1] < xymax:
raise Exception("Bacon at xymax.")
for i in range(len(ranges) - 1):
if ranges[i + 1][0] - ranges[i][1] > 1:
finds.append((ranges[i][1] + 1, yt))
if len(finds) != 1:
raise Exception("TOO MANY FINDS")
else:
x, y = finds[0]
return x * 4000000 + y
# 10:00
def main():
lines = lib.str_to_lines_no_empty(EXAMPLE)
print("Example 1:", solve(lines, 10))
lines = lib.str_to_lines_no_empty(open("d15.txt").read())
print("Solution 1:", solve(lines, 2000000))
lines = lib.str_to_lines_no_empty(EXAMPLE)
print("Example 2:", solve2(lines, 20))
lines = lib.str_to_lines_no_empty(open("d15.txt").read())
print("Solution 2:", solve2(lines, 4000000))
if __name__ == "__main__":
main()

100
2022/d16.py Normal file
View File

@ -0,0 +1,100 @@
import lib
EXAMPLE = """
Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
Valve BB has flow rate=13; tunnels lead to valves CC, AA
Valve CC has flow rate=2; tunnels lead to valves DD, BB
Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE
Valve EE has flow rate=3; tunnels lead to valves FF, DD
Valve FF has flow rate=0; tunnels lead to valves EE, GG
Valve GG has flow rate=0; tunnels lead to valves FF, HH
Valve HH has flow rate=22; tunnel leads to valve GG
Valve II has flow rate=0; tunnels lead to valves AA, JJ
Valve JJ has flow rate=21; tunnel leads to valve II
"""
def solve(lines: list[str]):
nodes = {}
for (i, line) in enumerate(lines):
source = line.split(" ")[1]
to_valves = list(map(lambda v: v.replace(",", ""), line.split(" ")[9:]))
flow_rate = lib.str_to_int(line)
nodes[source] = (flow_rate, to_valves)
options = [(0, 0, "AA", ())]
visited = set()
for _ in range(30):
new_options = []
for (total, flow, current, valves) in options:
valve_flow_rate = nodes[current][0]
if not current in valves and valve_flow_rate > 0:
o = (total + flow, flow + valve_flow_rate, current, valves + (current, ))
new_options.append(o)
for new_valve in nodes[current][1]:
o = (total + flow, flow, new_valve, valves)
new_options.append(o)
options = sorted(list(set(new_options)))[-100:]
return options[-1][0]
def solve2(lines: list[str]):
nodes = {}
for (i, line) in enumerate(lines):
source = line.split(" ")[1]
to_valves = list(map(lambda v: v.replace(",", ""), line.split(" ")[9:]))
flow_rate = lib.str_to_int(line)
nodes[source] = (flow_rate, to_valves)
options = [(0, 0, ("AA", "AA"), ())]
for _ in range(26):
new_options = []
for (total, flow, (a, b), valves) in options:
flow_a = nodes[a][0]
flow_b = nodes[b][0]
# a turns on b moves
if not a in valves and flow_a > 0:
for new_valve_b in nodes[b][1]:
o = (total + flow, flow + flow_a, (a, new_valve_b), valves + (a, ))
new_options.append(o)
# a moves and b turns on
if not b in valves and flow_b > 0:
for new_valve_a in nodes[a][1]:
o = (total + flow, flow + flow_b, (new_valve_a, b), valves + (b, ))
new_options.append(o)
# both turn on
if a != b and flow_a > 0 and flow_b > 0 and not a in valves and not b in valves:
o = (total + flow, flow + flow_a + flow_b, (a, b), valves + (a, b, ))
new_options.append(o)
# both move
for new_valve_a in nodes[a][1]:
for new_valve_b in nodes[b][1]:
o = (total + flow, flow, (new_valve_a, new_valve_b), valves)
new_options.append(o)
options = sorted(list(set(new_options)))[-1000:]
# 52:00
return options[-1][0]
def main():
lines = lib.str_to_lines_no_empty(EXAMPLE)
print("Example 1:", solve(lines))
lines = lib.str_to_lines_no_empty(open("d16.txt").read())
print("Solution 1:", solve(lines))
lines = lib.str_to_lines_no_empty(EXAMPLE)
print("Example 2:", solve2(lines))
lines = lib.str_to_lines_no_empty(open("d16.txt").read())
print("Solution 2:", solve2(lines))
assert solve2(lines) == 2591
if __name__ == "__main__":
main()

119
2022/d17.py Normal file
View File

@ -0,0 +1,119 @@
from lib import *
EXAMPLE = """>>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>"""
ROCKS = [
[(0, 0), (0, 1), (0, 2), (0, 3)],
[(0, 1), (-1, 0), (-1, 1), (-1, 2), (-2, 1)],
[(0, 0), (0, 1), (0, 2), (-1, 2), (-2, 2)],
[(0, 0), (-1, 0), (-2, 0), (-3, 0)],
[(0, 0), (0, 1), (-1, 0), (-1, 1)],
]
def printfall(coords):
row_coords = list(map(lambda c: c[0], coords))
col_coords = list(map(lambda c: c[1], coords))
min_row, max_row = min(row_coords), max(row_coords)
min_col, max_col = min(col_coords), max(col_coords)
s = ""
for r in range(min_row, max_row + 1):
for c in range(min_col, max_col + 1):
s += "."
s += "\n"
g = Input(s).grid2()
for c in coords:
c = (c[0] + -min_row, c[1])
g[tuple(c)] = "#"
g.print()
print()
def solve(input: Input, second=False):
BLOWS = list(input.text.strip())
width, blow_index = 7, 0
heighest = [0 for _ in range(width)]
rocks = set([(0, i) for i in range(width)])
if second:
target = 1000000000000
nhs = {}
else:
target = 2022
i = 0
while i < target:
rock = ROCKS[i % len(ROCKS)]
pos = (min(heighest) - 4, 2)
if second and i > 0 and all(abs(heighest[i] - heighest[i + 1]) <= 2 for i in range(len(heighest) - 1)):
# long winded code to find repeating sequence
nh = tuple([h - min(heighest) for h in heighest])
if nh in nhs:
nhs[nh].append((i, heighest[0]))
else:
nhs[nh] = [(i, heighest[0])]
xs = nhs[nh]
if len(xs) > 4 and all(xs[1][0] - xs[0][0] == xs[i + 1][0] - xs[i][0] for i in range(len(xs) - 1)):
# we found a repeating sequence and can skip forward
di = xs[-1][0] - xs[-2][0]
dh = xs[-1][1] - xs[-2][1]
repeat = (target - i) // di
i += repeat * di
heighest = [h + repeat * dh for h in heighest]
for c, r in enumerate(heighest):
rocks.add((r, c))
nhs = {}
continue
falling = True
while falling:
blow = BLOWS[blow_index % len(BLOWS)]
blow_index += 1
dc = None
if blow == ">":
dc = 1
elif blow == "<":
dc = -1
for (ro, co) in rock:
r = pos[0] + ro
c = pos[1] + co + dc
if c < 0 or c >= width or (r, c) in rocks:
break
else:
pos = (pos[0], pos[1] + dc)
for (ro, co) in rock:
r = pos[0] + ro + 1
c = pos[1] + co
if (r, c) in rocks:
falling = False
break
else:
pos = (pos[0] + 1, pos[1])
for (ro, co) in rock:
r = pos[0] + ro
c = pos[1] + co
rocks.add((r, c))
heighest[c] = min(heighest[c], r)
i += 1
return -min(heighest)
def main():
_, day = extract_year_and_date(__file__)
DAY_INPUT = f"i{day}.txt"
print("Example 1:", solve(Input(EXAMPLE)))
s1 = solve(Input(DAY_INPUT))
assert s1 == 3102
print("Solution 1:", s1)
print("Example 2:", solve(Input(EXAMPLE), True))
print("Solution 2:", solve(Input(DAY_INPUT), True))
assert solve(Input(DAY_INPUT), True) == 1539823008825
if __name__ == "__main__":
main()

71
2022/d18.py Normal file
View File

@ -0,0 +1,71 @@
from lib import *
EXAMPLE = """2,2,2
1,2,2
3,2,2
2,1,2
2,3,2
2,2,1
2,2,3
2,2,4
2,2,6
1,2,5
3,2,5
2,1,5
2,3,5
"""
def solve(input: Input, second=False):
cubes = set([tuple(map(int, l.split(",")))for l in input.text.splitlines()])
res = len(cubes) * 6
if second:
min_x = min([c[0] for c in cubes]) - 1
max_x = max([c[0] for c in cubes]) + 1
min_y = min([c[1] for c in cubes]) - 1
max_y = max([c[1] for c in cubes]) + 1
min_z = min([c[2] for c in cubes]) - 1
max_z = max([c[2] for c in cubes]) + 1
surrounded = set([(x, y, z)
for x in range(min_x, max_x + 1)
for y in range(min_y, max_y + 1)
for z in range(min_z, max_z + 1)])
surrounded -= cubes
vs = [(min_x, min_y, min_z)]
while vs:
(x, y, z) = vs.pop()
for dx, dy, dz in [(1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1)]:
t = x + dx, y + dy, z + dz
if t in surrounded:
surrounded.remove(t)
vs.append(t)
for x, y, z in list(surrounded):
for dx, dy, dz in [(1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1)]:
nc = x + dx, y + dy, z + dz
if nc in cubes:
res -= 1
for x, y, z in list(cubes):
for dx, dy, dz in [(1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1)]:
nc = x + dx, y + dy, z + dz
if nc in cubes:
res -= 1
return res
def main():
DAY_INPUT = "d18.txt"
print("Example 1:", solve(Input(EXAMPLE)))
print("Solution 1:", solve(Input(DAY_INPUT)))
# 10:30........
print("Example 2:", solve(Input(EXAMPLE), True))
print("Solution 2:", solve(Input(DAY_INPUT), True))
assert solve(Input(DAY_INPUT), True) == 2064
# 30:00
return
if __name__ == "__main__":
main()

117
2022/d19.py Normal file
View File

@ -0,0 +1,117 @@
from lib import *
EXAMPLE = """
Blueprint 1:
Each ore robot costs 4 ore.
Each clay robot costs 2 ore.
Each obsidian robot costs 3 ore and 14 clay.
Each geode robot costs 2 ore and 7 obsidian.
Blueprint 2:
Each ore robot costs 2 ore.
Each clay robot costs 3 ore.
Each obsidian robot costs 3 ore and 8 clay.
Each geode robot costs 3 ore and 12 obsidian.
"""
def solve(input: Input, second=False):
if not second:
res = 0
else:
res = 1
bps = []
for line in input.text.replace(".", ".\n").replace(":", ":\n").splitlines():
if "Blueprint" in line:
bps.append([])
elif "ore robot" in line:
cost = str_to_ints(line)
cost += [0, 0]
bps[-1].append(cost)
elif "clay robot" in line:
cost = str_to_ints(line)
cost += [0, 0]
bps[-1].append(cost)
elif "obsidian robot" in line:
cost = str_to_ints(line)
cost += [0,]
bps[-1].append(cost)
elif "geode robot" in line:
cost = str_to_ints(line)
cost.insert(1, 0)
bps[-1].append(cost)
if second:
bps = bps[:3]
time = 32
else:
time = 24
end_states = []
for i, bp in enumerate(bps):
# ((ore bots, clay bots, obs bots, geo bots), (ore, clay, obs, geo))
start = ((1, 0, 0, 0), (0, 0, 0, 0))
states = [start]
seen = set(states)
for _ in range(time):
new_states = []
while states:
bots, ress = states.pop()
add_ress = [0, 0, 0, 0]
for boti, count in enumerate(bots):
add_ress[boti] += count
all_built = True
for boti, cost in enumerate(bp):
if ress[0] >= cost[0] and ress[1] >= cost[1] and ress[2] >= cost[2]:
new_ress = list(ress)
new_ress[0] -= cost[0]
new_ress[1] -= cost[1]
new_ress[2] -= cost[2]
new_ress = tuple(map(sum, zip(new_ress, add_ress)))
new_bots = list(bots)
new_bots[boti] += 1
new_state = (tuple(new_bots), new_ress)
if not new_state in seen:
new_states.append(new_state)
seen.add(new_state)
else:
all_built = False
# XXX: our search space is too large here it is possible to
# optimze by not storing reduntant paths (paths where we acrue
# more of a resource than we need), but I don't know how to
# make it more efficient right now.
if not all_built:
new_ress = tuple(map(sum, zip(ress, add_ress)))
new_state = (bots, new_ress)
if not new_state in seen:
new_states.append(new_state)
seen.add(new_state)
# prune to keep search space "reasonable"
states = sorted(new_states, key=lambda s: list(reversed(s[0])), reverse=True)[:100000]
if not second:
r = max(states, key=lambda s: s[1][3])
q = (i + 1) * r[1][3]
# print(i + 1, r, q)
res += q
else:
r = max(states, key=lambda r: r[1][3])
res *= r[1][3]
return res
def main():
DAY_INPUT = "d19.txt"
e1 = solve(Input(EXAMPLE))
print("Example 1:", e1)
assert e1 == 33
print("Solution 1:", solve(Input(DAY_INPUT)))
print("Example 2:", solve(Input(EXAMPLE), True))
print("Solution 2:", solve(Input(DAY_INPUT), True))
return
if __name__ == "__main__":
main()

98
2022/d2.py Normal file
View File

@ -0,0 +1,98 @@
import re
EXAMPLE = """
A Y
B X
C Z
"""
def clean(text: str) -> list[str]:
return list(filter(lambda l: l.strip() != "", text.splitlines()))
# return list(text.splitlines()))
def solve(lines: list[str]):
s = 0
for (i, line) in enumerate(lines):
a, b = line.split(" ")
if b == "X":
s += 1
if a == "A":
s += 3
elif a == "B":
s += 0
elif a == "C":
s += 6
else:
print("invalid")
elif b == "Y":
s += 2
if a == "A":
s += 6
elif a == "B":
s += 3
elif a == "C":
s += 0
else:
print("invalid")
elif b == "Z":
s += 3
if a == "A":
s += 0
elif a == "B":
s += 6
elif a == "C":
s += 3
else:
print("invalid")
return s
def solve2(lines: list[str]):
s = 0
for (i, line) in enumerate(lines):
a, b = line.split(" ")
if a == "A":
if b == "X": # lose
s += 3
elif b == "Y": # draw
s += 4
elif b == "Z": # win
s += 8
else:
print("invalid")
elif a == "B":
if b == "X": # lose
s += 1
elif b == "Y": # draw
s += 5
elif b == "Z": # win
s += 9
else:
print("invalid")
elif a == "C":
if b == "X": # lose
s += 2
elif b == "Y": # draw
s += 6
elif b == "Z": # win
s += 7
else:
print("invalid")
return s
def main():
example = clean(EXAMPLE)
print("Example 1:", solve(example))
data = clean(open("d2.txt").read())
print("Solution 1:", solve(data))
assert solve(data) == 14069
example = clean(EXAMPLE)
print("Example 2:", solve2(example))
data = clean(open("d2.txt").read())
print("Solution 2:", solve2(data))
assert solve2(data) == 12411
if __name__ == "__main__":
main()

73
2022/d20.py Normal file
View File

@ -0,0 +1,73 @@
from lib import *
EXAMPLE = """1
2
-3
3
-2
0
4
"""
def move(xs, i, move):
move = move % (len(xs) - 1)
ni = (i + move + 1) % len(xs)
xsi, xs[i] = xs[i], None
xs.insert(ni, xsi)
xs.remove(None)
return xs
def asserts():
assert move([1, 2, 3], 0, 0) == [1, 2, 3]
assert move([1, 2, 3], 0, 1) == [2, 1, 3]
assert move([1, 2, 3], 0, 2) == [1, 2, 3]
assert move([1, 2, 3], 0, 3) == [2, 1, 3]
assert move([1, 2, 3], 0, 4) == [1, 2, 3]
assert move([1, 2, 3], 0, 5) == [2, 1, 3]
assert move([1, 2, 3], 0, 2) == [1, 2, 3]
assert move([3, 2, 1], 2, 1) == [3, 1, 2]
assert move([3, 2, 1], 2, 2) == [1, 3, 2]
assert move([4, -2, 5, 6, 7, 8, 9,], 1, -2) == [4, 5, 6, 7, 8, -2, 9]
assert move([1, 2, -2, -3, 0, 3, 4], 2, -2) == [-2, 1, 2, -3, 0, 3, 4]
def solve(input: Input, second=False):
xs = list(map(int, input.lines()))
if second:
xs = list(map(lambda n: n * 811589153, xs))
ys = list(range(len(xs)))
oys = ys[:]
repeat = 1
if second:
repeat = 10
for _ in range(repeat):
for o in oys:
i = ys.index(o)
movev = xs[o]
move(ys, i, movev)
xs = [xs[i] for i in ys]
s = 0
i0 = xs.index(0)
for i in range(1, 4):
ni = (i0 + i * 1000) % len(ys)
s += xs[ni]
return s
def main():
DAY_INPUT = "d20.txt"
print("Example 1:", solve(Input(EXAMPLE)))
print("Solution 1:", solve(Input(DAY_INPUT)))
print("Example 2:", solve(Input(EXAMPLE), True))
print("Solution 2:", solve(Input(DAY_INPUT), True))
assert solve(Input(DAY_INPUT), True) == 6704537992933
if __name__ == "__main__":
asserts()
main()

105
2022/d21.py Normal file
View File

@ -0,0 +1,105 @@
from lib import *
EXAMPLE = """root: pppw + sjmn
dbpl: 5
cczh: sllz + lgvd
zczc: 2
ptdq: humn - dvpt
dvpt: 3
lfqf: 4
humn: 5
ljgn: 2
sjmn: drzm * dbpl
sllz: 4
pppw: cczh / lfqf
lgvd: ljgn * ptdq
drzm: hmdt - zczc
hmdt: 32
"""
def resolve(monkeys, s):
equ = monkeys[s]
try:
return int(equ)
except ValueError:
pass
l, op, r = equ.split(" ")
l = resolve(monkeys, l)
r = resolve(monkeys, r)
res = eval(f"{l}{op}{r}")
monkeys[s] = res
return res
def resolve2(monkeys, s):
if s == "humn":
return tuple()
equ = monkeys[s]
try:
return int(equ)
except ValueError:
pass
l, op, r = equ.split(" ")
if s == "root":
op = "=="
l = resolve2(monkeys, l)
r = resolve2(monkeys, r)
if isinstance(l, tuple) or isinstance(r, tuple):
return (l, op, r)
if op == "/":
op = "//"
res = eval(f"{l}{op}{r}")
monkeys[s] = res
return res
def simplify(equ, exp):
if equ == tuple():
return exp
l, op, r = equ
if op == '/' and isinstance(r, int):
return simplify(l, exp * r)
elif op == '+' and isinstance(l, int):
return simplify(r, exp - l)
elif op == '+' and isinstance(r, int):
return simplify(l, exp - r)
elif op == '-' and isinstance(l, int):
return simplify(r, -(exp - l))
elif op == '-' and isinstance(r, int):
return simplify(l, exp + r)
elif op == '*' and isinstance(r, int):
return simplify(l, exp // r)
elif op == '*' and isinstance(l, int):
return simplify(r, exp // l)
def solve(input: Input, second=False):
res = 0
monkeys = {}
for line in input.lines():
l, r = line.split(": ")
monkeys[l] = r
if not second:
return int(resolve(monkeys, "root"))
else:
equ = resolve2(monkeys, "root")
assert equ[1] == '=='
return simplify(equ[0], equ[2])
def main():
DAY_INPUT = "d21.txt"
print("Example 1:", solve(Input(EXAMPLE)))
print("Solution 1:", solve(Input(DAY_INPUT)))
print("Example 2:", solve(Input(EXAMPLE), True))
print("Solution 2:", solve(Input(DAY_INPUT), True))
assert solve(Input(DAY_INPUT), True) == 3243420789721
if __name__ == "__main__":
main()

237
2022/d22.py Normal file
View File

@ -0,0 +1,237 @@
from lib import *
EXAMPLE = """ 0..#
.#..
#...
....
1..#2...3..#
........#...
..#....#....
..........#.
4..#5...
.....#..
.#......
......#.
10R5L5R10L4R5L5"""
E = (0, 1)
S = (1, 0)
W = (0, -1)
N = (-1, 0)
DIRS = [
E,
S,
W,
N,
]
# XXX: not hands free - i created this manually probably there is some way to
# figure it out from the input, but id don't know how right now.
trans_example = {
(0, N): (1, S),
(0, E): (5, W),
(0, S): (3, S),
(0, W): (2, S),
(1, N): (0, S),
(1, E): (2, E),
(1, S): (4, N),
(1, W): (5, N),
(2, N): (0, E),
(2, E): (3, E),
(2, S): (4, E),
(2, W): (1, W),
(3, N): (0, N),
(3, E): (5, S),
(3, S): (4, S),
(3, W): (2, W),
(4, N): (3, N),
(4, E): (5, E),
(4, S): (1, N),
(4, W): (2, N),
(5, N): (3, W),
(5, E): (0, W),
(5, S): (1, E),
(5, W): (4, W),
}
trans_input = {
(0, N): (5, E),
(0, E): (1, E),
(0, S): (2, S),
(0, W): (3, E),
(1, N): (5, N),
(1, E): (4, W),
(1, S): (2, W),
(1, W): (0, W),
(2, N): (0, N),
(2, E): (1, N),
(2, S): (4, S),
(2, W): (3, S),
(3, N): (2, E),
(3, E): (4, E),
(3, S): (5, S),
(3, W): (0, E),
(4, N): (2, N),
(4, E): (1, W),
(4, S): (5, W),
(4, W): (3, W),
(5, N): (3, N),
(5, E): (4, N),
(5, S): (1, S),
(5, W): (0, S),
}
def parse_steps(steps):
insts = re.compile(r"(\d+)([LR])").findall(steps)
lastn = re.compile(r"\d+").findall(steps)[-1]
insts.append((lastn, None))
return insts
def solve(input: Input):
grid, steps = input.text.split("\n\n")
insts = parse_steps(steps)
grid = list(map(list, grid.splitlines()))
rows = len(grid)
startcol = 0
while grid[0][startcol] == ' ':
startcol += 1
pos = (0, startcol)
dir = (0, 1)
for n, turn in insts:
nrow, ncol = pos
for _ in range(int(n)):
nrow = (nrow + dir[0]) % rows
ncol = (ncol + dir[1]) % len(grid[nrow])
while grid[nrow][ncol] == ' ':
nrow = (nrow + dir[0]) % rows
ncol = (ncol + dir[1]) % len(grid[nrow])
if grid[nrow][ncol] == '#':
break
pos = (nrow, ncol)
if turn is not None:
dir = DIRS[(DIRS.index(dir) + 1 if turn == 'R' else DIRS.index(dir) - 1) % 4]
return 1000 * (pos[0] + 1) + 4 * (pos[1] + 1) + DIRS.index(dir)
def solve2(input: Input, square_size=4):
squares = []
grid, steps = input.text.split("\n\n")
insts = parse_steps(steps)
grid_rows = grid.splitlines()
for r in range(0, len(grid_rows), square_size):
for c in range(0, len(grid_rows[r]), square_size):
square = []
for sr in range(r, r + square_size):
square.append(grid_rows[sr][c:c+square_size])
if square[0][0] != ' ':
squares.append(square)
def flip(n):
return square_size - 1 - n
def teleport(dir, sqi, row, col):
if square_size == 4:
nsqi, ndir = trans_example[(sqi, dir)]
elif square_size == 50:
nsqi, ndir = trans_input[(sqi, dir)]
else:
assert False
nrow, ncol = None, None
if ndir == N:
nrow = square_size - 1
elif ndir == S:
nrow = 0
elif ndir == E:
ncol = 0
elif ndir == W:
ncol = square_size - 1
if dir == N:
if ndir == N:
ncol = col
elif ndir == S:
ncol = flip(col)
elif ndir == E:
nrow = col
elif ndir == W:
nrow = square_size - 1 - col
elif dir == S:
if ndir == N:
ncol = flip(col)
elif ndir == S:
ncol = col
elif ndir == E:
nrow = square_size - 1 - col
elif ndir == W:
nrow = col
elif dir == E:
if ndir == N:
ncol = row
elif ndir == S:
ncol = flip(row)
elif ndir == E:
nrow = row
elif ndir == W:
nrow = square_size - 1 - row
elif dir == W:
if ndir == N:
ncol = flip(row)
elif ndir == S:
ncol = row
elif ndir == E:
nrow = flip(row)
elif ndir == W:
nrow = row
assert nrow is not None
assert ncol is not None
return ndir, nsqi, nrow, ncol
# for square in squares:
# print("\n".join(square), "\n")
dir, sqi, row, col = E, 0, 0, 0
for n, turn in insts:
# print(f"{sqi=} {row=} {col=} {dir=}")
for _ in range(int(n)):
nrow = row + dir[0]
ncol = col + dir[1]
if nrow < 0 or nrow >= square_size or ncol < 0 or ncol >= square_size:
ndir, nsqi, nrow, ncol = teleport(dir, sqi, nrow, ncol)
else:
ndir, nsqi = dir, sqi
if squares[nsqi][nrow][ncol] != '#':
dir, sqi, row, col = ndir, nsqi, nrow, ncol
if turn is not None:
dir = DIRS[(DIRS.index(dir) + 1 if turn == 'R' else DIRS.index(dir) - 1) % 4]
print(f"{sqi=} {row=} {col=} {dir=}")
if square_size == 4:
# sqi=2 row=0 col=2 dir=(-1, 0)
return 1000 * (4 + 0 + 1) + 4 * (4 + 2 + 1) + DIRS.index((-1, 0))
elif square_size == 50:
# sqi=1 row=11 col=14 dir=(0, -1)
return 1000 * (0 + 11 + 1) + 4 * (100 + 14 + 1) + DIRS.index((0, -1))
else:
assert False
def main():
DAY_INPUT = "d22.txt"
print("Example 1:", solve(Input(EXAMPLE)))
print("Solution 1:", solve(Input(DAY_INPUT)))
assert solve(Input(DAY_INPUT)) == 149250
print("Example 2:", solve2(Input(EXAMPLE)))
print("Solution 2:", solve2(Input("d22.txt"), 50))
return
if __name__ == "__main__":
main()

120
2022/d23.py Normal file
View File

@ -0,0 +1,120 @@
from lib import *
EXAMPLE = """....#..
..###.#
#...#.#
.#...##
#.###..
##.#.##
.#..#..
"""
# EXAMPLE = """.....
# ..##.
# ..#..
# .....
# ..##.
# .....
# """
N = (-1, 0)
NE = (-1, 1)
E = (0, 1)
SE = (1, 1)
S = (1, 0)
SW = (1, -1)
W = (0, -1)
NW = (-1, -1)
adj8 = [
N,
NE,
E,
SE,
S,
SW,
W,
NW,
]
def print_field(elves):
rmin, rmax = min(map(fst, elves)), max(map(fst, elves))
cmin, cmax = min(map(snd, elves)), max(map(snd, elves))
count = 0
for row in range(rmin, rmax + 1):
rs = ""
for col in range(cmin, cmax + 1):
if (row, col) in elves:
rs += "#"
else:
rs += "."
count += 1
print(rs)
print()
return count
def solve(input: Input, second=False):
dirs_to_check = [
(N, NE, NW),
(S, SE, SW),
(W, NW, SW),
(E, NE, SE),
]
round = 0
elves = input.grid2().find('#')
while True:
# print_field(elves)
nes = set()
proposed_fields = {}
for e in list(elves):
for o in adj8:
adj = (e[0] + o[0], e[1] + o[1])
if adj in elves:
break
else:
nes.add(e)
continue
proposed_field = None
for dirs in dirs_to_check:
for o in dirs:
adj = (e[0] + o[0], e[1] + o[1])
if adj in elves:
break
else:
o = dirs[0]
proposed_field = (e[0] + o[0], e[1] + o[1])
break
if proposed_field is None:
nes.add(e)
elif proposed_field in proposed_fields:
proposed_fields[proposed_field].append(e)
else:
proposed_fields[proposed_field] = [e]
for ne, olves in proposed_fields.items():
if len(olves) == 1:
nes.add(ne)
else:
for e in olves:
nes.add(e)
dirs_to_check = dirs_to_check[1:] + dirs_to_check[:1]
round += 1
if second and elves == nes:
return round
elif not second and round == 10:
return print_field(nes)
elves = nes
def main():
DAY_INPUT = "d23.txt"
print("Example 1:", solve(Input(EXAMPLE)))
print("Solution 1:", solve(Input(DAY_INPUT)))
print("Example 2:", solve(Input(EXAMPLE), True))
print("Solution 2:", solve(Input(DAY_INPUT), True))
if __name__ == "__main__":
main()

93
2022/d24.py Normal file
View File

@ -0,0 +1,93 @@
from lib import *
EXAMPLE = """#.#####
#.....#
#>....#
#.....#
#...v.#
#.....#
#####.#
"""
EXAMPLE = """#.######
#>>.<^<#
#.<..<<#
#>v.><>#
#<^v^^>#
######.#
"""
B2DIR = {
">": (0, 1),
"v": (1, 0),
"<": (0, -1),
"^": (-1, 0),
}
ADJ5 = [
(-1, 0),
(0, 1),
(1, 0),
(0, -1),
(0, 0),
]
def solve(input: Input, second=False):
g = input.grid2()
start = (0, g.rows()[0].index('.'))
poss = set([(start, 0)])
end = (g.n_rows - 1, g.rows()[-1].index('.'))
bzs = []
for d in B2DIR.keys():
for b in g.find(d):
bzs.append((b, B2DIR[d]))
for time in range(10**9):
nbzs = []
bzsset = set()
for pos, dir in bzs:
row, col = add2(pos, dir)
if row == 0:
row = g.n_rows - 2
elif row == g.n_rows - 1:
row = 1
if col == 0:
col = g.n_cols - 2
elif col == g.n_cols - 1:
col = 1
nbzs.append(((row, col), dir))
bzsset.add((row, col))
bzs = nbzs
nposs = set()
while poss:
pos, round = poss.pop()
for adj in ADJ5:
npos = add2(pos, adj)
if npos[0] < 0 or npos[0] >= g.n_rows or npos[1] < 0 or npos[1] >= g.n_cols:
continue
if not second:
if npos not in bzsset and g[npos] != '#':
nposs.add((npos, round))
if npos == end:
return time + 1
else:
if npos == end and round == 0:
nposs.add((npos, round + 1))
elif npos == end and round == 2:
return time + 1
elif npos == start and round == 1:
nposs.add((npos, round + 1))
elif npos not in bzsset and g[npos] != '#':
nposs.add((npos, round))
poss = nposs
def main():
DAY_INPUT = "d24.txt"
print("Example 1:", solve(Input(EXAMPLE)))
print("Solution 1:", solve(Input(DAY_INPUT)))
print("Example 2:", solve(Input(EXAMPLE), True))
print("Solution 2:", solve(Input(DAY_INPUT), True))
if __name__ == "__main__":
main()

82
2022/d25.py Normal file
View File

@ -0,0 +1,82 @@
from lib import *
EXAMPLE = """1=-0-2
12111
2=0=
21
2=01
111
20012
112
1=-1=
1-12
12
1=
122
"""
def snafu_to_dec(n: str) -> int:
r = 0
for i, c in enumerate(reversed(n)):
c = "=-012".index(c) - 2
r += c * 5**i
return r
def dec_to_snafu(n: int) -> str:
r = ""
while n != 0:
rem = n % 5
n //= 5
if rem <= 2:
r = str(rem) + r
else:
r = " =-"[rem] + r
n += 1
return r
def dec_to_snafu_cs(n: int) -> str:
import cpmpy as cp
import numpy as np
size = 20
xs = cp.intvar(-2, 2, shape=size, name="xs")
ns = np.array([5**i for i in range(size)])
m = cp.Model(cp.sum(ns * xs) == n)
m.solve()
value = xs.value()
assert value is not None
# print(value)
r = ""
for v in value:
if v >= 0:
r += str(v)
elif v == -1:
r += "-"
elif v == -2:
r += "="
else:
assert False
r = "".join(reversed(r))
r = r.lstrip("0")
return r
def solve(input: Input):
res = 0
for s in input.lines():
d = snafu_to_dec(s)
res += d
try:
assert dec_to_snafu_cs(res) == dec_to_snafu(res)
except ModuleNotFoundError:
print("Install 'cpmpy' to solve with constraint solver.")
return dec_to_snafu(res)
def main():
DAY_INPUT = "d25.txt"
print("Example 1:", solve(Input(EXAMPLE)))
print("Solution 1:", solve(Input(DAY_INPUT)))
if __name__ == "__main__":
main()

65
2022/d3.py Normal file
View File

@ -0,0 +1,65 @@
from string import ascii_lowercase, ascii_uppercase
EXAMPLE = """
vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw
"""
def let_to_score(cin):
d = {}
for i, c in enumerate(ascii_lowercase):
d[c] = i + 1
for i, c in enumerate(ascii_uppercase):
d[c] = i + 27
return d[cin]
def clean(text: str) -> list[str]:
return list(filter(lambda l: l.strip() != "", text.splitlines()))
def solve(lines: list[str]):
s = 0
for (i, line) in enumerate(lines):
half = len(line) // 2
a, b = line[:half], line[half:]
a = set(a)
b = set(b)
z = a.intersection(b)
l = list(z)[0]
score = let_to_score(l)
s += score
# 8:42
return s
def get_score(l):
s = 0
s = set(l[0]).intersection(set(l[1])).intersection(set(l[2]))
s = list(s)[0]
return let_to_score(s)
def solve2(lines: list[str]):
s = 0
for i in range(0, len(lines), 3):
s += get_score(lines[i:i+3])
return s
def main():
example = clean(EXAMPLE)
print("Example 1:", solve(example))
data = clean(open("d3.txt").read())
print("Solution 1:", solve(data))
example = clean(EXAMPLE)
print("Example 2:", solve2(example))
data = clean(open("d3.txt").read())
print("Solution 2:", solve2(data))
# 13:08
if __name__ == "__main__":
main()

64
2022/d4.py Normal file
View File

@ -0,0 +1,64 @@
import re
from string import ascii_lowercase, ascii_uppercase
EXAMPLE = """
2-4,6-8
2-3,4-5
5-7,7-9
2-8,3-7
6-6,4-6
2-6,4-8
"""
def clean(text: str) -> list[str]:
return list(filter(lambda l: l.strip() != "", text.splitlines()))
def solve(lines: list[str]):
s = 0
for (i, line) in enumerate(lines):
s1, s2 = line.split(',')
a, b = s1.split('-')
c, d = s2.split('-')
a = int(a)
b = int(b)
c = int(c)
d = int(d)
a = set(list(range(a, b + 1)))
b = set(list(range(c, d + 1)))
if a <= b or b <= a:
s += 1
return s
def solve2(lines: list[str]):
s = 0
for (i, line) in enumerate(lines):
s1, s2 = line.split(',')
a, b = s1.split('-')
c, d = s2.split('-')
a = int(a)
b = int(b)
c = int(c)
d = int(d)
a = set(list(range(a, b + 1)))
b = set(list(range(c, d + 1)))
c = a.intersection(b)
if c:
s += 1
# 7:08
return s
def main():
example = clean(EXAMPLE)
print("Example 1:", solve(example))
data = clean(open("d4.txt").read())
print("Solution 1:", solve(data))
example = clean(EXAMPLE)
print("Example 2:", solve2(example))
data = clean(open("d4.txt").read())
print("Solution 2:", solve2(data))
if __name__ == "__main__":
main()

97
2022/d5.py Normal file
View File

@ -0,0 +1,97 @@
import re
from string import ascii_lowercase, ascii_uppercase
EXAMPLE = """
[D]
[N] [C]
[Z] [M] [P]
1 2 3
1 5 9
move 1 from 2 to 1
move 3 from 1 to 3
move 2 from 2 to 1
move 1 from 1 to 2
"""
def clean(text: str) -> list[str]:
return list(filter(lambda l: l.strip() != "", text.splitlines()))
def solve(lines: list[str]):
s = 0
stacks = [[] for _ in range(10)]
for (_, line) in enumerate(lines):
if line.startswith("move"):
continue
for (j, c) in enumerate(line):
if c in ascii_uppercase:
i = (j - 1) // 4
stacks[i].append(c)
for stack in stacks:
stack.reverse()
r = re.compile(r"move (\d+) from (\d+) to (\d+)")
for line in lines:
if not line.startswith("move"):
continue
if (m := r.match(line)):
amount, fr, to = m.groups()
amount = int(amount)
fr = int(fr) - 1
to = int(to) - 1
stacks[to] += reversed(stacks[fr][-amount:])
stacks[fr] = stacks[fr][:-amount]
m = ""
for s in stacks:
if s:
m += s[-1]
# 10:34
return m
def solve2(lines: list[str]):
s = 0
stacks = [[] for _ in range(10)]
for (_, line) in enumerate(lines):
if line.startswith("move"):
continue
for (j, c) in enumerate(line):
if c in ascii_uppercase:
i = (j - 1) // 4
stacks[i].append(c)
for stack in stacks:
stack.reverse()
r = re.compile(r"move (\d+) from (\d+) to (\d+)")
for line in lines:
if not line.startswith("move"):
continue
if (m := r.match(line)):
amount, fr, to = m.groups()
amount = int(amount)
fr = int(fr) - 1
to = int(to) - 1
stacks[to] += stacks[fr][-amount:]
stacks[fr] = stacks[fr][:-amount]
m = ""
for s in stacks:
if s:
m += s[-1]
# 11:56
return m
def main():
example = clean(EXAMPLE)
print("Example 1:", solve(example))
data = clean(open("d5.txt").read())
print("Solution 1:", solve(data))
example = clean(EXAMPLE)
print("Example 2:", solve2(example))
data = clean(open("d5.txt").read())
print("Solution 2:", solve2(data))
if __name__ == "__main__":
main()

41
2022/d6.py Normal file
View File

@ -0,0 +1,41 @@
import re
from string import ascii_lowercase, ascii_uppercase
EXAMPLE = """
mjqjpqmgbljsphdztnvjfqwrcgsmlb
"""
def clean(text: str) -> list[str]:
return list(filter(lambda l: l.strip() != "", text.splitlines()))
def solve(lines: list[str]):
line = lines[0]
for i in range(len(lines[0])):
l = line[i:i + 4]
if len(set(l)) == len(l):
return i + 4
# 2:55
def solve2(lines: list[str]):
line = lines[0]
for i in range(len(lines[0])):
l = line[i:i + 14]
if len(set(l)) == len(l):
return i + 14
# 3:50
def main():
example = clean(EXAMPLE)
print("Example 1:", solve(example))
data = clean(open("d6.txt").read())
print("Solution 1:", solve(data))
example = clean(EXAMPLE)
print("Example 2:", solve2(example))
data = clean(open("d6.txt").read())
print("Solution 2:", solve2(data))
if __name__ == "__main__":
main()

117
2022/d7.py Normal file
View File

@ -0,0 +1,117 @@
import re
from string import ascii_lowercase, ascii_uppercase, digits
EXAMPLE = """
$ cd /
$ ls
dir a
14848514 b.txt
8504156 c.dat
dir d
$ cd a
$ ls
dir e
29116 f
2557 g
62596 h.lst
$ cd e
$ ls
584 i
$ cd ..
$ cd ..
$ cd d
$ ls
4060174 j
8033020 d.log
5626152 d.ext
7214296 k
"""
def clean(text: str) -> list[str]:
return list(filter(lambda l: l.strip() != "", text.splitlines()))
def solve(lines: list[str]):
sizes = {}
res = 0
current_dir = ()
for line in lines:
if line.startswith("$ cd"):
c = line.replace("$ cd ", "").strip()
if c == "..":
current_dir = tuple(list(current_dir)[:-1])
else:
current_dir = tuple(list(current_dir) + [c])
elif line[0] in digits:
size, _ = line.split()
size = int(size)
try:
sizes[current_dir] += size
except KeyError:
sizes[current_dir] = size
for i in range(1, len(current_dir)):
outer_dir = tuple(list(current_dir)[:-i])
try:
sizes[outer_dir] += size
except KeyError:
sizes[outer_dir] = size
for _, size in sizes.items():
if size <= 100000:
res += size
# 23:50
return res
def solve2(lines: list[str]):
sizes = {}
res = 0
current_dir = ()
for line in lines:
if line.startswith("$ cd"):
c = line.replace("$ cd ", "").strip()
if c == "..":
current_dir = tuple(list(current_dir)[:-1])
else:
current_dir = tuple(list(current_dir) + [c])
elif line[0] in digits:
size, _ = line.split()
size = int(size)
try:
sizes[current_dir] += size
except KeyError:
sizes[current_dir] = size
for i in range(1, len(current_dir)):
outer_dir = tuple(list(current_dir)[:-i])
try:
sizes[outer_dir] += size
except KeyError:
sizes[outer_dir] = size
total_space = 70000000
unused_space = 30000000
used_space = sizes[("/",)]
res = 2**32
for _, size in sizes.items():
if total_space - used_space + size >= unused_space and size < res:
res = size
# 27:55
return res
def main():
example = clean(EXAMPLE)
print("Example 1:", solve(example))
data = clean(open("d7.txt").read())
print("Solution 1:", solve(data))
example = clean(EXAMPLE)
print("Example 2:", solve2(example))
data = clean(open("d7.txt").read())
print("Solution 2:", solve2(data))
if __name__ == "__main__":
main()

132
2022/d8.py Normal file
View File

@ -0,0 +1,132 @@
import re
from string import ascii_lowercase, ascii_uppercase
EXAMPLE = """
30373
25512
65332
33549
35390
"""
def clean(text: str) -> list[str]:
return list(filter(lambda l: l.strip() != "", text.splitlines()))
def is_visible(trees, y_tree, x_tree):
y_max = len(trees)
x_max = len(trees[0])
tree_height = trees[y_tree][x_tree]
for x in range(0, x_tree):
if trees[y_tree][x] >= tree_height:
break
else:
return True
for x in range(x_tree + 1, x_max):
if trees[y_tree][x] >= tree_height:
break
else:
return True
for y in range(0, y_tree):
if trees[y][x_tree] >= tree_height:
break
else:
return True
for y in range(y_tree + 1, y_max):
if trees[y][x_tree] >= tree_height:
break
else:
return True
return False
def solve(lines: list[str]):
res = 0
trees = []
for i in range(len(lines)):
line = lines[i]
trees.append(list(map(int, line)))
for y in range(len(trees)):
for x in range(len(trees)):
if is_visible(trees, y, x):
res += 1
else:
pass
return res
def scenic_score(trees, y_tree, x_tree):
y_max = len(trees)
x_max = len(trees[0])
if x_tree == 0 or y_tree == 0 or x_tree == x_max - 1 or y_tree == y_max - 1:
return 0
tree_height = trees[y_tree][x_tree]
left, right, up, down = 0, 0, 0, 0
for x in range(x_tree - 1, -1, -1):
if trees[y_tree][x] < tree_height:
left += 1
else:
left += 1
break
for x in range(x_tree + 1, x_max):
if trees[y_tree][x] < tree_height:
right += 1
else:
right += 1
break
for y in range(y_tree - 1, -1, -1):
if trees[y][x_tree] < tree_height:
up += 1
else:
up += 1
break
for y in range(y_tree + 1, y_max):
if trees[y][x_tree] < tree_height:
down += 1
else:
down += 1
break
score = up * down * left * right
# print(f"{y_tree=} {x_tree=} {left=} {right=} {up=} {down=} {score=}")
return score
def solve2(lines: list[str]):
trees = []
for i in range(len(lines)):
line = lines[i]
trees.append(list(map(int, line)))
max_score = 0
for y in range(len(trees)):
for x in range(len(trees)):
score = scenic_score(trees, y, x)
if score > max_score:
max_score = score
return max_score
def main():
example = clean(EXAMPLE)
print("Example 1:", solve(example))
data = clean(open("d8.txt").read())
print("Solution 1:", solve(data))
example = clean(EXAMPLE)
print("Example 2:", solve2(example))
data = clean(open("d8.txt").read())
print("Solution 2:", solve2(data))
if __name__ == "__main__":
main()

128
2022/d9.py Normal file
View File

@ -0,0 +1,128 @@
EXAMPLE = """
R 4
U 4
L 3
D 1
R 4
D 1
L 5
R 2
"""
def clean(text: str) -> list[str]:
return list(filter(lambda l: l.strip() != "", text.splitlines()))
def new_pos(h_pos, t_pos, dir):
if dir == "R":
new_h_pos = (h_pos[0] + 1, h_pos[1])
elif dir == "U":
new_h_pos = (h_pos[0], h_pos[1] + 1)
elif dir == "L":
new_h_pos = (h_pos[0] - 1, h_pos[1])
elif dir == "D":
new_h_pos = (h_pos[0], h_pos[1] - 1)
elif dir == "NONE":
new_h_pos = (h_pos[0], h_pos[1])
else:
raise Exception("Unexpected direction.")
# xxxxx
# x---x
# x-O-x
# x---x
# xxxxx
dx, dy = t_pos[0] - new_h_pos[0], t_pos[1] - new_h_pos[1]
delta = {
(-1, -1): (0, 0),
(-1, 0): (0, 0),
(-1, 1): (0, 0),
(0, -1): (0, 0),
(0, 0): (0, 0),
(0, 1): (0, 0),
(1, -1): (0, 0),
(1, 0): (0, 0),
(1, 1): (0, 0),
(-2, 2): (1, -1),
(-2, 1): (1, -1),
(-2, 0): (1, 0),
(-2, -1): (1, 1),
(-2, -2): (1, 1),
(2, 2): (-1, -1),
(2, 1): (-1, -1),
(2, 0): (-1, 0),
(2, -1): (-1, 1),
(2, -2): (-1, 1),
(-1, 2): (1, -1),
(-1, -2): (1, 1),
(0, 2): (0, -1),
(0, -2): (0, 1),
(1, 2): (-1, -1),
(1, -2): (-1, 1),
}
dx_, dy_ = delta[(dx, dy)]
new_t_pos = t_pos[0] + dx_, t_pos[1] + dy_
# print(f"{h_pos=} {t_pos=} {dir=}")
# print(f"{new_h_pos=} {dx=} {dy=}")
# print(f"{new_t_pos=}")
# print("----")
return new_t_pos, new_h_pos
def solve(lines: list[str]):
pos = set()
h_pos = (0, 0)
t_pos = (0, 0)
pos.add(t_pos)
for i in range(len(lines)):
dir, amount = lines[i].split()
for _ in range(int(amount)):
t_pos, h_pos = new_pos(h_pos, t_pos, dir)
pos.add(t_pos)
# 44:00
return len(pos)
EXAMPLE2 = """
R 5
U 8
L 8
D 3
R 17
D 10
L 25
U 20
"""
def solve2(lines: list[str]):
vis = set()
poss = [(0, 0) for _ in range(10)]
vis.add((0, 0))
for i in range(len(lines)):
dir, amount = lines[i].split()
for _ in range(int(amount)):
for i in range(len(poss) - 1):
h_pos = poss[i]
t_pos = poss[i + 1]
if i == 0:
t_pos, h_pos = new_pos(h_pos, t_pos, dir)
else:
t_pos, h_pos = new_pos(h_pos, t_pos, "NONE")
poss[i] = h_pos
poss[i + 1] = t_pos
vis.add(poss[-1])
# 58:00
return len(vis)
def main():
example = clean(EXAMPLE)
print("Example 1:", solve(example))
data = clean(open("d9.txt").read())
print("Solution 1:", solve(data))
example = clean(EXAMPLE2)
print("Example 2:", solve2(example))
data = clean(open("d9.txt").read())
print("Solution 2:", solve2(data))
if __name__ == "__main__":
main()

1
2022/lib.py Symbolic link
View File

@ -0,0 +1 @@
../lib.py

View File

@ -1,30 +0,0 @@
from lib import *
EXAMPLE = """
"""
def solve(input: Input, second=False):
res = 0
input.stats()
# g = input.grid2()
# ls = input.lines()
# ps = input.paras()
return res
def main():
DAY_INPUT = "ix.txt"
print("Example 1:", solve(Input(EXAMPLE)))
return
print("Solution 1:", solve(Input(DAY_INPUT)))
return
print("Example 2:", solve(Input(EXAMPLE), True))
return
print("Solution 2:", solve(Input(DAY_INPUT), True))
return
if __name__ == "__main__":
main()

View File

@ -101,6 +101,51 @@ written in Python.
# 2022
Done with this. Overall everything is solvable. It's more about consistency
and focus. Of course, learning more algorithms and techniques helps.
Possible to-dos:
- [ ] Optimize day 19 because it is terrible.
**Times:**
- Day 1: 7:52 ... so slow brah :/ top 100 required 2:05...
- Day 2: 22:30 ... I mistyped the first and second was just bad top 100 would
have been 6:16 (doable?)
- Day 3: 13:08 actually decent but top 100 required 5:24
- Day 4: 7:08 but top 100 required 3:33 still okay
- Day 5: 11:56 but 7:58 for top 100... getting better?
- Day 6: 3:50 but 2:25 for leaderboard :D
- Day 7: 27:55 and 14:47 for leaderboard; okay, I would say
- Day 8: 61:00 and 10:00 for leaderboard; I need template code for searching
coordinate systems
- Day 9: 58:00 and 7:32 for leaderboard; I need code for 2D stuff
- Day 10: 25:20 and 12:17 for leaderboard; okay, okay
- Day 11: 45:00 and 18:00 for leaderboard; arg, it was doable man
- Day 12: 39:45 and 9:46 for leaderboard; the people are ready for their
searches :D
- Day 13: 44:00 and 12:56 for leaderboard; these people are just good,
seriously
- Day 14: 35:00 and 14:54; I just have to get that much quicker... 2D code!
- Day 15: 150:00 and 27:00; I didn't use Manhattan dist initially, lot's of
debugging
- Day 16: 52:00 and 64:00; ARE YOU SAYING I WOULD HAVE MADE THE
LEADERBOARD?!?!?!?!?!?!?!
- Day 17: Second one was fun with having to detect the repetition.
- Day 18: 12:00 and 32:00; really straightforward and of course way too slow.
- Day 19: Slow.
- Day 20: Struggled way too much.
- Day 21: Straightforward and relatively fast.
- Day 22: Very hard and wasn't able to do hands free. Even the best guys took
over an hour.
- Day 23: Super straightforward, but took me way longer than it should have
because I loose focus and make silly errors. Like back in Middleschool.
- Day 24: Easy. Should have been faster, but no crazy mistakes. Still way too
slow for something easy like this.
- Day 25: Quickly solved via constraint solver. Actual solution much simpler. Don't know
if I would have been able to figure it out.
# 2023