Rust
I don't love this code, but I didn't initially use a hashmap and it runs so fast it wasn't worth the time to refactor.
use std::{cmp::Ordering, fs, str::FromStr};
use color_eyre::eyre::{Report, Result};
use itertools::Itertools;
struct Updates(Vec<Vec<isize>>);
impl FromStr for Updates {
type Err = Report;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let pages = s
.lines()
.map(|l| l.split(",").map(|n| n.parse::<isize>()).collect())
.collect::<Result<_, _>>()?;
Ok(Self(pages))
}
}
impl Updates {
fn get_valid(&self, rules: &OrderingRules) -> Self {
let pages = self
.0
.clone()
.into_iter()
.filter(|p| rules.validate(p))
.collect();
Self(pages)
}
fn get_invalid(&self, rules: &OrderingRules) -> Self {
let pages = self
.0
.clone()
.into_iter()
.filter(|p| !rules.validate(p))
.collect();
Self(pages)
}
}
struct OrderingRules(Vec<(isize, isize)>);
impl OrderingRules {
fn validate(&self, pnums: &Vec<isize>) -> bool {
self.0.iter().all(|(a, b)| {
let Some(a_pos) = pnums.iter().position(|&x| x == *a) else {
return true;
};
let Some(b_pos) = pnums.iter().position(|&x| x == *b) else {
return true;
};
a_pos < b_pos
})
}
fn fix(&self, pnums: &Vec<isize>) -> Vec<isize> {
let mut v = pnums.clone();
v.sort_by(|a, b| {
let mut fr = self
.0
.iter()
.filter(|(ra, rb)| (ra == a || ra == b) && (rb == a || rb == b));
if let Some((ra, _rb)) = fr.next() {
if ra == a {
Ordering::Less
} else {
Ordering::Greater
}
} else {
Ordering::Equal
}
});
v
}
}
impl FromStr for OrderingRules {
type Err = Report;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let v = s
.lines()
.map(|l| {
l.splitn(2, "|")
.map(|n| n.parse::<isize>().unwrap())
.collect_tuple()
.ok_or_else(|| Report::msg("Rules need two items"))
})
.collect::<Result<_, _>>()?;
Ok(Self(v))
}
}
fn parse(s: &str) -> Result<(OrderingRules, Updates)> {
let parts: Vec<_> = s.splitn(2, "\n\n").collect();
let rules = OrderingRules::from_str(parts[0])?;
let updates = Updates::from_str(parts[1])?;
Ok((rules, updates))
}
fn part1(filepath: &str) -> Result<isize> {
let input = fs::read_to_string(filepath)?;
let (rules, updates) = parse(&input)?;
let res = updates
.get_valid(&rules)
.0
.iter()
.map(|v| v[v.len() / 2])
.sum();
Ok(res)
}
fn part2(filepath: &str) -> Result<isize> {
let input = fs::read_to_string(filepath)?;
let (rules, updates) = parse(&input)?;
let res = updates
.get_invalid(&rules)
.0
.iter()
.map(|v| rules.fix(&v))
.map(|v| v[v.len() / 2])
.sum();
Ok(res)
}
fn main() -> Result<()> {
color_eyre::install()?;
println!("Part 1: {}", part1("d05/input.txt")?);
println!("Part 2: {}", part2("d05/input.txt")?);
Ok(())
}