From 81f7933cc0e6f1db78142d6b99c1d29af2a8fbd7 Mon Sep 17 00:00:00 2001
From: Joe Ardent <code@ardent.nebcorp.com>
Date: Sun, 5 Jan 2025 20:20:20 -0800
Subject: [PATCH] day10, part1

---
 day10/src/main.rs | 163 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 161 insertions(+), 2 deletions(-)

diff --git a/day10/src/main.rs b/day10/src/main.rs
index f9a374b..fdfeb9f 100644
--- a/day10/src/main.rs
+++ b/day10/src/main.rs
@@ -1,13 +1,172 @@
+use std::collections::HashSet;
+
 fn main() {
-    println!("Hello, world!");
+    let input = std::fs::read_to_string("input").unwrap();
+    let grid = Grid::new(&input);
+    println!("{}", pt1(&grid));
 }
 
+fn pt1(grid: &Grid) -> usize {
+    dfs(grid)
+}
+
+fn dfs(grid: &Grid) -> usize {
+    let mut total = 0;
+
+    for head in grid.heads.iter() {
+        let mut processed = HashSet::new();
+        let mut q = Vec::new();
+
+        q.push(*head);
+        while let Some(current) = q.pop() {
+            if !processed.contains(&current) {
+                q.push(current);
+            }
+
+            let mut do_top = true;
+            let nexts = grid.next_step(&current);
+            for next in nexts.iter() {
+                if !processed.contains(next) {
+                    q.push(*next);
+                    do_top = false;
+                }
+            }
+
+            if do_top {
+                let _ = q.pop();
+                processed.insert(current);
+                if grid.get(&current).unwrap() == 9 {
+                    total += 1;
+                }
+            }
+        }
+    }
+
+    total
+}
+
+#[derive(Debug, Clone)]
 struct Grid {
     rows: Vec<Vec<usize>>,
+    heads: Vec<Loc>,
 }
 
 impl Grid {
     fn new(input: &str) -> Self {
-        todo!()
+        let mut rows = Vec::new();
+        let mut heads = Vec::new();
+        for (row, line) in input.lines().enumerate() {
+            let row = line
+                .chars()
+                .enumerate()
+                .map(|(col, c)| {
+                    let n = c.to_digit(10).unwrap() as usize;
+                    if n == 0 {
+                        heads.push(Loc { row, col });
+                    }
+                    n
+                })
+                .collect();
+            rows.push(row);
+        }
+        Self { rows, heads }
+    }
+
+    fn get(&self, loc: &Loc) -> Option<usize> {
+        self.rows
+            .get(loc.row)
+            .and_then(|row| row.get(loc.col).copied())
+    }
+
+    fn next(&self, loc: &Loc) -> Vec<Tile> {
+        let Loc { row, col } = *loc;
+        let mut out = Vec::new();
+
+        // north
+        {
+            let row = row.wrapping_sub(1);
+            if let Some(&alt) = self.rows.get(row).and_then(|r| r.get(col)) {
+                out.push(Tile {
+                    alt,
+                    loc: Loc { row, col },
+                });
+            }
+        }
+
+        // south
+        {
+            let row = row + 1;
+            if let Some(&alt) = self.rows.get(row).and_then(|r| r.get(col)) {
+                out.push(Tile {
+                    alt,
+                    loc: Loc { row, col },
+                });
+            }
+        }
+
+        // east
+        {
+            let col = col + 1;
+            if let Some(&alt) = self.rows.get(row).and_then(|r| r.get(col)) {
+                out.push(Tile {
+                    alt,
+                    loc: Loc { row, col },
+                });
+            }
+        }
+
+        // west
+        {
+            let col = col.wrapping_sub(1);
+            if let Some(&alt) = self.rows.get(row).and_then(|r| r.get(col)) {
+                out.push(Tile {
+                    alt,
+                    loc: Loc { row, col },
+                });
+            }
+        }
+        out
+    }
+
+    fn next_step(&self, loc: &Loc) -> Vec<Loc> {
+        let alt = self.rows[loc.row][loc.col];
+        let target = alt + 1;
+        self.next(loc)
+            .iter()
+            .filter_map(|t| if t.alt == target { Some(t.loc) } else { None })
+            .collect()
+    }
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
+struct Loc {
+    row: usize,
+    col: usize,
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
+struct Tile {
+    loc: Loc,
+    alt: usize,
+}
+
+#[cfg(test)]
+mod test {
+
+    use super::*;
+
+    static INPUT: &str = "89010123
+78121874
+87430965
+96549874
+45678903
+32019012
+01329801
+10456732";
+
+    #[test]
+    fn p1() {
+        let g = Grid::new(INPUT);
+        assert_eq!(36, pt1(&g));
     }
 }