mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
Compare commits
3 Commits
51ca5db42e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| bc6f3dc8c6 | |||
| a8a1c85498 | |||
| ff8b8dac0c |
1
2025/day10/.gitignore
vendored
Normal file
1
2025/day10/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
target/
|
||||
7
2025/day10/Cargo.toml
Normal file
7
2025/day10/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "solve"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
microlp = "0.2.11"
|
||||
3
2025/day10/sample.txt
Normal file
3
2025/day10/sample.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}
|
||||
[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2}
|
||||
[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}
|
||||
141
2025/day10/src/main.rs
Normal file
141
2025/day10/src/main.rs
Normal file
@@ -0,0 +1,141 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
|
||||
use microlp::ComparisonOp;
|
||||
use microlp::LinearExpr;
|
||||
use microlp::OptimizationDirection;
|
||||
use microlp::Problem;
|
||||
|
||||
fn parse_line(line: &str) -> (u32, Vec<u32>, Vec<u8>) {
|
||||
let mut buttons = vec![];
|
||||
let mut target = 0;
|
||||
|
||||
let mut it = line.chars();
|
||||
|
||||
for (i, c) in it.by_ref().skip(1).enumerate() {
|
||||
match c {
|
||||
'#' => target |= 1 << i,
|
||||
'.' => (),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
match it.nth(1) {
|
||||
Some('{') => break,
|
||||
Some('(') => (),
|
||||
other => panic!("Unexpected character \"{other:?}\" in: {line}"),
|
||||
}
|
||||
|
||||
let mut button = 0;
|
||||
|
||||
while let Some(c) = it.next() {
|
||||
let d = c.to_digit(10).unwrap();
|
||||
button |= 1 << d;
|
||||
|
||||
if let Some(')') = it.next() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
buttons.push(button);
|
||||
}
|
||||
|
||||
let rem = it.as_str().trim().trim_end_matches('}');
|
||||
|
||||
let joltage = rem.split(',').map(|j| j.parse().unwrap()).collect();
|
||||
|
||||
(target, buttons, joltage)
|
||||
}
|
||||
|
||||
fn min_joltage(buttons: &[u32], joltage: &[u8]) -> i32 {
|
||||
let mut problem = Problem::new(OptimizationDirection::Minimize);
|
||||
let max = i32::from(*joltage.iter().max().unwrap_or(&0));
|
||||
|
||||
let variables: Vec<_> = buttons
|
||||
.iter()
|
||||
.map(|_| problem.add_integer_var(1.0, (0, max)))
|
||||
.collect();
|
||||
|
||||
for (bit, &value) in joltage.iter().enumerate() {
|
||||
let mut equation = LinearExpr::empty();
|
||||
|
||||
for (&button, &variable) in buttons.iter().zip(&variables) {
|
||||
if button & (1 << bit) != 0 {
|
||||
equation.add(variable, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
problem.add_constraint(equation, ComparisonOp::Eq, value.into());
|
||||
}
|
||||
|
||||
problem.solve().unwrap().objective().round() as i32
|
||||
}
|
||||
|
||||
fn minimum_clicks(target: u32, buttons: &[u32]) -> i32 {
|
||||
let max = buttons
|
||||
.iter()
|
||||
.map(|s| 32 - s.leading_zeros())
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
let possible = 2 << max;
|
||||
let mut seen = vec![false; possible];
|
||||
let mut todo = VecDeque::new();
|
||||
todo.push_back((0, 0));
|
||||
|
||||
while let Some((steps, state)) = todo.pop_front() {
|
||||
for &button in buttons {
|
||||
let next = state ^ button;
|
||||
|
||||
if next == target {
|
||||
return steps + 1;
|
||||
} else if !seen[next as usize] {
|
||||
seen[next as usize] = true;
|
||||
todo.push_back((steps + 1, next));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!("Did not find target");
|
||||
}
|
||||
|
||||
fn solve(input: &str) -> (i32, i32) {
|
||||
let mut total_clicks = 0;
|
||||
let mut total_presses = 0;
|
||||
for line in input.trim().lines() {
|
||||
let (target, buttons, joltage) = parse_line(line);
|
||||
total_clicks += minimum_clicks(target, &buttons);
|
||||
total_presses += min_joltage(&buttons, &joltage)
|
||||
}
|
||||
|
||||
(total_clicks, total_presses)
|
||||
}
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
if let Some(path) = env::args_os().nth(1) {
|
||||
let input = fs::read_to_string(path)?;
|
||||
|
||||
let (part1, part2) = solve(&input);
|
||||
println!("Part 1: {part1}\nPart 2: {part2}");
|
||||
Ok(())
|
||||
} else {
|
||||
eprintln!("Usage: {} INPUT_FILE", env::args().next().unwrap());
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const SAMPLE: &str = include_str!("../sample.txt");
|
||||
|
||||
#[test]
|
||||
fn test_sample() {
|
||||
let (part1, part2) = solve(SAMPLE);
|
||||
assert_eq!(7, part1);
|
||||
assert_eq!(33, part2);
|
||||
}
|
||||
}
|
||||
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