commit 81dbdcb25a2246900a2f576c56208a2ef18f9557 Author: Patrick Michl Date: Sun Jan 8 23:08:22 2023 +0100 initial commit and solutions diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a1fc117 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/Cargo.lock +/data \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d7b7784 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "aoc-rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..feef248 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,58 @@ +#![allow(unused)] +mod year2022; + +#[macro_export] +macro_rules! solve1 { + ($test:expr) => { + #[test] + fn sample_part1() { + let result = solve1(SAMPLE_INPUT); + println!("{result}"); + assert_eq!(result, $test); + } + }; + ($test:expr, $solution:expr) => { + #[test] + fn sample_part1() { + let result = solve1(SAMPLE_INPUT); + println!("{result}"); + assert_eq!(result, $test); + } + #[test] + fn real_part1() { + let now = std::time::SystemTime::now(); + let result = solve1(INPUT); + println!("{:?}", now.elapsed().unwrap()); + println!("{result}"); + assert_eq!(result, $solution); + } + }; +} + +#[macro_export] +macro_rules! solve2 { + ($test:expr) => { + #[test] + fn sample_part2() { + let result = solve2(SAMPLE_INPUT); + println!("{result}"); + assert_eq!(result, $test); + } + }; + ($test:expr, $solution:expr) => { + #[test] + fn sample_part2() { + let result = solve2(SAMPLE_INPUT); + println!("{result}"); + assert_eq!(result, $test); + } + #[test] + fn real_part2() { + let now = std::time::SystemTime::now(); + let result = solve2(INPUT); + println!("{:?}", now.elapsed().unwrap()); + println!("{result}"); + assert_eq!(result, $solution); + } + }; +} \ No newline at end of file diff --git a/src/year2022/day01.rs b/src/year2022/day01.rs new file mode 100644 index 0000000..4118ce0 --- /dev/null +++ b/src/year2022/day01.rs @@ -0,0 +1,25 @@ +use crate::{solve1, solve2}; +static INPUT: &str = include_str!("../../data/year2022/day01.txt"); +static SAMPLE_INPUT: &str = include_str!("../../data/year2022/day01_test.txt"); + +fn calories_per_elf(input: &str) -> impl Iterator + '_ { + input.split("\r\n\r\n").map(|group| { + group + .split("\r\n") + .map(|num| num.parse::().unwrap()) + .sum::() + }) +} + +fn solve1(input: &str) -> i64 { + calories_per_elf(input).max().unwrap() +} + +fn solve2(input: &str) -> i64 { + let mut cals: Vec = calories_per_elf(input).collect(); + cals.sort(); + cals.iter().rev().take(3).sum() +} + +solve1!(24000, 71506); +solve2!(45000, 209603); diff --git a/src/year2022/day02.rs b/src/year2022/day02.rs new file mode 100644 index 0000000..54c004a --- /dev/null +++ b/src/year2022/day02.rs @@ -0,0 +1,65 @@ +use crate::{solve1, solve2}; +static INPUT: &str = include_str!("../../data/year2022/day02.txt"); +static SAMPLE_INPUT: &str = include_str!("../../data/year2022/day02_test.txt"); + +fn win(input: &str) -> i64 { + match input { + "A X" => 3, + "A Y" => 6, + "A Z" => 0, + "B X" => 0, + "B Y" => 3, + "B Z" => 6, + "C X" => 6, + "C Y" => 0, + "C Z" => 3, + _ => unreachable!(), + } +} + +fn play(input: &str) -> char { + match input { + "A X" => 'Z', + "A Y" => 'X', + "A Z" => 'Y', + "B X" => 'X', + "B Y" => 'Y', + "B Z" => 'Z', + "C X" => 'Y', + "C Y" => 'Z', + "C Z" => 'X', + _ => unreachable!(), + } +} + +fn score(input: char) -> i64 { + match input { + 'X' => 1, + 'Y' => 2, + 'Z' => 3, + _ => unreachable!(), + } +} + +fn solve1(input: &str) -> i64 { + input + .lines() + .map(|line| win(line) + score(line.chars().last().unwrap())) + .sum() +} + +fn solve2(input: &str) -> i64 { + input + .lines() + .map(|line| { + let mut out = String::with_capacity(3); + out.push_str(&line[0..2]); + out.push(play(line)); + out + }) + .map(|line| win(&line) + score(line.chars().last().unwrap())) + .sum() +} + +solve1!(15, 11475); +solve2!(12, 16862); diff --git a/src/year2022/day03.rs b/src/year2022/day03.rs new file mode 100644 index 0000000..5346a76 --- /dev/null +++ b/src/year2022/day03.rs @@ -0,0 +1,48 @@ +use std::collections::HashSet; + +use crate::{solve1, solve2}; +static INPUT: &str = include_str!("../../data/year2022/day03.txt"); +static SAMPLE_INPUT: &str = include_str!("../../data/year2022/day03_test.txt"); + +const fn prio(c: char) -> i64 { + match c { + 'a'..='z' => c as i64 - 96, + 'A'..='Z' => c as i64 - 64 + 26, + _ => unreachable!(), + } +} + +fn solve1(input: &str) -> i64 { + input + .lines() + .map(|line| { + let (left, right) = line.split_at(line.len() / 2); + let set: Vec = left.chars().collect(); + let dup = right + .chars() + .find(|c| set.contains(c)) + .expect("char has no duplicate"); + prio(dup) + }) + .sum() +} + +fn solve2(input: &str) -> i64 { + let mut lines = input.lines(); + let mut sum = 0; + while let Some(first) = lines.next() { + let second = lines.next().expect("No second rucksack"); + let third = lines.next().expect("No third rucksack"); + let set1: Vec = first.chars().collect(); + let set2: Vec = second.chars().collect(); + let dup = third + .chars() + .find(|c| set1.contains(c) && set2.contains(c)) + .expect("No dupes"); + sum += prio(dup) + } + sum +} + +solve1!(157, 8298); +solve2!(70, 2708); \ No newline at end of file diff --git a/src/year2022/day04.rs b/src/year2022/day04.rs new file mode 100644 index 0000000..0ffb7bb --- /dev/null +++ b/src/year2022/day04.rs @@ -0,0 +1,74 @@ +use crate::{solve1, solve2}; +static INPUT: &str = include_str!("../../data/year2022/day04.txt"); +static SAMPLE_INPUT: &str = include_str!("../../data/year2022/day04_test.txt"); + +struct AssignmentPair { + l1: i64, + l2: i64, + r1: i64, + r2: i64, +} + +impl AssignmentPair { + fn fully_contained(self) -> Option<()> { + if (self.l1 <= self.r1 && self.l2 >= self.r2) || (self.r1 <= self.l1 && self.r2 >= self.l2) + { + return Some(()); + } + None + } + + fn overlap(self) -> Option<()> { + if self.l1 >= self.r1 && self.l1 <= self.r2 + || self.l2 >= self.r1 && self.l2 <= self.r2 + || self.r1 >= self.l1 && self.r1 <= self.l2 + || self.r2 >= self.l1 && self.r2 <= self.l2 + { + return Some(()); + } + None + } +} + +fn parse_input(line: &str) -> AssignmentPair { + let mut split = line.split(','); + let (left, right) = ( + split.next().expect("No left side"), + split.next().expect("No right side"), + ); + let mut left_split = left.split('-'); + let mut right_split = right.split('-'); + let (l1, l2) = ( + left_split.next().expect("No left side"), + left_split.next().expect("No right side"), + ); + let (r1, r2) = ( + right_split.next().expect("No left side"), + right_split.next().expect("No right side"), + ); + AssignmentPair { + l1: l1.parse().expect("Not a number"), + l2: l2.parse().expect("Not a number"), + r1: r1.parse().expect("Not a number"), + r2: r2.parse().expect("Not a number"), + } +} + +fn solve1(input: &str) -> i64 { + input + .lines() + .map(parse_input) + .filter_map(AssignmentPair::fully_contained) + .count() as i64 +} + +fn solve2(input: &str) -> i64 { + input + .lines() + .map(parse_input) + .filter_map(AssignmentPair::overlap) + .count() as i64 +} + +solve1!(2, 547); +solve2!(4, 843); diff --git a/src/year2022/mod.rs b/src/year2022/mod.rs new file mode 100644 index 0000000..72bc7e3 --- /dev/null +++ b/src/year2022/mod.rs @@ -0,0 +1,4 @@ +mod day01; +mod day02; +mod day03; +mod day04; \ No newline at end of file