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::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
use std::result;
|
use std::result;
|
||||||
@ -10,15 +11,27 @@ use regex::{Captures, Regex};
|
|||||||
|
|
||||||
type Result<T> = result::Result<T, Box<Error>>;
|
type Result<T> = result::Result<T, Box<Error>>;
|
||||||
|
|
||||||
|
type Instructions = HashMap<char, Vec<char>>;
|
||||||
|
|
||||||
const INPUT: &str = "inputs/7.txt";
|
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> {
|
pub fn solve_part1() -> Result<String> {
|
||||||
let mut instructions = read_instructions(INPUT)?;
|
let mut instructions = read_instructions(INPUT)?;
|
||||||
Ok(get_step_sequence(&mut instructions))
|
Ok(get_step_sequence(&mut instructions))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_instructions(filename: &str) -> Result<HashMap<char, Vec<char>>> {
|
pub fn solve_part2() -> Result<u32> {
|
||||||
let mut instructions: HashMap<char, Vec<char>> = HashMap::new();
|
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! {
|
lazy_static! {
|
||||||
static ref INSTRUCTION_REGEX: Regex = Regex::new(
|
static ref INSTRUCTION_REGEX: Regex = Regex::new(
|
||||||
r"Step (?P<dependency>\w) must be finished before step (?P<step>\w) can begin."
|
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) => {
|
Some(captures) => {
|
||||||
let step = get_captured_field(&captures, "step")?;
|
let step = get_captured_field(&captures, "step")?;
|
||||||
let dependency: char = get_captured_field(&captures, "dependency")?;
|
let dependency: char = get_captured_field(&captures, "dependency")?;
|
||||||
instructions
|
instructions.entry(dependency).or_insert_with(Vec::new);
|
||||||
.entry(dependency)
|
|
||||||
.or_insert_with(Vec::new);
|
|
||||||
let dependencies = instructions.entry(step).or_insert_with(Vec::new);
|
let dependencies = instructions.entry(step).or_insert_with(Vec::new);
|
||||||
dependencies.push(dependency);
|
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();
|
let mut sequence = String::new();
|
||||||
loop {
|
loop {
|
||||||
let mut available: Vec<char> = instructions
|
let mut available: Vec<char> = instructions
|
||||||
@ -88,23 +99,178 @@ fn get_step_sequence(instructions: &mut HashMap<char, Vec<char>>) -> String {
|
|||||||
sequence
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const TEST_INPUT: &str = "inputs/7_test.txt";
|
const TEST_INPUT: &str = "inputs/7_test.txt";
|
||||||
|
|
||||||
fn test_instructions() -> HashMap<char, Vec<char>> {
|
fn test_instructions() -> Instructions {
|
||||||
[
|
[
|
||||||
('A', vec!['C']),
|
('A', vec!['C']),
|
||||||
('F', vec!['C']),
|
('F', vec!['C']),
|
||||||
('C', vec![]),
|
('C', vec![]),
|
||||||
('B', vec!['A']),
|
('B', vec!['A']),
|
||||||
('D', vec!['A']),
|
('D', vec!['A']),
|
||||||
(
|
('E', vec!['B', 'D', 'F']),
|
||||||
'E',
|
|
||||||
vec!['B', 'D', 'F'],
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
@ -120,4 +286,202 @@ mod tests {
|
|||||||
fn gets_step_sequence() {
|
fn gets_step_sequence() {
|
||||||
assert_eq!(get_step_sequence(&mut test_instructions()), "CABDFE");
|
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_part1().unwrap());
|
||||||
// println!("{}", day6::solve_part2().unwrap());
|
// println!("{}", day6::solve_part2().unwrap());
|
||||||
println!("Day 7:");
|
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