Days 8-11

This commit is contained in:
James Hogge
2025-12-17 02:16:48 +00:00
parent b3ae04b51d
commit 654d1d666c
11 changed files with 3065 additions and 2 deletions

131
src/10-factory.rs Normal file
View File

@@ -0,0 +1,131 @@
use std::cmp::min;
use std::fs::File;
use std::io::{self, BufRead, BufReader};
use std::str::FromStr;
use std::u64;
use nalgebra::{DMatrix, DVector};
#[derive(Debug)]
struct Machine {
lights: Vec<u64>,
buttons: Vec<Vec<u64>>,
joltages: Vec<u64>
}
impl FromStr for Machine {
type Err = io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let groups: Vec<_> = s.split(' ').collect();
let lights_part = groups[0];
let lights = lights_part[1..lights_part.len()-1]
.chars()
.rev()
.map(|c| if c == '#' { 1 } else { 0 })
.collect();
let buttons: Vec<_> = (1..groups.len()-1)
.map(|i| parse_button_group(groups[i]))
.collect();
let joltages_part = groups[groups.len()-1];
let joltages: Vec<_> = joltages_part[1..joltages_part.len()-1]
.split(',')
.map(|j_s| j_s.parse::<u64>().unwrap())
.collect();
Ok(Machine { lights: lights, buttons: buttons , joltages: joltages })
}
}
fn parse_button_group(s: &str) -> Vec<u64> {
s[1..s.len()-1].split(',')
.map(|x_s| x_s.parse::<u64>().unwrap())
.collect()
}
fn parity_solve(machine: &Machine, target: &Vec<u64>) -> Vec<u64> {
let mut solutions: Vec<u64> = Vec::new();
for selection in 0..(1 << machine.buttons.len()) {
let mut parity = target.iter().rev()
.fold(0, |state, x| (state << 1) + (x & 1));
for (i, button) in machine.buttons.iter().enumerate() {
if selection & (1 << i) != 0 {
for j in button {
parity ^= 1 << j;
}
}
}
if parity == 0 {
solutions.push(selection);
}
}
solutions
}
fn minimum_solve(machine: &Machine) -> u64 {
fn apply_button(machine: &Machine, button_idx: usize, mut current: Vec<u64>) -> Option<Vec<u64>> {
for i in &machine.buttons[button_idx] {
if current[*i as usize] == 0 {
return None;
}
current[*i as usize] -= 1;
}
Some(current)
}
fn recurse(machine: &Machine, target: &Vec<u64>, max_cnt: i64) -> Option<u64> {
if target.iter().sum::<u64>() == 0 { return Some(0) };
if max_cnt <= 0 { return None };
let parity_solutions = parity_solve(machine, target);
let mut min_solution = None;
for parity_sln in parity_solutions {
let current = (0..machine.buttons.len())
.fold(Some(target.clone()), |state, i| {
if parity_sln & (1 << i) != 0 {
apply_button(machine, i, state?)
} else {
state
}
});
let Some(current) = current else { continue };
let parity_ones = parity_sln.count_ones() as i64;
let new_max_cnt =
(match min_solution {
None => max_cnt,
Some(ms) => min(ms as i64, max_cnt)
} - parity_ones) / 2;
let new_target = current.iter().map(|&x| x / 2).collect();
let sln = recurse(machine, &new_target, new_max_cnt);
let Some(sln) = sln else { continue };
min_solution =
match min_solution {
None => Some(parity_ones as u64 + 2u64 * sln),
Some(ms) => Some(min(ms, parity_ones as u64 + 2u64 * sln))
};
}
min_solution
}
let result = recurse(machine, &machine.joltages, i64::MAX).unwrap_or(0);
println!("Result: {}", result);
result
}
fn main() -> Result<(), io::Error> {
let f = File::open("10-input.txt")?;
let reader = BufReader::new(f);
let machines: Vec<_> = reader.lines()
.map(|l| l?.parse::<Machine>())
.collect::<Result<_,_>>()?;
println!("Parsed {} machines: \n {:?}", machines.len(), machines);
let presses: u64 = machines.iter()
.map(|m| minimum_solve(&m))
.sum();
println!("Result: {}", presses);
Ok(())
}

70
src/11-reactor.rs Normal file
View File

