Initial commit

This commit is contained in:
Geoffrey Frogeye 2024-12-25 12:58:02 +01:00
commit 97a4330bc0
Signed by: geoffrey
GPG key ID: C72403E7F82E6AD8
110 changed files with 7006 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# Don't include prompts and inputs as they're not free to copy
**/input*
**/prompt
# Demos are also part of the prompt...
**/demo*
# ... except when I wrote those myself
!**/demog*

5
2023/1/obfuscated.py Normal file
View file

@ -0,0 +1,5 @@
import re
d="zero|one|two|three|four|five|six|seven|eight|nine|\\d)"
def f(l,p):
m=re.search(p,l)[1];return str(d.split("|").index(m)) if m in d else m
print(sum(map(lambda l:int(f(l,f"({d}.*")+f(l,".*("+d)),open(0).readlines())))

30
2023/1/script.py Normal file
View file

@ -0,0 +1,30 @@
#!/usr/bin/env python3
import re
digits = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
group = "|".join(digits + ["[0-9]"])
tot = 0
with open("lines.txt") as lines:
for line in lines.readlines():
print()
line = line.rstrip()
print(line)
last = re.search(rf"^.*({group})", line)
first = re.search(rf"({group}).*$", line)
print(first, last)
f = first[1]
l = last[1]
print(f, l)
if f in digits:
f = str(digits.index(f))
if l in digits:
l = str(digits.index(l))
print(f, l)
numb = int(f + l)
tot += numb
print(numb)
print()
print(tot)

28
2023/2/one.py Normal file
View file

@ -0,0 +1,28 @@
maxs = {"red": 12, "green": 13, "blue": 14}
gid = 0
possible_gid_sum = 0
with open("input") as lines:
for line in lines.readlines():
gid += 1
line = line.rstrip()
game_full, sets = line.split(":")
game, gid_str = game_full.split(" ")
assert int(gid_str) == gid
possible = True
for seet in sets.split(";"):
gcs = {"red": 0, "green": 0, "blue": 0}
for color in seet.split(","):
amount_str, color = color.strip().split(" ")
amount = int(amount_str)
gcs[color] += amount
for color, amount in gcs.items():
max = maxs[color]
if amount > max:
possible = False
if possible:
possible_gid_sum += gid
print(gid, possible)
print(possible_gid_sum)

24
2023/2/two.py Normal file
View file

@ -0,0 +1,24 @@
maxs = {"red": 12, "green": 13, "blue": 14}
gid = 0
power_sum = 0
with open("input") as lines:
for line in lines.readlines():
gid += 1
line = line.rstrip()
game_full, sets = line.split(":")
game, gid_str = game_full.split(" ")
assert int(gid_str) == gid
possible = True
gcs = {"red": 0, "green": 0, "blue": 0}
for seet in sets.split(";"):
for color in seet.split(","):
amount_str, color = color.strip().split(" ")
amount = int(amount_str)
gcs[color] = max(amount, gcs[color])
power = gcs["red"] * gcs["green"] * gcs["blue"]
print(gid, power)
power_sum += power
print(power_sum)

16
2023/24/one.py Normal file
View file

@ -0,0 +1,16 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
hailstones = []
with open(input_file) as fd:
for line in fd.readlines():
line = line.rstrip()
line.replace("@", ",")
hailstone = [int(h) for h in line.split(",")]
hailstones.append(hailstone)

41
2023/3/one.py Normal file
View file

@ -0,0 +1,41 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
sum = 0
for i in range(height):
line = lines[i]
pn_str = ""
for j in range(width):
c = line[j]
# print(19, c)
if c.isnumeric():
if not pn_str:
left = j
pn_str += c
# print(20, c, pn_str)
if pn_str and (j == width - 1 or not line[j + 1].isnumeric()):
print(25, pn_str)
adj = False
for ii in range(max(i - 1, 0), min(i + 1, height - 1) + 1):
for jj in range(max(left - 1, 0), min(j + 1, width - 1) + 1):
cc = lines[ii][jj]
print(ii, jj, cc)
if not cc.isnumeric() and cc != ".":
print("!")
adj = True
# print(pn_str, adj)
if adj:
pn = int(pn_str)
sum += pn
pn_str = ""
print(sum)

42
2023/3/two.py Normal file
View file

@ -0,0 +1,42 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
gears = dict()
sum = 0
for i in range(height):
line = lines[i]
pn_str = ""
for j in range(width):
c = line[j]
# print(19, c)
if c.isnumeric():
if not pn_str:
left = j
pn_str += c
# print(20, c, pn_str)
if pn_str and (j == width - 1 or not line[j + 1].isnumeric()):
for ii in range(max(i - 1, 0), min(i + 1, height - 1) + 1):
for jj in range(max(left - 1, 0), min(j + 1, width - 1) + 1):
cc = lines[ii][jj]
# print(ii, jj, cc)
if cc == "*":
gears.setdefault((ii, jj), list())
gears[(ii, jj)].append(int(pn_str))
pn_str = ""
for gear_numbers in gears.values():
if len(gear_numbers) != 2:
continue
gear_ratio = gear_numbers[0] * gear_numbers[1]
sum += gear_ratio
print(sum)

26
2024/1/one.py Normal file
View file

@ -0,0 +1,26 @@
listl = []
listr = []
with open("input") as lines:
for line in lines.readlines():
line = line.rstrip()
print(line)
spli = line.split(" ")
listl.append(int(spli[0]))
listr.append(int(spli[-1]))
assert len(listl) == len(listr)
listl.sort()
listr.sort()
dtot = 0
for i in range(len(listl)):
l = listl[i]
r = listr[i]
d = abs(l-r)
dtot += d
print(l, r, d)
print(dtot)

25
2024/1/two.py Normal file
View file

@ -0,0 +1,25 @@
listl = []
listr = []
with open("input") as lines:
for line in lines.readlines():
line = line.rstrip()
print(line)
spli = line.split(" ")
listl.append(int(spli[0]))
listr.append(int(spli[-1]))
assert len(listl) == len(listr)
listl.sort()
listr.sort()
dtot = 0
for i in range(len(listl)):
l = listl[i]
d = listr.count(l) * l
dtot += d
print(l, d)
print(dtot)

61
2024/10/one.py Normal file
View file

@ -0,0 +1,61 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
tmap: list[list[int]] = [[int(a) for a in line] for line in lines]
directions = [
(0, 1),
(0, -1),
(1, 0),
(-1, 0),
]
def print_path(path: list[tuple[int, int]]) -> None:
viz = [["."] * width for _ in range(height)]
for c, pos in enumerate(path):
i, j = pos
viz[i][j] = str(c)
for line in viz:
print("".join(line))
print()
def score(pos: tuple[int, int], path: list[tuple[int, int]]) -> set[tuple[int, int]]:
path = path + [pos]
i, j = pos
c = tmap[i][j]
if c == 9:
return {pos}
reachable = set()
for direction in directions:
ii, jj = i + direction[0], j + direction[1]
if ii not in range(height) or jj not in range(width):
continue
cc = tmap[ii][jj]
if cc != c + 1:
continue
reachable |= score((ii, jj), path)
return reachable
tscore = 0
for i in range(height):
for j in range(width):
c = tmap[i][j]
if c != 0:
continue
cscore = len(score((i, j), []))
# print(i, j, cscore)
tscore += cscore
print(tscore)

64
2024/10/two.py Normal file
View file

@ -0,0 +1,64 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
tmap: list[list[int]] = [[int(a) for a in line] for line in lines]
directions = [
(0, 1),
(0, -1),
(1, 0),
(-1, 0),
]
def print_path(path: list[tuple[int, int]]) -> None:
viz = [["."] * width for _ in range(height)]
for c, pos in enumerate(path):
i, j = pos
viz[i][j] = str(c)
for line in viz:
print("".join(line))
print()
def score(pos: tuple[int, int], path: list[tuple[int, int]]) -> int:
path = path + [pos]
i, j = pos
c = tmap[i][j]
if c == 9:
# print_path(path)
return 1
cscore = 0
for direction in directions:
ii, jj = i + direction[0], j + direction[1]
if ii not in range(height) or jj not in range(width):
continue
cc = tmap[ii][jj]
if cc != c + 1:
continue
cscore += score((ii, jj), path)
return cscore
tscore = 0
for i in range(height):
for j in range(width):
c = tmap[i][j]
if c != 0:
continue
cscore = score((i, j), [])
print(i, j, cscore)
tscore += cscore
# break
# break
print(tscore)

30
2024/11/one.py Normal file
View file

@ -0,0 +1,30 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
stones = [int(stone) for stone in lines[0].split()]
for _ in range(25):
new_stones = []
for stone in stones:
stone_str = str(stone)
if stone == 0:
new_stones.append(1)
elif len(stone_str) % 2 == 0:
mid = int(len(stone_str) / 2)
new_stones.append(int(stone_str[:mid]))
new_stones.append(int(stone_str[mid:]))
else:
new_stones.append(stone * 2024)
stones = new_stones
# print(" ".join(str(stone) for stone in stones))
print(len(stones))

121
2024/11/two.py Normal file
View file

@ -0,0 +1,121 @@
#!/usr/bin/env python3
import bisect
import sys
import time
import functools
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
start = time.time()
stones = [int(stone) for stone in lines[0].split()]
# for i in range(75):
# print(i, time.time() - start, len(stones))
# # for s, stone in enumerate(stones):
# # if stone == 0:
# # stones[s] = 1
# # continue
# # stone_str = str(stone)
# # if len(stone_str) % 2 == 0:
# # mid = int(len(stone_str) / 2)
# # stones[s] = int(stone_str[:mid])
# # stones.insert(s, int(stone_str[mid:]))
# # else:
# # stones[s] *= 2024
# new_stones = []
# for stone in stones:
# stone_str = str(stone)
# if stone == 0:
# new_stones.append(1)
# elif len(stone_str) % 2 == 0:
# mid = int(len(stone_str) / 2)
# new_stones.append(int(stone_str[:mid]))
# new_stones.append(int(stone_str[mid:]))
# else:
# new_stones.append(stone * 2024)
# stones = new_stones
target = int(sys.argv[2])
its = [0] * len(stones)
start_stones = stones.copy()
pow10 = list()
for i in range(25):
pow10.append(10**i)
def num_digits(a: int) -> int:
# for i, p in enumerate(pow10):
# if a < p:
# # assert len(str(a)) == i
# return i
# raise NotImplementedError
# return bisect.bisect(pow10, a)
# # nb = bisect.bisect(pow10, a)
num = 0
while a > 0:
num += 1
a //= 10
# assert nb == num
return num
# lstones = 0
# for e, sstone in enumerate(start_stones):
# print(f"47 {e}/{len(start_stones)} {time.time() - start}")
# stones = [sstone]
# while stones:
# stone = stones.pop(0)
# it = its.pop(0)
# lstones += 1
# if stone == 0:
# stone = 1
# it += 1
# nd = num_digits(stone)
# for i in range(it, target):
# print(stone)
# if nd % 2 == 0:
# mid = nd // 2
# left, right = divmod(stone, pow10[mid])
# # left, right = divmod(stone, 10**mid)
# stone = left
# stones.insert(0, right)
# its.insert(0, i + 1)
# nd = mid
# else:
# stone *= 2024
# nd = num_digits(stone)
# # print(f"64 {stone}")
# @functools.lru_cache(maxsize=1024)
@functools.cache
def proc(stone: int, target: int) -> int:
if target == 0:
return 1
target -= 1
if stone == 0:
return proc(1, target)
nd = num_digits(stone)
if nd % 2 == 0:
mid = nd // 2
left, right = divmod(stone, pow10[mid])
return proc(left, target) + proc(right, target)
else:
return proc(stone * 2024, target)
lstones = 0
for e, stone in enumerate(stones):
print(f"47 {e}/{len(stones)} {time.time() - start}")
lstones += proc(stone, target)
print(f"{target=}")
print(lstones)

83
2024/12/one.py Normal file
View file

@ -0,0 +1,83 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
visited: set[tuple[int, int]] = set()
directions = [
(0, 1),
(0, -1),
(1, 0),
(-1, 0),
]
def get_region(
pos: tuple[int, int], region: set[tuple[int, int]]
) -> set[tuple[int, int]]:
region.add(pos)
i, j = pos
ochar = lines[i][j]
for direction in directions:
i, j = pos[0] + direction[0], pos[1] + direction[1]
if i not in range(height) or j not in range(width):
continue
if (i, j) in region:
continue
char = lines[i][j]
if char != ochar:
continue
region |= get_region((i, j), region)
return region
def get_perimeter(region: set[tuple[int, int]]) -> int:
peri = 0
for axis in (0, 1):
oaxis = 0 if axis else 1
iss = set([pos[axis] for pos in region])
print(47, iss, peri)
for i in iss:
line = [pos[oaxis] for pos in region if pos[axis] == i]
line.sort()
last_j = None
for j in line:
if last_j is None:
peri += 1
elif last_j == j - 1:
pass
else:
peri += 2
last_j = j
if last_j is not None:
peri += 1
print(62, i, peri, line)
return peri
tprice = 0
for i in range(height):
for j in range(width):
pos = i, j
if pos in visited:
continue
region = get_region(pos, set())
visited |= region
area = len(region)
peri = get_perimeter(region)
price = area * peri
tprice += price
char = lines[i][j]
print(f"{char}: {area} × {peri} = {price}$")
print(tprice)

5
2024/12/reddit_test Normal file
View file

@ -0,0 +1,5 @@
AAXXX
AAXAX
AAAAX
AAXAX
AAXXX

136
2024/12/two.py Normal file
View file

@ -0,0 +1,136 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
visited: set[tuple[int, int]] = set()
directions = [
(0, 1),
(0, -1),
(1, 0),
(-1, 0),
]
def get_region(
pos: tuple[int, int], region: set[tuple[int, int]]
) -> set[tuple[int, int]]:
region.add(pos)
i, j = pos
ochar = lines[i][j]
for direction in directions:
i, j = pos[0] + direction[0], pos[1] + direction[1]
if i not in range(height) or j not in range(width):
continue
if (i, j) in region:
continue
char = lines[i][j]
if char != ochar:
continue
region |= get_region((i, j), region)
return region
# def get_sides(region: set[tuple[int, int]]) -> int:
# peri = 0
# for axis in (0, 1):
# oaxis = 0 if axis else 1
# iss = set([pos[axis] for pos in region])
# sta: set[int] = set()
# sto: set[int] = set()
# for i in iss:
# line = [pos[oaxis] for pos in region if pos[axis] == i]
# line.sort()
# last_j = None
# for j in line:
# if last_j is None:
# sta.add(j)
# elif last_j == j - 1:
# pass
# else:
# sta.add(j)
# sto.add(last_j)
# last_j = j
# if last_j is not None:
# sto.add(last_j)
# peri += len(sta) + len(sto)
# return peri
def get_perimeter(region: set[tuple[int, int]]) -> int:
peri = 0
for axis in (0, 1):
oaxis = 0 if axis else 1
iss = set([pos[axis] for pos in region])
for dire in (-1, 1):
print(47, axis, dire, iss, peri)
for i in iss:
oi = i + dire
line = [pos[oaxis] for pos in region if pos[axis] == i]
line.sort()
# last_j = None
for j in line:
if not axis:
opos = oi, j
else:
opos = j, oi
if opos in region:
continue
peri += 1
return peri
def get_sides(region: set[tuple[int, int]]) -> int:
peri = 0
for axis in (0, 1):
oaxis = 0 if axis else 1
iss = set([pos[axis] for pos in region])
for dire in (-1, 1):
print(47, axis, dire, iss, peri)
for i in iss:
oi = i + dire
line = [pos[oaxis] for pos in region if pos[axis] == i]
line.sort()
last_j = None
for j in line:
if not axis:
opos = oi, j
else:
opos = j, oi
if opos in region:
last_j = None
continue
if last_j == j - 1:
pass
else:
peri += 1
last_j = j
return peri
tprice = 0
for i in range(height):
for j in range(width):
pos = i, j
if pos in visited:
continue
region = get_region(pos, set())
visited |= region
area = len(region)
sides = get_sides(region)
price = area * sides
tprice += price
char = lines[i][j]
print(f"{char}: {area} × {sides} = {price}$")
print(tprice)

BIN
2024/13/Notes.xopp Normal file

Binary file not shown.

12
2024/13/demog Normal file
View file

@ -0,0 +1,12 @@
Button A: X+3, Y+1
Button B: X+4, Y+2
Prize: X=17, Y=7
Button A: X+1, Y+1
Button B: X+3, Y+3
Prize: X=7, Y=7
Button A: X+3, Y+3
Button B: X+1, Y+1
Prize: X=7, Y=7

78
2024/13/one.py Normal file
View file

@ -0,0 +1,78 @@
#!/usr/bin/env python3
import functools
import re
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
coords = tuple[int, int]
prizes: list[coords] = list()
buttons: list[tuple[coords, coords]] = list()
for li, line in enumerate(lines):
machine = li // 4
offset = li % 4
if offset == 0:
match = re.match(r"^Button A: X\+([0-9]+), Y\+([0-9]+)$", line)
assert match
button_a = int(match[1]), int(match[2])
elif offset == 1:
match = re.match(r"^Button B: X\+([0-9]+), Y\+([0-9]+)$", line)
assert match
button_b = int(match[1]), int(match[2])
buttons.append((button_a, button_b))
elif offset == 2:
match = re.match("^Prize: X=([0-9]+), Y=([0-9]+)$", line)
assert match
prize = int(match[1]), int(match[2])
prizes.append(prize)
assert len(prizes) == len(buttons)
ttoks = 0
for arcade, prize in enumerate(prizes):
butts = buttons[arcade]
button_a, button_b = butts
@functools.lru_cache(4096)
def fun(x: int, y: int, rem_a: int, rem_b: int) -> int | None:
if (x, y) == prize:
return 0
if x > prize[0] or y > prize[1]:
return None
ba = (
fun(x + button_a[0], y + button_a[1], rem_a - 1, rem_b)
if rem_a > 0
else None
)
bb = (
fun(x + button_b[0], y + button_b[1], rem_a, rem_b - 1)
if rem_b > 0
else None
)
if ba is not None:
ba += 3
if bb is not None:
bb += 1
if ba is None:
if bb is None:
return None
else:
return bb
else:
if bb is None or ba < bb:
return ba
else:
return bb
toks = fun(0, 0, 100, 100)
print(43, arcade, toks)
if toks is not None:
ttoks += toks
# break
print(ttoks)

