aoc/year2016/day22.rs
1//! # Grid Computing
2//!
3//! Part two is a decoy that can be solved in constant time with some analysis.
4//! Printing the actual node layout shows a structure similar to:
5//!
6//! ```none
7//! O......G
8//! ........
9//! ..######
10//! ........
11//! .....-..
12//! ```
13//!
14//! * `O` is our destination
15//! * `G` is the data
16//! * `#` are large nodes that can't be moved to neighbours, effectively acting as walls.
17//! * `-` is the empty node.
18//!
19//! First we move the empty spot in front of the data:
20//!
21//! ```none
22//! O>>>>>>G
23//! .^......
24//! .^######
25//! .^......
26//! .<<<<-..
27//! ```
28//!
29//! Then we move the data into the empty spot.
30//!
31//! ```none
32//! O.....G_
33//! ........
34//! ..######
35//! ........
36//! ........
37//! ```
38//!
39//! Finally we move the data to the origin by repeating the same sequence of 5 moves.
40//! First moving the empty spot back around to in front of the data in 4 moves.
41//!
42//! ```none
43//! O....^G_
44//! .....^<v
45//! ..######
46//! ........
47//! ........
48//! ```
49//!
50//! Then moving the data another spot to the left.
51//!
52//! ```none
53//! O....G_.
54//! ........
55//! ..######
56//! ........
57//! ........
58//! ```
59//!
60//! To find the minimum number of steps we only need to find the `(x, y)` coordinates of the empty
61//! spot and the width of the wall, then add up the sequence of moves.
62use crate::util::iter::*;
63use crate::util::parse::*;
64
65pub struct Node {
66 x: u32,
67 y: u32,
68 used: u32,
69 avail: u32,
70}
71
72pub fn parse(input: &str) -> Vec<Node> {
73 input
74 .iter_unsigned()
75 .chunk::<6>()
76 .map(|[x, y, _, used, avail, _]| Node { x, y, used, avail })
77 .collect()
78}
79
80/// Filter the used and available space in ascending order to find the viable pairs efficiently.
81pub fn part1(input: &[Node]) -> usize {
82 let mut used: Vec<_> = input.iter().map(|n| n.used).filter(|&n| n > 0).collect();
83 used.sort_unstable();
84
85 let mut avail: Vec<_> = input.iter().map(|n| n.avail).collect();
86 avail.sort_unstable();
87
88 let mut i = 0;
89 let mut viable = 0;
90
91 for next in used {
92 while i < avail.len() && avail[i] < next {
93 i += 1;
94 }
95 viable += avail.len() - i;
96 }
97
98 viable
99}
100
101pub fn part2(input: &[Node]) -> u32 {
102 let mut width = 0;
103 let mut empty_x = 0;
104 let mut empty_y = 0;
105 let mut wall_x = u32::MAX;
106
107 for &Node { x, y, used, .. } in input {
108 width = width.max(x + 1);
109
110 if used == 0 {
111 empty_x = x;
112 empty_y = y;
113 }
114
115 // Large nodes are bigger than 100T.
116 if used >= 100 {
117 wall_x = wall_x.min(x - 1);
118 }
119 }
120
121 // Move left to avoid wall.
122 let a = empty_x - wall_x;
123 // Move up to first row.
124 let b = empty_y;
125 // Move right to spot in front of data.
126 let c = width - 2 - wall_x;
127 // Move data into empty spot.
128 let d = 1;
129 // Repeatedly move empty spot 4 places around from behind data then move data one spot left.
130 let e = 5 * (width - 2);
131
132 a + b + c + d + e
133}