Compare commits
131 Commits
f0a6214052
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 288a0809ff | |||
| 42e0c68d93 | |||
| 867a7ed2df | |||
| df0682e989 | |||
| e5421854a9 | |||
| 183f909508 | |||
| e655580b3f | |||
| c9145e6106 | |||
| 7013c5b0a0 | |||
| 376a2eac09 | |||
| 2ee16eb1ba | |||
| 8cc199105c | |||
| 3dd208c088 | |||
| 265829715a | |||
| 3979a60fa4 | |||
| acdf82d768 | |||
| f4edfaa680 | |||
| 299f1cf2ff | |||
| 744dbb4ffc | |||
| 4ad7075785 | |||
| 3432d81941 | |||
| efb1e3e551 | |||
| 022b95eb97 | |||
| 96f15d07fc | |||
| 83fbf59bd7 | |||
| e9211c26a3 | |||
| 2a10543852 | |||
| b747d3973a | |||
| b622ca92b9 | |||
| e9a4ce6414 | |||
| 36f8f709e1 | |||
| fae61ae6db | |||
| 63166ddce8 | |||
| 24174b8cb9 | |||
| 0fb75687d1 | |||
| b627e97c5a | |||
| 893a8e3dbb | |||
| 12662b912c | |||
| 54bbc228f4 | |||
| 2d3a44760e | |||
| a2a6ca52d1 | |||
| 4acc229c5a | |||
| ff3d33a614 | |||
| bde0a5d622 | |||
| 9ac4782157 | |||
| 43767f7b62 | |||
| e9bd759dba | |||
| 11bcbce099 | |||
| 074a7c4458 | |||
| 10e1e567c8 | |||
| e73fa3bae7 | |||
| 87ab42743e | |||
| 800d1a6af3 | |||
| 5dc7c4392f | |||
| 67914a642a | |||
| df31b47934 | |||
| 3991842ef0 | |||
| 7d1dc3f95e | |||
| ea28a17ab9 | |||
| 6475681cfb | |||
| 97b05614ee | |||
| 36f20610ae | |||
| 695a4f7ddb | |||
| 25443fba48 | |||
| bb9e3321d7 | |||
| 2eba36a29a | |||
| 25bd810886 | |||
| 6c3aec3d6e | |||
| db383c7cfa | |||
| 7986ecfc21 | |||
| 0f7606410c | |||
| 6e588bb928 | |||
| 4a72eeb348 | |||
| 9cd17279d9 | |||
| bf88f13791 | |||
| fa6ea1dfbe | |||
| 6cea472035 | |||
| ad53396ca6 | |||
| b307e09f2b | |||
| 381f186a59 | |||
| 5fae7c0366 | |||
| 2647ccd353 | |||
| 9a30c8b88d | |||
| 305fe0b325 | |||
| 631205086d | |||
| 04d8c44d66 | |||
| 6b0d7057d1 | |||
| dd901c9f88 | |||
| e9f5542be8 | |||
| ebf65905b4 | |||
| aba7ad5bde | |||
| f88eb14d24 | |||
| 6efa70ea51 | |||
| fb1e2183cc | |||
| e46636b9b0 | |||
| f116293066 | |||
| ef8a247ad6 | |||
| af4507f785 | |||
| 6f71ed1717 | |||
| 9718165d7b | |||
| b0e2334427 | |||
| 29a556fd54 | |||
| d2417a9377 | |||
| 9f616a5b81 | |||
| b88326a7d1 | |||
| ab2e6f4ddb | |||
| db82589857 | |||
| 9346e3f4d0 | |||
| 5e68e08807 | |||
| f04f8ed426 | |||
| 26f523047e | |||
| e9ba9cee63 | |||
| d582dfc777 | |||
| da1c37ffa7 | |||
| 9df26ebe71 | |||
| 3a915cb9e3 | |||
| dcfae2cb13 | |||
| f62f5fcff2 | |||
| 869a22a310 | |||
| 8042b7f5e2 | |||
| ecda947fb4 | |||
| 5b7b9c87cb | |||
| 4ba0b25b77 | |||
| 1a71ab97c3 | |||
| 6f63a866e1 | |||
| 90b2134736 | |||
| a49eb9e8cb | |||
| 97e9fac07b | |||
| 78b1981ddf | |||
| 3c290b8da3 | |||
| 4e6a44f67b |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,6 +4,8 @@
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
.python-version
|
||||
uv.lock
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
16
2015/d1.py
16
2015/d1.py
@@ -1,8 +1,9 @@
|
||||
from lib import *
|
||||
from lib import get_data
|
||||
|
||||
data = open(0).read()
|
||||
data = get_data(__file__)
|
||||
|
||||
part_2 = True
|
||||
t = sum((1 if c == "(" else -1 for c in data))
|
||||
print(t)
|
||||
|
||||
floor = 0
|
||||
for i, c in enumerate(data):
|
||||
@@ -10,13 +11,6 @@ for i, c in enumerate(data):
|
||||
floor += 1
|
||||
elif c == ")":
|
||||
floor -= 1
|
||||
else:
|
||||
print(c)
|
||||
assert False
|
||||
|
||||
if part_2 and floor == -1:
|
||||
if floor == -1:
|
||||
print(i + 1)
|
||||
break
|
||||
|
||||
if not part_2:
|
||||
print(floor)
|
||||
|
||||
54
2015/d10.py
54
2015/d10.py
@@ -1,35 +1,29 @@
|
||||
from functools import lru_cache
|
||||
data = open(0).read().strip()
|
||||
from lib import get_data
|
||||
|
||||
part_1 = False
|
||||
if part_1:
|
||||
repeats = 40
|
||||
else:
|
||||
repeats = 50
|
||||
data = get_data(__file__)
|
||||
|
||||
@lru_cache
|
||||
def look_and_say(data: str) -> str:
|
||||
r = ""
|
||||
i = 0
|
||||
while i < len(data):
|
||||
count = 0
|
||||
c = data[i]
|
||||
while i < len(data) and data[i] == c:
|
||||
|
||||
def iter(xs):
|
||||
if not xs:
|
||||
return []
|
||||
grouped = []
|
||||
current_element = xs[0]
|
||||
count = 1
|
||||
for element in xs[1:]:
|
||||
if element == current_element:
|
||||
count += 1
|
||||
i += 1
|
||||
r += f"{count}{c}"
|
||||
return r
|
||||
else:
|
||||
grouped.append(count)
|
||||
grouped.append(current_element)
|
||||
current_element = element
|
||||
count = 1
|
||||
grouped.append(count)
|
||||
grouped.append(current_element)
|
||||
return grouped
|
||||
|
||||
CHUNK_SIZE = 10000
|
||||
for _ in range(repeats):
|
||||
ndata = ""
|
||||
lo, up = 0, CHUNK_SIZE
|
||||
while up < len(data):
|
||||
while up < len(data) and data[up - 1] == data[up]:
|
||||
up += 1
|
||||
ndata += look_and_say(data[lo:up])
|
||||
lo, up = up, up + CHUNK_SIZE
|
||||
ndata += look_and_say(data[lo:up])
|
||||
data = ndata
|
||||
|
||||
print(len(data))
|
||||
for repeat in [40, 50]:
|
||||
xs = list(map(int, list(data.strip())))
|
||||
for _ in range(repeat):
|
||||
xs = iter(xs)
|
||||
print(len(xs))
|
||||
|
||||
17
2015/d11.py
17
2015/d11.py
@@ -1,4 +1,7 @@
|
||||
data = open(0).read().strip()
|
||||
from lib import get_data
|
||||
|
||||
data = get_data(__file__).strip()
|
||||
|
||||
|
||||
def is_valid(pw):
|
||||
# Passwords must include one increasing straight of at least three letters,
|
||||
@@ -26,24 +29,26 @@ def is_valid(pw):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def inc(pw):
|
||||
pw = list(map(ord, pw))
|
||||
for i in range(len(pw) - 1, -1, -1):
|
||||
pw[i] += 1
|
||||
if pw[i] == ord('z') + 1:
|
||||
pw[i] = ord('a')
|
||||
if pw[i] == ord("z") + 1:
|
||||
pw[i] = ord("a")
|
||||
else:
|
||||
break
|
||||
return "".join(map(chr, pw))
|
||||
|
||||
part_1 = False
|
||||
|
||||
part_1 = True
|
||||
valid_count = 0
|
||||
while True:
|
||||
data = inc(data)
|
||||
if is_valid(data):
|
||||
valid_count += 1
|
||||
if part_1 and valid_count == 1:
|
||||
break
|
||||
print(data)
|
||||
elif valid_count == 2:
|
||||
print(data)
|
||||
break
|
||||
print(data)
|
||||
|
||||
54
2015/d12.py
54
2015/d12.py
@@ -1,27 +1,37 @@
|
||||
from lib import get_data
|
||||
import json
|
||||
|
||||
data = open(0).read().strip()
|
||||
part_2 = False
|
||||
data = get_data(__file__).strip()
|
||||
|
||||
def addup(d):
|
||||
r = 0
|
||||
if isinstance(d, list):
|
||||
for e in d:
|
||||
v = addup(e)
|
||||
if v is not None:
|
||||
r += v
|
||||
elif isinstance(d, dict):
|
||||
for e in d.values():
|
||||
v = addup(e)
|
||||
if v is None:
|
||||
return 0
|
||||
r += v
|
||||
elif isinstance(d, str):
|
||||
if part_2 and d == "red":
|
||||
return None
|
||||
o = json.loads(data)
|
||||
|
||||
|
||||
def xsum(obj, part_2=False):
|
||||
t = 0
|
||||
if type(obj) is int:
|
||||
t += obj
|
||||
elif type(obj) is list:
|
||||
for o in obj:
|
||||
t += xsum(o, part_2)
|
||||
elif type(obj) is dict:
|
||||
if part_2:
|
||||
st = 0
|
||||
for o in obj.values():
|
||||
st += xsum(o, part_2)
|
||||
if type(o) is str and o == "red":
|
||||
break
|
||||
else:
|
||||
r += d
|
||||
return r
|
||||
t += st
|
||||
else:
|
||||
for o in obj.values():
|
||||
t += xsum(o, part_2)
|
||||
elif type(obj) is str:
|
||||
pass
|
||||
else:
|
||||
print(obj)
|
||||
assert False
|
||||
return t
|
||||
|
||||
data = json.loads(data)
|
||||
print(addup(data))
|
||||
|
||||
print(xsum(o))
|
||||
print(xsum(o, True))
|
||||
|
||||
49
2015/d13.py
49
2015/d13.py
@@ -1,35 +1,32 @@
|
||||
from lib import get_data
|
||||
from collections import defaultdict
|
||||
from itertools import permutations
|
||||
data = open(0).read().strip()
|
||||
|
||||
part_2 = False
|
||||
data = get_data(__file__).strip()
|
||||
|
||||
pairs = defaultdict(int)
|
||||
people = set()
|
||||
scores = {}
|
||||
|
||||
for line in data.splitlines():
|
||||
a, _, wl, score, _, _, _, _, _, _, b = line.split()
|
||||
b = b.replace(".", "")
|
||||
score = int(score)
|
||||
if wl == "lose":
|
||||
score = -score
|
||||
a, _, gainlose, amount, _, _, _, _, _, _, b = line[:-1].split()
|
||||
amount = int(amount)
|
||||
if gainlose == "lose":
|
||||
amount = -amount
|
||||
pairs[(a, b)] = amount
|
||||
people.add(a)
|
||||
people.add(b)
|
||||
scores[(a, b)] = score
|
||||
if part_2:
|
||||
scores[(a, "me")] = 0
|
||||
scores[("me", a)] = 0
|
||||
scores[(b, "me")] = 0
|
||||
scores[("me", b)] = 0
|
||||
|
||||
if part_2:
|
||||
people.add("me")
|
||||
|
||||
max_score = 0
|
||||
for p in permutations(list(people)):
|
||||
s = 0
|
||||
for i in range(len(p)):
|
||||
a, b = p[i], p[(i + 1) % len(p)]
|
||||
s += scores[(a, b)]
|
||||
s += scores[(b, a)]
|
||||
max_score = max(max_score, s)
|
||||
def calc_gain(people):
|
||||
maxgain = 0
|
||||
for xs in permutations(list(people)):
|
||||
gain = 0
|
||||
for i in range(len(xs)):
|
||||
gain += pairs[(xs[i], xs[(i + 1) % len(xs)])]
|
||||
gain += pairs[(xs[(i + 1) % len(xs)], xs[i])]
|
||||
maxgain = max(maxgain, gain)
|
||||
return maxgain
|
||||
|
||||
print(max_score)
|
||||
|
||||
print(calc_gain(people))
|
||||
people.add("Felix")
|
||||
print(calc_gain(people))
|
||||
|
||||
79
2015/d14.py
79
2015/d14.py
@@ -1,53 +1,40 @@
|
||||
import lib
|
||||
data = open(0).read().strip()
|
||||
from lib import get_data
|
||||
from lib import ints, fst
|
||||
from collections import defaultdict
|
||||
|
||||
part_1 = False
|
||||
|
||||
rds = []
|
||||
max_dist = 0
|
||||
gtime = 2503
|
||||
rds = []
|
||||
data = get_data(__file__).strip()
|
||||
deers = []
|
||||
for line in data.splitlines():
|
||||
speed, time, rest = lib.str_to_ints(line)
|
||||
rds.append([speed, time, rest, 0, 0, 0])
|
||||
speed *= 1000
|
||||
speed, time, rest = ints(line)
|
||||
deers.append([0, speed, time, rest, time, 0])
|
||||
|
||||
dist = 0
|
||||
ctime = 0
|
||||
while ctime < gtime:
|
||||
t = min(time, gtime - ctime)
|
||||
dist += (speed * t)
|
||||
ctime += time
|
||||
ctime += rest
|
||||
max_dist = max(dist, max_dist)
|
||||
points = defaultdict(int)
|
||||
for _ in range(2503):
|
||||
max_dists = defaultdict(list)
|
||||
for i, deer in enumerate(deers):
|
||||
dist, speed, time, rest, time_left, rest_left = deer
|
||||
if time_left > 0:
|
||||
time_left -= 1
|
||||
dist += speed
|
||||
elif time_left == 0:
|
||||
time_left = -1
|
||||
rest_left = rest - 1
|
||||
elif rest_left > 0:
|
||||
rest_left -= 1
|
||||
elif rest_left == 0:
|
||||
time_left = time - 1
|
||||
dist += speed
|
||||
rest_left = -1
|
||||
max_dists[dist].append(i)
|
||||
|
||||
if part_1:
|
||||
print(max_dist // 1000)
|
||||
deer[0] = dist
|
||||
deer[4] = time_left
|
||||
deer[5] = rest_left
|
||||
|
||||
scores = [0 for _ in range(len(rds))]
|
||||
for _ in range(gtime):
|
||||
for i in range(len(rds)):
|
||||
speed, time, rest, dist, travel_time, rest_time = rds[i]
|
||||
rd = rds[i]
|
||||
if travel_time < time:
|
||||
rd[3] += speed
|
||||
rd[4] += 1
|
||||
elif rest_time < rest:
|
||||
rd[5] += 1
|
||||
if rd[5] == rest:
|
||||
rd[4] = 0
|
||||
rd[5] = 0
|
||||
max_deers = max(max_dists.items())
|
||||
for di in max_deers[1]:
|
||||
points[di] += 1
|
||||
|
||||
max_i = -1
|
||||
max_v = 0
|
||||
for i, rd in enumerate(rds):
|
||||
if rd[3] > max_v:
|
||||
max_i = i
|
||||
max_v = rd[3]
|
||||
for i, rd in enumerate(rds):
|
||||
if rd[3] == max_v:
|
||||
scores[i] += 1
|
||||
|
||||
if not part_1:
|
||||
print(max(scores))
|
||||
|
||||
print(max(map(fst, deers)))
|
||||
print(max(points.values()))
|
||||
|
||||
52
2015/d19.py
52
2015/d19.py
@@ -1,41 +1,41 @@
|
||||
import sys
|
||||
from random import shuffle
|
||||
|
||||
data = open(0).read().strip()
|
||||
with open("i19.txt", "r") as f:
|
||||
data = f.read()
|
||||
|
||||
eqs = []
|
||||
text = ""
|
||||
molecule = ""
|
||||
for line in data.splitlines():
|
||||
if "=>" in line:
|
||||
lhs, rhs = line.split(" => ")
|
||||
eqs.append((lhs, rhs))
|
||||
elif line:
|
||||
text = line
|
||||
molecule = line.strip()
|
||||
|
||||
molecules = set()
|
||||
for lhs, rhs in eqs:
|
||||
for i in range(len(text)):
|
||||
f, t = text[:i], text[i:]
|
||||
for i in range(len(molecule)):
|
||||
f, t = molecule[:i], molecule[i:]
|
||||
if t.startswith(lhs):
|
||||
n = f + t.replace(lhs, rhs, 1)
|
||||
molecules.add(n)
|
||||
print(len(molecules))
|
||||
|
||||
# m = float("inf")
|
||||
# for _ in range(100000):
|
||||
# getout = False
|
||||
# c = str(text)
|
||||
# for i in range(10**4):
|
||||
# if i % 1000 == 0:
|
||||
# shuffle(eqs)
|
||||
# for lhs, rhs in eqs:
|
||||
# if rhs in c:
|
||||
# c = c.replace(rhs, lhs, 1)
|
||||
# if c == "e":
|
||||
# if i < m:
|
||||
# m = i
|
||||
# print(i)
|
||||
# getout = True
|
||||
# if getout:
|
||||
# break
|
||||
# print(m)
|
||||
|
||||
# It seems like the problem input is specifically designed so that reversing
|
||||
# the molecule back to 'e' works by simply replacing RHS with LHS continuously.
|
||||
# Intuitively, it seems like this should not work in many cases or at least not
|
||||
# result in the shortest sequence. Probably I am just salty that it took me so
|
||||
# long to find this trivial solution.
|
||||
x = molecule
|
||||
count = 0
|
||||
while len(x) > 1:
|
||||
old_len = len(x)
|
||||
for lhs, rhs in eqs:
|
||||
if rhs in x:
|
||||
x = x.replace(rhs, lhs, 1)
|
||||
count += 1
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
assert x == 'e'
|
||||
print(count)
|
||||
|
||||
24
2015/d2.py
24
2015/d2.py
@@ -1,19 +1,17 @@
|
||||
from lib import *
|
||||
from lib import get_data, ints
|
||||
|
||||
data = open(0).readlines()
|
||||
data = get_data(__file__)
|
||||
|
||||
part_1 = True
|
||||
if part_1:
|
||||
a = 0
|
||||
for line in data:
|
||||
l, w, h = list(map(int, line.split("x")))
|
||||
a = 0
|
||||
for line in data.splitlines():
|
||||
l, w, h = ints(line)
|
||||
slack = min([l * w, w * h, h * l])
|
||||
a += (2*l*w + 2*w*h + 2*h*l + slack)
|
||||
else:
|
||||
a = 0
|
||||
for line in data:
|
||||
l, w, h = list(map(int, line.split("x")))
|
||||
a += 2 * l * w + 2 * w * h + 2 * h * l + slack
|
||||
print(a)
|
||||
|
||||
a = 0
|
||||
for line in data.splitlines():
|
||||
l, w, h = ints(line)
|
||||
sd = min([2 * (l + w), 2 * (w + h), 2 * (h + l)])
|
||||
a += sd + (l * w * h)
|
||||
|
||||
print(a)
|
||||
|
||||
@@ -4,6 +4,7 @@ import lib
|
||||
data = int(open(0).read().strip())
|
||||
part_1 = False
|
||||
|
||||
|
||||
def calc(n):
|
||||
fs = lib.prime_factors(n)
|
||||
vs = set([1, n])
|
||||
@@ -21,6 +22,7 @@ def calc(n):
|
||||
r += e * 11
|
||||
return r
|
||||
|
||||
|
||||
for i in range(3, 10000000):
|
||||
c = calc(i)
|
||||
if c >= data:
|
||||
|
||||
40
2015/d23.py
Normal file
40
2015/d23.py
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
insts = []
|
||||
with open("i23.txt", "r") as f:
|
||||
for line in f:
|
||||
args = line.strip().replace(",", "").split()
|
||||
insts.append(args)
|
||||
|
||||
part_2 = True
|
||||
i = 0
|
||||
regs = {"a": 0, "b": 0}
|
||||
if part_2:
|
||||
regs["a"] = 1
|
||||
|
||||
while i < len(insts):
|
||||
inst = insts[i]
|
||||
match inst:
|
||||
case ["inc", reg]:
|
||||
regs[reg] += 1
|
||||
case ["hlf", reg]:
|
||||
regs[reg] //= 2
|
||||
case ["tpl", reg]:
|
||||
regs[reg] *= 3
|
||||
case ["jio", reg, offset]:
|
||||
if regs[reg] == 1:
|
||||
i += int(offset)
|
||||
continue
|
||||
case ["jie", reg, offset]:
|
||||
offset = int(offset)
|
||||
if regs[reg] % 2 == 0:
|
||||
i += int(offset)
|
||||
continue
|
||||
case ["jmp", offset]:
|
||||
i += int(offset)
|
||||
continue
|
||||
case _:
|
||||
print(inst)
|
||||
assert False
|
||||
i += 1
|
||||
|
||||
print(regs["b"])
|
||||
46
2015/d24.py
Normal file
46
2015/d24.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import sys
|
||||
from itertools import combinations
|
||||
|
||||
with open("i24.txt", "r") as f:
|
||||
weights = list(map(int, f.readlines()))
|
||||
|
||||
total_sum = sum(weights)
|
||||
|
||||
indices = list(range(len(weights)))
|
||||
|
||||
lowest = None
|
||||
def find(part_1=True):
|
||||
if part_1:
|
||||
target_weight = total_sum // 3
|
||||
else:
|
||||
target_weight = total_sum // 4
|
||||
for group_1_size in range(1, len(weights) - 1):
|
||||
for group_1_indices in combinations(indices, group_1_size):
|
||||
group_1_weight = sum(weights[i] for i in group_1_indices)
|
||||
if not group_1_weight == target_weight:
|
||||
continue
|
||||
remaining_indices = list(set(indices) - set(group_1_indices))
|
||||
for group_2_size in range(1, len(remaining_indices) - 1):
|
||||
for group_2_indices in combinations(remaining_indices, group_2_size):
|
||||
group_2_weight = sum(weights[i] for i in group_2_indices)
|
||||
if not group_2_weight == target_weight:
|
||||
continue
|
||||
if part_1 :
|
||||
r = 1
|
||||
for i in group_1_indices:
|
||||
r *= weights[i]
|
||||
return r
|
||||
else:
|
||||
remaining_indices = list(set(indices) - set(group_1_indices))
|
||||
for group_3_size in range(1, len(remaining_indices)- 1):
|
||||
for group_3_indices in combinations(remaining_indices, group_3_size):
|
||||
group_3_weight = sum(weights[i] for i in group_3_indices)
|
||||
if not group_3_weight == target_weight:
|
||||
continue
|
||||
r = 1
|
||||
for i in group_1_indices:
|
||||
r *= weights[i]
|
||||
return r
|
||||
|
||||
print(find(True))
|
||||
print(find(False))
|
||||
19
2015/d25.py
Normal file
19
2015/d25.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import sys
|
||||
from lib import str_to_ints
|
||||
|
||||
with open("i25.txt", "r") as f:
|
||||
target_row, target_col = str_to_ints(f.read())
|
||||
|
||||
x = 20151125
|
||||
m = 252533
|
||||
d = 33554393
|
||||
|
||||
for start_row in range(1, 10000):
|
||||
row, col = start_row, 1
|
||||
while row > 0:
|
||||
if row == target_row and col == target_col:
|
||||
print(x)
|
||||
sys.exit(0)
|
||||
x = (x * m) % d
|
||||
row -= 1
|
||||
col += 1
|
||||
29
2015/d3.py
29
2015/d3.py
@@ -1,9 +1,7 @@
|
||||
import sys
|
||||
from lib import *
|
||||
from lib import get_data
|
||||
from lib import V
|
||||
|
||||
data = open(0).read()
|
||||
|
||||
part_1 = False
|
||||
data = get_data(__file__)
|
||||
|
||||
DIRS = {
|
||||
"^": (-1, 0),
|
||||
@@ -12,30 +10,27 @@ DIRS = {
|
||||
"<": (0, -1),
|
||||
}
|
||||
|
||||
if part_1:
|
||||
pos = (0, 0)
|
||||
poss = set([pos])
|
||||
pos = V(0, 0)
|
||||
poss = set([pos])
|
||||
|
||||
for c in data:
|
||||
for c in data:
|
||||
d = DIRS[c]
|
||||
pos = pos[0] + d[0], pos[1] + d[1]
|
||||
pos = pos + d
|
||||
poss.add(pos)
|
||||
|
||||
print(len(poss))
|
||||
sys.exit(0)
|
||||
print(len(poss))
|
||||
|
||||
a = (0, 0)
|
||||
b = (0, 0)
|
||||
a = V(0, 0)
|
||||
b = V(0, 0)
|
||||
poss = set([a, b])
|
||||
for i, c in enumerate(data):
|
||||
if i % 2 == 0:
|
||||
d = DIRS[c]
|
||||
a = a[0] + d[0], a[1] + d[1]
|
||||
a = a + d
|
||||
poss.add(a)
|
||||
else:
|
||||
d = DIRS[c]
|
||||
b = b[0] + d[0], b[1] + d[1]
|
||||
b = b + d
|
||||
poss.add(b)
|
||||
|
||||
print(len(poss))
|
||||
sys.exit(0)
|
||||
|
||||
15
2015/d4.py
15
2015/d4.py
@@ -1,16 +1,10 @@
|
||||
from lib import *
|
||||
from lib import get_data
|
||||
import hashlib
|
||||
|
||||
part_1 = True
|
||||
data = get_data(__file__).strip()
|
||||
|
||||
if part_1:
|
||||
digits = 5
|
||||
else:
|
||||
digits = 6
|
||||
|
||||
data = open(0).read().strip()
|
||||
|
||||
for i in range(10**9):
|
||||
for digits in [5, 6]:
|
||||
for i in range(10**9):
|
||||
text = data + str(i)
|
||||
md5_hash = hashlib.md5(text.encode()).hexdigest()
|
||||
for c in md5_hash[:digits]:
|
||||
@@ -19,4 +13,3 @@ for i in range(10**9):
|
||||
else:
|
||||
print(i)
|
||||
break
|
||||
|
||||
|
||||
95
2015/d5.py
95
2015/d5.py
@@ -1,53 +1,56 @@
|
||||
from lib import *
|
||||
from lib import get_data
|
||||
|
||||
data = open(0).read()
|
||||
data = get_data(__file__)
|
||||
|
||||
part_1 = True
|
||||
if part_1:
|
||||
res = 0
|
||||
for line in data.splitlines():
|
||||
|
||||
def is_nice(line):
|
||||
# It contains at least three vowels (aeiou only), like aei, xazegov, or aeiouaeiouaeiou.
|
||||
vc = 0
|
||||
for c in line:
|
||||
if c in "aoeui":
|
||||
vc += 1
|
||||
for v in "aeiou":
|
||||
vc += line.count(v)
|
||||
if vc < 3:
|
||||
continue
|
||||
return False
|
||||
|
||||
prev = None
|
||||
for c in line:
|
||||
if c == prev:
|
||||
break
|
||||
prev = c
|
||||
else:
|
||||
continue
|
||||
|
||||
contains = False
|
||||
ss = ["ab", "cd", "pq", "xy"]
|
||||
for s in ss:
|
||||
if s in line:
|
||||
contains = True
|
||||
|
||||
if contains:
|
||||
continue
|
||||
res += 1
|
||||
print(res)
|
||||
else:
|
||||
res = 0
|
||||
for line in data.splitlines():
|
||||
pairs = {}
|
||||
for i in range(0, len(line) - 1):
|
||||
p = line[i:i+2]
|
||||
if p in pairs and i > pairs[p] + 1:
|
||||
break
|
||||
if not p in pairs:
|
||||
pairs[p] = i
|
||||
else:
|
||||
continue
|
||||
|
||||
for i in range(0, len(line) - 2):
|
||||
if line[i] == line[i + 2]:
|
||||
# It contains at least one letter that appears twice in a row, like xx,
|
||||
# abcdde (dd), or aabbccdd (aa, bb, cc, or dd).
|
||||
for i in range(len(line) - 1):
|
||||
if line[i] == line[i + 1]:
|
||||
break
|
||||
else:
|
||||
continue
|
||||
res += 1
|
||||
print(res)
|
||||
return False
|
||||
|
||||
# It does not contain the strings ab, cd, pq, or xy, even if they are
|
||||
# part of one of the other requirements.
|
||||
for i in range(len(line) - 1):
|
||||
cc = "".join(line[i : i + 2])
|
||||
if cc in ["ab", "cd", "pq", "xy"]:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def is_nice_2(line):
|
||||
good = False
|
||||
for i in range(len(line) - 1):
|
||||
cc = "".join(line[i : i + 2])
|
||||
for j in range(i + 2, len(line) - 1):
|
||||
dd = "".join(line[j : j + 2])
|
||||
if cc == dd:
|
||||
good = True
|
||||
if not good:
|
||||
return False
|
||||
|
||||
for i in range(len(line) - 2):
|
||||
cc = "".join(line[i : i + 3])
|
||||
if cc[0] == cc[2]:
|
||||
break
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
t = sum(1 if is_nice(line) else 0 for line in data.splitlines())
|
||||
print(t)
|
||||
|
||||
|
||||
t = sum(1 if is_nice_2(line) else 0 for line in data.splitlines())
|
||||
print(t)
|
||||
|
||||
46
2015/d6.py
46
2015/d6.py
@@ -1,33 +1,29 @@
|
||||
from lib import *
|
||||
from lib import get_data, ints
|
||||
from collections import defaultdict as DD
|
||||
|
||||
data = open(0).read()
|
||||
|
||||
part_2 = True
|
||||
lights = [[0 for _ in range(1000)] for _ in range(1000)]
|
||||
data = get_data(__file__)
|
||||
|
||||
ons = set()
|
||||
ons2 = DD(int)
|
||||
for line in data.splitlines():
|
||||
x1, y1, x2, y2 = str_to_ints(line)
|
||||
x1, y1, x2, y2 = ints(line)
|
||||
for x in range(x1, x2 + 1):
|
||||
for y in range(y1, y2 + 1):
|
||||
if part_2:
|
||||
if "on" in line:
|
||||
lights[x][y] += 1
|
||||
elif "off" in line:
|
||||
lights[x][y] = max(0, lights[x][y] - 1)
|
||||
if "off" in line:
|
||||
if (x, y) in ons:
|
||||
ons.remove((x, y))
|
||||
ons2[(x, y)] = max(ons2[(x, y)] - 1, 0)
|
||||
elif "on" in line:
|
||||
ons.add((x, y))
|
||||
ons2[(x, y)] += 1
|
||||
elif "toggle" in line:
|
||||
if (x, y) in ons:
|
||||
ons.remove((x, y))
|
||||
else:
|
||||
lights[x][y] += 2
|
||||
ons.add((x, y))
|
||||
ons2[(x, y)] += 2
|
||||
else:
|
||||
if "on" in line:
|
||||
lights[x][y] = 1
|
||||
elif "off" in line:
|
||||
lights[x][y] = 0
|
||||
else:
|
||||
if lights[x][y] == 1:
|
||||
lights[x][y] = 0
|
||||
else:
|
||||
lights[x][y] = 1
|
||||
assert False
|
||||
|
||||
res = 0
|
||||
for row in lights:
|
||||
res += sum(row)
|
||||
print(res)
|
||||
print(len(ons))
|
||||
print(sum(ons2.values()))
|
||||
|
||||
126
2015/d7.py
126
2015/d7.py
@@ -1,90 +1,54 @@
|
||||
from lib import *
|
||||
from lib import get_data
|
||||
|
||||
data = open(0).read()
|
||||
data = get_data(__file__)
|
||||
|
||||
part_2 = False
|
||||
if part_2:
|
||||
gates = {"b": 16076}
|
||||
else:
|
||||
gates = {}
|
||||
|
||||
while "a" not in gates:
|
||||
def run(wires={}):
|
||||
def get(a):
|
||||
try:
|
||||
return int(a)
|
||||
except ValueError:
|
||||
pass
|
||||
if a in wires:
|
||||
return wires[a]
|
||||
else:
|
||||
return None
|
||||
|
||||
while "a" not in wires:
|
||||
for line in data.splitlines():
|
||||
lhs, rhs = line.split(" -> ")
|
||||
if part_2 and rhs == "b":
|
||||
if rhs in wires:
|
||||
continue
|
||||
lhs = lhs.strip()
|
||||
if "NOT" in lhs:
|
||||
op, op1 = lhs.split(" ")
|
||||
assert op == "NOT"
|
||||
try:
|
||||
op1 = int(op1)
|
||||
gates[rhs] = ~op1 & 0xffff
|
||||
except ValueError:
|
||||
if op1 in gates and isinstance(gates[op1], int):
|
||||
gates[rhs] = ~gates[op1] & 0xffff
|
||||
elif "OR" in lhs:
|
||||
op1, op, op2 = lhs.split(" ")
|
||||
assert op == "OR"
|
||||
try:
|
||||
op1 = int(op1)
|
||||
except ValueError:
|
||||
if op1 in gates and isinstance(gates[op1], int):
|
||||
op1 = gates[op1]
|
||||
try:
|
||||
op2 = int(op2)
|
||||
except ValueError:
|
||||
if op2 in gates and isinstance(gates[op2], int):
|
||||
op2 = gates[op2]
|
||||
match lhs.split():
|
||||
case [a, "AND", b]:
|
||||
a, b = get(a), get(b)
|
||||
if a is not None and b is not None:
|
||||
wires[rhs] = a & b
|
||||
case [a, "OR", b]:
|
||||
a, b = get(a), get(b)
|
||||
if a is not None and b is not None:
|
||||
wires[rhs] = a | b
|
||||
case [a, "LSHIFT", b]:
|
||||
a, b = get(a), get(b)
|
||||
if a is not None and b is not None:
|
||||
wires[rhs] = a << b
|
||||
case [a, "RSHIFT", b]:
|
||||
a, b = get(a), get(b)
|
||||
if a is not None and b is not None:
|
||||
wires[rhs] = a >> b
|
||||
case ["NOT", a]:
|
||||
a = get(a)
|
||||
if a is not None:
|
||||
wires[rhs] = ~a & 0xFFFF
|
||||
case [a]:
|
||||
a = get(a)
|
||||
if a is not None:
|
||||
wires[rhs] = a
|
||||
return wires
|
||||
|
||||
if not (isinstance(op1, int) and isinstance(op2, int)):
|
||||
continue
|
||||
gates[rhs] = (op1 | op2)
|
||||
elif "AND" in lhs:
|
||||
op1, op, op2 = lhs.split(" ")
|
||||
assert op == "AND"
|
||||
try:
|
||||
op1 = int(op1)
|
||||
except ValueError:
|
||||
if op1 in gates and isinstance(gates[op1], int):
|
||||
op1 = gates[op1]
|
||||
try:
|
||||
op2 = int(op2)
|
||||
except ValueError:
|
||||
if op2 in gates and isinstance(gates[op2], int):
|
||||
op2 = gates[op2]
|
||||
|
||||
if not (isinstance(op1, int) and isinstance(op2, int)):
|
||||
continue
|
||||
gates[rhs] = (op1 & op2)
|
||||
elif "LSHIFT" in lhs:
|
||||
op1, op, op2 = lhs.split(" ")
|
||||
op2 = int(op2)
|
||||
try:
|
||||
op1 = int(op1)
|
||||
except ValueError:
|
||||
if op1 in gates:
|
||||
op1 = gates[op1]
|
||||
else:
|
||||
continue
|
||||
gates[rhs] = (op1 << op2) & 0xffff
|
||||
elif "RSHIFT" in lhs:
|
||||
op1, op, op2 = lhs.split(" ")
|
||||
op2 = int(op2)
|
||||
try:
|
||||
op1 = int(op1)
|
||||
except ValueError:
|
||||
if op1 in gates:
|
||||
op1 = gates[op1]
|
||||
else:
|
||||
continue
|
||||
gates[rhs] = (op1 >> op2) & 0xffff
|
||||
else:
|
||||
try:
|
||||
lhs = int(lhs)
|
||||
gates[rhs] = lhs
|
||||
except ValueError:
|
||||
if lhs in gates:
|
||||
gates[rhs] = gates[lhs]
|
||||
a = run()["a"]
|
||||
print(a)
|
||||
|
||||
print(gates["a"])
|
||||
a2 = run(wires={"b": a})["a"]
|
||||
print(a2)
|
||||
|
||||
29
2015/d8.py
29
2015/d8.py
@@ -1,26 +1,25 @@
|
||||
from lib import *
|
||||
from lib import get_data
|
||||
import re
|
||||
|
||||
data = open(0).read()
|
||||
part_1 = False
|
||||
data = get_data(__file__)
|
||||
|
||||
if part_1:
|
||||
r1 = re.compile(r"\\x[0-9a-f][0-9a-f]")
|
||||
r2 = re.compile(r"\\\\")
|
||||
r3 = re.compile(r"\\\"")
|
||||
r1 = re.compile(r"\\x[0-9a-f][0-9a-f]")
|
||||
r2 = re.compile(r"\\\\")
|
||||
r3 = re.compile(r"\\\"")
|
||||
|
||||
enc, mem = 0, 0
|
||||
for line in data.splitlines():
|
||||
enc, mem = 0, 0
|
||||
for line in data.splitlines():
|
||||
mem += len(line)
|
||||
line = r1.sub("^", line)
|
||||
line = r2.sub("^", line)
|
||||
line = r3.sub("^", line)
|
||||
enc += len(line) - 2
|
||||
print(mem - enc)
|
||||
else:
|
||||
ori, enc = 0, 0
|
||||
for line in data.splitlines():
|
||||
print(mem - enc)
|
||||
|
||||
ori, enc = 0, 0
|
||||
for line in data.splitlines():
|
||||
ori += len(line)
|
||||
line = line.replace("\\", "\\\\")
|
||||
line = line.replace("\"", "\\\"")
|
||||
line = line.replace('"', '\\"')
|
||||
enc += len(line) + 2
|
||||
print(enc - ori)
|
||||
print(enc - ori)
|
||||
|
||||
49
2015/d9.py
49
2015/d9.py
@@ -1,42 +1,27 @@
|
||||
from lib import *
|
||||
from typing import Iterator
|
||||
from lib import get_data
|
||||
from itertools import permutations
|
||||
|
||||
# Just for fun instead of using itertools.permutations.
|
||||
def permutations(xs) -> Iterator:
|
||||
assert len(xs) > 0
|
||||
if len(xs) == 1:
|
||||
yield xs
|
||||
else:
|
||||
x = xs.pop()
|
||||
for p in permutations(xs):
|
||||
for i in range(len(p) + 1):
|
||||
pn = list(p)
|
||||
pn.insert(i, x)
|
||||
yield pn
|
||||
|
||||
part_1 = False
|
||||
data = open(0).read()
|
||||
data = get_data(__file__)
|
||||
|
||||
dists = {}
|
||||
nodes = set()
|
||||
for line in data.splitlines():
|
||||
path, dist = line.split(" = ")
|
||||
dist = int(dist)
|
||||
a, b = path.split(" to ")
|
||||
a, _, b, _, d = line.split()
|
||||
nodes.add(a)
|
||||
nodes.add(b)
|
||||
dist = int(d)
|
||||
dists[(a, b)] = dist
|
||||
dists[(b, a)] = dist
|
||||
|
||||
if part_1:
|
||||
mdist = 10**12
|
||||
for route in permutations(nodes):
|
||||
dist = sum([dists[(route[i], route[i + 1])] for i in range(len(route) - 1)])
|
||||
mdist = min(dist, mdist)
|
||||
print(mdist)
|
||||
else:
|
||||
mdist = 0
|
||||
for route in permutations(nodes):
|
||||
dist = sum([dists[(route[i], route[i + 1])] for i in range(len(route) - 1)])
|
||||
mdist = max(dist, mdist)
|
||||
print(mdist)
|
||||
d_min = 10**12
|
||||
d_max = 0
|
||||
for p in permutations(list(nodes)):
|
||||
d = 0
|
||||
for i in range(len(p) - 1):
|
||||
a, b = p[i], p[i + 1]
|
||||
d += dists[(a, b)]
|
||||
d_min = min(d, d_min)
|
||||
d_max = max(d, d_max)
|
||||
|
||||
print(d_min)
|
||||
print(d_max)
|
||||
|
||||
32
2016/d18.py
Normal file
32
2016/d18.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from lib import *
|
||||
|
||||
row = open(0).read().strip()
|
||||
|
||||
def is_trap(s):
|
||||
assert len(s) == 3
|
||||
match s:
|
||||
case "^^.":
|
||||
return "^"
|
||||
case ".^^":
|
||||
return "^"
|
||||
case "^..":
|
||||
return "^"
|
||||
case "..^":
|
||||
return "^"
|
||||
case _:
|
||||
return "."
|
||||
|
||||
# 400_000 is still easily bruteforcible. If it was a much larget number, we
|
||||
# would have to cache the rows till we get a repeated row. We would then
|
||||
# memorize the number of safe tiles from repeated row to repeated row and jump
|
||||
# forward by multiples of that amount to reach much higher numbers.
|
||||
r = 0
|
||||
for _ in range(400_000):
|
||||
r += row.count(".")
|
||||
nrow = is_trap("." + row[:2])
|
||||
for i in range(1, len(row) - 1):
|
||||
nrow += is_trap(row[i-1:i+2])
|
||||
nrow += is_trap(row[-2:] + ".")
|
||||
row = nrow
|
||||
|
||||
print(r)
|
||||
87
2016/d19.py
Normal file
87
2016/d19.py
Normal file
@@ -0,0 +1,87 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def part_1(data):
|
||||
elf_count = int(data)
|
||||
|
||||
elves = [i for i in range(1, elf_count + 1)]
|
||||
while len(elves) > 1:
|
||||
is_odd = len(elves) % 2 == 1
|
||||
elves = [elves[i] for i in range(0, len(elves), 2)]
|
||||
if is_odd and len(elves) > 1:
|
||||
elves = elves[1:]
|
||||
print(elves[0])
|
||||
|
||||
|
||||
def part_2(data):
|
||||
elf_count = int(data)
|
||||
power_3 = 3
|
||||
while power_3 * 3 < elf_count:
|
||||
power_3 *= 3
|
||||
elf_count -= power_3
|
||||
print(elf_count)
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
def part_2_naiv(data):
|
||||
@dataclass
|
||||
class Elf:
|
||||
id: int
|
||||
prev: Optional["Elf"]
|
||||
next: Optional["Elf"]
|
||||
|
||||
def delete(self):
|
||||
if self.prev is not None:
|
||||
self.prev.next = self.next
|
||||
if self.next is not None:
|
||||
self.next.prev = self.prev
|
||||
self.prev = None
|
||||
self.next = None
|
||||
|
||||
def all(self):
|
||||
elf = self
|
||||
elves = [elf]
|
||||
while elf.next is not None and elf.next.id != self.id:
|
||||
elf = elf.next
|
||||
elves.append(elf)
|
||||
return elves
|
||||
|
||||
def forward(self, n):
|
||||
current = self
|
||||
for _ in range(n):
|
||||
assert current.next is not None
|
||||
current = current.next
|
||||
assert current.id != self.id
|
||||
return current
|
||||
|
||||
def __repr__(self):
|
||||
assert self.prev is not None and self.next is not None
|
||||
return f"{self.prev.id} <- {self.id} -> {self.next.id}"
|
||||
|
||||
elf_count = int(data)
|
||||
# elf_count = 5
|
||||
elves = [Elf(i, None, None) for i in range(1, elf_count + 1)]
|
||||
for i in range(len(elves)):
|
||||
elves[i].prev = elves[(i - 1) % len(elves)]
|
||||
elves[i].next = elves[(i + 1) % len(elves)]
|
||||
|
||||
remaining_elves = elf_count
|
||||
current_elf = elves[0]
|
||||
while remaining_elves > 1:
|
||||
assert current_elf is not None
|
||||
# print(current_elf.all())
|
||||
current_elf.forward(remaining_elves // 2).delete()
|
||||
current_elf = current_elf.next
|
||||
remaining_elves -= 1
|
||||
assert current_elf is not None
|
||||
print(current_elf.id)
|
||||
48
2016/d20.py
Normal file
48
2016/d20.py
Normal file
@@ -0,0 +1,48 @@
|
||||
def part_1(data):
|
||||
ranges = []
|
||||
for line in data.splitlines():
|
||||
l, h = list(map(int, line.split("-")))
|
||||
ranges.append((l, h))
|
||||
|
||||
ranges.sort()
|
||||
merged = list(ranges[0])
|
||||
for i in range(1, len(ranges)):
|
||||
current = ranges[i]
|
||||
if current[0] <= merged[1] + 1:
|
||||
merged[1] = max(current[1], merged[1])
|
||||
else:
|
||||
print(merged[1] + 1)
|
||||
return
|
||||
|
||||
|
||||
def part_2(data):
|
||||
ranges = []
|
||||
for line in data.splitlines():
|
||||
l, h = list(map(int, line.strip().split("-")))
|
||||
ranges.append((l, h))
|
||||
|
||||
ranges.sort()
|
||||
blocked_merged = [list(ranges[0])]
|
||||
for block in ranges:
|
||||
merged = blocked_merged[-1]
|
||||
if block[0] <= merged[1] + 1:
|
||||
merged[1] = max(block[1], merged[1])
|
||||
else:
|
||||
blocked_merged.append(list(block))
|
||||
|
||||
total = 2**32
|
||||
for lo, hi in blocked_merged:
|
||||
total -= (hi - lo + 1)
|
||||
print(total)
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
66
2016/d21.py
Normal file
66
2016/d21.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from itertools import permutations
|
||||
|
||||
|
||||
def part_1(data, text):
|
||||
text = list(text)
|
||||
for line in data.splitlines():
|
||||
words = line.split()
|
||||
# print(text)
|
||||
# print(words)
|
||||
match words:
|
||||
case ["move", "position", a, "to", "position", b]:
|
||||
l = text.pop(int(a))
|
||||
text.insert(int(b), l)
|
||||
case ["rotate", "right", a, "steps"] | ["rotate", "right", a, "step"]:
|
||||
a = int(a)
|
||||
ntext = list(text)
|
||||
for i in range(len(text)):
|
||||
ntext[(i + a) % len(text)] = text[i]
|
||||
text = ntext
|
||||
case ["rotate", "left", a, "steps"] | ["rotate", "left", a, "step"]:
|
||||
a = int(a)
|
||||
ntext = list(text)
|
||||
for i in range(len(text)):
|
||||
ntext[(i - a) % len(text)] = text[i % len(text)]
|
||||
text = ntext
|
||||
case ["swap", "letter", a, "with", "letter", b]:
|
||||
ia = text.index(a)
|
||||
ib = text.index(b)
|
||||
text[ia], text[ib] = text[ib], text[ia]
|
||||
case ["swap", "position", a, "with", "position", b]:
|
||||
ia, ib = int(a), int(b)
|
||||
text[ia], text[ib] = text[ib], text[ia]
|
||||
case ["reverse", "positions", a, "through", b]:
|
||||
a, b = int(a), int(b)
|
||||
assert b > a
|
||||
text[a:b + 1] = reversed(text[a:b + 1])
|
||||
case ["rotate", "based", "on", "position", "of", "letter", c]:
|
||||
i = text.index(c)
|
||||
a = 1 + i + (1 if i >= 4 else 0)
|
||||
ntext = list(text)
|
||||
for i in range(len(text)):
|
||||
ntext[(i + a) % len(text)] = text[i % len(text)]
|
||||
text = ntext
|
||||
case _:
|
||||
assert False
|
||||
return "".join(text)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
pw = "fbgdceah"
|
||||
for p in permutations(pw):
|
||||
if part_1(data, p) == pw:
|
||||
print("".join(p))
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
print(part_1(data, "abcdefgh"))
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
89
2016/d22.py
Normal file
89
2016/d22.py
Normal file
@@ -0,0 +1,89 @@
|
||||
from lib import str_to_ints
|
||||
from dataclasses import dataclass
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
data = """ Filesystem Size Used Avail Use%
|
||||
|
||||
/dev/grid/node-x0-y0 10T 8T 2T 80%
|
||||
/dev/grid/node-x0-y1 11T 6T 5T 54%
|
||||
/dev/grid/node-x0-y2 32T 28T 4T 87%
|
||||
/dev/grid/node-x1-y0 9T 7T 2T 77%
|
||||
/dev/grid/node-x1-y1 8T 0T 8T 0%
|
||||
/dev/grid/node-x1-y2 11T 7T 4T 63%
|
||||
/dev/grid/node-x2-y0 10T 6T 4T 60%
|
||||
/dev/grid/node-x2-y1 9T 8T 1T 88%
|
||||
/dev/grid/node-x2-y2 9T 6T 3T 66%
|
||||
"""
|
||||
|
||||
|
||||
@dataclass
|
||||
class Node:
|
||||
id: int
|
||||
x: int
|
||||
y: int
|
||||
size: int
|
||||
used: int
|
||||
avail: int
|
||||
usep: int
|
||||
|
||||
|
||||
def get_nodes(data):
|
||||
return [Node(i, *str_to_ints(line)) for i, line in enumerate(data.splitlines()[2:])]
|
||||
|
||||
|
||||
def part_1(data):
|
||||
c = 0
|
||||
nodes = get_nodes(data)
|
||||
for a in nodes:
|
||||
for b in nodes:
|
||||
if a is not b and a.used != 0 and a.used <= b.avail:
|
||||
c += 1
|
||||
print(c)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
nodes = get_nodes(data)
|
||||
|
||||
empty = None
|
||||
for n in nodes:
|
||||
if n.usep < 50:
|
||||
assert empty is None
|
||||
empty = (n.x, n.y)
|
||||
assert empty is not None
|
||||
|
||||
grid = defaultdict(dict)
|
||||
for n in nodes:
|
||||
grid[n.x][n.y] = n
|
||||
cols = len(grid)
|
||||
|
||||
steps = 0
|
||||
steps += empty[1] # move space to y=0
|
||||
steps += ((cols - 2) - empty[0]) # move space to x=cols-1
|
||||
|
||||
# shuffle target cell to (0, 0) via empty space
|
||||
steps += ((cols - 2) * 5)
|
||||
steps += 1
|
||||
|
||||
# Account for the fact that there is a "barrier" through which we cannot
|
||||
# move the space. This can be seen by uncommenting the grid print code
|
||||
# below. Might have been better to code a proper search from the beginning.
|
||||
# Clearly he enjoyed setting a bit of a trap there.
|
||||
steps += 15 - 3
|
||||
|
||||
# for y in range(rows):
|
||||
# for x in range(cols):
|
||||
# print(f"{grid[x][y].used:2} ", end='')
|
||||
# print()
|
||||
|
||||
print(steps)
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
87
2016/d23.py
Normal file
87
2016/d23.py
Normal file
@@ -0,0 +1,87 @@
|
||||
from math import factorial
|
||||
|
||||
|
||||
def solve(data, part_2=False):
|
||||
REGS = "abcd"
|
||||
regs = {c: 0 for c in REGS}
|
||||
|
||||
if part_2:
|
||||
print(factorial(12) + (91 * 85))
|
||||
return
|
||||
else:
|
||||
regs["a"] = 7
|
||||
|
||||
insts = data.splitlines()
|
||||
i = 0
|
||||
inst_count = 0
|
||||
while i < len(insts):
|
||||
inst_count += 1
|
||||
# print(i, regs)
|
||||
parts = insts[i].split()
|
||||
cmd = parts[0]
|
||||
if cmd == "cpy":
|
||||
if parts[1] in REGS:
|
||||
regs[parts[2]] = regs[parts[1]]
|
||||
else:
|
||||
regs[parts[2]] = int(parts[1])
|
||||
elif cmd == "jnz":
|
||||
val = 0
|
||||
if parts[1] in REGS:
|
||||
val = regs[parts[1]]
|
||||
else:
|
||||
val = int(parts[1])
|
||||
if val != 0:
|
||||
if parts[2] in REGS:
|
||||
i += regs[parts[2]]
|
||||
else:
|
||||
i += int(parts[2])
|
||||
continue
|
||||
elif cmd == "inc":
|
||||
if insts[i + 1] == "dec d" and insts[i + 2] == "jnz d -2":
|
||||
regs[parts[1]] += regs["d"]
|
||||
regs["d"] = 0
|
||||
i += 3
|
||||
continue
|
||||
regs[parts[1]] += 1
|
||||
elif cmd == "dec":
|
||||
regs[parts[1]] -= 1
|
||||
elif cmd == "tgl":
|
||||
off = 0
|
||||
if parts[1] in REGS:
|
||||
off = regs[parts[1]]
|
||||
else:
|
||||
off = int(parts[1])
|
||||
addr = i + off
|
||||
|
||||
if addr < len(insts):
|
||||
parts = insts[addr].split()
|
||||
if len(parts) == 2:
|
||||
if parts[0] == "inc":
|
||||
parts[0] = "dec"
|
||||
else:
|
||||
parts[0] = "inc"
|
||||
insts[addr] = " ".join(parts)
|
||||
elif len(parts) == 3:
|
||||
if parts[0] == "jnz":
|
||||
parts[0] = "cpy"
|
||||
else:
|
||||
parts[0] = "jnz"
|
||||
insts[addr] = " ".join(parts)
|
||||
else:
|
||||
assert False
|
||||
else:
|
||||
assert False
|
||||
i += 1
|
||||
|
||||
|
||||
print(regs["a"])
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
solve(data)
|
||||
solve(data, True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
61
2016/d24.py
Normal file
61
2016/d24.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from collections import deque, defaultdict
|
||||
from itertools import permutations
|
||||
from lib import Grid2D, INF
|
||||
|
||||
|
||||
def shortest_path(grid, start, end):
|
||||
seen = set()
|
||||
open = deque([(start, 0)])
|
||||
|
||||
while open:
|
||||
current, steps = open.popleft()
|
||||
if current in seen:
|
||||
continue
|
||||
seen.add(current)
|
||||
if current == end:
|
||||
return steps
|
||||
for nb in grid.neighbors_ort(current):
|
||||
if grid[nb] == "#":
|
||||
continue
|
||||
open.append((nb, steps + 1))
|
||||
return None
|
||||
|
||||
|
||||
def solve(data, part_2=False):
|
||||
g = Grid2D(data)
|
||||
# g.print()
|
||||
start = g.find("0")[0]
|
||||
points = g.find_not("#.")
|
||||
|
||||
shortest = defaultdict(defaultdict)
|
||||
for i in range(len(points)):
|
||||
for j in range(i + 1, len(points)):
|
||||
a, b = points[i], points[j]
|
||||
s = shortest_path(g, a, b)
|
||||
assert s is not None
|
||||
shortest[a][b] = s
|
||||
shortest[b][a] = s
|
||||
|
||||
# Brute force all combinations.
|
||||
points.remove(start)
|
||||
dmin = INF
|
||||
for p in permutations(points):
|
||||
d = 0
|
||||
d += shortest[start][p[0]]
|
||||
for i in range(len(p) - 1):
|
||||
d += shortest[p[i]][p[i + 1]]
|
||||
if part_2:
|
||||
d += shortest[p[-1]][start]
|
||||
dmin = min(d, dmin)
|
||||
print(dmin)
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
solve(data)
|
||||
solve(data, True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
60
2016/d25.py
Normal file
60
2016/d25.py
Normal file
@@ -0,0 +1,60 @@
|
||||
def solve(data):
|
||||
REGS = "abcd"
|
||||
goal = [0, 1, 0, 1, 0, 1, 0, 1]
|
||||
|
||||
for a in range(0, 1000):
|
||||
insts = data.splitlines()
|
||||
regs = {c: 0 for c in REGS}
|
||||
regs["a"] = a
|
||||
outs = []
|
||||
i = 0
|
||||
inst_count = 0
|
||||
while i < len(insts):
|
||||
inst_count += 1
|
||||
if inst_count > 100_000:
|
||||
break
|
||||
parts = insts[i].split()
|
||||
cmd = parts[0]
|
||||
if cmd == "cpy":
|
||||
if parts[1] in "abcd":
|
||||
regs[parts[2]] = regs[parts[1]]
|
||||
else:
|
||||
regs[parts[2]] = int(parts[1])
|
||||
elif cmd == "out":
|
||||
if parts[1] in "abcd":
|
||||
v = regs[parts[1]]
|
||||
else:
|
||||
v = int(parts[1])
|
||||
outs.append(v)
|
||||
if len(outs) == len(goal):
|
||||
if outs == goal:
|
||||
print(a)
|
||||
return
|
||||
else:
|
||||
# print(outs)
|
||||
pass
|
||||
elif cmd == "jnz":
|
||||
val = 0
|
||||
if parts[1] in "abcd":
|
||||
val = regs[parts[1]]
|
||||
else:
|
||||
val = int(parts[1])
|
||||
if val != 0:
|
||||
i += int(parts[2])
|
||||
continue
|
||||
elif cmd == "inc":
|
||||
regs[parts[1]] += 1
|
||||
elif cmd == "dec":
|
||||
regs[parts[1]] -= 1
|
||||
else:
|
||||
assert False
|
||||
i += 1
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
solve(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
64
2017/d10.py
Normal file
64
2017/d10.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from lib import str_to_ints
|
||||
|
||||
def part_1(data):
|
||||
ns = list(range(256))
|
||||
lenghts = str_to_ints(data)
|
||||
i = 0
|
||||
skip_size = 0
|
||||
|
||||
for length in lenghts:
|
||||
a, b = i, (i + length) % len(ns)
|
||||
if a <= b:
|
||||
ns[a:b] = reversed(ns[a:b])
|
||||
else:
|
||||
rev = list(reversed(ns[a:] + ns[:b]))
|
||||
ns[a:] = rev[:len(ns) - a]
|
||||
ns[:b] = rev[-b:]
|
||||
i = (i + length + skip_size) % len(ns)
|
||||
skip_size += 1
|
||||
print(ns[0] * ns[1])
|
||||
|
||||
|
||||
def part_2(data):
|
||||
ns = list(range(256))
|
||||
lenghts = list(map(ord, data)) + [17, 31, 73, 47, 23]
|
||||
rounds = 64
|
||||
|
||||
i = 0
|
||||
skip_size = 0
|
||||
for _ in range(rounds):
|
||||
for length in lenghts:
|
||||
assert length < 256
|
||||
a, b = i, (i + length) % len(ns)
|
||||
if a <= b:
|
||||
ns[a:b] = reversed(ns[a:b])
|
||||
else:
|
||||
rev = list(reversed(ns[a:] + ns[:b]))
|
||||
ns[a:] = rev[:len(ns) - a]
|
||||
if b != 0:
|
||||
ns[:b] = rev[-b:]
|
||||
i = (i + length + skip_size) % len(ns)
|
||||
assert len(ns) == 256
|
||||
skip_size += 1
|
||||
|
||||
out = ""
|
||||
for i in range(0, len(ns), 16):
|
||||
r = 0
|
||||
for j in range(i, i + 16):
|
||||
r ^= ns[j]
|
||||
out += f"{r:02x}"
|
||||
return out
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
part_1(data)
|
||||
assert part_2("") == "a2582a3a0e66e6e86e3812dcb672a272"
|
||||
assert part_2("AoC 2017") == "33efeb34ea91902bb2f59c9920caa6cd"
|
||||
assert part_2("1,2,3") == "3efbe78a8d82f29979031a4aa0b16a9d"
|
||||
assert part_2("1,2,4") =="63960835bcdc130f0b66d7ff4f6a5a8e"
|
||||
print(part_2(data))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
52
2017/d11.py
Normal file
52
2017/d11.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from lib import add2
|
||||
from math import ceil
|
||||
|
||||
|
||||
DIR = {
|
||||
"n": (-2, 0),
|
||||
"s": (2, 0),
|
||||
"e": (0, 1),
|
||||
"w": (0, -1),
|
||||
"ne": (-1, 1),
|
||||
"nw": (-1, -1),
|
||||
"se": (1, 1),
|
||||
"sw": (1, -1),
|
||||
}
|
||||
|
||||
|
||||
def steps_to_zero(pos: tuple[int, int]):
|
||||
c = list(map(abs, pos))
|
||||
steps = c[1]
|
||||
c[0] -= c[1]
|
||||
if c[0] > 0:
|
||||
steps += ceil(c[0] / 2)
|
||||
return steps
|
||||
|
||||
|
||||
def part_1(data):
|
||||
c = (0, 0)
|
||||
for d in data.split(","):
|
||||
c = add2(c, DIR[d])
|
||||
return steps_to_zero(c)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
c = (0, 0)
|
||||
steps_max = 0
|
||||
for d in data.split(","):
|
||||
c = add2(c, DIR[d])
|
||||
steps_max = max(steps_to_zero(c), steps_max)
|
||||
return steps_max
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
print(part_1(data))
|
||||
print(part_2(data))
|
||||
assert part_1("ne,ne,ne") == 3
|
||||
assert part_1("ne,ne,sw,sw") == 0
|
||||
assert part_1("ne,ne,s,s") == 2
|
||||
assert part_1("se,sw,se,sw,sw") == 3
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
56
2017/d12.py
Normal file
56
2017/d12.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from lib import str_to_ints
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def get_graph(data):
|
||||
g = defaultdict(set)
|
||||
for line in data.splitlines():
|
||||
fs = str_to_ints(line)
|
||||
l = fs[0]
|
||||
for r in fs[1:]:
|
||||
g[l].add(r)
|
||||
g[r].add(l)
|
||||
return g
|
||||
|
||||
|
||||
def part_1(data):
|
||||
g = get_graph(data)
|
||||
to_visit = [0]
|
||||
seen = set()
|
||||
while to_visit:
|
||||
c = to_visit.pop()
|
||||
if c in seen:
|
||||
continue
|
||||
seen.add(c)
|
||||
for nb in g[c]:
|
||||
to_visit.append(nb)
|
||||
print(len(seen))
|
||||
|
||||
|
||||
def part_2(data):
|
||||
g = get_graph(data)
|
||||
|
||||
group_count = 0
|
||||
seen = set()
|
||||
for c in g.keys():
|
||||
if c in seen:
|
||||
continue
|
||||
group_count += 1
|
||||
to_visit = [c]
|
||||
while to_visit:
|
||||
c = to_visit.pop()
|
||||
if c in seen:
|
||||
continue
|
||||
seen.add(c)
|
||||
for nb in g[c]:
|
||||
to_visit.append(nb)
|
||||
print(group_count)
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
52
2017/d13.py
Normal file
52
2017/d13.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from lib import str_to_ints
|
||||
|
||||
|
||||
def part_1(data):
|
||||
layer_to_depth = {}
|
||||
for line in data.splitlines():
|
||||
a, b = str_to_ints(line)
|
||||
layer_to_depth[a] = b
|
||||
|
||||
layer_to_pos = {l: 0 for l in layer_to_depth.keys()}
|
||||
layer_to_dir = {l: -1 for l in layer_to_depth.keys()}
|
||||
target = max(list(layer_to_depth.keys()))
|
||||
|
||||
r = 0
|
||||
layer = 0
|
||||
while layer <= target:
|
||||
if layer in layer_to_pos and layer_to_pos[layer] == 0:
|
||||
r += (layer * layer_to_depth[layer])
|
||||
|
||||
for k in layer_to_pos.keys():
|
||||
if layer_to_pos[k] == 0 or (layer_to_pos[k] + 1) == layer_to_depth[k]:
|
||||
layer_to_dir[k] *= -1
|
||||
layer_to_pos[k] += layer_to_dir[k]
|
||||
layer += 1
|
||||
print(r)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
eqs = []
|
||||
for line in data.splitlines():
|
||||
layer, depth = str_to_ints(line)
|
||||
period = (depth - 1) * 2
|
||||
eqs.append((layer, period))
|
||||
|
||||
for delay in range(1, 1_000_000_000):
|
||||
for layer, period in eqs:
|
||||
if (delay + layer) % period == 0:
|
||||
break
|
||||
else:
|
||||
print(delay)
|
||||
return
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
72
2017/d14.py
Normal file
72
2017/d14.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from lib import Grid2D
|
||||
from d10 import part_2 as hash
|
||||
|
||||
|
||||
def to_bin(x):
|
||||
assert len(x) == 1
|
||||
xv = int(x, 16)
|
||||
c = 1
|
||||
r = ""
|
||||
for _ in range(4):
|
||||
if c & xv > 0:
|
||||
r = "1" + r
|
||||
else:
|
||||
r = "0" + r
|
||||
c = (c << 1)
|
||||
return r
|
||||
|
||||
def to_bits(xs):
|
||||
r = ""
|
||||
for x in xs:
|
||||
r += to_bin(x)
|
||||
return r
|
||||
|
||||
def part_1(data):
|
||||
r = 0
|
||||
for i in range(128):
|
||||
s = f"{data}-{i}"
|
||||
x = hash(s)
|
||||
b = to_bits(x)
|
||||
r += b.count("1")
|
||||
print(r)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
grid = ""
|
||||
for i in range(128):
|
||||
s = f"{data}-{i}"
|
||||
x = hash(s)
|
||||
grid += to_bits(x)
|
||||
grid += "\n"
|
||||
|
||||
g = Grid2D(grid)
|
||||
|
||||
one_group_count = 0
|
||||
seen_ones = set()
|
||||
all_ones = g.find("1")
|
||||
for c in all_ones:
|
||||
if c in seen_ones:
|
||||
continue
|
||||
one_group_count += 1
|
||||
|
||||
current_group = [c]
|
||||
while current_group:
|
||||
c = current_group.pop()
|
||||
if c in seen_ones:
|
||||
continue
|
||||
seen_ones.add(c)
|
||||
|
||||
for nb in g.neighbors_ort(c):
|
||||
if g[nb] == "1":
|
||||
current_group.append(nb)
|
||||
print(one_group_count)
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
59
2017/d15.py
Normal file
59
2017/d15.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from lib import str_to_int
|
||||
|
||||
|
||||
def part_1(data):
|
||||
fa = 16807
|
||||
fb = 48271
|
||||
m = 2147483647
|
||||
|
||||
xs = []
|
||||
for line in data.splitlines():
|
||||
xs.append(str_to_int(line))
|
||||
a, b = xs
|
||||
|
||||
c = 0
|
||||
for _ in range(40_000_000):
|
||||
a = (a * fa) % m
|
||||
b = (b * fb) % m
|
||||
if (a & 0xffff) == (b & 0xffff):
|
||||
c += 1
|
||||
print(c)
|
||||
|
||||
|
||||
def gen(a, f, m, d):
|
||||
while True:
|
||||
a = (a * f) % m
|
||||
if a % d == 0:
|
||||
yield a
|
||||
|
||||
|
||||
def part_2(data):
|
||||
fa = 16807
|
||||
fb = 48271
|
||||
m = 2147483647
|
||||
|
||||
xs = []
|
||||
for line in data.splitlines():
|
||||
xs.append(str_to_int(line))
|
||||
a, b = xs
|
||||
|
||||
xs = gen(a, fa, m, 4)
|
||||
ys = gen(b, fb, m, 8)
|
||||
|
||||
c = 0
|
||||
for _ in range(5_000_000):
|
||||
a = next(xs)
|
||||
b = next(ys)
|
||||
if (a & 0xffff) == (b & 0xffff):
|
||||
c += 1
|
||||
print(c)
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
60
2017/d16.py
Normal file
60
2017/d16.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from lib import LETTERS_LOWER, str_to_int, str_to_ints
|
||||
|
||||
|
||||
def one_cycle(xs, insts):
|
||||
for inst in insts:
|
||||
if inst.startswith("s"):
|
||||
v = str_to_int(inst)
|
||||
if v != 0:
|
||||
xs = xs[-v:] + xs[:len(xs) - v]
|
||||
elif inst.startswith("x"):
|
||||
a, b = str_to_ints(inst)
|
||||
xs[a], xs[b] = xs[b], xs[a]
|
||||
elif inst.startswith("p"):
|
||||
la, lb = inst[1], inst[3]
|
||||
a, b = xs.index(la), xs.index(lb)
|
||||
xs[a], xs[b] = xs[b], xs[a]
|
||||
else:
|
||||
assert False
|
||||
return xs
|
||||
|
||||
|
||||
def part_1(data):
|
||||
xs = list(LETTERS_LOWER[:16])
|
||||
insts = data.split(",")
|
||||
xs = one_cycle(xs, insts)
|
||||
r = "".join(xs)
|
||||
print(r)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
repeat = 10**9
|
||||
xs = list(LETTERS_LOWER[:16])
|
||||
insts = data.split(",")
|
||||
cycle = 0
|
||||
seen = {}
|
||||
for i in range(repeat):
|
||||
xs = one_cycle(xs, insts)
|
||||
xst = tuple(xs)
|
||||
if not xst in seen:
|
||||
seen[xst] = i
|
||||
else:
|
||||
cycle = (i - seen[xst])
|
||||
break
|
||||
|
||||
xs = list(LETTERS_LOWER[:16])
|
||||
repeat %= cycle
|
||||
for i in range(repeat):
|
||||
xs = one_cycle(xs, insts)
|
||||
r = "".join(xs)
|
||||
print(r)
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
39
2017/d17.py
Normal file
39
2017/d17.py
Normal file
@@ -0,0 +1,39 @@
|
||||
def part_1(data):
|
||||
target = 2017
|
||||
steps = int(data)
|
||||
xs = [0]
|
||||
i = 0
|
||||
for n in range(1, target + 1):
|
||||
i = (i + steps) % len(xs)
|
||||
xs.insert(i + 1, n)
|
||||
i += 1
|
||||
|
||||
for j in range(len(xs)):
|
||||
if xs[j] == target:
|
||||
print(xs[j + 1])
|
||||
return
|
||||
|
||||
|
||||
def part_2(data, m=50_000_000):
|
||||
steps = int(data)
|
||||
len_xs = 1
|
||||
last_zero = None
|
||||
i = 0
|
||||
for n in range(1, m + 1):
|
||||
i = (i + steps) % len_xs
|
||||
if i == 0:
|
||||
last_zero = n
|
||||
len_xs += 1
|
||||
i += 1
|
||||
return last_zero
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
part_1(data)
|
||||
assert part_2(data, 1000) == 531
|
||||
assert part_2(data, 10000) == 2616
|
||||
print(part_2(data))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
109
2017/d18.py
Normal file
109
2017/d18.py
Normal file
@@ -0,0 +1,109 @@
|
||||
from lib import LETTERS_LOWER
|
||||
from collections import defaultdict, deque
|
||||
|
||||
|
||||
def get_value(regs, val):
|
||||
if val in LETTERS_LOWER:
|
||||
return regs[val]
|
||||
return int(val)
|
||||
|
||||
|
||||
def part_1(data):
|
||||
regs = defaultdict(int)
|
||||
insts = data.splitlines()
|
||||
last_sound = None
|
||||
pc = 0
|
||||
while pc <= len(insts):
|
||||
cmds = insts[pc].split()
|
||||
match cmds:
|
||||
case ["set", reg, val]:
|
||||
val = get_value(regs, val)
|
||||
regs[reg] = val
|
||||
case ["mul", reg, val]:
|
||||
val = get_value(regs, val)
|
||||
regs[reg] = regs[reg] * val
|
||||
case ["add", reg, val]:
|
||||
val = get_value(regs, val)
|
||||
regs[reg] = regs[reg] + val
|
||||
case ["mod", reg, val]:
|
||||
val = get_value(regs, val)
|
||||
regs[reg] = regs[reg] % val
|
||||
case ["rcv", val]:
|
||||
if get_value(regs, val) != 0:
|
||||
print(last_sound)
|
||||
return
|
||||
case ["snd", val]:
|
||||
last_sound = get_value(regs, val)
|
||||
case ["jgz", cnd, off]:
|
||||
if get_value(regs, cnd) > 0:
|
||||
pc += get_value(regs, off) # XXX?
|
||||
continue
|
||||
case _:
|
||||
print(cmds)
|
||||
assert False
|
||||
pc += 1
|
||||
|
||||
|
||||
def part_2(data):
|
||||
progs = []
|
||||
insts = data.splitlines()
|
||||
prog1_counter = 0
|
||||
|
||||
for i in [0, 1]:
|
||||
regs = defaultdict(int)
|
||||
regs["p"] = i
|
||||
pc = 0
|
||||
progs.append([pc, regs, False, deque()])
|
||||
|
||||
while not all(prog[2] for prog in progs):
|
||||
for prog in progs:
|
||||
pc, regs, waiting, queue = prog
|
||||
|
||||
cmds = insts[pc].split()
|
||||
match cmds:
|
||||
case ["set", reg, val]:
|
||||
val = get_value(regs, val)
|
||||
regs[reg] = val
|
||||
case ["mul", reg, val]:
|
||||
val = get_value(regs, val)
|
||||
regs[reg] = regs[reg] * val
|
||||
case ["add", reg, val]:
|
||||
val = get_value(regs, val)
|
||||
regs[reg] = regs[reg] + val
|
||||
case ["mod", reg, val]:
|
||||
val = get_value(regs, val)
|
||||
regs[reg] = regs[reg] % val
|
||||
case ["rcv", reg]:
|
||||
if queue:
|
||||
val = queue.popleft()
|
||||
regs[reg] = val
|
||||
else:
|
||||
prog[2] = True
|
||||
pc -= 1
|
||||
case ["snd", val]:
|
||||
val = get_value(regs, val)
|
||||
if prog == progs[0]:
|
||||
other_prog = progs[1]
|
||||
else:
|
||||
prog1_counter += 1
|
||||
other_prog = progs[0]
|
||||
other_prog[3].append(val)
|
||||
case ["jgz", cnd, off]:
|
||||
if get_value(regs, cnd) > 0:
|
||||
pc += get_value(regs, off) # XXX?
|
||||
pc -= 1
|
||||
case _:
|
||||
print(cmds)
|
||||
assert False
|
||||
prog[0] = pc + 1
|
||||
print(prog1_counter)
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
50
2017/d19.py
Normal file
50
2017/d19.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from lib import Grid2D, LETTERS_UPPER, add2, sub2
|
||||
|
||||
|
||||
def part_1(data):
|
||||
g = Grid2D(data)
|
||||
g.print()
|
||||
|
||||
current = None
|
||||
for c in range(g.n_cols):
|
||||
if g[(0, c)] == "|":
|
||||
current = (0, c)
|
||||
|
||||
current_dir = Grid2D.S
|
||||
letters = ""
|
||||
done = False
|
||||
steps = 0
|
||||
|
||||
while not done:
|
||||
while g[current] != " ":
|
||||
current = add2(current, current_dir)
|
||||
steps += 1
|
||||
if g[current] in LETTERS_UPPER:
|
||||
letter = g[current]
|
||||
if letter in letters:
|
||||
done = True
|
||||
break
|
||||
letters += letter
|
||||
|
||||
if g[current] == "+":
|
||||
break
|
||||
|
||||
next_field = None
|
||||
for nb in g.neighbors_ort(current):
|
||||
if nb != sub2(current, current_dir) and (g[nb] in "-|" or g[nb] in LETTERS_UPPER):
|
||||
next_field = nb
|
||||
if next_field is None:
|
||||
done = True
|
||||
else:
|
||||
current_dir = sub2(next_field, current)
|
||||
print(letters)
|
||||
print(steps)
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read()
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
55
2017/d20.py
Normal file
55
2017/d20.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from lib import str_to_ints
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def part_1(data):
|
||||
ps = []
|
||||
for i, line in enumerate(data.splitlines()):
|
||||
ps.append(str_to_ints(line) + [i])
|
||||
|
||||
for _ in range(10_000):
|
||||
for p in ps:
|
||||
p[3] += p[6]
|
||||
p[4] += p[7]
|
||||
p[5] += p[8]
|
||||
p[0] += p[3]
|
||||
p[1] += p[4]
|
||||
p[2] += p[5]
|
||||
|
||||
ps.sort(key=lambda p: abs(p[0]) + abs(p[1]) + abs(p[2]))
|
||||
print(ps[0][-1])
|
||||
|
||||
|
||||
def part_2(data):
|
||||
ps = []
|
||||
for i, line in enumerate(data.splitlines()):
|
||||
ps.append(str_to_ints(line) + [i])
|
||||
|
||||
for _ in range(10_000):
|
||||
poss = defaultdict(list)
|
||||
|
||||
for p in ps:
|
||||
p[3] += p[6]
|
||||
p[4] += p[7]
|
||||
p[5] += p[8]
|
||||
p[0] += p[3]
|
||||
p[1] += p[4]
|
||||
p[2] += p[5]
|
||||
poss[(p[0], p[1], p[2])].append(p)
|
||||
|
||||
for pts in poss.values():
|
||||
if len(pts) > 1:
|
||||
for p in pts:
|
||||
ps.remove(p)
|
||||
|
||||
print(len(ps))
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
110
2017/d21.py
Normal file
110
2017/d21.py
Normal file
@@ -0,0 +1,110 @@
|
||||
IMAGE = """.#.
|
||||
..#
|
||||
###"""
|
||||
|
||||
|
||||
def flipv(pattern):
|
||||
return tuple(map(lambda row: tuple(reversed(row)), pattern))
|
||||
|
||||
|
||||
def fliph(pattern):
|
||||
return tuple(reversed(pattern))
|
||||
|
||||
|
||||
def rot90(pattern):
|
||||
return tuple(map(tuple, map(reversed, tuple(zip(*pattern)))))
|
||||
|
||||
|
||||
def parse_rule(line: str) -> dict:
|
||||
lhs, rhs = line.split(" => ")
|
||||
rules = {}
|
||||
lhs = tuple(map(tuple, lhs.split("/")))
|
||||
rhs = list(map(list, rhs.split("/")))
|
||||
|
||||
rules[lhs] = rhs
|
||||
|
||||
rules[rot90(lhs)] = rhs
|
||||
rules[rot90(rot90((lhs)))] = rhs
|
||||
rules[rot90(rot90(rot90((lhs))))] = rhs
|
||||
|
||||
rules[flipv(lhs)] = rhs
|
||||
rules[fliph(lhs)] = rhs
|
||||
|
||||
rules[rot90(flipv(lhs))] = rhs
|
||||
rules[rot90(rot90(flipv(lhs)))] = rhs
|
||||
rules[rot90(rot90(rot90(flipv(lhs))))] = rhs
|
||||
|
||||
rules[rot90(fliph(lhs))] = rhs
|
||||
rules[rot90(rot90(fliph(lhs)))] = rhs
|
||||
rules[rot90(rot90(rot90(fliph(lhs))))] = rhs
|
||||
|
||||
return rules
|
||||
|
||||
|
||||
def print_image(image):
|
||||
for row in image:
|
||||
print("".join(row))
|
||||
|
||||
|
||||
def slice_get(matrix, row, col, size):
|
||||
r = []
|
||||
for ri in range(row, row + size):
|
||||
r.append(matrix[ri][col:col+size])
|
||||
return r
|
||||
|
||||
|
||||
def slice_set(matrix, row, col, new):
|
||||
for ri, r in enumerate(new):
|
||||
matrix[ri + row][col:col + len(r)] = r
|
||||
|
||||
|
||||
def slice_append(matrix, slice):
|
||||
for ri, row in enumerate(slice):
|
||||
for c in row:
|
||||
matrix[-len(slice) + ri].append(c)
|
||||
|
||||
|
||||
def part_1(data, iterations=5):
|
||||
rules = {}
|
||||
image = list(map(list, IMAGE.splitlines()))
|
||||
for line in data.splitlines():
|
||||
line = line.strip()
|
||||
for k, v in parse_rule(line).items():
|
||||
assert (k not in rules) or rules[k] == v
|
||||
rules[k] = v
|
||||
|
||||
for _ in range(iterations):
|
||||
len_image = len(image)
|
||||
new_image = []
|
||||
if len_image % 2 == 0:
|
||||
for row in range(0, len(image), 2):
|
||||
for _ in range(3):
|
||||
new_image.append([])
|
||||
for col in range(0, len(image[0]), 2):
|
||||
slice = tuple(map(tuple, slice_get(image, row, col, 2)))
|
||||
new_slice = rules[slice]
|
||||
slice_append(new_image, new_slice)
|
||||
else:
|
||||
for row in range(0, len(image), 3):
|
||||
for _ in range(4):
|
||||
new_image.append([])
|
||||
for col in range(0, len(image[0]), 3):
|
||||
slice = tuple(map(tuple, slice_get(image, row, col, 3)))
|
||||
new_slice = rules[slice]
|
||||
slice_append(new_image, new_slice)
|
||||
image = new_image
|
||||
|
||||
count = 0
|
||||
for row in image:
|
||||
count += row.count("#")
|
||||
print(count)
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read()
|
||||
part_1(data)
|
||||
part_1(data, 18)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
71
2017/d22.py
Normal file
71
2017/d22.py
Normal file
@@ -0,0 +1,71 @@
|
||||
from lib import Grid2D, add2
|
||||
|
||||
|
||||
def part_1(data):
|
||||
steps = 10_000
|
||||
|
||||
g = Grid2D(data)
|
||||
# g.print()
|
||||
dirs = [g.N, g.E, g.S, g.W]
|
||||
|
||||
pos = (g.n_rows // 2, g.n_cols // 2)
|
||||
dir = g.N
|
||||
|
||||
inf = set(g.find("#"))
|
||||
|
||||
burst_inf = 0
|
||||
for _ in range(steps):
|
||||
if pos in inf:
|
||||
# turn right
|
||||
dir = dirs[(dirs.index(dir) + 1) % len(dirs)]
|
||||
inf.remove(pos)
|
||||
else:
|
||||
# turn left
|
||||
dir = dirs[(dirs.index(dir) - 1) % len(dirs)]
|
||||
inf.add(pos)
|
||||
burst_inf += 1
|
||||
pos = add2(pos, dir)
|
||||
print(burst_inf)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
steps = 10000000
|
||||
|
||||
g = Grid2D(data)
|
||||
dirs = [g.N, g.E, g.S, g.W]
|
||||
|
||||
pos = (g.n_rows // 2, g.n_cols // 2)
|
||||
dir = g.N
|
||||
|
||||
weak = set()
|
||||
inf = set(g.find("#"))
|
||||
flagged = set()
|
||||
|
||||
burst_inf = 0
|
||||
for _ in range(steps):
|
||||
if pos in weak:
|
||||
weak.remove(pos)
|
||||
inf.add(pos)
|
||||
burst_inf += 1
|
||||
elif pos in inf:
|
||||
dir = dirs[(dirs.index(dir) + 1) % len(dirs)]
|
||||
inf.remove(pos)
|
||||
flagged.add(pos)
|
||||
elif pos in flagged:
|
||||
dir = dirs[(dirs.index(dir) + 2) % len(dirs)]
|
||||
flagged.remove(pos)
|
||||
else:
|
||||
dir = dirs[(dirs.index(dir) - 1) % len(dirs)]
|
||||
weak.add(pos)
|
||||
pos = add2(pos, dir)
|
||||
print(burst_inf)
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
108
2017/d23.py
Normal file
108
2017/d23.py
Normal file
@@ -0,0 +1,108 @@
|
||||
import subprocess
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def print_game(regs, i, insts):
|
||||
print("\033[H\033[J", end="")
|
||||
for j, inst in enumerate(insts):
|
||||
start = " "
|
||||
if j == i:
|
||||
start = "> "
|
||||
print(f"{start} {' '.join(inst)}")
|
||||
for c in "abcdefgh":
|
||||
print(c, regs[c])
|
||||
|
||||
|
||||
def part_1(data):
|
||||
regs = {c: 0 for c in "abcdefgh"}
|
||||
insts = list(map(lambda line: line.split(), data.splitlines()))
|
||||
|
||||
i = 0
|
||||
while i < len(insts):
|
||||
inst = insts[i]
|
||||
match inst:
|
||||
case ["set", reg, val]:
|
||||
regs[reg] = regs[val] if val in regs else int(val)
|
||||
case ["sub", reg, val]:
|
||||
regs[reg] -= regs[val] if val in regs else int(val)
|
||||
case ["mul", reg, val]:
|
||||
regs[reg] *= regs[val] if val in regs else int(val)
|
||||
case ["jnz", reg, val]:
|
||||
val = regs[val] if val in regs else int(val)
|
||||
assert type(val) is int
|
||||
if val != 0:
|
||||
i += (val - 1)
|
||||
case _:
|
||||
assert False
|
||||
i += 1
|
||||
|
||||
|
||||
def to_c(data):
|
||||
insts = list(map(lambda line: line.split(), data.splitlines()))
|
||||
|
||||
label_count = 0
|
||||
labels = defaultdict(list)
|
||||
for i, inst in enumerate(insts):
|
||||
match inst:
|
||||
case ["jnz", reg, val]:
|
||||
label = f"label_{label_count}"
|
||||
label_count += 1
|
||||
inst[2] = label
|
||||
label_i = i + int(val)
|
||||
labels[label_i].append(label)
|
||||
case _:
|
||||
pass
|
||||
|
||||
i = 0
|
||||
begin = ["#include <stdio.h>", "",
|
||||
"int mul(int a, int b) {",
|
||||
" return a * b;",
|
||||
"}",
|
||||
"",
|
||||
"int main() {" ]
|
||||
decls = " int "
|
||||
for v in "abcdefgh":
|
||||
decls += f"{v} = 0, "
|
||||
decls = decls[:-2] + ";"
|
||||
begin.append(decls)
|
||||
body = []
|
||||
end = [ " return 0;", "}", ]
|
||||
|
||||
for i, inst in enumerate(insts):
|
||||
if i in labels:
|
||||
for label in labels[i]:
|
||||
body.append(label + ":")
|
||||
match inst:
|
||||
case ["set", reg, val]:
|
||||
body.append(f" {reg} = {val};")
|
||||
case ["sub", reg, val]:
|
||||
body.append(f" {reg} -= {val};")
|
||||
case ["mul", reg, val]:
|
||||
body.append(f" {reg} = mul({reg}, {val});")
|
||||
case ["jnz", reg, val]:
|
||||
body.append(f" if ({reg} != 0) goto {val};")
|
||||
case _:
|
||||
assert False
|
||||
body.append(labels[i + 1][0] + ":")
|
||||
|
||||
all = begin + body + end
|
||||
with open("i23.c", "w") as f:
|
||||
for line in all:
|
||||
f.write(line + "\n")
|
||||
|
||||
def run_c():
|
||||
subprocess.call(["gcc", "i23.c"])
|
||||
subprocess.call(["./a.out"])
|
||||
subprocess.call(["rm", "a.out"])
|
||||
|
||||
|
||||
def main():
|
||||
with open("i23.txt") as f:
|
||||
data = f.read()
|
||||
# to_c(data)
|
||||
# part_1(data)
|
||||
run_c()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
35
2017/d24.py
Normal file
35
2017/d24.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def part_1(data):
|
||||
ports = [list(map(int, line.split("/"))) for line in data.splitlines()]
|
||||
in_to_out = defaultdict(list)
|
||||
for index, (i, o) in enumerate(ports):
|
||||
in_to_out[i].append((o, index))
|
||||
in_to_out[o].append((i, index))
|
||||
|
||||
max_sum = 0
|
||||
max_len = (0, 0)
|
||||
bs = [(0, [], 0, 0)]
|
||||
|
||||
while bs:
|
||||
current_port, used, current_sum, current_len = bs.pop()
|
||||
max_sum = max(max_sum, current_sum)
|
||||
max_len = max(max_len, (current_len, current_sum))
|
||||
for out_port, index in in_to_out[current_port]:
|
||||
if index in used:
|
||||
continue
|
||||
new_sum = current_sum + current_port + out_port
|
||||
bs.append((out_port, used + [index], new_sum, current_len + 1))
|
||||
print(max_sum)
|
||||
print(max_len[1])
|
||||
|
||||
|
||||
def main():
|
||||
with open("i24.txt") as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
58
2017/d25.py
Normal file
58
2017/d25.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from lib import str_to_int
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def part_1(data):
|
||||
rules = {}
|
||||
steps = None
|
||||
start_state = None
|
||||
current_state = None
|
||||
current_value = None
|
||||
for line in data.splitlines():
|
||||
if line.startswith("In state"):
|
||||
current_state = line[-2]
|
||||
rules[current_state] = {}
|
||||
elif "current value" in line:
|
||||
current_value = str_to_int(line)
|
||||
rules[current_state][current_value] = []
|
||||
elif "- Write the value" in line:
|
||||
rules[current_state][current_value].append(str_to_int(line))
|
||||
elif "- Move one slot to the right" in line:
|
||||
rules[current_state][current_value].append(1)
|
||||
elif "- Move one slot to the left" in line:
|
||||
rules[current_state][current_value].append(-1)
|
||||
elif "Continue with state" in line:
|
||||
rules[current_state][current_value].append(line[-2])
|
||||
elif "Begin in state" in line:
|
||||
start_state = line[-2]
|
||||
elif "checksum after" in line:
|
||||
steps = str_to_int(line)
|
||||
elif line.strip() == "":
|
||||
pass
|
||||
else:
|
||||
print(line)
|
||||
assert False
|
||||
|
||||
assert type(steps) is int
|
||||
assert start_state is not None
|
||||
current_state = start_state
|
||||
tape = defaultdict(int)
|
||||
pos = 0
|
||||
for _ in range(steps):
|
||||
current_value = tape[pos]
|
||||
write_value, move, next_state = rules[current_state][current_value]
|
||||
tape[pos] = write_value
|
||||
pos += move
|
||||
current_state = next_state
|
||||
|
||||
print(sum(tape.values()))
|
||||
|
||||
|
||||
def main():
|
||||
with open("i25.txt") as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
16
2017/d4.py
Normal file
16
2017/d4.py
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
data = open(0).read().strip()
|
||||
|
||||
part_2 = False
|
||||
r = 0
|
||||
words = []
|
||||
for line in data.splitlines():
|
||||
if part_2:
|
||||
words = list(map(lambda w: "".join(sorted(w)), line.split()))
|
||||
else:
|
||||
words = line.split()
|
||||
if len(words) == len(set(words)):
|
||||
r += 1
|
||||
|
||||
print(r)
|
||||
17
2017/d5.py
Normal file
17
2017/d5.py
Normal file
@@ -0,0 +1,17 @@
|
||||
insts = list(map(int, open(0).read().strip().splitlines()))
|
||||
part_2 = True
|
||||
c = 0
|
||||
i = 0
|
||||
while i < len(insts):
|
||||
ci = i
|
||||
i += insts[i]
|
||||
if part_2:
|
||||
if insts[ci] >= 3:
|
||||
insts[ci] -= 1
|
||||
else:
|
||||
insts[ci] += 1
|
||||
else:
|
||||
insts[ci] += 1
|
||||
c += 1
|
||||
print(c)
|
||||
|
||||
27
2017/d6.py
Normal file
27
2017/d6.py
Normal file
@@ -0,0 +1,27 @@
|
||||
data = open(0).read().strip()
|
||||
|
||||
# data = "0 2 7 0"
|
||||
banks = list(map(int, data.split()))
|
||||
seen = dict()
|
||||
seen[tuple(banks)] = 0
|
||||
|
||||
c = 0
|
||||
while True:
|
||||
c += 1
|
||||
max_index = banks.index(max(banks))
|
||||
blocks = banks[max_index]
|
||||
banks[max_index] = 0
|
||||
index = max_index
|
||||
while blocks > 0:
|
||||
index = (index + 1) % len(banks)
|
||||
if index == max_index:
|
||||
continue
|
||||
banks[index] += 1
|
||||
blocks -= 1
|
||||
|
||||
if tuple(banks) in seen:
|
||||
break
|
||||
seen[tuple(banks)] = c
|
||||
|
||||
print(c)
|
||||
print(c - seen[tuple(banks)])
|
||||
63
2017/d7.py
Normal file
63
2017/d7.py
Normal file
@@ -0,0 +1,63 @@
|
||||
from dataclasses import dataclass
|
||||
from collections import defaultdict
|
||||
import sys
|
||||
|
||||
|
||||
@dataclass
|
||||
class Disc:
|
||||
name: str
|
||||
value: int
|
||||
holds: list
|
||||
|
||||
|
||||
discs = {}
|
||||
data = open(0).read().strip()
|
||||
|
||||
|
||||
for line in data.splitlines():
|
||||
right = None
|
||||
if "->" in line:
|
||||
left, right = line.split(" -> ")
|
||||
else:
|
||||
left = line
|
||||
name, value = left.split()
|
||||
value = int(value[1:-1])
|
||||
d = Disc(name, value, [])
|
||||
if right is not None:
|
||||
for r in right.split(", "):
|
||||
d.holds.append(r)
|
||||
discs[d.name] = d
|
||||
|
||||
|
||||
alldiscs = set(discs.keys())
|
||||
for d in discs.values():
|
||||
for h in d.holds:
|
||||
alldiscs.remove(h)
|
||||
assert len(alldiscs) == 1
|
||||
top_disc = alldiscs.pop()
|
||||
print(top_disc)
|
||||
|
||||
|
||||
def weight(disc_name):
|
||||
disc = discs[disc_name]
|
||||
child_weights = {}
|
||||
for child_disc_name in disc.holds:
|
||||
child_weights[child_disc_name] = weight(child_disc_name)
|
||||
|
||||
if not all([w == list(child_weights.values())[0] for w in child_weights.values()]):
|
||||
values = defaultdict(list)
|
||||
for child_name, child_value in child_weights.items():
|
||||
values[child_value].append(child_name)
|
||||
|
||||
same_weight, other_weight = 0, 0
|
||||
for child_weight, children in values.items():
|
||||
if len(children) == 1:
|
||||
other_weight = child_weight
|
||||
else:
|
||||
same_weight = child_weight
|
||||
weight_delta = other_weight - same_weight
|
||||
print(discs['dqwocyn'].value - weight_delta)
|
||||
sys.exit(0)
|
||||
return disc.value + sum(child_weights.values())
|
||||
|
||||
weight(top_disc)
|
||||
28
2017/d8.py
Normal file
28
2017/d8.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def part_1(data):
|
||||
regs = defaultdict(int)
|
||||
allmax = 0
|
||||
for line in data.splitlines():
|
||||
match line.split():
|
||||
case [reg_a, op, value, "if", reg_c, cmp, val_c]:
|
||||
value, val_c = int(value), int(val_c)
|
||||
reg_val = regs[reg_c]
|
||||
if eval(f"{reg_val} {cmp} {val_c}"):
|
||||
value = value if op == "inc" else -value
|
||||
regs[reg_a] += value
|
||||
case _:
|
||||
assert False
|
||||
allmax = max(max(regs.values()), allmax)
|
||||
print(max(regs.values()))
|
||||
print(allmax)
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
33
2017/d9.py
Normal file
33
2017/d9.py
Normal file
@@ -0,0 +1,33 @@
|
||||
def part_1(data):
|
||||
i = 0
|
||||
group_score, garbage_score = 0, 0
|
||||
in_garbage = False
|
||||
group_level = 0
|
||||
while i < len(data):
|
||||
match data[i]:
|
||||
case "{" if not in_garbage:
|
||||
group_level += 1
|
||||
case "}" if not in_garbage:
|
||||
assert group_level > 0
|
||||
group_score += group_level
|
||||
group_level -= 1
|
||||
case "<" if not in_garbage:
|
||||
in_garbage = True
|
||||
case ">" if in_garbage:
|
||||
in_garbage = False
|
||||
case "!" if in_garbage:
|
||||
i += 1
|
||||
case _ if in_garbage:
|
||||
garbage_score += 1
|
||||
i += 1
|
||||
print(group_score)
|
||||
print(garbage_score)
|
||||
|
||||
|
||||
def main():
|
||||
data = open(0).read().strip()
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
65
2017/i23.c
Normal file
65
2017/i23.c
Normal file
@@ -0,0 +1,65 @@
|
||||
#include <stdio.h>
|
||||
|
||||
unsigned int mul_count = 0;
|
||||
|
||||
int mul(int a, int b) {
|
||||
mul_count++;
|
||||
return a * b;
|
||||
}
|
||||
|
||||
int main() {
|
||||
for (int a = 0; a < 2; a++) {
|
||||
int b = 0, c = 0, d = 0, e = 0, f = 0, g = 1, h = 0;
|
||||
b = 67;
|
||||
c = b;
|
||||
if (a != 0) {
|
||||
b = mul(b, 100);
|
||||
b += 100000;
|
||||
c = b;
|
||||
c += 17000;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
f = 1;
|
||||
d = 2;
|
||||
|
||||
while (g != 0)
|
||||
{
|
||||
e = 2;
|
||||
while (g != 0) {
|
||||
if (b % d == 0) {
|
||||
int e_required = b / d;
|
||||
if (e_required >= 2 && e_required <= b) {
|
||||
f = 0;
|
||||
}
|
||||
}
|
||||
mul_count += (b - e);
|
||||
g = 0;
|
||||
}
|
||||
|
||||
d += 1;
|
||||
g = d;
|
||||
g -= b;
|
||||
}
|
||||
|
||||
if (f == 0) {
|
||||
h += 1;
|
||||
}
|
||||
|
||||
g = b;
|
||||
g -= c;
|
||||
if (g != 0) {
|
||||
b += 17;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (a == 0) {
|
||||
printf("%d\n", mul_count);
|
||||
} else {
|
||||
printf("%d\n", h);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -27,6 +27,9 @@ def mape(f, xs):
|
||||
def add2(a: tuple[int, int], b: tuple[int, int]) -> tuple[int, int]:
|
||||
return (a[0] + b[0], a[1] + b[1])
|
||||
|
||||
def sub2(a: tuple[int, int], b: tuple[int, int]) -> tuple[int, int]:
|
||||
return (a[0] - b[0], a[1] - b[1])
|
||||
|
||||
class Grid2D:
|
||||
N = (-1, 0)
|
||||
E = (0, 1)
|
||||
|
||||
28
2018/d1.py
Normal file
28
2018/d1.py
Normal file
@@ -0,0 +1,28 @@
|
||||
def part_1(data):
|
||||
r = 0
|
||||
for x in data.splitlines():
|
||||
r += int(x)
|
||||
print(r)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
seen = set()
|
||||
r = 0
|
||||
while True:
|
||||
for x in data.splitlines():
|
||||
r += int(x)
|
||||
if r in seen:
|
||||
print(r)
|
||||
return
|
||||
seen.add(r)
|
||||
|
||||
|
||||
def main():
|
||||
with open("i1.txt") as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
54
2018/d10.py
Normal file
54
2018/d10.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from lib import str_to_ints
|
||||
|
||||
|
||||
def print_points(objs):
|
||||
x_min, x_max = 10**9, 0
|
||||
y_min, y_max = 10**9, 0
|
||||
|
||||
exist = set()
|
||||
for x, y, _, _ in objs:
|
||||
x_min = min(x_min, x)
|
||||
y_min = min(y_min, y)
|
||||
x_max = max(x_max, x)
|
||||
y_max = max(y_max, y)
|
||||
exist.add((x, y))
|
||||
|
||||
if x_max - x_min > 67:
|
||||
return False
|
||||
|
||||
for y in range(y_min, y_max + 1):
|
||||
s = ""
|
||||
for x in range(x_min, x_max + 1):
|
||||
if (x, y) in exist:
|
||||
s += "X"
|
||||
else:
|
||||
s += " "
|
||||
print(s)
|
||||
return True
|
||||
|
||||
|
||||
def part_1(data):
|
||||
objs = []
|
||||
for line in data.splitlines():
|
||||
x, y, dx, dy = str_to_ints(line)
|
||||
objs.append((x,y, dx, dy))
|
||||
|
||||
for t in range(1_000_000):
|
||||
for i in range(len(objs)):
|
||||
x, y, dx, dy = objs[i]
|
||||
on = (x + dx, y + dy, dx, dy)
|
||||
objs[i] = on
|
||||
if print_points(objs):
|
||||
print(t + 1)
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
input_file = __file__.replace(".py", ".txt")
|
||||
with open(input_file) as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
80
2018/d11.py
Normal file
80
2018/d11.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
@lru_cache
|
||||
def score(serial, x, y):
|
||||
# Find the fuel cell's rack ID, which is its X coordinate plus 10.
|
||||
# Begin with a power level of the rack ID times the Y coordinate.
|
||||
# Increase the power level by the value of the grid serial number (your puzzle input).
|
||||
# Set the power level to itself multiplied by the rack ID.
|
||||
# Keep only the hundreds digit of the power level (so 12345 becomes 3; numbers with no hundreds digit become 0).
|
||||
# Subtract 5 from the power level.
|
||||
rack_id = x + 10
|
||||
power_level = rack_id * y
|
||||
power_level += serial
|
||||
power_level *= rack_id
|
||||
try:
|
||||
return int(str(power_level)[-3]) - 5
|
||||
except IndexError:
|
||||
return -5
|
||||
|
||||
|
||||
def part_1(data):
|
||||
v = int(data.strip())
|
||||
max_val = 0
|
||||
max_coord = None
|
||||
|
||||
assert score(8, 3, 5) == 4
|
||||
assert score(57, 122, 79) == -5
|
||||
assert score(39, 217, 196) == 0
|
||||
|
||||
for x in range(1, 299):
|
||||
for y in range(1, 299):
|
||||
s = 0
|
||||
for x2 in range(x, x + 3):
|
||||
for y2 in range(y, y + 3):
|
||||
s += score(v, x2, y2)
|
||||
if x2 > 300 or y2 > 300:
|
||||
assert False
|
||||
if s > max_val:
|
||||
max_val = s
|
||||
max_coord = (x, y)
|
||||
assert max_coord is not None
|
||||
print(",".join(list(map(str, max_coord))))
|
||||
|
||||
|
||||
def part_2(data):
|
||||
v = int(data.strip())
|
||||
max_val = 0
|
||||
max_coord = None
|
||||
|
||||
# brute force, just tried if 15 is small enough
|
||||
for size in range(2, 15):
|
||||
for x in range(1, 299):
|
||||
for y in range(1, 299):
|
||||
s = 0
|
||||
if x + size > 300 or y + size > 300:
|
||||
break
|
||||
|
||||
for x2 in range(x, x + size):
|
||||
for y2 in range(y, y + size):
|
||||
s += score(v, x2, y2)
|
||||
if x2 > 300 or y2 > 300:
|
||||
assert False
|
||||
if s > max_val:
|
||||
max_val = s
|
||||
max_coord = (x, y, size)
|
||||
assert max_coord is not None
|
||||
print(",".join(list(map(str, max_coord))))
|
||||
|
||||
|
||||
def main():
|
||||
input_file = __file__.replace(".py", ".txt")
|
||||
with open(input_file) as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
60
2018/d12.py
Normal file
60
2018/d12.py
Normal file
@@ -0,0 +1,60 @@
|
||||
def score(state, offset):
|
||||
r = 0
|
||||
for i in range(len(state)):
|
||||
if state[i] == "#":
|
||||
r += i + offset
|
||||
return r
|
||||
|
||||
|
||||
def solve(data, target_i):
|
||||
state = None
|
||||
map = {}
|
||||
for line in data.splitlines():
|
||||
if "initial state" in line:
|
||||
_, rest = line.split(":")
|
||||
state = rest.strip()
|
||||
if "=>" in line:
|
||||
lhs, rhs = line.split(" => ")
|
||||
assert lhs not in map
|
||||
map[lhs] = rhs
|
||||
|
||||
assert state is not None
|
||||
seen = {}
|
||||
offset = 0
|
||||
for i in range(target_i):
|
||||
while not state.startswith("...."):
|
||||
state = "." + state
|
||||
offset -= 1
|
||||
while not state.endswith("...."):
|
||||
state += "."
|
||||
|
||||
if state in seen:
|
||||
prev_i, prev_score = seen[state]
|
||||
assert (i - prev_i) == 1
|
||||
current_score = score(state, offset)
|
||||
delta_score = current_score - prev_score
|
||||
final_score = current_score + (target_i - i) * delta_score
|
||||
print(final_score)
|
||||
return
|
||||
else:
|
||||
seen[state] = (i, score(state, offset))
|
||||
|
||||
ns = ""
|
||||
for i in range(2, len(state) - 2):
|
||||
s = state[i - 2 : i + 3]
|
||||
ns += map[s]
|
||||
state = ns
|
||||
offset += 2
|
||||
print(score(state, offset))
|
||||
|
||||
|
||||
def main():
|
||||
input_file = __file__.replace(".py", ".txt")
|
||||
with open(input_file) as f:
|
||||
data = f.read()
|
||||
solve(data, 20)
|
||||
solve(data, 50 * 10**9)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
102
2018/d13.py
Normal file
102
2018/d13.py
Normal file
@@ -0,0 +1,102 @@
|
||||
from lib import Grid2D, add2
|
||||
|
||||
CARTS = (
|
||||
("^", Grid2D.N, "|"),
|
||||
("v", Grid2D.S, "|"),
|
||||
(">", Grid2D.E, "-"),
|
||||
("<", Grid2D.W, "-"),
|
||||
)
|
||||
TURNS = "lsr"
|
||||
DIRS = [(-1, 0), (0, 1), (1, 0), (0, -1)]
|
||||
|
||||
|
||||
def part_1(data):
|
||||
g = Grid2D(data)
|
||||
first_shown = False
|
||||
carts = []
|
||||
occupied = set()
|
||||
|
||||
for c, d, r in CARTS:
|
||||
for cart in g.find(c):
|
||||
carts.append((cart, d, "l"))
|
||||
g[cart] = r
|
||||
occupied.add(cart)
|
||||
|
||||
for _ in range(1, 100_000):
|
||||
carts = sorted(carts)
|
||||
|
||||
ncarts = []
|
||||
i_to_skip = []
|
||||
for i in range(len(carts)):
|
||||
if i in i_to_skip:
|
||||
continue
|
||||
|
||||
pos, dir, turn = carts[i]
|
||||
occupied.remove(pos)
|
||||
pos = add2(pos, dir)
|
||||
|
||||
# detect collision and remove carts
|
||||
if pos in occupied:
|
||||
if first_shown is False:
|
||||
print(f"{pos[1]},{pos[0]}")
|
||||
first_shown = True
|
||||
# either from carts that haven't moved, yet
|
||||
for j in range(len(carts)):
|
||||
jpos, _, _ = carts[j]
|
||||
if jpos == pos:
|
||||
i_to_skip.append(j)
|
||||
break
|
||||
else:
|
||||
# or from carts that have already moved
|
||||
ncarts = list(filter(lambda c: c[0] != pos, ncarts))
|
||||
occupied.remove(pos)
|
||||
continue
|
||||
else:
|
||||
occupied.add(pos)
|
||||
|
||||
if g[pos] == "+":
|
||||
if turn == "l":
|
||||
dir = DIRS[(DIRS.index(dir) - 1) % len(DIRS)]
|
||||
elif turn == "r":
|
||||
dir = DIRS[(DIRS.index(dir) + 1) % len(DIRS)]
|
||||
turn = TURNS[(TURNS.index(turn) + 1) % len(TURNS)]
|
||||
elif g[pos] == "/":
|
||||
if dir == g.E:
|
||||
dir = g.N
|
||||
elif dir == g.W:
|
||||
dir = g.S
|
||||
elif dir == g.N:
|
||||
dir = g.E
|
||||
elif dir == g.S:
|
||||
dir = g.W
|
||||
else:
|
||||
assert False
|
||||
elif g[pos] == "\\":
|
||||
if dir == g.E:
|
||||
dir = g.S
|
||||
elif dir == g.W:
|
||||
dir = g.N
|
||||
elif dir == g.N:
|
||||
dir = g.W
|
||||
elif dir == g.S:
|
||||
dir = g.E
|
||||
else:
|
||||
assert False
|
||||
|
||||
ncarts.append((pos, dir, turn))
|
||||
carts = ncarts
|
||||
if len(carts) == 1:
|
||||
pos = carts[0][0]
|
||||
print(f"{pos[1]},{pos[0]}")
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
input_file = __file__.replace(".py", ".txt")
|
||||
with open(input_file) as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
62
2018/d14.py
Normal file
62
2018/d14.py
Normal file
@@ -0,0 +1,62 @@
|
||||
def part_1(data):
|
||||
num = int(data.strip())
|
||||
recipes = [3, 7]
|
||||
i, j = 0, 1
|
||||
|
||||
while True:
|
||||
s = recipes[i] + recipes[j]
|
||||
ss = str(s)
|
||||
if len(ss) == 1:
|
||||
recipes.append(s)
|
||||
elif len(ss) == 2:
|
||||
recipes.append(int(ss[0]))
|
||||
recipes.append(int(ss[1]))
|
||||
else:
|
||||
assert False
|
||||
|
||||
i = (i + 1 + recipes[i]) % len(recipes)
|
||||
j = (j + 1 + recipes[j]) % len(recipes)
|
||||
if i == j:
|
||||
j = (j + 1) % len(recipes)
|
||||
if len(recipes) > num + 10:
|
||||
print("".join(map(str, recipes[num : num + 10])))
|
||||
break
|
||||
|
||||
|
||||
def part_2(data):
|
||||
target = list(map(int, data.strip()))
|
||||
recipes = [3, 7]
|
||||
|
||||
i, j = 0, 1
|
||||
while True:
|
||||
s = recipes[i] + recipes[j]
|
||||
if s < 10:
|
||||
recipes.append(s)
|
||||
elif s < 100:
|
||||
recipes.append(s // 10)
|
||||
recipes.append(s % 10)
|
||||
else:
|
||||
assert False
|
||||
|
||||
i = (i + 1 + recipes[i]) % len(recipes)
|
||||
j = (j + 1 + recipes[j]) % len(recipes)
|
||||
if i == j:
|
||||
j = (j + 1) % len(recipes)
|
||||
|
||||
if recipes[-len(target) :] == target:
|
||||
print(len(recipes) - len(target))
|
||||
elif recipes[-len(target) - 1 : -1] == target:
|
||||
print(len(recipes) - len(target) - 1)
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
input_file = __file__.replace(".py", ".txt")
|
||||
with open(input_file) as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
117
2018/d15.py
Normal file
117
2018/d15.py
Normal file
@@ -0,0 +1,117 @@
|
||||
from lib import Grid2D, add2
|
||||
from collections import deque
|
||||
|
||||
|
||||
DIRS = [Grid2D.N, Grid2D.W, Grid2D.E, Grid2D.S]
|
||||
|
||||
|
||||
def find_move(unit, grid):
|
||||
assert grid[unit] in "GE"
|
||||
target_fields = set()
|
||||
enemies = grid.find("G") if grid[unit] == "E" else grid.find("E")
|
||||
for e in enemies:
|
||||
for dir in DIRS:
|
||||
nb = add2(e, dir)
|
||||
if grid[nb] == ".":
|
||||
target_fields.add(nb)
|
||||
|
||||
# we are already next to an enemy
|
||||
if unit in target_fields:
|
||||
return None
|
||||
|
||||
seen = set()
|
||||
states = deque([(unit, [])])
|
||||
while states:
|
||||
pos, hist = states.popleft()
|
||||
|
||||
if pos in seen:
|
||||
continue
|
||||
else:
|
||||
seen.add(pos)
|
||||
|
||||
for dir in DIRS:
|
||||
nb = add2(pos, dir)
|
||||
if nb in target_fields:
|
||||
if hist:
|
||||
return hist[0]
|
||||
else:
|
||||
return nb
|
||||
elif grid[nb] == ".":
|
||||
nhist = hist + [nb]
|
||||
states.append((nb, nhist))
|
||||
|
||||
# we did not find a path to an enemy
|
||||
return None
|
||||
|
||||
|
||||
def part_2(data):
|
||||
for elf_attack in range(3, 100):
|
||||
g = Grid2D(data)
|
||||
pos_to_health = {pos: 200 for pos in g.find("GE")}
|
||||
len_elfs_orig = len(g.find("E"))
|
||||
|
||||
rounds = 0
|
||||
done = False
|
||||
while not done:
|
||||
for unit in sorted(list(pos_to_health.keys())):
|
||||
if not unit in pos_to_health:
|
||||
continue # unit died in the meantime
|
||||
enemy_type = "G" if g[unit] == "E" else "E"
|
||||
|
||||
# move stage
|
||||
for dir in DIRS:
|
||||
nb = add2(unit, dir)
|
||||
if g[nb] == enemy_type:
|
||||
break # we are already next to an enemy
|
||||
else:
|
||||
npos = find_move(unit, g)
|
||||
if npos is not None:
|
||||
assert npos not in pos_to_health and g[npos] == "."
|
||||
pos_to_health[npos] = pos_to_health[unit]
|
||||
del pos_to_health[unit]
|
||||
g[npos] = g[unit]
|
||||
g[unit] = "."
|
||||
unit = npos
|
||||
|
||||
# attack stage
|
||||
enemy_type = "G" if g[unit] == "E" else "E"
|
||||
enemy_hit = 999
|
||||
enemy = None
|
||||
for dir in DIRS:
|
||||
nb = add2(unit, dir)
|
||||
if g[nb] == enemy_type and pos_to_health[nb] < enemy_hit:
|
||||
enemy_hit = pos_to_health[nb]
|
||||
enemy = nb
|
||||
|
||||
if enemy is not None:
|
||||
if g[enemy] == "G":
|
||||
pos_to_health[enemy] -= elf_attack
|
||||
else:
|
||||
pos_to_health[enemy] -= 3
|
||||
if pos_to_health[enemy] <= 0:
|
||||
del pos_to_health[enemy]
|
||||
g[enemy] = "."
|
||||
|
||||
if len(g.find("E")) == 0 or len(g.find("G")) == 0:
|
||||
done = True
|
||||
break
|
||||
else:
|
||||
rounds += 1
|
||||
|
||||
if elf_attack == 3:
|
||||
print(sum(pos_to_health.values()) * rounds)
|
||||
|
||||
if len(g.find("E")) == len_elfs_orig:
|
||||
print(sum(pos_to_health.values()) * (rounds))
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
input_file = __file__.replace(".py", ".txt")
|
||||
with open(input_file) as f:
|
||||
data = f.read()
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
147
2018/d16.py
Normal file
147
2018/d16.py
Normal file
@@ -0,0 +1,147 @@
|
||||
from lib import str_to_ints, get_data
|
||||
|
||||
|
||||
def addr(regs, a, b, c):
|
||||
regs[c] = regs[a] + regs[b]
|
||||
|
||||
|
||||
def addi(regs, a, b, c):
|
||||
regs[c] = regs[a] + b
|
||||
|
||||
|
||||
def mulr(regs, a, b, c):
|
||||
regs[c] = regs[a] * regs[b]
|
||||
|
||||
|
||||
def muli(regs, a, b, c):
|
||||
regs[c] = regs[a] * b
|
||||
|
||||
|
||||
def banr(regs, a, b, c):
|
||||
regs[c] = regs[a] & regs[b]
|
||||
|
||||
|
||||
def bani(regs, a, b, c):
|
||||
regs[c] = regs[a] & b
|
||||
|
||||
|
||||
def borr(regs, a, b, c):
|
||||
regs[c] = regs[a] | regs[b]
|
||||
|
||||
|
||||
def bori(regs, a, b, c):
|
||||
regs[c] = regs[a] | b
|
||||
|
||||
|
||||
def setr(regs, a, _, c):
|
||||
regs[c] = regs[a]
|
||||
|
||||
|
||||
def seti(regs, a, _, c):
|
||||
regs[c] = a
|
||||
|
||||
|
||||
def gtir(regs, a, b, c):
|
||||
regs[c] = 1 if a > regs[b] else 0
|
||||
|
||||
|
||||
def gtri(regs, a, b, c):
|
||||
regs[c] = 1 if regs[a] > b else 0
|
||||
|
||||
|
||||
def gtrr(regs, a, b, c):
|
||||
regs[c] = 1 if regs[a] > regs[b] else 0
|
||||
|
||||
|
||||
def eqir(regs, a, b, c):
|
||||
regs[c] = 1 if a == regs[b] else 0
|
||||
|
||||
|
||||
def eqri(regs, a, b, c):
|
||||
regs[c] = 1 if regs[a] == b else 0
|
||||
|
||||
|
||||
def eqrr(regs, a, b, c):
|
||||
regs[c] = 1 if regs[a] == regs[b] else 0
|
||||
|
||||
|
||||
OPS = [
|
||||
addr,
|
||||
addi,
|
||||
mulr,
|
||||
muli,
|
||||
banr,
|
||||
bani,
|
||||
borr,
|
||||
bori,
|
||||
setr,
|
||||
seti,
|
||||
gtir,
|
||||
gtri,
|
||||
gtrr,
|
||||
eqir,
|
||||
eqri,
|
||||
eqrr,
|
||||
]
|
||||
|
||||
|
||||
def part_1(data):
|
||||
examples = []
|
||||
|
||||
lines = data.splitlines()
|
||||
test_prog = []
|
||||
last_i = None
|
||||
for i in range(len(lines)):
|
||||
line = lines[i]
|
||||
if line.startswith("Before:"):
|
||||
regs = str_to_ints(line)
|
||||
inst = str_to_ints(lines[i + 1])
|
||||
regs_after = str_to_ints(lines[i + 2])
|
||||
examples.append((regs, inst, regs_after))
|
||||
last_i = i + 2
|
||||
|
||||
assert last_i is not None
|
||||
for line in lines[last_i:]:
|
||||
if line.strip() != "":
|
||||
test_prog.append(str_to_ints(line))
|
||||
|
||||
r = 0
|
||||
for before_orig, inst, after in examples:
|
||||
ops_correct = []
|
||||
for op in OPS:
|
||||
before = list(before_orig)
|
||||
op(before, *inst[1:])
|
||||
if before == after:
|
||||
ops_correct.append(op)
|
||||
if len(ops_correct) >= 3:
|
||||
r += 1
|
||||
print(r)
|
||||
|
||||
code_to_op = {}
|
||||
while len(OPS) > 0:
|
||||
for before_orig, inst, after in examples:
|
||||
ops_correct = []
|
||||
for op in OPS:
|
||||
before = list(before_orig)
|
||||
op(before, *inst[1:])
|
||||
if before == after:
|
||||
ops_correct.append(op)
|
||||
if len(ops_correct) == 1:
|
||||
code_to_op[inst[0]] = ops_correct[0]
|
||||
OPS.remove(ops_correct[0])
|
||||
|
||||
regs = [0, 0, 0, 0]
|
||||
for line in test_prog:
|
||||
op_code = line[0]
|
||||
vals = line[1:]
|
||||
code_to_op[op_code](regs, *vals)
|
||||
print(regs[0])
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
136
2018/d17.py
Normal file
136
2018/d17.py
Normal file
@@ -0,0 +1,136 @@
|
||||
from lib import get_data, str_to_ints, add2
|
||||
|
||||
|
||||
def above(p):
|
||||
return add2(p, (0, -1))
|
||||
|
||||
|
||||
def below(p):
|
||||
return add2(p, (0, 1))
|
||||
|
||||
|
||||
def right(p):
|
||||
return add2(p, (1, 0))
|
||||
|
||||
|
||||
def left(p):
|
||||
return add2(p, (-1, 0))
|
||||
|
||||
|
||||
def print2d(xs, ys=[], zs=[], cs=".#~|"):
|
||||
xs, ys, zs = set(xs), set(ys), set(zs)
|
||||
all = xs | ys | zs
|
||||
x_min, x_max, y_min, y_max = 10**9, 0, 10**9, 0
|
||||
for x, y in all:
|
||||
x_min = min(x_min, x)
|
||||
y_min = min(y_min, y)
|
||||
x_max = max(x_max, x)
|
||||
y_max = max(y_max, y)
|
||||
for y in range(y_min, y_max + 1):
|
||||
row = ""
|
||||
for x in range(x_min, x_max + 1):
|
||||
if (x, y) in xs:
|
||||
row += cs[1]
|
||||
elif (x, y) in ys:
|
||||
row += cs[2]
|
||||
elif (x, y) in zs:
|
||||
row += cs[3]
|
||||
else:
|
||||
row += cs[0]
|
||||
print(row)
|
||||
|
||||
|
||||
def part_1(data):
|
||||
sand = []
|
||||
max_y = 0
|
||||
min_y = 10**9
|
||||
for line in data.splitlines():
|
||||
line = line.strip()
|
||||
a, b, c = str_to_ints(line)
|
||||
if line.startswith("x"):
|
||||
x = a
|
||||
for y in range(b, c + 1):
|
||||
max_y = max(max_y, y)
|
||||
min_y = min(min_y, y)
|
||||
sand.append((x, y))
|
||||
elif line.startswith("y"):
|
||||
y = a
|
||||
max_y = max(max_y, y)
|
||||
min_y = min(min_y, y)
|
||||
for x in range(b, c + 1):
|
||||
sand.append((x, y))
|
||||
else:
|
||||
assert False
|
||||
|
||||
# TODO: probably should use 2D array for better perf
|
||||
sources = [(500, 0)]
|
||||
water = set()
|
||||
sand = set(sand)
|
||||
water_rest = set()
|
||||
|
||||
while sources:
|
||||
source = sources.pop()
|
||||
current = below(source)
|
||||
while current[1] <= max_y:
|
||||
if current[1] < min_y:
|
||||
current = below(current)
|
||||
elif current in water_rest:
|
||||
break
|
||||
elif below(current) in sand or below(current) in water_rest:
|
||||
water.add(current)
|
||||
c = current
|
||||
to_water_rest = [current]
|
||||
left_sand, right_sand = False, False
|
||||
while True:
|
||||
c = right(c)
|
||||
if c in sand:
|
||||
right_sand = True
|
||||
break
|
||||
elif below(c) in sand or below(c) in water_rest:
|
||||
water.add(c)
|
||||
to_water_rest.append(c)
|
||||
else:
|
||||
# it's empty underneath
|
||||
water.add(c)
|
||||
sources.append(c)
|
||||
break
|
||||
|
||||
c = current
|
||||
while True:
|
||||
c = left(c)
|
||||
if c in sand:
|
||||
left_sand = True
|
||||
break
|
||||
elif below(c) in sand or below(c) in water_rest:
|
||||
water.add(c)
|
||||
to_water_rest.append(c)
|
||||
else:
|
||||
# it's empty underneath
|
||||
water.add(c)
|
||||
sources.append(c)
|
||||
break
|
||||
|
||||
if left_sand and right_sand:
|
||||
sources.append(above(above(current)))
|
||||
for w in to_water_rest:
|
||||
water_rest.add(w)
|
||||
break
|
||||
else:
|
||||
water.add(current)
|
||||
current = below(current)
|
||||
|
||||
# print2d(sand, water_rest, water)
|
||||
# print(sources)
|
||||
# input()
|
||||
current_water = len(water_rest | water)
|
||||
print(current_water)
|
||||
print(len(water_rest))
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
68
2018/d18.py
Normal file
68
2018/d18.py
Normal file
@@ -0,0 +1,68 @@
|
||||
from lib import get_data, Grid2D
|
||||
from copy import deepcopy
|
||||
|
||||
|
||||
def part_2(data, target=10):
|
||||
g = Grid2D(data)
|
||||
seen = {}
|
||||
i = 0
|
||||
while i < target:
|
||||
ng = deepcopy(g)
|
||||
for r in range(g.n_rows):
|
||||
for c in range(g.n_cols):
|
||||
if g[(r, c)] == ".":
|
||||
tree_count = sum(
|
||||
[1 if g[nb] == "|" else 0 for nb in g.neighbors_adj((r, c))]
|
||||
)
|
||||
if tree_count >= 3:
|
||||
ng[(r, c)] = "|"
|
||||
elif g[(r, c)] == "|":
|
||||
lumber_count = sum(
|
||||
[1 if g[nb] == "#" else 0 for nb in g.neighbors_adj((r, c))]
|
||||
)
|
||||
if lumber_count >= 3:
|
||||
ng[(r, c)] = "#"
|
||||
elif g[(r, c)] == "#":
|
||||
tree_count = sum(
|
||||
[1 if g[nb] == "|" else 0 for nb in g.neighbors_adj((r, c))]
|
||||
)
|
||||
lumber_count = sum(
|
||||
[1 if g[nb] == "#" else 0 for nb in g.neighbors_adj((r, c))]
|
||||
)
|
||||
if tree_count > 0 and lumber_count > 0:
|
||||
pass
|
||||
else:
|
||||
ng[(r, c)] = "."
|
||||
else:
|
||||
assert False
|
||||
|
||||
h = ng.hash()
|
||||
if h in seen:
|
||||
delta = i - seen[h]
|
||||
while (i + delta) < target:
|
||||
i += delta
|
||||
seen = {}
|
||||
else:
|
||||
seen[h] = i
|
||||
i += 1
|
||||
g = ng
|
||||
|
||||
w = 0
|
||||
l = 0
|
||||
for r in range(g.n_rows):
|
||||
for c in range(g.n_cols):
|
||||
if g[(r, c)] == "|":
|
||||
w += 1
|
||||
elif g[(r, c)] == "#":
|
||||
l += 1
|
||||
print(l * w)
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_2(data, 10)
|
||||
part_2(data, 1000000000)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
134
2018/d19.py
Normal file
134
2018/d19.py
Normal file
@@ -0,0 +1,134 @@
|
||||
from lib import get_data, str_to_ints
|
||||
import d16
|
||||
|
||||
|
||||
def print_regs(regs):
|
||||
print(" ".join([f"regs[{i}]={regs[i]:10}" for i in range(len(regs))]) + " (ip)")
|
||||
|
||||
|
||||
def print_inst(i, insts, ip):
|
||||
op, arg1, arg2, dest = insts[i]
|
||||
dest = f"regs[{dest}]" if dest != ip else "ip"
|
||||
match op:
|
||||
case "addi":
|
||||
print(f"{i:2} {dest} = regs[{arg1}] + {arg2}")
|
||||
case "addr":
|
||||
print(f"{i:2} {dest} = regs[{arg1}] + regs[{arg2}]")
|
||||
case "muli":
|
||||
print(f"{i:2} {dest} = regs[{arg1}] * {arg2}")
|
||||
case "mulr":
|
||||
print(f"{i:2} {dest} = regs[{arg1}] * regs[{arg2}]")
|
||||
case "seti":
|
||||
print(f"{i:2} {dest} = {arg1}")
|
||||
case "setr":
|
||||
print(f"{i:2} {dest} = regs[{arg1}]")
|
||||
case "eqrr":
|
||||
print(f"{i:2} {dest} = (regs[{arg1}] == regs[{arg2}]) ? 1 : 0")
|
||||
case "gtrr":
|
||||
print(f"{i:2} {dest} = (regs[{arg1}] > regs[{arg2}]) ? 1 : 0")
|
||||
case _:
|
||||
print(f"{i:2} {dest} = {op}({arg1}, {arg2})")
|
||||
|
||||
|
||||
def sum_of_divisors(n):
|
||||
total = 1
|
||||
for i in range(2, int(n**0.5) + 1):
|
||||
if n % i == 0:
|
||||
total += i
|
||||
if i != n // i: # do not count square root twice
|
||||
total += n // i
|
||||
if n != 1:
|
||||
total += n
|
||||
return total
|
||||
|
||||
|
||||
def run(data, break_after=None, reg_zero_init=0):
|
||||
ip = None
|
||||
regs = [0 for _ in range(6)]
|
||||
regs[0] = reg_zero_init
|
||||
|
||||
insts = []
|
||||
for line in data.splitlines():
|
||||
if line.startswith("#"):
|
||||
(ip,) = str_to_ints(line)
|
||||
else:
|
||||
fs = line.split()
|
||||
vals = str_to_ints(line)
|
||||
insts.append([fs[0]] + vals)
|
||||
|
||||
count = 0
|
||||
assert ip is not None
|
||||
while regs[ip] < len(insts):
|
||||
if break_after is not None and count > break_after:
|
||||
break
|
||||
inst = insts[regs[ip]]
|
||||
f = getattr(d16, inst[0])
|
||||
f(regs, *inst[1:])
|
||||
regs[ip] += 1
|
||||
count += 1
|
||||
return regs
|
||||
|
||||
|
||||
def part_1(data):
|
||||
regs = run(data)
|
||||
print(regs[0])
|
||||
|
||||
|
||||
def part_2(data):
|
||||
regs = run(data, 10_000, 1)
|
||||
# by analysing the code we can see that the int computer counts the sum of
|
||||
# the number of divisors
|
||||
print(sum_of_divisors(regs[1]))
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
listing = """
|
||||
ip=5
|
||||
0 ip = regs[5] + 16
|
||||
1 regs[2] = 1
|
||||
2 regs[4] = 1
|
||||
3 regs[3] = regs[2] * regs[4] |
|
||||
4 regs[3] = (regs[3] == regs[1]) ? 1 : 0 |
|
||||
5 ip = regs[3] + regs[5] | jump to 7 if regs[3] == regs[1]
|
||||
6 ip = regs[5] + 1 | skip 7
|
||||
7 regs[0] = regs[2] + regs[0]
|
||||
8 regs[4] = regs[4] + 1 | regs[4] += 1
|
||||
9 regs[3] = (regs[4] > regs[1]) ? 1 : 0 |
|
||||
10 ip = regs[5] + regs[3] | jump to 12 if regs[4] > regs[1]
|
||||
11 ip = 2 | goto 3
|
||||
12 regs[2] = regs[2] + 1 | regs[2] += 1
|
||||
13 regs[3] = (regs[2] > regs[1]) ? 1 : 0 |
|
||||
14 ip = regs[3] + regs[5] | jump to 16 if regs[2] > regs[1]
|
||||
15 ip = 1 | goto 2
|
||||
16 ip = regs[5] * regs[5]
|
||||
17 regs[1] = regs[1] + 2
|
||||
18 regs[1] = regs[1] * regs[1]
|
||||
19 regs[1] = regs[5] * regs[1]
|
||||
20 regs[1] = regs[1] * 11
|
||||
21 regs[3] = regs[3] + 6
|
||||
22 regs[3] = regs[3] * regs[5]
|
||||
23 regs[3] = regs[3] + 15
|
||||
24 regs[1] = regs[1] + regs[3]
|
||||
25 ip = regs[5] + regs[0]
|
||||
26 ip = 0
|
||||
27 regs[3] = regs[5]
|
||||
28 regs[3] = regs[3] * regs[5]
|
||||
29 regs[3] = regs[5] + regs[3]
|
||||
30 regs[3] = regs[5] * regs[3]
|
||||
31 regs[3] = regs[3] * 14
|
||||
32 regs[3] = regs[3] * regs[5]
|
||||
33 regs[1] = regs[1] + regs[3]
|
||||
34 regs[0] = 0
|
||||
35 ip = 0
|
||||
|
||||
hypothesis: usm of numbers that divide the number in regs[0]
|
||||
"""
|
||||
53
2018/d2.py
Normal file
53
2018/d2.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from lib import LETTERS_LOWER
|
||||
|
||||
|
||||
def part_1(data):
|
||||
two_count, three_count = 0, 0
|
||||
for line in data.splitlines():
|
||||
count = [line.count(c) for c in LETTERS_LOWER if line.count(c) > 1]
|
||||
if 2 in count:
|
||||
two_count += 1
|
||||
if 3 in count:
|
||||
three_count += 1
|
||||
r = two_count * three_count
|
||||
print(r)
|
||||
|
||||
|
||||
def equal(a, b):
|
||||
c = 0
|
||||
if len(a) != len(b):
|
||||
return False
|
||||
for i in range(len(a)):
|
||||
if a[i] != b[i]:
|
||||
c += 1
|
||||
if c == 1:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def strip(a, b):
|
||||
r = ""
|
||||
for a, b in zip(a, b):
|
||||
if a == b:
|
||||
r += a
|
||||
return r
|
||||
|
||||
|
||||
def part_2(data):
|
||||
lines = list(data.splitlines())
|
||||
for i in range(len(lines)):
|
||||
for j in range(i + 1, len(lines)):
|
||||
a, b = lines[i], lines[j]
|
||||
if equal(a, b):
|
||||
print(strip(a, b))
|
||||
|
||||
|
||||
def main():
|
||||
with open("i2.txt") as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
94
2018/d20.py
Normal file
94
2018/d20.py
Normal file
@@ -0,0 +1,94 @@
|
||||
from lib import get_data, add2
|
||||
from collections import defaultdict
|
||||
|
||||
DIRS = {
|
||||
"N": (-1, 0),
|
||||
"E": (0, 1),
|
||||
"S": (1, 0),
|
||||
"W": (0, -1),
|
||||
}
|
||||
|
||||
data = get_data(__file__).strip()
|
||||
g = defaultdict(set)
|
||||
seen = set()
|
||||
stack: list[tuple[tuple, int, list]] = [((0, 0), 0, [])]
|
||||
while len(stack) > 0:
|
||||
pos, i, i_outs = stack.pop()
|
||||
|
||||
c = (pos, i, tuple(i_outs))
|
||||
if c in seen:
|
||||
continue
|
||||
else:
|
||||
seen.add(c)
|
||||
|
||||
assert i is not None
|
||||
while i < len(data):
|
||||
c = data[i]
|
||||
if c in DIRS.keys():
|
||||
npos = add2(pos, DIRS[c])
|
||||
g[pos].add(npos)
|
||||
g[npos].add(pos)
|
||||
pos = npos
|
||||
i += 1
|
||||
elif c == "(":
|
||||
to_continue = [i + 1]
|
||||
open_count = 0
|
||||
j_out = None
|
||||
for j in range(i + 1, len(data)):
|
||||
c = data[j]
|
||||
if c == "|" and open_count == 0:
|
||||
to_continue.append(j + 1)
|
||||
elif c == "(":
|
||||
open_count += 1
|
||||
elif c == ")" and open_count != 0:
|
||||
open_count -= 1
|
||||
elif c == ")" and open_count == 0:
|
||||
j_out = j
|
||||
break
|
||||
assert j_out is not None
|
||||
|
||||
for new_i in to_continue:
|
||||
new_i_outs = list(i_outs)
|
||||
new_i_outs.append(j_out)
|
||||
stack.append((pos, new_i, new_i_outs))
|
||||
break
|
||||
elif c == "$":
|
||||
break
|
||||
elif c == ")" and len(i_outs) == 0:
|
||||
assert False, "Encountered | without i_out"
|
||||
elif c == ")":
|
||||
i_new = i_outs.pop()
|
||||
assert i == i_new
|
||||
i += 1
|
||||
elif c == "^":
|
||||
i += 1
|
||||
elif c == "|" and len(i_outs) == 0:
|
||||
assert False, "Encountered | without i_out"
|
||||
elif c == "|":
|
||||
i = i_outs.pop()
|
||||
i += 1
|
||||
else:
|
||||
assert False
|
||||
|
||||
seen = set()
|
||||
dists = {}
|
||||
xs = [(0, 0)]
|
||||
steps = 0
|
||||
over_thousand = set()
|
||||
while len(xs) > 0:
|
||||
nxs = []
|
||||
for x in xs:
|
||||
if x in seen:
|
||||
continue
|
||||
if steps >= 1000:
|
||||
over_thousand.add(x)
|
||||
seen.add(x)
|
||||
dists[x] = steps
|
||||
for nb in g[x]:
|
||||
if not nb in seen:
|
||||
nxs.append(nb)
|
||||
xs = nxs
|
||||
steps += 1
|
||||
|
||||
print(max(dists.values()))
|
||||
print(len(over_thousand))
|
||||
52
2018/d21.py
Normal file
52
2018/d21.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from lib import get_data, str_to_ints
|
||||
import d16
|
||||
|
||||
|
||||
def run(data, break_after=None, reg_zero_init=0):
|
||||
ip = None
|
||||
regs = [0 for _ in range(6)]
|
||||
regs[0] = reg_zero_init
|
||||
|
||||
insts = []
|
||||
for line in data.splitlines():
|
||||
if line.startswith("#"):
|
||||
(ip,) = str_to_ints(line)
|
||||
else:
|
||||
fs = line.split()
|
||||
vals = str_to_ints(line)
|
||||
insts.append([fs[0]] + vals)
|
||||
|
||||
count = 0
|
||||
assert ip is not None
|
||||
seen = set()
|
||||
last_added = None
|
||||
while regs[ip] < len(insts):
|
||||
if break_after is not None and count > break_after:
|
||||
break
|
||||
if regs[ip] == 28:
|
||||
r4 = regs[4]
|
||||
|
||||
if len(seen) == 0:
|
||||
print(r4)
|
||||
|
||||
if r4 in seen:
|
||||
print(last_added)
|
||||
break
|
||||
else:
|
||||
seen.add(r4)
|
||||
last_added = r4
|
||||
|
||||
inst = insts[regs[ip]]
|
||||
f = getattr(d16, inst[0])
|
||||
f(regs, *inst[1:])
|
||||
regs[ip] += 1
|
||||
count += 1
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
run(data, reg_zero_init=333)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
89
2018/d22.py
Normal file
89
2018/d22.py
Normal file
@@ -0,0 +1,89 @@
|
||||
from lib import get_data, str_to_ints, A_Star
|
||||
|
||||
data = get_data(__file__)
|
||||
|
||||
depth, x_target, y_target = str_to_ints(data)
|
||||
# depth, x_target, y_target = 510, 10, 10
|
||||
# depth, x_target, y_target = 9171, 7, 721
|
||||
# print(depth, x_target, y_target)
|
||||
|
||||
x_area_max = x_target * 10
|
||||
y_area_max = y_target * 10
|
||||
|
||||
maze = [[None for _ in range(x_area_max + 1)] for _ in range(y_area_max + 1)]
|
||||
|
||||
maze[0][0] = 0
|
||||
maze[y_target][x_target] = 0
|
||||
|
||||
mod = 20183
|
||||
for x in range(x_area_max + 1):
|
||||
maze[0][x] = x * 16807 % mod
|
||||
|
||||
for y in range(y_area_max + 1):
|
||||
maze[y][0] = y * 48271 % mod
|
||||
|
||||
for y in range(1, y_area_max + 1):
|
||||
for x in range(1, x_area_max + 1):
|
||||
if x == x_target and y == y_target:
|
||||
continue
|
||||
assert maze[y][x] is None
|
||||
geo_index = ((maze[y][x - 1] + depth) * (maze[y - 1][x] + depth)) % mod
|
||||
maze[y][x] = geo_index
|
||||
|
||||
t = 0
|
||||
for y in range(y_target + 1):
|
||||
for x in range(x_target + 1):
|
||||
t += ((maze[y][x] + depth) % mod) % 3
|
||||
print(t)
|
||||
|
||||
for y in range(y_area_max + 1):
|
||||
for x in range(x_area_max + 1):
|
||||
maze[y][x] = ((maze[y][x] + depth) % mod) % 3
|
||||
|
||||
|
||||
def allowed(area, tool):
|
||||
# 0 = rocky, 1 = wet, 2 = narrow
|
||||
# 0 = torch, 1 = climbing, 2 = neither
|
||||
return (
|
||||
(area == 0 and (tool in [0, 1]))
|
||||
or (area == 1 and (tool in [1, 2]))
|
||||
or (area == 2 and (tool in [0, 2]))
|
||||
)
|
||||
|
||||
|
||||
def neighbors(state):
|
||||
x, y, tool = state
|
||||
r = []
|
||||
area = maze[y][x]
|
||||
for t in range(3):
|
||||
if allowed(area, t) and t != tool:
|
||||
r.append((x, y, t))
|
||||
|
||||
for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
|
||||
nx, ny = x + dx, y + dy
|
||||
if not (nx >= 0 and nx < x_area_max and ny >= 0 and ny < y_area_max):
|
||||
continue
|
||||
if not allowed(maze[ny][nx], tool):
|
||||
continue
|
||||
r.append((nx, ny, tool))
|
||||
return r
|
||||
|
||||
|
||||
def distance(a, b):
|
||||
if a == 0:
|
||||
return 0
|
||||
elif a[2] != b[2]:
|
||||
assert a[0] == b[0] and a[1] == b[1]
|
||||
return 7
|
||||
else:
|
||||
return 1
|
||||
|
||||
|
||||
a = A_Star(
|
||||
starts=[(0, 0, 0)],
|
||||
is_goal=lambda s: s[0] == x_target and s[1] == y_target and s[2] == 0,
|
||||
h=lambda s: abs(s[0] - x_target) + abs(s[1] - y_target),
|
||||
d=distance,
|
||||
neighbors=neighbors,
|
||||
)
|
||||
print(a.cost)
|
||||
45
2018/d23.py
Normal file
45
2018/d23.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from lib import get_data, ints
|
||||
|
||||
data = get_data(__file__)
|
||||
|
||||
X, Y, Z, RADIUS = 0, 1, 2, 3
|
||||
|
||||
|
||||
def dist(a, b):
|
||||
return sum(abs(x - y) for x, y in zip(a[:RADIUS], b[:RADIUS]))
|
||||
|
||||
|
||||
bots = [ints(line) for line in data.splitlines()]
|
||||
max_bot = max(bots, key=lambda b: b[RADIUS])
|
||||
r = max_bot[3]
|
||||
t = sum(1 for bot in bots if dist(bot, max_bot) <= r)
|
||||
print(t)
|
||||
|
||||
from z3 import Int, If, Abs, Sum, IntVal, Optimize, set_param
|
||||
|
||||
|
||||
x = Int("x")
|
||||
y = Int("y")
|
||||
z = Int("z")
|
||||
|
||||
z3zero = IntVal(0)
|
||||
z3one = IntVal(1)
|
||||
n_out_of_range = Int("n_out_of_range")
|
||||
dist_origin = Int("dist_origin")
|
||||
|
||||
bots_out_of_range = [
|
||||
If(Abs(b[X] - x) + Abs(b[Y] - y) + Abs(b[Z] - z) > b[RADIUS], z3one, z3zero)
|
||||
for b in bots
|
||||
]
|
||||
|
||||
set_param("parallel.enable", True)
|
||||
|
||||
opt = Optimize()
|
||||
opt.add(n_out_of_range == Sum(bots_out_of_range))
|
||||
opt.add(dist_origin == Abs(x) + Abs(y) + Abs(z))
|
||||
opt.minimize(n_out_of_range)
|
||||
opt.minimize(dist_origin)
|
||||
|
||||
res = opt.check()
|
||||
m = opt.model()
|
||||
print(m[dist_origin])
|
||||
179
2018/d24.py
Normal file
179
2018/d24.py
Normal file
@@ -0,0 +1,179 @@
|
||||
import re
|
||||
from lib import get_data, ints
|
||||
from collections import defaultdict
|
||||
from copy import deepcopy
|
||||
|
||||
r = re.compile(r"(immune|weak) to ([\w ,]+)")
|
||||
r_strength = re.compile(r"(\w+) damage")
|
||||
|
||||
data = """Immune System:
|
||||
17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2
|
||||
989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3
|
||||
|
||||
Infection:
|
||||
801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
|
||||
4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4
|
||||
"""
|
||||
data = get_data(__file__)
|
||||
|
||||
ARMY, ID, UNITS, HITS, DAMG, INIT = 0, 1, 2, 3, 4, 5
|
||||
|
||||
groups = []
|
||||
unit_meta = {}
|
||||
|
||||
id = 1
|
||||
for a, p in enumerate(data.split("\n\n")):
|
||||
for line in p.splitlines():
|
||||
xs = ints(line)
|
||||
if len(xs) == 0:
|
||||
continue
|
||||
assert len(xs) == 4
|
||||
xs = [a, id] + xs
|
||||
|
||||
(attack,) = r_strength.findall(line)
|
||||
defense = defaultdict(list)
|
||||
for attr, values in r.findall(line):
|
||||
defense[attr] = values.split(", ")
|
||||
|
||||
unit_meta[id] = attack, defense
|
||||
groups.append(xs)
|
||||
id += 1
|
||||
|
||||
|
||||
def effective_power(group):
|
||||
return (group[UNITS] * group[DAMG], group[INIT])
|
||||
|
||||
|
||||
def damage(g1, g2):
|
||||
"""Damage g1 does to g2"""
|
||||
g1_id, g2_id = g1[ID], g2[ID]
|
||||
g1_attack_type = unit_meta[g1_id][0]
|
||||
g2_defenses = unit_meta[g2_id][1]
|
||||
g2_immune = True if g1_attack_type in g2_defenses["immune"] else False
|
||||
g2_weak = True if g1_attack_type in g2_defenses["weak"] else False
|
||||
|
||||
assert (g2_immune and g2_weak) != True
|
||||
|
||||
damage = g1[UNITS] * g1[DAMG]
|
||||
if g2_immune:
|
||||
damage = 0
|
||||
elif g2_weak:
|
||||
damage *= 2
|
||||
return damage
|
||||
|
||||
|
||||
groups_orig = deepcopy(groups)
|
||||
while True:
|
||||
groups = sorted(groups, key=effective_power, reverse=True)
|
||||
attacks = {}
|
||||
attacked = set()
|
||||
for g in groups:
|
||||
target_max_stats = 0, 0, 0
|
||||
target = None
|
||||
for t in groups:
|
||||
if g[ARMY] == t[ARMY]:
|
||||
continue
|
||||
if g[ID] == t[ID]:
|
||||
continue
|
||||
if t[ID] in attacked:
|
||||
continue
|
||||
target_stats = damage(g, t), effective_power(t)[0], t[INIT]
|
||||
if target_stats[0] == 0:
|
||||
continue
|
||||
if target_stats > target_max_stats:
|
||||
target_max_stats = target_stats
|
||||
target = t
|
||||
|
||||
if target is not None:
|
||||
attacked.add(target[ID])
|
||||
attacks[g[ID]] = target
|
||||
|
||||
groups = sorted(groups, key=lambda g: g[INIT], reverse=True)
|
||||
for g in groups:
|
||||
if g[ID] not in attacks:
|
||||
continue
|
||||
if g[UNITS] <= 0:
|
||||
continue
|
||||
|
||||
target = attacks[g[ID]]
|
||||
cdamage = damage(g, target)
|
||||
units_dead = cdamage // target[HITS]
|
||||
units_dead = units_dead if units_dead <= target[UNITS] else target[UNITS]
|
||||
# print("immune" if g[ARMY] == 0 else "infection", g[ID], "attacks", target[ID], "dead", units_dead)
|
||||
target[UNITS] -= units_dead
|
||||
|
||||
groups = [g for g in groups if g[UNITS] > 0]
|
||||
armies = set()
|
||||
for g in groups:
|
||||
armies.add(g[ARMY])
|
||||
assert len(armies) >= 1
|
||||
if len(armies) == 1:
|
||||
break
|
||||
|
||||
|
||||
t = sum(g[UNITS] for g in groups)
|
||||
print(t)
|
||||
|
||||
|
||||
for boost in range(0, 1800):
|
||||
groups = deepcopy(groups_orig)
|
||||
for g in groups:
|
||||
if g[ARMY] == 0:
|
||||
g[DAMG] += boost
|
||||
|
||||
while True:
|
||||
groups = sorted(groups, key=effective_power, reverse=True)
|
||||
attacks = {}
|
||||
attacked = set()
|
||||
for g in groups:
|
||||
target_max_stats = 0, 0, 0
|
||||
target = None
|
||||
for t in groups:
|
||||
if g[ARMY] == t[ARMY]:
|
||||
continue
|
||||
if g[ID] == t[ID]:
|
||||
continue
|
||||
if t[ID] in attacked:
|
||||
continue
|
||||
target_stats = damage(g, t), effective_power(t)[0], t[INIT]
|
||||
if target_stats[0] == 0:
|
||||
continue
|
||||
if target_stats > target_max_stats:
|
||||
target_max_stats = target_stats
|
||||
target = t
|
||||
|
||||
if target is not None:
|
||||
attacked.add(target[ID])
|
||||
attacks[g[ID]] = target
|
||||
|
||||
groups = sorted(groups, key=lambda g: g[INIT], reverse=True)
|
||||
max_dead = 0
|
||||
for g in groups:
|
||||
if g[ID] not in attacks:
|
||||
continue
|
||||
if g[UNITS] <= 0:
|
||||
continue
|
||||
|
||||
target = attacks[g[ID]]
|
||||
cdamage = damage(g, target)
|
||||
units_dead = cdamage // target[HITS]
|
||||
units_dead = units_dead if units_dead <= target[UNITS] else target[UNITS]
|
||||
max_dead = max(units_dead, max_dead)
|
||||
# print("immune" if g[ARMY] == 0 else "infection", g[ID], "attacks", target[ID], "dead", units_dead)
|
||||
target[UNITS] -= units_dead
|
||||
|
||||
if max_dead == 0:
|
||||
break
|
||||
|
||||
groups = [g for g in groups if g[UNITS] > 0]
|
||||
armies = set()
|
||||
for g in groups:
|
||||
armies.add(g[ARMY])
|
||||
assert len(armies) >= 1
|
||||
if len(armies) == 1:
|
||||
army = groups[0][ARMY]
|
||||
t = sum(g[UNITS] for g in groups)
|
||||
if army == 0:
|
||||
print(t)
|
||||
exit(0)
|
||||
break
|
||||
38
2018/d25.py
Normal file
38
2018/d25.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from lib import get_data, ints
|
||||
|
||||
|
||||
def mdist(x, y):
|
||||
return sum(abs(a - b) for a, b in zip(x, y))
|
||||
|
||||
|
||||
data = get_data(__file__)
|
||||
xss = [tuple(ints(line)) for line in data.splitlines()]
|
||||
|
||||
# check if we can use set (even though we didn't end up using sets)
|
||||
assert len(xss) == len(set(xss))
|
||||
|
||||
unplaced = list(xss)
|
||||
groups = [[unplaced.pop()]]
|
||||
while unplaced:
|
||||
# see if any start fits into the current group
|
||||
current_group = groups[-1]
|
||||
add = None
|
||||
for u in unplaced:
|
||||
for x in current_group:
|
||||
if mdist(u, x) <= 3:
|
||||
add = u
|
||||
break
|
||||
if add is not None:
|
||||
break
|
||||
|
||||
if add is not None:
|
||||
# if yes, add the start to that group and see if another one can be
|
||||
# added to the same group
|
||||
unplaced.remove(add)
|
||||
current_group.append(add)
|
||||
else:
|
||||
# if no, create a new group with an unplaced start
|
||||
groups.append([unplaced.pop()])
|
||||
|
||||
|
||||
print(len(groups))
|
||||
44
2018/d3.py
Normal file
44
2018/d3.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from lib import str_to_ints
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def part_1(data):
|
||||
claims = defaultdict(int)
|
||||
for line in data.splitlines():
|
||||
id, x, y, w, h = str_to_ints(line)
|
||||
for dx in range(w):
|
||||
for dy in range(h):
|
||||
claims[(x + dx, y + dy)] += 1
|
||||
|
||||
r = sum([1 for v in claims.values() if v > 1])
|
||||
print(r)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
claims = defaultdict(list)
|
||||
all_ids = set()
|
||||
for line in data.splitlines():
|
||||
id, x, y, w, h = str_to_ints(line)
|
||||
for dx in range(w):
|
||||
for dy in range(h):
|
||||
claims[(x + dx, y + dy)].append(id)
|
||||
all_ids.add(id)
|
||||
|
||||
for xs in claims.values():
|
||||
if len(xs) > 1:
|
||||
for x in xs:
|
||||
if x in all_ids:
|
||||
all_ids.remove(x)
|
||||
assert len(all_ids) == 1
|
||||
print(all_ids.pop())
|
||||
|
||||
|
||||
def main():
|
||||
with open("i3.txt") as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
67
2018/d4.py
Normal file
67
2018/d4.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from lib import str_to_ints
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def part_1(data):
|
||||
shifts = sorted(data.splitlines())
|
||||
guards = defaultdict(list)
|
||||
guard = None
|
||||
for s in shifts:
|
||||
try:
|
||||
_, _, _, h, m, guard = str_to_ints(s)
|
||||
except ValueError:
|
||||
_, _, _, h, m = str_to_ints(s)
|
||||
assert guard is not None
|
||||
guards[guard].append((h, m))
|
||||
|
||||
guard_sleep_times = {}
|
||||
guard_heat_maps = {}
|
||||
for guard, times in guards.items():
|
||||
heat_map = [0 for _ in range(60)]
|
||||
total_time = 0
|
||||
start = None
|
||||
for h, m in times:
|
||||
if start is None:
|
||||
start = (h, m)
|
||||
else:
|
||||
delta = (h - start[0]) * 60 + (m - start[1])
|
||||
total_time += delta
|
||||
for t in range(start[1], m):
|
||||
heat_map[t] += 1
|
||||
start = None
|
||||
guard_sleep_times[guard] = total_time
|
||||
guard_heat_maps[guard] = heat_map
|
||||
|
||||
max_guard, max_time = None, 0
|
||||
for guard, time in guard_sleep_times.items():
|
||||
if time > max_time:
|
||||
max_guard = guard
|
||||
max_time = time
|
||||
|
||||
assert type(max_guard) is int
|
||||
max_minute, max_asleep = 0, 0
|
||||
for minute, asleep in enumerate(guard_heat_maps[max_guard]):
|
||||
if asleep > max_asleep:
|
||||
max_minute = minute
|
||||
max_asleep = asleep
|
||||
print(max_guard * max_minute)
|
||||
|
||||
max_guard, max_minute, max_asleep = 0, 0, 0
|
||||
for guard in guard_sleep_times.keys():
|
||||
for minute, asleep in enumerate(guard_heat_maps[guard]):
|
||||
if asleep > max_asleep:
|
||||
max_minute = minute
|
||||
max_asleep = asleep
|
||||
max_guard = guard
|
||||
print(max_guard * max_minute)
|
||||
|
||||
|
||||
def main():
|
||||
input_file = __file__.replace(".py", ".txt")
|
||||
with open(input_file) as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
46
2018/d5.py
Normal file
46
2018/d5.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from lib import LETTERS_LOWER
|
||||
|
||||
|
||||
def part_1(data):
|
||||
data = data.strip()
|
||||
i = 0
|
||||
while i < (len(data) - 1):
|
||||
a, b = data[i], data[i + 1]
|
||||
if (a.upper() == b.upper()) and (a != b):
|
||||
data = data[:i] + data[i + 2 :]
|
||||
i = max(0, i - 1)
|
||||
else:
|
||||
i += 1
|
||||
print(len(data))
|
||||
|
||||
|
||||
def part_2(data):
|
||||
data_orig = data.strip()
|
||||
min_len = float("inf")
|
||||
for c in LETTERS_LOWER:
|
||||
data = str(data_orig)
|
||||
data = data.replace(c, "")
|
||||
data = data.replace(c.upper(), "")
|
||||
i = 0
|
||||
while i < (len(data) - 1):
|
||||
a, b = data[i], data[i + 1]
|
||||
if (a.upper() == b.upper()) and (a != b):
|
||||
data = data[:i] + data[i + 2 :]
|
||||
i = max(0, i - 1)
|
||||
else:
|
||||
i += 1
|
||||
|
||||
min_len = min(min_len, len(data))
|
||||
print(min_len)
|
||||
|
||||
|
||||
def main():
|
||||
input_file = __file__.replace(".py", ".txt")
|
||||
with open(input_file) as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
66
2018/d6.py
Normal file
66
2018/d6.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from lib import str_to_ints
|
||||
|
||||
|
||||
def part_1(data):
|
||||
counts = {}
|
||||
x_min, x_max, y_min, y_max = 10**9, 0, 10**9, 0
|
||||
for line in data.splitlines():
|
||||
x, y = str_to_ints(line)
|
||||
x_min = min(x_min, x)
|
||||
x_max = max(x_max, x)
|
||||
y_min = min(y_min, y)
|
||||
y_max = max(y_max, y)
|
||||
counts[(x, y)] = 0
|
||||
|
||||
infs = set()
|
||||
for x in range(x_min, x_max + 1):
|
||||
for y in range(y_min, y_max + 1):
|
||||
if (x, y) in counts:
|
||||
counts[(x, y)] += 1
|
||||
continue
|
||||
|
||||
min_dist = 10**9
|
||||
min_fields = []
|
||||
for (nx, ny) in counts.keys():
|
||||
d = abs(nx - x) + abs(ny - y)
|
||||
if d == min_dist:
|
||||
min_fields.append((nx, ny))
|
||||
elif d < min_dist:
|
||||
min_dist = d
|
||||
min_fields = [(nx, ny)]
|
||||
|
||||
if len(min_fields) == 1:
|
||||
(nx, ny), = min_fields
|
||||
if x == x_min or y == y_min or x == x_max or y == y_max:
|
||||
infs.add((nx, ny))
|
||||
else:
|
||||
counts[(nx, ny)] += 1
|
||||
for c in infs:
|
||||
del counts[c]
|
||||
print(max(list(counts.values())))
|
||||
|
||||
|
||||
def part_2(data):
|
||||
coords = []
|
||||
for line in data.splitlines():
|
||||
coords.append(str_to_ints(line))
|
||||
|
||||
r = 0
|
||||
for x in range(-500, 500):
|
||||
for y in range(-500, 500):
|
||||
d = sum([abs(nx - x) + abs(ny - y) for (nx, ny) in coords])
|
||||
if d < 10_000:
|
||||
r += 1
|
||||
print(r)
|
||||
|
||||
|
||||
def main():
|
||||
input_file = __file__.replace(".py", ".txt")
|
||||
with open(input_file) as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
80
2018/d7.py
Normal file
80
2018/d7.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from lib import LETTERS_UPPER
|
||||
|
||||
|
||||
def part_1(data):
|
||||
deps = {c: [] for c in LETTERS_UPPER}
|
||||
for line in data.splitlines():
|
||||
fields = line.split()
|
||||
a, b = fields[1], fields[7]
|
||||
deps[b].append(a)
|
||||
ready = []
|
||||
for x, ys in deps.items():
|
||||
if len(ys) == 0:
|
||||
ready.append(x)
|
||||
seq = []
|
||||
ready = sorted(ready)
|
||||
while ready:
|
||||
current, ready = ready[0], ready[1:]
|
||||
seq.append(current)
|
||||
for x, ys in deps.items():
|
||||
if current in ys:
|
||||
ys.remove(current)
|
||||
if len(ys) == 0:
|
||||
ready.append(x)
|
||||
ready = sorted(ready)
|
||||
|
||||
print("".join(seq))
|
||||
|
||||
|
||||
def score(c):
|
||||
return 1 + ord(c) - ord("A") + 60
|
||||
|
||||
|
||||
def part_2(data):
|
||||
deps = {c: [] for c in LETTERS_UPPER}
|
||||
for line in data.splitlines():
|
||||
fields = line.split()
|
||||
a, b = fields[1], fields[7]
|
||||
deps[b].append(a)
|
||||
|
||||
ready = []
|
||||
done = []
|
||||
for x, ys in deps.items():
|
||||
if len(ys) == 0:
|
||||
ready.append(x)
|
||||
workers = {}
|
||||
|
||||
steps = 0
|
||||
while len(done) != 26:
|
||||
ready = sorted(ready)
|
||||
while len(ready) > 0 and len(workers) < 5:
|
||||
current, ready = ready[0], ready[1:]
|
||||
workers[current] = score(current)
|
||||
|
||||
to_del = []
|
||||
for k in workers.keys():
|
||||
workers[k] -= 1
|
||||
if workers[k] == 0:
|
||||
to_del.append(k)
|
||||
done.append(k)
|
||||
for x, ys in deps.items():
|
||||
if k in ys:
|
||||
ys.remove(k)
|
||||
if len(ys) == 0:
|
||||
ready.append(x)
|
||||
for k in to_del:
|
||||
del workers[k]
|
||||
steps += 1
|
||||
print(steps)
|
||||
|
||||
|
||||
def main():
|
||||
input_file = __file__.replace(".py", ".txt")
|
||||
with open(input_file) as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
59
2018/d8.py
Normal file
59
2018/d8.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from lib import *
|
||||
|
||||
|
||||
def parse(fields, i):
|
||||
n_children = fields[i]
|
||||
n_meta = fields[i + 1]
|
||||
i += 2
|
||||
children = []
|
||||
meta_score = 0
|
||||
for _ in range(n_children):
|
||||
c, i, m = parse(fields, i)
|
||||
children.append(c)
|
||||
meta_score += m
|
||||
for i in range(i, i + n_meta):
|
||||
meta_score += fields[i]
|
||||
i += 1
|
||||
return children, i, meta_score
|
||||
|
||||
|
||||
def parse2(fields, i):
|
||||
n_children = fields[i]
|
||||
n_meta = fields[i + 1]
|
||||
i += 2
|
||||
children = []
|
||||
scores = []
|
||||
for _ in range(n_children):
|
||||
c, i, s = parse2(fields, i)
|
||||
children.append(c)
|
||||
scores.append(s)
|
||||
|
||||
score = 0
|
||||
if n_children == 0:
|
||||
for j in range(i, i + n_meta):
|
||||
score += fields[j]
|
||||
else:
|
||||
for j in range(i, i + n_meta):
|
||||
new_i = fields[j]
|
||||
new_i -= 1
|
||||
if new_i < 0:
|
||||
continue
|
||||
if new_i < len(children):
|
||||
score += scores[new_i]
|
||||
i += n_meta
|
||||
return children, i, score
|
||||
|
||||
|
||||
def main():
|
||||
input_file = __file__.replace(".py", ".txt")
|
||||
with open(input_file) as f:
|
||||
data = f.read()
|
||||
nums = str_to_ints(data)
|
||||
tree = parse(nums, 0)
|
||||
print(tree[2])
|
||||
tree = parse2(nums, 0)
|
||||
print(tree[2])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
73
2018/d9.py
Normal file
73
2018/d9.py
Normal file
@@ -0,0 +1,73 @@
|
||||
from lib import str_to_ints
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def part_1(data):
|
||||
scores = defaultdict(int)
|
||||
n_players, worth = str_to_ints(data)
|
||||
current_player = 0
|
||||
circle = [0]
|
||||
current = 1
|
||||
for n in range(1, worth + 1):
|
||||
if n % 23 == 0:
|
||||
scores[current_player] += n
|
||||
current = (current - 7) % len(circle)
|
||||
scores[current_player] += circle.pop(current)
|
||||
else:
|
||||
current = (current + 2) % len(circle)
|
||||
circle.insert(current, n)
|
||||
current_player = (current_player + 1) % n_players
|
||||
print(max(scores.values()))
|
||||
|
||||
|
||||
class Node:
|
||||
def __init__(self, value, prev=None, next=None):
|
||||
self.value = value
|
||||
if prev is None:
|
||||
self.prev = self
|
||||
else:
|
||||
self.prev = prev
|
||||
if next is None:
|
||||
self.next = self
|
||||
else:
|
||||
self.next = next
|
||||
|
||||
|
||||
def part_2(data):
|
||||
scores = defaultdict(int)
|
||||
n_players, worth = str_to_ints(data)
|
||||
current_player = 0
|
||||
first = Node(0)
|
||||
current = first
|
||||
current.next = current
|
||||
current.prev = current
|
||||
|
||||
for n in range(1, (worth * 100) + 1):
|
||||
if n % 23 == 0:
|
||||
scores[current_player] += n
|
||||
for _ in range(7):
|
||||
current = current.prev
|
||||
scores[current_player] += current.value
|
||||
current.prev.next = current.next
|
||||
current.next.prev = current.prev
|
||||
current = current.next
|
||||
else:
|
||||
new_current = Node(n, current.next, current.next.next)
|
||||
current.next.next.prev = new_current
|
||||
current.next.next = new_current
|
||||
current = new_current
|
||||
current_player = (current_player + 1) % n_players
|
||||
|
||||
print(max(scores.values()))
|
||||
|
||||
|
||||
def main():
|
||||
input_file = __file__.replace(".py", ".txt")
|
||||
with open(input_file) as f:
|
||||
data = f.read()
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
1
2018/lib.py
Symbolic link
1
2018/lib.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../lib.py
|
||||
1
2018/monitor.py
Symbolic link
1
2018/monitor.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../monitor.py
|
||||
31
2019/d1.py
Normal file
31
2019/d1.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from lib import get_data
|
||||
|
||||
|
||||
def f(i):
|
||||
i //= 3
|
||||
i -= 2
|
||||
return i
|
||||
|
||||
|
||||
def part_1(data):
|
||||
print(sum([f(int(line)) for line in data.splitlines()]))
|
||||
|
||||
|
||||
def part_2(data):
|
||||
r = 0
|
||||
for line in data.splitlines():
|
||||
i = f(int(line))
|
||||
while i > 0:
|
||||
r += i
|
||||
i = f(i)
|
||||
print(r)
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
102
2019/d10.py
Normal file
102
2019/d10.py
Normal file
@@ -0,0 +1,102 @@
|
||||
from lib import get_data, Grid2D
|
||||
from math import gcd, atan2, pi
|
||||
|
||||
|
||||
def angle(a1, a2):
|
||||
dy = a1[0] - a2[0]
|
||||
dx = a2[1] - a1[1]
|
||||
a = (atan2(dx, dy) + 2 * pi) % (2 * pi)
|
||||
return a
|
||||
|
||||
|
||||
def dist(a1, a2):
|
||||
return abs(a1[0] - a2[0]) + abs(a1[1] - a2[1])
|
||||
|
||||
|
||||
def part_1(data):
|
||||
g = Grid2D(data)
|
||||
asts = g.find("#")
|
||||
asts_set = set(asts)
|
||||
|
||||
best_ast = None
|
||||
visible_max = 0
|
||||
for a in asts:
|
||||
visible = 0
|
||||
for other in asts:
|
||||
if a == other:
|
||||
continue
|
||||
x1, x2 = a[0], other[0]
|
||||
y1, y2 = a[1], other[1]
|
||||
|
||||
if y1 == y2:
|
||||
for x in range(min(x1, x2) + 1, max(x1, x2)):
|
||||
if (x, y1) in asts_set:
|
||||
break
|
||||
else:
|
||||
visible += 1
|
||||
elif x1 == x2:
|
||||
for y in range(min(y1, y2) + 1, max(y1, y2)):
|
||||
if (x1, y) in asts_set:
|
||||
break
|
||||
else:
|
||||
visible += 1
|
||||
else:
|
||||
dx = abs(x1 - x2)
|
||||
dy = abs(y1 - y2)
|
||||
g = gcd(dx, dy)
|
||||
dx //= g
|
||||
dy //= g
|
||||
|
||||
x, y = x1, y1
|
||||
if x2 < x1:
|
||||
dx = -dx
|
||||
if y2 < y1:
|
||||
dy = -dy
|
||||
|
||||
blocked = False
|
||||
while True:
|
||||
x += dx
|
||||
y += dy
|
||||
if (x, y) == (x2, y2):
|
||||
break
|
||||
if (x, y) in asts_set:
|
||||
blocked = True
|
||||
break
|
||||
if not blocked:
|
||||
visible += 1
|
||||
|
||||
if visible > visible_max:
|
||||
visible_max = visible
|
||||
best_ast = a
|
||||
|
||||
assert best_ast is not None
|
||||
print(visible_max)
|
||||
|
||||
other_asts = [a for a in asts if a != best_ast]
|
||||
removed_count = 0
|
||||
while True:
|
||||
removed = set()
|
||||
other_asts = sorted(
|
||||
other_asts, key=lambda a: (angle(best_ast, a), dist(best_ast, a))
|
||||
)
|
||||
lowest_angle = -1
|
||||
for a in other_asts:
|
||||
current_angle = angle(best_ast, a)
|
||||
if current_angle > lowest_angle:
|
||||
removed.add(a)
|
||||
lowest_angle = current_angle
|
||||
removed_count += 1
|
||||
|
||||
if removed_count == 200:
|
||||
print(a[1] * 100 + a[0])
|
||||
return
|
||||
other_asts = [a for a in other_asts if a not in removed]
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
93
2019/d11.py
Normal file
93
2019/d11.py
Normal file
@@ -0,0 +1,93 @@
|
||||
from lib import get_data, str_to_ints, add2
|
||||
from d9 import Amp
|
||||
|
||||
DIRS = [(-1, 0), (0, 1), (1, 0), (0, -1)]
|
||||
BLACK = 0
|
||||
WHITE = 1
|
||||
|
||||
|
||||
def part_1(data):
|
||||
xs = str_to_ints(data)
|
||||
a = Amp(xs)
|
||||
current_dir_idx = 0
|
||||
pos = (0, 0)
|
||||
white_panels = set()
|
||||
got_painted = set()
|
||||
|
||||
while not a.done:
|
||||
if pos in white_panels:
|
||||
a.feed(WHITE)
|
||||
else:
|
||||
a.feed(BLACK)
|
||||
a.go()
|
||||
a.go()
|
||||
while a.outputs:
|
||||
paint_color = a.pop()
|
||||
if paint_color == BLACK:
|
||||
white_panels.remove(pos)
|
||||
elif paint_color == WHITE:
|
||||
white_panels.add(pos)
|
||||
else:
|
||||
assert False
|
||||
got_painted.add(pos)
|
||||
turn_dir_code = a.pop()
|
||||
turn_dir = -1 if turn_dir_code == 0 else 1
|
||||
current_dir_idx = (current_dir_idx + turn_dir) % len(DIRS)
|
||||
pos = add2(pos, DIRS[current_dir_idx])
|
||||
print(len(got_painted))
|
||||
|
||||
|
||||
def part_2(data):
|
||||
xs = str_to_ints(data)
|
||||
a = Amp(xs)
|
||||
current_dir_idx = 0
|
||||
pos = (0, 0)
|
||||
white_panels = set([pos])
|
||||
got_painted = set()
|
||||
|
||||
while not a.done:
|
||||
if pos in white_panels:
|
||||
a.feed(WHITE)
|
||||
else:
|
||||
a.feed(BLACK)
|
||||
a.go()
|
||||
a.go()
|
||||
while a.outputs:
|
||||
paint_color = a.pop()
|
||||
if paint_color == BLACK:
|
||||
if pos in white_panels:
|
||||
white_panels.remove(pos)
|
||||
elif paint_color == WHITE:
|
||||
white_panels.add(pos)
|
||||
else:
|
||||
assert False
|
||||
got_painted.add(pos)
|
||||
turn_dir_code = a.pop()
|
||||
turn_dir = -1 if turn_dir_code == 0 else 1
|
||||
current_dir_idx = (current_dir_idx + turn_dir) % len(DIRS)
|
||||
pos = add2(pos, DIRS[current_dir_idx])
|
||||
|
||||
white_panels = [(p[1], p[0]) for p in white_panels]
|
||||
xs = [p[0] for p in white_panels]
|
||||
ys = [p[1] for p in white_panels]
|
||||
white_panels_set = set(white_panels)
|
||||
x_min, x_max = min(xs), max(xs)
|
||||
y_min, y_max = min(ys), max(ys)
|
||||
for y in range(y_min, y_max + 1):
|
||||
s = ""
|
||||
for x in range(x_min, x_max + 1):
|
||||
if (x, y) in white_panels_set:
|
||||
s += "X"
|
||||
else:
|
||||
s += " "
|
||||
print(s)
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
87
2019/d12.py
Normal file
87
2019/d12.py
Normal file
@@ -0,0 +1,87 @@
|
||||
from lib import get_data, str_to_ints
|
||||
from math import lcm
|
||||
|
||||
|
||||
def freeze(coords, velos):
|
||||
coords = tuple(map(tuple, coords))
|
||||
velos = tuple(map(tuple, velos))
|
||||
return coords + velos
|
||||
|
||||
|
||||
def part_1(data):
|
||||
coords = []
|
||||
velos = []
|
||||
for line in data.splitlines():
|
||||
coords.append(str_to_ints(line))
|
||||
velos.append([0, 0, 0])
|
||||
|
||||
for _ in range(1_000):
|
||||
# apply gravity
|
||||
for i in range(len(coords)):
|
||||
for j in range(i + 1, len(coords)):
|
||||
for d in range(3):
|
||||
if coords[i][d] > coords[j][d]:
|
||||
velos[i][d] -= 1
|
||||
velos[j][d] += 1
|
||||
elif coords[i][d] < coords[j][d]:
|
||||
velos[i][d] += 1
|
||||
velos[j][d] -= 1
|
||||
|
||||
# update coords
|
||||
for i in range(len(coords)):
|
||||
for d in range(3):
|
||||
coords[i][d] += velos[i][d]
|
||||
|
||||
r = 0
|
||||
for i in range(len(coords)):
|
||||
p = sum(map(abs, coords[i]))
|
||||
k = sum(map(abs, velos[i]))
|
||||
r += p * k
|
||||
print(r)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
steps = []
|
||||
for i in range(3):
|
||||
coords = []
|
||||
velos = []
|
||||
|
||||
for line in data.splitlines():
|
||||
coords.append(str_to_ints(line)[i])
|
||||
velos.append(0)
|
||||
|
||||
seen = {}
|
||||
for step in range(1_000_000):
|
||||
state = tuple(coords) + tuple(velos)
|
||||
if state in seen:
|
||||
steps.append(step)
|
||||
break
|
||||
else:
|
||||
seen[state] = step
|
||||
|
||||
for i in range(len(coords)):
|
||||
for j in range(i + 1, len(coords)):
|
||||
if coords[i] > coords[j]:
|
||||
velos[i] -= 1
|
||||
velos[j] += 1
|
||||
elif coords[i] < coords[j]:
|
||||
velos[i] += 1
|
||||
velos[j] -= 1
|
||||
|
||||
# update coords
|
||||
for i in range(len(coords)):
|
||||
coords[i] += velos[i]
|
||||
|
||||
# Intuition: Find when position repeats on each axis and then find lowest
|
||||
# common multiple for all three values.
|
||||
print(lcm(*steps))
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
50
2019/d13.py
Normal file
50
2019/d13.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from lib import get_data, str_to_ints
|
||||
from d9 import Amp
|
||||
|
||||
|
||||
def part_1(data):
|
||||
xs = str_to_ints(data)
|
||||
a = Amp(xs)
|
||||
while not a.done:
|
||||
a.go()
|
||||
|
||||
r = sum([a.outputs[i : i + 3][2] == 2 for i in range(0, len(a.outputs), 3)])
|
||||
print(r)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
xs = str_to_ints(data)
|
||||
xs[0] = 2 # play for free
|
||||
a = Amp(xs)
|
||||
ball_x = 0
|
||||
paddle_x = 0
|
||||
score = 0
|
||||
while not a.done:
|
||||
a.go()
|
||||
if len(a.outputs) == 3:
|
||||
x, y, tile_id = a.pop(), a.pop(), a.pop()
|
||||
if x == -1 and y == 0:
|
||||
score = tile_id
|
||||
elif tile_id == 4:
|
||||
ball_x = x
|
||||
elif tile_id == 3:
|
||||
paddle_x = x
|
||||
if a.input_required:
|
||||
a.input_required = False
|
||||
x = 0
|
||||
if ball_x < paddle_x:
|
||||
x = -1
|
||||
elif ball_x > paddle_x:
|
||||
x = 1
|
||||
a.feed(x)
|
||||
print(score)
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
83
2019/d14.py
Normal file
83
2019/d14.py
Normal file
@@ -0,0 +1,83 @@
|
||||
from lib import get_data
|
||||
from math import ceil
|
||||
|
||||
|
||||
def ore_for_fuel(fuel_n, reactions_store):
|
||||
ore_qty = 0
|
||||
required = [(fuel_n, "FUEL")]
|
||||
store = {}
|
||||
while required:
|
||||
required_qty, required_code = required.pop()
|
||||
if required_code == "ORE":
|
||||
ore_qty += required_qty
|
||||
continue
|
||||
|
||||
if required_code in store:
|
||||
if store[required_code] == required_qty:
|
||||
del store[required_code]
|
||||
continue
|
||||
elif store[required_code] > required_qty:
|
||||
store[required_code] -= required_qty
|
||||
continue
|
||||
elif store[required_code] < required_qty:
|
||||
required_qty -= store[required_code]
|
||||
del store[required_code]
|
||||
|
||||
inputs, output = reactions_store[required_code]
|
||||
output_qty, output_code = output
|
||||
assert required_code == output_code
|
||||
|
||||
# how often must this process be executed
|
||||
n = ceil(required_qty / output_qty)
|
||||
|
||||
leftover_output_qty = n * output_qty - required_qty
|
||||
assert leftover_output_qty >= 0
|
||||
|
||||
if leftover_output_qty > 0:
|
||||
store[output_code] = leftover_output_qty
|
||||
|
||||
for qty_input, input_code in inputs:
|
||||
required.append((qty_input * n, input_code))
|
||||
return ore_qty
|
||||
|
||||
|
||||
def part_1(data):
|
||||
reactions = []
|
||||
to_get = {}
|
||||
for line in data.splitlines():
|
||||
left, right = line.split(" => ")
|
||||
inputs = left.split(", ")
|
||||
inputs = [(int(i.split()[0]), i.split()[1]) for i in inputs]
|
||||
output = (int(right.split()[0]), right.split()[1])
|
||||
reactions.append((inputs, output))
|
||||
if output[1] in to_get:
|
||||
assert False
|
||||
else:
|
||||
to_get[output[1]] = (inputs, output)
|
||||
|
||||
ore_qty = ore_for_fuel(1, to_get)
|
||||
print(ore_qty)
|
||||
|
||||
min_fuel, max_fuel = 1, 100_000_000
|
||||
target = 10**12 # trillion
|
||||
while max_fuel - min_fuel > 1:
|
||||
half_fuel = min_fuel + (max_fuel - min_fuel) // 2
|
||||
ore_qty = ore_for_fuel(half_fuel, to_get)
|
||||
if ore_qty < target:
|
||||
min_fuel = half_fuel
|
||||
elif ore_qty > target:
|
||||
max_fuel = half_fuel
|
||||
else:
|
||||
min_fuel = half_fuel
|
||||
max_fuel = half_fuel
|
||||
|
||||
print(min_fuel)
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
115
2019/d15.py
Normal file
115
2019/d15.py
Normal file
@@ -0,0 +1,115 @@
|
||||
from lib import get_data, str_to_ints, add2
|
||||
from d9 import Amp
|
||||
|
||||
|
||||
DIRS = {1: (-1, 0), 2: (1, 0), 3: (0, -1), 4: (0, 1)}
|
||||
PATH_REVERSED = {1: 2, 2: 1, 3: 4, 4: 3}
|
||||
|
||||
|
||||
def print_field(fields, walls, pos):
|
||||
dim = 40
|
||||
for y in range(-dim, dim + 1):
|
||||
row = ""
|
||||
for x in range(-dim, dim + 1):
|
||||
if (y, x) == pos:
|
||||
row += "X"
|
||||
elif (y, x) in fields:
|
||||
row += "."
|
||||
elif (y, x) in walls:
|
||||
row += "|"
|
||||
else:
|
||||
row += " "
|
||||
print(row)
|
||||
|
||||
|
||||
def part_1(data):
|
||||
xs = str_to_ints(data)
|
||||
a = Amp(xs)
|
||||
|
||||
walls = set()
|
||||
pos = (0, 0)
|
||||
target = None
|
||||
dir = None
|
||||
path = []
|
||||
dir_cmds_for_pos = {pos: [1, 2, 3, 4]}
|
||||
|
||||
while not a.done:
|
||||
if len(dir_cmds_for_pos[pos]) > 0:
|
||||
dir_cmd = dir_cmds_for_pos[pos].pop()
|
||||
dir = DIRS[dir_cmd]
|
||||
|
||||
new_pos = add2(pos, dir)
|
||||
if new_pos in dir_cmds_for_pos:
|
||||
continue
|
||||
|
||||
a.feed(dir_cmd)
|
||||
a.go()
|
||||
status = a.pop()
|
||||
|
||||
if status == 0:
|
||||
walls.add(new_pos)
|
||||
elif status == 1:
|
||||
pos = new_pos
|
||||
path.append(dir_cmd)
|
||||
elif status == 2:
|
||||
pos = new_pos
|
||||
path.append(dir_cmd)
|
||||
target = pos
|
||||
|
||||
if pos not in dir_cmds_for_pos:
|
||||
dir_cmds_for_pos[pos] = [1, 2, 3, 4]
|
||||
|
||||
elif len(path) > 0:
|
||||
dir_cmd = PATH_REVERSED[path.pop()]
|
||||
dir = DIRS[dir_cmd]
|
||||
pos = add2(pos, dir)
|
||||
a.feed(dir_cmd)
|
||||
a.go()
|
||||
status = a.pop()
|
||||
assert status == 1 or status == 2
|
||||
elif target is not None:
|
||||
break
|
||||
else:
|
||||
break
|
||||
fields = set(dir_cmds_for_pos.keys())
|
||||
# print_field(fields, walls, pos)
|
||||
|
||||
seen = set()
|
||||
to_visit = [(0, 0)]
|
||||
steps = 0
|
||||
while len(to_visit) > 0:
|
||||
new = []
|
||||
for current in to_visit:
|
||||
if current == target:
|
||||
print(steps)
|
||||
seen.add(current)
|
||||
for dir in DIRS.values():
|
||||
nb = add2(current, dir)
|
||||
if nb in fields and nb not in seen:
|
||||
new.append(nb)
|
||||
to_visit = new
|
||||
steps += 1
|
||||
|
||||
seen = set()
|
||||
to_visit = [target]
|
||||
steps = 0
|
||||
while len(to_visit) > 0:
|
||||
new = []
|
||||
for current in to_visit:
|
||||
seen.add(current)
|
||||
for dir in DIRS.values():
|
||||
nb = add2(current, dir)
|
||||
if nb in fields and nb not in seen:
|
||||
new.append(nb)
|
||||
to_visit = new
|
||||
steps += 1
|
||||
print(steps - 1)
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
132
2019/d16.py
Normal file
132
2019/d16.py
Normal file
@@ -0,0 +1,132 @@
|
||||
from lib import get_data
|
||||
|
||||
|
||||
def part_1_with_numpy(data):
|
||||
import numpy as np
|
||||
|
||||
def make_base_matrix(pattern, n):
|
||||
xss = []
|
||||
for round in range(n):
|
||||
xs = [pattern[((i + 1) // (round + 1)) % len(pattern)] for i in range(n)]
|
||||
xss.append(xs)
|
||||
return np.array(xss)
|
||||
|
||||
pattern = [0, 1, 0, -1]
|
||||
input = int(data.strip())
|
||||
v = np.array(list(map(int, str(input))))
|
||||
m = make_base_matrix(pattern, len(v))
|
||||
func = np.vectorize(lambda x: abs(x) % 10)
|
||||
|
||||
for _ in range(100):
|
||||
v = func(np.dot(m, v))
|
||||
print("".join(map(str, v[:8].tolist())))
|
||||
|
||||
|
||||
def phase(digits_in):
|
||||
pattern = [0, 1, 0, -1]
|
||||
digits_out = []
|
||||
for round in range(len(digits_in)):
|
||||
i, out = 0, 0
|
||||
while i < len(digits_in):
|
||||
pattern_i = ((i + 1) // (round + 1)) % len(pattern)
|
||||
out += pattern[pattern_i] * digits_in[i]
|
||||
i += 1
|
||||
out = abs(out) % 10
|
||||
digits_out.append(out)
|
||||
return digits_out
|
||||
|
||||
|
||||
def phase_with_offset(digits_in, pattern, offset):
|
||||
digits_out = digits_in.copy()
|
||||
for round in range(offset, len(digits_in)):
|
||||
i, out = 0, 0
|
||||
# print(round)
|
||||
|
||||
pattern_value = pattern[((i + 1) // (round + 1)) % len(pattern)]
|
||||
if pattern_value == 0:
|
||||
i += round
|
||||
|
||||
while i < len(digits_in):
|
||||
pattern_i = ((i + 1) // (round + 1)) % len(pattern)
|
||||
pattern_value = pattern[pattern_i]
|
||||
if pattern_value != 0:
|
||||
out += pattern_value * sum(
|
||||
digits_in[i : min(i + 1 + round, len(digits_in))]
|
||||
)
|
||||
i += round + 1
|
||||
|
||||
out = abs(out) % 10
|
||||
digits_out[round] = out
|
||||
|
||||
return digits_out
|
||||
|
||||
|
||||
def part_1(data):
|
||||
pattern = [0, 1, 0, -1]
|
||||
|
||||
input = list(map(int, (data.strip())))
|
||||
for _ in range(100):
|
||||
input = phase_with_offset(input, pattern, 0)
|
||||
print("".join(map(str, input[:8])))
|
||||
|
||||
out = list(map(int, (data.strip()))) * 10_000
|
||||
offset = int("".join(map(str, out[:7])))
|
||||
for _ in range(100):
|
||||
for i in range(len(out) - 2, len(out) - 1_000_000, -1):
|
||||
out[i] = abs(out[i] + out[i + 1]) % 10
|
||||
print("".join(map(str, out[offset : offset + 8])))
|
||||
|
||||
# digits = 40
|
||||
# for round in range(digits):
|
||||
# s = ""
|
||||
# for i in range(digits):
|
||||
# pattern_i = ((i + 1) // (round + 1)) % len(pattern)
|
||||
# pattern_value = pattern[pattern_i]
|
||||
# if pattern_value == 0:
|
||||
# s += " "
|
||||
# elif pattern_value == 1:
|
||||
# s += "+"
|
||||
# elif pattern_value == -1:
|
||||
# s += "-"
|
||||
# else:
|
||||
# assert False
|
||||
# print(s)
|
||||
# return
|
||||
|
||||
# Just here to document my thought process. Mental hack: Assume that you
|
||||
# have the capability to solve the problem easily.
|
||||
#
|
||||
# What do I know?
|
||||
#
|
||||
# 1. There is a solution. Other people have solved it.
|
||||
# 2. The solution is not crazy. It will be rather obvious.
|
||||
# 3. 6_500_000 * 6_500_000 is definitely too much to brute force.
|
||||
# 4. Can we go from O(N^2) to O(N) somehow? Yes, that's what we have to do.
|
||||
# The whole point of FFT is to get from O(N^2) to O(N*log(N)). Now,
|
||||
# how exactly do we do that?
|
||||
#
|
||||
# Ways to improve performance:
|
||||
#
|
||||
# 1. Speed up `phase` significantly. Yes, but how?
|
||||
# 2. Only compute a subset of the lists? - No!
|
||||
# 3. Discover some kind of pattern? - No!
|
||||
#
|
||||
# Assumptions:
|
||||
#
|
||||
# 1. I need every digit of the previous round. - False!
|
||||
# 2. I cannot just operate on a subset. - False!
|
||||
#
|
||||
# Non-approaches:
|
||||
#
|
||||
# 1. Fancy recursive algorithm that selectively picks fields.
|
||||
# 2. Pattern detection or subset consideration.
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
# part_1_with_numpy(data)
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
136
2019/d17.py
Normal file
136
2019/d17.py
Normal file
@@ -0,0 +1,136 @@
|
||||
from lib import get_data, str_to_ints, Grid2D, add2
|
||||
from collections import defaultdict
|
||||
from d9 import Amp
|
||||
|
||||
DIRS = [
|
||||
(-1, 0),
|
||||
(0, 1),
|
||||
(1, 0),
|
||||
(0, -1),
|
||||
]
|
||||
|
||||
DIRCHAR = list("^>v<")
|
||||
|
||||
|
||||
def find_path(g):
|
||||
(pos,) = g.find("^><v")
|
||||
assert g[pos] == "^"
|
||||
|
||||
to_visit = set(g.find("#"))
|
||||
seen = defaultdict(int)
|
||||
dir = DIRS[0] # up
|
||||
seen[pos] = 1
|
||||
|
||||
def best_neighbor(pos):
|
||||
best_nb = None
|
||||
seen_count = 20
|
||||
for nb in g.neighbors_ort(pos):
|
||||
if nb in to_visit:
|
||||
return nb
|
||||
if nb in seen and seen[nb] < seen_count:
|
||||
seen_count = seen[nb]
|
||||
best_nb = nb
|
||||
assert best_nb is not None
|
||||
return best_nb
|
||||
|
||||
path = ""
|
||||
while to_visit:
|
||||
|
||||
new_pos = add2(pos, dir)
|
||||
if new_pos in to_visit or (new_pos in seen and seen[new_pos] < 2):
|
||||
path += "F"
|
||||
g[pos] = "#"
|
||||
pos = new_pos
|
||||
if pos in to_visit:
|
||||
to_visit.remove(pos)
|
||||
g[pos] = DIRCHAR[DIRS.index(dir)]
|
||||
seen[pos] += 1
|
||||
else:
|
||||
best_nb = best_neighbor(pos)
|
||||
while add2(pos, dir) != best_nb:
|
||||
path += "R"
|
||||
dir = DIRS[(DIRS.index(dir) + 1) % len(DIRS)]
|
||||
g[pos] = DIRCHAR[DIRS.index(dir)]
|
||||
|
||||
# For debugging:
|
||||
# g.print()
|
||||
# input()
|
||||
# print()
|
||||
path = path.replace("RRR", "L")
|
||||
return path
|
||||
|
||||
|
||||
def part_1(data):
|
||||
xs = str_to_ints(data)
|
||||
a = Amp(xs)
|
||||
text = ""
|
||||
while not a.done:
|
||||
a.go()
|
||||
while a.outputs:
|
||||
o = a.pop()
|
||||
text += chr(o)
|
||||
|
||||
result = 0
|
||||
g = Grid2D(text)
|
||||
for r in range(g.n_rows):
|
||||
for c in range(g.n_cols):
|
||||
nbs = g.neighbors_ort((r, c))
|
||||
if (
|
||||
g[(r, c)] == "#"
|
||||
and len(nbs) == 4
|
||||
and all([g[(nr, nc)] == "#" for nr, nc in nbs])
|
||||
):
|
||||
# g[(r, c)] = 'o'
|
||||
result += r * c
|
||||
print(result)
|
||||
# g.print()
|
||||
|
||||
# Merge F commands into counts
|
||||
path = find_path(g)
|
||||
path = list(path)
|
||||
new_path = []
|
||||
i = 0
|
||||
while i < len(path):
|
||||
if path[i] == "R":
|
||||
new_path.append("R")
|
||||
i += 1
|
||||
elif path[i] == "L":
|
||||
new_path.append("L")
|
||||
i += 1
|
||||
elif path[i] == "F":
|
||||
count = 0
|
||||
while i < len(path) and path[i] == "F":
|
||||
count += 1
|
||||
i += 1
|
||||
new_path.append(str(count))
|
||||
else:
|
||||
assert False
|
||||
path = new_path
|
||||
print("Manually translate into commands:", "".join(path))
|
||||
|
||||
# manually created from above output
|
||||
inst = (
|
||||
"A,A,B,C,B,C,B,C,A,C\n"
|
||||
"R,6,L,8,R,8\n"
|
||||
"R,4,R,6,R,6,R,4,R,4\n"
|
||||
"L,8,R,6,L,10,L,10\n"
|
||||
"n\n"
|
||||
)
|
||||
|
||||
xs = str_to_ints(data)
|
||||
xs[0] = 2
|
||||
a = Amp(xs)
|
||||
for c in inst:
|
||||
a.feed(ord(c))
|
||||
while not a.done:
|
||||
a.go()
|
||||
print(a.outputs[-1])
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
199
2019/d18.py
Normal file
199
2019/d18.py
Normal file
@@ -0,0 +1,199 @@
|
||||
from lib import get_data, Grid2D, LETTERS_UPPER, LETTERS_LOWER, add2
|
||||
from collections import defaultdict
|
||||
|
||||
LETTERS = LETTERS_UPPER + LETTERS_LOWER
|
||||
|
||||
data = """#############
|
||||
#g#f.D#..h#l#
|
||||
#F###e#E###.#
|
||||
#dCba...BcIJ#
|
||||
#####.@.#####
|
||||
#nK.L...G...#
|
||||
#M###N#H###.#
|
||||
#o#m..#i#jk.#
|
||||
#############"""
|
||||
|
||||
|
||||
def part_1(data):
|
||||
g = Grid2D(data)
|
||||
(start,) = g.find("@")
|
||||
starts = [start]
|
||||
|
||||
graph = defaultdict(set)
|
||||
starts_seen = set()
|
||||
|
||||
while starts:
|
||||
start = starts.pop()
|
||||
if start in starts_seen:
|
||||
continue
|
||||
else:
|
||||
starts_seen.add(start)
|
||||
|
||||
start_symbol = g[start]
|
||||
xs = [start]
|
||||
seen = set()
|
||||
|
||||
for steps in range(1_000):
|
||||
nxs = []
|
||||
|
||||
for x in xs:
|
||||
if x in seen:
|
||||
continue
|
||||
else:
|
||||
seen.add(x)
|
||||
|
||||
for nb in g.neighbors_ort(x):
|
||||
if g[nb] == ".":
|
||||
nxs.append(nb)
|
||||
elif g[nb] in LETTERS:
|
||||
symbol = g[nb]
|
||||
if symbol != start_symbol:
|
||||
graph[symbol].add((start_symbol, steps + 1))
|
||||
graph[start_symbol].add((symbol, steps + 1))
|
||||
starts.append(nb)
|
||||
xs = nxs
|
||||
if len(xs) == 0:
|
||||
break
|
||||
|
||||
all_keys = [g[p] for p in g.find(LETTERS_LOWER)]
|
||||
poss = [(0, 0, tuple("@"), "@")]
|
||||
best: dict[tuple[tuple, str], int] = {(("@",), "@"): 0}
|
||||
|
||||
min_dist = 10**9
|
||||
while poss:
|
||||
current_distance, key_count, keys, symbol = poss.pop()
|
||||
# print(current_distance, key_count, keys, symbol)
|
||||
|
||||
if key_count - 1 == len(all_keys):
|
||||
min_dist = min(min_dist, current_distance)
|
||||
continue
|
||||
|
||||
for next_symbol, distance in graph[symbol]:
|
||||
if next_symbol in LETTERS_UPPER and not next_symbol.lower() in keys:
|
||||
continue
|
||||
|
||||
if next_symbol in LETTERS_LOWER:
|
||||
new_keys = set(keys)
|
||||
new_keys.add(next_symbol)
|
||||
new_keys = tuple(sorted(new_keys))
|
||||
new_key_count = len(new_keys)
|
||||
else:
|
||||
new_keys = keys
|
||||
new_key_count = key_count
|
||||
|
||||
new_distance = current_distance + distance
|
||||
key = (new_keys, next_symbol)
|
||||
if (key not in best) or (key in best and best[key] > new_distance):
|
||||
best[key] = new_distance
|
||||
poss.append((new_distance, new_key_count, new_keys, next_symbol))
|
||||
print(min_dist)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
g = Grid2D(data)
|
||||
(start,) = g.find("@")
|
||||
g[start] = "#"
|
||||
g[add2(start, (-1, 0))] = "#"
|
||||
g[add2(start, (1, 0))] = "#"
|
||||
g[add2(start, (0, 1))] = "#"
|
||||
g[add2(start, (0, -1))] = "#"
|
||||
|
||||
g[add2(start, (-1, -1))] = "0"
|
||||
g[add2(start, (-1, 1))] = "1"
|
||||
g[add2(start, (1, 1))] = "2"
|
||||
g[add2(start, (1, -1))] = "3"
|
||||
|
||||
starts = g.find("0") + g.find("1") + g.find("2") + g.find("3")
|
||||
graph = defaultdict(set)
|
||||
starts_seen = set()
|
||||
|
||||
while starts:
|
||||
start = starts.pop()
|
||||
if start in starts_seen:
|
||||
continue
|
||||
else:
|
||||
starts_seen.add(start)
|
||||
|
||||
start_symbol = g[start]
|
||||
xs = [start]
|
||||
seen = set()
|
||||
|
||||
for steps in range(1_000):
|
||||
nxs = []
|
||||
|
||||
for x in xs:
|
||||
if x in seen:
|
||||
continue
|
||||
else:
|
||||
seen.add(x)
|
||||
|
||||
for nb in g.neighbors_ort(x):
|
||||
if g[nb] == ".":
|
||||
nxs.append(nb)
|
||||
elif g[nb] in LETTERS:
|
||||
symbol = g[nb]
|
||||
if symbol != start_symbol:
|
||||
graph[start].add((nb, steps + 1))
|
||||
graph[nb].add((start, steps + 1))
|
||||
starts.append(nb)
|
||||
xs = nxs
|
||||
if len(xs) == 0:
|
||||
break
|
||||
# g.print()
|
||||
|
||||
all_keys = [g[p] for p in g.find(LETTERS_LOWER)]
|
||||
|
||||
robots = tuple(g.find("0") + g.find("1") + g.find("2") + g.find("3"))
|
||||
poss = [(0, tuple(), robots)]
|
||||
best: dict[tuple[tuple, tuple], int] = {(tuple(), tuple()): 0}
|
||||
|
||||
min_dist = 10**9
|
||||
while poss:
|
||||
current_distance, keys, robots = poss.pop()
|
||||
|
||||
if len(keys) == len(all_keys):
|
||||
min_dist = min(min_dist, current_distance)
|
||||
# print(min_dist)
|
||||
continue
|
||||
|
||||
# print(current_distance, keys, robots)
|
||||
for robot_i in range(len(robots)):
|
||||
robot = robots[robot_i]
|
||||
# robot_symbol = g[robot]
|
||||
for next_pos, distance in graph[robot]:
|
||||
next_symbol = g[next_pos]
|
||||
|
||||
if next_symbol in LETTERS_UPPER and not next_symbol.lower() in keys:
|
||||
continue
|
||||
|
||||
if next_symbol in LETTERS_LOWER:
|
||||
new_keys = set(keys)
|
||||
new_keys.add(next_symbol)
|
||||
new_keys = tuple(sorted(new_keys))
|
||||
else:
|
||||
new_keys = keys
|
||||
|
||||
new_distance = current_distance + distance
|
||||
|
||||
new_robots = list(robots)
|
||||
new_robots[robot_i] = next_pos
|
||||
new_robots = tuple(new_robots)
|
||||
|
||||
key = (new_keys, new_robots)
|
||||
if (key not in best) or (key in best and best[key] > new_distance):
|
||||
best[key] = new_distance
|
||||
poss.append((new_distance, new_keys, new_robots))
|
||||
|
||||
poss = sorted(poss, key=lambda xs: (xs[0], -len(xs[1])), reverse=True)
|
||||
poss = poss[-10000:]
|
||||
print(min_dist)
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
52
2019/d19.py
Normal file
52
2019/d19.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from lib import get_data, str_to_ints
|
||||
from d9 import Amp
|
||||
|
||||
|
||||
def isin(xs, x, y):
|
||||
a = Amp(xs, 100)
|
||||
a.feed(x)
|
||||
a.feed(y)
|
||||
a.go()
|
||||
return a.pop()
|
||||
|
||||
|
||||
def part_1(data):
|
||||
xs = str_to_ints(data)
|
||||
r = 0
|
||||
for y in range(50):
|
||||
for x in range(50):
|
||||
o = isin(xs, x, y)
|
||||
if o == 1:
|
||||
print("#", end="")
|
||||
else:
|
||||
print(" ", end="")
|
||||
r += o
|
||||
print()
|
||||
print(r)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
xs = str_to_ints(data)
|
||||
off = 99
|
||||
x, y = 3, 4
|
||||
while True:
|
||||
y += 1
|
||||
while True:
|
||||
o = isin(xs, x, y)
|
||||
if o == 1:
|
||||
break
|
||||
x += 1
|
||||
|
||||
if isin(xs, x, y - off) and isin(xs, x + off, y - off) and isin(xs, x + off, y):
|
||||
print(x * 10_000 + y - off)
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
41
2019/d2.py
Normal file
41
2019/d2.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from lib import get_data, str_to_ints
|
||||
|
||||
|
||||
def part_1(data):
|
||||
xs = str_to_ints(data)
|
||||
xs[1] = 12
|
||||
xs[2] = 2
|
||||
for i in range(0, len(xs), 4):
|
||||
match xs[i]:
|
||||
case 1:
|
||||
xs[xs[i + 3]] = xs[xs[i + 1]] + xs[xs[i + 2]]
|
||||
case 2:
|
||||
xs[xs[i + 3]] = xs[xs[i + 1]] * xs[xs[i + 2]]
|
||||
print(xs[0])
|
||||
|
||||
|
||||
def part_2(data):
|
||||
for noun in range(1, 101):
|
||||
for verb in range(1, 101):
|
||||
xs = str_to_ints(data)
|
||||
xs[1] = noun
|
||||
xs[2] = verb
|
||||
for i in range(0, len(xs), 4):
|
||||
match xs[i]:
|
||||
case 1:
|
||||
xs[xs[i + 3]] = xs[xs[i + 1]] + xs[xs[i + 2]]
|
||||
case 2:
|
||||
xs[xs[i + 3]] = xs[xs[i + 1]] * xs[xs[i + 2]]
|
||||
if xs[0] == 19690720:
|
||||
print(100 * noun + verb)
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
154
2019/d20.py
Normal file
154
2019/d20.py
Normal file
@@ -0,0 +1,154 @@
|
||||
from lib import get_data, Grid2D, LETTERS_UPPER
|
||||
from collections import defaultdict
|
||||
|
||||
data = get_data(__file__)
|
||||
|
||||
g = Grid2D(data)
|
||||
|
||||
start = None
|
||||
end = None
|
||||
warps = defaultdict(list)
|
||||
|
||||
inner = set()
|
||||
outer = set()
|
||||
|
||||
for row in range(g.n_rows - 2):
|
||||
for col in range(g.n_cols - 2):
|
||||
a, b, c = g[(row, col)], g[(row, col + 1)], g[(row, col + 2)]
|
||||
x, y, z = g[(row, col)], g[(row + 1, col)], g[(row + 2, col)]
|
||||
if a in LETTERS_UPPER and b in LETTERS_UPPER and c == ".":
|
||||
warps[a + b].append((row, col + 2))
|
||||
if col == 0:
|
||||
outer.add((row, col + 2))
|
||||
else:
|
||||
inner.add((row, col + 2))
|
||||
elif a == "." and b in LETTERS_UPPER and c in LETTERS_UPPER:
|
||||
warps[b + c].append((row, col))
|
||||
if col + 3 == g.n_cols:
|
||||
outer.add((row, col))
|
||||
else:
|
||||
inner.add((row, col))
|
||||
|
||||
if x in LETTERS_UPPER and y in LETTERS_UPPER and z == ".":
|
||||
if x + y == "AA":
|
||||
start = (row + 2, col)
|
||||
else:
|
||||
warps[x + y].append((row + 2, col))
|
||||
if row == 0:
|
||||
outer.add((row + 2, col))
|
||||
else:
|
||||
inner.add((row + 2, col))
|
||||
elif x == "." and y in LETTERS_UPPER and z in LETTERS_UPPER:
|
||||
if y + z == "ZZ":
|
||||
end = (row, col)
|
||||
else:
|
||||
warps[y + z].append((row, col))
|
||||
if row + 3 == g.n_rows:
|
||||
outer.add((row, col))
|
||||
else:
|
||||
inner.add((row, col))
|
||||
|
||||
|
||||
graph = defaultdict(list)
|
||||
allnodes = set([start, end])
|
||||
for key, (a, b) in warps.items():
|
||||
graph[a].append((b, 1))
|
||||
graph[b].append((a, 1))
|
||||
allnodes.add(a)
|
||||
allnodes.add(b)
|
||||
|
||||
for startnode in allnodes:
|
||||
to_visit = [startnode]
|
||||
steps = 0
|
||||
seen = set()
|
||||
while to_visit:
|
||||
steps += 1
|
||||
new_to_visit = []
|
||||
for node in to_visit:
|
||||
if node in seen:
|
||||
continue
|
||||
else:
|
||||
seen.add(node)
|
||||
|
||||
assert node is not None
|
||||
for nb in g.neighbors_ort(node):
|
||||
if nb in seen:
|
||||
continue
|
||||
|
||||
if nb in allnodes:
|
||||
if not (nb, steps) in graph[startnode]:
|
||||
graph[startnode].append((nb, steps))
|
||||
if not (startnode, steps) in graph[nb]:
|
||||
graph[nb].append((startnode, steps))
|
||||
seen.add(nb)
|
||||
elif g[nb] == ".":
|
||||
new_to_visit.append(nb)
|
||||
to_visit = new_to_visit
|
||||
|
||||
shortest = {start: 0}
|
||||
to_visit = [start]
|
||||
seen = set()
|
||||
|
||||
while to_visit:
|
||||
to_visit.sort(key=lambda node: shortest[node], reverse=True)
|
||||
current = to_visit.pop()
|
||||
if current in seen:
|
||||
continue
|
||||
else:
|
||||
seen.add(current)
|
||||
|
||||
for nb, dist in graph[current]:
|
||||
new_dist = shortest[current] + dist
|
||||
if not nb in shortest:
|
||||
shortest[nb] = new_dist
|
||||
elif new_dist < shortest[nb]:
|
||||
shortest[nb] = new_dist
|
||||
if nb not in seen:
|
||||
to_visit.append(nb)
|
||||
|
||||
print(shortest[end])
|
||||
|
||||
shortest = {(start, 0): 0}
|
||||
to_visit = [(start, 0)]
|
||||
seen = set()
|
||||
|
||||
while to_visit:
|
||||
to_visit.sort(key=lambda node: shortest[node], reverse=True)
|
||||
current, level = to_visit.pop()
|
||||
|
||||
if (current, level) in seen:
|
||||
continue
|
||||
else:
|
||||
seen.add((current, level))
|
||||
|
||||
for nb, dist in graph[current]:
|
||||
new_dist = shortest[(current, level)] + dist
|
||||
|
||||
if nb == end and level == 0:
|
||||
print(new_dist)
|
||||
to_visit = None
|
||||
break
|
||||
elif nb == end:
|
||||
continue
|
||||
elif nb == start:
|
||||
continue
|
||||
|
||||
if dist == 1:
|
||||
if current in inner:
|
||||
new_level = level + 1
|
||||
elif current in outer:
|
||||
new_level = level - 1
|
||||
else:
|
||||
assert False
|
||||
else:
|
||||
new_level = level
|
||||
|
||||
if new_level < 0:
|
||||
continue
|
||||
|
||||
nn = (nb, new_level)
|
||||
if not nn in shortest:
|
||||
shortest[nn] = new_dist
|
||||
elif new_dist < shortest[nn]:
|
||||
shortest[nn] = new_dist
|
||||
to_visit.append(nn)
|
||||
57
2019/d21.py
Normal file
57
2019/d21.py
Normal file
@@ -0,0 +1,57 @@
|
||||
from lib import get_data, str_to_ints
|
||||
from d9 import Amp
|
||||
|
||||
data = get_data(__file__)
|
||||
xs = str_to_ints(data)
|
||||
|
||||
script = """NOT A J
|
||||
AND A J
|
||||
NOT C T
|
||||
AND D T
|
||||
OR T J
|
||||
NOT A T
|
||||
OR T J
|
||||
"""
|
||||
|
||||
a = Amp(xs)
|
||||
while not a.done:
|
||||
a.go()
|
||||
if a.input_required:
|
||||
for c in script:
|
||||
a.feed(ord(c))
|
||||
for c in "WALK\n":
|
||||
a.feed(ord(c))
|
||||
|
||||
while a.outputs:
|
||||
c = a.pop()
|
||||
try:
|
||||
chr(c)
|
||||
# print(chr(c), end="")
|
||||
except ValueError:
|
||||
print(c)
|
||||
|
||||
script = """NOT C J
|
||||
AND H J
|
||||
NOT A T
|
||||
OR T J
|
||||
NOT B T
|
||||
OR T J
|
||||
AND D J
|
||||
"""
|
||||
|
||||
a = Amp(xs)
|
||||
while not a.done:
|
||||
a.go()
|
||||
if a.input_required:
|
||||
for c in script:
|
||||
a.feed(ord(c))
|
||||
for c in "RUN\n":
|
||||
a.feed(ord(c))
|
||||
|
||||
while a.outputs:
|
||||
c = a.pop()
|
||||
try:
|
||||
chr(c)
|
||||
# print(chr(c), end="")
|
||||
except ValueError:
|
||||
print(c)
|
||||
118
2019/d22.py
Normal file
118
2019/d22.py
Normal file
@@ -0,0 +1,118 @@
|
||||
from lib import get_data, str_to_ints, mod_inverse
|
||||
from math import gcd
|
||||
|
||||
data = get_data(__file__)
|
||||
deck = list(range(10007))
|
||||
|
||||
# part 1
|
||||
for line in data.splitlines():
|
||||
if "new stack" in line:
|
||||
deck = list(reversed(deck))
|
||||
elif "cut" in line:
|
||||
(n,) = str_to_ints(line)
|
||||
deck = deck[n:] + deck[:n]
|
||||
elif "increment" in line:
|
||||
new_deck = [-1] * len(deck)
|
||||
pos = 0
|
||||
(n,) = str_to_ints(line)
|
||||
deck = list(reversed(deck))
|
||||
while deck:
|
||||
new_deck[pos] = deck.pop()
|
||||
pos = (pos + n) % len(new_deck)
|
||||
deck = new_deck
|
||||
else:
|
||||
assert False
|
||||
|
||||
print(deck.index(2019))
|
||||
|
||||
len = 10007
|
||||
orig_index = 2019
|
||||
|
||||
# figure out how to reverse...
|
||||
index = orig_index
|
||||
for line in data.splitlines():
|
||||
if "new stack" in line:
|
||||
new_index = len - (index + 1)
|
||||
rev_index = -(new_index - len + 1)
|
||||
assert rev_index == index
|
||||
index = new_index
|
||||
elif "cut" in line:
|
||||
(cut,) = str_to_ints(line)
|
||||
cut = (len + cut) % len
|
||||
if index >= cut:
|
||||
new_index = index - cut
|
||||
else:
|
||||
new_index = (len - cut) + index
|
||||
rev_index = (new_index + cut) % len
|
||||
assert rev_index == index
|
||||
index = new_index
|
||||
# calculate index from new_index and store in rev_index
|
||||
elif "increment" in line:
|
||||
(n,) = str_to_ints(line)
|
||||
assert gcd(n, len) == 1
|
||||
new_index = (n * index) % len
|
||||
m = mod_inverse(n, len)
|
||||
rev_index = (new_index * m) % len
|
||||
assert rev_index == index
|
||||
index = new_index
|
||||
|
||||
assert index == deck.index(2019)
|
||||
|
||||
# check that reverse approach works
|
||||
for line in reversed(data.splitlines()):
|
||||
if "new stack" in line:
|
||||
index = -(index - len + 1)
|
||||
elif "cut" in line:
|
||||
(cut,) = str_to_ints(line)
|
||||
cut = (len + cut) % len
|
||||
index = (index + cut) % len
|
||||
elif "increment" in line:
|
||||
(n,) = str_to_ints(line)
|
||||
assert gcd(n, len) == 1
|
||||
m = mod_inverse(n, len)
|
||||
index = (index * m) % len
|
||||
assert index == orig_index
|
||||
|
||||
lines = list(reversed(data.splitlines()))
|
||||
|
||||
# new length of deck
|
||||
len = 119315717514047
|
||||
|
||||
# get expression for one loop using sympy
|
||||
from sympy import symbols, simplify
|
||||
|
||||
index = symbols("index")
|
||||
expr = index
|
||||
for line in lines:
|
||||
if "new stack" in line:
|
||||
# index = -(index - len + 1)
|
||||
expr = -(expr - len + 1)
|
||||
elif "cut" in line:
|
||||
(cut,) = str_to_ints(line)
|
||||
cut = (len + cut) % len
|
||||
# index = (index + cut) % len
|
||||
expr = expr + cut
|
||||
elif "increment" in line:
|
||||
(n,) = str_to_ints(line)
|
||||
assert gcd(n, len) == 1
|
||||
m = mod_inverse(n, len)
|
||||
# index = (index * m) % len
|
||||
expr = expr * m
|
||||
|
||||
# we can see that expression is in the form (a - b * i) % m
|
||||
expr = simplify(expr % len)
|
||||
coeff_dict = expr.args[0].as_coefficients_dict()
|
||||
a = coeff_dict[1]
|
||||
b = -coeff_dict[index]
|
||||
|
||||
# math
|
||||
n_shuffles = 101741582076661
|
||||
r0 = 2020
|
||||
m = len
|
||||
p = (-b) % m
|
||||
p_t = pow(p, n_shuffles, m)
|
||||
inv_b1 = mod_inverse(b + 1, m)
|
||||
term1 = (p_t * r0) % m
|
||||
term2 = (a * (1 - p_t) * inv_b1) % m
|
||||
r_t = (term1 + term2) % m
|
||||
print(r_t)
|
||||
47
2019/d23.py
Normal file
47
2019/d23.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from lib import get_data, str_to_ints
|
||||
from d9 import Amp
|
||||
|
||||
xs = str_to_ints(get_data(__file__))
|
||||
first_255 = True
|
||||
nat = None
|
||||
y_prev = None
|
||||
|
||||
amps = []
|
||||
for i in range(50):
|
||||
a = Amp(xs)
|
||||
a.feed(i)
|
||||
a.go()
|
||||
amps.append(a)
|
||||
|
||||
for j in range(1_000_000):
|
||||
was_active = False
|
||||
for i, a in enumerate(amps):
|
||||
a.go()
|
||||
if a.input_required:
|
||||
a.feed(-1)
|
||||
|
||||
if len(a.outputs) > 0:
|
||||
was_active = True
|
||||
while len(a.outputs) < 3:
|
||||
a.go()
|
||||
addr = a.pop()
|
||||
x = a.pop()
|
||||
y = a.pop()
|
||||
if addr == 255:
|
||||
nat = (x, y)
|
||||
if first_255:
|
||||
print(y)
|
||||
first_255 = False
|
||||
else:
|
||||
amps[addr].feed(x)
|
||||
amps[addr].feed(y)
|
||||
|
||||
if not was_active and nat is not None:
|
||||
x, y = nat
|
||||
if y == y_prev:
|
||||
print(y)
|
||||
exit()
|
||||
amps[0].feed(x)
|
||||
amps[0].feed(y)
|
||||
nat = None
|
||||
y_prev = y
|
||||
107
2019/d24.py
Normal file
107
2019/d24.py
Normal file
@@ -0,0 +1,107 @@
|
||||
from lib import get_data, Grid2D, add2
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
data = get_data(__file__)
|
||||
|
||||
g = Grid2D(data)
|
||||
seen = set()
|
||||
while True:
|
||||
h = g.hash()
|
||||
if h in seen:
|
||||
break
|
||||
else:
|
||||
seen.add(h)
|
||||
|
||||
gn = g.clone_with_val(".")
|
||||
for r in range(g.n_rows):
|
||||
for c in range(g.n_cols):
|
||||
p = (r, c)
|
||||
nb_bugs = sum(1 for nb in g.neighbors_ort(p) if g[nb] == "#")
|
||||
if g[p] == "#" and nb_bugs == 1:
|
||||
gn[p] = "#"
|
||||
if g[p] == "." and nb_bugs in [1, 2]:
|
||||
gn[p] = "#"
|
||||
g = gn
|
||||
|
||||
|
||||
t = 0
|
||||
p = 1
|
||||
for r in range(g.n_rows):
|
||||
for c in range(g.n_cols):
|
||||
if g[(r, c)] == "#":
|
||||
t += p
|
||||
p *= 2
|
||||
print(t)
|
||||
|
||||
N = (-1, 0)
|
||||
E = (0, 1)
|
||||
S = (1, 0)
|
||||
W = (0, -1)
|
||||
DIRS = [N, E, S, W]
|
||||
ROW, COL = 0, 1
|
||||
|
||||
# data = get_data(__file__)
|
||||
g = Grid2D(data)
|
||||
bugs = [(0, r, c) for (r, c) in g.find("#")]
|
||||
middle = (g.n_rows // 2, g.n_cols // 2)
|
||||
rows, cols = g.n_rows, g.n_cols
|
||||
|
||||
|
||||
def get_edge(side):
|
||||
edges = {
|
||||
S: [(0, c) for c in range(cols)],
|
||||
N: [(rows - 1, c) for c in range(cols)],
|
||||
W: [(r, cols - 1) for r in range(rows)],
|
||||
E: [(r, 0) for r in range(rows)],
|
||||
}
|
||||
return edges[side]
|
||||
|
||||
|
||||
def get_neighbors(pos):
|
||||
level, row, col = pos
|
||||
neighbors = []
|
||||
|
||||
for d in DIRS:
|
||||
nb = add2((row, col), d)
|
||||
if nb == middle:
|
||||
for ir, ic in get_edge(d):
|
||||
neighbors.append((level - 1, ir, ic))
|
||||
elif nb[ROW] in {-1, rows} or nb[COL] in {-1, cols}:
|
||||
nr, nc = nb
|
||||
if nr == -1:
|
||||
neighbors.append((level + 1, *add2(middle, N)))
|
||||
if nr == rows:
|
||||
neighbors.append((level + 1, *add2(middle, S)))
|
||||
if nc == -1:
|
||||
neighbors.append((level + 1, *add2(middle, W)))
|
||||
if nc == cols:
|
||||
neighbors.append((level + 1, *add2(middle, E)))
|
||||
else:
|
||||
nr, nc = nb
|
||||
neighbors.append((level, nr, nc))
|
||||
return neighbors
|
||||
|
||||
|
||||
assert len(get_neighbors((0, 1, 1))) == 4
|
||||
assert len(get_neighbors((0, 3, 3))) == 4
|
||||
assert len(get_neighbors((0, 0, 3))) == 4
|
||||
assert len(get_neighbors((0, 0, 4))) == 4
|
||||
assert len(get_neighbors((0, 2, 3))) == 8
|
||||
|
||||
for _ in range(200):
|
||||
neighbors = defaultdict(int)
|
||||
current_bugs = set(bugs)
|
||||
for pos in bugs:
|
||||
for nb in get_neighbors(pos):
|
||||
neighbors[nb] += 1
|
||||
|
||||
new_bugs = []
|
||||
for pos, count in neighbors.items():
|
||||
if pos in current_bugs and count == 1:
|
||||
new_bugs.append(pos)
|
||||
if pos not in current_bugs and count in [1, 2]:
|
||||
new_bugs.append(pos)
|
||||
bugs = new_bugs
|
||||
|
||||
print(len(bugs))
|
||||
160
2019/d25.py
Normal file
160
2019/d25.py
Normal file
@@ -0,0 +1,160 @@
|
||||
from lib import get_data, ints
|
||||
from d9 import Amp
|
||||
from itertools import combinations
|
||||
|
||||
data = get_data(__file__)
|
||||
xs = ints(data)
|
||||
|
||||
commands = [
|
||||
"north",
|
||||
"take candy cane",
|
||||
"west",
|
||||
"south",
|
||||
"south",
|
||||
"take fuel cell",
|
||||
"south",
|
||||
"take manifold",
|
||||
"north",
|
||||
"north",
|
||||
"west",
|
||||
"take mutex",
|
||||
"north",
|
||||
"south",
|
||||
"south",
|
||||
"south",
|
||||
"take coin",
|
||||
"south",
|
||||
"north",
|
||||
"east",
|
||||
"take cake",
|
||||
"west",
|
||||
"east",
|
||||
"north",
|
||||
"south",
|
||||
"west",
|
||||
"east",
|
||||
"east",
|
||||
"north",
|
||||
"south",
|
||||
"west",
|
||||
"north",
|
||||
"west",
|
||||
"east",
|
||||
"west",
|
||||
"south",
|
||||
"west",
|
||||
"north",
|
||||
"north",
|
||||
"south",
|
||||
"west",
|
||||
"north",
|
||||
"east",
|
||||
"south",
|
||||
"west",
|
||||
"east",
|
||||
"west",
|
||||
"south",
|
||||
"west",
|
||||
"take dehydrated water",
|
||||
"west",
|
||||
"east",
|
||||
"west",
|
||||
"south",
|
||||
"take prime number",
|
||||
"south",
|
||||
"north",
|
||||
"east",
|
||||
"east",
|
||||
"west",
|
||||
"north",
|
||||
"east",
|
||||
"east",
|
||||
"north",
|
||||
"south",
|
||||
"west",
|
||||
"north",
|
||||
"west",
|
||||
"south",
|
||||
"inv",
|
||||
"west",
|
||||
]
|
||||
|
||||
items = [
|
||||
"cake",
|
||||
"prime number",
|
||||
"mutex",
|
||||
"dehydrated water",
|
||||
"coin",
|
||||
"manifold",
|
||||
"candy cane",
|
||||
"fuel cell",
|
||||
]
|
||||
|
||||
|
||||
def run_command(a, s):
|
||||
for c in s:
|
||||
a.feed(ord(c))
|
||||
a.feed(10)
|
||||
a.go()
|
||||
|
||||
|
||||
a = Amp(xs)
|
||||
for c in commands:
|
||||
a.go()
|
||||
while a.outputs:
|
||||
a.go()
|
||||
a.pop()
|
||||
# print(chr(a.pop()), end="")
|
||||
run_command(a, c)
|
||||
a.go()
|
||||
|
||||
|
||||
def try_all(a):
|
||||
subsets = [list(combinations(items, r)) for r in range(1, len(items) + 1)]
|
||||
subsets = [item for sublist in subsets for item in sublist]
|
||||
for subset in subsets:
|
||||
for item in items:
|
||||
c = f"drop {item}"
|
||||
run_command(a, c)
|
||||
a.go()
|
||||
while a.outputs:
|
||||
a.pop()
|
||||
a.go()
|
||||
|
||||
for item in subset:
|
||||
c = f"take {item}"
|
||||
run_command(a, c)
|
||||
a.go()
|
||||
while a.outputs:
|
||||
a.pop()
|
||||
a.go()
|
||||
|
||||
run_command(a, "west")
|
||||
|
||||
output_str = ""
|
||||
while a.outputs:
|
||||
a.go()
|
||||
output_str += chr(a.pop())
|
||||
if "lighter" in output_str:
|
||||
pass
|
||||
elif "heavier" in output_str:
|
||||
pass
|
||||
else:
|
||||
(password,) = ints(output_str)
|
||||
print(password)
|
||||
exit()
|
||||
|
||||
|
||||
while True:
|
||||
a.go()
|
||||
|
||||
output_str = ""
|
||||
while a.outputs:
|
||||
a.go()
|
||||
output_str += chr(a.pop())
|
||||
|
||||
try_all(a)
|
||||
|
||||
if a.input_required:
|
||||
c = input(">")
|
||||
run_command(a, c)
|
||||
57
2019/d3.py
Normal file
57
2019/d3.py
Normal file
@@ -0,0 +1,57 @@
|
||||
from lib import get_data, add2
|
||||
|
||||
|
||||
DIRS = {"R": (0, 1), "U": (-1, 0), "D": (1, 0), "L": (0, -1)}
|
||||
|
||||
|
||||
def part_1(data):
|
||||
xss = []
|
||||
for path in data.splitlines():
|
||||
xs = [(0, 0)]
|
||||
for word in path.split(","):
|
||||
dir = DIRS[word[0]]
|
||||
x = int(word[1:])
|
||||
for _ in range(x):
|
||||
xs.append(add2(xs[-1], dir))
|
||||
xss.append(xs)
|
||||
ys = set(xss[0]) & set(xss[1])
|
||||
ys.remove((0, 0))
|
||||
min_dist = 10**9
|
||||
for y in ys:
|
||||
d = abs(y[0]) + abs(y[1])
|
||||
min_dist = min(min_dist, d)
|
||||
print(min_dist)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
xss = []
|
||||
for path in data.splitlines():
|
||||
pos = (0, 0)
|
||||
steps = 0
|
||||
xs = {}
|
||||
for word in path.split(","):
|
||||
dir = DIRS[word[0]]
|
||||
x = int(word[1:])
|
||||
for _ in range(x):
|
||||
steps += 1
|
||||
pos = add2(pos, dir)
|
||||
if not pos in xs:
|
||||
xs[pos] = steps
|
||||
xss.append(xs)
|
||||
|
||||
min_steps = 10**9
|
||||
for x in xss[0].keys():
|
||||
if x in xss[1]:
|
||||
steps = xss[0][x] + xss[1][x]
|
||||
min_steps = min(steps, min_steps)
|
||||
print(min_steps)
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
70
2019/d4.py
Normal file
70
2019/d4.py
Normal file
@@ -0,0 +1,70 @@
|
||||
from lib import get_data, str_to_ints
|
||||
|
||||
|
||||
def in_range_1(x):
|
||||
contains_double = False
|
||||
s = str(x)
|
||||
for i in range(len(s) - 1):
|
||||
if s[i] == s[i + 1]:
|
||||
contains_double = True
|
||||
break
|
||||
|
||||
if not contains_double:
|
||||
return False
|
||||
|
||||
for i in range(len(s) - 1):
|
||||
if ord(s[i]) > ord(s[i + 1]):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def in_range_2(x):
|
||||
contains_double = False
|
||||
s = str(x)
|
||||
for i in range(len(s) - 1):
|
||||
if s[i] == s[i + 1]:
|
||||
if i + 2 < len(s):
|
||||
if s[i + 2] == s[i + 1]:
|
||||
continue
|
||||
if i - 1 >= 0:
|
||||
if s[i - 1] == s[i]:
|
||||
continue
|
||||
contains_double = True
|
||||
|
||||
if not contains_double:
|
||||
return False
|
||||
|
||||
for i in range(len(s) - 1):
|
||||
if ord(s[i]) > ord(s[i + 1]):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def part_1(data):
|
||||
a, b = str_to_ints(data)
|
||||
b = -b
|
||||
r = 0
|
||||
for x in range(a, b + 1):
|
||||
if in_range_1(x):
|
||||
r += 1
|
||||
print(r)
|
||||
|
||||
|
||||
def part_2(data):
|
||||
a, b = str_to_ints(data)
|
||||
b = -b
|
||||
r = 0
|
||||
for x in range(a, b + 1):
|
||||
if in_range_2(x):
|
||||
r += 1
|
||||
print(r)
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
123
2019/d5.py
Normal file
123
2019/d5.py
Normal file
@@ -0,0 +1,123 @@
|
||||
from lib import get_data, str_to_ints
|
||||
|
||||
|
||||
def part_1(data):
|
||||
xs = str_to_ints(data)
|
||||
i = 0
|
||||
while i < len(xs):
|
||||
inst = str(xs[i])
|
||||
inst = "0" * (5 - len(inst)) + inst
|
||||
assert len(inst) == 5
|
||||
op = int(inst[3:5])
|
||||
mode_p1 = int(inst[2])
|
||||
mode_p2 = int(inst[1])
|
||||
mode_p3 = int(inst[0])
|
||||
match op:
|
||||
case 1:
|
||||
p1 = xs[xs[i + 1]] if mode_p1 == 0 else xs[i + 1]
|
||||
p2 = xs[xs[i + 2]] if mode_p2 == 0 else xs[i + 2]
|
||||
assert mode_p3 == 0
|
||||
xs[xs[i + 3]] = p1 + p2
|
||||
i += 4
|
||||
case 2:
|
||||
p1 = xs[xs[i + 1]] if mode_p1 == 0 else xs[i + 1]
|
||||
p2 = xs[xs[i + 2]] if mode_p2 == 0 else xs[i + 2]
|
||||
assert mode_p3 == 0
|
||||
xs[xs[i + 3]] = p1 * p2
|
||||
i += 4
|
||||
case 3:
|
||||
print("input", i, 1)
|
||||
assert mode_p1 == 0
|
||||
xs[xs[i + 1]] = 1
|
||||
i += 2
|
||||
case 4:
|
||||
if mode_p1 == 0:
|
||||
v = xs[xs[i + 1]]
|
||||
else:
|
||||
v = xs[i + 1]
|
||||
print("output", v)
|
||||
i += 2
|
||||
case 99:
|
||||
break
|
||||
|
||||
|
||||
def part_2(data):
|
||||
xs = str_to_ints(data)
|
||||
i = 0
|
||||
while i < len(xs):
|
||||
inst = str(xs[i])
|
||||
inst = "0" * (5 - len(inst)) + inst
|
||||
assert len(inst) == 5
|
||||
op = int(inst[3:5])
|
||||
mode_p1 = int(inst[2])
|
||||
mode_p2 = int(inst[1])
|
||||
mode_p3 = int(inst[0])
|
||||
match op:
|
||||
case 1:
|
||||
p1 = xs[xs[i + 1]] if mode_p1 == 0 else xs[i + 1]
|
||||
p2 = xs[xs[i + 2]] if mode_p2 == 0 else xs[i + 2]
|
||||
assert mode_p3 == 0
|
||||
xs[xs[i + 3]] = p1 + p2
|
||||
i += 4
|
||||
case 2:
|
||||
p1 = xs[xs[i + 1]] if mode_p1 == 0 else xs[i + 1]
|
||||
p2 = xs[xs[i + 2]] if mode_p2 == 0 else xs[i + 2]
|
||||
assert mode_p3 == 0
|
||||
xs[xs[i + 3]] = p1 * p2
|
||||
i += 4
|
||||
case 3:
|
||||
print("input", i, 5)
|
||||
assert mode_p1 == 0
|
||||
xs[xs[i + 1]] = 5
|
||||
i += 2
|
||||
case 4:
|
||||
if mode_p1 == 0:
|
||||
v = xs[xs[i + 1]]
|
||||
else:
|
||||
v = xs[i + 1]
|
||||
print("output", v)
|
||||
i += 2
|
||||
case 99:
|
||||
break
|
||||
case 5:
|
||||
p1 = xs[xs[i + 1]] if mode_p1 == 0 else xs[i + 1]
|
||||
p2 = xs[xs[i + 2]] if mode_p2 == 0 else xs[i + 2]
|
||||
if p1 != 0:
|
||||
i = p2
|
||||
else:
|
||||
i += 3
|
||||
case 6:
|
||||
p1 = xs[xs[i + 1]] if mode_p1 == 0 else xs[i + 1]
|
||||
p2 = xs[xs[i + 2]] if mode_p2 == 0 else xs[i + 2]
|
||||
if p1 == 0:
|
||||
i = p2
|
||||
else:
|
||||
i += 3
|
||||
case 7:
|
||||
p1 = xs[xs[i + 1]] if mode_p1 == 0 else xs[i + 1]
|
||||
p2 = xs[xs[i + 2]] if mode_p2 == 0 else xs[i + 2]
|
||||
assert mode_p3 == 0
|
||||
if p1 < p2:
|
||||
xs[xs[i + 3]] = 1
|
||||
else:
|
||||
xs[xs[i + 3]] = 0
|
||||
i += 4
|
||||
case 8:
|
||||
p1 = xs[xs[i + 1]] if mode_p1 == 0 else xs[i + 1]
|
||||
p2 = xs[xs[i + 2]] if mode_p2 == 0 else xs[i + 2]
|
||||
assert mode_p3 == 0
|
||||
if p1 == p2:
|
||||
xs[xs[i + 3]] = 1
|
||||
else:
|
||||
xs[xs[i + 3]] = 0
|
||||
i += 4
|
||||
|
||||
|
||||
def main():
|
||||
data = get_data(__file__)
|
||||
part_1(data)
|
||||
part_2(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user