Improve solutions

This commit is contained in:
James Hogge
2025-12-27 06:42:38 +00:00
parent 8e31c24d4d
commit 0db0cff560
8 changed files with 250 additions and 264 deletions

View File

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

View File

@@ -1,3 +1,7 @@
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::{self, BufRead, BufReader}, str::FromStr};
struct Piece { struct Piece {
@@ -5,27 +9,28 @@ struct Piece {
} }
impl FromStr for Piece { impl FromStr for Piece {
type Err = (); type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let cells: Vec<_> = let cells =
s.split('\n').map(|row| { s.split('\n').map(|row| {
row.chars().fold(Vec::new(), |mut state, c| { row.chars().filter_map(|c| {
match c { match c {
'#' => state.push(true), '#' => Some(true),
'.' => state.push(false), '.' => Some(false),
_ => () _ => None
}; }
state
}) })
.collect()
}) })
.collect(); .collect();
Ok(Piece { cells: cells }) Ok(Piece { cells })
} }
} }
impl Piece { impl Piece {
fn bounds_area(&self) -> u64 { #[allow(dead_code)]
fn bounding_area(&self) -> u64 {
let height = self.cells.len(); let height = self.cells.len();
if height > 0 { if height > 0 {
(self.cells[0].len() * height) as u64 (self.cells[0].len() * height) as u64
@@ -37,8 +42,8 @@ impl Piece {
fn true_area(&self) -> u64 { fn true_area(&self) -> u64 {
self.cells.iter() self.cells.iter()
.flatten() .flatten()
.map(|&b| if b { 1 } else { 0 }) .filter(|&&b| b)
.sum() .count() as u64
} }
} }
@@ -49,21 +54,17 @@ struct Region {
} }
impl FromStr for Region { impl FromStr for Region {
type Err = (); type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let (area_str, requirements_str) = s.split_once(':').unwrap(); let (area_str, requirements_str) = s.split_once(':').unwrap();
let (width_str, height_str) = area_str.split_once('x').unwrap(); let (width_str, height_str) = area_str.split_once('x').unwrap();
let width = width_str.parse::<u64>().unwrap(); let width = width_str.parse::<u64>()?;
let height = height_str.parse::<u64>().unwrap(); let height = height_str.parse::<u64>()?;
let requirements: Vec<_> = requirements_str.split(' ') let requirements = requirements_str.split(' ')
.fold(Vec::new(), |mut state, count| { .filter_map(|s| s.parse::<u64>().ok())
if let Ok(count) = count.parse::<u64>() { .collect();
state.push(count); Ok(Region { width, height, requirements })
}
state
});
Ok(Region { width: width, height: height, requirements: requirements })
} }
} }
@@ -71,7 +72,7 @@ fn is_solveable(pieces: &[Piece], region: &Region) -> bool {
// Lower area bound = perfectly packed // Lower area bound = perfectly packed
let region_area = region.width * region.height; let region_area = region.width * region.height;
let used_area: u64 = pieces.iter() let used_area: u64 = pieces.iter()
.zip(region.requirements.clone()) .zip(region.requirements.iter())
.map(|(piece, cnt)| piece.true_area() * cnt) .map(|(piece, cnt)| piece.true_area() * cnt)
.sum(); .sum();
if used_area > region_area { return false }; if used_area > region_area { return false };
@@ -85,7 +86,7 @@ fn is_solveable(pieces: &[Piece], region: &Region) -> bool {
false false
} }
fn main() -> Result<(), io::Error> { fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("12-input.txt")?; let f = File::open("12-input.txt")?;
let reader = BufReader::new(f); let reader = BufReader::new(f);
@@ -94,25 +95,19 @@ fn main() -> Result<(), io::Error> {
let pieces: Vec<_> = lines_pieces.chunks(5) let pieces: Vec<_> = lines_pieces.chunks(5)
.map(|lines| { .map(|lines| {
lines[1..lines.len()-1].join("\n").parse::<Piece>().unwrap() Ok(lines[1..lines.len()-1].join("\n").parse::<Piece>()?)
}) })
.collect(); .collect::<Result<_, Box<dyn Error>>>()?;
let regions: Vec<_> = lines_regions.iter() let regions: Vec<_> = lines_regions.iter()
.map(|line| { .map(|line| {
line.parse::<Region>().unwrap() Ok(line.parse::<Region>()?)
}) })
.collect(); .collect::<Result<_, Box<dyn Error>>>()?;
let result = regions.iter() let result: u64 = regions.iter()
.map(|region| { .filter(|region| is_solveable(&pieces, region))
if is_solveable(&pieces, region) { .count() as u64;
1
} else {
0
}
})
.sum::<u64>();
println!("Result: {}", result); println!("Result: {}", result);
Ok(()) Ok(())
} }

View File

@@ -1,4 +1,5 @@
use std::{ use std::{
cmp::max,
fs::File, fs::File,
io::{self, BufRead, BufReader}, io::{self, BufRead, BufReader},
}; };
@@ -8,48 +9,37 @@ fn parse_range(s: &str) -> (i64, i64) {
(low_s.parse().unwrap(), high_s.parse().unwrap()) (low_s.parse().unwrap(), high_s.parse().unwrap())
} }
fn invalid_in_range(low: i64, high: i64) -> i64 { fn is_invalid(value: i64) -> bool {
let mut total: i64 = 0; let len = value.ilog10() + 1;
for i in low..=high { for j in (1..len).filter(|x| len % x == 0) {
let len = i.ilog10() + 1; let split = 10_i64.pow(j);
for j in (2..=len).filter(|x| len % x == 0) { let target = value % split;
let split = 10i64.pow(len / j); let mut mut_value = value;
let target = i % split; while mut_value > 0 && (mut_value % split) == target {
let mut mut_i = i; mut_value /= split;
while mut_i > 0 && (mut_i % split) == target { }
mut_i /= split; if mut_value == 0 {
} return true;
if mut_i == 0 {
// println!("{} in {}-{}", i, low, high);
total += i;
break;
}
} }
} }
total false
} }
fn main() -> Result<(), io::Error> { fn main() -> Result<(), io::Error> {
let f = File::open("2-input.txt")?; let f = File::open("2-input.txt")?;
let reader = BufReader::new(f); let reader = BufReader::new(f);
let mut ranges = reader let range_line = reader.lines().next().unwrap()?;
.lines() let mut ranges: Vec<_> = range_line.split(",")
.next()
.unwrap()
.unwrap()
.split(",")
.map(parse_range) .map(parse_range)
.collect::<Vec<_>>(); .collect();
ranges.sort_unstable(); ranges.sort_unstable();
let mut total: i64 = 0; let mut total: i64 = 0;
let mut last: i64 = 0; let mut last: i64 = 0;
for (low, high) in ranges { for (low, high) in ranges {
let low = low.max(last); let low = max(low, last);
if low <= high { last = max(high + 1, last);
total += invalid_in_range(low, high); total += (low..last).filter(|x| is_invalid(*x)).sum::<i64>();
}
last = high + 1;
} }
println!("Result {}", total); println!("Result {}", total);
Ok(()) Ok(())

View File

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

View File

@@ -14,6 +14,12 @@ const NEIGHBOURS: [(i32, i32); 8] = [
(1, 1), (1, 1),
]; ];
fn parse_grid_line(line: &str) -> Vec<bool> {
line.chars()
.map(|c| c == '@')
.collect()
}
fn get_grid_cell(grid: &[Vec<bool>], x: i32, y: i32) -> bool { fn get_grid_cell(grid: &[Vec<bool>], x: i32, y: i32) -> bool {
let (Ok(ux), Ok(uy)) = (usize::try_from(x), usize::try_from(y)) else { let (Ok(ux), Ok(uy)) = (usize::try_from(x), usize::try_from(y)) else {
return false; return false;
@@ -55,21 +61,19 @@ fn count_all_true(grid: &[Vec<bool>]) -> usize {
fn main() -> Result<(), io::Error> { fn main() -> Result<(), io::Error> {
let f = File::open("4-input.txt")?; let f = File::open("4-input.txt")?;
let reader = BufReader::new(f); let reader = BufReader::new(f);
let original_grid = reader let original_grid: Vec<_> = reader
.lines() .lines()
.collect::<Result<Vec<_>, _>>()? .map(|line| Ok(parse_grid_line(&line?)))
.iter() .collect::<Result<_, io::Error>>()?;
.map(|s| s.chars().map(|c| c == '@').collect::<Vec<_>>())
.collect::<Vec<_>>(); let mut current_grid = original_grid.clone();
let last_grid = loop {
let mut last_grid = original_grid.clone(); let next_grid = update_grid(&current_grid);
loop { if next_grid == current_grid {
let next_grid = update_grid(&last_grid); break next_grid;
if next_grid == last_grid {
break;
} }
last_grid = next_grid; current_grid = next_grid;
} };
println!( println!(
"Result: {}", "Result: {}",

View File

@@ -7,38 +7,40 @@ fn main() -> Result<(), io::Error> {
let f = File::open("7-input.txt")?; let f = File::open("7-input.txt")?;
let reader = BufReader::new(f); let reader = BufReader::new(f);
let lines: Vec<_> = reader.lines().collect::<Result<_, _>>()?; let mut lines = reader.lines();
let initial_state: Vec<_> = lines[0] let first = lines.next().transpose()?.unwrap();
let mut current_state: Vec<_> = first
.chars() .chars()
.map(|c| if c == 'S' { 1 } else { 0 }) .map(|c| if c == 'S' { 1 } else { 0 })
.collect(); .collect();
let mut next_state = vec![0; current_state.len()];
let mut splits: u64 = 0;
let (final_state, splits) = for line in lines {
lines[1..] for (i, c) in line?.chars().enumerate() {
.iter() match (c, current_state[i]) {
.fold((initial_state, 0u64), |(state, mut splits), row| { (_, 0) => (),
let mut next_state = vec![0; state.len()]; ('.', x) => {
for (i, c) in row.chars().enumerate() { next_state[i] += x;
match (c, state[i]) { }
(_, 0) => (), ('^', x) => {
('.', x) => { splits += 1;
next_state[i] += x; if i > 0 {
} next_state[i - 1] += x;
('^', x) => { }
splits += 1; if i < current_state.len() - 1 {
if i > 0 { next_state[i + 1] += x;
next_state[i - 1] += x;
}
if i < state.len() - 1 {
next_state[i + 1] += x;
}
}
_ => (),
} }
} }
(next_state, splits) _ => (),
}); }
let paths: u64 = final_state.iter().sum(); }
std::mem::swap(&mut current_state, &mut next_state);
next_state.fill(0);
}
let paths: u64 = current_state.iter().sum();
println!("Result: {} splits, {} paths", splits, paths); println!("Result: {} splits, {} paths", splits, paths);
Ok(()) Ok(())
} }

View File

@@ -1,6 +1,6 @@
use std::{collections::HashMap, fs::File, hash::Hash,io::{self, BufRead, BufReader}}; use std::{error::Error, fs::File, io::{BufRead, BufReader}, num::ParseIntError, str::FromStr};
#[derive(PartialEq, Eq, Clone, Hash, Debug)] #[derive(Debug)]
struct Node { struct Node {
x: u32, x: u32,
y: u32, y: u32,
@@ -16,88 +16,79 @@ impl Node {
} }
} }
impl From<&str> for Node { impl FromStr for Node {
fn from(value: &str) -> Self { type Err = ParseIntError;
let coords: Vec<_> = value.split(',').map(|s| s.parse::<u32>().unwrap()).collect(); fn from_str(s: &str) -> Result<Self, Self::Err> {
Node { let mut it = s.split(',').map(str::parse::<u32>);
x: coords[0], Ok(Node {
y: coords[1], x: it.next().unwrap()?,
z: coords[2] y: it.next().unwrap()?,
} z: it.next().unwrap()?,
})
} }
} }
#[derive(Debug, Clone)]
struct Edge {
a: Node,
b: Node
}
#[derive(Debug)] #[derive(Debug)]
struct DisjointSet<T> { struct DisjointSet<T> {
parents: HashMap<T, Option<T>> nodes: Vec<T>,
parents: Vec<usize>
} }
impl<T: Eq + Hash> FromIterator<T> for DisjointSet<T> { impl<T> DisjointSet<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self { fn new(nodes: Vec<T>) -> Self {
DisjointSet { let parents = (0..nodes.len()).collect();
parents: iter.into_iter().map(|x| (x, None)).collect() DisjointSet { nodes, parents }
}
} }
}
impl<T: Eq + Hash + Clone> DisjointSet<T> { fn representative_idx(&mut self, i: usize) -> usize {
fn representative(&mut self, x: &T) -> Option<T> { let rep = self.parents[i];
match self.parents.get(x)?.clone() { if rep == i {
None => Some(x.clone()), rep
Some(p) => { } else {
let rep = self.representative(&p)?; let rep = self.representative_idx(rep);
self.parents.insert(x.clone(), Some(rep.clone())); self.parents[i] = rep;
Some(rep) rep
}
} }
} }
fn union(&mut self, a: &T, b: &T) -> Option<T> { fn union(&mut self, i: usize, j: usize) -> Option<usize> {
let rep_a = self.representative(a).unwrap(); let rep_i = self.representative_idx(i);
let rep_b = self.representative(b).unwrap(); let rep_j = self.representative_idx(j);
if rep_a != rep_b { if rep_i != rep_j {
self.parents.insert(rep_a, Some(rep_b.clone())); self.parents[rep_j] = rep_i;
Some(rep_b) Some(rep_i)
} else { } else {
None None
} }
} }
} }
fn main() -> Result<(), io::Error> { fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("8-input.txt")?; let f = File::open("8-input.txt")?;
let reader = BufReader::new(f); let reader = BufReader::new(f);
let nodes: Vec<Node> = reader.lines() let nodes: Vec<_> = reader.lines()
.map(|lr| lr.map(|l| l.as_str().into())) .map(|l| Ok(l?.parse::<Node>()?))
.collect::<Result<_, _>>()?; .collect::<Result<_, Box<dyn Error>>>()?;
let node_cnt = nodes.len();
let mut all_edges: Vec<_> = let mut edges: Vec<(i64, usize, usize)> = Vec::new();
(0..node_cnt).flat_map(|a| { for i in 0..nodes.len() {
let na = &nodes[a]; for j in i+1..nodes.len() {
(a+1..node_cnt).map(|b| { let sqr_dist = Node::square_dist(&nodes[i], &nodes[j]);
let nb = &nodes[b]; edges.push((sqr_dist, i, j));
let sqr_dist = Node::square_dist(na, nb); }
(sqr_dist, Edge {a: na.clone(), b: nb.clone()}) }
}) edges.sort_unstable_by_key(|(d, _, _)| *d);
})
.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 mut ds: DisjointSet<_> = DisjointSet::new(nodes);
let connections: Vec<_> = all_edges.iter() let connections: Vec<_> = edges.iter()
.filter_map(|(_, e)| ds.union(&e.a, &e.b).map(|_|e.clone())) .filter_map(|(_, i, j)| ds.union(*i, *j).map(|_| (*i, *j)))
.collect(); .collect();
let nodes = ds.nodes;
let last_connection = connections.last().unwrap(); let last_connection = connections.last().unwrap();
println!("Result {}", u64::from(last_connection.a.x) * u64::from(last_connection.b.x)); let last_from = &nodes[last_connection.0];
let last_to = &nodes[last_connection.1];
println!("Result {}", u64::from(last_from.x) * u64::from(last_to.x));
Ok(()) Ok(())
} }

View File

@@ -1,32 +1,42 @@
use std::{cmp::{max, min, Ordering}, fs::File, i64, io::{self, BufRead, BufReader, ErrorKind}, fmt::Debug, str::FromStr}; use std::cmp::{Ordering, max, min};
use std::error::Error;
use std::fmt::{Debug, Display};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::str::FromStr;
#[derive(Clone, Copy, Debug)]
#[derive(Debug)]
struct ParseVertexError;
impl Display for ParseVertexError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "cannot parse vertex")
}
}
impl Error for ParseVertexError {}
#[derive(Debug)]
struct Vertex { struct Vertex {
x: i64, x: i64,
y: i64 y: i64
} }
impl FromStr for Vertex { impl FromStr for Vertex {
type Err = io::Error; type Err = ParseVertexError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<_> = s.split(',') let mut values = s.split(',').map(str::parse::<i64>);
.map(|s| s.parse::<i64>())
.collect();
let err = io::Error::new(ErrorKind::Other, "Unable to parse vertex"); let Some(Ok(x)) = values.next() else { return Err(ParseVertexError) };
let Some(&Ok(x)) = parts.get(0) else { return Err(err) }; let Some(Ok(y)) = values.next() else { return Err(ParseVertexError) };
let Some(&Ok(y)) = parts.get(1) else { return Err(err) }; Ok(Vertex { x, y })
Ok(Vertex { x: x, y: y})
} }
} }
#[derive(Debug)]
struct Edge {
a: Vertex,
b: Vertex
}
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
struct Segment { struct Segment {
lo: i64, lo: i64,
hi: i64, hi: i64,
@@ -36,14 +46,14 @@ struct Segment {
} }
impl Segment { impl Segment {
fn new(e: Edge) -> Segment { fn new(start: &Vertex, end: &Vertex) -> Segment {
Segment { Segment {
lo: min(e.a.x, e.b.x), lo: min(start.x, end.x),
hi: max(e.a.x, e.b.x), hi: max(start.x, end.x),
truncate_lo: false, truncate_lo: false,
truncate_hi: false, truncate_hi: false,
dist: dist:
if e.a.x < e.b.x { Some(e.a.y) } if start.x < end.x { Some(start.y) }
else { None } else { None }
} }
} }
@@ -51,33 +61,28 @@ impl Segment {
impl PartialOrd for Segment { impl PartialOrd for Segment {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.hi <= other.lo { Some(self.cmp(&other))
Some(Ordering::Less)
} else if other.hi <= self.lo {
Some(Ordering::Greater)
} else {
Some(Ordering::Equal)
}
} }
} }
impl Ord for Segment { impl Ord for Segment {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap() self.lo.cmp(&other.lo)
} }
} }
impl Debug for Segment { impl Debug for Segment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(format!("({}, {})", self.lo, self.hi).as_str()) write!(f, "({}, {})", self.lo, self.hi)
} }
} }
fn rect_area(x1: i64, x2: i64, y1: i64, y2: i64) -> i64 { fn rect_area(x1: i64, x2: i64, y1: i64, y2: i64) -> i64 {
(1 + i64::abs(x1 - x2)) * (1 + i64::abs(y1 - y2)) (1 + i64::abs(x1 - x2)) * (1 + i64::abs(y1 - y2))
} }
fn rect_from_point(state: &Vec<Segment>, v: &Vertex) -> i64 { fn rect_from_point(state: &[Segment], v: &Vertex) -> i64 {
let mut max_area = i64::MIN; let mut max_area = i64::MIN;
let mut max_y = i64::MIN; let mut max_y = i64::MIN;
@@ -116,7 +121,7 @@ fn rect_from_point(state: &Vec<Segment>, v: &Vertex) -> i64 {
fn update_state(state: &mut Vec<Segment>, incoming: Segment) { fn update_state(state: &mut Vec<Segment>, incoming: Segment) {
for i in (0..state.len()).rev() { for i in (0..state.len()).rev() {
let current = state[i]; let current = state[i].clone();
if incoming.lo <= current.lo && current.hi <= incoming.hi { if incoming.lo <= current.lo && current.hi <= incoming.hi {
state.remove(i); state.remove(i);
} else if current.lo < incoming.lo && incoming.hi < current.hi { } else if current.lo < incoming.lo && incoming.hi < current.hi {
@@ -135,34 +140,31 @@ fn update_state(state: &mut Vec<Segment>, incoming: Segment) {
state.sort(); state.sort();
} }
fn main() -> Result<(), io::Error> { fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("9-input.txt")?; let f = File::open("9-input.txt")?;
let reader = BufReader::new(f); let reader = BufReader::new(f);
let vertices: Vec<_> = reader.lines() let vertices: Vec<_> = reader.lines()
.map(|l| l?.parse::<Vertex>()) .map(|l| Ok(l?.parse::<Vertex>()?))
.collect::<Result<_, _>>()?; .collect::<Result<_, Box<dyn Error>>>()?;
let mut edges: Vec<Edge> = Vec::new();
let mut horizontal_edges: Vec<(usize, usize)> = Vec::new();
for i in 0..vertices.len() { for i in 0..vertices.len() {
let j = (i + 1) % vertices.len(); let j = (i + 1) % vertices.len();
edges.push(Edge { if vertices[i].y == vertices[j].y {
a: vertices[i], horizontal_edges.push((i, j));
b: vertices[j] }
});
} }
edges.sort_unstable_by_key(|e| e.a.y + e.b.y); horizontal_edges.sort_unstable_by_key(|e| vertices[e.0].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 max_area: i64 = 0;
let mut state: Vec<Segment> = Vec::new(); let mut state = vec![Segment { lo: i64::MIN, hi: i64::MAX, truncate_lo: false, truncate_hi: false, dist: None }];
state.push(Segment { lo: i64::MIN, hi: i64::MAX, truncate_lo: false, truncate_hi: false, dist: None });
for e in horizontal_edges { for e in horizontal_edges {
max_area = max(max_area, rect_from_point(&state, &e.a)); let start = &vertices[e.0];
max_area = max(max_area, rect_from_point(&state, &e.b)); let end = &vertices[e.1];
update_state(&mut state, Segment::new(e)); max_area = max(max_area, rect_from_point(&state, start));
max_area = max(max_area, rect_from_point(&state, end));
update_state(&mut state, Segment::new(start, end));
} }
println!("Result {}", max_area); println!("Result {}", max_area);