[2018][rust][15.1] combat
change ommrmownxspwqtnlsukqsrnnxyqtopru
commit 48aeab7053d22e4b82bdb08d712d17cade16d6ea
author Alpha Chen <alpha.chen@gmail.com>
date
parent vrpllxqq
diff --git a/2018/rust/src/bin/day_15.rs b/2018/rust/src/bin/day_15.rs
index fa899a0..8ace5dc 100644
--- a/2018/rust/src/bin/day_15.rs
+++ b/2018/rust/src/bin/day_15.rs
@@ -1,6 +1,7 @@
 use std::cmp::Ordering;
 use std::collections::{BinaryHeap, HashMap, HashSet};
 use std::error::Error;
+use std::fmt::{self, Display, Formatter};
 use std::str::FromStr;
 
 use advent_of_code::main;
@@ -15,14 +16,118 @@ struct Combat {
     map: Map,
 }
 
-impl Combat {}
+impl Combat {
+    fn initiative(&self) -> Vec<(Square, Unit)> {
+        let mut initiative: Vec<_> = self.map.units.iter().map(|(&k, &v)| (k, v)).collect();
+        initiative.sort_by_key(|(x, _)| *x);
+        initiative
+    }
+
+    fn move_unit(&mut self, from: &Square, to: &Square) {
+        let unit = self.map.units.remove(from).unwrap();
+        self.map.units.insert(*to, unit);
+    }
+}
+
+impl Iterator for Combat {
+    type Item = Map;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        for (square, unit) in self.initiative() {
+            if let Some(next_step) = unit.next_step(&square, &self.map) {
+                println!("{:?} -> {:?}", square, next_step);
+                self.move_unit(&square, &next_step);
+            }
+        }
+
+        Some(self.map.clone())
+    }
+}
+
+#[test]
+fn test_combat() {
+    let map: Map = r"
+#########
+#G..G..G#
+#.......#
+#.......#
+#G..E..G#
+#.......#
+#.......#
+#G..G..G#
+#########
+    "
+    .parse()
+    .unwrap();
+
+    let mut combat = Combat { map };
+
+    let map = combat.next().unwrap();
+    assert_eq!(
+        format!("{}", map),
+        r"
+#########
+#.G...G.#
+#...G...#
+#...E..G#
+#.G.....#
+#.......#
+#G..G..G#
+#.......#
+#########
+        "
+        .trim()
+        .to_string()
+    );
+
+    let map = combat.next().unwrap();
+    assert_eq!(
+        format!("{}", map),
+        r"
+#########
+#..G.G..#
+#...G...#
+#.G.E.G.#
+#.......#
+#G..G..G#
+#.......#
+#.......#
+#########
+        "
+        .trim()
+        .to_string()
+    );
+
+    let map = combat.next().unwrap();
+    assert_eq!(
+        format!("{}", map),
+        r"
+#########
+#.......#
+#..GGG..#
+#..GEG..#
+#G..G...#
+#......G#
+#.......#
+#.......#
+#########
+        "
+        .trim()
+        .to_string()
+    );
+}
 
+#[derive(Clone)]
 struct Map {
     walls: HashSet<Square>,
     units: HashMap<Square, Unit>,
 }
 
 impl Map {
+    fn squares(&self) -> impl Iterator<Item = &Square> {
+        self.walls.iter().chain(self.units.keys())
+    }
+
     fn is_open(&self, square: &Square) -> bool {
         !self.walls.contains(&square) && !self.units.contains_key(&square)
     }
@@ -78,6 +183,33 @@ impl Map {
     }
 }
 
+impl Display for Map {
+    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
+        let min_y = self.squares().map(|x| (x.0).0).min().unwrap();
+        let max_y = self.squares().map(|x| (x.0).0).max().unwrap();
+        let min_x = self.squares().map(|x| (x.0).1).min().unwrap();
+        let max_x = self.squares().map(|x| (x.0).1).max().unwrap();
+        let mut out = String::new();
+        for y in min_y..=max_y {
+            for x in min_x..=max_x {
+                let square = Square((y, x));
+                out.push(if self.walls.contains(&square) {
+                    '#'
+                } else if let Some(Unit { race, .. }) = self.units.get(&square) {
+                    match race {
+                        Race::Elf => 'E',
+                        Race::Goblin => 'G',
+                    }
+                } else {
+                    '.'
+                })
+            }
+            out.push('\n');
+        }
+        write!(f, "{}", out.trim())
+    }
+}
+
 impl FromStr for Map {
     type Err = Box<Error>;
 
@@ -191,6 +323,7 @@ fn test_square_ord() {
     );
 }
 
+#[derive(Clone, Copy, Debug)]
 struct Unit {
     race: Race,
     hp: usize,
@@ -210,15 +343,19 @@ impl Unit {
             .collect()
     }
 
-    fn in_range(&self, map: &Map) -> HashSet<Square> {
-        self.targets(&map)
-            .iter()
+    fn in_range(&self, square: &Square, map: &Map) -> HashSet<Square> {
+        let targets = self.targets(&map);
+        if square.neighbors().iter().any(|x| targets.contains(x)) {
+            return HashSet::new();
+        }
+
+        targets.iter()
             .flat_map(|x| map.open_neighbors(x))
             .collect()
     }
 
     fn reachable(&self, square: &Square, map: &Map) -> HashMap<Square, usize> {
-        let in_range = self.in_range(&map);
+        let in_range = self.in_range(square, map);
         map.distances(square)
             .into_iter()
             .filter(|(x, _)| in_range.contains(x))
@@ -279,7 +416,7 @@ fn test_unit() {
         .map(|&x| Square(x))
         .all(|x| targets.contains(&x)));
 
-    let in_range = unit.in_range(&map);
+    let in_range = unit.in_range(&square, &map);
     assert_eq!(in_range.len(), 6);
     assert!(vec![(1, 3), (1, 5), (2, 2), (2, 5), (3, 1), (3, 3)]
         .iter()
@@ -316,7 +453,7 @@ fn test_unit_next_step() {
     assert_eq!(next_step.unwrap().0, (1, 3));
 }
 
-#[derive(Debug, Eq, PartialEq)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 enum Race {
     Elf,
     Goblin,