Cleanup
This commit is contained in:
parent
a13b049276
commit
242f285231
|
@ -1,6 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "aoc2024"
|
name = "aoc2024"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "day1"
|
name = "day1"
|
||||||
|
|
|
@ -1,66 +1,47 @@
|
||||||
use std::{collections::HashMap, iter::zip, num::ParseIntError, str::FromStr};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
mod prelude;
|
mod prelude;
|
||||||
|
pub use crate::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> EmptyResult {
|
||||||
Day1::show(day_input(1), day_input(1))
|
let mut note: HistorianNote = day_input(1).parse()?;
|
||||||
|
show_answers(note.smallests_distances(), note.similarity_score());
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Day1 {}
|
struct HistorianNote {
|
||||||
impl Day1 {
|
left: Vec<i64>,
|
||||||
fn smallests_distances(input: &str) -> i64 {
|
right: Vec<i64>,
|
||||||
let mut lefts = Vec::<i64>::new();
|
}
|
||||||
let mut rights = Vec::<i64>::new();
|
|
||||||
for l in input.lines() {
|
|
||||||
let mut tokens = l.split(" ");
|
|
||||||
lefts.push(tokens.next().unwrap().parse().unwrap());
|
|
||||||
rights.push(tokens.next().unwrap().parse().unwrap());
|
|
||||||
}
|
|
||||||
lefts.sort();
|
|
||||||
rights.sort();
|
|
||||||
let mut sum = 0;
|
|
||||||
for (l, r) in zip(&lefts, &rights) {
|
|
||||||
sum += (l - r).abs();
|
|
||||||
}
|
|
||||||
sum
|
|
||||||
}
|
|
||||||
|
|
||||||
fn similarity_score(input: &str) -> i64 {
|
impl std::str::FromStr for HistorianNote {
|
||||||
let mut lefts = Vec::<i64>::new();
|
type Err = std::num::ParseIntError;
|
||||||
let mut rights: HashMap<i64, i64> = HashMap::new();
|
|
||||||
for l in input.lines() {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut left = Vec::<i64>::new();
|
||||||
|
let mut right = Vec::<i64>::new();
|
||||||
|
for l in s.lines() {
|
||||||
let mut tokens = l.split(" ");
|
let mut tokens = l.split(" ");
|
||||||
lefts.push(tokens.next().unwrap().parse().unwrap());
|
left.push(tokens.next().unwrap_or("").parse()?);
|
||||||
let right: i64 = tokens.next().unwrap().parse().unwrap();
|
right.push(tokens.next().unwrap_or("").parse()?);
|
||||||
match rights.get_mut(&right) {
|
|
||||||
None => {
|
|
||||||
rights.insert(right, 1);
|
|
||||||
}
|
|
||||||
Some(n) => {
|
|
||||||
*n += 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
let mut sum = 0;
|
Ok(Self { left, right })
|
||||||
for l in &lefts {
|
|
||||||
sum += (rights.get(l).unwrap_or(&0) * l)
|
|
||||||
}
|
|
||||||
sum
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AoCSolution for Day1 {
|
impl HistorianNote {
|
||||||
type Input = String;
|
fn smallests_distances(&mut self) -> i64 {
|
||||||
type Solution = i64;
|
self.left.sort();
|
||||||
|
self.right.sort();
|
||||||
fn part1(input: Self::Input) -> Self::Solution {
|
std::iter::zip(&self.left, &self.right).fold(0, |sum, (l, r)| sum + (l - r).abs())
|
||||||
Day1::smallests_distances(&input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: Self::Input) -> Self::Solution {
|
fn similarity_score(&self) -> i64 {
|
||||||
Day1::similarity_score(&input)
|
let frequencies = self.right.iter().fold(HashMap::new(), |mut map, r| {
|
||||||
|
*map.entry(r).or_default() += 1;
|
||||||
|
map
|
||||||
|
});
|
||||||
|
self.left
|
||||||
|
.iter()
|
||||||
|
.fold(0, |sum, l| sum + frequencies.get(l).unwrap_or(&0) * l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,30 +50,10 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() -> EmptyResult {
|
||||||
assert_eq!(
|
let mut note: HistorianNote = "3 4\n4 3\n2 5\n1 3\n3 9\n3 3".parse()?;
|
||||||
Day1::part1(
|
assert_eq!(note.smallests_distances(), 11);
|
||||||
r#"3 4
|
assert_eq!(note.similarity_score(), 31);
|
||||||
4 3
|
Ok(())
|
||||||
2 5
|
|
||||||
1 3
|
|
||||||
3 9
|
|
||||||
3 3"#
|
|
||||||
.into()
|
|
||||||
),
|
|
||||||
11
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Day1::part2(
|
|
||||||
r#"3 4
|
|
||||||
4 3
|
|
||||||
2 5
|
|
||||||
1 3
|
|
||||||
3 9
|
|
||||||
3 3"#
|
|
||||||
.into()
|
|
||||||
),
|
|
||||||
31
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
|
pub use std::collections::HashMap;
|
||||||
pub use std::fs::File;
|
pub use std::fs::File;
|
||||||
pub use std::io::Read;
|
pub use std::io::Read;
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::{env, io};
|
use std::{env, io};
|
||||||
|
|
||||||
|
pub type AnyResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||||
|
pub type EmptyResult = AnyResult<()>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum InputFileError {
|
enum InputFileError {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -42,29 +46,7 @@ pub fn day_input_file(day: u8) -> File {
|
||||||
File::open(&f).expect(format!("Failed to open file {}", f.display()).as_str())
|
File::open(&f).expect(format!("Failed to open file {}", f.display()).as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AoCSolution {
|
pub fn show_answers<T: std::fmt::Display>(part1: T, part2: T) {
|
||||||
type Input;
|
println!("Part 1: {}", part1);
|
||||||
type Solution: std::fmt::Debug + std::cmp::PartialEq + std::fmt::Display;
|
println!("Part 2: {}", part2);
|
||||||
|
|
||||||
// Are part1 and part2 solutions _always_ the same?
|
|
||||||
fn part1(input: Self::Input) -> Self::Solution;
|
|
||||||
fn part2(input: Self::Input) -> Self::Solution;
|
|
||||||
|
|
||||||
fn show(i1: Self::Input, i2: Self::Input) {
|
|
||||||
println!("Part 1: {}", Self::part1(i1));
|
|
||||||
println!("Part 2: {}", Self::part2(i2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub trait AoCSolutionTest: AoCSolution {
|
|
||||||
const PART1_TEST_INPUT: Self::Input;
|
|
||||||
const PART2_TEST_INPUT: Self::Input;
|
|
||||||
const PART1_TEST_RESULT: Self::Solution;
|
|
||||||
const PART2_TEST_RESULT: Self::Solution;
|
|
||||||
|
|
||||||
fn assert_valid() {
|
|
||||||
assert_eq!(Self::part1(Self::PART1_TEST_INPUT), Self::PART1_TEST_RESULT);
|
|
||||||
assert_eq!(Self::part2(Self::PART2_TEST_INPUT), Self::PART2_TEST_RESULT);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue