2020-12-08 10:58:41 +01:00
#![ feature(test, str_split_once) ]
extern crate test ;
2020-12-10 14:44:42 +01:00
use aoc2020 ::common ::* ;
2020-12-08 10:58:41 +01:00
#[ derive(Debug, PartialEq) ]
enum Command {
NOP ( i32 ) ,
ACC ( i32 ) ,
JMP ( i32 ) ,
}
fn read_input ( ) -> String {
2020-12-10 14:44:42 +01:00
read_file ( 8 )
2020-12-08 10:58:41 +01:00
}
fn parse_input ( raw : & str ) -> Vec < Command > {
raw . lines ( )
. map ( | l | match l . split_once ( ' ' ) {
Some ( ( " nop " , x ) ) = > Command ::NOP ( x . parse ( ) . unwrap ( ) ) ,
Some ( ( " acc " , x ) ) = > Command ::ACC ( x . parse ( ) . unwrap ( ) ) ,
Some ( ( " jmp " , x ) ) = > Command ::JMP ( x . parse ( ) . unwrap ( ) ) ,
_ = > unreachable! ( ) ,
} )
. collect ( )
}
fn main ( ) {
let commands = parse_input ( & read_input ( ) ) ;
println! ( " Part 1: {} " , part1 ( & commands ) ) ;
2020-12-10 12:44:07 +01:00
println! (
" Part 2: {} " ,
part2 ( & commands , & mut vec! [ false ; commands . len ( ) ] , 0 , 0 , false ) . unwrap ( )
) ;
2020-12-08 10:58:41 +01:00
}
fn part1 ( commands : & Vec < Command > ) -> i32 {
let mut seen = vec! [ false ; commands . len ( ) ] ;
let mut index = 0 i32 ;
let mut acc = 0 ;
loop {
if seen [ index as usize ] {
return acc ;
}
seen [ index as usize ] = true ;
match commands [ index as usize ] {
Command ::NOP ( _ ) = > index + = 1 ,
Command ::ACC ( x ) = > {
acc + = x ;
index + = 1
}
Command ::JMP ( x ) = > index + = x ,
}
}
}
2020-12-08 13:17:12 +01:00
fn part2 ( commands : & Vec < Command > , seen : & mut Vec < bool > , mut index : i32 , mut acc : i32 , changed : bool ) -> Option < i32 > {
2020-12-08 10:58:41 +01:00
loop {
if index as usize > = commands . len ( ) {
return Some ( acc ) ;
}
2020-12-08 13:17:12 +01:00
if changed & & seen [ index as usize ] {
2020-12-08 10:58:41 +01:00
return None ;
}
seen [ index as usize ] = true ;
match commands [ index as usize ] {
Command ::NOP ( x ) = > {
2020-12-08 15:03:09 +01:00
if ! changed & & index > - x {
2020-12-08 13:17:12 +01:00
if let Some ( n ) = part2 ( commands , seen , index + x , acc , true ) {
2020-12-08 10:58:41 +01:00
return Some ( n ) ;
}
}
index + = 1 ;
}
Command ::ACC ( x ) = > {
acc + = x ;
index + = 1
}
Command ::JMP ( x ) = > {
// is there no way to have regular if and if let in one statement?
if ! changed {
2020-12-08 13:17:12 +01:00
if let Some ( n ) = part2 ( commands , seen , index + 1 , acc , true ) {
2020-12-08 10:58:41 +01:00
return Some ( n ) ;
}
}
index + = x ;
}
}
}
}
#[ cfg(test) ]
mod tests {
use super ::* ;
use test ::black_box ;
const TEST_INPUT : & str = " nop +0
acc + 1
jmp + 4
acc + 3
jmp - 3
acc - 99
acc + 1
jmp - 4
acc + 6 " ;
#[ test ]
fn part1_test ( ) {
let commands = parse_input ( TEST_INPUT ) ;
assert_eq! ( part1 ( & commands ) , 5 ) ;
}
#[ test ]
fn part2_test ( ) {
let commands = parse_input ( TEST_INPUT ) ;
2020-12-08 13:17:12 +01:00
assert_eq! ( part2 ( & commands , & mut vec! [ false ; commands . len ( ) ] , 0 , 0 , false ) , Some ( 8 ) ) ;
2020-12-08 10:58:41 +01:00
}
#[ bench ]
fn bench_part1 ( b : & mut test ::Bencher ) {
let commands = parse_input ( & read_input ( ) ) ;
b . iter ( | | assert_eq! ( part1 ( black_box ( & commands ) ) , 1317 ) ) ;
}
#[ bench ]
fn bench_part2 ( b : & mut test ::Bencher ) {
let commands = parse_input ( & read_input ( ) ) ;
2020-12-10 12:44:07 +01:00
b . iter ( | | {
assert_eq! (
part2 ( black_box ( & commands ) , & mut vec! [ false ; commands . len ( ) ] , 0 , 0 , false ) ,
Some ( 1033 )
)
} ) ;
2020-12-08 10:58:41 +01:00
}
#[ bench ]
fn bench_input_parsing ( b : & mut test ::Bencher ) {
let raw = read_input ( ) ;
b . iter ( | | assert_eq! ( parse_input ( black_box ( & raw ) ) . len ( ) , 626 ) )
}
}