mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 12:50:32 +01:00
2025 day 11 in Python
This commit is contained in:
10
2025/day11/README.md
Normal file
10
2025/day11/README.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Day 11: Python
|
||||||
|
|
||||||
|
Straightforward. Uses `networkx` because I thought it would be very helpful but it only really saves
|
||||||
|
me from writing a topological sort algorithm in the end.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ uv run ./solve.py input.txt
|
||||||
|
Part 1: secret
|
||||||
|
Part 2: secret
|
||||||
|
```
|
||||||
9
2025/day11/pyproject.toml
Normal file
9
2025/day11/pyproject.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[project]
|
||||||
|
name = "solve"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Add your description here"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
dependencies = [
|
||||||
|
"networkx>=3.6.1",
|
||||||
|
]
|
||||||
10
2025/day11/sample.txt
Normal file
10
2025/day11/sample.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
aaa: you hhh
|
||||||
|
you: bbb ccc
|
||||||
|
bbb: ddd eee
|
||||||
|
ccc: ddd eee fff
|
||||||
|
ddd: ggg
|
||||||
|
eee: out
|
||||||
|
fff: out
|
||||||
|
ggg: out
|
||||||
|
hhh: ccc fff iii
|
||||||
|
iii: out
|
||||||
14
2025/day11/sample2.txt
Normal file
14
2025/day11/sample2.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
svr: aaa bbb
|
||||||
|
aaa: fft
|
||||||
|
fft: ccc
|
||||||
|
bbb: tty
|
||||||
|
tty: ccc
|
||||||
|
ccc: ddd eee
|
||||||
|
ddd: hub
|
||||||
|
hub: fff
|
||||||
|
eee: dac
|
||||||
|
dac: fff
|
||||||
|
fff: ggg hhh
|
||||||
|
ggg: out
|
||||||
|
hhh: out
|
||||||
|
you: out
|
||||||
70
2025/day11/solve.py
Executable file
70
2025/day11/solve.py
Executable file
@@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
import fileinput
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
import networkx as nx
|
||||||
|
|
||||||
|
def parse_graph() -> nx.Graph:
|
||||||
|
graph = nx.DiGraph()
|
||||||
|
|
||||||
|
for line in fileinput.input():
|
||||||
|
source, rem = line.split(": ")
|
||||||
|
|
||||||
|
for sink in rem.strip().split(" "):
|
||||||
|
graph.add_edge(source, sink)
|
||||||
|
|
||||||
|
return graph
|
||||||
|
|
||||||
|
def count(gen: Iterable) -> int:
|
||||||
|
return sum(1 for _ in gen)
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
graph = parse_graph()
|
||||||
|
|
||||||
|
# Observation: graph is a DAG, so one needs to go in front of the other. We can do this in three
|
||||||
|
# steps:
|
||||||
|
# svr → closest(dac, fft) -> furthest(dac,fft)
|
||||||
|
rank = {
|
||||||
|
node: rank
|
||||||
|
for rank, node in enumerate(nx.topological_sort(graph))
|
||||||
|
}
|
||||||
|
|
||||||
|
rev_rank = {rank: node for node, rank in rank.items()}
|
||||||
|
|
||||||
|
if rank["dac"] > rank["fft"]:
|
||||||
|
closest = "fft"
|
||||||
|
furthest = "dac"
|
||||||
|
else:
|
||||||
|
closest = "dac"
|
||||||
|
furthest = "fft"
|
||||||
|
|
||||||
|
|
||||||
|
def ranked_all_paths(source: str, dest: str) -> int:
|
||||||
|
counts = defaultdict(int)
|
||||||
|
|
||||||
|
counts[dest] = 1
|
||||||
|
|
||||||
|
for r in range(rank[dest], rank[source], -1):
|
||||||
|
node = rev_rank[r]
|
||||||
|
if node not in counts:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for u, _ in graph.in_edges(node):
|
||||||
|
counts[u] += counts[node]
|
||||||
|
|
||||||
|
return counts[source]
|
||||||
|
|
||||||
|
assert nx.has_path(graph, closest, furthest)
|
||||||
|
|
||||||
|
print("Part 1", ranked_all_paths("you", "out"))
|
||||||
|
|
||||||
|
first = ranked_all_paths("svr", closest)
|
||||||
|
second = ranked_all_paths(closest, furthest)
|
||||||
|
third = ranked_all_paths(furthest, "out")
|
||||||
|
print("Part 2:", first * second * third)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user