[2018][rust][06.0]
change sywzxqoopmssulmvxpxmznptyywoznzt
commit 415ce45ba24868681b0380f2406306f13ff1ff13
author Alpha Chen <alpha.chen@gmail.com>
date
parent opntluyk
diff --git a/2018/rust/src/bin/day_06.rs b/2018/rust/src/bin/day_06.rs
new file mode 100644
index 0000000..236f739
--- /dev/null
+++ b/2018/rust/src/bin/day_06.rs
@@ -0,0 +1,227 @@
+use std::collections::{HashMap, HashSet};
+use std::error::Error;
+use std::hash::Hash;
+use std::io::{self, Read};
+use std::str::FromStr;
+
+fn main() -> Result<(), Box<Error>> {
+    let mut input = String::new();
+    io::stdin().read_to_string(&mut input)?;
+
+    let output = solve(&input)?;
+    println!("{}", output);
+
+    Ok(())
+}
+
+fn solve(input: &str) -> Result<String, Box<Error>> {
+    let coords = Coords::from_str(input)?;
+
+    let min_x = coords.0.iter().map(|c| c.x).min().unwrap();
+    let max_x = coords.0.iter().map(|c| c.x).max().unwrap();
+    let min_y = coords.0.iter().map(|c| c.y).min().unwrap();
+    let max_y = coords.0.iter().map(|c| c.y).max().unwrap();
+    let top_left = Coord { x: min_x, y: min_y };
+    let bottom_right = Coord { x: max_x, y: max_y };
+
+    let coord_names: HashMap<_, _> = coords
+        .0
+        .iter()
+        .enumerate()
+        .map(|(i, c)| (c, (i + 65) as u8 as char))
+        .collect();
+
+    let grid = Grid::new(top_left, bottom_right, |coord| {
+        coords
+            .closest(*coord)
+            .and_then(|closest| coord_names.get(&closest))
+    });
+    let edges: HashSet<_> = grid.edges().iter().flat_map(|&x| x).collect();
+
+    let mut area_sizes = HashMap::new();
+    grid.locations
+        .values()
+        .flat_map(|x| x)
+        .filter(|x| !edges.contains(x))
+        .for_each(|x| {
+            let count = area_sizes.entry(x).or_insert(0);
+            *count += 1;
+        });
+
+    let max_area = area_sizes.iter().map(|(_, v)| v).max().unwrap();
+    Ok(max_area.to_string())
+}
+
+#[test]
+fn test_solve() {
+    let input = r"
+1, 1
+1, 6
+8, 3
+3, 4
+5, 5
+8, 9
+    ";
+
+    let output = solve(&input).unwrap();
+    assert_eq!(output, "17".to_string());
+}
+
+#[derive(Debug)]
+struct Grid<T> {
+    top_left: Coord,
+    bottom_right: Coord,
+    locations: HashMap<Coord, T>,
+}
+
+impl<T> Grid<T> {
+    fn new<C: Into<Coord>, F: Fn(&Coord) -> T>(top_left: C, bottom_right: C, f: F) -> Self {
+        let top_left = top_left.into();
+        let bottom_right = bottom_right.into();
+        let mut locations = HashMap::new();
+
+        for coord in top_left.grid(&bottom_right) {
+            locations.insert(coord, f(&coord));
+        }
+
+        Grid {
+            top_left,
+            bottom_right,
+            locations,
+        }
+    }
+}
+
+impl<T: Eq + Hash> Grid<T> {
+    fn edges(&self) -> HashSet<&T> {
+        let mut edges = HashSet::new();
+
+        for x in self.top_left.x..=self.bottom_right.x {
+            for &y in &[self.top_left.y, self.bottom_right.y] {
+                if let Some(t) = self.locations.get(&Coord { x, y }) {
+                    edges.insert(t);
+                }
+            }
+        }
+
+        for y in self.top_left.y..=self.bottom_right.y {
+            for &x in &[self.top_left.x, self.bottom_right.x] {
+                if let Some(t) = self.locations.get(&Coord { x, y }) {
+                    edges.insert(t);
+                }
+            }
+        }
+
+        edges
+    }
+}
+
+#[test]
+fn test_edges() {
+    let top_left = Coord { x: 0, y: 0 };
+    let bottom_right = Coord { x: 2, y: 2 };
+    let grid = Grid::new(top_left, bottom_right, |coord| coord.y * 3 + coord.x);
+    let expected: HashSet<_> = vec![0, 1, 2, 3, 5, 6, 7, 8]
+        .iter()
+        .map(|&x| x as isize)
+        .collect();
+    assert_eq!(grid.edges(), expected.iter().collect());
+}
+
+#[derive(Debug)]
+struct Coords(Vec<Coord>);
+
+impl Coords {
+    fn closest<C: Into<Coord>>(&self, c: C) -> Option<Coord> {
+        let c = c.into();
+
+        let mut distances = HashMap::new();
+        for coord in &self.0 {
+            let distance = coord.distance(c);
+            let v = distances.entry(distance).or_insert_with(Vec::new);
+            v.push(coord);
+        }
+
+        let (_, closest_coords) = distances.iter().min_by_key(|(&k, _)| k)?;
+        if closest_coords.len() == 1 {
+            Some(*closest_coords[0])
+        } else {
+            None
+        }
+    }
+}
+
+#[test]
+fn test_closest() {
+    let coords = Coords::from_str(
+        r"
+1, 1
+1, 6
+8, 3
+3, 4
+5, 5
+8, 9
+    ",
+    )
+    .unwrap();
+
+    assert_eq!(coords.closest((0, 0)), Some(Coord::from((1, 1))));
+    assert_eq!(coords.closest((3, 2)), Some(Coord::from((3, 4))));
+    assert_eq!(coords.closest((5, 0)), None);
+    assert_eq!(coords.closest((0, 4)), None);
+}
+
+impl FromStr for Coords {
+    type Err = Box<Error>;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let coords = s
+            .trim()
+            .lines()
+            .map(Coord::from_str)
+            .collect::<Result<_, _>>()?;
+        Ok(Coords(coords))
+    }
+}
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+struct Coord {
+    x: isize,
+    y: isize,
+}
+
+impl Coord {
+    fn distance<C: Into<Self>>(&self, other: C) -> isize {
+        let other = other.into();
+        (self.x - other.x).abs() + (self.y - other.y).abs()
+    }
+
+    fn grid(&self, to: &Self) -> impl Iterator<Item = Coord> + '_ {
+        let to = *to;
+        (self.x..=to.x).flat_map(move |x| (self.y..=to.y).map(move |y| (x, y).into()))
+    }
+}
+
+#[test]
+fn test_distance() {
+    let coord = Coord { x: 4, y: 3 };
+    assert_eq!(coord.distance((1, 1)), 5);
+    assert_eq!(coord.distance((8, 9)), 10);
+}
+
+impl From<(isize, isize)> for Coord {
+    fn from((x, y): (isize, isize)) -> Self {
+        Coord { x, y }
+    }
+}
+
+impl FromStr for Coord {
+    type Err = Box<Error>;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let mut xy = s.split(", ");
+        let x = xy.next().unwrap().parse()?;
+        let y = xy.next().unwrap().parse()?;
+        Ok(Coord { x, y })
+    }
+}