223
2024/13/two.py Normal file
View file

@ -0,0 +1,223 @@
#!/usr/bin/env python3
import math
import re
import sys
import rich.progress
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
coords = tuple[int, int]
prizes: list[coords] = list()
buttons: list[tuple[coords, coords]] = list()
for li, line in enumerate(lines):
machine = li // 4
offset = li % 4
if offset == 0:
match = re.match(r"^Button A: X\+([0-9]+), Y\+([0-9]+)$", line)
assert match
button_a = int(match[1]), int(match[2])
elif offset == 1:
match = re.match(r"^Button B: X\+([0-9]+), Y\+([0-9]+)$", line)
assert match
button_b = int(match[1]), int(match[2])
buttons.append((button_a, button_b))
elif offset == 2:
match = re.match("^Prize: X=([0-9]+), Y=([0-9]+)$", line)
assert match
prize = int(match[1]), int(match[2])
prize = prize[0] + 10000000000000, prize[1] + 10000000000000
prizes.append(prize)
assert len(prizes) == len(buttons)
def slope(point: coords) -> float:
return point[1] / point[0]
def norm(point: coords) -> float:
return math.sqrt(math.pow(point[1], 2) + math.pow(point[0], 2))
#
# def in_range(p: coords, a: coords, b: coords) -> bool:
# slope_a = slope(button_a)
# slope_b = slope(button_b)
# slope_p = slope(p)
# slope_but_min = min(slope_a, slope_b)
# slope_but_max = max(slope_a, slope_b)
# return not (slope_p < slope_but_min or slope_p > slope_but_max)
ttoks = 0
token_a, token_b = 3, 1
for arcade, prize in enumerate(prizes):
butts = buttons[arcade]
button_a, button_b = butts
print(43, prize, button_a, button_b)
toks = None
max_a_x = int(math.ceil(prize[0] / button_a[0]))
max_a_y = int(math.ceil(prize[1] / button_a[1]))
max_a = min(max_a_x, max_a_y)
max_b_x = int(math.ceil(prize[0] / button_b[0]))
max_b_y = int(math.ceil(prize[1] / button_b[1]))
max_b = min(max_b_x, max_b_y)
slope_a = slope(button_a)
slope_b = slope(button_b)
slope_prize = slope(prize)
slope_but_min = min(slope_a, slope_b)
slope_but_max = max(slope_a, slope_b)
print(57, slope_but_min, slope_prize, slope_but_max)
if slope_prize < slope_but_min or slope_prize > slope_but_max:
print("Not in range")
continue
norm_a = norm(button_a)
norm_b = norm(button_b)
speed_a = norm_a / 3
speed_b = norm_b / 1
if speed_a > speed_b:
button_fastest, button_slowest = button_a, button_b
token_fastest, token_slowest = token_a, token_b
max_fastest, max_slowest = max_a, max_b
# slope_fastest, slope_slowes = slope_a, slope_b
# norm_fastest, norm_slowest = norm_a, norm_b
else:
button_fastest, button_slowest = button_b, button_a
token_fastest, token_slowest = token_b, token_a
max_fastest, max_slowest = max_b, max_a
# slope_fastest, slope_slowes = slope_b, slope_a
# norm_fastest, norm_slowest = norm_b, norm_a
toks = 0
# pri_x, pri_y = prize
# slope_pri = slope((pri_x, pri_y))
# while slope_pri >= slope_but_min and slope_pri <= slope_but_max:
# toks += token_fastest
# pri_x -= button_fastest[0]
# pri_y -= button_fastest[1]
# slope_pri = slope((pri_x, pri_y))
# # print(98, pri_x, pri_y, slope_pri, toks)
# pri_x += button_fastest[0]
# pri_y += button_fastest[1]
# toks -= token_fastest
# print(100, token_fastest, toks / token_fastest, toks)
min_presses_fastest = 0
max_presses_fastest = max_fastest
while min_presses_fastest + 1 < max_presses_fastest:
presses_fastest = int(
math.floor((min_presses_fastest + max_presses_fastest) / 2)
)
print(120, min_presses_fastest, max_presses_fastest, presses_fastest)
pri_x, pri_y = (
prize[0] - button_fastest[0] * presses_fastest,
prize[1] - button_fastest[1] * presses_fastest,
)
slope_pri = slope((pri_x, pri_y))
if slope_pri >= slope_but_min and slope_pri <= slope_but_max:
min_presses_fastest = presses_fastest
else:
max_presses_fastest = presses_fastest
presses_fastest = max_presses_fastest
pri_x, pri_y = (
prize[0] - button_fastest[0] * presses_fastest,
prize[1] - button_fastest[1] * presses_fastest,
)
pri_x += button_fastest[0]
pri_y += button_fastest[1]
toks = presses_fastest * token_fastest
toks -= token_fastest
print(101, token_fastest, toks / token_fastest, toks)
# while pri_x > 0 and pri_y > 0:
# toks += token_slowest
# pri_x -= button_slowest[0]
# pri_y -= button_slowest[1]
# print(103, token_slowest, toks)
# if (pri_x, pri_y) != (0, 0):
# toks = None
presses_slowest, remainder = divmod(pri_x, button_slowest[0])
if remainder == 0 and (pri_y == presses_slowest * button_slowest[1]):
toks += presses_slowest * token_slowest
else:
toks = None
# dist = norm((pri_x, pri_y))
# rem_presses, remainder = divmod(dist, norm_slowest)
# presses_slowest = dist / norm_slowest
# if remainder == 0:
# toks += rem_presses * token_slowest
# else:
# toks = None
#
# with rich.progress.Progress() as progress:
# nb_a = max_a
# nb_b = 0
# task_a = progress.add_task("Button A", total=max_a)
# task_b = progress.add_task("Button B", total=max_b)
# x = nb_a * button_a[0] + nb_b * button_b[0]
# while nb_a > 0 or x < prize[0]:
# # print(54, nb_a, nb_b)
# if x == prize[0]:
# y = nb_a * button_a[1] + nb_b * button_b[1]
# if y == prize[1]:
# tok = 3 * nb_a + 1 * nb_b
# if toks is None or tok < toks:
# toks = tok
# if x >= prize[0]:
# # print(67)
# nb_a -= 1
# # progress.update(task_a, advance=1)
# elif x < prize[0]:
# nb_b += 1
# # print(71)
# # progress.update(task_b, advance=1)
# if nb_b > max_b:
# break
# x = nb_a * button_a[0] + nb_b * button_b[0]
# @functools.lru_cache(4096)
# def fun(x: int, y: int) -> int | None:
# if (x, y) == prize:
# return 0
# if x > prize[0] or y > prize[1]:
# return None
# ba = fun(x + button_a[0], y + button_a[1])
# bb = fun(x + button_b[0], y + button_b[1])
# if ba is not None:
# ba += 3
# if bb is not None:
# bb += 1
# if ba is None:
# if bb is None:
# return None
# else:
# return bb
# else:
# if bb is None or ba < bb:
# return ba
# else:
# return bb
#
# toks = fun(0, 0)
print(43, arcade, toks)
if toks is not None:
ttoks += toks
# break
print(ttoks)

123
2024/13/two_clean.py Normal file
View file

@ -0,0 +1,123 @@
#!/usr/bin/env python3
import math
import re
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
coords = tuple[int, int]
prizes: list[coords] = list()
buttons: list[tuple[coords, coords]] = list()
for li, line in enumerate(lines):
machine = li // 4
offset = li % 4
if offset == 0:
match = re.match(r"^Button A: X\+([0-9]+), Y\+([0-9]+)$", line)
assert match
button_a = int(match[1]), int(match[2])
elif offset == 1:
match = re.match(r"^Button B: X\+([0-9]+), Y\+([0-9]+)$", line)
assert match
button_b = int(match[1]), int(match[2])
buttons.append((button_a, button_b))
elif offset == 2:
match = re.match("^Prize: X=([0-9]+), Y=([0-9]+)$", line)
assert match
prize = int(match[1]), int(match[2])
# prize = prize[0] + 10000000000000, prize[1] + 10000000000000
prizes.append(prize)
assert len(prizes) == len(buttons)
def slope(point: coords) -> float:
return point[1] / point[0]
def norm(point: coords) -> float:
return math.sqrt(math.pow(point[1], 2) + math.pow(point[0], 2))
ttoks = 0
token_a, token_b = 3, 1
for arcade, prize in enumerate(prizes):
butts = buttons[arcade]
button_a, button_b = butts
print(43, prize, button_a, button_b)
toks = None
max_a_x = int(math.ceil(prize[0] / button_a[0]))
max_a_y = int(math.ceil(prize[1] / button_a[1]))
max_a = min(max_a_x, max_a_y)
max_b_x = int(math.ceil(prize[0] / button_b[0]))
max_b_y = int(math.ceil(prize[1] / button_b[1]))
max_b = min(max_b_x, max_b_y)
slope_a = slope(button_a)
slope_b = slope(button_b)
slope_prize = slope(prize)
slope_but_min = min(slope_a, slope_b)
slope_but_max = max(slope_a, slope_b)
if slope_prize < slope_but_min or slope_prize > slope_but_max:
print("Not in range")
continue
norm_a = norm(button_a)
norm_b = norm(button_b)
speed_a = norm_a / 3
speed_b = norm_b / 1
if speed_a > speed_b:
button_fastest, button_slowest = button_a, button_b
token_fastest, token_slowest = token_a, token_b
max_fastest = max_a
else:
button_fastest, button_slowest = button_b, button_a
token_fastest, token_slowest = token_b, token_a
max_fastest = max_b
toks = 0
min_presses_fastest = 0
max_presses_fastest = max_fastest
while min_presses_fastest + 1 < max_presses_fastest:
presses_fastest = int(
math.floor((min_presses_fastest + max_presses_fastest) / 2)
)
pri_x, pri_y = (
prize[0] - button_fastest[0] * presses_fastest,
prize[1] - button_fastest[1] * presses_fastest,
)
slope_pri = slope((pri_x, pri_y))
if slope_pri >= slope_but_min and slope_pri <= slope_but_max:
min_presses_fastest = presses_fastest
else:
max_presses_fastest = presses_fastest
presses_fastest = max_presses_fastest
pri_x, pri_y = (
prize[0] - button_fastest[0] * presses_fastest,
prize[1] - button_fastest[1] * presses_fastest,
)
pri_x += button_fastest[0]
pri_y += button_fastest[1]
toks = presses_fastest * token_fastest
toks -= token_fastest
presses_slowest, remainder = divmod(pri_x, button_slowest[0])
if remainder == 0 and (pri_y == presses_slowest * button_slowest[1]):
toks += presses_slowest * token_slowest
else:
toks = None
print(76, toks)
if toks is not None:
ttoks += toks
print(ttoks)

64
2024/13/two_reddit.py Normal file
View file

@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""
Implementing:
https://www.reddit.com/r/adventofcode/comments/1hd7irq/2024_day_13_an_explanation_of_the_mathematics/
"""
import re
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
coords = tuple[int, int]
prizes: list[coords] = list()
buttons: list[tuple[coords, coords]] = list()
for li, line in enumerate(lines):
machine = li // 4
offset = li % 4
if offset == 0:
match = re.match(r"^Button A: X\+([0-9]+), Y\+([0-9]+)$", line)
assert match
button_a = int(match[1]), int(match[2])
elif offset == 1:
match = re.match(r"^Button B: X\+([0-9]+), Y\+([0-9]+)$", line)
assert match
button_b = int(match[1]), int(match[2])
buttons.append((button_a, button_b))
elif offset == 2:
match = re.match("^Prize: X=([0-9]+), Y=([0-9]+)$", line)
assert match
prize = int(match[1]), int(match[2])
# prize = prize[0] + 10000000000000, prize[1] + 10000000000000
prizes.append(prize)
assert len(prizes) == len(buttons)
ttoks = 0
token_a, token_b = 3, 1
for arcade, prize in enumerate(prizes):
butts = buttons[arcade]
button_a, button_b = butts
print(43, prize, button_a, button_b)
p_x, p_y = prize
a_x, a_y = button_a
b_x, b_y = button_b
denom = a_x * b_y - a_y * b_x
a = (p_x * b_y - p_y * b_x) / denom
b = (a_x * p_y - a_y * p_x) / denom
if not a.is_integer() or not b.is_integer():
print(76, None)
continue
toks = int(a) * token_a + int(b) * token_b
print(76, toks)
ttoks += toks
print(ttoks)

82
2024/13/two_simpy.py Normal file
View file

@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""
Someone mentionned sympy on reddit, wanted to see what I could do with it.
"""
import re
import sys
import sympy
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
coords = tuple[int, int]
prizes: list[coords] = list()
buttons: list[tuple[coords, coords]] = list()
for li, line in enumerate(lines):
machine = li // 4
offset = li % 4
if offset == 0:
match = re.match(r"^Button A: X\+([0-9]+), Y\+([0-9]+)$", line)
assert match
button_a = int(match[1]), int(match[2])
elif offset == 1:
match = re.match(r"^Button B: X\+([0-9]+), Y\+([0-9]+)$", line)
assert match
button_b = int(match[1]), int(match[2])
buttons.append((button_a, button_b))
elif offset == 2:
match = re.match("^Prize: X=([0-9]+), Y=([0-9]+)$", line)
assert match
prize = int(match[1]), int(match[2])
# prize = prize[0] + 10000000000000, prize[1] + 10000000000000
prizes.append(prize)
assert len(prizes) == len(buttons)
sympy.init_printing()
a, b, Ax, Ay, Bx, By, Px, Py = sympy.symbols(
"a b Ax Ay Bx By Px Py", positive=True, integer=True
)
x_eq = sympy.Eq(a * Ax + b * Bx, Px)
y_eq = sympy.Eq(a * Ay + b * By, Py)
tokens = 3 * a + 1 * b
sols = sympy.solve([x_eq, y_eq], a, b, dict=True)
# In that case, should use linsolve directly (solve ain't great)
# Would allow to .subs the whole solution set at once.
ttoks = sympy.Integer(0)
for arcade, prize in enumerate(prizes):
button_a, button_b = buttons[arcade]
print(43, prize, button_a, button_b)
vars = {
Ax: button_a[0],
Ay: button_a[1],
Bx: button_b[0],
By: button_b[1],
Px: prize[0],
Py: prize[1],
}
toks = None
for sol in sols:
a_presses, b_presses = sol[a].subs(vars), sol[b].subs(vars)
if not a_presses.is_integer or not b_presses.is_integer:
continue
ntoks = tokens.subs({a: a_presses, b: b_presses})
if toks is None or ntoks < toks:
toks = ntoks
print(76, toks)
if toks is not None:
ttoks += toks
assert ttoks.is_integer
print(int(ttoks.evalf()))

88
2024/14/one.py Normal file
View file

@ -0,0 +1,88 @@
#!/usr/bin/env python3
import functools
import re
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
vec = tuple[int, int]
poss: list[vec] = list()
vits: list[vec] = list()
for line in lines:
match = re.findall(r"(-?\d+)", line)
assert match
pos = int(match[0]), int(match[1])
vit = int(match[2]), int(match[3])
poss.append(pos)
vits.append(vit)
print(poss, vits)
def print_poss(poss: list[vec]) -> None:
viz = [[0] * width for _ in range(height)]
for pos in poss:
px, py = pos
viz[py][px] += 1
for line in viz:
print("".join([(str(c) if c > 0 else ".") for c in line]))
print()
# x→ y↓
if input_file == "input":
width = 101
height = 103
else:
width = 11
height = 7
if input_file == "demo1":
secs = 5
else:
secs = 100
print_poss(poss)
for s in range(secs):
for r, vit in enumerate(vits):
px, py = poss[r]
px += vit[0]
py += vit[1]
while px >= width:
px -= width
while py >= height:
py -= height
while px < 0:
px += width
while py < 0:
py += height
poss[r] = px, py
print(s)
print_poss(poss)
half_width = width // 2
half_height = height // 2
# <<<<<|>>>>>
# <= first quadrant
quadrants = [0, 0, 0, 0]
for pos in poss:
px, py = pos
q = 0
if px == half_width or py == half_height:
continue
if px > half_width:
q += 1
if py > half_height:
q += 2
quadrants[q] += 1
print(quadrants)
print(functools.reduce(int.__mul__, quadrants))

95
2024/14/two.py Normal file
View file

@ -0,0 +1,95 @@
#!/usr/bin/env python3
import functools
import re
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
vec = tuple[int, int]
poss: list[vec] = list()
vits: list[vec] = list()
for line in lines:
match = re.findall(r"(-?\d+)", line)
assert match
pos = int(match[0]), int(match[1])
vit = int(match[2]), int(match[3])
poss.append(pos)
vits.append(vit)
print(poss, vits)
def print_poss(poss: list[vec]) -> None:
stop = True
viz = [[0] * width for _ in range(height)]
for pos in poss:
px, py = pos
if viz[py][px] > 0:
stop = False
viz[py][px] += 1
if stop:
for line in viz:
print("".join([(str(c) if c > 0 else ".") for c in line]))
input()
# x→ y↓
if input_file == "input":
width = 101
height = 103
else:
width = 11
height = 7
if input_file == "demo1":
secs = 5
else:
secs = 100
print_poss(poss)
# for s in range(secs):
s = 0
while True:
s += 1
for r, vit in enumerate(vits):
px, py = poss[r]
px += vit[0]
py += vit[1]
while px >= width:
px -= width
while py >= height:
py -= height
while px < 0:
px += width
while py < 0:
py += height
poss[r] = px, py
print(s)
print_poss(poss)
#
#
# half_width = width // 2
# half_height = height // 2
# # <<<<<|>>>>>
# # <= first quadrant
#
# quadrants = [0, 0, 0, 0]
# for pos in poss:
# px, py = pos
# q = 0
# if px == half_width or py == half_height:
# continue
# if px > half_width:
# q += 1
# if py > half_height:
# q += 2
# quadrants[q] += 1
#
# print(quadrants)
# print(functools.reduce(int.__mul__, quadrants))

