Format all

This commit is contained in:
James Hogge
2025-12-27 06:45:39 +00:00
parent 0db0cff560
commit 4533333153
8 changed files with 187 additions and 134 deletions

View File

@@ -10,7 +10,7 @@ struct Machine {
#[allow(dead_code)]
lights: Vec<u64>,
buttons: Vec<Vec<usize>>,
joltages: Vec<u64>
joltages: Vec<u64>,
}
impl FromStr for Machine {
@@ -19,42 +19,46 @@ impl FromStr for Machine {
let groups: Vec<_> = s.split(' ').collect();
let lights_part = groups[0];
let lights = lights_part[1..lights_part.len()-1]
let lights = lights_part[1..lights_part.len() - 1]
.chars()
.map(|c| if c == '#' { 1 } else { 0 })
.collect();
let buttons: Vec<_> = (1..groups.len()-1)
let buttons: Vec<_> = (1..groups.len() - 1)
.map(|i| parse_button_group(groups[i]))
.collect::<Result<_, _>>()?;
let joltages_part = groups[groups.len()-1];
let joltages: Vec<_> = joltages_part[1..joltages_part.len()-1]
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>())
.collect::<Result<_, _>>()?;
Ok(Machine { lights, buttons , joltages })
Ok(Machine {
lights,
buttons,
joltages,
})
}
}
fn parse_button_group(s: &str) -> Result<Vec<usize>, ParseIntError> {
s[1..s.len()-1].split(',')
s[1..s.len() - 1]
.split(',')
.map(str::parse::<usize>)
.collect()
}
fn parity_solve(machine: &Machine, target: &[u64]) -> Vec<u64> {
let mut solutions: Vec<u64> = Vec::new();
for selection in 0..(1 << machine.buttons.len()) {
let mut parity = target.iter().rev()
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 {
parity = button.iter().fold(parity, |parity, j| {
parity ^ (1 << j)
});
parity = button.iter().fold(parity, |parity, j| parity ^ (1 << j));
}
}
if parity == 0 {
@@ -65,7 +69,11 @@ fn parity_solve(machine: &Machine, target: &[u64]) -> Vec<u64> {
}
fn minimum_solve(machine: &Machine) -> u64 {
fn apply_button<'a>(machine: &Machine, button_idx: usize, current: &'a mut Vec<u64>) -> Option<&'a mut Vec<u64>> {
fn apply_button<'a>(
machine: &Machine,
button_idx: usize,
current: &'a mut Vec<u64>,
) -> Option<&'a mut Vec<u64>> {
for i in &machine.buttons[button_idx] {
if current[*i] == 0 {
return None;
@@ -76,8 +84,12 @@ fn minimum_solve(machine: &Machine) -> u64 {
}
fn recurse(machine: &Machine, target: &[u64], max_cnt: i64) -> Option<u64> {
if target.iter().sum::<u64>() == 0 { return Some(0) };
if max_cnt <= 0 { return None };
if target.iter().sum::<u64>() == 0 {
return Some(0);
};
if max_cnt <= 0 {
return None;
};
let parity_solutions = parity_solve(machine, target);
@@ -85,8 +97,8 @@ fn minimum_solve(machine: &Machine) -> u64 {
let mut current_storage = target.to_vec();
for parity_sln in parity_solutions {
current_storage.copy_from_slice(target);
let current = (0..machine.buttons.len())
.fold(Some(&mut current_storage), |state, i| {
let current =
(0..machine.buttons.len()).fold(Some(&mut current_storage), |state, i| {
if parity_sln & (1 << i) != 0 {
apply_button(machine, i, state?)
} else {
@@ -96,17 +108,19 @@ fn minimum_solve(machine: &Machine) -> u64 {
let Some(current) = current else { continue };
let parity_ones = parity_sln.count_ones() as i64;
let new_max_cnt =
(match min_presses {
None => max_cnt,
Some(ms) => min(ms as i64, max_cnt)
} - parity_ones) / 2;
let new_max_cnt = (match min_presses {
None => max_cnt,
Some(ms) => min(ms as i64, max_cnt),
} - parity_ones)
/ 2;
let new_target: Vec<_> = current.iter().map(|&x| x / 2).collect();
let Some(sln) = recurse(machine, &new_target, new_max_cnt) else { continue };
let Some(sln) = recurse(machine, &new_target, new_max_cnt) else {
continue;
};
let presses = parity_ones as u64 + 2u64 * sln;
min_presses = Some(match min_presses {
None => presses,
Some(ms) => min(ms, presses)
Some(ms) => min(ms, presses),
});
}
min_presses
@@ -119,7 +133,8 @@ fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("10-input.txt")?;
let reader = BufReader::new(f);
let machines: Vec<_> = reader.lines()
let machines: Vec<_> = reader
.lines()
.map(|l| Ok(l?.parse::<Machine>()?))
.collect::<Result<_, Box<dyn Error>>>()?;
println!("Parsed {} machines", machines.len());

View File

@@ -1,30 +1,31 @@
use std::{collections::HashMap, fs::File, io::{self, BufRead, BufReader}};
use std::{
collections::HashMap,
fs::File,
io::{self, BufRead, BufReader},
};
#[derive(Clone)]
enum GraphNode {
Output,
Branch(Vec<String>)
Branch(Vec<String>),
}
fn count_paths(
node: &str,
nodes: &HashMap<String, GraphNode>,
memo: &mut HashMap<String, usize>
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()
}
};
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
}
@@ -33,7 +34,8 @@ fn main() -> Result<(), io::Error> {
let f = File::open("11-input.txt")?;
let reader = BufReader::new(f);
let nodes: HashMap<String, GraphNode> = reader.lines()
let nodes: HashMap<String, GraphNode> = reader
.lines()
.map(|line| {
let line = line?;
let parts: Vec<_> = line.split(' ').collect();
@@ -48,23 +50,38 @@ fn main() -> Result<(), io::Error> {
.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());
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);
println!(
"Result {}",
svr_paths + missing_both_paths - missing_fft_paths - missing_dac_paths
);
Ok(())
}

View File

@@ -1,27 +1,30 @@
use std::convert::Infallible;
use std::error::Error;
use std::num::ParseIntError;
use std::string::ParseError;
use std::{fs::File, io::{self, BufRead, BufReader}, str::FromStr};
use std::{
fs::File,
io::{BufRead, BufReader},
str::FromStr,
};
struct Piece {
cells: Vec<Vec<bool>>
cells: Vec<Vec<bool>>,
}
impl FromStr for Piece {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let cells =
s.split('\n').map(|row| {
row.chars().filter_map(|c| {
match c {
let cells = s
.split('\n')
.map(|row| {
row.chars()
.filter_map(|c| match c {
'#' => Some(true),
'.' => Some(false),
_ => None
}
})
.collect()
_ => None,
})
.collect()
})
.collect();
Ok(Piece { cells })
@@ -40,17 +43,14 @@ impl Piece {
}
fn true_area(&self) -> u64 {
self.cells.iter()
.flatten()
.filter(|&&b| b)
.count() as u64
self.cells.iter().flatten().filter(|&&b| b).count() as u64
}
}
struct Region {
width: u64,
height: u64,
requirements: Vec<u64>
requirements: Vec<u64>,
}
impl FromStr for Region {
@@ -61,26 +61,36 @@ impl FromStr for Region {
let (width_str, height_str) = area_str.split_once('x').unwrap();
let width = width_str.parse::<u64>()?;
let height = height_str.parse::<u64>()?;
let requirements = requirements_str.split(' ')
.filter_map(|s| s.parse::<u64>().ok())
.collect();
Ok(Region { width, height, requirements })
let requirements = requirements_str
.split(' ')
.filter_map(|s| s.parse::<u64>().ok())
.collect();
Ok(Region {
width,
height,
requirements,
})
}
}
fn is_solveable(pieces: &[Piece], region: &Region) -> bool {
// Lower area bound = perfectly packed
let region_area = region.width * region.height;
let used_area: u64 = pieces.iter()
let used_area: u64 = pieces
.iter()
.zip(region.requirements.iter())
.map(|(piece, cnt)| piece.true_area() * cnt)
.sum();
if used_area > region_area { return false };
if used_area > region_area {
return false;
};
// Upper area bound = 3x3 pieces
let available_3x3s = region.width / 3 * region.height / 3;
let piece_cnt: u64 = region.requirements.iter().sum();
if piece_cnt <= available_3x3s { return true };
if piece_cnt <= available_3x3s {
return true;
};
println!("Non-trivial solution");
false
@@ -90,22 +100,21 @@ fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("12-input.txt")?;
let reader = BufReader::new(f);
let lines: Vec<_> = reader.lines().collect::<Result<_,_>>()?;
let lines: Vec<_> = reader.lines().collect::<Result<_, _>>()?;
let (lines_pieces, lines_regions) = lines.split_at(30);
let pieces: Vec<_> = lines_pieces.chunks(5)
.map(|lines| {
Ok(lines[1..lines.len()-1].join("\n").parse::<Piece>()?)
})
let pieces: Vec<_> = lines_pieces
.chunks(5)
.map(|lines| Ok(lines[1..lines.len() - 1].join("\n").parse::<Piece>()?))
.collect::<Result<_, Box<dyn Error>>>()?;
let regions: Vec<_> = lines_regions.iter()
.map(|line| {
Ok(line.parse::<Region>()?)
})
let regions: Vec<_> = lines_regions
.iter()
.map(|line| Ok(line.parse::<Region>()?))
.collect::<Result<_, Box<dyn Error>>>()?;
let result: u64 = regions.iter()
let result: u64 = regions
.iter()
.filter(|region| is_solveable(&pieces, region))
.count() as u64;
println!("Result: {}", result);

View File

@@ -29,9 +29,7 @@ fn main() -> Result<(), io::Error> {
let f = File::open("2-input.txt")?;
let reader = BufReader::new(f);
let range_line = reader.lines().next().unwrap()?;
let mut ranges: Vec<_> = range_line.split(",")
.map(parse_range)
.collect();
let mut ranges: Vec<_> = range_line.split(",").map(parse_range).collect();
ranges.sort_unstable();
let mut total: i64 = 0;

View File

@@ -4,13 +4,9 @@ use std::{
};
fn max_joltage(s: &str) -> u64 {
let values: Vec<_> = s
.chars()
.map(|x| x.to_digit(10).unwrap())
.collect();
let values: Vec<_> = s.chars().map(|x| x.to_digit(10).unwrap()).collect();
let max_idx = |xs: &[u32]| {
xs.iter()
.enumerate()
.fold(0, |max, (i, &x)| if x > xs[max] { i } else { max })
@@ -27,11 +23,7 @@ fn main() -> Result<(), io::Error> {
let f = File::open("3-input.txt")?;
let reader = BufReader::new(f);
let total: u64 = reader.lines()
.map(|l| {
max_joltage(&l.unwrap())
})
.sum();
let total: u64 = reader.lines().map(|l| max_joltage(&l.unwrap())).sum();
println!("Result: {}", total);
Ok(())
}

View File

@@ -15,9 +15,7 @@ const NEIGHBOURS: [(i32, i32); 8] = [
];
fn parse_grid_line(line: &str) -> Vec<bool> {
line.chars()
.map(|c| c == '@')
.collect()
line.chars().map(|c| c == '@').collect()
}
fn get_grid_cell(grid: &[Vec<bool>], x: i32, y: i32) -> bool {

View File

@@ -1,4 +1,10 @@
use std::{error::Error, fs::File, io::{BufRead, BufReader}, num::ParseIntError, str::FromStr};
use std::{
error::Error,
fs::File,
io::{BufRead, BufReader},
num::ParseIntError,
str::FromStr,
};
#[derive(Debug)]
struct Node {
@@ -12,7 +18,7 @@ impl Node {
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
dx * dx + dy * dy + dz * dz
}
}
@@ -31,7 +37,7 @@ impl FromStr for Node {
#[derive(Debug)]
struct DisjointSet<T> {
nodes: Vec<T>,
parents: Vec<usize>
parents: Vec<usize>,
}
impl<T> DisjointSet<T> {
@@ -67,13 +73,14 @@ fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("8-input.txt")?;
let reader = BufReader::new(f);
let nodes: Vec<_> = reader.lines()
let nodes: Vec<_> = reader
.lines()
.map(|l| Ok(l?.parse::<Node>()?))
.collect::<Result<_, Box<dyn Error>>>()?;
let mut edges: Vec<(i64, usize, usize)> = Vec::new();
for i in 0..nodes.len() {
for j in i+1..nodes.len() {
for j in i + 1..nodes.len() {
let sqr_dist = Node::square_dist(&nodes[i], &nodes[j]);
edges.push((sqr_dist, i, j));
}
@@ -81,7 +88,8 @@ fn main() -> Result<(), Box<dyn Error>> {
edges.sort_unstable_by_key(|(d, _, _)| *d);
let mut ds: DisjointSet<_> = DisjointSet::new(nodes);
let connections: Vec<_> = edges.iter()
let connections: Vec<_> = edges
.iter()
.filter_map(|(_, i, j)| ds.union(*i, *j).map(|_| (*i, *j)))
.collect();

View File

@@ -5,7 +5,6 @@ use std::fs::File;
use std::io::{BufRead, BufReader};
use std::str::FromStr;
#[derive(Debug)]
struct ParseVertexError;
@@ -17,11 +16,10 @@ impl Display for ParseVertexError {
impl Error for ParseVertexError {}
#[derive(Debug)]
struct Vertex {
x: i64,
y: i64
y: i64,
}
impl FromStr for Vertex {
@@ -29,20 +27,23 @@ impl FromStr for Vertex {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut values = s.split(',').map(str::parse::<i64>);
let Some(Ok(x)) = values.next() else { return Err(ParseVertexError) };
let Some(Ok(y)) = values.next() else { return Err(ParseVertexError) };
let Some(Ok(x)) = values.next() else {
return Err(ParseVertexError);
};
let Some(Ok(y)) = values.next() else {
return Err(ParseVertexError);
};
Ok(Vertex { x, y })
}
}
#[derive(Clone, PartialEq, Eq)]
struct Segment {
lo: i64,
hi: i64,
truncate_lo: bool,
truncate_hi: bool,
dist: Option<i64>
dist: Option<i64>,
}
impl Segment {
@@ -52,9 +53,7 @@ impl Segment {
hi: max(start.x, end.x),
truncate_lo: false,
truncate_hi: false,
dist:
if start.x < end.x { Some(start.y) }
else { None }
dist: if start.x < end.x { Some(start.y) } else { None },
}
}
}
@@ -77,7 +76,6 @@ impl Debug for Segment {
}
}
fn rect_area(x1: i64, x2: i64, y1: i64, y2: i64) -> i64 {
(1 + i64::abs(x1 - x2)) * (1 + i64::abs(y1 - y2))
}
@@ -87,7 +85,9 @@ fn rect_from_point(state: &[Segment], v: &Vertex) -> i64 {
let mut max_y = i64::MIN;
for segment in state {
if segment.hi <= v.x { continue }
if segment.hi <= v.x {
continue;
}
let Some(seg_y) = segment.dist else { break };
if seg_y >= max_y {
@@ -103,7 +103,9 @@ fn rect_from_point(state: &[Segment], v: &Vertex) -> i64 {
let mut max_y = i64::MIN;
for segment in state.iter().rev() {
if segment.lo >= v.x { continue }
if segment.lo >= v.x {
continue;
}
let Some(seg_y) = segment.dist else { break };
if seg_y >= max_y {
@@ -125,8 +127,16 @@ fn update_state(state: &mut Vec<Segment>, incoming: Segment) {
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.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;
@@ -143,11 +153,11 @@ fn update_state(state: &mut Vec<Segment>, incoming: Segment) {
fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("9-input.txt")?;
let reader = BufReader::new(f);
let vertices: Vec<_> = reader.lines()
let vertices: Vec<_> = reader
.lines()
.map(|l| Ok(l?.parse::<Vertex>()?))
.collect::<Result<_, Box<dyn Error>>>()?;
let mut horizontal_edges: Vec<(usize, usize)> = Vec::new();
for i in 0..vertices.len() {
let j = (i + 1) % vertices.len();
@@ -158,7 +168,13 @@ fn main() -> Result<(), Box<dyn Error>> {
horizontal_edges.sort_unstable_by_key(|e| vertices[e.0].y);
let mut max_area: i64 = 0;
let mut state = vec![Segment { lo: i64::MIN, hi: i64::MAX, truncate_lo: false, truncate_hi: false, dist: None }];
let mut state = vec![Segment {
lo: i64::MIN,
hi: i64::MAX,
truncate_lo: false,
truncate_hi: false,
dist: None,
}];
for e in horizontal_edges {
let start = &vertices[e.0];
let end = &vertices[e.1];