Skip to content

Commit

Permalink
2017 day 16
Browse files Browse the repository at this point in the history
  • Loading branch information
ictrobot committed Nov 2, 2024
1 parent b8746ea commit be6fa02
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 0 deletions.
1 change: 1 addition & 0 deletions crates/utils/src/parser/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ impl<I: Integer + Parseable> Parser for NumberRange<I> {
/// Ok((123u8, &b", 120"[..]))
/// );
/// ```
#[inline]
#[must_use]
pub fn number_range<I: Integer + Parseable>(range: RangeInclusive<I>) -> NumberRange<I> {
let min = *range.start();
Expand Down
1 change: 1 addition & 0 deletions crates/utils/src/parser/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ impl Parser for ByteRange {
/// Ok((b'h', &b"ello world"[..]))
/// );
/// ```
#[inline]
#[must_use]
pub fn byte_range(range: RangeInclusive<u8>) -> ByteRange {
let min = *range.start();
Expand Down
130 changes: 130 additions & 0 deletions crates/year2017/src/day16.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use std::array;
use std::fmt::{Display, Formatter, Write};
use std::ops::MulAssign;
use utils::prelude::*;

/// Composing permutations.
///
/// The key optimization is that all the index-based permutations (Spin and Exchange) can be
/// combined into one permutation, and all the value-based permutations (Partner) can be combined
/// into another.
#[derive(Clone, Debug)]
pub struct Day16 {
dance: Dance,
}

#[derive(Clone, Debug)]
enum DanceMove {
Spin(u8),
Exchange(u8, u8),
Partner(u8, u8),
}

#[derive(Copy, Clone, Debug)]
struct Dance {
index_perm: [usize; 16],
value_perm: [usize; 16],
}

impl Day16 {
pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
let program = parser::byte_range(b'a'..=b'p').map(|b| b - b'a');
let position = parser::number_range(0..=15);

Ok(Self {
dance: parser::one_of((
position.with_prefix(b's').map(DanceMove::Spin),
position
.with_prefix(b'x')
.then(position.with_prefix(b'/'))
.map(|(a, b)| DanceMove::Exchange(a, b)),
program
.with_prefix(b'p')
.then(program.with_prefix(b'/'))
.map(|(a, b)| DanceMove::Partner(a, b)),
))
.with_suffix(b','.or(parser::eof()))
.parse_iterator(input)
.collect::<Result<Dance, InputError>>()?,
})
}

#[must_use]
pub fn part1(&self) -> String {
self.dance.to_string()
}

#[must_use]
pub fn part2(&self) -> String {
// Exponentiation by squaring, but for dance permutations
let mut result = Dance::default();
let mut base = self.dance;
let mut exponent = 1_000_000_000;

while exponent > 0 {
if exponent % 2 == 1 {
result *= base;
}
exponent >>= 1;
base *= base;
}

result.to_string()
}
}

impl Default for Dance {
fn default() -> Self {
Dance {
index_perm: array::from_fn(|i| i),
value_perm: array::from_fn(|i| i),
}
}
}

impl FromIterator<DanceMove> for Dance {
#[inline]
fn from_iter<T: IntoIterator<Item = DanceMove>>(iter: T) -> Self {
let mut dance = Dance::default();
let mut value_positions: [usize; 16] = array::from_fn(|i| i);
let mut rotation = 0; // Rotate once at the end as it is expensive

for dance_move in iter {
match dance_move {
DanceMove::Spin(r) => rotation += 16 - r as usize,
DanceMove::Exchange(a, b) => {
let a_idx = (a as usize + rotation) % 16;
let b_idx = (b as usize + rotation) % 16;
dance.index_perm.swap(a_idx, b_idx);
}
DanceMove::Partner(a, b) => {
value_positions.swap(a as usize, b as usize);
let a_idx = value_positions[a as usize];
let b_idx = value_positions[b as usize];
dance.value_perm.swap(a_idx, b_idx);
}
}
}
dance.index_perm.rotate_left(rotation % 16);

dance
}
}

impl Display for Dance {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for &i in &self.index_perm {
f.write_char((self.value_perm[i] as u8 + b'a') as char)?;
}
Ok(())
}
}

impl MulAssign for Dance {
fn mul_assign(&mut self, rhs: Self) {
self.index_perm = self.index_perm.map(|i| rhs.index_perm[i]);
self.value_perm = self.value_perm.map(|i| rhs.value_perm[i]);
}
}

examples!(Day16 -> (&'static str, &'static str) []);
1 change: 1 addition & 0 deletions crates/year2017/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ utils::year!(2017 => year2017, ${
13 => day13::Day13,
14 => day14::Day14,
15 => day15::Day15,
16 => day16::Day16,
});

0 comments on commit be6fa02

Please sign in to comment.