Completed day 7 part 2
This commit is contained in:
parent
595605daaf
commit
33c926887d
386
src/day7.rs
386
src/day7.rs
@ -2,6 +2,7 @@ extern crate regex;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::result;
|
||||
@ -10,15 +11,27 @@ use regex::{Captures, Regex};
|
||||
|
||||
type Result<T> = result::Result<T, Box<Error>>;
|
||||
|
||||
type Instructions = HashMap<char, Vec<char>>;
|
||||
|
||||
const INPUT: &str = "inputs/7.txt";
|
||||
static ALPHABET: [char; 26] = [
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
|
||||
'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
];
|
||||
|
||||
pub fn solve_part1() -> Result<String> {
|
||||
let mut instructions = read_instructions(INPUT)?;
|
||||
Ok(get_step_sequence(&mut instructions))
|
||||
}
|
||||
|
||||
fn read_instructions(filename: &str) -> Result<HashMap<char, Vec<char>>> {
|
||||
let mut instructions: HashMap<char, Vec<char>> = HashMap::new();
|
||||
pub fn solve_part2() -> Result<u32> {
|
||||
let mut pool = WorkerPool::new(5);
|
||||
let mut instructions = read_instructions(INPUT)?;
|
||||
Ok(get_parallel_step_sequence_seconds(&mut instructions, &mut pool))
|
||||
}
|
||||
|
||||
fn read_instructions(filename: &str) -> Result<Instructions> {
|
||||
let mut instructions: Instructions = HashMap::new();
|
||||
lazy_static! {
|
||||
static ref INSTRUCTION_REGEX: Regex = Regex::new(
|
||||
r"Step (?P<dependency>\w) must be finished before step (?P<step>\w) can begin."
|
||||
@ -31,9 +44,7 @@ fn read_instructions(filename: &str) -> Result<HashMap<char, Vec<char>>> {
|
||||
Some(captures) => {
|
||||
let step = get_captured_field(&captures, "step")?;
|
||||
let dependency: char = get_captured_field(&captures, "dependency")?;
|
||||
instructions
|
||||
.entry(dependency)
|
||||
.or_insert_with(Vec::new);
|
||||
instructions.entry(dependency).or_insert_with(Vec::new);
|
||||
let dependencies = instructions.entry(step).or_insert_with(Vec::new);
|
||||
dependencies.push(dependency);
|
||||
}
|
||||
@ -63,7 +74,7 @@ fn get_captured_field(captures: &Captures, field: &str) -> Result<char> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_step_sequence(instructions: &mut HashMap<char, Vec<char>>) -> String {
|
||||
fn get_step_sequence(instructions: &mut Instructions) -> String {
|
||||
let mut sequence = String::new();
|
||||
loop {
|
||||
let mut available: Vec<char> = instructions
|
||||
@ -88,23 +99,178 @@ fn get_step_sequence(instructions: &mut HashMap<char, Vec<char>>) -> String {
|
||||
sequence
|
||||
}
|
||||
|
||||
fn get_parallel_step_sequence_seconds(
|
||||
mut instructions: &mut Instructions,
|
||||
worker_pool: &mut WorkerPool,
|
||||
) -> u32 {
|
||||
let mut sequence = String::new();
|
||||
let mut seconds = 0;
|
||||
loop {
|
||||
worker_pool.run_one_second(&mut instructions, &mut sequence);
|
||||
|
||||
let mut available: Vec<char> = instructions
|
||||
.iter()
|
||||
.filter(|(_, dependencies)| dependencies.is_empty())
|
||||
.map(|(step, _)| *step)
|
||||
.collect();
|
||||
|
||||
if available.is_empty() && worker_pool.all_idle() {
|
||||
break;
|
||||
}
|
||||
|
||||
available.sort();
|
||||
available.reverse();
|
||||
|
||||
for mut worker in worker_pool.available() {
|
||||
let next = match available.pop() {
|
||||
None => break,
|
||||
Some(next) => next,
|
||||
};
|
||||
instructions.remove(&next);
|
||||
worker_pool.assign_worker(worker.id, next);
|
||||
}
|
||||
seconds += 1;
|
||||
}
|
||||
println!("{}", sequence);
|
||||
seconds
|
||||
}
|
||||
|
||||
fn get_seconds_for_step(step_letter: char) -> u8 {
|
||||
ALPHABET.iter().position(|&c| c == step_letter).unwrap_or(0) as u8 + 61 as u8
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
struct Worker {
|
||||
id: u8,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum Status {
|
||||
Idle,
|
||||
Working { step: char, remaining: u8 },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct WorkerPool {
|
||||
workers: Vec<Worker>,
|
||||
}
|
||||
|
||||
impl WorkerPool {
|
||||
fn new(count: u8) -> WorkerPool {
|
||||
let mut workers = Vec::new();
|
||||
for i in 0..count {
|
||||
workers.push(Worker {
|
||||
id: i,
|
||||
status: Status::Idle,
|
||||
})
|
||||
}
|
||||
WorkerPool { workers }
|
||||
}
|
||||
|
||||
fn available(&self) -> Vec<Worker> {
|
||||
self.workers
|
||||
.iter()
|
||||
.filter(|worker| match worker.status {
|
||||
Status::Idle => true,
|
||||
Status::Working { .. } => false,
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn all_idle(&self) -> bool {
|
||||
self.workers
|
||||
.iter()
|
||||
.all(|worker| worker.status == Status::Idle)
|
||||
}
|
||||
|
||||
fn run_one_second(&mut self, instructions: &mut Instructions, sequence: &mut String) {
|
||||
let new_workers = self
|
||||
.workers
|
||||
.iter()
|
||||
.map(|worker| Worker {
|
||||
id: worker.id,
|
||||
status: match worker.status {
|
||||
Status::Idle => Status::Idle,
|
||||
Status::Working { step, remaining } => {
|
||||
if remaining == 1 {
|
||||
for dependencies in instructions.values_mut() {
|
||||
if let Some(index) = dependencies.iter().position(|d| *d == step) {
|
||||
dependencies.remove(index);
|
||||
}
|
||||
}
|
||||
sequence.push(step);
|
||||
Status::Idle
|
||||
} else {
|
||||
Status::Working {
|
||||
step,
|
||||
remaining: remaining - 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
self.workers = new_workers;
|
||||
}
|
||||
|
||||
fn assign_worker(&mut self, id: u8, step: char) {
|
||||
let new_workers = self
|
||||
.workers
|
||||
.iter()
|
||||
.map(|worker| {
|
||||
if worker.id == id {
|
||||
Worker {
|
||||
id: worker.id,
|
||||
status: Status::Working {
|
||||
step,
|
||||
remaining: get_seconds_for_step(step),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
*worker
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
self.workers = new_workers;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for WorkerPool {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
for worker in &self.workers {
|
||||
writeln!(f, "{}", worker)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Worker {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.status {
|
||||
Status::Idle => write!(f, "{}: idle", self.id),
|
||||
Status::Working { step, remaining } => {
|
||||
write!(f, "{}: {} - {}", self.id, step, remaining)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const TEST_INPUT: &str = "inputs/7_test.txt";
|
||||
|
||||
fn test_instructions() -> HashMap<char, Vec<char>> {
|
||||
fn test_instructions() -> Instructions {
|
||||
[
|
||||
('A', vec!['C']),
|
||||
('F', vec!['C']),
|
||||
('C', vec![]),
|
||||
('B', vec!['A']),
|
||||
('D', vec!['A']),
|
||||
(
|
||||
'E',
|
||||
vec!['B', 'D', 'F'],
|
||||
),
|
||||
('E', vec!['B', 'D', 'F']),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
@ -120,4 +286,202 @@ mod tests {
|
||||
fn gets_step_sequence() {
|
||||
assert_eq!(get_step_sequence(&mut test_instructions()), "CABDFE");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_worker_pool() {
|
||||
assert_eq!(
|
||||
WorkerPool::new(3),
|
||||
WorkerPool {
|
||||
workers: vec![
|
||||
Worker {
|
||||
id: 0,
|
||||
status: Status::Idle
|
||||
},
|
||||
Worker {
|
||||
id: 1,
|
||||
status: Status::Idle
|
||||
},
|
||||
Worker {
|
||||
id: 2,
|
||||
status: Status::Idle
|
||||
},
|
||||
]
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn available_workers_in_pool() {
|
||||
let pool = WorkerPool {
|
||||
workers: vec![
|
||||
Worker {
|
||||
id: 0,
|
||||
status: Status::Idle,
|
||||
},
|
||||
Worker {
|
||||
id: 1,
|
||||
status: Status::Working {
|
||||
step: 'A',
|
||||
remaining: 1,
|
||||
},
|
||||
},
|
||||
Worker {
|
||||
id: 2,
|
||||
status: Status::Idle,
|
||||
},
|
||||
],
|
||||
};
|
||||
assert_eq!(
|
||||
pool.available(),
|
||||
vec![
|
||||
Worker {
|
||||
id: 0,
|
||||
status: Status::Idle
|
||||
},
|
||||
Worker {
|
||||
id: 2,
|
||||
status: Status::Idle
|
||||
},
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_workers_one_second() {
|
||||
let mut pool = WorkerPool {
|
||||
workers: vec![
|
||||
Worker {
|
||||
id: 0,
|
||||
status: Status::Idle,
|
||||
},
|
||||
Worker {
|
||||
id: 1,
|
||||
status: Status::Working {
|
||||
step: 'A',
|
||||
remaining: 2,
|
||||
},
|
||||
},
|
||||
Worker {
|
||||
id: 2,
|
||||
status: Status::Idle,
|
||||
},
|
||||
],
|
||||
};
|
||||
pool.run_one_second(&mut test_instructions(), &mut String::new());
|
||||
assert_eq!(
|
||||
pool,
|
||||
WorkerPool {
|
||||
workers: vec![
|
||||
Worker {
|
||||
id: 0,
|
||||
status: Status::Idle
|
||||
},
|
||||
Worker {
|
||||
id: 1,
|
||||
status: Status::Working {
|
||||
step: 'A',
|
||||
remaining: 1
|
||||
}
|
||||
},
|
||||
Worker {
|
||||
id: 2,
|
||||
status: Status::Idle
|
||||
},
|
||||
]
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_workers_one_second_and_complete_step() {
|
||||
let mut pool = WorkerPool {
|
||||
workers: vec![Worker {
|
||||
id: 0,
|
||||
status: Status::Working {
|
||||
step: 'A',
|
||||
remaining: 1,
|
||||
},
|
||||
}],
|
||||
};
|
||||
let mut instructions = [('A', vec![])].iter().cloned().collect();
|
||||
let mut sequence = "Z".to_string();
|
||||
pool.run_one_second(&mut instructions, &mut sequence);
|
||||
assert_eq!(
|
||||
pool,
|
||||
WorkerPool {
|
||||
workers: vec![Worker {
|
||||
id: 0,
|
||||
status: Status::Idle
|
||||
},]
|
||||
}
|
||||
);
|
||||
assert_eq!(instructions, [('A', vec![])].iter().cloned().collect());
|
||||
assert_eq!(sequence, "ZA".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn worker_pool_all_idle() {
|
||||
assert_eq!(WorkerPool::new(3).all_idle(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn worker_pool_not_all_idle() {
|
||||
assert_eq!(
|
||||
WorkerPool {
|
||||
workers: vec![
|
||||
Worker {
|
||||
id: 0,
|
||||
status: Status::Idle
|
||||
},
|
||||
Worker {
|
||||
id: 1,
|
||||
status: Status::Working {
|
||||
step: 'A',
|
||||
remaining: 1
|
||||
}
|
||||
},
|
||||
],
|
||||
}
|
||||
.all_idle(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assign_step_to_worker_in_pool() {
|
||||
let mut pool = WorkerPool::new(2);
|
||||
pool.assign_worker(0, 'A');
|
||||
assert_eq!(
|
||||
pool,
|
||||
WorkerPool {
|
||||
workers: vec![
|
||||
Worker {
|
||||
id: 0,
|
||||
status: Status::Working {
|
||||
step: 'A',
|
||||
remaining: 61,
|
||||
}
|
||||
},
|
||||
Worker {
|
||||
id: 1,
|
||||
status: Status::Idle,
|
||||
}
|
||||
],
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gets_seconds_for_step() {
|
||||
assert_eq!(get_seconds_for_step('A'), 61);
|
||||
assert_eq!(get_seconds_for_step('B'), 62);
|
||||
assert_eq!(get_seconds_for_step('Z'), 86);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gets_sequence_with_workers() {
|
||||
let mut pool = WorkerPool::new(2);
|
||||
let mut instructions = test_instructions();
|
||||
assert_eq!(get_parallel_step_sequence_seconds(&mut instructions, &mut pool), 258);
|
||||
}
|
||||
}
|
||||
|
@ -31,5 +31,6 @@ fn main() {
|
||||
// println!("{}", day6::solve_part1().unwrap());
|
||||
// println!("{}", day6::solve_part2().unwrap());
|
||||
println!("Day 7:");
|
||||
println!("{}", day7::solve_part1().unwrap());
|
||||
// println!("{}", day7::solve_part1().unwrap());
|
||||
println!("{}", day7::solve_part2().unwrap());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user