chore: refactor for cargo-aoc / codspeed
This commit is contained in:
parent
3bfde9fd9b
commit
11b29a4d57
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
|||||||
**/target
|
**/target
|
||||||
input
|
input
|
||||||
flamegraph.svg
|
flamegraph.svg
|
||||||
perf.data
|
perf.data*
|
||||||
guesses
|
guesses
|
||||||
|
7
1/Cargo.lock
generated
7
1/Cargo.lock
generated
@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day1"
|
|
||||||
version = "0.1.0"
|
|
@ -1,6 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day1"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
105
1/src/main.rs
105
1/src/main.rs
@ -1,105 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufRead, BufReader, Lines};
|
|
||||||
use std::iter::zip;
|
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
// BOILERPLATE
|
|
||||||
type InputIter = Lines<BufReader<File>>;
|
|
||||||
|
|
||||||
fn get_input() -> InputIter {
|
|
||||||
let f = File::open("input").unwrap();
|
|
||||||
let br = BufReader::new(f);
|
|
||||||
br.lines()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans1 = problem1(get_input());
|
|
||||||
let duration = start.elapsed();
|
|
||||||
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
|
|
||||||
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans2 = problem2(get_input());
|
|
||||||
let duration = start.elapsed();
|
|
||||||
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Locations {
|
|
||||||
left: Vec<u64>,
|
|
||||||
right: Vec<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: BufRead> From<Lines<T>> for Locations {
|
|
||||||
fn from(input: Lines<T>) -> Self {
|
|
||||||
let mut left = Vec::new();
|
|
||||||
let mut right = Vec::new();
|
|
||||||
for line in input.map(|i| i.unwrap()) {
|
|
||||||
let parts: Vec<&str> = line.split_ascii_whitespace().collect();
|
|
||||||
left.push(parts[0].parse::<u64>().unwrap());
|
|
||||||
right.push(parts[1].parse::<u64>().unwrap());
|
|
||||||
}
|
|
||||||
Self { left, right }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Locations {
|
|
||||||
fn sort(&mut self) {
|
|
||||||
self.left.sort();
|
|
||||||
self.right.sort();
|
|
||||||
}
|
|
||||||
fn right_count(&self) -> HashMap<u64, u64> {
|
|
||||||
let mut right_count: HashMap<u64, u64> = HashMap::new();
|
|
||||||
for rval in &self.right {
|
|
||||||
right_count.insert(*rval, *right_count.get(rval).unwrap_or(&0) + 1);
|
|
||||||
}
|
|
||||||
right_count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PROBLEM 1 solution
|
|
||||||
|
|
||||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
|
||||||
let mut locations = Locations::from(input);
|
|
||||||
locations.sort();
|
|
||||||
|
|
||||||
zip(locations.left, locations.right)
|
|
||||||
.map(|(l, r)| u64::abs_diff(l, r))
|
|
||||||
.sum()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PROBLEM 2 solution
|
|
||||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
|
||||||
let locations = Locations::from(input);
|
|
||||||
let right_count = locations.right_count();
|
|
||||||
locations
|
|
||||||
.left
|
|
||||||
.iter()
|
|
||||||
.map(|l| l * right_count.get(l).unwrap_or(&0))
|
|
||||||
.sum::<u64>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::*;
|
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
const EXAMPLE: &str = &"3 4
|
|
||||||
4 3
|
|
||||||
2 5
|
|
||||||
1 3
|
|
||||||
3 9
|
|
||||||
3 3";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn problem1_example() {
|
|
||||||
let c = Cursor::new(EXAMPLE);
|
|
||||||
assert_eq!(problem1(c.lines()), 11);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn problem2_example() {
|
|
||||||
let c = Cursor::new(EXAMPLE);
|
|
||||||
assert_eq!(problem2(c.lines()), 31);
|
|
||||||
}
|
|
||||||
}
|
|
30
10/Cargo.lock
generated
30
10/Cargo.lock
generated
@ -1,30 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day10"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"grid",
|
|
||||||
"itertools",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "either"
|
|
||||||
version = "1.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "grid"
|
|
||||||
version = "0.1.0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day10"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
grid = { version = "0.1.0", path = "../libs/grid" }
|
|
||||||
itertools = "0.13.0"
|
|
25
11/Cargo.lock
generated
25
11/Cargo.lock
generated
@ -1,25 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day11"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"itertools",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "either"
|
|
||||||
version = "1.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
@ -1,7 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day11"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
itertools = "0.13.0"
|
|
135
11/src/main.rs
135
11/src/main.rs
@ -1,135 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufRead, BufReader, Lines};
|
|
||||||
use std::iter::repeat;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
// BOILERPLATE
|
|
||||||
type InputIter = Lines<BufReader<File>>;
|
|
||||||
|
|
||||||
pub fn get_input() -> InputIter {
|
|
||||||
let f = File::open("input").unwrap();
|
|
||||||
let br = BufReader::new(f);
|
|
||||||
br.lines()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn duration_format(duration: Duration) -> String {
|
|
||||||
match duration.as_secs_f64() {
|
|
||||||
x if x > 1.0 => format!("{:.3}s", x),
|
|
||||||
x if x > 0.010 => format!("{:.3}ms", x * 1e3),
|
|
||||||
x => format!("{:.3}us", x * 1e6),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans1 = problem1(input);
|
|
||||||
let duration1 = start.elapsed();
|
|
||||||
println!("Problem 1 solution: {} [{}]", ans1, duration_format(duration1));
|
|
||||||
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans2 = problem2(input);
|
|
||||||
let duration2 = start.elapsed();
|
|
||||||
println!("Problem 2 solution: {} [{}]", ans2, duration_format(duration2));
|
|
||||||
println!("Total duration: {}", duration_format(duration1 + duration2));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
|
||||||
struct Stone(u64);
|
|
||||||
struct Stones(Vec<Stone>);
|
|
||||||
|
|
||||||
impl From<&str> for Stones {
|
|
||||||
fn from(input: &str) -> Self {
|
|
||||||
Stones(
|
|
||||||
input
|
|
||||||
.split_ascii_whitespace()
|
|
||||||
.map(|v| Stone(v.parse().unwrap()))
|
|
||||||
.collect_vec(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BlinkResult {
|
|
||||||
One(Stone),
|
|
||||||
Two(Stone, Stone),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stone {
|
|
||||||
fn blink_once(self) -> BlinkResult {
|
|
||||||
let n_digits = if self.0 == 0 { 1 } else { self.0.ilog10() + 1 };
|
|
||||||
if self.0 == 0 {
|
|
||||||
BlinkResult::One(Stone(1))
|
|
||||||
} else if n_digits % 2 == 0 {
|
|
||||||
let parts = (self.0 / 10u64.pow(n_digits / 2), self.0 % 10u64.pow(n_digits / 2));
|
|
||||||
BlinkResult::Two(Stone(parts.0), Stone(parts.1))
|
|
||||||
} else {
|
|
||||||
BlinkResult::One(Stone(self.0 * 2024))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn count_blinks(stone: Stone, blink: usize, cache: &mut Vec<HashMap<Stone, u64>>) -> u64 {
|
|
||||||
if cache[blink].contains_key(&stone) {
|
|
||||||
return cache[blink][&stone];
|
|
||||||
}
|
|
||||||
let stones = stone.blink_once();
|
|
||||||
let result = if blink == 0 {
|
|
||||||
match stones {
|
|
||||||
BlinkResult::One(_) => 1,
|
|
||||||
BlinkResult::Two(_, _) => 2,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match stones {
|
|
||||||
BlinkResult::One(s) => count_blinks(s, blink - 1, cache),
|
|
||||||
BlinkResult::Two(s1, s2) => count_blinks(s1, blink - 1, cache) + count_blinks(s2, blink - 1, cache),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
cache[blink].insert(stone, result);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn blink_stones(stones: &Stones, blinks: usize) -> u64 {
|
|
||||||
let mut cache = Vec::from_iter(repeat(HashMap::new()).take(blinks));
|
|
||||||
stones
|
|
||||||
.0
|
|
||||||
.iter()
|
|
||||||
.map(|stone| count_blinks(*stone, blinks - 1, &mut cache))
|
|
||||||
.sum()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PROBLEM 1 solution
|
|
||||||
|
|
||||||
fn problem1<T: BufRead>(mut input: Lines<T>) -> u64 {
|
|
||||||
let stones = input.next().unwrap().unwrap().as_str().into();
|
|
||||||
blink_stones(&stones, 25)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PROBLEM 2 solution
|
|
||||||
fn problem2<T: BufRead>(mut input: Lines<T>) -> u64 {
|
|
||||||
let stones = input.next().unwrap().unwrap().as_str().into();
|
|
||||||
blink_stones(&stones, 75)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::*;
|
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
const EXAMPLE: &str = &"125 17";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn problem1_example() {
|
|
||||||
let c = Cursor::new(EXAMPLE);
|
|
||||||
assert_eq!(problem1(c.lines()), 55312);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn problem2_example() {
|
|
||||||
let c = Cursor::new(EXAMPLE);
|
|
||||||
assert_eq!(problem2(c.lines()), 65601038650482);
|
|
||||||
}
|
|
||||||
}
|
|
7
2/Cargo.lock
generated
7
2/Cargo.lock
generated
@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day2"
|
|
||||||
version = "0.1.0"
|
|
@ -1,6 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day2"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
133
2/src/main.rs
133
2/src/main.rs
@ -1,133 +0,0 @@
|
|||||||
use std::fs::File;
|
|
||||||
use std::io::{BufRead, BufReader, Lines};
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
// BOILERPLATE
|
|
||||||
type InputIter = Lines<BufReader<File>>;
|
|
||||||
|
|
||||||
fn get_input() -> InputIter {
|
|
||||||
let f = File::open("input").unwrap();
|
|
||||||
let br = BufReader::new(f);
|
|
||||||
br.lines()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn duration_format(duration: Duration) -> String {
|
|
||||||
match duration.as_secs_f64() {
|
|
||||||
x if x > 1.0 => format!("{:.3}s", x),
|
|
||||||
x if x > 0.010 => format!("{:.3}ms", x * 1e3),
|
|
||||||
x => format!("{:.3}us", x * 1e6),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans1 = problem1(input);
|
|
||||||
let duration1 = start.elapsed();
|
|
||||||
println!("Problem 1 solution: {} [{}]", ans1, duration_format(duration1));
|
|
||||||
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans2 = problem2(input);
|
|
||||||
let duration2 = start.elapsed();
|
|
||||||
println!("Problem 2 solution: {} [{}]", ans2, duration_format(duration2));
|
|
||||||
println!("Total duration: {}", duration_format(duration1 + duration2));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Reports {
|
|
||||||
reports: Vec<Vec<u64>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: BufRead> From<Lines<T>> for Reports {
|
|
||||||
fn from(lines: Lines<T>) -> Self {
|
|
||||||
let mut reports = Vec::new();
|
|
||||||
for line in lines.map(|i| i.unwrap()) {
|
|
||||||
reports.push(
|
|
||||||
line.split_ascii_whitespace()
|
|
||||||
.map(|record| record.parse::<u64>().unwrap())
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Reports { reports }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reports {
|
|
||||||
fn is_safe(report: &Vec<u64>) -> bool {
|
|
||||||
let mut ascending: bool = true;
|
|
||||||
let mut descending: bool = true;
|
|
||||||
for (a, b) in report.iter().zip(report.iter().skip(1)) {
|
|
||||||
if a > b {
|
|
||||||
ascending = false
|
|
||||||
}
|
|
||||||
if a < b {
|
|
||||||
descending = false;
|
|
||||||
}
|
|
||||||
let ad = a.abs_diff(*b);
|
|
||||||
if !(ad >= 1 && ad <= 3) || (!ascending && !descending) {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
fn count_safe(&self) -> u64 {
|
|
||||||
self.reports.iter().filter(|report| Self::is_safe(report)).count() as u64
|
|
||||||
}
|
|
||||||
fn is_dumb_dampened_safe(report: &Vec<u64>) -> bool {
|
|
||||||
if Self::is_safe(report) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for i in 0..report.len() {
|
|
||||||
let mut new_vec = report.clone();
|
|
||||||
new_vec.remove(i);
|
|
||||||
if Self::is_safe(&new_vec) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
fn dampened_count_safe(&self) -> u64 {
|
|
||||||
self.reports
|
|
||||||
.iter()
|
|
||||||
.filter(|report| Self::is_dumb_dampened_safe(report))
|
|
||||||
.count() as u64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PROBLEM 1 solution
|
|
||||||
|
|
||||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
|
||||||
let reports = Reports::from(input);
|
|
||||||
reports.count_safe()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PROBLEM 2 solution
|
|
||||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
|
||||||
let reports = Reports::from(input);
|
|
||||||
reports.dampened_count_safe()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::*;
|
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
const EXAMPLE: &str = &"7 6 4 2 1
|
|
||||||
1 2 7 8 9
|
|
||||||
9 7 6 2 1
|
|
||||||
1 3 2 4 5
|
|
||||||
8 6 4 4 1
|
|
||||||
1 3 6 7 9";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn problem1_example() {
|
|
||||||
let c = Cursor::new(EXAMPLE);
|
|
||||||
assert_eq!(problem1(c.lines()), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn problem2_example() {
|
|
||||||
let c = Cursor::new(EXAMPLE);
|
|
||||||
assert_eq!(problem2(c.lines()), 4);
|
|
||||||
}
|
|
||||||
}
|
|
71
3/Cargo.lock
generated
71
3/Cargo.lock
generated
@ -1,71 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aho-corasick"
|
|
||||||
version = "1.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day3"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"nom",
|
|
||||||
"regex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memchr"
|
|
||||||
version = "2.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "minimal-lexical"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nom"
|
|
||||||
version = "7.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
"minimal-lexical",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex"
|
|
||||||
version = "1.11.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-automata",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-automata"
|
|
||||||
version = "0.4.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
|
@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day3"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
nom = "7.1.3"
|
|
||||||
regex = { version = "1.11.1", default-features = false, features = ["perf", "std"] }
|
|
@ -1,94 +0,0 @@
|
|||||||
use regex::bytes::Regex;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufRead, BufReader, Lines};
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
// BOILERPLATE
|
|
||||||
type InputIter = Lines<BufReader<File>>;
|
|
||||||
|
|
||||||
pub fn get_input() -> InputIter {
|
|
||||||
let f = File::open("input").unwrap();
|
|
||||||
let br = BufReader::new(f);
|
|
||||||
br.lines()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn duration_format(duration: Duration) -> String {
|
|
||||||
match duration.as_secs_f64() {
|
|
||||||
x if x > 1.0 => format!("{:.3}s", x),
|
|
||||||
x if x > 0.010 => format!("{:.3}ms", x * 1e3),
|
|
||||||
x => format!("{:.3}us", x * 1e6),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans1 = problem1(input);
|
|
||||||
let duration1 = start.elapsed();
|
|
||||||
println!("Problem 1 solution: {} [{}]", ans1, duration_format(duration1));
|
|
||||||
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans2 = problem2(input);
|
|
||||||
let duration2 = start.elapsed();
|
|
||||||
println!("Problem 2 solution: {} [{}]", ans2, duration_format(duration2));
|
|
||||||
println!("Total duration: {}", duration_format(duration1 + duration2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// PROBLEM 1 solution
|
|
||||||
|
|
||||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
|
||||||
let mut sum = 0u64;
|
|
||||||
let re = Regex::new(r"(?-u)mul\((\d+),(\d+)\)").unwrap();
|
|
||||||
for line in input.map(|i| i.unwrap()) {
|
|
||||||
let line = line.as_bytes();
|
|
||||||
for m in re.captures_iter(line) {
|
|
||||||
sum += std::str::from_utf8(&m[1]).unwrap().parse::<u64>().unwrap()
|
|
||||||
* std::str::from_utf8(&m[2]).unwrap().parse::<u64>().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sum
|
|
||||||
}
|
|
||||||
|
|
||||||
// PROBLEM 2 solution
|
|
||||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
|
||||||
let mut sum = 0u64;
|
|
||||||
let mut do_mul = true;
|
|
||||||
let re = Regex::new(r"(?-u)(do\(\)|don't\(\)|mul\((\d+),(\d+)\))").unwrap();
|
|
||||||
for line in input.map(|i| i.unwrap()) {
|
|
||||||
let line = line.as_bytes();
|
|
||||||
for m in re.captures_iter(line) {
|
|
||||||
match std::str::from_utf8(&m[1]).unwrap() {
|
|
||||||
"do()" => do_mul = true,
|
|
||||||
"don't()" => do_mul = false,
|
|
||||||
_ if do_mul => {
|
|
||||||
sum += std::str::from_utf8(&m[2]).unwrap().parse::<u64>().unwrap()
|
|
||||||
* std::str::from_utf8(&m[3]).unwrap().parse::<u64>().unwrap()
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sum
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::*;
|
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
const EXAMPLE1: &str = &"xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))";
|
|
||||||
const EXAMPLE2: &str = &"xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn problem1_example() {
|
|
||||||
let c = Cursor::new(EXAMPLE1);
|
|
||||||
assert_eq!(problem1(c.lines()), 161);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn problem2_example() {
|
|
||||||
let c = Cursor::new(EXAMPLE2);
|
|
||||||
assert_eq!(problem2(c.lines()), 48);
|
|
||||||
}
|
|
||||||
}
|
|
7
4/Cargo.lock
generated
7
4/Cargo.lock
generated
@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day4"
|
|
||||||
version = "0.1.0"
|
|
@ -1,6 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day4"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
7
5/Cargo.lock
generated
7
5/Cargo.lock
generated
@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day5"
|
|
||||||
version = "0.1.0"
|
|
@ -1,6 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day5"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
21
6/Cargo.lock
generated
21
6/Cargo.lock
generated
@ -1,21 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "2.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day6"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"grid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "grid"
|
|
||||||
version = "0.1.0"
|
|
@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day6"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bitflags = "2.6.0"
|
|
||||||
grid = { version = "0.1.0", path = "../libs/grid" }
|
|
94
7/Cargo.lock
generated
94
7/Cargo.lock
generated
@ -1,94 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-deque"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-epoch",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-epoch"
|
|
||||||
version = "0.9.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-utils"
|
|
||||||
version = "0.8.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day7"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"itertools",
|
|
||||||
"rayon",
|
|
||||||
"thread_local",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "either"
|
|
||||||
version = "1.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.20.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon"
|
|
||||||
version = "1.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
"rayon-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon-core"
|
|
||||||
version = "1.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-deque",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thread_local"
|
|
||||||
version = "1.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
@ -1,9 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day7"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
itertools = "0.13.0"
|
|
||||||
rayon = "1.10.0"
|
|
||||||
thread_local = "1.1.8"
|
|
30
8/Cargo.lock
generated
30
8/Cargo.lock
generated
@ -1,30 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day8"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"grid",
|
|
||||||
"itertools",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "either"
|
|
||||||
version = "1.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "grid"
|
|
||||||
version = "0.1.0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day8"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
grid = { version = "0.1.0", path = "../libs/grid" }
|
|
||||||
itertools = "0.13.0"
|
|
25
9/Cargo.lock
generated
25
9/Cargo.lock
generated
@ -1,25 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day9"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"itertools",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "either"
|
|
||||||
version = "1.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
@ -1,7 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day9"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
itertools = "0.13.0"
|
|
272
Cargo.lock
generated
Normal file
272
Cargo.lock
generated
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aoc-runner"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d21ef9204ad206a5a3e918e9920da04e1118ad91ce4f23570be964b9d6b9dfcb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aoc-runner-derive"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba8b944269d3fee645d281b1335e1797044db497bb02d0098cc3fdb8900069cc"
|
||||||
|
dependencies = [
|
||||||
|
"aoc-runner-internal",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aoc-runner-internal"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "274b0ba7f3669a45ec0aaacf94eb032a749de880ab776091576cca94037c9982"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aoc2024"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"aoc-runner",
|
||||||
|
"aoc-runner-derive",
|
||||||
|
"bitflags",
|
||||||
|
"grid",
|
||||||
|
"itertools",
|
||||||
|
"rayon",
|
||||||
|
"regex",
|
||||||
|
"thread_local",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "grid"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.216"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.216"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.90",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.133"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.90"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "aoc2024"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
aoc-runner = "0.3.0"
|
||||||
|
aoc-runner-derive = "0.3.0"
|
||||||
|
bitflags = "2.6.0"
|
||||||
|
grid = { version = "0.1.0", path = "utils/grid" }
|
||||||
|
itertools = "0.13.0"
|
||||||
|
rayon = "1.10.0"
|
||||||
|
regex = "1.11.1"
|
||||||
|
thread_local = "1.1.8"
|
7
libs/grid/Cargo.lock
generated
7
libs/grid/Cargo.lock
generated
@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "grid"
|
|
||||||
version = "0.1.0"
|
|
90
src/day1.rs
Normal file
90
src/day1.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::io::{BufRead, Lines};
|
||||||
|
|
||||||
|
#[aoc_generator(day1)]
|
||||||
|
pub fn get_input(input: &[u8]) -> Locations {
|
||||||
|
Locations::from(input.lines())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Locations {
|
||||||
|
left: Vec<u64>,
|
||||||
|
right: Vec<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: BufRead> From<Lines<T>> for Locations {
|
||||||
|
fn from(input: Lines<T>) -> Self {
|
||||||
|
let mut left = Vec::new();
|
||||||
|
let mut right = Vec::new();
|
||||||
|
for line in input.map(|i| i.unwrap()) {
|
||||||
|
let parts: Vec<&str> = line.split_ascii_whitespace().collect();
|
||||||
|
left.push(parts[0].parse::<u64>().unwrap());
|
||||||
|
right.push(parts[1].parse::<u64>().unwrap());
|
||||||
|
}
|
||||||
|
Self { left, right }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Locations {
|
||||||
|
fn sort(&mut self) {
|
||||||
|
self.left.sort();
|
||||||
|
self.right.sort();
|
||||||
|
}
|
||||||
|
fn right_count(&self) -> HashMap<u64, u64> {
|
||||||
|
let mut right_count: HashMap<u64, u64> = HashMap::new();
|
||||||
|
for rval in &self.right {
|
||||||
|
right_count.insert(*rval, *right_count.get(rval).unwrap_or(&0) + 1);
|
||||||
|
}
|
||||||
|
right_count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROBLEM 1 solution
|
||||||
|
#[aoc(day1, part1)]
|
||||||
|
pub fn part1(locations: &Locations) -> u64 {
|
||||||
|
let mut locations = locations.clone();
|
||||||
|
locations.sort();
|
||||||
|
|
||||||
|
locations
|
||||||
|
.left
|
||||||
|
.iter()
|
||||||
|
.zip(locations.right)
|
||||||
|
.map(|(l, r)| u64::abs_diff(*l, r))
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROBLEM 2 solution
|
||||||
|
#[aoc(day1, part2)]
|
||||||
|
pub fn part2(locations: &Locations) -> u64 {
|
||||||
|
let right_count = locations.right_count();
|
||||||
|
locations
|
||||||
|
.left
|
||||||
|
.iter()
|
||||||
|
.map(|l| l * right_count.get(l).unwrap_or(&0))
|
||||||
|
.sum::<u64>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::day1::*;
|
||||||
|
|
||||||
|
const EXAMPLE: &[u8] = b"3 4
|
||||||
|
4 3
|
||||||
|
2 5
|
||||||
|
1 3
|
||||||
|
3 9
|
||||||
|
3 3";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
let input = get_input(EXAMPLE);
|
||||||
|
assert_eq!(part1(&input), 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
let input = get_input(EXAMPLE);
|
||||||
|
assert_eq!(part2(&input), 31);
|
||||||
|
}
|
||||||
|
}
|
@ -1,43 +1,14 @@
|
|||||||
use std::fs::File;
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
use std::io::{BufRead, BufReader, Lines};
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
use grid::Grid;
|
use grid::Grid;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use std::io::{BufRead, Lines};
|
||||||
|
|
||||||
// BOILERPLATE
|
#[aoc_generator(day10)]
|
||||||
type InputIter = Lines<BufReader<File>>;
|
pub fn get_input(input: &[u8]) -> TrailMap {
|
||||||
|
TrailMap::from(input.lines())
|
||||||
pub fn get_input() -> InputIter {
|
|
||||||
let f = File::open("input").unwrap();
|
|
||||||
let br = BufReader::new(f);
|
|
||||||
br.lines()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn duration_format(duration: Duration) -> String {
|
pub struct TrailMap {
|
||||||
match duration.as_secs_f64() {
|
|
||||||
x if x > 1.0 => format!("{:.3}s", x),
|
|
||||||
x if x > 0.010 => format!("{:.3}ms", x * 1e3),
|
|
||||||
x => format!("{:.3}us", x * 1e6),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans1 = problem1(input);
|
|
||||||
let duration1 = start.elapsed();
|
|
||||||
println!("Problem 1 solution: {} [{}]", ans1, duration_format(duration1));
|
|
||||||
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans2 = problem2(input);
|
|
||||||
let duration2 = start.elapsed();
|
|
||||||
println!("Problem 2 solution: {} [{}]", ans2, duration_format(duration2));
|
|
||||||
println!("Total duration: {}", duration_format(duration1 + duration2));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TrailMap {
|
|
||||||
map: Grid<u8>,
|
map: Grid<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,8 +37,6 @@ impl TrailMap {
|
|||||||
let our_val = self.map.get(pos.0, pos.1).unwrap();
|
let our_val = self.map.get(pos.0, pos.1).unwrap();
|
||||||
if our_val == needle {
|
if our_val == needle {
|
||||||
return 1;
|
return 1;
|
||||||
} else if our_val == needle {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
// adjacents that are +1
|
// adjacents that are +1
|
||||||
[(-1, 0), (1, 0), (0, -1), (0, 1)] // left, right, up, down
|
[(-1, 0), (1, 0), (0, -1), (0, 1)] // left, right, up, down
|
||||||
@ -92,15 +61,13 @@ impl TrailMap {
|
|||||||
.filter(|(_, val)| *val == Some(our_val + 1)) // only interested if it's our value + 1
|
.filter(|(_, val)| *val == Some(our_val + 1)) // only interested if it's our value + 1
|
||||||
.map(|(pos, _)| pos) // discard the value
|
.map(|(pos, _)| pos) // discard the value
|
||||||
.map(|mov| self.count_paths_to(&mov, needle))
|
.map(|mov| self.count_paths_to(&mov, needle))
|
||||||
.sum::<u64>() as u64
|
.sum::<u64>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 1 solution
|
// PROBLEM 1 solution
|
||||||
|
#[aoc(day10, part1)]
|
||||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
pub fn part1(map: &TrailMap) -> u64 {
|
||||||
let map: TrailMap = input.into();
|
|
||||||
|
|
||||||
map.trailheads()
|
map.trailheads()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|pos| {
|
.map(|pos| {
|
||||||
@ -111,21 +78,19 @@ fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 2 solution
|
// PROBLEM 2 solution
|
||||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
#[aoc(day10, part2)]
|
||||||
let map: TrailMap = input.into();
|
pub fn part2(map: &TrailMap) -> u64 {
|
||||||
|
|
||||||
map.trailheads()
|
map.trailheads()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|pos| map.count_paths_to(pos, b'9'))
|
.map(|pos| map.count_paths_to(pos, b'9'))
|
||||||
.sum::<u64>() as u64
|
.sum::<u64>()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::*;
|
use crate::day10::*;
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
const EXAMPLE: &str = &"89010123
|
const EXAMPLE: &[u8] = b"89010123
|
||||||
78121874
|
78121874
|
||||||
87430965
|
87430965
|
||||||
96549874
|
96549874
|
||||||
@ -135,14 +100,12 @@ mod tests {
|
|||||||
10456732";
|
10456732";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem1_example() {
|
fn part1_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part1(&get_input(EXAMPLE)), 36);
|
||||||
assert_eq!(problem1(c.lines()), 36);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem2_example() {
|
fn part2_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part2(&get_input(EXAMPLE)), 81);
|
||||||
assert_eq!(problem2(c.lines()), 81);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
102
src/day11.rs
Normal file
102
src/day11.rs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use itertools::Itertools;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::iter::repeat;
|
||||||
|
|
||||||
|
type IntType = u64;
|
||||||
|
type CacheType = HashMap<Stone, IntType>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
|
struct Stone(IntType);
|
||||||
|
struct Stones(Vec<Stone>);
|
||||||
|
|
||||||
|
enum BlinkResult {
|
||||||
|
One(Stone),
|
||||||
|
Two(Stone, Stone),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Stones {
|
||||||
|
fn from(input: &str) -> Self {
|
||||||
|
Stones(
|
||||||
|
input
|
||||||
|
.split_ascii_whitespace()
|
||||||
|
.map(|v| Stone(v.parse().unwrap()))
|
||||||
|
.collect_vec(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day11)]
|
||||||
|
fn parse(input: &str) -> Stones {
|
||||||
|
Stones::from(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stone {
|
||||||
|
fn blink_once(&self) -> BlinkResult {
|
||||||
|
let n_digits = if self.0 == 0 { 1 } else { self.0.ilog10() + 1 };
|
||||||
|
if self.0 == 0 {
|
||||||
|
BlinkResult::One(Stone(1))
|
||||||
|
} else if n_digits % 2 == 0 {
|
||||||
|
let split_factor = (10 as IntType).pow(n_digits / 2);
|
||||||
|
let parts = (self.0 / split_factor, self.0 % split_factor);
|
||||||
|
BlinkResult::Two(Stone(parts.0), Stone(parts.1))
|
||||||
|
} else {
|
||||||
|
BlinkResult::One(Stone(&self.0 * 2024))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count_blinks(stone: &Stone, blink: usize, cache: &mut Vec<CacheType>) -> IntType {
|
||||||
|
if cache[blink].contains_key(&stone) {
|
||||||
|
return cache[blink][&stone].clone();
|
||||||
|
}
|
||||||
|
let stones = stone.blink_once();
|
||||||
|
let result = if blink == 0 {
|
||||||
|
match stones {
|
||||||
|
BlinkResult::One(_) => 1,
|
||||||
|
BlinkResult::Two(_, _) => 2,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match stones {
|
||||||
|
BlinkResult::One(s) => count_blinks(&s, blink - 1, cache),
|
||||||
|
BlinkResult::Two(s1, s2) => count_blinks(&s1, blink - 1, cache) + count_blinks(&s2, blink - 1, cache),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cache[blink].insert(stone.clone(), result);
|
||||||
|
cache[blink][&stone].clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blink_stones(stones: &Stones, blinks: usize) -> IntType {
|
||||||
|
let mut cache = Vec::from_iter(repeat(CacheType::new()).take(blinks));
|
||||||
|
stones
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.map(|stone| count_blinks(stone, blinks - 1, &mut cache))
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day11, part1)]
|
||||||
|
fn part1(stones: &Stones) -> IntType {
|
||||||
|
blink_stones(stones, 25)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day11, part2)]
|
||||||
|
fn part2(stones: &Stones) -> IntType {
|
||||||
|
blink_stones(stones, 75)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
pub const EXAMPLE: &str = &"125 17";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EXAMPLE)), 55312);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EXAMPLE)), 65601038650482);
|
||||||
|
}
|
||||||
|
}
|
105
src/day2.rs
Normal file
105
src/day2.rs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use std::io::{BufRead, Lines};
|
||||||
|
|
||||||
|
#[aoc_generator(day2)]
|
||||||
|
pub fn get_input(input: &[u8]) -> Reports {
|
||||||
|
Reports::from(input.lines())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Reports {
|
||||||
|
reports: Vec<Vec<u64>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: BufRead> From<Lines<T>> for Reports {
|
||||||
|
fn from(lines: Lines<T>) -> Self {
|
||||||
|
let mut reports = Vec::new();
|
||||||
|
for line in lines.map(|i| i.unwrap()) {
|
||||||
|
reports.push(
|
||||||
|
line.split_ascii_whitespace()
|
||||||
|
.map(|record| record.parse::<u64>().unwrap())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Reports { reports }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Reports {
|
||||||
|
fn is_safe(report: &[u64]) -> bool {
|
||||||
|
let mut ascending: bool = true;
|
||||||
|
let mut descending: bool = true;
|
||||||
|
for (a, b) in report.iter().zip(report.iter().skip(1)) {
|
||||||
|
if a > b {
|
||||||
|
ascending = false
|
||||||
|
}
|
||||||
|
if a < b {
|
||||||
|
descending = false;
|
||||||
|
}
|
||||||
|
let ad = a.abs_diff(*b);
|
||||||
|
if !(1..=3).contains(&ad) || (!ascending && !descending) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
fn count_safe(&self) -> u64 {
|
||||||
|
self.reports.iter().filter(|report| Self::is_safe(report)).count() as u64
|
||||||
|
}
|
||||||
|
fn is_dumb_dampened_safe(report: &[u64]) -> bool {
|
||||||
|
if Self::is_safe(report) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for i in 0..report.len() {
|
||||||
|
let mut new_vec = report.to_owned();
|
||||||
|
new_vec.remove(i);
|
||||||
|
if Self::is_safe(&new_vec) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn dampened_count_safe(&self) -> u64 {
|
||||||
|
self.reports
|
||||||
|
.iter()
|
||||||
|
.filter(|report| Self::is_dumb_dampened_safe(report))
|
||||||
|
.count() as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROBLEM 1 solution
|
||||||
|
#[aoc(day2, part1)]
|
||||||
|
pub fn part1(input: &Reports) -> u64 {
|
||||||
|
input.count_safe()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROBLEM 2 solution
|
||||||
|
#[aoc(day2, part2)]
|
||||||
|
pub fn part2(input: &Reports) -> u64 {
|
||||||
|
input.dampened_count_safe()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::day2::*;
|
||||||
|
|
||||||
|
const EXAMPLE: &[u8] = b"7 6 4 2 1
|
||||||
|
1 2 7 8 9
|
||||||
|
9 7 6 2 1
|
||||||
|
1 3 2 4 5
|
||||||
|
8 6 4 4 1
|
||||||
|
1 3 6 7 9";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
let input = get_input(EXAMPLE);
|
||||||
|
println!("{:?}", input);
|
||||||
|
assert_eq!(part1(&input), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
let input = get_input(EXAMPLE);
|
||||||
|
assert_eq!(part2(&input), 4);
|
||||||
|
}
|
||||||
|
}
|
66
src/day3.rs
Normal file
66
src/day3.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use regex::bytes::Regex;
|
||||||
|
use std::io::BufRead;
|
||||||
|
|
||||||
|
#[aoc_generator(day3)]
|
||||||
|
pub fn get_input(input: &[u8]) -> Vec<String> {
|
||||||
|
input.lines().map(|l| l.unwrap()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROBLEM 1 solution
|
||||||
|
#[aoc(day3, part1)]
|
||||||
|
pub fn part1(input: &Vec<String>) -> u64 {
|
||||||
|
let mut sum = 0u64;
|
||||||
|
let re = Regex::new(r"(?-u)mul\((\d+),(\d+)\)").unwrap();
|
||||||
|
for line in input {
|
||||||
|
let line = line.as_bytes();
|
||||||
|
for m in re.captures_iter(line) {
|
||||||
|
sum += std::str::from_utf8(&m[1]).unwrap().parse::<u64>().unwrap()
|
||||||
|
* std::str::from_utf8(&m[2]).unwrap().parse::<u64>().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROBLEM 2 solution
|
||||||
|
#[aoc(day3, part2)]
|
||||||
|
pub fn part2(input: &Vec<String>) -> u64 {
|
||||||
|
let mut sum = 0u64;
|
||||||
|
let mut do_mul = true;
|
||||||
|
let re = Regex::new(r"(?-u)(do\(\)|don't\(\)|mul\((\d+),(\d+)\))").unwrap();
|
||||||
|
for line in input {
|
||||||
|
let line = line.as_bytes();
|
||||||
|
for m in re.captures_iter(line) {
|
||||||
|
match std::str::from_utf8(&m[1]).unwrap() {
|
||||||
|
"do()" => do_mul = true,
|
||||||
|
"don't()" => do_mul = false,
|
||||||
|
_ if do_mul => {
|
||||||
|
sum += std::str::from_utf8(&m[2]).unwrap().parse::<u64>().unwrap()
|
||||||
|
* std::str::from_utf8(&m[3]).unwrap().parse::<u64>().unwrap()
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::day3::*;
|
||||||
|
|
||||||
|
const EXAMPLE1: &[u8] = b"xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))";
|
||||||
|
const EXAMPLE2: &[u8] = b"xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
let input = get_input(EXAMPLE1);
|
||||||
|
assert_eq!(part1(&input), 161);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
let input = get_input(EXAMPLE2);
|
||||||
|
assert_eq!(part2(&input), 48);
|
||||||
|
}
|
||||||
|
}
|
@ -1,40 +1,12 @@
|
|||||||
use std::fs::File;
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
use std::io::{BufRead, BufReader, Lines};
|
use std::io::{BufRead, Lines};
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
// BOILERPLATE
|
#[aoc_generator(day4)]
|
||||||
type InputIter = Lines<BufReader<File>>;
|
pub fn get_input(input: &[u8]) -> WordSearch {
|
||||||
|
WordSearch::from(input.lines())
|
||||||
pub fn get_input() -> InputIter {
|
|
||||||
let f = File::open("input").unwrap();
|
|
||||||
let br = BufReader::new(f);
|
|
||||||
br.lines()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn duration_format(duration: Duration) -> String {
|
pub struct WordSearch {
|
||||||
match duration.as_secs_f64() {
|
|
||||||
x if x > 1.0 => format!("{:.3}s", x),
|
|
||||||
x if x > 0.010 => format!("{:.3}ms", x * 1e3),
|
|
||||||
x => format!("{:.3}us", x * 1e6),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans1 = problem1(input);
|
|
||||||
let duration1 = start.elapsed();
|
|
||||||
println!("Problem 1 solution: {} [{}]", ans1, duration_format(duration1));
|
|
||||||
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans2 = problem2(input);
|
|
||||||
let duration2 = start.elapsed();
|
|
||||||
println!("Problem 2 solution: {} [{}]", ans2, duration_format(duration2));
|
|
||||||
println!("Total duration: {}", duration_format(duration1 + duration2));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WordSearch {
|
|
||||||
rows: Vec<String>,
|
rows: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,22 +53,12 @@ impl WordSearch {
|
|||||||
for x in 0..width {
|
for x in 0..width {
|
||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
// check down-right
|
// check down-right
|
||||||
if x <= width - needle.len() && y <= height - needle.len() {
|
if x <= width - needle.len() && y <= height - needle.len() && (0..needle.len()).all(|i| self.get(x + i, y + i) == needle.as_bytes()[i].into()) {
|
||||||
if (0..needle.len())
|
count += 1
|
||||||
.into_iter()
|
|
||||||
.all(|i| self.get(x + i, y + i) == needle.as_bytes()[i].into())
|
|
||||||
{
|
|
||||||
count += 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// check down-left
|
// check down-left
|
||||||
if x >= needle.len() - 1 && y <= height - needle.len() {
|
if x >= needle.len() - 1 && y <= height - needle.len() && (0..needle.len()).all(|i| self.get(x - i, y + i) == needle.as_bytes()[i].into()) {
|
||||||
if (0..needle.len())
|
count += 1
|
||||||
.into_iter()
|
|
||||||
.all(|i| self.get(x - i, y + i) == needle.as_bytes()[i].into())
|
|
||||||
{
|
|
||||||
count += 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,11 +100,10 @@ impl WordSearch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 1 solution
|
// PROBLEM 1 solution
|
||||||
|
#[aoc(day4, part1)]
|
||||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
pub fn part1(ws: &WordSearch) -> u64 {
|
||||||
let needle = "XMAS";
|
let needle = "XMAS";
|
||||||
let needle_rev: String = needle.chars().rev().collect();
|
let needle_rev: String = needle.chars().rev().collect();
|
||||||
let ws = WordSearch::from(input);
|
|
||||||
ws.count_forward(needle)
|
ws.count_forward(needle)
|
||||||
+ ws.count_forward(&needle_rev)
|
+ ws.count_forward(&needle_rev)
|
||||||
+ ws.count_vertical(needle)
|
+ ws.count_vertical(needle)
|
||||||
@ -152,17 +113,16 @@ fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 2 solution
|
// PROBLEM 2 solution
|
||||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
#[aoc(day4, part2)]
|
||||||
let ws = WordSearch::from(input);
|
pub fn part2(ws: &WordSearch) -> u64 {
|
||||||
ws.count_x_mas()
|
ws.count_x_mas()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::*;
|
use crate::day4::*;
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
const EXAMPLE: &str = &"MMMSXXMASM
|
const EXAMPLE: &[u8] = b"MMMSXXMASM
|
||||||
MSAMXMSMSA
|
MSAMXMSMSA
|
||||||
AMXSXMAAMM
|
AMXSXMAAMM
|
||||||
MSAMASMSMX
|
MSAMASMSMX
|
||||||
@ -174,14 +134,12 @@ MAMMMXMMMM
|
|||||||
MXMXAXMASX";
|
MXMXAXMASX";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem1_example() {
|
fn part1_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part1(&get_input(EXAMPLE)), 18);
|
||||||
assert_eq!(problem1(c.lines()), 18);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem2_example() {
|
fn part2_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part2(&get_input(EXAMPLE)), 9);
|
||||||
assert_eq!(problem2(c.lines()), 9);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,43 +1,29 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fs::File;
|
use std::io::BufRead;
|
||||||
use std::io::{BufRead, BufReader, Lines};
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
// BOILERPLATE
|
#[aoc_generator(day5)]
|
||||||
type InputIter = Lines<BufReader<File>>;
|
pub fn get_input(input: &[u8]) -> (OrderingRules, Vec<Vec<u64>>) {
|
||||||
|
let mut lines = input.lines();
|
||||||
pub fn get_input() -> InputIter {
|
let rules = OrderingRules {
|
||||||
let f = File::open("input").unwrap();
|
rules: lines
|
||||||
let br = BufReader::new(f);
|
.by_ref()
|
||||||
br.lines()
|
.map_while(|l| match l {
|
||||||
}
|
Ok(line) if !line.is_empty() => Some(Box::new(BeforeRule::from(line)) as _),
|
||||||
|
_ => None,
|
||||||
fn duration_format(duration: Duration) -> String {
|
})
|
||||||
match duration.as_secs_f64() {
|
.collect(),
|
||||||
x if x > 1.0 => format!("{:.3}s", x),
|
};
|
||||||
x if x > 0.010 => format!("{:.3}ms", x * 1e3),
|
let updates: Vec<Vec<u64>> = lines
|
||||||
x => format!("{:.3}us", x * 1e6),
|
.by_ref()
|
||||||
}
|
.map(|l| l.unwrap().split(',').map(|n| n.parse::<u64>().unwrap()).collect())
|
||||||
}
|
.collect();
|
||||||
|
(rules, updates)
|
||||||
fn main() {
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans1 = problem1(input);
|
|
||||||
let duration1 = start.elapsed();
|
|
||||||
println!("Problem 1 solution: {} [{}]", ans1, duration_format(duration1));
|
|
||||||
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans2 = problem2(input);
|
|
||||||
let duration2 = start.elapsed();
|
|
||||||
println!("Problem 2 solution: {} [{}]", ans2, duration_format(duration2));
|
|
||||||
println!("Total duration: {}", duration_format(duration1 + duration2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Rule: Debug {
|
trait Rule: Debug {
|
||||||
fn check(&self, pages: &Vec<u64>) -> bool;
|
fn check(&self, pages: &[u64]) -> bool;
|
||||||
fn fail_pos(&self, pages: &Vec<u64>) -> Option<(usize, usize)>;
|
fn fail_pos(&self, pages: &[u64]) -> Option<(usize, usize)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -47,7 +33,7 @@ struct BeforeRule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Rule for BeforeRule {
|
impl Rule for BeforeRule {
|
||||||
fn check(&self, pages: &Vec<u64>) -> bool {
|
fn check(&self, pages: &[u64]) -> bool {
|
||||||
let mut seen_a = false;
|
let mut seen_a = false;
|
||||||
let mut seen_b = false;
|
let mut seen_b = false;
|
||||||
for page in pages {
|
for page in pages {
|
||||||
@ -63,15 +49,15 @@ impl Rule for BeforeRule {
|
|||||||
seen_b = true
|
seen_b = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
true
|
||||||
}
|
}
|
||||||
fn fail_pos(&self, pages: &Vec<u64>) -> Option<(usize, usize)> {
|
fn fail_pos(&self, pages: &[u64]) -> Option<(usize, usize)> {
|
||||||
let mut a_pos = None;
|
let mut a_pos = None;
|
||||||
let mut b_pos = None;
|
let mut b_pos = None;
|
||||||
for (pos, page) in pages.iter().enumerate() {
|
for (pos, page) in pages.iter().enumerate() {
|
||||||
if *page == self.a {
|
if *page == self.a {
|
||||||
if b_pos.is_some() {
|
if let Some(b_pos) = b_pos {
|
||||||
return Some((b_pos.unwrap(), pos));
|
return Some((b_pos, pos));
|
||||||
}
|
}
|
||||||
a_pos = Some(pos);
|
a_pos = Some(pos);
|
||||||
} else if *page == self.b {
|
} else if *page == self.b {
|
||||||
@ -93,19 +79,19 @@ impl From<String> for BeforeRule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct OrderingRules {
|
pub struct OrderingRules {
|
||||||
rules: Vec<Box<dyn Rule>>,
|
rules: Vec<Box<dyn Rule>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OrderingRules {
|
impl OrderingRules {
|
||||||
fn check(&self, pages: &Vec<u64>) -> bool {
|
fn check(&self, pages: &[u64]) -> bool {
|
||||||
self.rules.iter().all(|p| p.check(pages))
|
self.rules.iter().all(|p| p.check(pages))
|
||||||
}
|
}
|
||||||
fn fail_pos(&self, pages: &Vec<u64>) -> Option<(usize, usize)> {
|
fn fail_pos(&self, pages: &[u64]) -> Option<(usize, usize)> {
|
||||||
for rule in &self.rules {
|
for rule in &self.rules {
|
||||||
let fail_pos = rule.fail_pos(pages);
|
let fail_pos = rule.fail_pos(pages);
|
||||||
if fail_pos.is_some() {
|
if fail_pos.is_some() {
|
||||||
return fail_pos
|
return fail_pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@ -123,25 +109,11 @@ impl OrderingRules {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// PROBLEM 1 solution
|
// PROBLEM 1 solution
|
||||||
|
#[aoc(day5, part1)]
|
||||||
fn problem1<T: BufRead>(mut input: Lines<T>) -> u64 {
|
pub fn part1((rules, updates): &(OrderingRules, Vec<Vec<u64>>)) -> u64 {
|
||||||
let rules = OrderingRules {
|
|
||||||
rules: input
|
|
||||||
.by_ref()
|
|
||||||
.map_while(|l| match l {
|
|
||||||
Ok(line) if line != "" => Some(Box::new(BeforeRule::from(line)) as _),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
};
|
|
||||||
let updates: Vec<Vec<u64>> = input
|
|
||||||
.by_ref()
|
|
||||||
.map(|l| l.unwrap().split(',').map(|n| n.parse::<u64>().unwrap()).collect())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut res = 0;
|
let mut res = 0;
|
||||||
for update in updates {
|
for update in updates {
|
||||||
if rules.check(&update) {
|
if rules.check(update) {
|
||||||
res += update[update.len() / 2]
|
res += update[update.len() / 2]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,30 +121,18 @@ fn problem1<T: BufRead>(mut input: Lines<T>) -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 2 solution
|
// PROBLEM 2 solution
|
||||||
fn problem2<T: BufRead>(mut input: Lines<T>) -> u64 {
|
#[aoc(day5, part2)]
|
||||||
let rules = OrderingRules {
|
pub fn part2((rules, updates): &(OrderingRules, Vec<Vec<u64>>)) -> u64 {
|
||||||
rules: input
|
let mut updates = updates.clone();
|
||||||
.by_ref()
|
|
||||||
.map_while(|l| match l {
|
|
||||||
Ok(line) if line != "" => Some(Box::new(BeforeRule::from(line)) as _),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
};
|
|
||||||
let updates: Vec<Vec<u64>> = input
|
|
||||||
.by_ref()
|
|
||||||
.map(|l| l.unwrap().split(',').map(|n| n.parse::<u64>().unwrap()).collect())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut res = 0;
|
let mut res = 0;
|
||||||
for mut update in updates {
|
for update in updates.as_mut_slice() {
|
||||||
let mut did_swaps = false;
|
let mut did_swaps = false;
|
||||||
while let Some((a,b)) = rules.fail_pos(&update) {
|
while let Some((a, b)) = rules.fail_pos(update) {
|
||||||
did_swaps = true;
|
did_swaps = true;
|
||||||
update.swap(a, b);
|
update.swap(a, b);
|
||||||
}
|
}
|
||||||
if did_swaps {
|
if did_swaps {
|
||||||
if !rules.check(&update) {
|
if !rules.check(update) {
|
||||||
panic!("update still fails after swaps")
|
panic!("update still fails after swaps")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,10 +144,9 @@ fn problem2<T: BufRead>(mut input: Lines<T>) -> u64 {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::*;
|
use crate::day5::*;
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
const EXAMPLE: &str = &"47|53
|
const EXAMPLE: &[u8] = b"47|53
|
||||||
97|13
|
97|13
|
||||||
97|61
|
97|61
|
||||||
97|47
|
97|47
|
||||||
@ -217,14 +176,12 @@ mod tests {
|
|||||||
97,13,75,29,47";
|
97,13,75,29,47";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem1_example() {
|
fn part1_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part1(&get_input(EXAMPLE)), 143);
|
||||||
assert_eq!(problem1(c.lines()), 143);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem2_example() {
|
fn part2_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part2(&get_input(EXAMPLE)), 123);
|
||||||
assert_eq!(problem2(c.lines()), 123);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,40 +1,14 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fs::File;
|
use std::io::{BufRead, Lines};
|
||||||
use std::io::{BufRead, BufReader, Lines};
|
|
||||||
use std::ops::BitAnd;
|
use std::ops::BitAnd;
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
// BOILERPLATE
|
use grid::Grid;
|
||||||
type InputIter = Lines<BufReader<File>>;
|
|
||||||
|
|
||||||
pub fn get_input() -> InputIter {
|
#[aoc_generator(day6)]
|
||||||
let f = File::open("input").unwrap();
|
pub fn get_input(input: &[u8]) -> Map {
|
||||||
let br = BufReader::new(f);
|
Map::from(input.lines())
|
||||||
br.lines()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn duration_format(duration: Duration) -> String {
|
|
||||||
match duration.as_secs_f64() {
|
|
||||||
x if x > 1.0 => format!("{:.3}s", x),
|
|
||||||
x if x > 0.010 => format!("{:.3}ms", x * 1e3),
|
|
||||||
x => format!("{:.3}us", x * 1e6),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans1 = problem1(input);
|
|
||||||
let duration1 = start.elapsed();
|
|
||||||
println!("Problem 1 solution: {} [{}]", ans1, duration_format(duration1));
|
|
||||||
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans2 = problem2(input);
|
|
||||||
let duration2 = start.elapsed();
|
|
||||||
println!("Problem 2 solution: {} [{}]", ans2, duration_format(duration2));
|
|
||||||
println!("Total duration: {}", duration_format(duration1 + duration2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
@ -74,7 +48,6 @@ enum StepOutcome {
|
|||||||
enum RunOutcome {
|
enum RunOutcome {
|
||||||
LeftMap,
|
LeftMap,
|
||||||
LoopFound,
|
LoopFound,
|
||||||
Stuck,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
@ -112,9 +85,9 @@ impl BitAnd<FacingDirection> for DirectionSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Map {
|
pub struct Map {
|
||||||
grid: grid::Grid<u8>,
|
grid: Grid<u8>,
|
||||||
visited_from: grid::Grid<DirectionSet>,
|
visited_from: Grid<DirectionSet>,
|
||||||
guard_facing: FacingDirection,
|
guard_facing: FacingDirection,
|
||||||
guard_pos: (i64, i64),
|
guard_pos: (i64, i64),
|
||||||
path: Vec<((i64, i64), FacingDirection)>,
|
path: Vec<((i64, i64), FacingDirection)>,
|
||||||
@ -122,8 +95,8 @@ struct Map {
|
|||||||
|
|
||||||
impl<T: BufRead> From<Lines<T>> for Map {
|
impl<T: BufRead> From<Lines<T>> for Map {
|
||||||
fn from(input: Lines<T>) -> Self {
|
fn from(input: Lines<T>) -> Self {
|
||||||
let grid = grid::Grid::from(input);
|
let grid = Grid::from(input);
|
||||||
let mut visited_from: grid::Grid<DirectionSet> = grid::Grid::new(grid.width() as i64);
|
let mut visited_from: Grid<DirectionSet> = Grid::new(grid.width() as i64);
|
||||||
visited_from.data.resize(grid.data.len(), DirectionSet::empty());
|
visited_from.data.resize(grid.data.len(), DirectionSet::empty());
|
||||||
let guard_pos = grid.find(b'^').expect("Guard not found");
|
let guard_pos = grid.find(b'^').expect("Guard not found");
|
||||||
let guard_facing = FacingDirection::Up;
|
let guard_facing = FacingDirection::Up;
|
||||||
@ -190,17 +163,17 @@ impl Map {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 1 solution
|
// PROBLEM 1 solution
|
||||||
|
#[aoc(day6, part1)]
|
||||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
pub fn part1(map: &Map) -> u64 {
|
||||||
let mut map = Map::from(input);
|
let mut map = map.clone();
|
||||||
map.run_guard::<false>();
|
map.run_guard::<false>();
|
||||||
|
|
||||||
(map.grid.count(b'X') + map.grid.count(b'-') + map.grid.count(b'|') + map.grid.count(b'^')) as u64
|
(map.grid.count(b'X') + map.grid.count(b'-') + map.grid.count(b'|') + map.grid.count(b'^')) as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 2 solution
|
// PROBLEM 2 solution
|
||||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
#[aoc(day6, part2)]
|
||||||
let input_map = Map::from(input);
|
pub fn part2(input_map: &Map) -> u64 {
|
||||||
// Use the solution from problem 1 to reduce the number of positions where obstacle placement will change the path
|
// Use the solution from problem 1 to reduce the number of positions where obstacle placement will change the path
|
||||||
let mut path_map = input_map.clone();
|
let mut path_map = input_map.clone();
|
||||||
path_map.run_guard::<true>();
|
path_map.run_guard::<true>();
|
||||||
@ -231,10 +204,9 @@ fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::*;
|
use crate::day6::*;
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
const EXAMPLE: &str = &"....#.....
|
const EXAMPLE: &[u8] = b"....#.....
|
||||||
.........#
|
.........#
|
||||||
..........
|
..........
|
||||||
..#.......
|
..#.......
|
||||||
@ -246,14 +218,12 @@ mod tests {
|
|||||||
......#...";
|
......#...";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem1_example() {
|
fn part1_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part1(&get_input(EXAMPLE)), 41);
|
||||||
assert_eq!(problem1(c.lines()), 41);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem2_example() {
|
fn part2_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part2(&get_input(EXAMPLE)), 6);
|
||||||
assert_eq!(problem2(c.lines()), 6);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,41 +1,12 @@
|
|||||||
use std::fs::File;
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
use std::io::{BufRead, BufReader, Lines};
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||||
|
use std::io::{BufRead, Lines};
|
||||||
use thread_local::ThreadLocal;
|
use thread_local::ThreadLocal;
|
||||||
|
|
||||||
// BOILERPLATE
|
#[aoc_generator(day7)]
|
||||||
type InputIter = Lines<BufReader<File>>;
|
pub fn get_input(input: &[u8]) -> Calibrations {
|
||||||
|
Calibrations::from(input.lines())
|
||||||
pub fn get_input() -> InputIter {
|
|
||||||
let f = File::open("input").unwrap();
|
|
||||||
let br = BufReader::new(f);
|
|
||||||
br.lines()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn duration_format(duration: Duration) -> String {
|
|
||||||
match duration.as_secs_f64() {
|
|
||||||
x if x > 1.0 => format!("{:.3}s", x),
|
|
||||||
x if x > 0.010 => format!("{:.3}ms", x * 1e3),
|
|
||||||
x => format!("{:.3}us", x * 1e6),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans1 = problem1(input);
|
|
||||||
let duration1 = start.elapsed();
|
|
||||||
println!("Problem 1 solution: {} [{}]", ans1, duration_format(duration1));
|
|
||||||
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans2 = problem2(input);
|
|
||||||
let duration2 = start.elapsed();
|
|
||||||
println!("Problem 2 solution: {} [{}]", ans2, duration_format(duration2));
|
|
||||||
println!("Total duration: {}", duration_format(duration1 + duration2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -46,7 +17,7 @@ struct Calibration {
|
|||||||
|
|
||||||
impl From<&str> for Calibration {
|
impl From<&str> for Calibration {
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: &str) -> Self {
|
||||||
let (result, rest) = value.splitn(2, ':').next_tuple().unwrap();
|
let (result, rest) = value.split_once(':').unwrap();
|
||||||
Self {
|
Self {
|
||||||
result: result.parse().unwrap(),
|
result: result.parse().unwrap(),
|
||||||
numbers: rest.split_ascii_whitespace().map(|s| s.parse().unwrap()).collect(),
|
numbers: rest.split_ascii_whitespace().map(|s| s.parse().unwrap()).collect(),
|
||||||
@ -55,7 +26,7 @@ impl From<&str> for Calibration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Calibrations {
|
pub struct Calibrations {
|
||||||
cals: Vec<Calibration>,
|
cals: Vec<Calibration>,
|
||||||
longest_cal: usize,
|
longest_cal: usize,
|
||||||
}
|
}
|
||||||
@ -94,22 +65,18 @@ impl Calibrations {
|
|||||||
fn make_operator_sets(operators: &[Operator], n_opers: usize) -> Vec<Vec<Vec<Operator>>> {
|
fn make_operator_sets(operators: &[Operator], n_opers: usize) -> Vec<Vec<Vec<Operator>>> {
|
||||||
(0..n_opers)
|
(0..n_opers)
|
||||||
.map(|k| {
|
.map(|k| {
|
||||||
std::iter::repeat_n(operators.iter().map(|v| *v), k)
|
std::iter::repeat_n(operators.iter().copied(), k)
|
||||||
.multi_cartesian_product()
|
.multi_cartesian_product()
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
fn check_oper_set(cal: &Calibration, oper_set: &Vec<Operator>) -> bool {
|
fn check_oper_set(cal: &Calibration, oper_set: &[Operator]) -> bool {
|
||||||
let accum = oper_set
|
let accum = oper_set
|
||||||
.iter()
|
.iter()
|
||||||
.zip(cal.numbers.iter().skip(1))
|
.zip(cal.numbers.iter().skip(1))
|
||||||
.fold(cal.numbers[0], |accum, (oper, val)| oper.exec(accum, *val));
|
.fold(cal.numbers[0], |accum, (oper, val)| oper.exec(accum, *val));
|
||||||
if accum == cal.result {
|
accum == cal.result
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn possible(&self, operators: &[Operator]) -> u64 {
|
fn possible(&self, operators: &[Operator]) -> u64 {
|
||||||
let operator_sets = Calibrations::make_operator_sets(operators, self.longest_cal);
|
let operator_sets = Calibrations::make_operator_sets(operators, self.longest_cal);
|
||||||
@ -120,7 +87,7 @@ impl Calibrations {
|
|||||||
let tl = ThreadLocal::new();
|
let tl = ThreadLocal::new();
|
||||||
if operator_sets[n_opers]
|
if operator_sets[n_opers]
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.find_any(|oper_set| Self::check_oper_set(&cal, &oper_set))
|
.find_any(|oper_set| Self::check_oper_set(cal, oper_set))
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
let cal_local = tl.get_or(|| cal.clone());
|
let cal_local = tl.get_or(|| cal.clone());
|
||||||
@ -133,26 +100,24 @@ impl Calibrations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 1 solution
|
// PROBLEM 1 solution
|
||||||
|
#[aoc(day7, part1)]
|
||||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
pub fn part1(cals: &Calibrations) -> u64 {
|
||||||
let cals = Calibrations::from(input);
|
|
||||||
let operators = [Operator::Add, Operator::Multiply];
|
let operators = [Operator::Add, Operator::Multiply];
|
||||||
cals.possible(&operators)
|
cals.possible(&operators)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 2 solution
|
// PROBLEM 2 solution
|
||||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
#[aoc(day7, part2)]
|
||||||
let cals = Calibrations::from(input);
|
pub fn part2(cals: &Calibrations) -> u64 {
|
||||||
let operators = [Operator::Add, Operator::Multiply, Operator::Concatenate];
|
let operators = [Operator::Add, Operator::Multiply, Operator::Concatenate];
|
||||||
cals.possible(&operators)
|
cals.possible(&operators)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::*;
|
use crate::day7::*;
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
const EXAMPLE: &str = &"190: 10 19
|
const EXAMPLE: &[u8] = b"190: 10 19
|
||||||
3267: 81 40 27
|
3267: 81 40 27
|
||||||
83: 17 5
|
83: 17 5
|
||||||
156: 15 6
|
156: 15 6
|
||||||
@ -163,14 +128,12 @@ mod tests {
|
|||||||
292: 11 6 16 20";
|
292: 11 6 16 20";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem1_example() {
|
fn part1_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part1(&get_input(EXAMPLE)), 3749);
|
||||||
assert_eq!(problem1(c.lines()), 3749);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem2_example() {
|
fn part2_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part2(&get_input(EXAMPLE)), 11387);
|
||||||
assert_eq!(problem2(c.lines()), 11387);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,43 +1,15 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
use grid::Grid;
|
use grid::Grid;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fs::File;
|
use std::io::{BufRead, Lines};
|
||||||
use std::io::{BufRead, BufReader, Lines};
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
// BOILERPLATE
|
#[aoc_generator(day8)]
|
||||||
type InputIter = Lines<BufReader<File>>;
|
pub fn get_input(input: &[u8]) -> AntennaMap {
|
||||||
|
AntennaMap::from(input.lines())
|
||||||
pub fn get_input() -> InputIter {
|
|
||||||
let f = File::open("input").unwrap();
|
|
||||||
let br = BufReader::new(f);
|
|
||||||
br.lines()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn duration_format(duration: Duration) -> String {
|
pub struct AntennaMap {
|
||||||
match duration.as_secs_f64() {
|
|
||||||
x if x > 1.0 => format!("{:.3}s", x),
|
|
||||||
x if x > 0.010 => format!("{:.3}ms", x * 1e3),
|
|
||||||
x => format!("{:.3}us", x * 1e6),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans1 = problem1(input);
|
|
||||||
let duration1 = start.elapsed();
|
|
||||||
println!("Problem 1 solution: {} [{}]", ans1, duration_format(duration1));
|
|
||||||
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans2 = problem2(input);
|
|
||||||
let duration2 = start.elapsed();
|
|
||||||
println!("Problem 2 solution: {} [{}]", ans2, duration_format(duration2));
|
|
||||||
println!("Total duration: {}", duration_format(duration1 + duration2));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AntennaMap {
|
|
||||||
map: Grid<u8>,
|
map: Grid<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,13 +41,7 @@ impl AntennaMap {
|
|||||||
// to consider the 'negative' side of the line, which will be generated by the other pair
|
// to consider the 'negative' side of the line, which will be generated by the other pair
|
||||||
let (a, b) = (pair[0], pair[1]);
|
let (a, b) = (pair[0], pair[1]);
|
||||||
let offset = (a.0 - b.0, a.1 - b.1);
|
let offset = (a.0 - b.0, a.1 - b.1);
|
||||||
for i in (start..).map_while(|i| {
|
for i in (start..).map_while(|i| if Some(i - start) != reps { Some(i as i64) } else { None }) {
|
||||||
if Some(i - start) != reps {
|
|
||||||
Some(i as i64)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
let node_pos = (a.0 + i * offset.0, a.1 + i * offset.1);
|
let node_pos = (a.0 + i * offset.0, a.1 + i * offset.1);
|
||||||
if !antinodes.set(node_pos.0, node_pos.1, true) {
|
if !antinodes.set(node_pos.0, node_pos.1, true) {
|
||||||
// left the grid
|
// left the grid
|
||||||
@ -89,28 +55,24 @@ impl AntennaMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 1 solution
|
// PROBLEM 1 solution
|
||||||
|
#[aoc(day8, part1)]
|
||||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
pub fn part1(map: &AntennaMap) -> u64 {
|
||||||
let map = AntennaMap::from(input);
|
|
||||||
|
|
||||||
let antinodes = map.find_antinodes(1, Some(1));
|
let antinodes = map.find_antinodes(1, Some(1));
|
||||||
antinodes.count(true) as u64
|
antinodes.count(true) as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 2 solution
|
// PROBLEM 2 solution
|
||||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
#[aoc(day8, part2)]
|
||||||
let map = AntennaMap::from(input);
|
pub fn part2(map: &AntennaMap) -> u64 {
|
||||||
|
|
||||||
let antinodes = map.find_antinodes(0, None);
|
let antinodes = map.find_antinodes(0, None);
|
||||||
antinodes.count(true) as u64
|
antinodes.count(true) as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::*;
|
use crate::day8::*;
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
const EXAMPLE: &str = &"............
|
const EXAMPLE: &[u8] = b"............
|
||||||
........0...
|
........0...
|
||||||
.....0......
|
.....0......
|
||||||
.......0....
|
.......0....
|
||||||
@ -124,14 +86,12 @@ mod tests {
|
|||||||
............";
|
............";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem1_example() {
|
fn part1_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part1(&get_input(EXAMPLE)), 14);
|
||||||
assert_eq!(problem1(c.lines()), 14);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem2_example() {
|
fn part2_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part2(&get_input(EXAMPLE)), 34);
|
||||||
assert_eq!(problem2(c.lines()), 34);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,40 +1,11 @@
|
|||||||
use std::fmt::{Display, Write};
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufRead, BufReader, Lines};
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use std::fmt::{Display, Write};
|
||||||
|
use std::io::{BufRead, Lines};
|
||||||
|
|
||||||
// BOILERPLATE
|
#[aoc_generator(day9)]
|
||||||
type InputIter = Lines<BufReader<File>>;
|
pub fn get_input(input: &[u8]) -> DiskMap {
|
||||||
|
DiskMap::from(input.lines())
|
||||||
pub fn get_input() -> InputIter {
|
|
||||||
let f = File::open("input").unwrap();
|
|
||||||
let br = BufReader::new(f);
|
|
||||||
br.lines()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn duration_format(duration: Duration) -> String {
|
|
||||||
match duration.as_secs_f64() {
|
|
||||||
x if x > 1.0 => format!("{:.3}s", x),
|
|
||||||
x if x > 0.010 => format!("{:.3}ms", x * 1e3),
|
|
||||||
x => format!("{:.3}us", x * 1e6),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans1 = problem1(input);
|
|
||||||
let duration1 = start.elapsed();
|
|
||||||
println!("Problem 1 solution: {} [{}]", ans1, duration_format(duration1));
|
|
||||||
|
|
||||||
let input = get_input();
|
|
||||||
let start = Instant::now();
|
|
||||||
let ans2 = problem2(input);
|
|
||||||
let duration2 = start.elapsed();
|
|
||||||
println!("Problem 2 solution: {} [{}]", ans2, duration_format(duration2));
|
|
||||||
println!("Total duration: {}", duration_format(duration1 + duration2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
@ -43,13 +14,15 @@ enum Unit {
|
|||||||
Free,
|
Free,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct Inode {
|
struct Inode {
|
||||||
id: usize,
|
id: usize,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
len: u8,
|
len: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DiskMap {
|
#[derive(Clone)]
|
||||||
|
pub struct DiskMap {
|
||||||
map: Vec<Unit>,
|
map: Vec<Unit>,
|
||||||
files: Vec<Inode>,
|
files: Vec<Inode>,
|
||||||
frees: Vec<Inode>,
|
frees: Vec<Inode>,
|
||||||
@ -118,9 +91,9 @@ impl DiskMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 1 solution
|
// PROBLEM 1 solution
|
||||||
|
#[aoc(day9, part1)]
|
||||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
pub fn part1(map: &DiskMap) -> u64 {
|
||||||
let mut map = DiskMap::from(input);
|
let mut map = map.to_owned();
|
||||||
let mut last_free = 0;
|
let mut last_free = 0;
|
||||||
for file in map.files.iter().rev() {
|
for file in map.files.iter().rev() {
|
||||||
let frees = map
|
let frees = map
|
||||||
@ -148,8 +121,9 @@ fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 2 solution
|
// PROBLEM 2 solution
|
||||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
#[aoc(day9, part2)]
|
||||||
let mut map = DiskMap::from(input);
|
pub fn part2(map: &DiskMap) -> u64 {
|
||||||
|
let mut map = map.to_owned();
|
||||||
for file in map.files.iter().rev() {
|
for file in map.files.iter().rev() {
|
||||||
let free = map.frees.iter_mut().find(|inode| inode.len >= file.len); // find the first entry in the free space map large enough
|
let free = map.frees.iter_mut().find(|inode| inode.len >= file.len); // find the first entry in the free space map large enough
|
||||||
if let Some(free) = free {
|
if let Some(free) = free {
|
||||||
@ -176,20 +150,17 @@ fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::*;
|
use crate::day9::*;
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
const EXAMPLE: &str = &"2333133121414131402";
|
const EXAMPLE: &[u8] = b"2333133121414131402";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem1_example() {
|
fn part1_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part1(&get_input(EXAMPLE)), 1928);
|
||||||
assert_eq!(problem1(c.lines()), 1928);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn problem2_example() {
|
fn part2_example() {
|
||||||
let c = Cursor::new(EXAMPLE);
|
assert_eq!(part2(&get_input(EXAMPLE)), 2858);
|
||||||
assert_eq!(problem2(c.lines()), 2858);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
15
src/lib.rs
Normal file
15
src/lib.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
mod day11;
|
||||||
|
use aoc_runner_derive::aoc_lib;
|
||||||
|
|
||||||
|
mod day1;
|
||||||
|
mod day2;
|
||||||
|
mod day3;
|
||||||
|
mod day4;
|
||||||
|
mod day5;
|
||||||
|
mod day6;
|
||||||
|
mod day7;
|
||||||
|
mod day8;
|
||||||
|
mod day9;
|
||||||
|
mod day10;
|
||||||
|
|
||||||
|
aoc_lib! { year = 2024 }
|
@ -4,3 +4,6 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "lib.rs"
|
@ -1,5 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Debug, Display, Formatter, Write},
|
fmt::{Debug, Display, Formatter, Write},
|
||||||
io::{BufRead, Lines},
|
io::{BufRead, Lines},
|
||||||
iter::repeat,
|
iter::repeat,
|
||||||
};
|
};
|
||||||
@ -135,12 +135,25 @@ impl<T: BufRead> From<Lines<T>> for Grid<u8> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy + Eq + PartialEq + Display + Debug> Display for Grid<T> {
|
// impl<T: Copy + Eq + PartialEq + Display + Debug + Into<char>> Display for Grid<T> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
// for y in 0..self.height() {
|
||||||
|
// for x in 0..self.width() {
|
||||||
|
// f.write_fmt(format_args!("{}",self.get(x as i64, y as i64).unwrap() as char))?;
|
||||||
|
// }
|
||||||
|
// f.write_char('\n')?;
|
||||||
|
// }
|
||||||
|
// f.write_char('\n')
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl Display for Grid<u8> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
for y in 0..self.height() {
|
for y in 0..self.height() {
|
||||||
for x in 0..self.width() {
|
for x in 0..self.width() {
|
||||||
self.get(x as i64, y as i64).fmt(f)?;
|
f.write_fmt(format_args!("{}",self.get(x as i64, y as i64).unwrap() as char))?;
|
||||||
}
|
}
|
||||||
|
f.write_char('\n')?;
|
||||||
}
|
}
|
||||||
f.write_char('\n')
|
f.write_char('\n')
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user