Skip to content

Commit 421df70

Browse files
committed
Refactor to handle only 12 days of puzzles from 2025
See https://adventofcode.com/2025/about#faq_num_days
1 parent e30dd2c commit 421df70

File tree

16 files changed

+199
-95
lines changed

16 files changed

+199
-95
lines changed

crates/aoc/src/cli/arguments.rs

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::error::Error;
66
use std::num::NonZeroUsize;
77
use std::path::PathBuf;
88
use std::{fs, io};
9-
use utils::date::{Day, Year};
9+
use utils::date::{Date, Day, Year};
1010
use utils::multiversion::{VERSIONS, Version};
1111

1212
#[derive(Debug, Default)]
@@ -18,7 +18,7 @@ pub struct Arguments {
1818
pub inputs_dir: Option<PathBuf>,
1919
mode: Option<MainFn>,
2020
pub year: Option<Year>,
21-
pub day: Option<Day>,
21+
pub date: Option<Date>,
2222
pub extra_args: VecDeque<String>,
2323
}
2424

@@ -87,16 +87,20 @@ impl Arguments {
8787
}
8888

8989
if let Some(year) = args.pop_front() {
90-
result.year = match year.parse() {
91-
Ok(y) => Some(y),
92-
Err(err) => return Err(UsageError::InvalidArguments(err.into())),
93-
};
90+
let year = year
91+
.parse::<Year>()
92+
.map_err(|err| UsageError::InvalidArguments(err.into()))?;
93+
result.year = Some(year);
9494

9595
if let Some(day) = args.pop_front() {
96-
result.day = match day.parse() {
97-
Ok(y) => Some(y),
98-
Err(err) => return Err(UsageError::InvalidArguments(err.into())),
99-
};
96+
let day = day
97+
.parse::<Day>()
98+
.map_err(|err| UsageError::InvalidArguments(err.into()))?;
99+
100+
result.date = Some(
101+
Date::try_from((year, day))
102+
.map_err(|err| UsageError::InvalidArguments(err.into()))?,
103+
);
100104

101105
if !args.is_empty() {
102106
return Err(UsageError::TooManyArguments);
@@ -221,12 +225,22 @@ Options:
221225
self.mode.unwrap_or(mode::default::main)
222226
}
223227

224-
pub fn matching_puzzles(&self) -> Vec<(Year, Day, PuzzleFn)> {
225-
PUZZLES
226-
.iter()
227-
.copied()
228-
.filter(|&(y, d, ..)| self.year.unwrap_or(y) == y && self.day.unwrap_or(d) == d)
229-
.collect()
228+
pub fn matching_puzzles(&self) -> Vec<(Date, PuzzleFn)> {
229+
if let Some(date) = self.date {
230+
PUZZLES
231+
.iter()
232+
.copied()
233+
.filter(|&(d, ..)| d == date)
234+
.collect()
235+
} else if let Some(year) = self.year {
236+
PUZZLES
237+
.iter()
238+
.copied()
239+
.filter(|&(d, ..)| d.year() == year)
240+
.collect()
241+
} else {
242+
PUZZLES.to_vec()
243+
}
230244
}
231245

232246
pub fn inputs_dir(&self) -> PathBuf {
@@ -235,10 +249,10 @@ Options:
235249
.unwrap_or_else(|| PathBuf::from("./inputs"))
236250
}
237251

238-
pub fn read_input(&self, year: Year, day: Day) -> Result<String, (String, io::Error)> {
252+
pub fn read_input(&self, date: Date) -> Result<String, (String, io::Error)> {
239253
let mut path = self.inputs_dir();
240-
path.push(format!("year{year:#}"));
241-
path.push(format!("day{day:#}.txt"));
254+
path.push(format!("year{:#}", date.year()));
255+
path.push(format!("day{:#}.txt", date.day()));
242256
fs::read_to_string(&path).map_err(|err| (path.to_string_lossy().to_string(), err))
243257
}
244258
}

crates/aoc/src/cli/error.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use std::error::Error;
22
use std::fmt::{Debug, Display, Formatter};
33
use std::process::ExitCode;
4-
use utils::date::{Day, Year};
4+
use utils::date::Date;
55

66
#[derive(Debug)]
77
pub enum UsageError {
88
InvalidArguments(Box<dyn Error>),
99
MissingArguments(Box<dyn Error>),
1010
TooManyArguments,
11-
UnsupportedPuzzle(Year, Day),
11+
UnsupportedPuzzle(Date),
1212
NoSupportedPuzzles,
1313
}
1414

@@ -18,8 +18,8 @@ impl Display for UsageError {
1818
UsageError::InvalidArguments(err) => write!(f, "invalid arguments: {err}"),
1919
UsageError::MissingArguments(err) => write!(f, "missing required arguments: {err}"),
2020
UsageError::TooManyArguments => write!(f, "too many arguments"),
21-
UsageError::UnsupportedPuzzle(year, day) => {
22-
write!(f, "unsupported puzzle: {year:#} day {day:#}")
21+
UsageError::UnsupportedPuzzle(d) => {
22+
write!(f, "unsupported puzzle: {:#} day {:#}", d.year(), d.day())
2323
}
2424
UsageError::NoSupportedPuzzles => write!(f, "no matching supported puzzles"),
2525
}

crates/aoc/src/cli/mode/default.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ pub fn main(args: &Arguments) -> Result<(), Box<dyn Error>> {
2121
"────────┼────────────────────────────┼────────────────────────────────────────┼───────────"
2222
);
2323
let mut total = Duration::default();
24-
for (year, day, f) in puzzles {
24+
for (date, f) in puzzles {
25+
let year = date.year();
26+
let day = date.day();
27+
2528
let input = args
26-
.read_input(year, day)
29+
.read_input(date)
2730
.map_err(|(path, err)| format!("{year:#} {day:#}: failed to read {path:?}: {err}"))?;
2831

2932
let start = Instant::now();

crates/aoc/src/cli/mode/stdin.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::io::{Read, stdin};
44

55
#[expect(clippy::print_stdout)]
66
pub fn main(args: &Arguments) -> Result<(), Box<dyn Error>> {
7-
let (Some(year), Some(day)) = (args.year, args.day) else {
7+
let Some(date) = args.date else {
88
return Err(UsageError::MissingArguments("year and day must be provided".into()).into());
99
};
1010

@@ -19,17 +19,18 @@ pub fn main(args: &Arguments) -> Result<(), Box<dyn Error>> {
1919

2020
let puzzles = args.matching_puzzles();
2121
if puzzles.is_empty() {
22-
return Err(UsageError::UnsupportedPuzzle(year, day).into());
22+
return Err(UsageError::UnsupportedPuzzle(date).into());
2323
}
2424
assert_eq!(puzzles.len(), 1);
25-
let (_, _, f) = puzzles[0];
25+
let (_, f) = puzzles[0];
2626

2727
let mut input = String::new();
2828
stdin()
2929
.read_to_string(&mut input)
3030
.map_err(|err| format!("failed to read input: {err}"))?;
3131

32-
let (part1, part2) = f(&input).map_err(|err| format!("{year:#} {day:#}: {err}"))?;
32+
let (part1, part2) =
33+
f(&input).map_err(|err| format!("{:#} {:#}: {err}", date.year(), date.day()))?;
3334
assert!(!part1.contains('\n'));
3435
assert!(!part2.contains('\n'));
3536

crates/aoc/src/cli/mode/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ mod test_case;
1515
mod thread;
1616

1717
pub fn main(args: &Arguments) -> Result<(), Box<dyn Error>> {
18-
if args.day.is_some() {
18+
if args.date.is_some() {
1919
return Err(UsageError::InvalidArguments(
2020
"specifying day is incompatible with --test".into(),
2121
)

crates/aoc/src/cli/mode/test/manager.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl Manager {
3737
test_cases: TestCases::new(input_dir, min_year, max_year)?,
3838
process_pool: ProcessPool::new(multithreading::get_thread_count())?,
3939
pending_updates: BTreeSet::new(),
40-
puzzles: PuzzleVec::new(min_year, max_year, |_, _| Puzzle::default()),
40+
puzzles: PuzzleVec::new(min_year, max_year),
4141
};
4242
let mut stdout = stdout().lock();
4343
let mut grid = OutputGrid::new(min_year, max_year, &mut stdout)?;

crates/aoc/src/cli/mode/test/output_grid.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ impl<W: Write> OutputGrid<W> {
2525
out,
2626
cursor_row: 0,
2727
cursor_col: 0,
28-
statuses: PuzzleVec::new(min_year, max_year, |_, _| Status::default()),
28+
statuses: PuzzleVec::new(min_year, max_year),
2929
};
3030

3131
grid.print_grid()?;

crates/aoc/src/cli/mode/test/puzzle.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ impl Puzzle {
142142
pub fn iter(min_year: Year, max_year: Year) -> impl FusedIterator<Item = (Year, Day)> {
143143
(min_year.to_u16()..=max_year.to_u16())
144144
.map(|y| Year::new(y).unwrap())
145-
.flat_map(|y| (1..=25).map(|d| Day::new(d).unwrap()).map(move |d| (y, d)))
145+
.flat_map(|y| y.days().map(move |d| (y, d)))
146146
}
147147
}
148148

@@ -190,12 +190,16 @@ pub struct PuzzleVec<T> {
190190
}
191191

192192
impl<T> PuzzleVec<T> {
193-
pub fn new(min_year: Year, max_year: Year, init_fn: impl Fn(Year, Day) -> T) -> Self {
193+
pub fn new(min_year: Year, max_year: Year) -> Self
194+
where
195+
T: Default,
196+
{
194197
Self {
195198
min_year,
196199
max_year,
197-
vec: Puzzle::iter(min_year, max_year)
198-
.map(|(y, d)| init_fn(y, d))
200+
// Store 25 days even if the year is shorter to make indexing easier
201+
vec: std::iter::repeat_with(T::default)
202+
.take(25 * (max_year.to_u16() as usize - min_year.to_u16() as usize + 1))
199203
.collect(),
200204
}
201205
}
@@ -211,7 +215,8 @@ impl<T> PuzzleVec<T> {
211215
}
212216

213217
pub fn iter(&self) -> impl FusedIterator<Item = ((Year, Day), &T)> {
214-
self.puzzles().zip(self.vec.iter())
218+
self.puzzles()
219+
.map(|(year, day)| ((year, day), &self[(year, day)]))
215220
}
216221
}
217222

@@ -221,15 +226,15 @@ impl<T> Index<Year> for PuzzleVec<T> {
221226
#[inline]
222227
fn index(&self, year: Year) -> &Self::Output {
223228
let index = self.index(year, Day::new_const::<1>());
224-
&self.vec[index..index + 25]
229+
&self.vec[index..index + year.max_day().to_u8() as usize]
225230
}
226231
}
227232

228233
impl<T> IndexMut<Year> for PuzzleVec<T> {
229234
#[inline]
230235
fn index_mut(&mut self, year: Year) -> &mut Self::Output {
231236
let index = self.index(year, Day::new_const::<1>());
232-
&mut self.vec[index..index + 25]
237+
&mut self.vec[index..index + year.max_day().to_u8() as usize]
233238
}
234239
}
235240

crates/aoc/src/puzzles.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::all_puzzles;
2-
use utils::date::{Day, Year};
2+
use utils::date::Date;
33
use utils::input::strip_final_newline;
44

55
// These imports are unused if none of the year features are enabled
66
#[allow(clippy::allow_attributes, unused_imports)]
77
use utils::{
8-
Puzzle,
8+
PuzzleDate,
99
input::{InputError, InputType},
1010
};
1111

@@ -22,12 +22,12 @@ macro_rules! matcher {
2222
)*) => {
2323
/// Slice containing all supported puzzle solutions.
2424
///
25-
/// Each puzzle is represented by a tuple of [`Year`], [`Day`] and [`PuzzleFn`], which takes
26-
/// a input string and returns the part 1 and 2 solutions as strings, or an [`InputError`].
25+
/// Each puzzle is represented by a tuple of [`Date`] and a [`PuzzleFn`], which takes
26+
/// an input string and returns the part 1 and 2 solutions as strings, or an [`InputError`].
2727
///
2828
/// Generated from [`all_puzzles!`].
29-
pub static PUZZLES: &[(Year, Day, PuzzleFn)] = &[$($(
30-
(crate::$year::$day::YEAR, crate::$year::$day::DAY, |input: &str| {
29+
pub static PUZZLES: &[(Date, PuzzleFn)] = &[$($(
30+
(crate::$year::$day::DATE, |input: &str| {
3131
let input = strip_final_newline(input);
3232
let solution = crate::$year::$day::new(input, InputType::Real)?;
3333
let part1 = solution.part1();

crates/aoc/src/years.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,19 @@ pub use ::year2024;
8080
///
8181
/// ```
8282
/// # use aoc::all_puzzles;
83-
/// # use utils::date::{Day, Year};
84-
/// # use utils::{PuzzleExamples, Puzzle};
83+
/// # use utils::date::Date;
84+
/// # use utils::{PuzzleExamples, PuzzleDate};
8585
/// #
86-
/// fn example_count(year: Year, day: Day) -> Option<usize> {
86+
/// fn example_count(date: Date) -> Option<usize> {
8787
/// macro_rules! callback {
8888
/// ($(
8989
/// $y:literal => $year:ident{$(
9090
/// $d:literal => $day:ident,
9191
/// )*}
9292
/// )*) => {
93-
/// match (year, day) {
93+
/// match date {
9494
/// $($(
95-
/// (aoc::$year::$day::YEAR, aoc::$year::$day::DAY) => Some(aoc::$year::$day::EXAMPLES.len()),
95+
/// aoc::$year::$day::DATE => Some(aoc::$year::$day::EXAMPLES.len()),
9696
/// )*)*
9797
/// _ => None,
9898
/// }

0 commit comments

Comments
 (0)