]> localhost Git - adventofcode2024.git/commitdiff
Complete day 4
authorJack Kinsey <j.jameskinsey@gmail.com>
Thu, 5 Dec 2024 02:29:39 +0000 (21:29 -0500)
committerJack Kinsey <j.jameskinsey@gmail.com>
Thu, 5 Dec 2024 02:29:39 +0000 (21:29 -0500)
src/day04.rs [new file with mode: 0644]
src/main.rs

diff --git a/src/day04.rs b/src/day04.rs
new file mode 100644 (file)
index 0000000..96e8f7b
--- /dev/null
@@ -0,0 +1,260 @@
+fn input() -> &'static str {
+    include_str!("../input/day04.txt")
+}
+
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+enum Char {
+    X,
+    M,
+    A,
+    S,
+}
+
+fn parse(input: &str) -> Vec<Vec<Char>> {
+    input
+        .split_terminator('\n')
+        .map(|v| {
+            v.split("")
+                .filter(|s| !s.is_empty())
+                .map(|c| match c {
+                    "X" => Char::X,
+                    "M" => Char::M,
+                    "A" => Char::A,
+                    "S" => Char::S,
+                    _ => panic!(),
+                })
+                .collect()
+        })
+        .collect()
+}
+
+fn find_chars(chars: &[Vec<Char>], char: Char) -> Vec<(i32, i32)> {
+    chars
+        .iter()
+        .enumerate()
+        .flat_map(|(i, v)| {
+            v.iter()
+                .enumerate()
+                .filter(|(_, &c)| c == char)
+                .map(move |(j, _)| (i, j))
+                .map(|(i, j)| (i.try_into().unwrap(), j.try_into().unwrap()))
+        })
+        .collect()
+}
+
+#[rustfmt::skip]
+const DIRS: [(i32, i32); 8] = [
+    (-1, -1), (-1, 0), (-1, 1),
+     (0, -1), /* ctr */ (0, 1),
+     (1, -1),  (1, 0),  (1, 1),
+];
+
+fn make_dirs((i, j): (i32, i32), n: i32) -> Vec<[(usize, usize); 4]> {
+    DIRS.iter()
+        .map(|dir| {
+            [dir]
+                .iter()
+                .cycle()
+                .take(4)
+                .enumerate()
+                .map(|(k, dij)| (std::convert::TryInto::<i32>::try_into(k).unwrap(), dij))
+                .map(|(k, (di, dj))| (i + k * di, j + k * dj))
+                .collect::<Vec<_>>()
+                .try_into()
+                .unwrap()
+        })
+        .filter(|ds: &[(i32, i32); 4]| {
+            ds.iter()
+                .all(|(ii, jj)| *ii >= 0 && *jj >= 0 && *ii < n && *jj < n)
+        })
+        .map(|ds: [(i32, i32); 4]| {
+            ds.iter()
+                .map(|(ii, jj)| {
+                    (
+                        TryInto::<usize>::try_into(*ii).unwrap(),
+                        TryInto::<usize>::try_into(*jj).unwrap(),
+                    )
+                })
+                .collect::<Vec<_>>()
+                .try_into()
+                .unwrap()
+        })
+        .collect()
+}
+
+#[rustfmt::skip]
+const XDIRS: [(i32, i32); 4] = [
+    (-1, -1),          (-1, 1),
+              /* ctr */        
+     (1, -1),           (1, 1),
+];
+
+fn make_xdirs((i, j): (i32, i32), n: i32) -> Option<[(usize, usize); 4]> {
+    if i == 0 || i == n - 1 || j == 0 || j == n - 1 {
+        None
+    } else {
+        Some(
+            XDIRS
+                .iter()
+                .map(|&(di, dj)| (i + di, j + dj))
+                .map(|(ii, jj)| {
+                    (
+                        TryInto::<usize>::try_into(ii).unwrap(),
+                        TryInto::<usize>::try_into(jj).unwrap(),
+                    )
+                })
+                .collect::<Vec<_>>()
+                .try_into()
+                .unwrap(),
+        )
+    }
+}
+
+fn check_x_for_xmas(chars: &[Vec<Char>], coords: (i32, i32)) -> usize {
+    make_dirs(coords, chars.len().try_into().unwrap())
+        .iter()
+        .map(|ijs| {
+            matches!(
+                ijs.iter().map(|(i, j)| chars[*i][*j]).collect::<Vec<_>>()[..],
+                [Char::X, Char::M, Char::A, Char::S]
+            )
+        })
+        .filter(|&x| x)
+        .count()
+}
+
+fn check_xs_for_xmas(chars: &[Vec<Char>]) -> usize {
+    find_chars(chars, Char::X)
+        .iter()
+        .map(|&coords| check_x_for_xmas(chars, coords))
+        .sum()
+}
+
+const XPATTERNS: [[Char; 4]; 4] = [
+    [Char::M, Char::M, Char::S, Char::S],
+    [Char::M, Char::S, Char::M, Char::S],
+    [Char::S, Char::S, Char::M, Char::M],
+    [Char::S, Char::M, Char::S, Char::M],
+];
+
+fn check_a_for_xmas(chars: &[Vec<Char>], coords: (i32, i32)) -> usize {
+    if let Some(x) = make_xdirs(coords, chars.len().try_into().unwrap()) {
+        let xc: [Char; 4] = x
+            .iter()
+            .map(|(i, j): &(usize, usize)| chars[*i][*j])
+            .collect::<Vec<_>>()
+            .try_into()
+            .unwrap();
+        XPATTERNS.iter().filter(|&&p| p == xc).count()
+    } else {
+        0
+    }
+}
+
+fn check_as_for_xmas(chars: &[Vec<Char>]) -> usize {
+    find_chars(chars, Char::A)
+        .iter()
+        .map(|&coords| check_a_for_xmas(chars, coords))
+        .sum()
+}
+
+pub fn part1() {
+    let n = check_xs_for_xmas(&parse(input()));
+    println!("Day 4 Part 1: {}", n);
+}
+
+pub fn part2() {
+    let n = check_as_for_xmas(&parse(input()));
+    println!("Day 4 Part 2: {}", n);
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    const INPUT_STR: &str = concat!(
+        "MMMSXXMASM\n",
+        "MSAMXMSMSA\n",
+        "AMXSXMAAMM\n",
+        "MSAMASMSMX\n",
+        "XMASAMXAMM\n",
+        "XXAMMXXAMA\n",
+        "SMSMSASXSS\n",
+        "SAXAMASAAA\n",
+        "MAMMMXMMMM\n",
+        "MXMXAXMASX\n",
+    );
+
+    #[rustfmt::skip]
+    #[test]
+    fn test_parse() {
+        assert_eq!(
+            parse(INPUT_STR),
+            [
+                [ Char::M, Char::M, Char::M, Char::S, Char::X, Char::X, Char::M, Char::A, Char::S, Char::M ],
+                [ Char::M, Char::S, Char::A, Char::M, Char::X, Char::M, Char::S, Char::M, Char::S, Char::A ],
+                [ Char::A, Char::M, Char::X, Char::S, Char::X, Char::M, Char::A, Char::A, Char::M, Char::M ],
+                [ Char::M, Char::S, Char::A, Char::M, Char::A, Char::S, Char::M, Char::S, Char::M, Char::X ],
+                [ Char::X, Char::M, Char::A, Char::S, Char::A, Char::M, Char::X, Char::A, Char::M, Char::M ],
+                [ Char::X, Char::X, Char::A, Char::M, Char::M, Char::X, Char::X, Char::A, Char::M, Char::A ],
+                [ Char::S, Char::M, Char::S, Char::M, Char::S, Char::A, Char::S, Char::X, Char::S, Char::S ],
+                [ Char::S, Char::A, Char::X, Char::A, Char::M, Char::A, Char::S, Char::A, Char::A, Char::A ],
+                [ Char::M, Char::A, Char::M, Char::M, Char::M, Char::X, Char::M, Char::M, Char::M, Char::M ],
+                [ Char::M, Char::X, Char::M, Char::X, Char::A, Char::X, Char::M, Char::A, Char::S, Char::X ]
+            ]
+        )
+    }
+
+    #[rustfmt::skip]
+    #[test]
+    fn test_find_xs() {
+        assert_eq!(
+            find_chars(&parse(INPUT_STR), Char::X),
+            [
+                (0, 4), (0, 5),
+                (1, 4),
+                (2, 2), (2, 4),
+                (3, 9),
+                (4, 0), (4, 6),
+                (5, 0), (5, 1), (5, 5), (5, 6),
+                (6, 7),
+                (7, 2),
+                (8, 5),
+                (9, 1), (9, 3), (9, 5), (9, 9)
+            ]
+        )
+    }
+
+    #[test]
+    fn test_make_dirs() {
+        assert_eq!(
+            make_dirs((0, 4), 10),
+            [
+                [(0, 4), (0, 3), (0, 2), (0, 1)],
+                [(0, 4), (0, 5), (0, 6), (0, 7)],
+                [(0, 4), (1, 3), (2, 2), (3, 1)],
+                [(0, 4), (1, 4), (2, 4), (3, 4)],
+                [(0, 4), (1, 5), (2, 6), (3, 7)]
+            ]
+        )
+    }
+
+    #[test]
+    fn test_check_xs() {
+        assert_eq!(check_xs_for_xmas(&parse(INPUT_STR)), 18)
+    }
+
+    #[test]
+    fn test_make_xdirs() {
+        assert_eq!(make_xdirs((0, 4), 10), None);
+        assert_eq!(
+            make_xdirs((1, 1), 10),
+            Some([(0, 0), (0, 2), (2, 0), (2, 2)])
+        );
+    }
+
+    #[test]
+    fn test_check_as() {
+        assert_eq!(check_as_for_xmas(&parse(INPUT_STR)), 9)
+    }
+}
index d6680b52d35824891362ebf694419c31f50b486e..ca144b900de79ecd655aa5597d16f88b21be662d 100644 (file)
@@ -1,7 +1,7 @@
 pub mod day01;
 pub mod day02;
 pub mod day03;
-// pub mod day04;
+pub mod day04;
 // pub mod day05;
 // pub mod day06;
 // pub mod day07;
@@ -26,11 +26,11 @@ pub mod day03;
 
 type Part = fn();
 
-const DAYS: [(Part, Part); 3] = [
+const DAYS: [(Part, Part); 4] = [
     (day01::part1 as fn(), day01::part2 as fn()),
     (day02::part1 as fn(), day02::part2 as fn()),
     (day03::part1 as fn(), day03::part2 as fn()),
-    // (day04::part1 as fn(), day04::part2 as fn()),
+    (day04::part1 as fn(), day04::part2 as fn()),
     // (day05::part1 as fn(), day05::part2 as fn()),
     // (day06::part1 as fn(), day06::part2 as fn()),
     // (day07::part1 as fn(), day07::part2 as fn()),