96
2024/14/two_extra.py Normal file
View file

@ -0,0 +1,96 @@
#!/usr/bin/env python3
import functools
import re
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
vec = tuple[int, int]
poss: list[vec] = list()
vits: list[vec] = list()
for line in lines:
match = re.findall(r"(-?\d+)", line)
assert match
pos = int(match[0]), int(match[1])
vit = int(match[2]), int(match[3])
poss.append(pos)
vits.append(vit)
print(poss, vits)
def print_poss(poss: list[vec]) -> None:
stop = False
viz = [[0] * width for _ in range(height)]
for pos in poss:
px, py = pos
viz[py][px] += 1
for line in viz:
lin = "".join([(str(c) if c > 0 else ".") for c in line])
print(lin)
if "111111111" in lin:
stop = True
if stop:
input()
# x→ y↓
if input_file.startswith("input"):
width = 101
height = 103
else:
width = 11
height = 7
if input_file == "demo1":
secs = 5
else:
secs = 100
print_poss(poss)
# for s in range(secs):
s = 0
while True:
s += 1
for r, vit in enumerate(vits):
px, py = poss[r]
px += vit[0]
py += vit[1]
while px >= width:
px -= width
while py >= height:
py -= height
while px < 0:
px += width
while py < 0:
py += height
poss[r] = px, py
print(s)
print_poss(poss)
#
#
# half_width = width // 2
# half_height = height // 2
# # <<<<<|>>>>>
# # <= first quadrant
#
# quadrants = [0, 0, 0, 0]
# for pos in poss:
# px, py = pos
# q = 0
# if px == half_width or py == half_height:
# continue
# if px > half_width:
# q += 1
# if py > half_height:
# q += 2
# quadrants[q] += 1
#
# print(quadrants)
# print(functools.reduce(int.__mul__, quadrants))

6
2024/15/demog Normal file
View file

@ -0,0 +1,6 @@
#######
#...O..
#......
#...@..
>

75
2024/15/one.py Normal file
View file

@ -0,0 +1,75 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
coords = tuple[int, int]
pos: coords
box = list()
moves: list[coords] = list()
directions = {
"^": (-1, 0), # ^
">": (0, 1), # >
"v": (1, 0), # v
"<": (0, -1), # <
}
boxing = True
for i, line in enumerate(lines):
if not line:
boxing = False
elif boxing:
bline = list(line)
if "@" in bline:
j = bline.index("@")
pos = i, j
# bline[j] = "."
box.append(bline)
else:
for c in line:
direction = directions[c]
moves.append(direction)
def print_box() -> None:
for bline in box:
print("".join(bline))
print()
print_box()
for move in moves:
first = pos[0] + move[0], pos[1] + move[1]
last = first
possible = True
while True:
c = box[last[0]][last[1]]
# print(c)
if c == ".":
break
elif c == "#":
possible = False
break
last = last[0] + move[0], last[1] + move[1]
if possible:
if first != last:
box[last[0]][last[1]] = "O"
box[pos[0]][pos[1]] = "."
box[first[0]][first[1]] = "@"
pos = first
print(move, possible, pos, first, last)
print_box()
score = 0
for i, bline in enumerate(box):
for j, char in enumerate(bline):
if char == "O":
score += 100 * i + j
print(score)

110
2024/15/two.py Normal file
View file

@ -0,0 +1,110 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
vec = tuple[int, int]
pos: vec
box = list()
moves: list[vec] = list()
directions = {
"^": (-1, 0), # ^
">": (0, 1), # >
"v": (1, 0), # v
"<": (0, -1), # <
}
sub = {
"#": "##",
"O": "[]",
".": "..",
"@": "@.",
}
boxing = True
for i, line in enumerate(lines):
if not line:
boxing = False
elif boxing:
bline = list()
for char in line:
bline += list(sub[char])
if "@" in bline:
j = bline.index("@")
pos = i, j
# bline[j] = "."
box.append(bline)
else:
for c in line:
direction = directions[c]
moves.append(direction)
def print_box() -> None:
for bline in box:
print("".join(bline))
print()
print_box()
for move in moves:
def do(i: int, j: int) -> None | set[vec]:
ii, jj = i + move[0], j + move[1]
char = box[ii][jj]
moving = {(i, j)}
if char == "#":
return None
elif char == ".":
return moving
else:
r = do(ii, jj)
if r is None:
return None
moving |= r
if char == "[":
npos = (ii, jj + 1)
if npos not in moving:
r = do(npos[0], npos[1])
if r is None:
return None
moving |= r
elif char == "]":
npos = (ii, jj - 1)
if npos not in moving:
r = do(npos[0], npos[1])
if r is None:
return None
moving |= r
return moving
moving = do(pos[0], pos[1])
if moving is None:
continue
olc: list[str] = list()
for i, j in moving:
olc.append(box[i][j])
box[i][j] = "."
print(82, moving)
print(83, olc)
for i, j in moving:
ii, jj = i + move[0], j + move[1]
box[ii][jj] = olc.pop(0)
# box[pos[0]][pos[1]] = "."
pos = pos[0] + move[0], pos[1] + move[1]
print(move, pos)
# print_box()
score = 0
for i, bline in enumerate(box):
for j, char in enumerate(bline):
if char == "[":
score += 100 * i + j
print(score)

4
2024/16/:w Normal file
View file

@ -0,0 +1,4 @@
####
#.E#
#S##
####

23
2024/16/README.md Normal file
View file

@ -0,0 +1,23 @@
Oh boy. My first time in a while doing something with mazes.
I decided to go with a filling thing.
I realized I need to put the direction as a dimension in the table of visited scores.
So basically it would go up to the very last tile, and then be like "op, what if I went back to the start?", although I did not know that at the time.
I did exhaust Python call stack, so I increased it, again and again.
But I thought that surely, even with this dumb of an algorithm it should be able to do, so I decided to look for a bug.
I used the depth variable (initally only used to indent print statements) as my own call stack limit that would still print the maze.
I realized that even at 1000 depth, the maze was already filled, which puzzled me.
I submitted the answer... and it was correct x)
For part 2 I first implemented something that would print only one best path.
Then I realized my mistake, and then did something that would work if not for the fact that I optimized out
2 paths arriving with the same score. A <= changed to < later and it was fixed.
The optimisation didn't cost much, the "allocating a list every recursion", very much so.
Since it was taking a long time to compute I realized maybe I could do something clever with only considering crossways,
since there's quite a lot of corridors.
But I decided to instead firsrt code two2.py, which would add the position to the set of best places only when the recursion hit the best score,
which was hardcoded from part 1 (it took 48s to run alone, sooo).
I tried it on demo, got None as answer to part 1 and 0 as answer to part 2, completly overlooked that, and let it cook on the real input.
In the end, the first iteration of two.py ended up being cooked first, 11 minutes after, with the right answer.
...a win is a win I guess

15
2024/16/demog Normal file
View file

@ -0,0 +1,15 @@
################
############..E#
###########..#.#
##########..##.#
#########..###.#
########..##...#
#######..###.###
######..####...#
#####..#######.#
####..##.......#
###..###.#######
##..####.......#
#..###########.#
#S.............#
################

4
2024/16/demog0 Normal file
View file

@ -0,0 +1,4 @@
####
#.E#
#S.#
####

86
2024/16/networkx_test.py Normal file
View file

@ -0,0 +1,86 @@
#!/usr/bin/env python3
import sys
import typing
import matplotlib.pyplot as plt
import networkx as nx
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
# Parse input
g = nx.DiGraph()
for i in range(height):
for j in range(width):
char = lines[i][j]
if char == "#":
continue
for d, direction in enumerate(directions):
cur = (i, j, d)
# Start/end
if char == "E":
g.add_edge(cur, "end", weight=0)
elif char == "S" and d == 1:
g.add_edge("start", cur, weight=0)
# Rotate
g.add_edge(cur, (i, j, (d + 1) % len(directions)), weight=1000)
g.add_edge(cur, (i, j, (d - 1) % len(directions)), weight=1000)
# Advance
ii, jj = i + direction[0], j + direction[1]
if lines[ii][jj] == "#":
continue
g.add_edge(cur, (ii, jj, d), weight=1)
# Part 1
score = nx.shortest_path_length(g, "start", "end", weight="weight")
print(f"{score=}")
# Part 2
paths = nx.all_shortest_paths(g, "start", "end", weight="weight")
best_orientations = set()
for path in paths:
best_orientations |= set(path)
path_edges = list(zip(path, path[1:])) # Will be one random best path
best_places = set(bo[:2] for bo in best_orientations - {"start", "end"})
print(f"{len(best_places)=}")
# Draw graph
if len(g.nodes) > 1000:
sys.exit(0)
node_colors = ["blue" if node in best_orientations else "cyan" for node in g.nodes()]
edge_colors = ["red" if edge in path_edges else "black" for edge in g.edges()]
node_pos: dict[typing.Any, tuple[float, float]] = dict()
for node in g.nodes():
pos: tuple[float, float]
if node == "start":
pos = height - 1, 0
elif node == "end":
pos = 0, width - 1
else:
i, j, d = node
direction = directions[d]
pos = i + direction[0] / 3, j + direction[1] / 3
node_pos[node] = pos[1], pos[0] * -1
nx.draw_networkx_nodes(g, node_pos, node_color=node_colors)
nx.draw_networkx_edges(g, node_pos, edge_color=edge_colors)
# nx.draw_networkx_labels(g, node_pos)
# nx.draw_networkx_edge_labels(
# g, node_pos, edge_labels={(u, v): d["weight"] for u, v, d in g.edges(data=True)}
# )
plt.show()

135
2024/16/numpy_test.py Normal file
View file

@ -0,0 +1,135 @@
#!/usr/bin/env python3
import sys
import numpy as np
import scipy as sp
dtype = np.int32
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
start = height - 2, 1
stop = 1, width - 2
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
directions_keys = ("^", ">", "v", "<")
walls = np.zeros((height, width), dtype)
for i in range(height):
for j in range(width):
if lines[i][j] == "#":
walls[i, j] = 1
invwalls = 1 - walls
# print("Walls")
# print(walls)
# TEST 4
scores = np.zeros((height, width, len(directions)), dtype)
scores[start[0], start[1], 1] = 1
for i in range(1000):
print("Step", i)
oldscores = scores.copy()
for od, odir in enumerate(directions):
for nd, ndir in enumerate(directions):
score = scores[:, :, nd]
moved = sp.ndimage.shift(oldscores[:, :, od], ndir)
increment = 1 if nd == od else 1001
moved = (moved + increment) * (moved > 0)
moved = moved * invwalls
mask = (moved > 0) & ((score == 0) | (score > moved))
scores[:, :, nd] = (score * ~mask) + (moved * mask)
# for d, dir in enumerate(directions):
# print("Direction", directions_keys[d])
# print(scores[:, :, d])
if (scores == oldscores).all():
break
else:
print("Limit!")
end_score = min(filter(lambda d: d > 0, scores[stop])) - 1
print(f"{end_score=}")
# TEST 3
# scores = [np.zeros((height, width), dtype) for _ in directions]
# scores[1][start] = 1
#
# for i in range(100):
# print("Step", i)
# for od, odir in enumerate(directions):
# oldscore = scores[od].copy()
# for nd, ndir in enumerate(directions):
# score = scores[nd]
# moved = sp.ndimage.shift(oldscore, ndir)
# increment = 1 if nd == od else 1001
# moved = (moved + increment) * (moved > 0)
# moved = moved * invwalls
# mask = (moved > 0) & ((score == 0) | (score > moved))
# scores[nd] = (score * ~mask) + (moved * mask)
#
# final_score = None
# for d, dir in enumerate(directions):
# print("Direction", directions_keys[d])
# print(scores[d])
# end_score = scores[d][stop]
# if end_score > 0:
# if final_score is None or end_score < final_score:
# final_score = end_score
#
# if final_score:
# final_score -= 1
# print(f"{final_score=}")
# break
# else:
# print("Limit!")
# TEST 2
# score = np.zeros((height, width), dtype)
# score[start] = 1
#
# for i in range(10):
# print("Step", i)
# oldscore = score.copy()
# for nd, ndir in enumerate(directions):
# moved = sp.ndimage.shift(oldscore, ndir)
# moved = (moved + 1) * (moved > 0)
# moved = moved * invwalls
# mask = (moved > 0) & ((score == 0) | (score > moved))
# score = (score * ~mask) + (moved * mask)
# print(score)
# TEST 1
# directions = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]], dtype)
#
# visited = np.zeros((height, width), dtype)
# visited[start] = 1
#
# for i in range(1000):
# print("Step", i)
# new = sp.signal.convolve2d(visited, directions, mode="same")
# visited = (((new > 0) - walls) > 0)
# print(visited * 1)
# if visited[stop]:
# break
# else:
# print("Limit!")
# print(i)

86
2024/16/one.py Normal file
View file

@ -0,0 +1,86 @@
#!/usr/bin/env python3
import pprint
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
directions_keys = ("^", ">", "v", "<")
min_scores: list[list[list[None | int]]] = list()
for _ in range(height):
line: list[list[None | int]] = list()
for _ in range(width):
cell: list[None | int] = [None] * len(directions)
line.append(cell)
min_scores.append(line)
start = height - 2, 1
def walk(pos: vec, dir: int, score: int, depth: int = 0) -> int | None:
i, j = pos
char = lines[i][j]
if depth > 1000:
return None
# print("-" * depth, 28, pos, char, directions_keys[dir], score)
if char == "E":
return score
elif char == "#":
return None
min_score = min_scores[i][j][dir]
if min_score is not None and score >= min_score:
# print("-" * depth, f" 32 already taken {score} >= {min_score}")
return None
min_scores[i][j][dir] = score
mscore = None
for ndir, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
price = 1 if dir == ndir else 1001
nscore = walk((ii, jj), ndir, score + price, depth+1)
if nscore is None:
continue
if mscore is None or nscore < mscore:
mscore = nscore
return mscore
sys.setrecursionlimit(9000)
tot_score = walk(start, 1, 0)
# Viz
for i in range(height):
cline = ""
for j in range(width):
char = lines[i][j]
min_score = None
for d in range(len(directions)):
score = min_scores[i][j][d]
if score is None:
continue
if min_score is None or score < min_score:
char = directions_keys[d]
min_score = score
cline += char
print(cline)
print()
print(tot_score)

93
2024/16/one_opti.py Normal file
View file

@ -0,0 +1,93 @@
#!/usr/bin/env python3
import pprint
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
directions_keys = ("^", ">", "v", "<")
min_scores: list[list[list[None | int]]] = list()
for _ in range(height):
line: list[list[None | int]] = list()
for _ in range(width):
cell: list[None | int] = [None] * len(directions)
line.append(cell)
min_scores.append(line)
start = height - 2, 1
def walk(pos: vec, dir: int, score: int, depth: int = 0) -> int | None:
i, j = pos
char = lines[i][j]
if depth > 1000:
return None
# print("-" * depth, 28, pos, char, directions_keys[dir], score)
if char == "E":
return score
elif char == "#":
return None
min_score = min_scores[i][j][dir]
for d, min_score in enumerate(min_scores[i][j]):
if min_score is None:
continue
if d == dir:
if score >= min_score:
return None
else:
if score >= min_score + 1000:
return None
min_scores[i][j][dir] = score
mscore = None
for ndir, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
price = 1 if dir == ndir else 1001
nscore = walk((ii, jj), ndir, score + price, depth + 1)
if nscore is None:
continue
if mscore is None or nscore < mscore:
mscore = nscore
return mscore
sys.setrecursionlimit(3000)
tot_score = walk(start, 1, 0)
# Viz
for i in range(height):
cline = ""
for j in range(width):
char = lines[i][j]
min_score = None
for d in range(len(directions)):
score = min_scores[i][j][d]
if score is None:
continue
if min_score is None or score < min_score:
char = directions_keys[d]
min_score = score
cline += char
print(cline)
print()
print(tot_score)

27
2024/16/reddit_edge_case Normal file
View file

@ -0,0 +1,27 @@
###########################
#######################..E#
######################..#.#
#####################..##.#
####################..###.#
###################..##...#
##################..###.###
#################..####...#
################..#######.#
###############..##.......#
##############..###.#######
#############..####.......#
############..###########.#
###########..##...........#
##########..###.###########
#########..####...........#
########..###############.#
#######..##...............#
######..###.###############
#####..####...............#
####..###################.#
###..##...................#
##..###.###################
#..####...................#
#.#######################.#
#S........................#
###########################

14
2024/16/reddit_open_maze Normal file
View file

@ -0,0 +1,14 @@
####################################################
#......................................#..........E#
#......................................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.............................#
#S...................#.............................#
####################################################

105
2024/16/two.py Normal file
View file

