Compare commits

..

14 Commits

Author SHA1 Message Date
e58ddb9f96 day 8, generators are faster (2x) 2025-09-08 16:38:35 +02:00
d165384617 day 7, saved by Counter 2025-09-08 01:46:10 +02:00
6fe70f851f day 14, use rotation, proper cycle detection, merge both parts, enable codon 2023-12-16 02:21:09 +01:00
a4e02c42a7 day 15, factor both parts 2023-12-16 00:26:27 +01:00
032f0c4caa day 14, 1E9 is not more than 1E3 2023-12-15 23:58:40 +01:00
a59c3c697c day 15, trivial 2023-12-15 17:24:51 +01:00
6be1a222b1 day 12, part 1 faster, but not fast enough for part 2 2023-12-13 14:23:41 +01:00
a6c027a1af day 11, 1 is also 2-1 2023-12-11 23:05:13 +01:00
3142bc1ebb day 10, part 1 ok, part 2 with help, but nice drawing 2023-12-10 15:56:37 +01:00
95665139a7 day 9, refactor 2023-12-09 13:50:09 +01:00
1d4c5f6f81 day 9, trivial recursion 2023-12-09 09:17:26 +01:00
492af0c576 day 8, somewhat easy
part 1 realy easy
part 2 made with a trick, got from reddit megathread
else naive approach would have taken 40 day of computation
2023-12-08 11:30:35 +01:00
baabb6cd88 day 5, part 2 in brute force
compiled with codon, runs for 7 minutes
should rewrite algorithme with range intersection
2023-12-07 02:13:45 +01:00
6860e71dbd add niceties to day 2 2023-12-06 23:41:41 +01:00
17 changed files with 499 additions and 58 deletions

View File

@@ -11,16 +11,15 @@ def get_G(x,y):
def is_sym(a:str) -> bool:
return a != '.' and not a.isdigit()
S = 0
c = 0 # current
valid = False
Ns = [] # all part numbers
for y in range(H):
for x in range(W):
i = get_G(x,y)
if not i.isdigit():
# appent current if valid and reset
if valid:
Ns.append(c)
S += c
valid = False
c = 0
else:
@@ -29,4 +28,4 @@ for y in range(H):
for dy in [-1,0,1]:
valid |= is_sym(get_G(x+dx,y+dy))
print(sum(Ns))
print(S)

View File

@@ -28,8 +28,6 @@ for y in range(H):
if '*' == get_G(x+dx,y+dy):
valid = (x+dx,y+dy)
S = 0
for _,v in Rs.items():
if len(v)==2:
S += v[0] * v[1]
print(S)
print(
sum( v[0] * v[1] for _,v in Rs.items() if len(v)==2)
)

View File

@@ -2,55 +2,30 @@ import sys
en=enumerate
def r2v(a,l):
return (a, a+l-1)
def v2r(a,b):
return (a, b-a+1)
def intersec(A:tuple, B:tuple) -> list(tuple):
a0, l = A
b0, m = B
a1 = r2v(A)[1]
b1 = r2v(B)[1]
if b1 < a0 or a1 < b0: # B outside
return A
if a0 < b0 and b1 < a1:
return [v2r(a0,b0-1),B,(b1+1,a1)]
if a0 == b0 and b1 < a1:
return [B,(b1+1,a1)]
if a0 < b0 and b1 < a1:
return [v2r(a0,b0-1),B,(b1+1,a1)]
if a0 < b0 and b1 < a1:
return [v2r(a0,b0-1),B,(b1+1,a1)]
if a0 < b0 and b1 < a1:
return [v2r(a0,b0-1),B,(b1+1,a1)]
if b0 <= a1 <= b1:
return (b0, a1-b0+1)
elif a1 > b1:
return B
return None
if __name__ == '__main__':
L = open(0).read().split('\n\n')
L:list[str] = sys.stdin.read().split('\n\n')
L = [l.split(':') for l in L]
L = [l[1].strip().split('\n') for l in L]
L = [list(map(lambda x: tuple(map(int,x.split())),l)) for l in L]
L = [list(map(lambda x: list(map(int,x.split())),l)) for l in L]
S = list(v for s in L[0] for v in s)
S = list(zip(S[0::2],S[1::2])) # list of range (start, length)
for i,l in en(L[1:]): # for each transformation
print(i, l)
R = []
for s in S: # transform each seed range
ns = None # new range
for dst, src, rg in l:
if src <= s < src+rg:
ns = dst+(s-src)
low = 99999999999
for s,r in S:
print(f'{s=} {r=}')
for i in range(r):
t = s+i
#print(f'{t=}')
for j,l in en(L[1:]): # for each transformation
ns = None # transformed by a range
#print(f'transf {j}, {l}')
for [dst, src, rg] in l:
#print(f'{src=} {dst=} {rg}')
if src <= t < src+rg:
ns = dst+(t-src)
#print(f'transformed {ns}')
break
R.append(ns if ns else s)
S = R
print(S)
print(min(S))
if ns:
t = ns
low = min(low, t)
print(low)