@@ -0,0 +1,70 @@
use std::{collections::HashMap, fs::File, io::{self, BufRead, BufReader}};
#[derive(Clone)]
enum GraphNode {
Output,
Branch(Vec<String>)
}
fn count_paths(
node: &str,
nodes: &HashMap<String, GraphNode>,
memo: &mut HashMap<String, usize>
) -> usize {
if let Some(&count) = memo.get(node) {
return count;
}
let count =
match nodes.get(node) {
None => 0,
Some(GraphNode::Output) => 1,
Some(GraphNode::Branch(children)) => {
children.iter()
.map(|c| count_paths(c, nodes, memo))
.sum()
}
};
memo.insert(node.to_string(), count);
count
}
fn main() -> Result<(), io::Error> {
let f = File::open("11-input.txt")?;
let reader = BufReader::new(f);
let nodes: HashMap<String, GraphNode> = reader.lines()
.map(|line| {
let line = line?;
let parts: Vec<_> = line.split(' ').collect();
let name = parts[0][..3].to_string();
if parts[1] == "out" {
Ok((name, GraphNode::Output))
} else {
let branches: Vec<String> = parts[1..].iter().map(|s| s.to_string()).collect();
Ok((name, GraphNode::Branch(branches)))
}
})
.collect::<Result<_, io::Error>>()?;
let svr_paths = count_paths("svr", &nodes, &mut HashMap::new());
let missing_fft_paths = count_paths("svr",
&(nodes.clone()
.into_iter()
.filter(|(k, _)| k != "fft")
.collect()), &mut HashMap::new());
let missing_dac_paths = count_paths("svr",
&(nodes.clone()
.into_iter()
.filter(|(k, _)| k != "dac")
.collect()), &mut HashMap::new());
let missing_both_paths = count_paths("svr",
&(nodes.clone()
.into_iter()
.filter(|(k, _)| k != "fft" && k != "dac")
.collect()), &mut HashMap::new());
println!("Result {}", svr_paths + missing_both_paths - missing_fft_paths - missing_dac_paths);
Ok(())
}

103
src/8-playground.rs Normal file
View File

@@ -0,0 +1,103 @@
use std::{collections::HashMap, fs::File, hash::Hash,io::{self, BufRead, BufReader}};
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
struct Node {
x: u32,
y: u32,
z: u32,
}
impl Node {
fn square_dist(a: &Node, b: &Node) -> i64 {
let dx = i64::from(a.x) - i64::from(b.x);
let dy = i64::from(a.y) - i64::from(b.y);
let dz = i64::from(a.z) - i64::from(b.z);
dx*dx + dy*dy + dz*dz
}
}
impl From<&str> for Node {
fn from(value: &str) -> Self {
let coords: Vec<_> = value.split(',').map(|s| s.parse::<u32>().unwrap()).collect();
Node {
x: coords[0],
y: coords[1],
z: coords[2]
}
}
}
#[derive(Debug, Clone)]
struct Edge {
a: Node,
b: Node
}
#[derive(Debug)]
struct DisjointSet<T> {
parents: HashMap<T, Option<T>>
}
impl<T: Eq + Hash> FromIterator<T> for DisjointSet<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
DisjointSet {
parents: iter.into_iter().map(|x| (x, None)).collect()
}
}
}
impl<T: Eq + Hash + Clone> DisjointSet<T> {
fn representative(&mut self, x: &T) -> Option<T> {
match self.parents.get(x)?.clone() {
None => Some(x.clone()),
Some(p) => {
let rep = self.representative(&p)?;
self.parents.insert(x.clone(), Some(rep.clone()));
Some(rep)
}
}
}
fn union(&mut self, a: &T, b: &T) -> Option<T> {
let rep_a = self.representative(a).unwrap();
let rep_b = self.representative(b).unwrap();
if rep_a != rep_b {
self.parents.insert(rep_a, Some(rep_b.clone()));
Some(rep_b)
} else {
None
}
}
}
fn main() -> Result<(), io::Error> {
let f = File::open("8-input.txt")?;
let reader = BufReader::new(f);
let nodes: Vec<Node> = reader.lines()
.map(|lr| lr.map(|l| l.as_str().into()))
.collect::<Result<_, _>>()?;
let node_cnt = nodes.len();
let mut all_edges: Vec<_> =
(0..node_cnt).flat_map(|a| {
let na = &nodes[a];
(a+1..node_cnt).map(|b| {
let nb = &nodes[b];
let sqr_dist = Node::square_dist(na, nb);
(sqr_dist, Edge {a: na.clone(), b: nb.clone()})
})
})
.filter(|(_, e)| e.a != e.b)
.collect();
all_edges.sort_unstable_by_key(|(d, _)| *d);
let mut ds: DisjointSet<_> = nodes.clone().into_iter().collect();
let connections: Vec<_> = all_edges.iter()
.filter_map(|(_, e)| ds.union(&e.a, &e.b).map(|_|e.clone()))
.collect();
let last_connection = connections.last().unwrap();
println!("Result {}", u64::from(last_connection.a.x) * u64::from(last_connection.b.x));
Ok(())
}

170
src/9-movie-theater.rs Normal file
View File