@ -0,0 +1,105 @@
#!/usr/bin/env python3
import pprint
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
directions_keys = ("^", ">", "v", "<")
min_scores: list[list[list[None | int]]] = list()
for _ in range(height):
line: list[list[None | int]] = list()
for _ in range(width):
cell: list[None | int] = [None] * len(directions)
line.append(cell)
min_scores.append(line)
start = height - 2, 1
valid_paths: list[tuple[int, set[vec]]] = list()
def walk(pos: vec, dir: int, score: int, path: list[vec]) -> int | None:
i, j = pos
char = lines[i][j]
depth = len(path)
path = path + [pos]
if depth > 1000:
return None
# print("-" * depth, 28, pos, char, directions_keys[dir], score)
if char == "E":
valid_paths.append((score, set(path)))
return score
elif char == "#":
return None
min_score = min_scores[i][j][dir]
if min_score is not None and score > min_score:
# print("-" * depth, f" 32 already taken {score} >= {min_score}")
return None
min_scores[i][j][dir] = score
mscore = None
for ndir, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
price = 1 if dir == ndir else 1001
nscore = walk((ii, jj), ndir, score + price, path)
if nscore is None:
continue
if mscore is None or nscore < mscore:
mscore = nscore
return mscore
sys.setrecursionlimit(9000)
tot_score = walk(start, 1, 0, [])
print(76, len(valid_paths))
all_best: set[vec] = set()
for s, path in valid_paths:
if s != tot_score:
continue
print(81, "BEST")
all_best |= path
# Viz
for i in range(height):
cline = ""
for j in range(width):
char = lines[i][j]
pos = i, j
if pos in all_best:
min_score = None
for d in range(len(directions)):
score = min_scores[i][j][d]
if score is None:
continue
if min_score is None or score < min_score:
char = directions_keys[d]
min_score = score
cline += char
print(cline)
print()
print(tot_score)
print(len(all_best))

95
2024/16/two2.py Normal file
View file

@ -0,0 +1,95 @@
#!/usr/bin/env python3
import pprint
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
directions_keys = ("^", ">", "v", "<")
min_scores: list[list[list[None | int]]] = list()
for _ in range(height):
line: list[list[None | int]] = list()
for _ in range(width):
cell: list[None | int] = [None] * len(directions)
line.append(cell)
min_scores.append(line)
start = height - 2, 1
best: set[vec] = set()
def walk(pos: vec, dir: int, score: int, depth: int) -> int | None:
i, j = pos
char = lines[i][j]
if depth > 1000:
return None
# print("-" * depth, 28, pos, char, directions_keys[dir], score)
if char == "E":
if score == 90440:
return score
else:
return None
elif char == "#":
return None
min_score = min_scores[i][j][dir]
if min_score is not None and score > min_score:
# print("-" * depth, f" 32 already taken {score} >= {min_score}")
return None
min_scores[i][j][dir] = score
mscore = None
for ndir, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
price = 1 if dir == ndir else 1001
nscore = walk((ii, jj), ndir, score + price, depth + 1)
if nscore is None:
continue
best.add((ii, jj))
# if mscore is None or nscore < mscore:
# mscore = nscore
return mscore
sys.setrecursionlimit(9000)
tot_score = walk(start, 1, 0, 0)
# Viz
for i in range(height):
cline = ""
for j in range(width):
char = lines[i][j]
pos = i, j
min_score = None
for d in range(len(directions)):
score = min_scores[i][j][d]
if score is None:
continue
if min_score is None or score < min_score:
char = directions_keys[d]
min_score = score
cline += char
print(cline)
print()
print(tot_score)
print(len(best))

111
2024/16/two_opti.py Normal file
View file

@ -0,0 +1,111 @@
#!/usr/bin/env python3
import pprint
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
directions_keys = ("^", ">", "v", "<")
min_scores: list[list[list[None | int]]] = list()
for _ in range(height):
line: list[list[None | int]] = list()
for _ in range(width):
cell: list[None | int] = [None] * len(directions)
line.append(cell)
min_scores.append(line)
start = height - 2, 1
valid_paths: list[tuple[int, set[vec]]] = list()
def walk(pos: vec, dir: int, score: int, path: list[vec]) -> int | None:
i, j = pos
char = lines[i][j]
depth = len(path)
path = path + [pos]
if depth > 1000:
return None
# print("-" * depth, 28, pos, char, directions_keys[dir], score)
if char == "E":
valid_paths.append((score, set(path)))
return score
elif char == "#":
return None
min_score = min_scores[i][j][dir]
for d, min_score in enumerate(min_scores[i][j]):
if min_score is None:
continue
if d == dir:
if score > min_score:
return None
else:
if score > min_score + 1000:
return None
min_scores[i][j][dir] = score
mscore = None
for ndir, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
price = 1 if dir == ndir else 1001
nscore = walk((ii, jj), ndir, score + price, path)
if nscore is None:
continue
if mscore is None or nscore < mscore:
mscore = nscore
return mscore
sys.setrecursionlimit(9000)
tot_score = walk(start, 1, 0, [])
print(76, len(valid_paths))
all_best: set[vec] = set()
for s, path in valid_paths:
if s != tot_score:
continue
print(81, "BEST")
all_best |= path
# Viz
for i in range(height):
cline = ""
for j in range(width):
char = lines[i][j]
pos = i, j
if pos in all_best:
min_score = None
for d in range(len(directions)):
score = min_scores[i][j][d]
if score is None:
continue
if min_score is None or score < min_score:
char = directions_keys[d]
min_score = score
cline += char
print(cline)
print()
print(tot_score)
print(len(all_best))

BIN
2024/17/Notes.xopp Normal file

Binary file not shown.

4
2024/17/README.md Normal file
View file

@ -0,0 +1,4 @@
two.py and two_bf.py were written before I got the 2 stars.
First fails on demo2, seconds takes 23 years (literally).

5
2024/17/demog Normal file
View file

@ -0,0 +1,5 @@
Register A: 41644071
Register B: 0
Register C: 0
Program: 2,4,1,2,0,3,5,5,3,0

69
2024/17/one.py Normal file
View file

@ -0,0 +1,69 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
# program 3 bits numbers
# registers A, B, C: any int
# instruction 3 bits, operand 3 bits
# literal operand, combo operand: 0-3 literal, 4-6: register A,B,C
# instruction pointer starts at 0
# 0 adv: truncated division, numerator = A, denominator 2**combo operand -> A
# 1 bxl: bitwise XOR, B ^ literal operand -> B
# 2 bst: combo operand % 8 -> B
# 3 jnz: { A = 0: nothing; A != 0: literal operand -> IP } # no increase there!
# 4 bxc: bitwise XOR, B ^ C -> B # operand ignored
# 5 out: combo operand % 8 -> output
# 6 bdv: truncated division, numerator = A, denominator 2**combo operand -> B
# 7 cdv: truncated division, numerator = A, denominator 2**combo operand -> C
A = int(lines[0].split(":")[1])
B = int(lines[1].split(":")[1])
C = int(lines[2].split(":")[1])
program = [int(p) for p in lines[4].split(":")[1].split(",")]
output: list[int] = list()
ip = 0
while ip in range(len(program)):
inst = program[ip]
liop = program[ip + 1]
coop = liop
if liop == 4:
coop = A
elif liop == 5:
coop = B
elif liop == 6:
coop = C
trunc_div = A // 2**coop
if inst == 0:
A = trunc_div
elif inst == 1:
B = B ^ liop
elif inst == 2:
B = coop % 8
elif inst == 3:
if A != 0:
ip = liop
continue
elif inst == 4:
B = B ^ C
elif inst == 5:
output.append(coop % 8)
elif inst == 6:
B = trunc_div
elif inst == 7:
C = trunc_div
else:
raise NotImplementedError()
ip += 2
print(",".join([str(o) for o in output]))

202
2024/17/two.py Normal file
View file

@ -0,0 +1,202 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
# program 3 bits numbers
# registers A, B, C: any int
# instruction 3 bits, operand 3 bits
# literal operand, combo operand: 0-3 literal, 4-6: register A,B,C
# instruction pointer starts at 0
# 0 adv: truncated division, numerator = A, denominator 2**combo operand -> A
# 1 bxl: bitwise XOR, B ^ literal operand -> B
# 2 bst: combo operand % 8 -> B
# 3 jnz: { A = 0: nothing; A != 0: literal operand -> IP } # no increase there!
# 4 bxc: bitwise XOR, B ^ C -> B # operand ignored
# 5 out: combo operand % 8 -> output
# 6 bdv: truncated division, numerator = A, denominator 2**combo operand -> B
# 7 cdv: truncated division, numerator = A, denominator 2**combo operand -> C
instnames = ["adv", "bxl", "bst", "jnz", "bxc", "out", "bdv", "cdv"]
oA = int(lines[0].split(":")[1])
oB = int(lines[1].split(":")[1])
oC = int(lines[2].split(":")[1])
program = [int(p) for p in lines[4].split(":")[1].split(",")]
def test(oA: int) -> list[int]:
# print(32, "Testing", A)
ip = 0
A = oA
B = oB
C = oC
output: list[int] = list()
oi = 0
# jumped: set[tuple[int, int, int, int]] = set()
while ip in range(len(program)):
inst = program[ip]
liop = program[ip + 1]
# print(50, ip, instnames[inst], liop)
if inst == 1:
B = B ^ liop
elif inst == 3:
if A != 0:
ip = liop
# Infinite loop prevention
# state = (ip, A, B, C)
# # print(66, state, jumped)
# if state in jumped:
# print("Infinite loop!")
# return False
# jumped.add(state)
continue
elif inst == 4:
B = B ^ C
else:
coop = liop
if liop == 4:
coop = A
elif liop == 5:
coop = B
elif liop == 6:
coop = C
if inst == 2:
B = coop % 8
elif inst == 5:
ou = coop % 8
output.append(ou)
# if oi >= len(program) or program[oi] != ou:
# # if len(output) >= 6:
# # print(84, oA, output)
# return False
oi += 1
else:
trunc_div = A // 2**coop
if inst == 0:
A = trunc_div
elif inst == 6:
B = trunc_div
elif inst == 7:
C = trunc_div
else:
raise NotImplementedError()
ip += 2
print(102, oA, output, len(output))
# return oi == len(program)
return output
print(program, len(program))
for i in range(0, len(program), 2):
inst = program[i]
liop = program[i + 1]
print(106, i, instnames[inst], liop)
print()
# Bruteforce
indexes = list(range(len(program)))
def to_number(inps: list[int]) -> int:
A = 0
for i in indexes[::-1]:
A <<= 3
A += inps[i]
return A
inps = program.copy()
res = test(to_number(inps))
while res != program:
for i in indexes[::-1]:
j = 0
revi = - len(program) + i
while res[revi] != program[i]:
inps[i] = j
A = to_number(inps)
res = test(A)
j += 1
print("Hi")
# i = 3
# for j in range(8):
# inps = program.copy()
# inps[i] = j
# A = to_number(inps)
# test(A)
print()
# indexes = list(range(len(inps)))
# for i in indexes[::-1]:
# An = A & 0b111
# A <<= 3
# inp = program[i]
# A += inp ^ 2
# A <<= 3
# A += 1
# # Smartforce
# A = 0
# indexes = list(range(len(program)))
# for i in indexes[::-1]:
# An = A & 0b111
# A <<= 3
# inp = program[i]
# A += inp ^ 2
# # I thought it would be easy
# assoc: dict[tuple[int, int], int] = dict()
# for i in range(8):
# for j in range(8):
# out = test(i)[0]
# assoc[out] = i, j
#
# A = 0
# for p in program:
# A = A << 3
# A += assoc[p]
#
# A = assoc[7] << 6
res = test(A)
print("Ref", program)
print("Res", res)
print("Cor", res == program)
print(A)
# print(test(100000000))
#
# Amin = 2**(3*(len(program)-1))
# Amax = 2**(3*len(program))
# Amin = 0
# Amax = 7
# for A in range(Amin, Amax + 1):
# # if A % 65536 == 0:
# # print(91, A)
# if test(A):
# print(A)
# break
# A += 1
# print(",".join([str(o) for o in output]))

125
2024/17/two_bf.py Normal file
View file

@ -0,0 +1,125 @@
#!/usr/bin/env python3
import sys
import rich.progress
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
# program 3 bits numbers
# registers A, B, C: any int
# instruction 3 bits, operand 3 bits
# literal operand, combo operand: 0-3 literal, 4-6: register A,B,C
# instruction pointer starts at 0
# 0 adv: truncated division, numerator = A, denominator 2**combo operand -> A
# 1 bxl: bitwise XOR, B ^ literal operand -> B
# 2 bst: combo operand % 8 -> B
# 3 jnz: { A = 0: nothing; A != 0: literal operand -> IP } # no increase there!
# 4 bxc: bitwise XOR, B ^ C -> B # operand ignored
# 5 out: combo operand % 8 -> output
# 6 bdv: truncated division, numerator = A, denominator 2**combo operand -> B
# 7 cdv: truncated division, numerator = A, denominator 2**combo operand -> C
instnames = ["adv", "bxl", "bst", "jnz", "bxc", "out", "bdv", "cdv"]
oA = int(lines[0].split(":")[1])
oB = int(lines[1].split(":")[1])
oC = int(lines[2].split(":")[1])
program = [int(p) for p in lines[4].split(":")[1].split(",")]
def test(oA: int) -> bool:
# print(32, "Testing", A)
ip = 0
A = oA
B = oB
C = oC
# output: list[int] = list()
oi = 0
# jumped: set[tuple[int, int, int, int]] = set()
while ip in range(len(program)):
inst = program[ip]
liop = program[ip + 1]
# print(50, ip, instnames[inst], liop)
if inst == 1:
B = B ^ liop
elif inst == 3:
if A != 0:
ip = liop
# Infinite loop prevention
# state = (ip, A, B, C)
# # print(66, state, jumped)
# if state in jumped:
# print("Infinite loop!")
# return False
# jumped.add(state)
continue
elif inst == 4:
B = B ^ C
else:
coop = liop
if liop == 4:
coop = A
elif liop == 5:
coop = B
elif liop == 6:
coop = C
if inst == 2:
B = coop % 8
elif inst == 5:
ou = coop % 8
# output.append(ou)
if oi >= len(program) or program[oi] != ou:
# if len(output) >= 6:
# print(84, oA, output)
return False
oi += 1
else:
trunc_div = A // 2**coop
if inst == 0:
A = trunc_div
elif inst == 6:
B = trunc_div
elif inst == 7:
C = trunc_div
else:
raise NotImplementedError()
ip += 2
return oi == len(program)
# return output == program
# print(program)
# for i in range(0, len(program), 2):
# inst = program[i]
# liop = program[i + 1]
# print(106, i, instnames[inst], liop)
# print()
#
# print(test(100000000))
Amin = 2**(3*(len(program)-1))
Amax = 2**(3*len(program))
for A in rich.progress.track(range(Amin, Amax+1)):
if test(A):
print(A)
break
A += 1
# print(",".join([str(o) for o in output]))

83
2024/17/two_dfs.py Normal file
View file

@ -0,0 +1,83 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
original_B = int(lines[1].split(":")[1])
original_C = int(lines[2].split(":")[1])
program = [int(p) for p in lines[4].split(":")[1].split(",")]
def output_given_A(A: int) -> list[int]:
instruction_pointer = 0
B = original_B
C = original_C
output: list[int] = list()
while instruction_pointer in range(len(program)):
instruction = program[instruction_pointer]
literal_operator = program[instruction_pointer + 1]
if instruction == 1:
B = B ^ literal_operator
elif instruction == 3:
if A != 0:
instruction_pointer = literal_operator
continue
elif instruction == 4:
B = B ^ C
else:
combined_operator = literal_operator
if literal_operator == 4:
combined_operator = A
elif literal_operator == 5:
combined_operator = B
elif literal_operator == 6:
combined_operator = C
if instruction == 2:
B = combined_operator % 8
elif instruction == 5:
output.append(combined_operator % 8)
else:
trunc_div = A >> combined_operator
if instruction == 0:
A = trunc_div
elif instruction == 6:
B = trunc_div
else:
C = trunc_div
instruction_pointer += 2
return output
def input_to_a(input_numbers: list[int]) -> int:
A = 0
for number in reversed(input_numbers):
A = (A << 3) + number
return A
def dig(depth: int, input_numbers: list[int]) -> int | None:
input_numbers = input_numbers.copy()
depth += 1
for i in range(8):
input_numbers[-depth] = i
A = input_to_a(input_numbers)
output_numbers = output_given_A(A)
if output_numbers[-depth:] == program[-depth:]:
if depth == len(program):
return A
res = dig(depth, input_numbers)
if res is not None:
return res
return None
print(dig(0, [0] * len(program)))

78
2024/17/two_fixed.py Normal file
View file

@ -0,0 +1,78 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
original_B = int(lines[1].split(":")[1])
original_C = int(lines[2].split(":")[1])
program = [int(p) for p in lines[4].split(":")[1].split(",")]
def output_given_A(A: int) -> list[int]:
instruction_pointer = 0
B = original_B
C = original_C
output: list[int] = list()
while instruction_pointer in range(len(program)):
instruction = program[instruction_pointer]
literal_operator = program[instruction_pointer + 1]
if instruction == 1:
B = B ^ literal_operator
elif instruction == 3:
if A != 0:
instruction_pointer = literal_operator
continue
elif instruction == 4:
B = B ^ C
else:
combined_operator = literal_operator
if literal_operator == 4:
combined_operator = A
elif literal_operator == 5:
combined_operator = B
elif literal_operator == 6:
combined_operator = C
if instruction == 2:
B = combined_operator % 8
elif instruction == 5:
output.append(combined_operator % 8)
else:
trunc_div = A >> combined_operator
if instruction == 0:
A = trunc_div
elif instruction == 6:
B = trunc_div
else:
C = trunc_div
instruction_pointer += 2
return output
def input_to_a(input_numbers: list[int]) -> int:
A = 0
for number in reversed(input_numbers):
A = (A << 3) + number
return A
input_numbers: list[int] = program.copy()
output_numbers: list[int] = output_given_A(input_to_a(input_numbers))
while output_numbers != program:
for i in reversed(range(len(input_numbers))):
attempted_number = 0
while len(output_numbers) != len(program) or output_numbers[i] != program[i]:
input_numbers[i] = attempted_number
A = input_to_a(input_numbers)
output_numbers = output_given_A(A)
attempted_number += 1
print(A)

