Added Solution for 2019 day 22

This commit is contained in:
Burnus 2023-03-31 16:54:37 +02:00
parent ff7e66b259
commit 44759cc979
6 changed files with 454 additions and 0 deletions

View file

@ -0,0 +1,8 @@
[package]
name = "day22_slam_shuffle"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,170 @@
There isn't much to do while you wait for the droids to repair your ship. At least you're drifting in the right direction. You decide to practice a new [card shuffle](https://en.wikipedia.org/wiki/Shuffling) you've been working on.
Digging through the ship's storage, you find a deck of *space cards*! Just like any deck of space cards, there are 10007 cards in the deck numbered `0` through `10006`. The deck must be new - they're still in *factory order*, with `0` on the top, then `1`, then `2`, and so on, all the way through to `10006` on the bottom.
You've been practicing three different *techniques* that you use while shuffling. Suppose you have a deck of only 10 cards (numbered `0` through `9`):
*To `deal into new stack`*, create a new stack of cards by dealing the top card of the deck onto the top of the new stack repeatedly until you run out of cards:
```
Top Bottom
0 1 2 3 4 5 6 7 8 9 Your deck
New stack
1 2 3 4 5 6 7 8 9 Your deck
0 New stack
2 3 4 5 6 7 8 9 Your deck
1 0 New stack
3 4 5 6 7 8 9 Your deck
2 1 0 New stack
Several steps later...
9 Your deck
8 7 6 5 4 3 2 1 0 New stack
Your deck
9 8 7 6 5 4 3 2 1 0 New stack
```
Finally, pick up the new stack you've just created and use it as the deck for the next technique.
*To `cut N` cards*, take the top `N` cards off the top of the deck and move them as a single unit to the bottom of the deck, retaining their order. For example, to `cut 3`:
```
Top Bottom
0 1 2 3 4 5 6 7 8 9 Your deck
3 4 5 6 7 8 9 Your deck
0 1 2 Cut cards
3 4 5 6 7 8 9 Your deck
0 1 2 Cut cards
3 4 5 6 7 8 9 0 1 2 Your deck
```
You've also been getting pretty good at a version of this technique where `N` is negative! In that case, cut (the absolute value of) `N` cards from the bottom of the deck onto the top. For example, to `cut -4`:
```
Top Bottom
0 1 2 3 4 5 6 7 8 9 Your deck
0 1 2 3 4 5 Your deck
6 7 8 9 Cut cards
0 1 2 3 4 5 Your deck
6 7 8 9 Cut cards
6 7 8 9 0 1 2 3 4 5 Your deck
```
*To `deal with increment N`*, start by clearing enough space on your table to lay out all of the cards individually in a long line. Deal the top card into the leftmost position. Then, move `N` positions to the right and deal the next card there. If you would move into a position past the end of the space on your table, wrap around and keep counting from the leftmost card again. Continue this process until you run out of cards.
For example, to `deal with increment 3`:
```
0 1 2 3 4 5 6 7 8 9 Your deck
. . . . . . . . . . Space on table
^ Current position
Deal the top card to the current position:
1 2 3 4 5 6 7 8 9 Your deck
0 . . . . . . . . . Space on table
^ Current position
Move the current position right 3:
1 2 3 4 5 6 7 8 9 Your deck
0 . . . . . . . . . Space on table
^ Current position
Deal the top card:
2 3 4 5 6 7 8 9 Your deck
0 . . 1 . . . . . . Space on table
^ Current position
Move right 3 and deal:
3 4 5 6 7 8 9 Your deck
0 . . 1 . . 2 . . . Space on table
^ Current position
Move right 3 and deal:
4 5 6 7 8 9 Your deck
0 . . 1 . . 2 . . 3 Space on table
^ Current position
Move right 3, wrapping around, and deal:
5 6 7 8 9 Your deck
0 . 4 1 . . 2 . . 3 Space on table
^ Current position
And so on:
0 7 4 1 8 5 2 9 6 3 Space on table
```
Positions on the table which already contain cards are still counted; they're not skipped. Of course, this technique is carefully designed so it will never put two cards in the same position or leave a position empty.
Finally, collect the cards on the table so that the leftmost card ends up at the top of your deck, the card to its right ends up just below the top card, and so on, until the rightmost card ends up at the bottom of the deck.
The complete shuffle process (your puzzle input) consists of applying many of these techniques. Here are some examples that combine techniques; they all start with a *factory order* deck of 10 cards:
```
deal with increment 7
deal into new stack
deal into new stack
Result: 0 3 6 9 2 5 8 1 4 7
```
```
cut 6
deal with increment 7
deal into new stack
Result: 3 0 7 4 1 8 5 2 9 6
```
```
deal with increment 7
deal with increment 9
cut -2
Result: 6 3 0 7 4 1 8 5 2 9
```
```
deal into new stack
cut -2
deal with increment 7
cut 8
cut -4
deal with increment 7
cut 3
deal with increment 9
deal with increment 3
cut -1
Result: 9 2 5 8 1 4 7 0 3 6
```
Positions within the deck count from `0` at the top, then `1` for the card immediately below the top card, and so on to the bottom. (That is, cards start in the position matching their number.)
After shuffling your *factory order* deck of 10007 cards, *what is the position of card `2019`?*
To begin, [get your puzzle input](22/input).
Answer:

Binary file not shown.

View file

@ -0,0 +1,166 @@
use core::fmt::Display;
use std::{num::ParseIntError, collections::VecDeque};
#[derive(Debug, PartialEq, Eq)]
pub enum ParseError {
ParseIntError(std::num::ParseIntError),
LineMalformed(String),
}
impl From<ParseIntError> for ParseError {
fn from(value: ParseIntError) -> Self {
Self::ParseIntError(value)
}
}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ParseIntError(e) => write!(f, "Unable to parse into integer: {e}"),
Self::LineMalformed(v) => write!(f, "Line is malformed: {v}"),
}
}
}
enum Technique {
Rev,
Shift(isize),
Zip(usize),
}
impl TryFrom<&str> for Technique {
type Error = ParseError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let components: Vec<_> = value.split_whitespace().collect();
match components[1] {
"into" => Ok(Self::Rev),
"with" => Ok(Self::Zip(components[3].parse()?)),
n => Ok(Self::Shift(n.parse()?)),
}
}
}
impl Technique {
fn perform(&self, cards: &mut VecDeque<usize>) {
let len = cards.len();
match self {
Self::Rev => (0..len/2).for_each(|a| cards.swap(a, len-a-1)),
Self::Shift(i) if i >= &0 => cards.rotate_left(i.unsigned_abs()),
Self::Shift(n) => cards.rotate_right(n.unsigned_abs()),
Self::Zip(n) => {
let mut new: VecDeque<usize> = cards.clone();
(0..len).for_each(|idx| new[(n*idx)%len] = cards[idx]);
std::mem::swap(cards, &mut new);
},
}
}
fn get_parameters(&self, offset_diff: u128, increment_mul: u128, modulo: u128) -> (u128, u128) {
match self {
Self::Rev => ((offset_diff+modulo-increment_mul)%modulo, modulo-increment_mul),
Self::Shift(p) if p >= &0 => ((offset_diff+(p.unsigned_abs() as u128)*increment_mul)%modulo, increment_mul),
Self::Shift(n) => ((offset_diff+(modulo-(n.unsigned_abs()) as u128)*increment_mul)%modulo, increment_mul),
Self::Zip(i) => (offset_diff, (increment_mul*pow_mod(*i as u128, modulo-2, modulo))%modulo),
}
}
}
pub fn run_1(input: &str, cards: usize, target: usize) -> Result<usize, ParseError> {
let mut cards: VecDeque<usize> = (0..cards).collect();
let instructions: Vec<_> = input.lines().map(Technique::try_from).collect::<Result<Vec<_>, _>>()?;
instructions.iter().for_each(|instr| instr.perform(&mut cards));
Ok(cards.iter().position(|card| *card == target).unwrap())
}
pub fn run_2(input: &str, cards: u128, target: u128, iterations: u128) -> Result<u128, ParseError> {
let mut offset_diff = 0;
let mut increment_mul = 1;
let instructions: Vec<_> = input.lines().map(Technique::try_from).collect::<Result<Vec<_>, _>>()?;
for instruction in instructions {
(offset_diff, increment_mul) = instruction.get_parameters(offset_diff, increment_mul, cards);
}
let increment = pow_mod(increment_mul, iterations, cards);
let offset = (((cards*cards - offset_diff * (increment-1)) % cards) * pow_mod(cards+1-increment_mul, cards-2, cards))%cards;
Ok((offset+target*increment)%cards)
}
fn pow_mod(mantisse: u128, exponent: u128, modulo: u128) -> u128 {
match exponent {
0 => 1,
e if e % 2 == 0 => pow_mod((mantisse*mantisse) % modulo, exponent/2, modulo),
_ => (mantisse * pow_mod((mantisse * mantisse) % modulo, exponent/2, modulo)) % modulo,
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn pow_test() {
for mantisse in 10..23 {
for exponent in 5..14 {
for modulo in (3..1_000_003).step_by(1_000) {
eprintln!("Testing {mantisse}^{exponent}%{modulo}");
assert_eq!(pow_mod(mantisse, exponent as u128, modulo), mantisse.pow(exponent)%modulo);
}
}
}
}
#[test]
fn perform_test() {
let master: VecDeque<usize> = (0..10).collect();
let reversed = VecDeque::from([9, 8, 7, 6, 5, 4, 3, 2, 1, 0]);
let shifted = VecDeque::from([3, 4, 5, 6, 7, 8, 9, 0, 1, 2]);
let zipped = VecDeque::from([0, 7, 4, 1, 8, 5, 2, 9, 6, 3]);
let mut cards = master.clone();
Technique::Rev.perform(&mut cards);
assert_eq!(cards, reversed);
let mut cards = master.clone();
Technique::Shift(3).perform(&mut cards);
assert_eq!(cards, shifted);
let mut cards = master;
Technique::Zip(3).perform(&mut cards);
assert_eq!(cards, zipped);
}
#[test]
fn get_parameters_samples() {
assert_eq!(Technique::Rev.get_parameters(0, 1, 10), (9, 9));
assert_eq!(Technique::Shift(3).get_parameters(0, 1, 10), (3, 1));
assert_eq!(Technique::Shift(-4).get_parameters(0, 1, 10), (6, 1));
assert_eq!(Technique::Zip(7).get_parameters(0, 1, 11), (0, 8));
}
#[test]
fn test_sample() {
let sample_input = read_file("tests/sample_input");
assert_eq!(run_1(&sample_input, 10, 5), Ok(2));
}
#[test]
fn test_challenge_part_1() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run_1(&challenge_input, 10007, 2019), Ok(8191));
assert_eq!(run_2(&challenge_input, 10007, 2019, 1), Ok(1545));
}
#[test]
fn test_challenge_part_2() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run_2(&challenge_input, 119315717514047, 2020, 101741582076661), Ok(1644352419829));
}
}