@@ -0,0 +1,170 @@
use std::{cmp::{max, min, Ordering}, fs::File, i64, io::{self, BufRead, BufReader, ErrorKind}, fmt::Debug, str::FromStr};
#[derive(Clone, Copy, Debug)]
struct Vertex {
x: i64,
y: i64
}
impl FromStr for Vertex {
type Err = io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<_> = s.split(',')
.map(|s| s.parse::<i64>())
.collect();
let err = io::Error::new(ErrorKind::Other, "Unable to parse vertex");
let Some(&Ok(x)) = parts.get(0) else { return Err(err) };
let Some(&Ok(y)) = parts.get(1) else { return Err(err) };
Ok(Vertex { x: x, y: y})
}
}
#[derive(Debug)]
struct Edge {
a: Vertex,
b: Vertex
}
#[derive(Clone, Copy, PartialEq, Eq)]
struct Segment {
lo: i64,
hi: i64,
truncate_lo: bool,
truncate_hi: bool,
dist: Option<i64>
}
impl Segment {
fn new(e: Edge) -> Segment {
Segment {
lo: min(e.a.x, e.b.x),
hi: max(e.a.x, e.b.x),
truncate_lo: false,
truncate_hi: false,
dist:
if e.a.x < e.b.x { Some(e.a.y) }
else { None }
}
}
}
impl PartialOrd for Segment {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.hi <= other.lo {
Some(Ordering::Less)
} else if other.hi <= self.lo {
Some(Ordering::Greater)
} else {
Some(Ordering::Equal)
}
}
}
impl Ord for Segment {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
impl Debug for Segment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(format!("({}, {})", self.lo, self.hi).as_str())
}
}
fn rect_area(x1: i64, x2: i64, y1: i64, y2: i64) -> i64 {
(1 + i64::abs(x1 - x2)) * (1 + i64::abs(y1 - y2))
}
fn rect_from_point(state: &Vec<Segment>, v: &Vertex) -> i64 {
let mut max_area = i64::MIN;
let mut max_y = i64::MIN;
for segment in state {
if segment.hi <= v.x { continue }
let Some(seg_y) = segment.dist else { break };
if seg_y >= max_y {
max_y = seg_y;
if !segment.truncate_hi {
max_area = max(max_area, rect_area(v.x, segment.hi, v.y, seg_y));
}
if !segment.truncate_lo {
max_area = max(max_area, rect_area(v.x, segment.lo, v.y, seg_y));
}
}
}
let mut max_y = i64::MIN;
for segment in state.iter().rev() {
if segment.lo >= v.x { continue }
let Some(seg_y) = segment.dist else { break };
if seg_y >= max_y {
max_y = seg_y;
if !segment.truncate_hi {
max_area = max(max_area, rect_area(v.x, segment.hi, v.y, seg_y));
}
if !segment.truncate_lo {
max_area = max(max_area, rect_area(v.x, segment.lo, v.y, seg_y));
}
}
}
max_area
}
fn update_state(state: &mut Vec<Segment>, incoming: Segment) {
for i in (0..state.len()).rev() {
let current = state[i];
if incoming.lo <= current.lo && current.hi <= incoming.hi {
state.remove(i);
} else if current.lo < incoming.lo && incoming.hi < current.hi {
state.push(Segment { hi: incoming.lo, truncate_hi: true, ..current });
state.push(Segment { lo: incoming.hi, truncate_lo: true, ..current });
state.remove(i);
} else if current.lo < incoming.lo && incoming.lo < current.hi {
state[i].hi = incoming.lo;
state[i].truncate_hi = true;
} else if current.lo < incoming.hi && incoming.hi < current.hi {
state[i].lo = incoming.hi;
state[i].truncate_lo = true;
}
}
state.push(incoming);
state.sort();
}
fn main() -> Result<(), io::Error> {
let f = File::open("9-input.txt")?;
let reader = BufReader::new(f);
let vertices: Vec<_> = reader.lines()
.map(|l| l?.parse::<Vertex>())
.collect::<Result<_, _>>()?;
let mut edges: Vec<Edge> = Vec::new();
for i in 0..vertices.len() {
let j = (i + 1) % vertices.len();
edges.push(Edge {
a: vertices[i],
b: vertices[j]
});
}
edges.sort_unstable_by_key(|e| e.a.y + e.b.y);
let horizontal_edges: Vec<_> = edges.into_iter()
.filter(|e| e.a.y == e.b.y)
.collect();
let mut max_area: i64 = 0;
let mut state: Vec<Segment> = Vec::new();
state.push(Segment { lo: i64::MIN, hi: i64::MAX, truncate_lo: false, truncate_hi: false, dist: None });
for e in horizontal_edges {
max_area = max(max_area, rect_from_point(&state, &e.a));
max_area = max(max_area, rect_from_point(&state, &e.b));
update_state(&mut state, Segment::new(e));
}
println!("Result {}", max_area);
Ok(())
}