Skip to content

Commit be6fa02

Browse files
committed
2017 day 16
1 parent b8746ea commit be6fa02

File tree

4 files changed

+133
-0
lines changed

4 files changed

+133
-0
lines changed

crates/utils/src/parser/number.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ impl<I: Integer + Parseable> Parser for NumberRange<I> {
128128
/// Ok((123u8, &b", 120"[..]))
129129
/// );
130130
/// ```
131+
#[inline]
131132
#[must_use]
132133
pub fn number_range<I: Integer + Parseable>(range: RangeInclusive<I>) -> NumberRange<I> {
133134
let min = *range.start();

crates/utils/src/parser/simple.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ impl Parser for ByteRange {
7474
/// Ok((b'h', &b"ello world"[..]))
7575
/// );
7676
/// ```
77+
#[inline]
7778
#[must_use]
7879
pub fn byte_range(range: RangeInclusive<u8>) -> ByteRange {
7980
let min = *range.start();

crates/year2017/src/day16.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use std::array;
2+
use std::fmt::{Display, Formatter, Write};
3+
use std::ops::MulAssign;
4+
use utils::prelude::*;
5+
6+
/// Composing permutations.
7+
///
8+
/// The key optimization is that all the index-based permutations (Spin and Exchange) can be
9+
/// combined into one permutation, and all the value-based permutations (Partner) can be combined
10+
/// into another.
11+
#[derive(Clone, Debug)]
12+
pub struct Day16 {
13+
dance: Dance,
14+
}
15+
16+
#[derive(Clone, Debug)]
17+
enum DanceMove {
18+
Spin(u8),
19+
Exchange(u8, u8),
20+
Partner(u8, u8),
21+
}
22+
23+
#[derive(Copy, Clone, Debug)]
24+
struct Dance {
25+
index_perm: [usize; 16],
26+
value_perm: [usize; 16],
27+
}
28+
29+
impl Day16 {
30+
pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
31+
let program = parser::byte_range(b'a'..=b'p').map(|b| b - b'a');
32+
let position = parser::number_range(0..=15);
33+
34+
Ok(Self {
35+
dance: parser::one_of((
36+
position.with_prefix(b's').map(DanceMove::Spin),
37+
position
38+
.with_prefix(b'x')
39+
.then(position.with_prefix(b'/'))
40+
.map(|(a, b)| DanceMove::Exchange(a, b)),
41+
program
42+
.with_prefix(b'p')
43+
.then(program.with_prefix(b'/'))
44+
.map(|(a, b)| DanceMove::Partner(a, b)),
45+
))
46+
.with_suffix(b','.or(parser::eof()))
47+
.parse_iterator(input)
48+
.collect::<Result<Dance, InputError>>()?,
49+
})
50+
}
51+
52+
#[must_use]
53+
pub fn part1(&self) -> String {
54+
self.dance.to_string()
55+
}
56+
57+
#[must_use]
58+
pub fn part2(&self) -> String {
59+
// Exponentiation by squaring, but for dance permutations
60+
let mut result = Dance::default();
61+
let mut base = self.dance;
62+
let mut exponent = 1_000_000_000;
63+
64+
while exponent > 0 {
65+
if exponent % 2 == 1 {
66+
result *= base;
67+
}
68+
exponent >>= 1;
69+
base *= base;
70+
}
71+
72+
result.to_string()
73+
}
74+
}
75+
76+
impl Default for Dance {
77+
fn default() -> Self {
78+
Dance {
79+
index_perm: array::from_fn(|i| i),
80+
value_perm: array::from_fn(|i| i),
81+
}
82+
}
83+
}
84+
85+
impl FromIterator<DanceMove> for Dance {
86+
#[inline]
87+
fn from_iter<T: IntoIterator<Item = DanceMove>>(iter: T) -> Self {
88+
let mut dance = Dance::default();
89+
let mut value_positions: [usize; 16] = array::from_fn(|i| i);
90+
let mut rotation = 0; // Rotate once at the end as it is expensive
91+
92+
for dance_move in iter {
93+
match dance_move {
94+
DanceMove::Spin(r) => rotation += 16 - r as usize,
95+
DanceMove::Exchange(a, b) => {
96+
let a_idx = (a as usize + rotation) % 16;
97+
let b_idx = (b as usize + rotation) % 16;
98+
dance.index_perm.swap(a_idx, b_idx);
99+
}
100+
DanceMove::Partner(a, b) => {
101+
value_positions.swap(a as usize, b as usize);
102+
let a_idx = value_positions[a as usize];
103+
let b_idx = value_positions[b as usize];
104+
dance.value_perm.swap(a_idx, b_idx);
105+
}
106+
}
107+
}
108+
dance.index_perm.rotate_left(rotation % 16);
109+
110+
dance
111+
}
112+
}
113+
114+
impl Display for Dance {
115+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
116+
for &i in &self.index_perm {
117+
f.write_char((self.value_perm[i] as u8 + b'a') as char)?;
118+
}
119+
Ok(())
120+
}
121+
}
122+
123+
impl MulAssign for Dance {
124+
fn mul_assign(&mut self, rhs: Self) {
125+
self.index_perm = self.index_perm.map(|i| rhs.index_perm[i]);
126+
self.value_perm = self.value_perm.map(|i| rhs.value_perm[i]);
127+
}
128+
}
129+
130+
examples!(Day16 -> (&'static str, &'static str) []);

crates/year2017/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ utils::year!(2017 => year2017, ${
1919
13 => day13::Day13,
2020
14 => day14::Day14,
2121
15 => day15::Day15,
22+
16 => day16::Day16,
2223
});

0 commit comments

Comments
 (0)