diff --git a/.aoc_tiles/tiles/2024/24.png b/.aoc_tiles/tiles/2024/24.png
index 89f755a..b966019 100644
Binary files a/.aoc_tiles/tiles/2024/24.png and b/.aoc_tiles/tiles/2024/24.png differ
diff --git a/.aoc_tiles/tiles/2024/25.png b/.aoc_tiles/tiles/2024/25.png
index e1ab8d3..a1daeb8 100644
Binary files a/.aoc_tiles/tiles/2024/25.png and b/.aoc_tiles/tiles/2024/25.png differ
diff --git a/README.md b/README.md
index bd4c125..dfc7c92 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
- 2024 - 46 ⭐ - Rust
+ 2024 - 48 ⭐ - Rust
@@ -74,4 +74,7 @@
+
+
+
diff --git a/src/day25.rs b/src/day25.rs
new file mode 100644
index 0000000..2b8d7b4
--- /dev/null
+++ b/src/day25.rs
@@ -0,0 +1,143 @@
+use aoc_runner_derive::aoc;
+use itertools::Itertools;
+
+enum LockKey {
+ Lock,
+ Key,
+}
+
+#[derive(Clone, Debug)]
+struct LockPile {
+ keys: Vec>,
+ locks: Vec>,
+}
+
+fn parse_grid(lines: &Vec<&str>) -> (LockKey, Vec) {
+ assert_eq!(lines.len(), 7);
+ if lines[0].chars().all(|c| c == '#') {
+ // lock
+ let mut pins = vec![0; 5];
+ for row in 1..lines.len() {
+ let row_s = lines[row];
+ for i in 0..row_s.len() {
+ if row_s.chars().nth(i) == Some('#') {
+ pins[i] = row
+ }
+ }
+ }
+ (LockKey::Lock, pins)
+ } else if lines[6].chars().all(|c| c == '#') {
+ // key
+ let mut pins = vec![5; 5];
+ for row in (1..lines.len()).rev() {
+ let row_s = lines[row];
+ for i in 0..row_s.len() {
+ if row_s.chars().nth(i) == Some('#') {
+ pins[i] = 6 - row
+ }
+ }
+ }
+ (LockKey::Key, pins)
+ } else {
+ panic!("not a lock or a key: {:?}", lines);
+ }
+}
+
+fn parse(input: &str) -> LockPile {
+ let mut locks = Vec::new();
+ let mut keys = Vec::new();
+ let mut accum: Vec<&str> = Vec::new();
+ for line in input.lines() {
+ if line == "" {
+ let (lk, pins) = parse_grid(&accum);
+ match lk {
+ LockKey::Lock => locks.push(pins),
+ LockKey::Key => keys.push(pins),
+ }
+ accum.clear();
+ } else {
+ accum.push(line);
+ }
+ }
+ if accum.len() != 0 {
+ let (lk, pins) = parse_grid(&accum);
+ match lk {
+ LockKey::Lock => locks.push(pins),
+ LockKey::Key => keys.push(pins),
+ }
+ }
+ LockPile { keys, locks }
+}
+
+fn test_lock_key(lock: &Vec, key: &Vec) -> bool {
+ !lock.iter().zip(key.iter()).any(|(lp, kp)| lp + kp > 5)
+}
+
+#[aoc(day25, part1)]
+pub fn part1(input: &str) -> i64 {
+ let lockpile = parse(input);
+
+ lockpile
+ .locks
+ .iter()
+ .cartesian_product(lockpile.keys.iter())
+ .filter(|(l, k)| test_lock_key(l, k))
+ .count() as i64
+}
+
+#[aoc(day25, part2)]
+pub fn part2(_input: &str) -> String {
+ "run the other solutions for day 25 part 2!".to_string()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ const EXAMPLE: &str = "#####
+.####
+.####
+.####
+.#.#.
+.#...
+.....
+
+#####
+##.##
+.#.##
+...##
+...#.
+...#.
+.....
+
+.....
+#....
+#....
+#...#
+#.#.#
+#.###
+#####
+
+.....
+.....
+#.#..
+###..
+###.#
+###.#
+#####
+
+.....
+.....
+.....
+#....
+#.#..
+#.#.#
+#####";
+
+ #[test]
+ fn part1_example() {
+ assert_eq!(part1(EXAMPLE), 3);
+ }
+
+ #[test]
+ fn part2_example() {}
+}
diff --git a/src/lib.rs b/src/lib.rs
index 6cfecaf..ee2182e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -15,6 +15,7 @@ pub mod day21;
pub mod day22;
pub mod day23;
pub mod day24;
+pub mod day25;
pub mod day3;
pub mod day4;
pub mod day5;