advent-of-code/2025-python/output/__init__.py

224 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import re
from math import inf
# Directions/Adjacents for 2D matrices, in the order UP, RIGHT, DOWN, LEFT
D = [
(-1, 0),
(0, 1),
(1, 0),
(0, -1),
]
Di = [
(-1, -1),
(-1, 1),
(1, -1),
(1, 1),
]
# Directions for 2D matrices, as a dict with keys U, R, D, L
DD = {
"U": (-1, 0),
"R": (0, 1),
"D": (1, 0),
"L": (0, -1),
}
DDa = {
"^": (-1, 0),
">": (0, 1),
"v": (1, 0),
"<": (0, -1),
}
# Adjacent relative positions including diagonals for 2D matrices, in the order NW, N, NE, W, E, SW, S, SE
ADJ = [
(-1, -1),
(-1, 0),
(-1, 1),
(0, -1),
(0, 1),
(1, 1),
(1, 0),
(1, -1),
]
def ints(s):
"""Extract all integers from a string"""
return [int(n) for n in re.findall(r"\d+", s)]
def sints(s):
"""Extract all signed integers from a string"""
return [int(n) for n in re.findall(r"-?\d+", s)]
def mhd(a, b):
"""Calculates the Manhattan distance between 2 positions in the format (y, x) or (x, y)"""
ar, ac = a
br, bc = b
return abs(ar - br) + abs(ac - bc)
def grid(d, o="#"):
"""Transform a string into an iterable matrix. Returns the matrix, row count and col count"""
m = [tuple(r) for r in d.split()]
return set([(r, c) for c in range(len(m[0])) for r in range(len(m)) if m[r][c] == o])
def matrix(d):
"""Transform a string into an iterable matrix. Returns the matrix, row count and col count"""
m = [tuple(r) for r in d.split()]
return m, len(m), len(m[0])
def mdbg(m):
"""Print-debug a matrix"""
for r in m:
print("".join(r))
def vdbg(seen, mask=("#", "."), M=None):
"""Print-debug visited positions of a matrix"""
t = inf
l = inf
b = 0
r = 0
for y, x in seen:
t = min(t, y)
r = max(r, x)
b = max(b, y)
l = min(l, x)
H = b - t + 1
W = r - l + 1
osr = t
osc = l
C, Z = mask
def _m(r, c):
return M[r][c] if M else Z
for r in range(H):
print("".join([C if (r + osr, c + osc) in seen else _m(r, c) for c in range(W)]))
def vvdbg(seen, h, w):
"""Print-debug visited positions of a matrix, with values"""
for r in range(h):
print("".join([seen[(r, c)] if (r, c) in seen else "." for c in range(w)]))
def cw(y, x):
"""Flip a (y, x) direction counterwise: U->R, R->D, D->L, L->U.
>>> cw(-1, 0)
(0, 1)
>>> cw(0, 1)
(1, 0)
>>> cw(1, 0)
(0, -1)
>>> cw(0, -1)
(-1, 0)
"""
return (x, y) if y == 0 else (x, -y)
def ccw(y, x):
"""Flip a (y, x) direction counterwise: U->L, L->D, D->R, R->U.
>>> ccw(-1, 0)
(0, -1)
>>> ccw(0, -1)
(1, 0)
>>> ccw(1, 0)
(0, 1)
>>> ccw(0, 1)
(-1, 0)
"""
return (x, y) if x == 0 else (-x, y)
def bfs(S, E=None):
"""BFS algorithm, equal weighted nodes"""
seen = set()
q = [(S, 0)]
g = {} # graph, required to be provided at some point
while q:
m, w = q.pop(0)
if m in seen:
continue
seen.add(m)
# investigate here
for s in g[m]:
q.append((s, w + 1))
# return insights
def mhd_search(r, c, R=20):
"""returns all coords that are within R manhattan distance from (r,c)"""
p = set()
for d in range(1, R + 1):
p.add((r, c + d))
p.add((r, c - d))
p.add((r + d, c))
p.add((r - d, c))
for dd in range(d):
p.add((r - dd, c - d + dd))
p.add((r + dd, c - d + dd))
p.add((r - dd, c - dd + d))
p.add((r + dd, c - dd + d))
return p
def dijkstras(grid, start, target):
"""
1. Create an array that holds the distance of each vertex from the starting
vertex. Initially, set this distance to infinity for all vertices except
the starting vertex which should be set to 0.
2. Create a priority queue (heap) and insert the starting vertex with its
distance of 0.
3. While there are still vertices left in the priority queue, select the vertex
with the smallest recorded distance from the starting vertex and visit its
neighboring vertices.
4. For each neighboring vertex, check if it is visited already or not. If it
isnt visited yet, calculate its tentative distance by adding its weight
to the smallest distance found so far for its parent/previous node
(starting vertex in case of first-level vertices).
5. If this tentative distance is smaller than previously recorded value
(if any), update it in our distances array.
6. Finally, add this visited vertex with its updated distance to our priority
queue and repeat step-3 until we have reached our destination or exhausted
all nodes.
"""
import heapq
target = max(grid)
seen = set()
queue = [(start, 0)]
while queue:
cost, pos, direction, steps = heapq.heappop(queue)
y, x = pos
dy, dx = direction
if pos == target:
return cost
if ((pos, "and stuff")) in seen:
continue
seen.add((pos, "and stuff"))
neighbors = []
for n in neighbors:
heapq.heappush(queue, ("stuffs"))
return -1
def bk(graph, p, r=set(), x=set()):
"""Bron-Kerbosch algoritm, no pivot: https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm"""
if not p and not x:
yield r
while p:
v = p.pop()
yield from bk(graph, p & set(graph[v]), r | {v}, x & graph[v])
x.add(v)