109
2024/18/one.py Normal file
View file

@ -0,0 +1,109 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
falls = list()
for line in lines:
xs, ys = line.split(",")
falls.append((int(xs), int(ys)))
if input_file.startswith("input"):
cote = 71
simu = 1024
else:
cote = 7
simu = 12
vec = tuple[int, int]
directions = [
# COMMENT NOT CORRECT BUT WHO CARES
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
exit = cote - 1, cote - 1
fallen = set(falls[:simu])
visited: set[vec] = set()
def print_grid() -> None:
for y in range(cote):
line = ""
for x in range(cote):
char = "."
pos = x, y
if pos in fallen:
char = "#"
elif pos in visited:
char = "O"
line += char
print(line)
print()
print_grid()
curs: set[vec] = {(0, 0)}
steps = 0
while exit not in curs:
print("Step", steps)
visited |= curs
ncurs: set[vec] = set()
for x, y in curs:
for direction in directions:
xx, yy = x + direction[0], y + direction[1]
npos = xx, yy
if npos in visited or npos in fallen:
continue
if x not in range(cote) or y not in range(cote):
continue
ncurs.add(npos)
curs = ncurs
steps += 1
print_grid()
print(steps)
# visited: dict[vec, int] = dict()
#
# def dig(pos: vec, steps: int) -> int | None:
# if steps > 300:
# return None
# # print(" " * steps, 55, pos)
# if pos == exit:
# return steps
# if pos in fallen:
# return None
# x, y = pos
# if x not in range(cote) or y not in range(cote):
# return None
# if pos in visited and visited[pos] < steps:
# return None
# visited[pos] = steps
#
# mini = None
# steps += 1
# for direction in directions:
# xx, yy = x + direction[0], y + direction[1]
# res = dig((xx, yy), steps)
# if res is None:
# continue
# if mini is None or res < mini:
# mini = res
# return mini
# sys.setrecursionlimit(9000)
# res = dig((0, 0), 0)
#
# print_grid()
#
# print(res)

121
2024/18/two.py Normal file
View file

@ -0,0 +1,121 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
falls = list()
for line in lines:
xs, ys = line.split(",")
falls.append((int(xs), int(ys)))
if input_file.startswith("input"):
cote = 71
simu = 1024
else:
cote = 7
simu = 12
vec = tuple[int, int]
directions = [
# COMMENT NOT CORRECT BUT WHO CARES
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
exit = cote - 1, cote - 1
visited: set[vec] = set()
fallen: set[vec] = set()
def print_grid() -> None:
for y in range(cote):
line = ""
for x in range(cote):
char = "."
pos = x, y
if pos in fallen:
char = "#"
elif pos in visited:
char = "O"
line += char
print(line)
print()
for simu in range(len(falls)):
print("Simu", simu)
fallen = set(falls[:simu])
visited = set()
curs: set[vec] = {(0, 0)}
steps = 0
found = False
while exit not in curs:
# print("Step", steps)
if not curs:
break
visited |= curs
ncurs: set[vec] = set()
for x, y in curs:
for direction in directions:
xx, yy = x + direction[0], y + direction[1]
npos = xx, yy
if npos in visited or npos in fallen:
continue
if x not in range(cote) or y not in range(cote):
continue
ncurs.add(npos)
curs = ncurs
steps += 1
else:
found = True
if not found:
break
print_grid()
print(simu)
print(falls[simu-1])
# visited: dict[vec, int] = dict()
#
# def dig(pos: vec, steps: int) -> int | None:
# if steps > 300:
# return None
# # print(" " * steps, 55, pos)
# if pos == exit:
# return steps
# if pos in fallen:
# return None
# x, y = pos
# if x not in range(cote) or y not in range(cote):
# return None
# if pos in visited and visited[pos] < steps:
# return None
# visited[pos] = steps
#
# mini = None
# steps += 1
# for direction in directions:
# xx, yy = x + direction[0], y + direction[1]
# res = dig((xx, yy), steps)
# if res is None:
# continue
# if mini is None or res < mini:
# mini = res
# return mini
# sys.setrecursionlimit(9000)
# res = dig((0, 0), 0)
#
# print_grid()
#
# print(res)

1
2024/19/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
colors

8
2024/19/colors.py Normal file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
print(fd.read().replace("w", "").replace("u", "🟦").replace("b", "").replace("r", "🟥").replace("g", "🟩"))

28
2024/19/one.py Normal file
View file

@ -0,0 +1,28 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
towels = set(map(str.strip, lines[0].split(",")))
patterns = lines[2:]
def possible(pattern: str) -> bool:
if not pattern:
return True
for towel in towels:
if pattern.startswith(towel):
if possible(pattern[len(towel):]):
return True
return False
possible_count = 0
for pattern in patterns:
if possible(pattern):
possible_count += 1
print(possible_count)

32
2024/19/two.py Normal file
View file

@ -0,0 +1,32 @@
#!/usr/bin/env python3
import sys
import functools
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
towels = set(map(str.strip, lines[0].split(",")))
patterns = lines[2:]
@functools.cache
def possible(pattern: str) -> int:
if not pattern:
return 1
possible_count = 0
for towel in towels:
if pattern.startswith(towel):
possible_count += possible(pattern[len(towel) :])
return possible_count
possible_count = 0
for pattern in patterns:
res = possible(pattern)
print(27, pattern, res)
possible_count += res
print(possible_count)

26
2024/2/one.py Normal file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
safe_num = 0
for line in lines:
report = [int(level) for level in line.split(" ")]
acc = sorted(report)
dec = acc[::-1]
if report != acc and report != dec:
continue
for i in range(len(report)-1):
diff = abs(report[i] - report[i+1])
if diff < 1 or diff > 3:
break
else:
safe_num += 1
print(safe_num)

37
2024/2/two.py Normal file
View file

@ -0,0 +1,37 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
def is_safe(report) -> bool:
acc = sorted(report)
dec = acc[::-1]
if report != acc and report != dec:
return False
for i in range(len(report) - 1):
diff = abs(report[i] - report[i + 1])
if diff < 1 or diff > 3:
return False
else:
return True
safe_num = 0
for line in lines:
report = [int(level) for level in line.split(" ")]
possible_reports = [report]
for i in range(len(report)):
rep = report.copy()
rep.pop(i)
possible_reports.append(rep)
for rep in possible_reports:
if is_safe(rep):
safe_num += 1
break
print(safe_num)

2
2024/20/README.md Normal file
View file

@ -0,0 +1,2 @@
Reading comprehension got me on the second part,
a one byte change helped 🙃

4
2024/20/demog Normal file
View file

@ -0,0 +1,4 @@
#####
#S#E#
#...#
#####

32
2024/20/demog2 Normal file
View file

@ -0,0 +1,32 @@
#############################################################################################
#S#........................................................................................E#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#.#########################################################################################.#
#...........................................................................................#
#############################################################################################

5
2024/20/demog3 Normal file
View file

@ -0,0 +1,5 @@
######
#...E#
#S####
#....#
######

129
2024/20/one.py Normal file
View file

@ -0,0 +1,129 @@
#!/usr/bin/env python3
import collections
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
Cheat = tuple[vec, int]
cheats: set[Cheat] = set()
for i, line in enumerate(lines):
if "S" in line:
j = line.index("S")
start = i, j
if i in range(1, height - 1):
for j in range(1, width - 1):
char = lines[i][j]
if char != "#":
continue
for d, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
if ii not in range(1, height - 1) or jj not in range(1, width - 1):
continue
cchar = lines[ii][jj]
if cchar == "#":
continue
cheats.add(((i, j), d))
def print_grid(visited: list[list[int | None]], cheat: Cheat) -> None:
for i in range(height):
line = ""
for j in range(width):
char = lines[i][j]
if visited[i][j] is not None:
char = "O"
if cheat[0] == (i, j):
char = "X"
line += char
print(line)
print()
def time(cheat: Cheat) -> int | None:
visited: list[list[int | None]] = list()
for _ in range(height):
visited.append([None] * width)
stack: set[vec] = {start}
for s in range(1, 10000):
nstack: set[vec] = set()
for pos in stack:
i, j = pos
for d, direction in enumerate(directions):
if (i, j) == cheat[0]:
if d != cheat[1]:
continue
ii, jj = i + direction[0], j + direction[1]
cchar = lines[ii][jj]
if cchar == "#" and cheat != ((ii, jj), d):
continue
elif cchar == "E":
# if s == 84 - 8:
# print_grid(visited, cheat)
return s
previs = visited[ii][jj]
if previs is not None and previs < s:
continue
visited[ii][jj] = s
nstack.add((ii, jj))
stack = nstack
# print("Second", s)
# print_grid(visited)
else:
return None
canon_saves: collections.Counter[int] = collections.Counter()
for k, v in (
{
2: 14,
4: 14,
6: 2,
8: 4,
10: 2,
12: 3,
20: 1,
36: 1,
38: 1,
40: 1,
64: 1,
}
).items():
canon_saves[k] = v
normal = time(((0, 0), 0))
assert normal
saves: collections.Counter[int] = collections.Counter()
saves_mo100 = 0
print(f"{normal=}")
print(f"{len(cheats)=}")
for c, cheat in enumerate(cheats):
print("Cheat", c, "/", len(cheats))
ntime = time(cheat)
assert ntime
diff = normal - ntime
saves[diff] += 1
if diff >= 100:
saves_mo100 += 1
del saves[0]
print(f"{saves=}")
print(f"{canon_saves=}")
print(f"{(saves == canon_saves)=}")
print(saves_mo100)
# 1282: too low

41
2024/20/reddit_part3 Normal file
View file

@ -0,0 +1,41 @@
#########################################
#...#.............#.....#.....#.....#...#
###.#.###.#########.###.###.#####.###.#.#
#...#...#.#.#.....#...#...#.#.........#.#
#..##.###.#.#####.#####.#.#.#.#####.#.#.#
#.......#.....#.#.....#.#...#...#...#.#.#
#.###########.#.#.####.####.#.###########
#.#.#...#...#.....#.................#...#
#.#.#.#.#.#.###.#.#.###.#########.#####.#
#.....#...#.....#...#.........#...#.#.#.#
#####.#####.#####.#.#.#.#.#######.#.#.#.#
#.....#.........#.#.#...#...#...#.#...#.#
#.#########.#######.#####.#.##..###.###.#
#...#.......#.....#.#...#.#...#.....#...#
#.###.###########.#.###.#.#.###.#######.#
#.#.#.............#.....#.#...#...#.....#
###.#.#####.#####.#.###.#.#####.#####.###
#...#.#.........#.#...#...#...#.#.....#.#
###.###.#.#########.#####.###.#.#.#.#.#.#
#S#.#...#.#.....#.....#.........#.#.#..E#
#.#.#.#########.#.#########.#.###.#####.#
#.....#.........#...#.#...#.#.....#...#.#
###.#####..##.#.#####.#.###.#####.###.###
#.#.#...#.#.#.#.#...#...#...#.........#.#
#.#.###.###.#.#.#.#####.####.##.#.#####.#
#.#.#.#.#.#...#.........#.#...#.#.#...#.#
#.#.#.#.#.#####.###.#.#.#.###.#.###.###.#
#...#.......#...#...#.#.#.........#.#...#
#######.#####.#####.###.#.#.#####.#.###.#
#.............#.....#.#.#.#.....#.......#
###############.#####.#.#########.#.#.###
#.....#...#.#.........#.#...#...#.#.#.#.#
#.#.#.#.#.#.###.#########.###.###.#####.#
#.#.#.#.#...........#.#.............#...#
###.#.#.###.#######.#.#.#.###.###.#.#.###
#...#...#...#.#...#.#...#...#.#.#.#.#...#
###.#.#######.#.#.#.###.#####.#..##.#.###
#.#.#...#.....#.#.#.......#.#.#...#.....#
#.#.#####.###.#.#.#.#.#####.#####.###.#.#
#.....#.....#.......#.............#...#.#
#########################################

41
2024/20/reddit_part3g Normal file
View file

@ -0,0 +1,41 @@
#########################################
#...#.............#.....#.....#.....#...#
###.#.###.#########.###.###.#####.###.#.#
#...#...#.#.#.....#...#...#.#.........#.#
#..##.###.#.#####.#####.#.#.#.#####.#.#.#
#.......#.....#.#.....#.#...#...#...#.#.#
#.###########.#.#.####.####.#.###########
#.#.#...#...#.....#.................#...#
#.#.#.#.#.#.###.#.#.###.#########.#####.#
#.....#...#.....#...#.........#...#.#.#.#
#####.#####.#####.#.#.#.#.#######.#.#.#.#
#.....#.........#.#.#...#...#...#.#...#.#
#.#########.#######.#####.#.##..###.###.#
#...#.......#.....#.#...#.#...#.....#...#
#.###.###########.#.###.#.#.###.#######.#
#.#.#.............#.....#.#...#...#.....#
###.#.#####.#####.#.###.#.#####.#####.###
#...#.#.........#.#...#...#...#.#.....#.#
###.###.#.#########.#####.###.#.#.#.#.#.#
#S#.#...#.#.....#.....#.........#.#.#..E#
#.#.#.#########.#.#########.#.###.#####.#
#.....#.........#...#.#...#.#.....#...#.#
###.#####..##.#.#####.#.###.#####.###.###
#.#.#...#.#.#.#.#...#...#...#.........#.#
#.#.###.###.#.#.#.#####.####.##.#.#####.#
#.#.#.#.#.#...#.........#.#...#.#.#...#.#
#.#.#.#.#.#####.###.#.#.#.###.#.###.###.#
#...#.......#...#...#.#.#.........#.#...#
#######.#####.#####.###.#.#.#####.#.###.#
#.............#.....#.#.#.#.....#.......#
#####################.#.#########.#.#.###
#.....#...#.#.........#.#...#...#.#.#.#.#
#.#.#.#.#.#.###.#########.###.###.#####.#
#.#.#.#.#...........#.#.............#...#
###.#.#.###.#######.#.#.#.###.###.#.#.###
#...#...#...#.#...#.#...#...#.#.#.#.#...#
###.#.#######.#.#.#.###.#####.#..##.#.###
#.#.#...#.....#.#.#.......#.#.#...#.....#
#.#.#####.###.#.#.#.#.#####.#####.###.#.#
#.....#.....#.......#.............#...#.#
#########################################

153
2024/20/two.py Normal file
View file

@ -0,0 +1,153 @@
#!/usr/bin/env python3
import collections
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
# Adjust parameters for part/file
part = int(sys.argv[2])
assert part in (1, 2)
if input_file.startswith("input"):
minic = 100
else:
if part == 1:
minic = 1
else:
minic = 50
skips = 1 if part == 1 else 20
canon: collections.Counter[int] = collections.Counter()
demo = {}
if input_file == "demo":
if skips == 1:
demo = {2: 14, 4: 14, 6: 2, 8: 4, 10: 2} | (
{12: 3, 20: 1, 36: 1, 38: 1, 40: 1, 64: 1}
)
elif skips == 20:
demo = {50: 32, 52: 31, 54: 29, 56: 39, 58: 25, 60: 23} | (
{62: 20, 64: 19, 66: 12, 68: 14, 70: 12, 72: 22, 74: 4, 76: 3}
)
for k, v in demo.items():
canon[k] = v
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
# Find start position
for i, line in enumerate(lines):
if "S" in line:
j = line.index("S")
start = i, j
# Visit normally
normal = None
visited: list[list[int | None]] = list()
for _ in range(height):
visited.append([None] * width)
visited[start[0]][start[1]] = 0
stack: set[vec] = {start}
s = 0
while stack:
s += 1
nstack: set[vec] = set()
for pos in stack:
i, j = pos
for d, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
previs = visited[ii][jj]
if previs is not None and previs < s:
continue
visited[ii][jj] = s
cchar = lines[ii][jj]
if cchar == "#":
continue
elif cchar == "E":
if normal is None:
normal = s
nstack.add((ii, jj))
stack = nstack
assert normal
# Print
for i in range(height):
line = ""
for j in range(width):
char = lines[i][j]
if visited[i][j] is not None:
if char == "#":
char = "@"
else:
char = "O"
line += char
print(line)
print()
# Find cheats
saves: collections.Counter[int] = collections.Counter()
for i in range(1, height - 1):
if height > 100:
print(103, i, "/", height)
for j in range(1, width - 1):
char = lines[i][j]
if char == "#":
continue
ovis = visited[i][j]
if ovis is None:
continue
if ovis >= normal:
continue
# for di in range(-skips, skips):
# ii = i + di
# G
for ii in range(1, height - 1):
for jj in range(1, width - 1):
manh = abs(i - ii) + abs(j - jj)
if manh > skips:
continue
cchar = lines[ii][jj]
if cchar == "#":
continue
nvis = visited[ii][jj]
if nvis is None:
continue
orem = normal - ovis
nrem = abs(normal - nvis) + manh
save = orem - nrem
if save < minic:
continue
saves[save] += 1
print(f"{normal=}")
print(f"{dict(sorted(saves.items()))=}")
if demo:
print(f"{dict(sorted(canon.items()))=}")
diff = canon.copy()
diff.subtract(saves)
print(f"{dict(sorted(diff.items()))=}")
print(f"{(saves == canon)=}")
print(f"{saves.total()=}")
print(f"{canon.total()=}")
difft = 0
for v in diff.values():
difft += abs(v)
print(f"{difft=}")
print(saves.total())
# 1119834 too high
# 982425 correct!

212
2024/20/two_correct.py Normal file
View file

@ -0,0 +1,212 @@
#!/usr/bin/env python3
import collections
import colorsys
import sys
import rich.console
import rich.text
import rich.progress
console = rich.console.Console()
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
# Adjust parameters for part/file
part = int(sys.argv[2])
assert part in (1, 2)
if input_file.startswith("input"):
minic = 100
elif input_file.startswith("reddit_part3"):
minic = 30
else:
if part == 1:
minic = 1
else:
minic = 50
skips = 2 if part == 1 else 20
canon: collections.Counter[int] = collections.Counter()
demo = {}
if input_file == "demo":
if part == 1:
demo = {2: 14, 4: 14, 6: 2, 8: 4, 10: 2} | (
{12: 3, 20: 1, 36: 1, 38: 1, 40: 1, 64: 1}
)
elif part == 2:
demo = {50: 32, 52: 31, 54: 29, 56: 39, 58: 25, 60: 23} | (
{62: 20, 64: 19, 66: 12, 68: 14, 70: 12, 72: 22, 74: 4, 76: 3}
)
for k, v in demo.items():
canon[k] = v
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
# Find start position
for i, line in enumerate(lines):
if "S" in line:
j = line.index("S")
start = i, j
if "E" in line:
j = line.index("E")
stop = i, j
# Visit forward
normal = None
forward: list[list[int | None]] = list()
for _ in range(height):
forward.append([None] * width)
forward[start[0]][start[1]] = 0
stack: set[vec] = {start}
s = 0
while stack:
s += 1
nstack: set[vec] = set()
for pos in stack:
i, j = pos
for d, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
cchar = lines[ii][jj]
if cchar == "#":
continue
previs = forward[ii][jj]
if previs is not None and previs < s:
continue
forward[ii][jj] = s
if cchar == "E":
if normal is None:
normal = s
nstack.add((ii, jj))
stack = nstack
assert normal
# Visit backwards
backward: list[list[int | None]] = list()
for _ in range(height):
backward.append([None] * width)
backward[stop[0]][stop[1]] = 0
stack = {stop}
s = 0
while stack:
s += 1
nstack = set()
for pos in stack:
i, j = pos
for d, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
cchar = lines[ii][jj]
if cchar == "#":
continue
previs = backward[ii][jj]
if previs is not None and previs < s:
continue
backward[ii][jj] = s
if cchar == "E":
assert s == normal
nstack.add((ii, jj))
stack = nstack
# Print
def perc2color(perc: float) -> str:
rgb = colorsys.hsv_to_rgb(perc, 1.0, 1.0)
r, g, b = tuple(round(c * 255) for c in rgb)
return f"rgb({r},{g},{b})"
text = rich.text.Text()
for i in range(height):
for j in range(width):
fg = "white"
bg = "black"
char = lines[i][j]
forw = forward[i][j]
if char == ".":
if forw is not None:
fg = perc2color(forw / normal)
char = str(forw % 10)
bckw = backward[i][j]
if bckw is not None:
bg = perc2color(bckw / normal)
if char == "#":
char = ""
text.append(char, style=f"{fg} on {bg}")
text.append("\n")
console.print(text)
# Find cheats
saves: collections.Counter[int] = collections.Counter()
for i in rich.progress.track(range(1, height - 1), description="Finding cheats"):
for j in range(1, width - 1):
char = lines[i][j]
if char == "#":
continue
ovis = forward[i][j]
if ovis is None:
continue
if ovis >= normal:
continue
min_i = max(1, i - skips)
max_i = min(height - 1, i + skips)
for ii in range(min_i, max_i + 1):
rem = skips - abs(ii - i)
min_j = max(1, j - rem)
max_j = min(width - 1, j + rem)
for jj in range(min_j, max_j + 1):
manh = abs(i - ii) + abs(j - jj)
if manh > skips:
continue
cchar = lines[ii][jj]
if cchar == "#":
continue
nvis = backward[ii][jj]
if nvis is None:
continue
orem = normal - ovis
nrem = nvis + manh
save = orem - nrem
if save < minic:
continue
saves[save] += 1
log = console.log
log(f"{normal=}")
log(f"{dict(sorted(saves.items()))=}")
if demo:
log(f"{dict(sorted(canon.items()))=}")
diff = canon.copy()
diff.subtract(saves)
log(f"{dict(sorted(diff.items()))=}")
log(f"{(saves == canon)=}")
log(f"{saves.total()=}")
log(f"{canon.total()=}")
difft = 0
for v in diff.values():
difft += abs(v)
log(f"{difft=}")
print(saves.total())
# 1119834 too high
# 982425 correct!

172
2024/20/two_fast.py Normal file
View file

@ -0,0 +1,172 @@
#!/usr/bin/env python3
import collections
import colorsys
import sys
import rich.console
console = rich.console.Console()
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
# Adjust parameters for part/file
part = int(sys.argv[2])
assert part in (1, 2)
if input_file.startswith("input"):
minic = 100
elif input_file.startswith("reddit_part3"):
minic = 30
else:
if part == 1:
minic = 1
else:
minic = 50
skips = 2 if part == 1 else 20
canon: collections.Counter[int] = collections.Counter()
demo = {}
if input_file == "demo":
if part == 1:
demo = {2: 14, 4: 14, 6: 2, 8: 4, 10: 2} | (
{12: 3, 20: 1, 36: 1, 38: 1, 40: 1, 64: 1}
)
elif part == 2:
demo = {50: 32, 52: 31, 54: 29, 56: 39, 58: 25, 60: 23} | (
{62: 20, 64: 19, 66: 12, 68: 14, 70: 12, 72: 22, 74: 4, 76: 3}
)
for k, v in demo.items():
canon[k] = v
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
# Find start position
for i, line in enumerate(lines):
if "S" in line:
j = line.index("S")
start = i, j
# Visit normally
normal = None
visited: list[list[int | None]] = list()
for _ in range(height):
visited.append([None] * width)
visited[start[0]][start[1]] = 0
stack: set[vec] = {start}
s = 0
while stack:
s += 1
nstack: set[vec] = set()
for pos in stack:
i, j = pos
for d, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
cchar = lines[ii][jj]
if cchar == "#":
continue
previs = visited[ii][jj]
if previs is not None and previs < s:
continue
visited[ii][jj] = s
if cchar == "E":
if normal is None:
normal = s
nstack.add((ii, jj))
stack = nstack
assert normal
# Print
for i in range(height):
line = ""
for j in range(width):
char = lines[i][j]
vis = visited[i][j]
if (i, j) == (19, 1):
char = "[bold red on black]@"
elif (i, j) == (15, 1):
char = "[bold red on black]a"
elif vis is not None and char == ".":
hue = vis / normal
rgb = colorsys.hsv_to_rgb(hue, 1.0, 1.0)
r, g, b = tuple(round(c * 255) for c in rgb)
char = f"[on rgb({r},{g},{b})]{vis % 10}"
elif char == "#":
char = "[white]█"
else:
char = f"[bold green on black]{char}"
line += char
console.print(line)
print()
# Find cheats
saves: collections.Counter[int] = collections.Counter()
for i in range(1, height - 1):
if height > 100:
print(103, i, "/", height-2)
for j in range(1, width - 1):
char = lines[i][j]
if char == "#":
continue
ovis = visited[i][j]
if ovis is None:
continue
if ovis >= normal:
continue
min_i = max(1, i-skips)
max_i = min(height-1, i+skips)
for ii in range(min_i, max_i+1):
rem = skips - abs(ii - i)
min_j = max(1, j-rem)
max_j = min(width-1, j+rem)
for jj in range(min_j, max_j+1):
manh = abs(i - ii) + abs(j - jj)
if manh > skips:
continue
cchar = lines[ii][jj]
if cchar == "#":
continue
nvis = visited[ii][jj]
if nvis is None:
continue
orem = normal - ovis
# Works if there's space after the E, but catches unrelated paths
nrem = abs(normal - nvis) + manh
save = orem - nrem
if save < minic:
continue
saves[save] += 1
print(f"{normal=}")
print(f"{dict(sorted(saves.items()))=}")
if demo:
print(f"{dict(sorted(canon.items()))=}")
diff = canon.copy()
diff.subtract(saves)
print(f"{dict(sorted(diff.items()))=}")
print(f"{(saves == canon)=}")
print(f"{saves.total()=}")
print(f"{canon.total()=}")
difft = 0
for v in diff.values():
difft += abs(v)
print(f"{difft=}")
print(saves.total())
# 1119834 too high
# 982425 correct!

BIN
2024/21/Notes.xopp Normal file

Binary file not shown.

BIN
2024/21/Spreadsheet.ods Normal file

Binary file not shown.

1
2024/21/demog Normal file
View file

@ -0,0 +1 @@
3A

98
2024/21/one.py Normal file
View file

@ -0,0 +1,98 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
numeric_keypad = ["789", "456", "123", " 0A"]
directional_keypad = [" ^A", "<v>"]
vec = tuple[int, int]
directions = {
"^": (-1, 0), # ^ North
">": (0, 1), # > East
"v": (1, 0), # v South
"<": (0, -1), # < West
}
directional_keypad_buttons = tuple(directions.keys()) + ("A",)
complexity = int(sys.argv[2])
keypads = [directional_keypad] * complexity + [numeric_keypad]
def but_pos(but: str, keypad: list[str]) -> vec:
for i, line in enumerate(keypad):
if but in line:
return i, line.index(but)
raise IndexError("No such button")
def in_bounds(i: int, j: int, keypad: list[str]) -> bool:
if j not in range(3) or i not in range(len(keypad)):
return False
return keypad[i][j] != " "
last_but = "A"
all_a = [but_pos("A", keypad) for keypad in keypads]
score = 0
for code in lines:
print("Code", code)
topresses = 0
for desir_but in code:
print("Button", desir_but)
all_a[-1] = but_pos(last_but, keypads[-1])
start_poss = tuple(all_a)
all_a[-1] = but_pos(desir_but, keypads[-1])
desir_poss = tuple(all_a)
stack = {start_poss}
seen = set()
presses = 0
while desir_poss not in stack:
# print("Press", presses, stack)
presses += 1
nstack = set()
for poss in stack:
for but in directional_keypad_buttons:
# Find which keypad this will move
k = 0
while but == "A" and k < len(keypads) - 1:
i, j = poss[k]
but = keypads[k][i][j]
k += 1
# Do not press the final keypad
if k == len(keypads) - 1 and but == "A":
continue
# Move
direction = directions[but]
i, j = poss[k]
ii, jj = i + direction[0], j + direction[1]
if not in_bounds(ii, jj, keypads[k]):
continue
# Ensure we haven't been in this state before
state = poss[:k] + ((ii, jj),) + poss[k + 1 :]
if state in seen:
continue
seen.add(state)
# print(" Kept", state)
nstack.add(state)
stack = nstack
topresses += presses + 1
last_but = desir_but
numpart = int("0" + code.replace("A", ""))
print(f"{topresses=} * {numpart=}")
score += topresses * numpart
print(score)

97
2024/21/onet.py Normal file
View file

@ -0,0 +1,97 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
numeric_keypad = ["789", "456", "123", " 0A"]
directional_keypad = [" ^A", "<v>"]
vec = tuple[int, int]
directions = {
"^": (-1, 0), # ^ North
">": (0, 1), # > East
"v": (1, 0), # v South
"<": (0, -1), # < West
}
directional_keypad_buttons = tuple(directions.keys()) + ("A",)
complexity = int(sys.argv[2])
keypads = [directional_keypad] * complexity + [numeric_keypad]
def but_pos(but: str, keypad: list[str]) -> vec:
for i, line in enumerate(keypad):
if but in line:
return i, line.index(but)
raise IndexError("No such button")
def in_bounds(i: int, j: int, keypad: list[str]) -> bool:
if j not in range(3) or i not in range(len(keypad)):
return False
return keypad[i][j] != " "
last_but = "A"
all_a = [but_pos("A", keypad) for keypad in keypads]
score = 0
for code in lines:
# print("Code", code)
topresses = 0
for desir_but in code:
# print("Button", desir_but)
all_a[-1] = but_pos(last_but, keypads[-1])
start_poss = tuple(all_a)
all_a[-1] = but_pos(desir_but, keypads[-1])
if len(keypads) > 1:
all_a[-2] = but_pos("^", keypads[-2])
desir_poss = tuple(all_a)
stack = {start_poss}
seen = set()
presses = 0
while desir_poss not in stack:
# print("Press", presses, stack)
presses += 1
nstack = set()
for poss in stack:
for but in directional_keypad_buttons:
# Find which keypad this will move
k = 0
while but == "A" and k < len(keypads) - 1:
i, j = poss[k]
but = keypads[k][i][j]
k += 1
# Do not press the final keypad
if k == len(keypads) - 1 and but == "A":
continue
# Move
direction = directions[but]
i, j = poss[k]
ii, jj = i + direction[0], j + direction[1]
if not in_bounds(ii, jj, keypads[k]):
continue
# Ensure we haven't been in this state before
state = poss[:k] + ((ii, jj),) + poss[k + 1 :]
if state in seen:
continue
seen.add(state)
# print(" Kept", state)
nstack.add(state)
stack = nstack
topresses += presses + 0
last_but = desir_but
score += topresses
print(score)

281
2024/21/two.py Normal file
View file

@ -0,0 +1,281 @@
#!/usr/bin/env python3
import sys
import typing
import functools
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
numeric_keypad = ["789", "456", "123", " 0A"]
directional_keypad = [" ^A", "<v>"]
vec = tuple[int, int]
directions = {
"^": (-1, 0), # ^ North
">": (0, 1), # > East
"v": (1, 0), # v South
"<": (0, -1), # < West
}
directional_keypad_buttons = tuple(directions.keys()) + ("A",)
complexity = int(sys.argv[2])
keypads = [numeric_keypad] + ([directional_keypad] * complexity)
def but_pos(but: str, keypad: list[str]) -> vec:
for i, line in enumerate(keypad):
if but in line:
return i, line.index(but)
raise IndexError(f"No such button: {but} in {keypad}")
def in_bounds(i: int, j: int, keypad: list[str]) -> bool:
if j not in range(3) or i not in range(len(keypad)):
return False
return keypad[i][j] != " "
# FIFTH TRY
# Using 2 as a base
@functools.cache
def press(buts: str, depth: int) -> int:
if depth == len(keypads):
return len(buts)
keypad = keypads[depth]
i, j = but_pos("A", keypad)
nums = 0
for but in buts:
nnbuts = ""
ni, nj = but_pos(but, keypad)
bounded = True
while nj < j:
nnbuts += "<"
j -= 1
bounded &= in_bounds(i, j, keypad)
while ni > i:
nnbuts += "v"
i += 1
bounded &= in_bounds(i, j, keypad)
while ni < i:
nnbuts += "^"
i -= 1
bounded &= in_bounds(i, j, keypad)
while nj > j:
nnbuts += ">"
j += 1
bounded &= in_bounds(i, j, keypad)
if not bounded:
nnbuts = nnbuts[::-1]
nnbuts += "A"
nums += press(nnbuts, depth + 1)
return nums
score = 0
for code in lines:
print("Code", code)
topresses = press(code, 0)
numpart = int("0" + code.replace("A", ""))
print(f"{topresses=} * {numpart=}")
score += topresses * numpart
print(score)
# FOURTH TRY
# def press4(buts: str) -> int:
# poss = [list(but_pos("A")) for keypad in keypads]
# indexes = [0 for _ in keypads]
# combis = ["" for _ in keypads]
# combis[0] = buts
# while indexes[0] != len(buts):
# for k in len(keypads):
# pass
# return 0
#
#
# score = 0
# for code in lines:
# print("Code", code)
#
# topresses = press4(code)
#
# numpart = int("0" + code.replace("A", ""))
# print(f"{topresses=} * {numpart=}")
# score += topresses * numpart
#
# print(score)
sys.exit(0)
# THIRD TRY
def press3(buts: str, depth: int) -> typing.Generator[str, None, None]:
if depth >= len(keypads):
yield from buts
return
keypad = keypads[::-1][depth]
but_poss: dict[str, vec] = dict()
for i, line in enumerate(keypad):
for j, but in enumerate(line):
but_poss[but] = i, j
i, j = but_poss["A"]
ai, ij = but_poss[" "]
for but in press3(buts, depth + 1):
nnbuts = ""
ni, nj = but_poss[but]
bounded = True
if nj < j:
nnbuts += "<" * (j - nj)
j = nj
bounded &= (ai, ij) != (i, j)
if ni > i:
nnbuts += "v" * (ni - i)
i = ni
bounded &= (ai, ij) != (i, j)
if ni < i:
nnbuts += "^" * (i - ni)
i = ni
bounded &= (ai, ij) != (i, j)
if nj > j:
nnbuts += ">" * (nj - j)
j = nj
if not bounded:
nnbuts = nnbuts[::-1]
nnbuts += "A"
yield from nnbuts
score = 0
for code in lines:
print("Code", code)
topresses = 0
for _ in press3(code, 0):
topresses += 1
numpart = int("0" + code.replace("A", ""))
print(f"{topresses=} * {numpart=}")
score += topresses * numpart
print(score)
# SECOND TRY
# # Shouldn't move over the bounds, repeat movements when possible, also use movements
# # that start further to A then closer to A, because we're going to press A after anyways
# moves = {
# "AA": "",
# "A^": "<",
# "A>": "v",
# "Av": "<v", # sort
# "A<": "v<<", # hole avoid
# "^A": ">",
# "^^": "",
# "^>": "v>", # sort
# "^v": "v",
# "^<": "v<", # hole avoid
# ">A": "^",
# ">^": "<^", # sort
# ">>": "",
# ">v": "<",
# "><": "<<",
# "vA": "^>", # symetric. but lower layer sort!
# "v^": "^",
# "v>": ">",
# "vv": "",
# "v<": "<",
# "<A": ">>^", # hole avoid
# "<^": ">^", # hole avoid
# "<>": ">>",
# "<v": ">",
# "<<": "",
# }
#
#
# def press(buts: str, depth: int) -> str:
# if depth == len(keypads):
# return buts
# keypad = keypads[depth]
# numerical = keypad == numeric_keypad
# prev_but = "A"
# i, j = but_pos(prev_but, keypad)
# nbuts = ""
# for but in buts:
# if numerical:
# nnbuts = ""
# ni, nj = but_pos(but, keypad)
# # right before down
# # up before left
# bounded = True
# while nj < j:
# nnbuts += "<"
# j -= 1
# bounded &= in_bounds(i, j, keypad)
# while ni > i:
# nnbuts += "v"
# i += 1
# bounded &= in_bounds(i, j, keypad)
# while ni < i:
# nnbuts += "^"
# i -= 1
# bounded &= in_bounds(i, j, keypad)
# while nj > j:
# nnbuts += ">"
# j += 1
# bounded &= in_bounds(i, j, keypad)
# if not bounded:
# nnbuts = nnbuts[::-1]
# nbuts += nnbuts
# else:
# move = prev_but + but
# nbuts += moves[move]
# nbuts += "A"
# prev_but = but
# return press(nbuts, depth + 1)
#
#
# score = 0
# for code in lines:
# print("Code", code)
# presses = press(code, 0)
# print(f"{presses=}")
# topresses = len(presses)
#
# numpart = int("0" + code.replace("A", ""))
# print(f"{topresses=} * {numpart=}")
# score += topresses * numpart
#
# print(score)
# FIRST TRY
# keys = {
# "A": "A",
# "^": "<A" + ">",
# ">": "VA" + "^",
# "V": "V<A" + ">^",
# "<": "V<<A" + ">>^",
# }
#
# def press(but: str, depth: int) -> str:
# if depth <= 0:
# return but
# depth -= 1
# return "".join(press(b, depth) for b in keys[but])
#
#
# res = press("^", complexity)
# # res += press("A", complexity)
# print(len(res), res)

64
2024/21/two_fast.py Normal file
View file

@ -0,0 +1,64 @@
#!/usr/bin/env python3
import functools
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
numeric_keypad = ["789", "456", "123", " 0A"]
directional_keypad = [" ^A", "<v>"]
vec = tuple[int, int]
complexity = int(sys.argv[2])
keypads = [numeric_keypad] + ([directional_keypad] * complexity)
@functools.cache
def press(buts: str, depth: int) -> int:
if depth == len(keypads):
return len(buts)
keypad = keypads[depth]
but_poss: dict[str, vec] = dict()
for i, line in enumerate(keypad):
for j, but in enumerate(line):
but_poss[but] = i, j
i, j = but_poss["A"]
ai, aj = but_poss[" "]
depth += 1
nums = 0
for but in buts:
nnbuts = ""
ni, nj = but_poss[but]
crossing_gap = ((i == ai) and (nj == aj)) or ((ni == ai) and (j == aj))
if nj < j:
nnbuts += "<" * (j - nj)
if ni < i:
nnbuts += "^" * (i - ni)
elif ni > i:
nnbuts += "v" * (ni - i)
if nj > j:
nnbuts += ">" * (nj - j)
i, j = ni, nj
if crossing_gap:
nnbuts = nnbuts[::-1]
nnbuts += "A"
nums += press(nnbuts, depth)
return nums
score = 0
for code in lines:
print("Code", code)
topresses = press(code, 0)
numpart = int("0" + code.replace("A", ""))
print(f"{topresses=} * {numpart=}")
score += topresses * numpart
print(score)

20
2024/22/README.md Normal file
View file

@ -0,0 +1,20 @@
Struggled on the fact that "rounding *down* to the **nearest** integer" doesn't mean `round()` it means `floor()` for part1.
Got the idea in the back of my mind this is the thing that could be heavily paralellized.
For part2 I found a solution that would take 12h in brute force.
Removing a list creation I got it down to 3 hours (`two_bf.py`). I let this run in the background.
Using numpy shenanigans I got it down to 20 minutes.
It worked before I could do further optimisation.
I have no fucking idea what could be the optimisations here,
and not super interested in figuring out. I'll spoil myself.
---
OH
🤚: buyer × buyer's sequences
👉: all possible sequences × buyers
Yeah alright

29
2024/22/one.py Normal file
View file

@ -0,0 +1,29 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
def prng(secwet: int) -> int:
secwet ^= secwet * 64
secwet %= 16777216
secwet ^= secwet // 32
secwet %= 16777216
secwet ^= secwet * 2048
secwet %= 16777216
return secwet
tot = 0
for line in lines:
secwet = int(line)
print(f"Init {secwet=}")
for i in range(2000):
secwet = prng(secwet)
print(f" -> {secwet=}")
tot += secwet
print(tot)

121
2024/22/two.py Normal file
View file

@ -0,0 +1,121 @@
#!/usr/bin/env python3
import sys
import numpy as np
import rich.progress
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
buyers = len(lines)
def prng(secwet: int) -> int:
secwet ^= secwet * 64
secwet %= 16777216
secwet ^= secwet // 32
secwet %= 16777216
secwet ^= secwet * 2048
secwet %= 16777216
return secwet
buyers_ban: list[list[int]] = []
buyers_dif: list[list[int]] = []
for line in rich.progress.track(lines, description="Calculating future banana prices"):
secwet = int(line)
buyer_ban: list[int] = []
buyer_dif: list[int] = []
last_ban = secwet % 10
for i in range(2000):
secwet = prng(secwet)
ban = secwet % 10
buyer_ban.append(ban)
dif = ban - last_ban
buyer_dif.append(dif)
last_ban = ban
# print(f"{secwet=} {ban=} {dif=}")
# print(f"{buyer_ban=}")
# print(f"{buyer_dif=}")
buyers_ban.append(buyer_ban)
buyers_dif.append(buyer_dif)
buyers_dif_np = np.array(buyers_dif)
buyers_ban_np = np.array(buyers_ban)
sequence = tuple[int, int, int, int]
def totbans(seq: sequence) -> int:
match = None
for i, num in enumerate(seq):
nmatch = buyers_dif_np == num
if match is not None:
# nmatch = sp.ndimage.shift(nmatch, (0, -i))
nmatch = np.roll(nmatch, -i, axis=1)
nmatch &= match
match = nmatch
# bans = buyers_ban_np * match
# found = match.max(axis=1)
# indexes = np.argmax(match > 0, axis=1)
tot = 0
assert match is not None
for b, buyer_match in enumerate(match):
if not buyer_match.max():
continue
arg: int = np.argmax(buyer_match > 0)
try:
ban = buyers_ban_np[b, arg+3]
tot += ban
except IndexError:
pass # shrug
return tot
for buyer in range(buyers):
buyer_dif = buyers_dif[buyer]
for i in range(2000 - 4):
if (
buyer_dif[i] == seq[0]
and buyer_dif[i + 1] == seq[1]
and buyer_dif[i + 2] == seq[2]
and buyer_dif[i + 3] == seq[3]
):
# if tuple(buyer_dif[i : i + 4]) == seq:
tot += buyers_ban[buyer][i + 3]
break
return tot
# print(f"{totbans((6, -1, -1, 0))=}") # demo0
# print(f"{totbans((-2, 1, -1, 3))=}") # demo2 aoc
# print(f"{totbans((6, -4, 4, -9))=}") # demo2 first
all_seqs: set[sequence] = set()
for buyer in rich.progress.track(
range(buyers), description="Generating possible sequences"
):
buyer_dif = buyers_dif[buyer]
for i in range(2000 - 4):
seq: sequence = tuple(buyer_dif[i : i + 4])
all_seqs.add(seq)
print(f"{len(all_seqs)=}")
maxi = 0
max_seq = None
for seq in rich.progress.track(all_seqs, description="Finding score for sequences"):
tb = totbans(seq)
if tb > maxi:
maxi = tb
max_seq = seq
print(f"{max_seq=}")
print(maxi)
# 1909 too low

87
2024/22/two_bf.py Normal file
View file

@ -0,0 +1,87 @@
#!/usr/bin/env python3
import sys
import rich.progress
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
buyers = len(lines)
def prng(secwet: int) -> int:
secwet ^= secwet * 64
secwet %= 16777216
secwet ^= secwet // 32
secwet %= 16777216
secwet ^= secwet * 2048
secwet %= 16777216
return secwet
buyers_ban: list[list[int]] = []
buyers_dif: list[list[int]] = []
for line in rich.progress.track(lines, description="Calculating future banana prices"):
secwet = int(line)
buyer_ban: list[int] = []
buyer_dif: list[int] = []
last_ban = secwet % 10
for i in range(2000):
secwet = prng(secwet)
ban = secwet % 10
buyer_ban.append(ban)
dif = ban - last_ban
buyer_dif.append(dif)
last_ban = ban
# print(f"{secwet=} {ban=} {dif=}")
# print(f"{buyer_ban=}")
# print(f"{buyer_dif=}")
buyers_ban.append(buyer_ban)
buyers_dif.append(buyer_dif)
sequence = tuple[int, int, int, int]
def totbans(seq: sequence) -> int:
tot = 0
for buyer in range(buyers):
buyer_dif = buyers_dif[buyer]
for i in range(2000 - 4):
if (
buyer_dif[i] == seq[0]
and buyer_dif[i + 1] == seq[1]
and buyer_dif[i + 2] == seq[2]
and buyer_dif[i + 3] == seq[3]
):
# if tuple(buyer_dif[i : i + 4]) == seq:
tot += buyers_ban[buyer][i + 3]
break
return tot
all_seqs: set[sequence] = set()
for buyer in rich.progress.track(
range(buyers), description="Generating possible sequences"
):
buyer_dif = buyers_dif[buyer]
for i in range(2000 - 4):
seq: sequence = tuple(buyer_dif[i : i + 4])
all_seqs.add(seq)
print(f"{len(all_seqs)=}")
maxi = 0
max_seq = None
for seq in rich.progress.track(all_seqs, description="Finding score for sequences"):
tb = totbans(seq)
if tb > maxi:
maxi = tb
max_seq = seq
print(f"{max_seq=}")
print(maxi)

112
2024/22/two_fast.py Normal file
View file

@ -0,0 +1,112 @@
#!/usr/bin/env python3
import collections
import math
import sys
import numpy as np
import rich.progress
dtype = np.int32
input_file = sys.argv[1]
with open(input_file) as fd:
buyers_init_secret = np.array([int(line) for line in fd.readlines()], dtype=dtype)
buyers = len(buyers_init_secret)
ITERATIONS = 2000
SEQUENCE_LENGTH = 4
buyers_prices = np.ndarray((buyers, ITERATIONS), dtype=dtype)
buyers_diffs = np.ndarray((buyers, ITERATIONS), dtype=dtype)
buyers_secret = buyers_init_secret.copy()
last_buyers_price = buyers_init_secret % 10
for i in rich.progress.track(range(ITERATIONS), description="Simulating stock market"):
buyers_secret ^= buyers_secret * 64
buyers_secret %= 16777216
buyers_secret ^= buyers_secret // 32
buyers_secret %= 16777216
buyers_secret ^= buyers_secret * 2048
buyers_secret %= 16777216
buyers_price = buyers_secret % 10
buyers_diff = buyers_price - last_buyers_price
buyers_prices[:, i] = buyers_price
buyers_diffs[:, i] = buyers_diff
last_buyers_price = buyers_price
# Compress sequence tuples into a single integer
# Move to positive numbers
trans = -buyers_diffs.min()
buyers_diffs_translated = buyers_diffs + trans
# Decide on a value to shift
shift = math.ceil(math.log2(buyers_diffs_translated.max()))
buyers_sequences = buyers_diffs_translated.copy()
for i in range(1, SEQUENCE_LENGTH):
buyers_sequences += np.roll(buyers_diffs_translated << (shift * i), i, axis=1)
# Make first few sequences invalid
buyers_sequences[:, :SEQUENCE_LENGTH] = -1
# NEW (knowing it's best to iterate per buyer than per sequence) ~ 5 seconds
# Inspired by the following, which is even faster, probably because data locality
# achieves more than SIMD-fying?
# https://github.com/mkern75/AdventOfCodePython/blob/23b6becdc873c6b865e783122a7dbce0b5f40f60/year2024/Day22.py
max_sequence = 1 << shift * SEQUENCE_LENGTH
sequences_total = np.zeros(max_sequence, dtype=dtype)
seen = np.zeros((buyers, max_sequence), dtype=bool)
for b in rich.progress.track(range(buyers), description="Evaluating each buyer"):
for i in range(SEQUENCE_LENGTH, ITERATIONS):
seq = buyers_sequences[b, i]
if seen[b, seq]:
continue
seen[b, seq] = True
sequences_total[seq] += buyers_prices[b, i]
print(f"{sequences_total.argmax()=}")
print(sequences_total.max())
sys.exit(0)
# OLD (knowing you can compresses sequences only) ~ 1.5 minute
def totbans(seq: int) -> int:
match = buyers_sequences == seq
found = match.max(axis=1)
indexes = np.argmax(match, axis=1)
bans = buyers_prices[range(buyers), indexes]
bans *= found
return bans.sum()
def seq_to_int(seq: tuple[int, ...]) -> int:
tot = 0
for s, num in enumerate(seq):
tot += (num + trans) << (SEQUENCE_LENGTH - s - 1) * shift
return tot
print(f"{totbans(seq_to_int((-2, 1, -1, 3)))=}")
all_seqs: set[int] = set(buyers_sequences.flat) - {-1}
maxi = 0
max_seq = None
for seq in rich.progress.track(all_seqs, description="Finding score for sequences"):
tb = totbans(seq)
if tb > maxi:
maxi = tb
max_seq = seq
print(f"{max_seq=}")
print(maxi)

65
2024/23/both_networkx.py Normal file
View file

@ -0,0 +1,65 @@
#!/usr/bin/env python3
import sys
import matplotlib.pyplot as plt
import networkx
import networkx as nx
input_file = sys.argv[1]
G = nx.Graph()
with open(input_file) as fd:
for line in fd.readlines():
a, b = line.rstrip().split("-")
G.add_edge(a, b)
trio_cliques: list[list[str]] = list()
lan = None
for clique in nx.enumerate_all_cliques(G):
if lan is None or len(clique) > len(lan):
lan = clique
if len(clique) != 3:
continue
if not any(c.startswith("t") for c in clique):
continue
trio_cliques.append(clique)
part1_ans = len(trio_cliques)
assert lan is not None
part2_ans = ",".join(sorted(lan))
print(f"{part1_ans=}")
print(f"{part2_ans=}")
trio_nodes = set(node for trio_clique in trio_cliques for node in trio_clique)
trio_edges = set(
edge
for clique in trio_cliques
for edge in list(nx.edge_boundary(G, clique, clique))
)
lan_edges = set(nx.edge_boundary(G, lan, lan))
for node in trio_nodes:
G.nodes[node]["color"] = "green"
for edge in trio_edges:
G.edges[edge]["color"] = "green"
G.edges[edge]["weight"] = 2
for node in lan:
G.nodes[node]["color"] = "red"
for edge in lan_edges:
# G.edges[edge]["color"] = "red"
G.edges[edge]["weight"] = 5
node_colors = [G.nodes[node].get("color", "blue") for node in G.nodes()]
edge_colors = [G.edges[edge].get("color", "blue") for edge in G.edges()]
node_pos = nx.layout.spring_layout(G)
nx.draw(
G, node_color=node_colors, edge_color=edge_colors, pos=node_pos, with_labels=True
)
plt.show()

36
2024/23/one.py Normal file
View file

@ -0,0 +1,36 @@
#!/usr/bin/env python3
import collections
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
connections = [tuple(line.split("-")) for line in lines]
codi: collections.defaultdict[str, set[str]] = collections.defaultdict(set)
for connection in connections:
a, b = connection
codi[a].add(b)
codi[b].add(a)
threes: set[tuple[str, ...]] = set()
for connection in connections:
a, b = connection
ac, bc = codi[a], codi[b]
iis = ac.intersection(bc)
for i in iis:
threel = [a, b, i]
if not (a.startswith("t") or b.startswith("t") or i.startswith("t")):
continue
threel.sort()
threes.add(tuple(threel))
for three in threes:
print(three)
print(len(threes))
# 11011 too high (forgot starts with t)

55
2024/23/two.py Normal file
View file

@ -0,0 +1,55 @@
#!/usr/bin/env python3
import collections
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
connections = [set(line.split("-")) for line in lines]
codi: collections.defaultdict[str, set[str]] = collections.defaultdict(set)
for connection in connections:
a, b = connection
codi[a].add(b)
codi[b].add(a)
threes: set[tuple[str, ...]] = set()
for connection in connections:
a, b = connection
ac, bc = codi[a], codi[b]
iis = ac.intersection(bc)
for i in iis:
threel = [a, b, i]
threel.sort()
threes.add(tuple(threel))
j = 0
while len(threes) > 1:
inthrees: set[str] = set()
for three in threes:
inthrees.update(set(three))
print(j, len(threes), len(inthrees))
fours: set[tuple[str, ...]] = set()
for three in threes:
threeset = set(three)
for comp in inthrees - threeset:
compc = codi[comp]
if threeset.issubset(compc):
fourl = list(threeset) + [comp]
fourl.sort()
fours.add(tuple(fourl))
threes = fours
threesl = list(threes)
if len(threesl) == 1:
three = threesl[0]
print(",".join(three))
print(None)

47
2024/24/one.py Normal file
View file

@ -0,0 +1,47 @@
#!/usr/bin/env python3
import functools
import sys
import typing
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
gates: dict[str, tuple[str, typing.Callable, str]] = dict()
varis: dict[str, int] = dict()
funs = {
"AND": int.__and__,
"OR": int.__or__,
"XOR": int.__xor__,
}
step = False
for line in lines:
if not line:
step = True
elif step:
a, op, b, _, dest = line.split()
fun = funs[op]
gates[dest] = (a, fun, b)
else:
dest, val = line.split(":")
varis[dest] = int(val)
@functools.cache
def get_var(var: str) -> int:
if var in varis:
return varis[var]
a, fun, b = gates[var]
avar = get_var(a)
bvar = get_var(b)
return fun(avar, bvar)
zees = sorted([key for key in gates.keys() if key.startswith("z")])
bits = reversed([str(get_var(key)) for key in zees])
res = int("".join(bits), base=2)
print(res)

161
2024/24/two.py Normal file
View file

@ -0,0 +1,161 @@
#!/usr/bin/env python3
import functools
import sys
import typing
import matplotlib.pyplot as plt
import networkx as nx
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
gates: dict[str, tuple[str, typing.Callable, str]] = dict()
varis: dict[str, int] = dict()
funs = {
"AND": int.__and__, # orange
"OR": int.__or__, # green
"XOR": int.__xor__, # purple
}
G = nx.DiGraph()
swaps = [
("ncd", "nfj"),
("z37", "vkg"),
("z20", "cqr"),
("z15", "qnw"),
]
swapdict: dict[str, str] = dict()
for a, b in swaps:
swapdict[a] = b
swapdict[b] = a
step = False
for line in lines:
if not line:
step = True
elif step:
a, op, b, _, dest = line.split()
dest = swapdict.get(dest, dest)
fun = funs[op]
gates[dest] = (a, fun, b)
G.add_node(dest, op=op)
G.add_edge(a, dest)
G.add_edge(b, dest)
else:
dest, val = line.split(":")
varis[dest] = int(val)
def swap(a: str, b: str) -> None:
temp = gates[a]
gates[a] = gates[b]
gates[b] = temp
@functools.cache
def get_var(var: str) -> int:
if var in varis:
return varis[var]
a, fun, b = gates[var]
avar = get_var(a)
bvar = get_var(b)
return fun(avar, bvar)
all_keys = list(gates.keys()) + list(varis.keys())
all_keys.sort(reverse=True)
def get_number(prefix: str) -> int:
tot = 0
keys = [key for key in all_keys if key.startswith(prefix)]
for key in keys:
tot <<= 1
tot |= get_var(key)
return tot
X = get_number("x")
Y = get_number("y")
Z = get_number("z")
print(f"{X+Y=} = {X=} + {Y=}")
print(f" {Z=} {Z == X + Y=}")
print(",".join(sorted(swapdict.keys())))
# Viz
@functools.cache
def get_node_pos(node: str) -> tuple[float, float]:
x: float
y: float
if node.startswith("x"):
x = -int(node[1:]) * 2
y = 0
elif node.startswith("y"):
x = -int(node[1:]) * 2 - 1
y = 0
elif node.startswith("z"):
x = -int(node[1:]) * 2
y = 100
else:
a, _, b = gates[node]
ax, ay = get_node_pos(a)
bx, by = get_node_pos(b)
x = (ax + bx) / 2
y = max(ay, by) + 1
return x, y
colors = {
"AND": "orange",
"OR": "green",
"XOR": "purple",
}
node_colors = []
node_pos: dict[str, tuple[float, float]] = dict()
node_fixed: set[str] = set()
for node in G.nodes():
op = G.nodes[node].get("op")
node_colors.append(colors.get(op, "cyan" if node.startswith("x") else "blue"))
x: float
y: float
fixed = True
if node.startswith("x"):
x = -int(node[1:]) * 2
y = 0
elif node.startswith("y"):
x = -int(node[1:]) * 2 - 1
y = 0
elif node.startswith("z"):
x = -int(node[1:]) * 2
y = 50
else:
fixed = False
x = -23
y = 25
node_pos[node] = x, y
if fixed:
node_fixed.add(node)
# My own layout
for i in range(50):
for node in G.nodes():
if node in node_fixed:
continue
neighs = list(G.succ[node]) + list(G.pred[node])
x = sum(node_pos[neigh][0] for neigh in neighs) / len(neighs)
y = sum(node_pos[neigh][1] for neigh in neighs) / len(neighs)
node_pos[node] = x, y
node_fixed = set(G.nodes())
node_layout = nx.layout.spring_layout(G, pos=node_pos, fixed=node_fixed)
nx.draw(G, pos=node_layout, node_color=node_colors, with_labels=True)
plt.show()

163
2024/24/two_test.py Normal file
View file

@ -0,0 +1,163 @@
#!/usr/bin/env python3
import functools
import sys
import typing
import matplotlib.pyplot as plt
import networkx as nx
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
gates: dict[str, tuple[str, typing.Callable, str]] = dict()
varis: dict[str, int] = dict()
funs = {
"AND": int.__and__, # orange
"OR": int.__or__, # green
"XOR": int.__xor__, # purple
}
G = nx.DiGraph()
swaps = [
# ("ncd", "nfj"),
# ("z37", "vkg"),
# ("z20", "cqr"),
# ("z15", "qnw"),
]
swapdict: dict[str, str] = dict()
for a, b in swaps:
swapdict[a] = b
swapdict[b] = a
step = False
for line in lines:
if not line:
step = True
elif step:
a, op, b, _, dest = line.split()
dest = swapdict.get(dest, dest)
fun = funs[op]
gates[dest] = (a, fun, b)
G.add_node(dest, op=op)
G.add_edge(a, dest)
G.add_edge(b, dest)
else:
dest, val = line.split(":")
varis[dest] = int(val)
def swap(a: str, b: str) -> None:
temp = gates[a]
gates[a] = gates[b]
gates[b] = temp
@functools.cache
def get_var(var: str) -> int:
if var in varis:
return varis[var]
a, fun, b = gates[var]
avar = get_var(a)
bvar = get_var(b)
return fun(avar, bvar)
all_keys = list(gates.keys()) + list(varis.keys())
all_keys.sort(reverse=True)
def get_number(prefix: str) -> int:
tot = 0
keys = [key for key in all_keys if key.startswith(prefix)]
for key in keys:
tot <<= 1
tot |= get_var(key)
return tot
X = get_number("x")
Y = get_number("y")
Z = get_number("z")
print(f"{X+Y=} = {X=} + {Y=}")
print(f" {Z=} {Z == X + Y=}")
print(",".join(sorted(swapdict.keys())))
# Viz
@functools.cache
def get_node_pos(node: str) -> tuple[float, float]:
x: float
y: float
if node.startswith("x"):
x = -int(node[1:]) * 2
y = 0
elif node.startswith("y"):
x = -int(node[1:]) * 2 - 1
y = 0
elif node.startswith("z"):
x = -int(node[1:]) * 2
y = 100
else:
a, _, b = gates[node]
ax, ay = get_node_pos(a)
bx, by = get_node_pos(b)
x = (ax + bx) / 2
y = max(ay, by) + 1
return x, y
colors = {
"AND": "orange",
"OR": "green",
"XOR": "purple",
}
node_colors = []
node_pos: dict[str, tuple[float, float]] = dict()
node_fixed: set[str] = set()
for node in G.nodes():
op = G.nodes[node].get("op")
node_colors.append(colors.get(op, "cyan" if node.startswith("x") else "blue"))
x: float
y: float
fixed = True
if node.startswith("x"):
x = -int(node[1:]) * 2
y = 0
elif node.startswith("y"):
x = -int(node[1:]) * 2 - 1
y = 0
elif node.startswith("z"):
x = -int(node[1:]) * 2
y = 50
else:
fixed = False
x = -23
y = 25
node_pos[node] = x, y
if fixed:
node_fixed.add(node)
# # My own layout
# for i in range(50):
# for node in G.nodes():
# if node in node_fixed:
# continue
# neighs = list(G.succ[node]) + list(G.pred[node])
# x = sum(node_pos[neigh][0] for neigh in neighs) / len(neighs)
# y = sum(node_pos[neigh][1] for neigh in neighs) / len(neighs)
# node_pos[node] = x, y
# node_fixed = set(G.nodes())
node_layout = nx.layout.spring_layout(
G.to_undirected(), k=1, iterations=1000, pos=node_pos, fixed=node_fixed
)
nx.draw(G, pos=node_layout, node_color=node_colors, with_labels=True)
plt.show()

45
2024/25/one.py Normal file
View file

@ -0,0 +1,45 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
lockey = tuple[int, int, int, int, int]
locks: list[lockey] = list()
keys: list[lockey] = list()
i = 0
for line in lines:
if not line:
i = 0
elif i == 0:
is_lock = line == "#####"
i = 1
sharps = [0, 0, 0, 0, 0]
elif i == 6:
if is_lock:
locks.append(tuple(sharps))
else:
keys.append(tuple(sharps))
else:
for j in range(5):
if line[j] == "#":
sharps[j] += 1
i += 1
print(locks)
print(keys)
fit = 0
for lock in locks:
for key in keys:
print(39, lock, key)
for i in range(5):
if lock[i] + key[i] > 5:
break
else:
fit += 1
print(fit)

19
2024/3/one.py Normal file
View file

@ -0,0 +1,19 @@
#!/usr/bin/env python3
import sys
import re
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
t = 0
reg = r"mul\(([0-9]+),([0-9]+)\)"
for line in lines:
for match in re.findall(reg, line):
ast, bst = match
a, b = int(ast), int(bst)
m = a * b
t += m
print(t)

26
2024/3/two.py Normal file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env python3
import sys
import re
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
t = 0
en = True
reg = r"mul\(([0-9]+),([0-9]+)\)|do\(\)|don't\(\)"
for line in lines:
for match in re.finditer(reg, line):
all = match.group(0)
if all == "do()":
en = True
elif all == "don't()":
en = False
elif en:
ast, bst = match.group(1), match.group(2)
a, b = int(ast), int(bst)
m = a * b
t += m
print(t)

59
2024/4/one.py Normal file
View file

@ -0,0 +1,59 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
word = "XMAS"
directions = [
(0, 1),
(1, 1),
(1, 0),
(1, -1),
(0, -1),
(-1, -1),
(-1, 0),
(-1, 1),
]
arrows = ["➡️", "↘️", "⬇️", "↙️", "⬅️", "↖️", "⬆️", "↗️"]
assert len(directions) == len(set(directions))
viz = [["."] * width for i in range(height)]
count = 0
for i in range(height):
for j in range(width):
for direction in directions:
ii = i
jj = j
for letter in word:
if (
ii not in range(height)
or jj not in range(width)
or lines[ii][jj] != letter
):
break
ii += direction[0]
jj += direction[1]
else:
count += 1
# d = directions.index(direction)
# viz[i][j] = arrows[d]
ii = i
jj = j
for letter in word:
viz[ii][jj] = letter
ii += direction[0]
jj += direction[1]
for line in viz:
print("".join(line))
print(count)

27
2024/4/two.py Normal file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
count = 0
for i in range(1, height - 1):
for j in range(1, width - 1):
if lines[i][j] != "A":
continue
tl = lines[i - 1][j - 1]
br = lines[i + 1][j + 1]
tr = lines[i - 1][j + 1]
bl = lines[i + 1][j - 1]
if not ((tl, br) == ("M", "S") or (tl, br) == ("S", "M")) or not (
(tr, bl) == ("M", "S") or (tr, bl) == ("S", "M")
):
continue
count += 1
print(count)

41
2024/5/one.py Normal file
View file

@ -0,0 +1,41 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
page_mode = False
orders: list[tuple[int, int]] = list()
updates: list[list[int]] = list()
for line in lines:
if not page_mode:
if line == "":
page_mode = True
else:
order = tuple(int(a) for a in line.split("|"))
assert len(order) == 2
orders.append(order)
else:
update = list(int(a) for a in line.split(","))
updates.append(update)
total = 0
for update in updates:
for fi, se in orders:
try:
ifi = update.index(fi)
ise = update.index(se)
except ValueError:
continue
if ifi > ise:
break
else:
imid = int(len(update)/2)
mid = update[imid]
total += mid
print(total)

57
2024/5/two.py Normal file
View file

@ -0,0 +1,57 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
page_mode = False
orders: list[tuple[int, int]] = list()
class Page:
def __init__(self, pn: int):
self.pn = pn
def __lt__(self, other: "Page") -> bool:
a = self.pn
b = other.pn
for fi, se in orders:
if a == fi and b == se:
return True
elif a == se and b == fi:
return False
raise RuntimeError
updates: list[list[Page]] = list()
for line in lines:
if not page_mode:
if line == "":
page_mode = True
else:
order = tuple(int(a) for a in line.split("|"))
assert len(order) == 2
orders.append(order)
else:
update = list(Page(int(a)) for a in line.split(","))
updates.append(update)
total = 0
for update in updates:
update_sorted = sorted(update)
if update == update_sorted:
continue
update = update_sorted
# Add
imid = int(len(update)/2)
mid = update[imid]
total += mid.pn
print(total)

47
2024/6/one.py Normal file
View file

@ -0,0 +1,47 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
for i in range(height):
if "^" in lines[i]:
j = lines[i].index("^")
break
d = 0
directions = [
(-1, 0), # ^
(0, 1), # >
(1, 0), # v
(0, -1), # <
]
vis = [[False] * width for h in range(height)]
while True:
print(i, j)
vis[i][j] = True
ii, jj = i + directions[d][0], j + directions[d][1]
if ii not in range(height) or jj not in range(width):
break
if lines[ii][jj] == "#":
d += 1
d %= len(directions)
continue
i, j = ii, jj
count = 0
for i in range(height):
for j in range(width):
if vis[i][j]:
count += 1
print(count)

67
2024/6/two.py Normal file
View file

@ -0,0 +1,67 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
for i_start in range(height):
if "^" in lines[i_start]:
j_start = lines[i_start].index("^")
break
directions = [
(-1, 0), # ^
(0, 1), # >
(1, 0), # v
(0, -1), # <
]
positions: set[tuple[int, int]] = set()
i, j = i_start, j_start
d = 0
while True:
positions.add((i, j))
ii, jj = i + directions[d][0], j + directions[d][1]
if ii not in range(height) or jj not in range(width):
break
if lines[ii][jj] == "#":
d += 1
d %= len(directions)
continue
i, j = ii, jj
print(len(positions))
positions.remove((i_start, j_start))
tot = 0
for obstacle in positions:
i, j = i_start, j_start
d = 0
path: set[tuple[int, int, int]] = set()
while True:
state = (i, j, d)
if state in path:
loop = True
tot += 1
break
path.add(state)
ii, jj = i + directions[d][0], j + directions[d][1]
if ii not in range(height) or jj not in range(width):
loop = False
break
if lines[ii][jj] == "#" or (ii, jj) == obstacle:
d += 1
d %= len(directions)
continue
i, j = ii, jj
print(tot)

34
2024/7/one.py Normal file
View file

@ -0,0 +1,34 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
final = 0
for line in lines:
spli = line.split()
res = int(spli[0][:-1])
nums = [int(num) for num in spli[1:]]
def check(tot: int, nums: list[int]) -> bool:
for op in (int.__add__, int.__mul__):
ntot = op(tot, nums[0])
if ntot > res:
continue
if len(nums) == 1:
if ntot == res:
return True
else:
if check(ntot, nums[1:]):
return True
return False
if check(nums[0], nums[1:]):
final += res
print(final)
# 2664444091381: too low

37
2024/7/two.py Normal file
View file

@ -0,0 +1,37 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
def concat(a: int, b: int) -> int:
return int(str(a) + str(b))
final = 0
for line in lines:
spli = line.split()
res = int(spli[0][:-1])
nums = [int(num) for num in spli[1:]]
def check(tot: int, nums: list[int]) -> bool:
for op in (int.__add__, int.__mul__, concat):
ntot = op(tot, nums[0])
if ntot > res:
continue
if len(nums) == 1:
if ntot == res:
return True
else:
if check(ntot, nums[1:]):
return True
return False
if check(nums[0], nums[1:]):
final += res
print(final)

38
2024/8/one.py Normal file
View file

@ -0,0 +1,38 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
antennas: dict[str, list[tuple[int, int]]] = dict()
for i in range(height):
for j in range(width):
char = lines[i][j]
if char == ".":
continue
antennas.setdefault(char, list())
antennas[char].append((i, j))
antinodes_locations: set[tuple[int, int]] = set()
for char, char_antennas in antennas.items():
print(f"25 {char} {char_antennas}")
for ant_a in char_antennas:
for ant_b in char_antennas:
if ant_a == ant_b:
continue
i, j = 2 * ant_b[0] - ant_a[0], 2 * ant_b[1] - ant_a[1]
antinode_loc = i, j
print(f"30 {antinode_loc}")
if i not in range(height) or j not in range(width):
continue
print(f"kept")
antinodes_locations.add(antinode_loc)
print(len(antinodes_locations))

49
2024/8/two.py Normal file
View file

@ -0,0 +1,49 @@
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
antennas: dict[str, list[tuple[int, int]]] = dict()
viz = [["."] * width for _ in range(height)]
for i in range(height):
for j in range(width):
char = lines[i][j]
if char == ".":
continue
antennas.setdefault(char, list())
antennas[char].append((i, j))
viz[i][j] = char
antinodes_locations: set[tuple[int, int]] = set()
for char, char_antennas in antennas.items():
print(f"25 {char} {char_antennas}")
for ant_a in char_antennas:
for ant_b in char_antennas:
if ant_a == ant_b:
continue
m = 0
while True:
i, j = ant_b[0] + m * (ant_b[0] - ant_a[0]), ant_b[1] + m * (
ant_b[1] - ant_a[1]
)
antinode_loc = i, j
print(f"30 {antinode_loc}")
if i not in range(height) or j not in range(width):
break
print("kept")
antinodes_locations.add(antinode_loc)
viz[i][j] = "#"
m += 1
for vline in viz:
print("".join(vline))
print(len(antinodes_locations))

Some files were not shown because too many files have changed in this diff Show more