2019-12-27 16:35:28 +01:00
use std ::io ::{ self , BufRead } ;
2019-12-28 13:18:24 +01:00
enum ShuffleOperation {
CutLeft ( usize ) ,
CutRight ( usize ) ,
DealWithIncrement ( usize ) ,
Reverse ,
}
fn parse_operation ( operation : & str ) -> ShuffleOperation {
2019-12-27 16:35:28 +01:00
let words : Vec < _ > = operation . split ( ' ' ) . collect ( ) ;
match words [ 0 ] {
" cut " = > {
let offset : isize = words [ 1 ] . parse ( ) . expect ( " Not a valid number for cutting " ) ;
if offset < 0 {
2019-12-28 13:18:24 +01:00
ShuffleOperation ::CutRight ( offset . abs ( ) as usize )
2019-12-27 16:35:28 +01:00
} else {
2019-12-28 13:18:24 +01:00
ShuffleOperation ::CutLeft ( offset as usize )
2019-12-27 16:35:28 +01:00
}
}
" deal " = > match words [ 1 ] {
// with increment
2019-12-28 13:18:24 +01:00
" with " = > ShuffleOperation ::DealWithIncrement ( words [ 3 ] . parse ( ) . unwrap ( ) ) ,
2019-12-27 16:35:28 +01:00
// into new stack
2019-12-28 13:18:24 +01:00
" into " = > ShuffleOperation ::Reverse ,
2019-12-27 16:35:28 +01:00
_ = > unreachable! ( " Unknown deal command " ) ,
} ,
_ = > unreachable! ( " Unknown shuffle command " ) ,
2019-12-28 13:18:24 +01:00
}
}
fn apply ( deck : & mut Vec < usize > , operation : & str ) {
match parse_operation ( operation ) {
ShuffleOperation ::Reverse = > deck . reverse ( ) ,
ShuffleOperation ::CutLeft ( offset ) = > {
let clone = deck . clone ( ) ;
let ( left , right ) = clone . split_at ( offset as usize ) ;
* deck = right . to_vec ( ) ;
deck . append ( & mut left . to_vec ( ) ) ;
}
ShuffleOperation ::CutRight ( offset ) = > {
let offset = deck . len ( ) - offset ;
let cut : Vec < _ > = deck . drain ( 0 .. offset ) . collect ( ) ;
cut . into_iter ( ) . for_each ( | n | deck . push ( n ) ) ;
}
ShuffleOperation ::DealWithIncrement ( increment ) = > {
let length = deck . len ( ) ;
let old_deck = deck . clone ( ) ;
for i in 0 .. length {
deck [ ( i * increment ) % length ] = old_deck [ i ] ;
}
}
2019-12-27 16:35:28 +01:00
} ;
}
2019-12-28 13:18:24 +01:00
fn get_previous_index ( current : usize , operation : & str , deck_size : usize ) -> usize {
match parse_operation ( operation ) {
ShuffleOperation ::Reverse = > deck_size - current - 1 ,
ShuffleOperation ::CutLeft ( offset ) = > {
if current > = deck_size - offset {
current - ( deck_size - offset )
} else {
current + offset
}
}
ShuffleOperation ::CutRight ( offset ) = > {
if current < offset {
deck_size - offset + current
} else {
current - offset
}
}
ShuffleOperation ::DealWithIncrement ( increment ) = > ( 0 .. )
. filter_map ( | x | {
if ( x * deck_size + current ) % increment = = 0 {
Some ( ( x * deck_size + current ) / increment )
} else {
None
}
} )
. next ( )
. unwrap ( ) ,
}
}
2019-12-27 16:43:22 +01:00
fn create_deck ( len : usize ) -> Vec < usize > {
let mut deck = Vec ::with_capacity ( len ) ;
( 0 .. len ) . for_each ( | n | deck . push ( n ) ) ;
deck
}
2019-12-28 13:18:24 +01:00
fn get_original_index (
initial : usize ,
iterations : u128 ,
deck_size : usize ,
operations : & [ String ] ,
) -> u128 {
let mut last = initial ;
for operation in operations . iter ( ) . rev ( ) {
last = get_previous_index ( last , operation , deck_size ) ;
}
let drift = ( last - initial ) as u128 ;
( P2_INDEX as u128 + drift * iterations ) % deck_size as u128
}
2019-12-27 16:35:28 +01:00
const DECK_SIZE : usize = 10007 ;
2019-12-27 16:43:22 +01:00
const P2_DECK_SIZE : usize = 119315717514047 ;
2019-12-28 13:18:24 +01:00
const P2_ITERATIONS : u128 = 101741582076661 ;
const P2_INDEX : usize = 2020 ;
2019-12-27 16:35:28 +01:00
fn main ( ) {
let input : Vec < _ > = io ::stdin ( ) . lock ( ) . lines ( ) . map ( | l | l . unwrap ( ) ) . collect ( ) ;
2019-12-27 16:43:22 +01:00
let mut deck = create_deck ( DECK_SIZE ) ;
2019-12-28 13:18:24 +01:00
for operation in & input {
apply ( & mut deck , operation ) ;
2019-12-27 16:35:28 +01:00
}
2019-12-28 13:18:24 +01:00
println! ( " Part 1: {} " , deck . iter ( ) . position ( | & x | x = = 2019 ) . unwrap ( ) ) ;
let p2 = get_original_index ( P2_INDEX , P2_ITERATIONS , P2_DECK_SIZE , & input ) ;
println! ( " Part 2: {} " , p2 ) ;
2019-12-27 16:35:28 +01:00
}
mod tests {
use super ::* ;
2019-12-28 13:18:24 +01:00
const EXAMPLE_1 : & str = " deal with increment 7
deal into new stack
deal into new stack " ;
const EXAMPLE_2 : & str = " cut 6
deal with increment 7
deal into new stack " ;
const EXAMPLE_3 : & str = " deal with increment 7
deal with increment 9
cut - 2 " ;
const EXAMPLE_4 : & str = " 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 " ;
/// Example 4 but with an even number of reversals
const EXAMPLE_5 : & str = " deal into new stack
cut - 2
deal with increment 7
cut 8
cut - 4
deal with increment 7
cut 3
deal into new stack
deal with increment 9
deal with increment 3
cut - 1 " ;
2019-12-27 16:35:28 +01:00
#[ test ]
fn deal_test ( ) {
2019-12-27 16:43:22 +01:00
let mut deck = create_deck ( 10 ) ;
2019-12-28 13:18:24 +01:00
for line in EXAMPLE_1 . lines ( ) {
2019-12-27 16:35:28 +01:00
apply ( & mut deck , line ) ;
}
assert_eq! ( deck , & [ 0 , 3 , 6 , 9 , 2 , 5 , 8 , 1 , 4 , 7 ] ) ;
}
#[ test ]
fn cut_deal_test ( ) {
2019-12-27 16:43:22 +01:00
let mut deck = create_deck ( 10 ) ;
2019-12-28 13:18:24 +01:00
for line in EXAMPLE_2 . lines ( ) {
2019-12-27 16:35:28 +01:00
apply ( & mut deck , line ) ;
}
assert_eq! ( deck , & [ 3 , 0 , 7 , 4 , 1 , 8 , 5 , 2 , 9 , 6 ] ) ;
}
#[ test ]
fn test_all ( ) {
2019-12-27 16:43:22 +01:00
let mut deck = create_deck ( 10 ) ;
2019-12-28 13:18:24 +01:00
for line in EXAMPLE_4 . lines ( ) {
2019-12-27 16:35:28 +01:00
apply ( & mut deck , line ) ;
}
assert_eq! ( deck , & [ 9 , 2 , 5 , 8 , 1 , 4 , 7 , 0 , 3 , 6 ] ) ;
}
2019-12-28 13:18:24 +01:00
// tests for part 2
#[ test ]
fn reverse_stack_deal_test ( ) {
assert_eq! (
reverse_step ( " deal into new stack " , 10 ) ,
[ 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0 ]
) ;
}
#[ test ]
fn reverse_cut_test ( ) {
assert_eq! ( reverse_step ( " cut 3 " , 10 ) , [ 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 , 1 , 2 ] ) ;
assert_eq! ( reverse_step ( " cut -4 " , 10 ) , [ 6 , 7 , 8 , 9 , 0 , 1 , 2 , 3 , 4 , 5 ] ) ;
}
#[ test ]
fn reverse_increment_test ( ) {
assert_eq! (
reverse_step ( " deal with increment 3 " , 10 ) ,
[ 0 , 7 , 4 , 1 , 8 , 5 , 2 , 9 , 6 , 3 ]
) ;
}
#[ test ]
fn full_reverse_test1 ( ) {
assert_eq! ( index_before_instructions ( 0 , EXAMPLE_1 ) , 0 ) ;
assert_eq! ( index_before_instructions ( 2 , EXAMPLE_1 ) , 6 ) ;
}
#[ test ]
fn full_reverse_test2 ( ) {
assert_eq! ( index_before_instructions ( 0 , EXAMPLE_2 ) , 3 ) ;
assert_eq! ( index_before_instructions ( 6 , EXAMPLE_2 ) , 5 ) ;
}
#[ test ]
fn full_reverse_test3 ( ) {
assert_eq! ( index_before_instructions ( 0 , EXAMPLE_3 ) , 6 ) ;
assert_eq! ( index_before_instructions ( 9 , EXAMPLE_3 ) , 9 ) ;
}
#[ test ]
fn test_twice ( ) {
let mut deck = create_deck ( 10 ) ;
for line in EXAMPLE_5 . lines ( ) {
apply ( & mut deck , line ) ;
}
for line in EXAMPLE_5 . lines ( ) {
apply ( & mut deck , line ) ;
}
assert_eq! ( deck , & [ 2 , 1 , 0 , 9 , 8 , 7 , 6 , 5 , 4 , 3 ] ) ;
}
#[ test ]
fn interpolate_drift_test ( ) {
//get_original_index(0, 1, 10, EXAMPLE_4);
}
#[ test ]
fn full_reverse_test4 ( ) {
let instructions = EXAMPLE_4 ;
assert_eq! ( index_before_instructions ( 1 , instructions ) , 2 ) ;
}
fn index_before_instructions ( mut last : usize , instructions : & str ) -> usize {
for line in instructions . lines ( ) . rev ( ) {
last = get_previous_index ( last , line , 10 ) ;
}
last
}
fn reverse_step ( instruction : & str , len : usize ) -> Vec < usize > {
( 0 .. len )
. into_iter ( )
. map ( | n | get_previous_index ( n , instruction , 10 ) )
. collect ( )
}
2019-12-27 16:35:28 +01:00
}