From: Greg Burri Date: Fri, 1 Dec 2023 14:29:28 +0000 (+0100) Subject: First day X-Git-Url: https://git.euphorik.ch/?a=commitdiff_plain;h=HEAD;p=advent_of_code_2023.git First day --- 6d5f75a6d6b38fcf22a71a74709bd67d26e2e19b diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..088ba6b --- /dev/null +++ b/.gitignore @@ -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 index 0000000..d596fa8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "advent_of_code_2023" +version = "0.1.0" +authors = ["Greg Burri "] +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.6" + +[profile.release] +opt-level = 3 +lto = true +codegen-units = 1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..cf6601d --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# AdventOfCode2023 + +https://adventofcode.com/2023 + + +# 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 index 0000000..94e535c --- /dev/null +++ b/src/day01.rs @@ -0,0 +1,86 @@ +use std::str::Lines; + +use itertools::Itertools; + +pub fn parse(s: &str) -> Lines { + s.lines() +} + +fn calibration_value(line: &str) -> u32 { + let first = line.chars().find(|c| c.is_digit(10)).unwrap(); + let last = line.chars().rev().find(|c| c.is_digit(10)).unwrap(); + format!("{}{}", first, last).parse().unwrap() +} + +pub fn calibration_sum(lines: &Lines) -> u32 { + lines.clone().map(calibration_value).sum() +} + +fn calibration_value_corrected(line: &str) -> u32 { + let line_chars = line.chars().collect_vec(); + let mut first: i32 = -1; + let mut last: i32 = -1; + let spelled_digits = [ + "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", + ]; + let mut i = 0; + while i < line.len() { + if let Some(d) = line_chars[i].to_digit(10) { + if first == -1 { + first = d as i32; + } + last = d as i32; + } else { + for j in 0..spelled_digits.len() { + let d = spelled_digits[j]; + if line[i..].starts_with(d) { + if first == -1 { + first = j as i32; + } + last = j as i32; + i += 1; + // We can't skip an entire word because of cases like + // "twone", "nineight", etc. + break; + } + } + } + + i += 1; + } + format!("{}{}", first, last).parse().unwrap() +} + +pub fn calibration_sum_corrected(lines: &Lines) -> u32 { + lines.clone().map(calibration_value_corrected).sum() +} + +#[cfg(test)] +mod tests { + use super::*; + + static CALIBRATION_LINES: &str = "1abc2 + pqr3stu8vwx + a1b2c3d4e5f + treb7uchet"; + + #[test] + fn part1() { + let lines = parse(CALIBRATION_LINES); + assert_eq!(calibration_sum(&lines), 142); + } + + static CALIBRATION_LINES_2: &str = "two1nine + eightwothree + abcone2threexyz + xtwone3four + 4nineeightseven2 + zoneight234 + 7pqrstsixteen"; + + #[test] + fn part2() { + let lines = parse(CALIBRATION_LINES_2); + assert_eq!(calibration_sum_corrected(&lines), 281); + } +} diff --git a/src/days.rs b/src/days.rs new file mode 100644 index 0000000..c1a501e --- /dev/null +++ b/src/days.rs @@ -0,0 +1,13 @@ +use std::fs; + +use crate::*; + +pub fn day01() -> String { + let input = fs::read_to_string("data/day01.input").unwrap(); + let lines = day01::parse(&input); + format!( + "part1: {}, part2: {}", + day01::calibration_sum(&lines), + day01::calibration_sum_corrected(&lines) + ) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7219d65 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,72 @@ +use std::time::Instant; + +use clap::Parser; +use rayon::prelude::*; + +mod day01; +mod days; + +#[derive(Parser, Debug)] +#[command(author = "Greg Burri", version = "1.0", about = "Advent of Code 2023")] +struct Args { + #[arg(index(1), exclusive(true))] + day: Option, + + #[arg(short, long)] + parallel: bool, +} + +fn main() { + println!("https://adventofcode.com/2023"); + + let days: Vec String> = vec![days::day01]; + + 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) + } +}