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