Initial commit
This commit is contained in:
commit
97a4330bc0
.gitignore
2023
2024
1
10
11
12
13
14
15
16
:wREADME.mddemogdemog0networkx_test.pynumpy_test.pyone.pyone_opti.pyreddit_edge_casereddit_open_mazetwo.pytwo2.pytwo_opti.py
17
18
19
2
20
21
22
23
24
25
3
4
5
6
7
8
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal 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
5
2023/1/obfuscated.py
Normal 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
30
2023/1/script.py
Normal 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
28
2023/2/one.py
Normal 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
24
2023/2/two.py
Normal 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
16
2023/24/one.py
Normal 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
41
2023/3/one.py
Normal 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
42
2023/3/two.py
Normal 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
26
2024/1/one.py
Normal 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
25
2024/1/two.py
Normal 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
61
2024/10/one.py
Normal 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
64
2024/10/two.py
Normal 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
30
2024/11/one.py
Normal 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
121
2024/11/two.py
Normal 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
83
2024/12/one.py
Normal 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
5
2024/12/reddit_test
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
AAXXX
|
||||||
|
AAXAX
|
||||||
|
AAAAX
|
||||||
|
AAXAX
|
||||||
|
AAXXX
|
136
2024/12/two.py
Normal file
136
2024/12/two.py
Normal 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
BIN
2024/13/Notes.xopp
Normal file
Binary file not shown.
12
2024/13/demog
Normal file
12
2024/13/demog
Normal 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
78
2024/13/one.py
Normal 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
223
2024/13/two.py
Normal 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
123
2024/13/two_clean.py
Normal 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
64
2024/13/two_reddit.py
Normal 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
82
2024/13/two_simpy.py
Normal 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
88
2024/14/one.py
Normal 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
95
2024/14/two.py
Normal 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
96
2024/14/two_extra.py
Normal 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
6
2024/15/demog
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#######
|
||||||
|
#...O..
|
||||||
|
#......
|
||||||
|
#...@..
|
||||||
|
|
||||||
|
>
|
75
2024/15/one.py
Normal file
75
2024/15/one.py
Normal 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
110
2024/15/two.py
Normal 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
4
2024/16/:w
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
####
|
||||||
|
#.E#
|
||||||
|
#S##
|
||||||
|
####
|
23
2024/16/README.md
Normal file
23
2024/16/README.md
Normal 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
15
2024/16/demog
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
################
|
||||||
|
############..E#
|
||||||
|
###########..#.#
|
||||||
|
##########..##.#
|
||||||
|
#########..###.#
|
||||||
|
########..##...#
|
||||||
|
#######..###.###
|
||||||
|
######..####...#
|
||||||
|
#####..#######.#
|
||||||
|
####..##.......#
|
||||||
|
###..###.#######
|
||||||
|
##..####.......#
|
||||||
|
#..###########.#
|
||||||
|
#S.............#
|
||||||
|
################
|
4
2024/16/demog0
Normal file
4
2024/16/demog0
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
####
|
||||||
|
#.E#
|
||||||
|
#S.#
|
||||||
|
####
|
86
2024/16/networkx_test.py
Normal file
86
2024/16/networkx_test.py
Normal 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
135
2024/16/numpy_test.py
Normal 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
86
2024/16/one.py
Normal 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
93
2024/16/one_opti.py
Normal 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
27
2024/16/reddit_edge_case
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
###########################
|
||||||
|
#######################..E#
|
||||||
|
######################..#.#
|
||||||
|
#####################..##.#
|
||||||
|
####################..###.#
|
||||||
|
###################..##...#
|
||||||
|
##################..###.###
|
||||||
|
#################..####...#
|
||||||
|
################..#######.#
|
||||||
|
###############..##.......#
|
||||||
|
##############..###.#######
|
||||||
|
#############..####.......#
|
||||||
|
############..###########.#
|
||||||
|
###########..##...........#
|
||||||
|
##########..###.###########
|
||||||
|
#########..####...........#
|
||||||
|
########..###############.#
|
||||||
|
#######..##...............#
|
||||||
|
######..###.###############
|
||||||
|
#####..####...............#
|
||||||
|
####..###################.#
|
||||||
|
###..##...................#
|
||||||
|
##..###.###################
|
||||||
|
#..####...................#
|
||||||
|
#.#######################.#
|
||||||
|
#S........................#
|
||||||
|
###########################
|
14
2024/16/reddit_open_maze
Normal file
14
2024/16/reddit_open_maze
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
####################################################
|
||||||
|
#......................................#..........E#
|
||||||
|
#......................................#...........#
|
||||||
|
#....................#.................#...........#
|
||||||
|
#....................#.................#...........#
|
||||||
|
#....................#.................#...........#
|
||||||
|
#....................#.................#...........#
|
||||||
|
#....................#.................#...........#
|
||||||
|
#....................#.................#...........#
|
||||||
|
#....................#.................#...........#
|
||||||
|
#....................#.................#...........#
|
||||||
|
#....................#.............................#
|
||||||
|
#S...................#.............................#
|
||||||
|
####################################################
|
105
2024/16/two.py
Normal file
105
2024/16/two.py
Normal 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
95
2024/16/two2.py
Normal 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
111
2024/16/two_opti.py
Normal 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
BIN
2024/17/Notes.xopp
Normal file
Binary file not shown.
4
2024/17/README.md
Normal file
4
2024/17/README.md
Normal 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
5
2024/17/demog
Normal 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
69
2024/17/one.py
Normal 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
202
2024/17/two.py
Normal 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
125
2024/17/two_bf.py
Normal 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
83
2024/17/two_dfs.py
Normal 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
78
2024/17/two_fixed.py
Normal 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
109
2024/18/one.py
Normal 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
121
2024/18/two.py
Normal 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
1
2024/19/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
colors
|
8
2024/19/colors.py
Normal file
8
2024/19/colors.py
Normal 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
28
2024/19/one.py
Normal 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
32
2024/19/two.py
Normal 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
26
2024/2/one.py
Normal 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
37
2024/2/two.py
Normal 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
2
2024/20/README.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Reading comprehension got me on the second part,
|
||||||
|
a one byte change helped 🙃
|
4
2024/20/demog
Normal file
4
2024/20/demog
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#####
|
||||||
|
#S#E#
|
||||||
|
#...#
|
||||||
|
#####
|
32
2024/20/demog2
Normal file
32
2024/20/demog2
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#############################################################################################
|
||||||
|
#S#........................................................................................E#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#.#########################################################################################.#
|
||||||
|
#...........................................................................................#
|
||||||
|
#############################################################################################
|
5
2024/20/demog3
Normal file
5
2024/20/demog3
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
######
|
||||||
|
#...E#
|
||||||
|
#S####
|
||||||
|
#....#
|
||||||
|
######
|
129
2024/20/one.py
Normal file
129
2024/20/one.py
Normal 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
41
2024/20/reddit_part3
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#########################################
|
||||||
|
#...#.............#.....#.....#.....#...#
|
||||||
|
###.#.###.#########.###.###.#####.###.#.#
|
||||||
|
#...#...#.#.#.....#...#...#.#.........#.#
|
||||||
|
#..##.###.#.#####.#####.#.#.#.#####.#.#.#
|
||||||
|
#.......#.....#.#.....#.#...#...#...#.#.#
|
||||||
|
#.###########.#.#.####.####.#.###########
|
||||||
|
#.#.#...#...#.....#.................#...#
|
||||||
|
#.#.#.#.#.#.###.#.#.###.#########.#####.#
|
||||||
|
#.....#...#.....#...#.........#...#.#.#.#
|
||||||
|
#####.#####.#####.#.#.#.#.#######.#.#.#.#
|
||||||
|
#.....#.........#.#.#...#...#...#.#...#.#
|
||||||
|
#.#########.#######.#####.#.##..###.###.#
|
||||||
|
#...#.......#.....#.#...#.#...#.....#...#
|
||||||
|
#.###.###########.#.###.#.#.###.#######.#
|
||||||
|
#.#.#.............#.....#.#...#...#.....#
|
||||||
|
###.#.#####.#####.#.###.#.#####.#####.###
|
||||||
|
#...#.#.........#.#...#...#...#.#.....#.#
|
||||||
|
###.###.#.#########.#####.###.#.#.#.#.#.#
|
||||||
|
#S#.#...#.#.....#.....#.........#.#.#..E#
|
||||||
|
#.#.#.#########.#.#########.#.###.#####.#
|
||||||
|
#.....#.........#...#.#...#.#.....#...#.#
|
||||||
|
###.#####..##.#.#####.#.###.#####.###.###
|
||||||
|
#.#.#...#.#.#.#.#...#...#...#.........#.#
|
||||||
|
#.#.###.###.#.#.#.#####.####.##.#.#####.#
|
||||||
|
#.#.#.#.#.#...#.........#.#...#.#.#...#.#
|
||||||
|
#.#.#.#.#.#####.###.#.#.#.###.#.###.###.#
|
||||||
|
#...#.......#...#...#.#.#.........#.#...#
|
||||||
|
#######.#####.#####.###.#.#.#####.#.###.#
|
||||||
|
#.............#.....#.#.#.#.....#.......#
|
||||||
|
###############.#####.#.#########.#.#.###
|
||||||
|
#.....#...#.#.........#.#...#...#.#.#.#.#
|
||||||
|
#.#.#.#.#.#.###.#########.###.###.#####.#
|
||||||
|
#.#.#.#.#...........#.#.............#...#
|
||||||
|
###.#.#.###.#######.#.#.#.###.###.#.#.###
|
||||||
|
#...#...#...#.#...#.#...#...#.#.#.#.#...#
|
||||||
|
###.#.#######.#.#.#.###.#####.#..##.#.###
|
||||||
|
#.#.#...#.....#.#.#.......#.#.#...#.....#
|
||||||
|
#.#.#####.###.#.#.#.#.#####.#####.###.#.#
|
||||||
|
#.....#.....#.......#.............#...#.#
|
||||||
|
#########################################
|
41
2024/20/reddit_part3g
Normal file
41
2024/20/reddit_part3g
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#########################################
|
||||||
|
#...#.............#.....#.....#.....#...#
|
||||||
|
###.#.###.#########.###.###.#####.###.#.#
|
||||||
|
#...#...#.#.#.....#...#...#.#.........#.#
|
||||||
|
#..##.###.#.#####.#####.#.#.#.#####.#.#.#
|
||||||
|
#.......#.....#.#.....#.#...#...#...#.#.#
|
||||||
|
#.###########.#.#.####.####.#.###########
|
||||||
|
#.#.#...#...#.....#.................#...#
|
||||||
|
#.#.#.#.#.#.###.#.#.###.#########.#####.#
|
||||||
|
#.....#...#.....#...#.........#...#.#.#.#
|
||||||
|
#####.#####.#####.#.#.#.#.#######.#.#.#.#
|
||||||
|
#.....#.........#.#.#...#...#...#.#...#.#
|
||||||
|
#.#########.#######.#####.#.##..###.###.#
|
||||||
|
#...#.......#.....#.#...#.#...#.....#...#
|
||||||
|
#.###.###########.#.###.#.#.###.#######.#
|
||||||
|
#.#.#.............#.....#.#...#...#.....#
|
||||||
|
###.#.#####.#####.#.###.#.#####.#####.###
|
||||||
|
#...#.#.........#.#...#...#...#.#.....#.#
|
||||||
|
###.###.#.#########.#####.###.#.#.#.#.#.#
|
||||||
|
#S#.#...#.#.....#.....#.........#.#.#..E#
|
||||||
|
#.#.#.#########.#.#########.#.###.#####.#
|
||||||
|
#.....#.........#...#.#...#.#.....#...#.#
|
||||||
|
###.#####..##.#.#####.#.###.#####.###.###
|
||||||
|
#.#.#...#.#.#.#.#...#...#...#.........#.#
|
||||||
|
#.#.###.###.#.#.#.#####.####.##.#.#####.#
|
||||||
|
#.#.#.#.#.#...#.........#.#...#.#.#...#.#
|
||||||
|
#.#.#.#.#.#####.###.#.#.#.###.#.###.###.#
|
||||||
|
#...#.......#...#...#.#.#.........#.#...#
|
||||||
|
#######.#####.#####.###.#.#.#####.#.###.#
|
||||||
|
#.............#.....#.#.#.#.....#.......#
|
||||||
|
#####################.#.#########.#.#.###
|
||||||
|
#.....#...#.#.........#.#...#...#.#.#.#.#
|
||||||
|
#.#.#.#.#.#.###.#########.###.###.#####.#
|
||||||
|
#.#.#.#.#...........#.#.............#...#
|
||||||
|
###.#.#.###.#######.#.#.#.###.###.#.#.###
|
||||||
|
#...#...#...#.#...#.#...#...#.#.#.#.#...#
|
||||||
|
###.#.#######.#.#.#.###.#####.#..##.#.###
|
||||||
|
#.#.#...#.....#.#.#.......#.#.#...#.....#
|
||||||
|
#.#.#####.###.#.#.#.#.#####.#####.###.#.#
|
||||||
|
#.....#.....#.......#.............#...#.#
|
||||||
|
#########################################
|
153
2024/20/two.py
Normal file
153
2024/20/two.py
Normal 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
212
2024/20/two_correct.py
Normal 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
172
2024/20/two_fast.py
Normal 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
BIN
2024/21/Notes.xopp
Normal file
Binary file not shown.
BIN
2024/21/Spreadsheet.ods
Normal file
BIN
2024/21/Spreadsheet.ods
Normal file
Binary file not shown.
1
2024/21/demog
Normal file
1
2024/21/demog
Normal file
|
@ -0,0 +1 @@
|
||||||
|
3A
|
98
2024/21/one.py
Normal file
98
2024/21/one.py
Normal 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
97
2024/21/onet.py
Normal 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
281
2024/21/two.py
Normal 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
64
2024/21/two_fast.py
Normal 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
20
2024/22/README.md
Normal 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
29
2024/22/one.py
Normal 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
121
2024/22/two.py
Normal 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
87
2024/22/two_bf.py
Normal 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
112
2024/22/two_fast.py
Normal 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
65
2024/23/both_networkx.py
Normal 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
36
2024/23/one.py
Normal 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
55
2024/23/two.py
Normal 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
47
2024/24/one.py
Normal 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
161
2024/24/two.py
Normal 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
163
2024/24/two_test.py
Normal 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
45
2024/25/one.py
Normal 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
19
2024/3/one.py
Normal 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
26
2024/3/two.py
Normal 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
59
2024/4/one.py
Normal 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
27
2024/4/two.py
Normal 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
41
2024/5/one.py
Normal 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
57
2024/5/two.py
Normal 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
47
2024/6/one.py
Normal 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
67
2024/6/two.py
Normal 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
34
2024/7/one.py
Normal 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
37
2024/7/two.py
Normal 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
38
2024/8/one.py
Normal 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
49
2024/8/two.py
Normal 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
Loading…
Reference in a new issue