56
d07/part1.py Normal file
View File

@@ -0,0 +1,56 @@
import sys
from collections import Counter as C
from functools import cmp_to_key as ctk
def score(h:str) -> int: # get type of a hand
D = C(h)
E = C(v for k,v in D.items())
if 5 in E: # five of a kind
return 7
elif 4 in E: # four of a kind
return 6
elif 3 in E and 2 in E: # full house
return 5
elif 3 in E: # three of a kind
return 4
elif 2 in E and E[2] == 2: # two pairs
return 3
elif 2 in E:
return 2
else: # high card
return 1
H = list(reversed('AKQJT98765432'))
def compareH(h1, h2):
for a,b in zip(h1,h2):
if H.index(a) < H.index(b):
return 1
elif H.index(a) > H.index(b):
return -1
return 0
def compare(T1, T2): # return >:-1, ==:0, <:1
h1, s1, *_ = T1
h2, s2, *_ = T2
s1 = score(h1)
s2 = score(h2)
if s1 < s2:
return 1
elif s2 < s1:
return -1
else:
return compareH(h1,h2)
L = [ l.split() for l in sys.stdin.read().splitlines()]
L = [[h,int(b)] for [h,b] in L]
for h,b in L:
print(h, score(h))
pass
L.sort(key=ctk(compare))
print(
sum((i+1)*b for i,(h,b) in enumerate(reversed(L)))
)

42
d07/part2.py Normal file
View File

@@ -0,0 +1,42 @@
import sys
from collections import Counter as C
from functools import cmp_to_key as ctk
H = list(reversed('AKQT98765432J'))
def score(h:str) -> int:
D = C(h)
j = D['J']
del D['J']
if j<5:
m = max(D.values())
for k,v in D.items():
if v == m:
D[k] = v+j
break
E = C(v for k,v in D.items()) if j<5 else {5:1}
r = 0
if 5 in E: # five of a kind
r = 7
elif 4 in E: # four of a kind
r = 6
elif 3 in E and 2 in E: # full house
r = 5
elif 3 in E: # three of a kind
r = 4
elif 2 in E and E[2] == 2: # two pairs
r = 3
elif 2 in E: # one pair
r = 2
else: # high card
r = 1
return (r, *[H.index(c) for c in h])
L = [ l.split() for l in sys.stdin.read().splitlines()]
L = [(score(h),int(b)) for [h,b] in L]
sorted(L)
print(
sum((i+1)*b for i,(h,b) in enumerate(reversed(L)))
)

18
d07/terse.py Normal file
View File

@@ -0,0 +1,18 @@
from collections import Counter
lines = [i for i in open('input').read().split('\n') if i.strip()]
def hand(h,part1):
if part1: h = h.replace('J', 'X')
h2 = ['J23456789TXQKA'.index(i)for i in h]
ts = []
for r in '23456789TQKA':
c = Counter(h.replace('J', r))
p = tuple(sorted(c.values()))
t = [(1,1,1,1,1),(1,1,1,2),(1,2,2),(1,1,3),(2,3),(1,4),(5,)].index(p)
ts.append(t)
return (max(ts), *h2)
for part1 in (True, False):
h = sorted((hand(h,part1), int(b)) for h, b in (l.split() for l in lines))
t = 0
for i,(_,b) in enumerate(h):
t+=i*b+b
print('Part', 2-part1, ':', t)

24
d08/part1.py Normal file
View File

@@ -0,0 +1,24 @@
import sys
from itertools import cycle
L = [l for l in sys.stdin.read().splitlines()]
I = L[0] # instructions
for l in L[2:]:
print(f'%{l}%')
Q = [l.split(' = ') for l in L[2:] ]
P = {k:v[1:-1].split(", ") for [k,v] in Q }
print(P)
C = 'AAA'
N = 0
for i in cycle(I):
if C == 'ZZZ':
break
C = P[C][i == 'R']
N += 1
print(C)
print(N)

22
d08/part2.py Normal file
View File

@@ -0,0 +1,22 @@
import sys
from itertools import cycle
from math import lcm
I, _, *P = [l for l in sys.stdin.read().splitlines()]
Q = [l.split(' = ') for l in P ]
P = {k:v[1:-1].split(", ") for [k,v] in Q }
G = [p for p in P.keys() if p[2] == 'A' ]
R = []
for n,g in enumerate(G):
print(n+1, g, '', end='')
C = g
N = 0
for i in cycle(I):
if C[2] == 'Z':
break
C = P[C][i == 'R']
N += 1
print(N)
R.append(N)
print('steps: sum:', sum(R), 'lcm:', lcm(*R))

