[2018][rust][07.2]
change vtzrlvlpntmkmyxpnsosourorzpsysok
commit f16900342df0a7b634d25111c52df622a9222d6d
author Alpha Chen <alpha.chen@gmail.com>
date
parent yzrxokyk
diff --git a/2018/rust/src/bin/day_07.rs b/2018/rust/src/bin/day_07.rs
index 7b627c1..a604351 100644
--- a/2018/rust/src/bin/day_07.rs
+++ b/2018/rust/src/bin/day_07.rs
@@ -1,3 +1,6 @@
+#![feature(vec_remove_item)]
+
+use std::cmp::{Ord, Ordering};
 use std::collections::{HashMap, HashSet};
 use std::error::Error;
 use std::io::{self, Read};
@@ -17,23 +20,115 @@ fn main() -> Result<(), Box<Error>> {
 
 fn solve(input: &str) -> Result<String, Box<Error>> {
     let mut assembly: Assembly = input.parse()?;
-    let output = part_one(&mut assembly).iter().map(char::to_string).collect();
-    Ok(output)
+    let output = part_two(&mut assembly, 5, |x| (x as usize) - ('A' as usize) + 61);
+    Ok(output.to_string())
 }
 
-fn part_two<F: Fn(char) -> usize>(assembly: Assembly, worker_count: usize, step_time: F) {}
+fn part_two<F: Fn(char) -> usize>(
+    assembly: &mut Assembly,
+    worker_count: usize,
+    step_time: F,
+) -> usize {
+    let mut workers = Workers(vec![None; worker_count]);
+
+    let mut output = 0;
+    while !assembly.is_done() || workers.are_working() {
+        for (worker, step_id) in workers.available().iter_mut().zip(assembly.available()) {
+            worker.replace((step_id, step_time(step_id)));
+            assembly.start(&step_id);
+        }
+
+        let done = workers.tick();
+        for step_id in done {
+            assembly.finish(&step_id);
+        }
+
+        output += 1;
+    }
+
+    output
+}
+
+#[test]
+fn test_part_two() {
+    let input = r"
+Step C must be finished before step A can begin.
+Step C must be finished before step F can begin.
+Step A must be finished before step B can begin.
+Step A must be finished before step D can begin.
+Step B must be finished before step E can begin.
+Step D must be finished before step E can begin.
+Step F must be finished before step E can begin.
+    ";
+
+    let mut assembly: Assembly = input.parse().unwrap();
+    let output = part_two(&mut assembly, 2, |x| (x as usize) - ('A' as usize) + 1);
+    assert_eq!(output, 15);
+}
+
+#[derive(Debug)]
+struct Workers(Vec<Option<(char, usize)>>);
+
+impl Workers {
+    fn are_working(&self) -> bool {
+        !self.current_work().is_empty()
+    }
+
+    fn available(&mut self) -> Vec<&mut Option<(char, usize)>> {
+        self.0.iter_mut().filter(|x| x.is_none()).collect()
+    }
+
+    fn current_work(&self) -> Vec<&char> {
+        self.0.iter().flat_map(|x| x).map(|(x, _)| x).collect()
+    }
+
+    fn tick(&mut self) -> Vec<char> {
+        let mut done = Vec::new();
+        for maybe_work in self.0.iter_mut() {
+            if let Some((step, mut time)) = maybe_work.take() {
+                time -= 1;
+                if time > 0 {
+                    maybe_work.replace((step, time));
+                } else {
+                    done.push(step);
+                }
+            }
+        }
+        done
+    }
+}
+
+#[test]
+fn test_workers() {
+    let mut workers = Workers(vec![None; 5]);
+
+    let steps = workers.tick();
+    assert!(steps.is_empty());
+
+    let worker = &mut workers.available()[0];
+    worker.replace(('a', 5));
+    assert_eq!(workers.0[0], Some(('a', 5)));
+    assert_eq!(workers.current_work(), vec![&'a']);
+
+    let worker = &mut workers.available()[0];
+    worker.replace(('b', 1));
+    assert_eq!(workers.0[1], Some(('b', 1)));
+    assert_eq!(workers.current_work(), vec![&'a', &'b']);
+
+    let steps = workers.tick();
+    assert_eq!(workers.0[0], Some(('a', 4)));
+    assert_eq!(steps, vec!['b']);
+}
 
 #[allow(dead_code)]
 fn part_one(assembly: &mut Assembly) -> Vec<char> {
     let mut output = Vec::new();
     while !assembly.is_done() {
-        let mut available = assembly.available_steps();
-        available.sort();
-        let done = *available[0];
-
-        assembly.finish(&done);
+        let done = assembly.available()[0];
 
         output.push(done);
+
+        assembly.finish(&done);
     }
 
     output
@@ -56,27 +151,43 @@ Step F must be finished before step E can begin.
     assert_eq!(output, vec!['C', 'A', 'B', 'D', 'F', 'E']);
 }
 
-struct Assembly {
-    steps: HashMap<char, HashSet<char>>,
-}
+struct Assembly(Vec<Step>);
 
 impl Assembly {
     fn is_done(&self) -> bool {
-        self.steps.is_empty()
+        self.0.iter().all(|x| x.state == State::Finished)
+    }
+
+    fn available(&self) -> Vec<char> {
+        let finished = self.finished();
+        let mut available: Vec<_> = self
+            .0
+            .iter()
+            .filter(|x| x.state == State::Unstarted)
+            .filter(|x| x.deps.iter().all(|x| finished.contains(x)))
+            .map(|x| x.id)
+            .collect();
+        available.sort();
+        available
     }
 
-    fn available_steps(&self) -> Vec<&char> {
-        self.steps
+    fn finished(&self) -> Vec<char> {
+        self.0
             .iter()
-            .filter(|(_, v)| v.is_empty())
-            .map(|(k, _)| k)
+            .filter(|x| x.state == State::Finished)
+            .map(|x| x.id)
             .collect()
     }
 
-    fn finish(&mut self, step: &char) {
-        self.steps.remove(step);
-        for dependencies in self.steps.values_mut() {
-            dependencies.remove(step);
+    fn start(&mut self, step_id: &char) {
+        if let Some(step) = self.0.iter_mut().find(|x| &x.id == step_id) {
+            step.state = State::InProgress;
+        }
+    }
+
+    fn finish(&mut self, step_id: &char) {
+        if let Some(step) = self.0.iter_mut().find(|x| &x.id == step_id) {
+            step.state = State::Finished;
         }
     }
 }
@@ -103,7 +214,40 @@ impl FromStr for Assembly {
                 let entry = steps.entry(y).or_insert_with(HashSet::new);
                 entry.insert(x);
             });
+        let steps = steps
+            .into_iter()
+            .map(|(id, deps)| {
+                let state = State::Unstarted;
+                Step { id, deps, state }
+            })
+            .collect();
+
+        Ok(Assembly(steps))
+    }
+}
+
+#[derive(Eq, PartialEq)]
+struct Step {
+    id: char,
+    deps: HashSet<char>,
+    state: State,
+}
+
+impl PartialOrd for Step {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
 
-        Ok(Assembly { steps })
+impl Ord for Step {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.id.cmp(&other.id)
     }
 }
+
+#[derive(Eq, PartialEq)]
+enum State {
+    Unstarted,
+    InProgress,
+    Finished,
+}