Skip to content

Commit 902825a

Browse files
committed
feat: introduce shared aoc-rust-common framework and workspace
This commit sets up a root Cargo workspace and a shared framework for Rust solutions, including standardized Solution traits, input loading, and execution timing. Refactored 2015 Day 1 and 2 as a proof of concept.
1 parent 4751c6e commit 902825a

12 files changed

Lines changed: 1478 additions & 115 deletions

File tree

2015/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2021"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9+
aoc-rust-common = { path = "../aoc-rust-common" }
910
iter_tools = "0.1.4"
1011
lazy_static = "1.4.0"
1112
md5 = "0.7.0"

2015/src/day01.rs

Lines changed: 27 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,54 @@
1-
use crate::{Exercise, Solvable};
1+
use aoc_rust_common::Solution;
2+
use std::fmt::Display;
23

3-
struct FirstDay {
4-
exercise: Exercise,
5-
}
4+
pub struct Day01;
65

7-
impl Solvable for FirstDay {
8-
fn solve_first(&self, is_prod: bool) -> i64 {
9-
if is_prod {
10-
self.first(&self.exercise.content)
11-
} else {
12-
self.first(&self.exercise.example)
13-
}
14-
}
6+
impl Solution for Day01 {
7+
fn year(&self) -> u32 { 2015 }
8+
fn day(&self) -> u32 { 1 }
159

16-
fn solve_second(&self, is_prod: bool) -> i64 {
17-
if is_prod {
18-
self.second(&self.exercise.content)
19-
} else {
20-
self.second(&self.exercise.example)
21-
}
22-
}
23-
24-
fn first(&self, content: &str) -> i64 {
25-
content.chars().fold(0, |acc, c| match c {
10+
fn part1(&self, input: &str) -> Box<dyn Display> {
11+
Box::new(input.chars().fold(0, |acc, c| match c {
2612
'(' => acc + 1,
2713
')' => acc - 1,
2814
_ => acc,
29-
})
15+
}))
3016
}
3117

32-
fn second(&self, content: &str) -> i64 {
18+
fn part2(&self, input: &str) -> Box<dyn Display> {
3319
let mut floor = 0;
34-
for (pos, c) in content.chars().enumerate() {
20+
for (pos, c) in input.chars().enumerate() {
3521
floor += match c {
3622
'(' => 1,
3723
')' => -1,
3824
_ => 0,
3925
};
4026
if floor == -1 {
41-
return (pos + 1) as i64;
27+
return Box::new((pos + 1) as i64);
4228
}
4329
}
44-
(content.len() + 1) as i64
30+
Box::new((input.len() + 1) as i64)
4531
}
4632
}
4733

4834
#[cfg(test)]
4935
mod tests {
5036
use super::*;
51-
const EXAMPLE_1: &str = include_str!("inputs/1_test.txt");
52-
const PROD_1: &str = include_str!("inputs/1_prod.txt");
5337

5438
#[test]
55-
fn first_test() {
56-
let first_exercise = FirstDay {
57-
exercise: Exercise {
58-
content: String::from(PROD_1),
59-
example: String::from(EXAMPLE_1),
60-
},
61-
};
62-
63-
let expected_example = 3;
64-
let expected_prod = 232;
65-
66-
let result_example = first_exercise.solve_first(false);
67-
let result_prod = first_exercise.solve_first(true);
68-
assert_eq!(expected_example, result_example);
69-
assert_eq!(expected_prod, result_prod);
70-
71-
let expected_example = 1;
72-
let expected_prod = 1783;
73-
let result_example = first_exercise.solve_second(false);
74-
let result_prod = first_exercise.solve_second(true);
75-
assert_eq!(expected_example, result_example);
76-
assert_eq!(expected_prod, result_prod);
39+
fn test_day01() {
40+
let day = Day01;
41+
assert_eq!(day.part1("(())").to_string(), "0");
42+
assert_eq!(day.part1("()()").to_string(), "0");
43+
assert_eq!(day.part1("(((").to_string(), "3");
44+
assert_eq!(day.part1("(()(()(").to_string(), "3");
45+
assert_eq!(day.part1("))(((((").to_string(), "3");
46+
assert_eq!(day.part1("())").to_string(), "-1");
47+
assert_eq!(day.part1("))(").to_string(), "-1");
48+
assert_eq!(day.part1(")))").to_string(), "-3");
49+
assert_eq!(day.part1(")())())").to_string(), "-3");
50+
51+
assert_eq!(day.part2(")").to_string(), "1");
52+
assert_eq!(day.part2("()())").to_string(), "5");
7753
}
7854
}

2015/src/day02.rs

Lines changed: 27 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,62 @@
1-
use super::{Exercise, Solvable};
1+
use aoc_rust_common::Solution;
2+
use std::fmt::Display;
23
use rayon::prelude::*;
34

4-
pub struct SecondDay {
5-
exercise: Exercise,
6-
}
5+
pub struct Day02;
76

8-
/// Calculate wrapping paper and ribbon needed for a present
9-
/// Returns (wrapping_paper_area, ribbon_length)
107
fn calculate_wrapper_needed_materials(line: &str) -> (i64, i64) {
118
let dimensions = line
12-
.split("x")
9+
.split('x')
1310
.map(|x| x.parse::<i64>().unwrap())
1411
.collect::<Vec<i64>>();
1512
let l = dimensions[0];
1613
let w = dimensions[1];
1714
let h = dimensions[2];
18-
let mut smallest = l * w;
19-
let mut smallest_perimeter = l + w;
15+
16+
let areas = [l * w, w * h, h * l];
17+
let smallest = areas.iter().min().unwrap();
18+
19+
let perimeters = [2 * (l + w), 2 * (w + h), 2 * (h + l)];
20+
let smallest_perimeter = perimeters.iter().min().unwrap();
2021

21-
if w * h < smallest {
22-
smallest = w * h;
23-
smallest_perimeter = w + h;
24-
}
25-
if h * l < smallest {
26-
smallest = h * l;
27-
smallest_perimeter = h + l;
28-
}
2922
(
3023
2 * l * w + 2 * w * h + 2 * h * l + smallest,
31-
smallest_perimeter * 2 + l * w * h,
24+
smallest_perimeter + l * w * h,
3225
)
3326
}
3427

35-
impl Solvable for SecondDay {
36-
fn solve_first(&self, is_prod: bool) -> i64 {
37-
if is_prod {
38-
self.first(&self.exercise.content)
39-
} else {
40-
self.first(&self.exercise.example)
41-
}
42-
}
28+
impl Solution for Day02 {
29+
fn year(&self) -> u32 { 2015 }
30+
fn day(&self) -> u32 { 2 }
4331

44-
fn solve_second(&self, is_prod: bool) -> i64 {
45-
if is_prod {
46-
self.second(&self.exercise.content)
47-
} else {
48-
self.second(&self.exercise.example)
49-
}
50-
}
51-
52-
fn first(&self, content: &str) -> i64 {
53-
content
32+
fn part1(&self, input: &str) -> Box<dyn Display> {
33+
Box::new(input
5434
.par_lines()
5535
.map(calculate_wrapper_needed_materials)
5636
.map(|(wrapping, _)| wrapping)
57-
.sum()
37+
.sum::<i64>())
5838
}
5939

60-
fn second(&self, content: &str) -> i64 {
61-
content
40+
fn part2(&self, input: &str) -> Box<dyn Display> {
41+
Box::new(input
6242
.par_lines()
6343
.map(calculate_wrapper_needed_materials)
6444
.map(|(_, ribbon)| ribbon)
65-
.sum()
45+
.sum::<i64>())
6646
}
6747
}
6848

6949
#[cfg(test)]
7050
mod tests {
7151
use super::*;
72-
const EXAMPLE_1: &str = include_str!("inputs/2_test.txt");
73-
const PROD_1: &str = include_str!("inputs/2_prod.txt");
7452

7553
#[test]
76-
fn first_test() {
77-
let first_exercise = SecondDay {
78-
exercise: Exercise {
79-
content: String::from(PROD_1),
80-
example: String::from(EXAMPLE_1),
81-
},
82-
};
83-
84-
let expected_example = 58;
85-
let expected_prod = 1588178;
86-
87-
let result_example = first_exercise.solve_first(false);
88-
let result_prod = first_exercise.solve_first(true);
89-
assert_eq!(expected_example, result_example);
90-
assert_eq!(expected_prod, result_prod);
91-
92-
let expected_example = 34;
93-
let expected_prod = 3783758;
94-
let result_example = first_exercise.solve_second(false);
95-
let result_prod = first_exercise.solve_second(true);
96-
assert_eq!(expected_example, result_example);
97-
assert_eq!(expected_prod, result_prod);
54+
fn test_day02() {
55+
let day = Day02;
56+
assert_eq!(day.part1("2x3x4").to_string(), "58");
57+
assert_eq!(day.part1("1x1x10").to_string(), "43");
58+
59+
assert_eq!(day.part2("2x3x4").to_string(), "34");
60+
assert_eq!(day.part2("1x1x10").to_string(), "14");
9861
}
9962
}

2015/src/main.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
mod day01;
2+
mod day02;
3+
4+
use aoc_rust_common::run_solution;
5+
use day01::Day01;
6+
use day02::Day02;
7+
8+
fn main() {
9+
run_solution(Day01);
10+
run_solution(Day02);
11+
}

0 commit comments

Comments
 (0)