aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Kingsley <josh@joshkingsley.me>2025-12-04 00:03:03 +0200
committerJosh Kingsley <josh@joshkingsley.me>2025-12-04 00:04:51 +0200
commita488f089c49bb2ce1f43ce966e5f2921c0f6e72c (patch)
treeffe01859552c413ee0b7549cc9af304bdb84d0ad
parent1496f52e32378d140a3b4680764d98caf512fbf1 (diff)
Day 2, part 1 solve
-rw-r--r--Cargo.lock4
-rw-r--r--Cargo.toml2
-rw-r--r--day2/Cargo.toml6
-rw-r--r--day2/src/main.rs135
4 files changed, 146 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 168a14b..2e0bc8d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -14,3 +14,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
]
+
+[[package]]
+name = "day2"
+version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
index b512d30..d8418cc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,3 +1,3 @@
[workspace]
resolver = "3"
-members = ["day1"]
+members = ["day1", "day2"]
diff --git a/day2/Cargo.toml b/day2/Cargo.toml
new file mode 100644
index 0000000..6c22320
--- /dev/null
+++ b/day2/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "day2"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
diff --git a/day2/src/main.rs b/day2/src/main.rs
new file mode 100644
index 0000000..5bb661f
--- /dev/null
+++ b/day2/src/main.rs
@@ -0,0 +1,135 @@
+use std::{env, fs, num::ParseIntError, ops::Range, str::FromStr};
+
+#[derive(Debug, Clone, Copy)]
+struct IDRange(u64, u64);
+
+#[derive(Debug)]
+struct ParseIDRangeError;
+
+impl From<ParseIntError> for ParseIDRangeError {
+ fn from(_: ParseIntError) -> Self {
+ Self
+ }
+}
+
+impl FromStr for IDRange {
+ type Err = ParseIDRangeError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let (start, end) = s.split_once('-').ok_or(ParseIDRangeError)?;
+ Ok(Self(start.parse()?, end.parse()?))
+ }
+}
+
+struct IDRangeIntoIter {
+ range: IDRange,
+ cur: u64,
+}
+
+impl Iterator for IDRangeIntoIter {
+ type Item = ID;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.cur <= self.range.1 {
+ let id = ID(self.cur);
+ self.cur += 1;
+ Some(id)
+ } else {
+ None
+ }
+ }
+}
+
+impl IntoIterator for IDRange {
+ type Item = ID;
+
+ type IntoIter = IDRangeIntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ Self::IntoIter {
+ cur: self.0,
+ range: self,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+struct ID(u64);
+
+impl ID {
+ fn is_invalid(&self) -> bool {
+ let s = self.0.to_string();
+ s.len() % 2 == 0 && {
+ let (a, b) = s.split_at(s.len() / 2);
+ a == b
+ }
+ }
+}
+
+#[derive(Debug)]
+enum Error {
+ BadArgument,
+ BadRange(String),
+}
+
+impl From<std::io::Error> for Error {
+ fn from(_: std::io::Error) -> Self {
+ Self::BadArgument
+ }
+}
+
+fn main() -> Result<(), Error> {
+ let x: Range<u32> = 0..99;
+ let mut args = env::args();
+ let input_path = args.nth(1).ok_or_else(|| Error::BadArgument)?;
+ let input = fs::read_to_string(input_path)?;
+
+ let ranges: Vec<IDRange> = input
+ .split_whitespace()
+ .flat_map(|s| s.split(','))
+ .filter(|s| s.len() != 0)
+ .map(|s| s.parse().map_err(|_| Error::BadRange(s.into())))
+ .collect::<Result<_, _>>()?;
+
+ let result: u64 = ranges
+ .iter()
+ .flat_map(|range| range.into_iter().filter(ID::is_invalid).map(|id| id.0))
+ .sum();
+
+ println!("Result: {}", result);
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn parse_id_range() -> Result<(), ParseIDRangeError> {
+ let range: IDRange = "99-100".parse()?;
+ assert_eq!(range.0, 99);
+ assert_eq!(range.1, 100);
+ Ok(())
+ }
+
+ #[test]
+ fn id_range_into_iter() {
+ let range = IDRange(2, 4);
+ let ids: Vec<ID> = range.into_iter().collect();
+ assert_eq!(ids, vec![ID(2), ID(3), ID(4)]);
+ }
+
+ #[test]
+ fn id_is_valid() {
+ assert!(!ID(0).is_invalid());
+ assert!(!ID(1).is_invalid());
+ assert!(!ID(2).is_invalid());
+ assert!(!ID(10).is_invalid());
+ assert!(!ID(12).is_invalid());
+ assert!(!ID(21).is_invalid());
+
+ assert!(ID(11).is_invalid());
+ assert!(ID(5555).is_invalid());
+ }
+}