Browse Source

Completed day 7 part 2

Tyler Hallada 5 years ago
parent
commit
33c926887d
2 changed files with 377 additions and 12 deletions
  1. 375 11
      src/day7.rs
  2. 2 1
      src/main.rs

+ 375 - 11
src/day7.rs

@@ -2,6 +2,7 @@ extern crate regex;
2 2
 
3 3
 use std::collections::HashMap;
4 4
 use std::error::Error;
5
+use std::fmt;
5 6
 use std::fs::File;
6 7
 use std::io::{BufRead, BufReader};
7 8
 use std::result;
@@ -10,15 +11,27 @@ use regex::{Captures, Regex};
10 11
 
11 12
 type Result<T> = result::Result<T, Box<Error>>;
12 13
 
14
+type Instructions = HashMap<char, Vec<char>>;
15
+
13 16
 const INPUT: &str = "inputs/7.txt";
17
+static ALPHABET: [char; 26] = [
18
+    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
19
+    'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
20
+];
14 21
 
15 22
 pub fn solve_part1() -> Result<String> {
16 23
     let mut instructions = read_instructions(INPUT)?;
17 24
     Ok(get_step_sequence(&mut instructions))
18 25
 }
19 26
 
20
-fn read_instructions(filename: &str) -> Result<HashMap<char, Vec<char>>> {
21
-    let mut instructions: HashMap<char, Vec<char>> = HashMap::new();
27
+pub fn solve_part2() -> Result<u32> {
28
+    let mut pool = WorkerPool::new(5);
29
+    let mut instructions = read_instructions(INPUT)?;
30
+    Ok(get_parallel_step_sequence_seconds(&mut instructions, &mut pool))
31
+}
32
+
33
+fn read_instructions(filename: &str) -> Result<Instructions> {
34
+    let mut instructions: Instructions = HashMap::new();
22 35
     lazy_static! {
23 36
         static ref INSTRUCTION_REGEX: Regex = Regex::new(
24 37
             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>>> {
31 44
             Some(captures) => {
32 45
                 let step = get_captured_field(&captures, "step")?;
33 46
                 let dependency: char = get_captured_field(&captures, "dependency")?;
34
-                instructions
35
-                    .entry(dependency)
36
-                    .or_insert_with(Vec::new);
47
+                instructions.entry(dependency).or_insert_with(Vec::new);
37 48
                 let dependencies = instructions.entry(step).or_insert_with(Vec::new);
38 49
                 dependencies.push(dependency);
39 50
             }
@@ -63,7 +74,7 @@ fn get_captured_field(captures: &Captures, field: &str) -> Result<char> {
63 74
     }
64 75
 }
65 76
 
66
-fn get_step_sequence(instructions: &mut HashMap<char, Vec<char>>) -> String {
77
+fn get_step_sequence(instructions: &mut Instructions) -> String {
67 78
     let mut sequence = String::new();
68 79
     loop {
69 80
         let mut available: Vec<char> = instructions
@@ -88,23 +99,178 @@ fn get_step_sequence(instructions: &mut HashMap<char, Vec<char>>) -> String {
88 99
     sequence
89 100
 }
90 101
 
102
+fn get_parallel_step_sequence_seconds(
103
+    mut instructions: &mut Instructions,
104
+    worker_pool: &mut WorkerPool,
105
+) -> u32 {
106
+    let mut sequence = String::new();
107
+    let mut seconds = 0;
108
+    loop {
109
+        worker_pool.run_one_second(&mut instructions, &mut sequence);
110
+
111
+        let mut available: Vec<char> = instructions
112
+            .iter()
113
+            .filter(|(_, dependencies)| dependencies.is_empty())
114
+            .map(|(step, _)| *step)
115
+            .collect();
116
+
117
+        if available.is_empty() && worker_pool.all_idle() {
118
+            break;
119
+        }
120
+
121
+        available.sort();
122
+        available.reverse();
123
+
124
+        for mut worker in worker_pool.available() {
125
+            let next = match available.pop() {
126
+                None => break,
127
+                Some(next) => next,
128
+            };
129
+            instructions.remove(&next);
130
+            worker_pool.assign_worker(worker.id, next);
131
+        }
132
+        seconds += 1;
133
+    }
134
+    println!("{}", sequence);
135
+    seconds
136
+}
137
+
138
+fn get_seconds_for_step(step_letter: char) -> u8 {
139
+    ALPHABET.iter().position(|&c| c == step_letter).unwrap_or(0) as u8 + 61 as u8
140
+}
141
+
142
+#[derive(Clone, Copy, Debug, PartialEq)]
143
+struct Worker {
144
+    id: u8,
145
+    status: Status,
146
+}
147
+
148
+#[derive(Clone, Copy, Debug, PartialEq)]
149
+enum Status {
150
+    Idle,
151
+    Working { step: char, remaining: u8 },
152
+}
153
+
154
+#[derive(Debug, PartialEq)]
155
+struct WorkerPool {
156
+    workers: Vec<Worker>,
157
+}
158
+
159
+impl WorkerPool {
160
+    fn new(count: u8) -> WorkerPool {
161
+        let mut workers = Vec::new();
162
+        for i in 0..count {
163
+            workers.push(Worker {
164
+                id: i,
165
+                status: Status::Idle,
166
+            })
167
+        }
168
+        WorkerPool { workers }
169
+    }
170
+
171
+    fn available(&self) -> Vec<Worker> {
172
+        self.workers
173
+            .iter()
174
+            .filter(|worker| match worker.status {
175
+                Status::Idle => true,
176
+                Status::Working { .. } => false,
177
+            })
178
+            .cloned()
179
+            .collect()
180
+    }
181
+
182
+    fn all_idle(&self) -> bool {
183
+        self.workers
184
+            .iter()
185
+            .all(|worker| worker.status == Status::Idle)
186
+    }
187
+
188
+    fn run_one_second(&mut self, instructions: &mut Instructions, sequence: &mut String) {
189
+        let new_workers = self
190
+            .workers
191
+            .iter()
192
+            .map(|worker| Worker {
193
+                id: worker.id,
194
+                status: match worker.status {
195
+                    Status::Idle => Status::Idle,
196
+                    Status::Working { step, remaining } => {
197
+                        if remaining == 1 {
198
+                            for dependencies in instructions.values_mut() {
199
+                                if let Some(index) = dependencies.iter().position(|d| *d == step) {
200
+                                    dependencies.remove(index);
201
+                                }
202
+                            }
203
+                            sequence.push(step);
204
+                            Status::Idle
205
+                        } else {
206
+                            Status::Working {
207
+                                step,
208
+                                remaining: remaining - 1,
209
+                            }
210
+                        }
211
+                    }
212
+                },
213
+            })
214
+            .collect();
215
+        self.workers = new_workers;
216
+    }
217
+
218
+    fn assign_worker(&mut self, id: u8, step: char) {
219
+        let new_workers = self
220
+            .workers
221
+            .iter()
222
+            .map(|worker| {
223
+                if worker.id == id {
224
+                    Worker {
225
+                        id: worker.id,
226
+                        status: Status::Working {
227
+                            step,
228
+                            remaining: get_seconds_for_step(step),
229
+                        },
230
+                    }
231
+                } else {
232
+                    *worker
233
+                }
234
+            })
235
+            .collect();
236
+        self.workers = new_workers;
237
+    }
238
+}
239
+
240
+impl fmt::Display for WorkerPool {
241
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
242
+        for worker in &self.workers {
243
+            writeln!(f, "{}", worker)?;
244
+        }
245
+        Ok(())
246
+    }
247
+}
248
+
249
+impl fmt::Display for Worker {
250
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
251
+        match self.status {
252
+            Status::Idle => write!(f, "{}: idle", self.id),
253
+            Status::Working { step, remaining } => {
254
+                write!(f, "{}: {} - {}", self.id, step, remaining)
255
+            }
256
+        }
257
+    }
258
+}
259
+
91 260
 #[cfg(test)]
92 261
 mod tests {
93 262
     use super::*;
94 263
 
95 264
     const TEST_INPUT: &str = "inputs/7_test.txt";
96 265
 
97
-    fn test_instructions() -> HashMap<char, Vec<char>> {
266
+    fn test_instructions() -> Instructions {
98 267
         [
99 268
             ('A', vec!['C']),
100 269
             ('F', vec!['C']),
101 270
             ('C', vec![]),
102 271
             ('B', vec!['A']),
103 272
             ('D', vec!['A']),
104
-            (
105
-                'E',
106
-                vec!['B', 'D', 'F'],
107
-            ),
273
+            ('E', vec!['B', 'D', 'F']),
108 274
         ]
109 275
         .iter()
110 276
         .cloned()
@@ -120,4 +286,202 @@ mod tests {
120 286
     fn gets_step_sequence() {
121 287
         assert_eq!(get_step_sequence(&mut test_instructions()), "CABDFE");
122 288
     }
289
+
290
+    #[test]
291
+    fn new_worker_pool() {
292
+        assert_eq!(
293
+            WorkerPool::new(3),
294
+            WorkerPool {
295
+                workers: vec![
296
+                    Worker {
297
+                        id: 0,
298
+                        status: Status::Idle
299
+                    },
300
+                    Worker {
301
+                        id: 1,
302
+                        status: Status::Idle
303
+                    },
304
+                    Worker {
305
+                        id: 2,
306
+                        status: Status::Idle
307
+                    },
308
+                ]
309
+            }
310
+        )
311
+    }
312
+
313
+    #[test]
314
+    fn available_workers_in_pool() {
315
+        let pool = WorkerPool {
316
+            workers: vec![
317
+                Worker {
318
+                    id: 0,
319
+                    status: Status::Idle,
320
+                },
321
+                Worker {
322
+                    id: 1,
323
+                    status: Status::Working {
324
+                        step: 'A',
325
+                        remaining: 1,
326
+                    },
327
+                },
328
+                Worker {
329
+                    id: 2,
330
+                    status: Status::Idle,
331
+                },
332
+            ],
333
+        };
334
+        assert_eq!(
335
+            pool.available(),
336
+            vec![
337
+                Worker {
338
+                    id: 0,
339
+                    status: Status::Idle
340
+                },
341
+                Worker {
342
+                    id: 2,
343
+                    status: Status::Idle
344
+                },
345
+            ]
346
+        )
347
+    }
348
+
349
+    #[test]
350
+    fn run_workers_one_second() {
351
+        let mut pool = WorkerPool {
352
+            workers: vec![
353
+                Worker {
354
+                    id: 0,
355
+                    status: Status::Idle,
356
+                },
357
+                Worker {
358
+                    id: 1,
359
+                    status: Status::Working {
360
+                        step: 'A',
361
+                        remaining: 2,
362
+                    },
363
+                },
364
+                Worker {
365
+                    id: 2,
366
+                    status: Status::Idle,
367
+                },
368
+            ],
369
+        };
370
+        pool.run_one_second(&mut test_instructions(), &mut String::new());
371
+        assert_eq!(
372
+            pool,
373
+            WorkerPool {
374
+                workers: vec![
375
+                    Worker {
376
+                        id: 0,
377
+                        status: Status::Idle
378
+                    },
379
+                    Worker {
380
+                        id: 1,
381
+                        status: Status::Working {
382
+                            step: 'A',
383
+                            remaining: 1
384
+                        }
385
+                    },
386
+                    Worker {
387
+                        id: 2,
388
+                        status: Status::Idle
389
+                    },
390
+                ]
391
+            }
392
+        )
393
+    }
394
+
395
+    #[test]
396
+    fn run_workers_one_second_and_complete_step() {
397
+        let mut pool = WorkerPool {
398
+            workers: vec![Worker {
399
+                id: 0,
400
+                status: Status::Working {
401
+                    step: 'A',
402
+                    remaining: 1,
403
+                },
404
+            }],
405
+        };
406
+        let mut instructions = [('A', vec![])].iter().cloned().collect();
407
+        let mut sequence = "Z".to_string();
408
+        pool.run_one_second(&mut instructions, &mut sequence);
409
+        assert_eq!(
410
+            pool,
411
+            WorkerPool {
412
+                workers: vec![Worker {
413
+                    id: 0,
414
+                    status: Status::Idle
415
+                },]
416
+            }
417
+        );
418
+        assert_eq!(instructions, [('A', vec![])].iter().cloned().collect());
419
+        assert_eq!(sequence, "ZA".to_string());
420
+    }
421
+
422
+    #[test]
423
+    fn worker_pool_all_idle() {
424
+        assert_eq!(WorkerPool::new(3).all_idle(), true);
425
+    }
426
+
427
+    #[test]
428
+    fn worker_pool_not_all_idle() {
429
+        assert_eq!(
430
+            WorkerPool {
431
+                workers: vec![
432
+                    Worker {
433
+                        id: 0,
434
+                        status: Status::Idle
435
+                    },
436
+                    Worker {
437
+                        id: 1,
438
+                        status: Status::Working {
439
+                            step: 'A',
440
+                            remaining: 1
441
+                        }
442
+                    },
443
+                ],
444
+            }
445
+            .all_idle(),
446
+            false
447
+        );
448
+    }
449
+
450
+    #[test]
451
+    fn assign_step_to_worker_in_pool() {
452
+        let mut pool = WorkerPool::new(2);
453
+        pool.assign_worker(0, 'A');
454
+        assert_eq!(
455
+            pool,
456
+            WorkerPool {
457
+                workers: vec![
458
+                    Worker {
459
+                        id: 0,
460
+                        status: Status::Working {
461
+                            step: 'A',
462
+                            remaining: 61,
463
+                        }
464
+                    },
465
+                    Worker {
466
+                        id: 1,
467
+                        status: Status::Idle,
468
+                    }
469
+                ],
470
+            }
471
+        )
472
+    }
473
+
474
+    #[test]
475
+    fn gets_seconds_for_step() {
476
+        assert_eq!(get_seconds_for_step('A'), 61);
477
+        assert_eq!(get_seconds_for_step('B'), 62);
478
+        assert_eq!(get_seconds_for_step('Z'), 86);
479
+    }
480
+
481
+    #[test]
482
+    fn gets_sequence_with_workers() {
483
+        let mut pool = WorkerPool::new(2);
484
+        let mut instructions = test_instructions();
485
+        assert_eq!(get_parallel_step_sequence_seconds(&mut instructions, &mut pool), 258);
486
+    }
123 487
 }

+ 2 - 1
src/main.rs

@@ -31,5 +31,6 @@ fn main() {
31 31
     // println!("{}", day6::solve_part1().unwrap());
32 32
     // println!("{}", day6::solve_part2().unwrap());
33 33
     println!("Day 7:");
34
-    println!("{}", day7::solve_part1().unwrap());
34
+    // println!("{}", day7::solve_part1().unwrap());
35
+    println!("{}", day7::solve_part2().unwrap());
35 36
 }