Skip to content

Commit cac276c

Browse files
committed
feat: 2025 day 03
1 parent 5617b1e commit cac276c

11 files changed

Lines changed: 298 additions & 5 deletions

File tree

.github/badges/completion2025.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"schemaVersion": 1,
33
"label": "2025",
4-
"message": "04/50",
4+
"message": "06/50",
55
"color": "red",
66
"style": "for-the-badge"
77
}

2025/day03/Cargo.toml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[package]
2+
name = "day03_2025"
3+
version = "0.1.0"
4+
authors.workspace = true
5+
repository.workspace = true
6+
edition.workspace = true
7+
license.workspace = true
8+
rust-version.workspace = true
9+
readme.workspace = true
10+
11+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12+
13+
[lib]
14+
name = "day03_2025"
15+
path = "src/lib.rs"
16+
17+
[dependencies]
18+
aoc-solution = { path = "../../aoc-solution" }
19+
aoc-common = { path = "../../common" }
20+
anyhow = { workspace = true }
21+
winnow = { workspace = true }
22+
23+
[dev-dependencies]
24+
criterion = { workspace = true }
25+
26+
[[bench]]
27+
name = "benchmarks"
28+
harness = false
29+
30+
[lints]
31+
workspace = true

2025/day03/benches/benchmarks.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2023 Jedrzej Stuczynski
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use aoc_common::define_aoc_benchmark;
16+
use day03_2025::Day03;
17+
18+
define_aoc_benchmark!("inputs/2025/day03", Day03);

2025/day03/src/common.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright 2024 Jedrzej Stuczynski
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use anyhow::Context;
16+
use aoc_common::helpers::digits_to_number;
17+
use std::str::FromStr;
18+
19+
#[derive(Debug, Clone)]
20+
pub struct BatteryBank {
21+
pub batteries: Vec<usize>,
22+
}
23+
24+
impl BatteryBank {
25+
fn maximum_joltage(&self, num_digits: usize) -> usize {
26+
let mut digits = Vec::new();
27+
let mut remaining_to_choose = num_digits;
28+
29+
let len = self.batteries.len();
30+
if len < num_digits {
31+
return 0;
32+
}
33+
if len == num_digits {
34+
return digits_to_number(&self.batteries);
35+
}
36+
37+
let mut window_start = 0;
38+
for _ in 0..num_digits {
39+
let mut chosen = 0;
40+
41+
// our unused window must always be sufficiently big for the remaining digits
42+
let selection_window = &self.batteries[window_start..len - remaining_to_choose + 1];
43+
44+
// check if we simply run out of choices
45+
if self.batteries[window_start..].len() == remaining_to_choose {
46+
digits.extend_from_slice(&self.batteries[window_start..]);
47+
break;
48+
}
49+
50+
let mut found_at_index = 0;
51+
for (i, value) in selection_window.iter().enumerate() {
52+
if *value > chosen {
53+
chosen = *value;
54+
found_at_index = i;
55+
}
56+
if *value == 9 {
57+
break;
58+
}
59+
}
60+
window_start += found_at_index + 1;
61+
remaining_to_choose -= 1;
62+
63+
digits.push(chosen);
64+
}
65+
66+
digits_to_number(&digits)
67+
}
68+
69+
pub fn maximum_joltage_with_two(&self) -> usize {
70+
self.maximum_joltage(2)
71+
}
72+
73+
pub fn maximum_joltage_with_twelve(&self) -> usize {
74+
self.maximum_joltage(12)
75+
}
76+
}
77+
78+
impl FromStr for BatteryBank {
79+
type Err = anyhow::Error;
80+
81+
fn from_str(s: &str) -> Result<Self, Self::Err> {
82+
let batteries = s
83+
.chars()
84+
.map(|c| {
85+
c.to_digit(10)
86+
.map(|d| d as usize)
87+
.context("invalid battery value")
88+
})
89+
.collect::<Result<Vec<_>, _>>()?;
90+
Ok(BatteryBank { batteries })
91+
}
92+
}
93+
94+
#[cfg(test)]
95+
mod tests {
96+
use super::*;
97+
98+
#[test]
99+
fn maximum_joltage_with_two() {
100+
let bank = BatteryBank::from_str("987654321111111").unwrap();
101+
assert_eq!(bank.maximum_joltage_with_two(), 98);
102+
103+
let bank = BatteryBank::from_str("811111111111119").unwrap();
104+
assert_eq!(bank.maximum_joltage_with_two(), 89);
105+
106+
let bank = BatteryBank::from_str("234234234234278").unwrap();
107+
assert_eq!(bank.maximum_joltage_with_two(), 78);
108+
109+
let bank = BatteryBank::from_str("818181911112111").unwrap();
110+
assert_eq!(bank.maximum_joltage_with_two(), 92);
111+
}
112+
113+
#[test]
114+
fn maximum_joltage_with_twelve() {
115+
let bank = BatteryBank::from_str("987654321111111").unwrap();
116+
assert_eq!(bank.maximum_joltage_with_twelve(), 987654321111);
117+
118+
let bank = BatteryBank::from_str("811111111111119").unwrap();
119+
assert_eq!(bank.maximum_joltage_with_twelve(), 811111111119);
120+
121+
let bank = BatteryBank::from_str("234234234234278").unwrap();
122+
assert_eq!(bank.maximum_joltage_with_twelve(), 434234234278);
123+
124+
let bank = BatteryBank::from_str("818181911112111").unwrap();
125+
assert_eq!(bank.maximum_joltage_with_twelve(), 888911112111);
126+
}
127+
}

