Day 01
authorGreg Burri <greg.burri@gmail.com>
Sun, 1 Dec 2024 21:54:29 +0000 (22:54 +0100)
committerGreg Burri <greg.burri@gmail.com>
Sun, 1 Dec 2024 21:54:29 +0000 (22:54 +0100)
.gitignore [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
README.md [new file with mode: 0644]
src/day01.rs [new file with mode: 0644]
src/days.rs [new file with mode: 0644]
src/main.rs [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..088ba6b
--- /dev/null
@@ -0,0 +1,10 @@
+# Generated by Cargo
+# will have compiled files and executables
+/target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644 (file)
index 0000000..34640d9
--- /dev/null
@@ -0,0 +1,18 @@
+[package]
+name = "advent_of_code_2022"
+version = "0.1.0"
+authors = ["Greg Burri <greg.burri@gmail.com>"]
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+itertools = "0.10"
+regex = "1"
+clap = { version = "4", features = ["derive"] }
+rayon = "1.10"
+
+[profile.release]
+opt-level = 3
+lto = true
+codegen-units = 1
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..5c71c81
--- /dev/null
+++ b/README.md
@@ -0,0 +1,27 @@
+# AdventOfCode2024
+
+https://adventofcode.com/2024
+
+
+# Running tests
+
+Example for day 1 tests:
+
+~~~
+cargo test day01 -- --nocapture
+~~~
+
+All tests:
+
+~~~
+cargo test -- --nocapture
+~~~
+
+
+# Running a day code
+
+~~~
+cargo run -- n
+~~~
+
+Where 'n' is a number from 1 to 25
diff --git a/src/day01.rs b/src/day01.rs
new file mode 100644 (file)
index 0000000..67f5cce
--- /dev/null
@@ -0,0 +1,80 @@
+use std::{cmp::Ordering, io::BufRead, iter::Iterator};
+
+pub fn read_location_ids_as_sorted<R>(reader: R) -> (Vec<i32>, Vec<i32>)
+where
+    R: BufRead,
+{
+    let mut l1 = Vec::<i32>::new();
+    let mut l2 = Vec::<i32>::new();
+
+    for l in reader.lines() {
+        let ids: Vec<i32> = l
+            .unwrap()
+            .split_whitespace()
+            .into_iter()
+            .map(|n| n.parse::<i32>().unwrap())
+            .collect();
+
+        l1.push(ids[0]);
+        l2.push(ids[1]);
+    }
+
+    l1.sort();
+    l2.sort();
+
+    (l1, l2)
+}
+
+pub fn sum_distances(sorted_ids1: &[i32], sorted_ids2: &[i32]) -> i32 {
+    sorted_ids1
+        .iter()
+        .zip(sorted_ids2)
+        .fold(0, |sum, (id1, id2)| sum + (id1 - id2).abs())
+}
+
+pub fn similarity_score(sorted_ids1: &[i32], sorted_ids2: &[i32]) -> i32 {
+    let mut score: i32 = 0;
+    let (mut i, mut j) = (0, 0);
+    while i < sorted_ids1.len() {
+        match sorted_ids1[i].cmp(&sorted_ids2[j]) {
+            Ordering::Greater => {
+                j += 1;
+            }
+            Ordering::Less => {
+                i += 1;
+            }
+            Ordering::Equal => {
+                score += sorted_ids2[j..]
+                    .iter()
+                    .take_while(|v| **v == sorted_ids1[i])
+                    .sum::<i32>();
+                i += 1;
+            }
+        }
+    }
+    score
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    static IDS: &str = "3   4
+4   3
+2   5
+1   3
+3   9
+3   3";
+
+    #[test]
+    fn part1() {
+        let (ids1, ids2) = read_location_ids_as_sorted(IDS.as_bytes());
+        assert_eq!(sum_distances(&ids1, &ids2), 11);
+    }
+
+    #[test]
+    fn part2() {
+        let (ids1, ids2) = read_location_ids_as_sorted(IDS.as_bytes());
+        assert_eq!(similarity_score(&ids1, &ids2), 31);
+    }
+}
diff --git a/src/days.rs b/src/days.rs
new file mode 100644 (file)
index 0000000..5c6ed4b
--- /dev/null
@@ -0,0 +1,13 @@
+use std::{fs, io::BufReader};
+
+use crate::*;
+
+pub fn day01() -> String {
+    let f = fs::File::open("data/day01.input").unwrap();
+    let (ids1, ids2) = day01::read_location_ids_as_sorted(BufReader::new(f));
+    format!(
+        "part1: {}, part2: {}",
+        day01::sum_distances(&ids1, &ids2),
+        day01::similarity_score(&ids1, &ids2)
+    )
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644 (file)
index 0000000..d268f58
--- /dev/null
@@ -0,0 +1,120 @@
+use std::time::Instant;
+
+use clap::Parser;
+use rayon::prelude::*;
+
+mod day01;
+// mod day02;
+// mod day03;
+// mod day04;
+// mod day05;
+// mod day06;
+// mod day07;
+// mod day08;
+// mod day09;
+// mod day10;
+// mod day11;
+// mod day12;
+// mod day13;
+// mod day14;
+// mod day15;
+// mod day16;
+// mod day17;
+// mod day18;
+// mod day19;
+// mod day20;
+// mod day21;
+// mod day22;
+// mod day23;
+// mod day24;
+mod days;
+
+#[derive(Parser, Debug)]
+#[command(author = "Greg Burri", version = "1.0", about = "Advent of Code 2024")]
+struct Args {
+    #[arg(index(1), exclusive(true))]
+    day: Option<usize>,
+
+    #[arg(short, long)]
+    parallel: bool,
+}
+
+fn main() {
+    println!("https://adventofcode.com/2024");
+
+    let days: Vec<fn() -> String> = vec![
+        days::day01,
+        // days::day02,
+        // days::day03,
+        // days::day04,
+        // days::day05,
+        // days::day06,
+        // days::day07,
+        // days::day08,
+        // days::day09,
+        // days::day10,
+        // days::day11,
+        // days::day12,
+        // days::day13,
+        // days::day14,
+        // days::day15,
+        // days::day16,
+        // days::day17,
+        // days::day18,
+        // days::day19,
+        // days::day20,
+        // days::day21,
+        // days::day22,
+        // days::day23,
+        // days::day24,
+    ];
+
+    let args = Args::parse();
+
+    match args.day {
+        Some(day) => {
+            if day >= 1 && day <= days.len() {
+                do_day(&days, day)
+            } else {
+                println!("Unknown day: {}", day)
+            }
+        }
+        // No argument -> execute all day problems.
+        None => {
+            let now = Instant::now();
+
+            if args.parallel {
+                (1..=days.len())
+                    .into_par_iter()
+                    .for_each(|d| do_day(&days, d));
+            } else {
+                (1..=days.len()).for_each(|d| do_day(&days, d));
+            }
+
+            println!(
+                "Time to execute all days: {}",
+                format_micros(now.elapsed().as_micros())
+            );
+        }
+    }
+}
+
+fn do_day(days: &[fn() -> String], day: usize) {
+    let now = Instant::now();
+    println!(
+        "Result of day {:02}: {} (time: {})",
+        day,
+        days[day - 1](),
+        format_micros(now.elapsed().as_micros())
+    );
+}
+
+fn format_micros(t: u128) -> String {
+    if t < 10_000 {
+        format!("{} μs", t)
+    } else if t < 10_000_000u128 {
+        format!("{:.2} ms", t as f64 / 1e3f64)
+    } else {
+        format!("{:.2} s", t as f64 / 1e6f64)
+    }
+}