[2018][rust][15.1] should probably start committing...
change wpzsuurkprzlurnrquwoqxvkumvknrrs
commit 187a1a7c4b42a1176475fb5048183b73b969cf38
author Alpha Chen <alpha.chen@gmail.com>
date
parent szxtwzyq
diff --git a/2018/rust/src/bin/day_15.rs b/2018/rust/src/bin/day_15.rs
new file mode 100644
index 0000000..c3d556b
--- /dev/null
+++ b/2018/rust/src/bin/day_15.rs
@@ -0,0 +1,263 @@
+use std::cmp::Ordering;
+use std::collections::{BinaryHeap, HashMap, HashSet};
+use std::error::Error;
+use std::str::FromStr;
+
+use advent_of_code::main;
+
+main!();
+
+fn solve(input: &str) -> Result<String, Box<Error>> {
+    unimplemented!();
+}
+
+struct Combat {
+    map: Map,
+}
+
+impl Combat {}
+
+struct Map {
+    walls: HashSet<Square>,
+    units: HashMap<Square, Unit>,
+}
+
+impl Map {
+    fn is_open(&self, square: &Square) -> bool {
+        !self.walls.contains(&square) && !self.units.contains_key(&square)
+    }
+
+    fn open_neighbors(&self, square: &Square) -> HashSet<Square> {
+        square
+            .neighbors()
+            .iter()
+            .filter(|x| self.is_open(x))
+            .map(|&x| x)
+            .collect()
+    }
+
+    fn distances(&self, from: &Square) -> HashMap<Square, usize> {
+        #[derive(Eq, PartialEq)]
+        struct State {
+            distance: usize,
+            square: Square,
+        }
+
+        impl Ord for State {
+            fn cmp(&self, other: &Self) -> Ordering {
+                other.distance.cmp(&self.distance)
+            }
+        }
+
+        impl PartialOrd for State {
+            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+                Some(self.cmp(other))
+            }
+        }
+
+        let mut distances = HashMap::new();
+        let mut queue = BinaryHeap::new();
+        queue.push(State{distance: 0, square: *from});
+
+        while let Some(State {distance, square}) = queue.pop() {
+            distances.entry(square).or_insert(distance);
+
+            self
+                .open_neighbors(&square)
+                .iter()
+                .filter(|x| !distances.contains_key(x))
+                .map(|&x| State {
+                    distance: distance + 1,
+                    square: x,
+                })
+                .for_each(|x| queue.push(x));
+        }
+
+        distances
+    }
+}
+
+impl FromStr for Map {
+    type Err = Box<Error>;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let mut walls = HashSet::new();
+        let mut units = HashMap::new();
+        s.trim()
+            .lines()
+            .enumerate()
+            .flat_map(|(y, line)| {
+                line.chars()
+                    .enumerate()
+                    .map(move |(x, c)| (Square((y, x)), c))
+            })
+            .for_each(|(square, c)| {
+                match c {
+                    '#' => {
+                        walls.insert(square);
+                    }
+                    'G' => {
+                        units.insert(square, Unit::new(Race::Goblin));
+                    }
+                    'E' => {
+                        units.insert(square, Unit::new(Race::Elf));
+                    }
+                    '.' => {}
+                    _ => panic!(),
+                };
+            });
+
+        Ok(Self { walls, units })
+    }
+}
+
+#[test]
+fn test_map_distances() {
+    let map: Map = r"
+#######
+#E..G.#
+#...#.#
+#.G.#G#
+#######
+    "
+    .parse()
+    .unwrap();
+
+    let distances = map.distances(&Square((1, 1)));
+    assert!(!distances.contains_key(&Square((0, 0))));
+    assert!(!distances.contains_key(&Square((1, 4))));
+    assert!(!distances.contains_key(&Square((1, 5))));
+    assert_eq!(distances.get(&Square((2, 2))).unwrap(), &2);
+    assert_eq!(distances.get(&Square((1, 3))).unwrap(), &2);
+    assert_eq!(distances.get(&Square((3, 3))).unwrap(), &4);
+}
+
+#[test]
+fn test_map_from_str() {
+    let input = r"
+#######
+#.G.E.#
+#E.G.E#
+#.G.E.#
+#######
+    ";
+
+    let map: Map = input.parse().unwrap();
+
+    assert!(map.walls.contains(&Square((0, 0))));
+    assert!(!map.walls.contains(&Square((1, 1))));
+
+    assert_eq!(map.units.get(&Square((1, 2))).unwrap().race, Race::Goblin);
+    assert_eq!(map.units.get(&Square((1, 4))).unwrap().race, Race::Elf);
+}
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+struct Square((usize, usize));
+
+impl Square {
+    fn neighbors(&self) -> HashSet<Self> {
+        let (y, x) = self.0;
+        let mut set = HashSet::new();
+        set.insert(Square((y - 1, x)));
+        set.insert(Square((y + 1, x)));
+        set.insert(Square((y, x - 1)));
+        set.insert(Square((y, x + 1)));
+        set
+    }
+}
+
+impl Ord for Square {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.0.cmp(&other.0)
+    }
+}
+
+impl PartialOrd for Square {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+#[test]
+fn test_square_ord() {
+    let mut squares: Vec<Square> = vec![(1, 2), (1, 4), (2, 1), (2, 3), (2, 5), (3, 2), (3, 4)]
+        .iter()
+        .map(|&x| Square(x))
+        .collect();
+    squares.sort();
+    assert_eq!(
+        squares.iter().map(|x| x.0).collect::<Vec<_>>(),
+        vec![(1, 2), (1, 4), (2, 1), (2, 3), (2, 5), (3, 2), (3, 4)]
+    );
+}
+
+struct Unit {
+    race: Race,
+    hp: usize,
+}
+
+impl Unit {
+    fn new(race: Race) -> Self {
+        let hp = 200;
+        Unit { race, hp }
+    }
+
+    fn targets(&self, map: &Map) -> HashSet<Square> {
+        map.units
+            .iter()
+            .filter(|(_, x)| x.race == self.race.enemy())
+            .map(|(&x, _)| x)
+            .collect()
+    }
+
+    fn in_range(&self, map: &Map) -> HashSet<Square> {
+        self.targets(&map)
+            .iter()
+            .flat_map(|x| map.open_neighbors(x))
+            .collect()
+    }
+}
+
+#[test]
+fn test_unit_targets() {
+    let map: Map = r"
+#######
+#E..G.#
+#...#.#
+#.G.#G#
+#######
+    "
+    .parse()
+    .unwrap();
+
+    let square = Square((1, 1));
+    let unit = map.units.get(&square).unwrap();
+
+    let targets = unit.targets(&map);
+    assert_eq!(targets.len(), 3);
+    assert!(vec![(1, 4), (3, 2), (3, 5)]
+        .iter()
+        .map(|&x| Square(x))
+        .all(|x| targets.contains(&x)));
+
+    let in_range = unit.in_range(&map);
+    assert_eq!(in_range.len(), 6);
+    assert!(vec![(1, 3), (1, 5), (2, 2), (2, 5), (3, 1), (3, 3)]
+        .iter()
+        .map(|&x| Square(x))
+        .all(|x| in_range.contains(&x)));
+}
+
+#[derive(Debug, Eq, PartialEq)]
+enum Race {
+    Elf,
+    Goblin,
+}
+
+impl Race {
+    fn enemy(&self) -> Race {
+        match self {
+            Race::Elf => Race::Goblin,
+            Race::Goblin => Race::Elf,
+        }
+    }
+}