View file

@ -0,0 +1,100 @@
deal with increment 41
cut 6859
deal with increment 23
cut -4435
deal into new stack
deal with increment 27
cut -337
deal with increment 50
cut 5290
deal into new stack
deal with increment 59
cut -9939
deal with increment 32
cut 4074
deal with increment 25
cut -2391
deal into new stack
deal with increment 40
cut -8095
deal with increment 44
cut 9150
deal with increment 5
cut -5330
deal with increment 61
cut -1038
deal with increment 3
cut 2873
deal with increment 56
cut 6080
deal with increment 59
cut -6859
deal with increment 21
cut -2316
deal with increment 42
cut -8349
deal with increment 60
cut 5774
deal with increment 63
cut -1754
deal with increment 48
cut 4009
deal with increment 10
cut -7026
deal with increment 73
cut 3867
deal into new stack
cut 3754
deal with increment 23
cut 4222
deal with increment 23
deal into new stack
cut 7294
deal into new stack
deal with increment 13
cut -9537
deal with increment 20
cut 2910
deal with increment 30
deal into new stack
cut 9409
deal with increment 23
deal into new stack
deal with increment 32
cut 6945
deal with increment 21
deal into new stack
cut -3297
deal with increment 75
cut -5300
deal into new stack
deal with increment 29
cut 8131
deal with increment 50
cut -8998
deal with increment 19
cut -1983
deal with increment 13
deal into new stack
cut -7555
deal with increment 62
cut 5612
deal with increment 14
cut -412
deal with increment 46
cut -7349
deal with increment 57
cut -8783
deal with increment 33
deal into new stack
deal with increment 56
cut 4283
deal into new stack
cut 8053
deal with increment 7
cut -2776
deal with increment 66
cut -9633
deal with increment 62
deal into new stack
deal with increment 12

View file

@ -0,0 +1,10 @@
deal into new stack
cut -2
deal with increment 7
cut 8
cut -4
deal with increment 7
cut 3
deal with increment 9
deal with increment 3
cut -1