22
d08/run.py Normal file
View File

@@ -0,0 +1,22 @@
import sys
from itertools import cycle
from math import lcm
I, _, *P = [l for l in sys.stdin.read().splitlines()]
# 0000000000111111111
# 0123456789012345678
# BCN = (HFN, KFC)
P = {l[0:3]:(l[7:10],l[12:15]) for l in P}
def countsteps(g):
C = g
N = 0
for i in cycle(I):
if C[2] == 'Z':
break
C = P[C][i == 'R']
N += 1
return N
print("part 1:", countsteps("AAA"))
print("part 2:", lcm(*(countsteps(p) for p in P.keys() if p[2] == 'A')))

12
d09/run.py Normal file
View File

@@ -0,0 +1,12 @@
import sys
L = [list(map(int,l.split(' '))) for l in sys.stdin.read().splitlines()]
def extrapolate(L):
if all(a == 0 for a in L):
return 0
M = [b-a for a,b in zip(L,L[1:])]
return L[-1]+extrapolate(M)
print(sum(extrapolate(l) for l in L))
print(sum(extrapolate(l[::-1]) for l in L))

59
d10/run.py Normal file
View File

@@ -0,0 +1,59 @@
import sys
# parse
I = sys.stdin.read().splitlines()
G = [list(l) for l in I]
W = len(G[0])
H = len(G)
# look for S
S = None
for y in range(H):
if (x := I[y].find("S")) > -1:
S = (x,y)
break
# encode moves
D = {
'u': {'|': ( 0,-1,'u'), 'F': ( 1, 0,'r'), '7': (-1, 0,'l')},
'r': {'-': ( 1, 0,'r'), 'J': ( 0,-1,'u'), '7': ( 0, 1,'d')},
'd': {'|': ( 0, 1,'d'), 'L': ( 1, 0,'r'), 'J': (-1, 0,'l')},
'l': {'-': (-1, 0,'l'), 'L': ( 0,-1,'u'), 'F': ( 0, 1,'d')},
}
# part 1
poly = [S] # vertices of the polygone
x0, y0 = S
P = (x0+1, y0, 'r') # start at S and go right
for _ in range(100_000):
x, y, d = P
poly.append((x,y))
z = G[y][x]
if z == 'S':
break
dx,dy,nd = D[d].get(z)
P = (x+dx,y+dy,nd)
print(len(poly)//2)
# part 2
from matplotlib.path import Path
pg = Path(poly)
J = [['.']*W for _ in range(H)]
R = 0
for y in range(W):
for x in range(H):
if (x,y) in poly:
J[y][x] = G[y][x]
elif pg.contains_point((x,y)):
R += 1
J[y][x] = "O"
print(R)
# plot
J[y0][x0] = "S"
T = str.maketrans("|-LJ7F", "│─└┘┐┌")
with open("grid.txt","wt") as f:
for row in J:
print("".join(row).translate(T), file=f)

27
d11/run.py Normal file
View File

@@ -0,0 +1,27 @@
import sys
from itertools import combinations as Comb
I = sys.stdin.read().splitlines() # for codon type inference
L = [list(l) for l in I]
H = len(L)
W = len(L[0])
RD = [y for y in range(W) if all(L[y][x] == '.' for x in range(H))]
CD = [x for x in range(H) if all(L[y][x] == '.' for y in range(W))]
G = [(x,y) for y in range(W) for x in range(H) if L[y][x] == '#']
# expantion factor, 2 for part 1, 1E6 for part 2
X = int(float(sys.argv[1])) # float to parse scientific form, int for codon
def exp(a:int, b:int, S:list[int]) -> int:
if a > b:
b, a = a, b
return sum(1 for s in S if a<s<b)
S = 0
for (x1,y1),(x2,y2) in Comb(G,2):
ex = exp(x1, x2, CD)
ey = exp(y1, y2, RD)
d = abs(x2-x1) + abs(y2-y1) + (X-1)*(ex+ey)
S += d
print(int(S))

40
d12/part1.block.py Normal file
View File

@@ -0,0 +1,40 @@
import sys
def strset(s,i,c):
return s[:i]+c+s[i+1:]
def next(pat, nums, patc=''): # pat must terminiate with '.'
#print(f"next {pat=} {nums=} ({patc=}), {len(pat)+len(patc)}")
if len(nums) == 0:
if pat.find('#')<0:
#print("valid", patc)
return 1
return 0
# find all ways to build first block
l = nums[0]
block = '#'*l
rest = sum(nums[1:])
n = 0
m = len(pat)-l-rest
for i in range(m):
# if can build block:
z = pat[i:i+l].replace('?','#')
#print(f"{i=}/{m=} {z=}, {pat[i+l]}")
if z == block:
##print("march, and ", pat[i+l])
if pat[:i].find('#')<0 and not pat[i+l] == '#': # '.' or '?'
#pat = strset(pat, i+l, '.')
# recurse
r = next(pat[i+l+1:], nums[1:], patc+pat[:i].replace('?','.')+z+'.')
if r > 0:
n += r
return n
S= 0
for i,l in enumerate(sys.stdin.read().splitlines()):
pattern, snums = l.split(' ')
nums = list(map(int,snums.split(',')))
n = next(pattern+'.', nums)
print(i,l,n)
S += n
print(S)

37
d12/part1.len.py Normal file
View File

@@ -0,0 +1,37 @@
import sys
def valid(pat:str, nums:list[int]):
pat += '.'
c = 0
for i,p in enumerate(pat):
if p == '#':
c += 1
else:
if c>0:
if c == nums[0]:
c = 0
nums = nums[1:]
if len(nums) == 0:
return 1 if pat[i:].find('#')<0 else 0
else:
return 0
return 0
def next(pattern, nums):
i = pattern.find('?')
if i < 0:
v = valid(pattern, nums)
if v == 1:
print("valid", pattern)
return v
else:
return next(pattern[:i]+'.'+pattern[i+1:], nums)+next(pattern[:i]+'#'+pattern[i+1:], nums)
S= 0
for i,l in enumerate(sys.stdin.read().splitlines()):
pattern, snums = l.split(' ')
nums = list(map(int,snums.split(',')))
n = next(pattern, nums)
print(i,l,n)
S += n
print(S)

31
d12/part1.list.py Normal file
View File

@@ -0,0 +1,31 @@
import sys
def valid(pattern, nums):
l = []
c = ''
for p in pattern:
if p == '#':
c+=p
else:
if len(c):
l.append(c)
c = ''
if len(c):
l.append(c)
c = ''
l = [len(m) for m in l]
return 1 if l == nums else 0
def next(pattern, nums):
i = pattern.find('?')
if i < 0:
return valid(pattern, nums)
else:
return next(pattern[:i]+'.'+pattern[i+1:], nums)+next(pattern[:i]+'#'+pattern[i+1:], nums)
S= 0
for l in sys.stdin.read().splitlines():
pattern, snums = l.split(' ')
nums = list(map(int,snums.split(',')))
S += next(pattern, nums)
print(S)

49
d14/run.py Normal file
View File

@@ -0,0 +1,49 @@
import sys
I = sys.stdin.read().splitlines()
G = [list(l) for l in I]
R = len(G)
C = len(G[0])
def rotate(G, F):
for r in range(R):
for c in range(C):
F[c][R-1-r] = G[r][c]
def tilt(G):
for x in range(C):
for y in range(R):
if G[y][x] == "O":
for j in range(y-1,0-1,-1):
if G[j][x] == '.':
G[j][x] = "O"
G[j+1][x] = "."
else:
break
score = lambda: sum((R-y)*row.count('O') for y,row in enumerate(G))
cache = {}
F = [['?' for _ in range(C)] for _ in range(R)] # pre-allocate saves 10% time (no GC)
target = 1_000_000_000
t = 0
while t<target:
print(t, end='\r')
t += 1
for j in range(4):
tilt(G)
if t==1 and j==0: # part 1
print(score())
rotate(G, F); F, G = G, F
#Gh = tuple(tuple(row) for row in G)
Gh = ''.join(''.join(row) for row in G)
if Gh in cache:
cycle_length = t-cache[Gh]
print(f'{t=}, {cycle_length=}')
amt = (target-t)//cycle_length
t += amt * cycle_length
cache[Gh] = t
print(t, score())

30
d15/run.py Normal file
View File

@@ -0,0 +1,30 @@
import sys
def hash(l):
c = 0
for w in l:
c += ord(w)
c *= 17
c = c % 256
return c
L = sys.stdin.read().split(',')
print(sum(hash(l) for l in L))
B = [ dict() for _ in range(256) ]
for l in L:
e = l.find('=')
d = l.find('-')
i = max(e,d)
lbl = l[:i]
box = hash(lbl)
if e > 0: # set
v = int(l[i+1:])
B[box][lbl] = v
elif d > 0: # remove
if lbl in B[box]:
del B[box][lbl]
print(sum((i+1)*(j+1)*v for i, b in enumerate(B) for j,(k,v) in enumerate(b.items())))