Compare commits
2 Commits
7d1dc3f95e
...
df31b47934
| Author | SHA1 | Date | |
|---|---|---|---|
| df31b47934 | |||
| 3991842ef0 |
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))
|
||||||
111
2019/d22.py
111
2019/d22.py
@@ -4,6 +4,7 @@ from math import gcd
|
|||||||
data = get_data(__file__)
|
data = get_data(__file__)
|
||||||
deck = list(range(10007))
|
deck = list(range(10007))
|
||||||
|
|
||||||
|
# part 1
|
||||||
for line in data.splitlines():
|
for line in data.splitlines():
|
||||||
if "new stack" in line:
|
if "new stack" in line:
|
||||||
deck = list(reversed(deck))
|
deck = list(reversed(deck))
|
||||||
@@ -24,30 +25,94 @@ for line in data.splitlines():
|
|||||||
|
|
||||||
print(deck.index(2019))
|
print(deck.index(2019))
|
||||||
|
|
||||||
# idea: just follow the index of the target card
|
|
||||||
len = 119315717514047
|
|
||||||
len = 10007
|
len = 10007
|
||||||
index = 2019
|
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
|
||||||
|
|
||||||
for i in range(101741582076661):
|
assert index == deck.index(2019)
|
||||||
for line in data.splitlines():
|
|
||||||
if "new stack" in line:
|
# check that reverse approach works
|
||||||
index = len - (index + 1)
|
for line in reversed(data.splitlines()):
|
||||||
elif "cut" in line:
|
if "new stack" in line:
|
||||||
cut, = str_to_ints(line)
|
index = -(index - len + 1)
|
||||||
cut = (len + cut) % len
|
elif "cut" in line:
|
||||||
if index >= cut:
|
cut, = str_to_ints(line)
|
||||||
index = index - cut
|
cut = (len + cut) % len
|
||||||
else:
|
index = (index + cut) % len
|
||||||
index = (len - cut) + index
|
elif "increment" in line:
|
||||||
elif "increment" in line:
|
n, = str_to_ints(line)
|
||||||
n, = str_to_ints(line)
|
assert gcd(n, len) == 1
|
||||||
assert gcd(n, len) == 1
|
m = mod_inverse(n, len)
|
||||||
new_index = (n * index) % len
|
index = (index * m) % len
|
||||||
m = mod_inverse(n, len)
|
assert index == orig_index
|
||||||
assert (new_index * m) % len == index
|
|
||||||
index = new_index
|
lines = list(reversed(data.splitlines()))
|
||||||
break
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
print(index)
|
|
||||||
|
|||||||
@@ -106,11 +106,13 @@ Solutions and utility script for Advent of Code challenges in Python.
|
|||||||
- Day 16: 36:10
|
- Day 16: 36:10
|
||||||
- Day 17: 180:00
|
- Day 17: 180:00
|
||||||
- Day 18: 24:04
|
- Day 18: 24:04
|
||||||
- Day 19: days, super fun, but hard for me
|
- Day 19: days (super fun, but hard for me)
|
||||||
- Day 20:
|
- Day 20:
|
||||||
- Day 21: 28:40 (16th - brute force but still not so bad)
|
- Day 21: 28:40 (16th - brute force but still not so bad)
|
||||||
- Day 22: 185:00 (should not have been so slow but was fun)
|
- Day 22: 185:00 (should not have been so slow but was fun)
|
||||||
- Day 23:
|
- Day 23: days
|
||||||
|
- Day 24: days
|
||||||
|
- Day 25: 29:20 (slow)
|
||||||
|
|
||||||
## AoC 2019
|
## AoC 2019
|
||||||
|
|
||||||
@@ -135,7 +137,8 @@ Solutions and utility script for Advent of Code challenges in Python.
|
|||||||
- Day 19: 40:00 (Way too slow! Oversight error. Come on.)
|
- Day 19: 40:00 (Way too slow! Oversight error. Come on.)
|
||||||
- Day 20: days (Not actually that hard but I struggled for no reason.)
|
- Day 20: days (Not actually that hard but I struggled for no reason.)
|
||||||
- Day 21: days (But it was super fun!)
|
- Day 21: days (But it was super fun!)
|
||||||
- Day 22:
|
- Day 22: days (Needed some help...)
|
||||||
|
- Day 23:
|
||||||
|
|
||||||
## AoC 2020
|
## AoC 2020
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user