2025/day03/src/lib.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2024 Jedrzej Stuczynski
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use crate::common::BatteryBank;
16+
use aoc_common::parsing::LineParser;
17+
use aoc_solution::Aoc;
18+
19+
mod common;
20+
21+
#[derive(Aoc)]
22+
#[aoc(input = Vec<BatteryBank>)]
23+
#[aoc(parser = LineParser)]
24+
#[aoc(part1(output = usize, runner = part1))]
25+
#[aoc(part2(output = usize, runner = part2))]
26+
pub struct Day03;
27+
28+
pub fn part1(input: Vec<BatteryBank>) -> usize {
29+
input
30+
.into_iter()
31+
.map(|b| b.maximum_joltage_with_two())
32+
.sum()
33+
}
34+
35+
pub fn part2(input: Vec<BatteryBank>) -> usize {
36+
input
37+
.into_iter()
38+
.map(|b| b.maximum_joltage_with_twelve())
39+
.sum()
40+
}
41+
42+
#[cfg(test)]
43+
mod tests {
44+
use super::*;
45+
use aoc_solution::parser::AocInputParser;
46+
47+
fn sample_input() -> Vec<BatteryBank> {
48+
LineParser::parse_input(
49+
r#"987654321111111
50+
811111111111119
51+
234234234234278
52+
818181911112111"#,
53+
)
54+
.unwrap()
55+
}
56+
57+
#[test]
58+
fn part1_sample_input() {
59+
let expected = 357;
60+
assert_eq!(expected, part1(sample_input()))
61+
}
62+
63+
#[test]
64+
fn part2_sample_input() {
65+
let expected = 3121910778619;
66+
assert_eq!(expected, part2(sample_input()))
67+
}
68+
}

2025/day03/src/main.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2024 Jedrzej Stuczynski
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use aoc_common::helpers::root_path;
16+
use aoc_solution::AocSolutionSolver;
17+
use day03_2025::Day03;
18+
19+
#[cfg(not(tarpaulin_include))]
20+
fn main() {
21+
Day03::try_solve_from_file(root_path("inputs/2025/day03"))
22+
}

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ members = [
9191
"2024/day10",
9292
"2024/day11",
9393
"2025/day01",
94-
"2025/day02"
94+
"2025/day02",
95+
"2025/day03"
9596
]
9697

9798
[workspace.package]

common/src/benchmark.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ macro_rules! define_aoc_benchmark {
1919

2020
use aoc_common::helpers::root_path;
2121
use aoc_common::input_read::read_input;
22-
use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
22+
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
2323
use std::fs;
2424

2525
fn get_input() -> <$typ as AocSolution>::Input {
@@ -42,7 +42,7 @@ macro_rules! define_aoc_benchmark {
4242
c.bench_function(&bench_name, move |b| {
4343
b.iter_batched(
4444
|| input.clone(),
45-
|input| <$typ as AocSolution>::part1((black_box(input))),
45+
|input| <$typ as AocSolution>::part1((std::hint::black_box(input))),
4646
BatchSize::SmallInput,
4747
)
4848
});
@@ -54,7 +54,7 @@ macro_rules! define_aoc_benchmark {
5454
c.bench_function(&bench_name, move |b| {
5555
b.iter_batched(
5656
|| input.clone(),
57-
|input| <$typ as AocSolution>::part2((black_box(input))),
57+
|input| <$typ as AocSolution>::part2((std::hint::black_box(input))),
5858
BatchSize::SmallInput,
5959
)
6060
});

common/src/constants.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,27 @@
1414

1515
pub const FILLED_PIXEL: char = '█';
1616
pub const EMPTY_PIXEL: char = '⠀';
17+
18+
pub const POWERS_OF_TEN: [usize; 21] = [
19+
0,
20+
1,
21+
10,
22+
100,
23+
1000,
24+
10000,
25+
100000,
26+
1000000,
27+
10000000,
28+
100000000,
29+
1000000000,
30+
10000000000,
31+
100000000000,
32+
1000000000000,
33+
10000000000000,
34+
100000000000000,
35+
1000000000000000,
36+
10000000000000000,
37+
100000000000000000,
38+
1000000000000000000,
39+
10000000000000000000,
40+
];

solution-runner/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,4 @@ day10_2024 = { path = "../2024/day10" }
111111
day11_2024 = { path = "../2024/day11" }
112112
day01_2025 = { path = "../2025/day01" }
113113
day02_2025 = { path = "../2025/day02" }
114+
day03_2025 = { path = "../2025/day03" }

0 commit comments

Comments
 (0)