2019-12-07 08:40:38 +01:00
import Data.List.Split
import Data.Char as Char
import Data.List as List
2019-12-07 15:16:35 +01:00
import Data.Either as Either
2019-12-07 08:40:38 +01:00
main = do
software <- getList <$> getContents
--let output = operation state state 0 input []
2019-12-07 15:16:35 +01:00
let combs = getComb [ 5 , 6 , 7 , 8 , 9 ]
mapM putStrLn ( map show combs )
mapM putStrLn ( map show ( map ( \ x -> part2 $ prepareAmps x software ) combs ) )
2019-12-07 08:40:38 +01:00
-- let output = calcthruster software [4,3,2,1,0]
2019-12-07 15:16:35 +01:00
data Amplifier = Amplifier { state :: [ Int ]
, index :: Int
, input :: [ Int ]
, output :: [ Int ] }
type Outputtype = Either [ Int ] Amplifier
2019-12-07 08:40:38 +01:00
getList :: String -> [ Int ]
getList = map read . splitOn " , "
getComb :: [ Int ] -> [ [ Int ] ]
getComb array = filter ( ( 5 == ) . length . ( List . nub ) ) $ mapM ( const array ) [ 1 .. 5 ]
2019-12-07 15:16:35 +01:00
prepareAmps :: [ Int ] -> [ Int ] -> [ Amplifier ]
prepareAmps ( p1 : p2 : p3 : p4 : p5 : _ ) software = do
let a = step ( Amplifier software 0 [] [] ) [ p1 ]
let b = step ( Amplifier software 0 [] [] ) [ p2 ]
let c = step ( Amplifier software 0 [] [] ) [ p3 ]
let d = step ( Amplifier software 0 [] [] ) [ p4 ]
let e = step ( Amplifier software 0 [] [] ) [ p5 ]
let e = Amplifier ( state e ) ( index e ) ( input e ) [ 0 ]
[ a , b , c , d , e ]
part2 :: [ Amplifier ] -> Int
part2 amps = do
let ampA = link ( amps !! 4 ) ( amps !! 0 )
2019-12-07 16:44:43 +01:00
let ampB = link ( ampA ) ( amps !! 1 )
let ampC = link ( ampB ) ( amps !! 2 )
let ampD = link ( ampC ) ( amps !! 3 )
let ampE = link ( ampD ) ( amps !! 4 )
2019-12-07 15:16:35 +01:00
if state ampE == [ 88 ]
then head ( output ( amps !! 4 ) )
else part2 [ ampA , ampB , ampC , ampD , ampE ]
link :: Amplifier -> Amplifier -> Amplifier
link left calc
| length ( output left ) == 0 = Amplifier ( [ 88 ] ) ( index calc ) ( input calc ) ( output calc )
| otherwise = step calc [ last $ output left ]
step :: Amplifier -> [ Int ] -> Amplifier
step amp input = operation ( drop ( index amp ) ( state amp ) ) ( state amp ) ( index amp ) input []
2019-12-07 08:40:38 +01:00
2019-12-07 15:16:35 +01:00
calcthrusters :: [ Int ] -> [ Int ] -> Int -> Int
calcthrusters software ( p1 : p2 : p3 : p4 : p5 : _ ) start = do
let outputA = operation software software 0 [ p1 , start ] []
let outputB = operation software software 0 [ p2 , last $ output outputA ] []
let outputC = operation software software 0 [ p3 , last $ output outputB ] []
let outputD = operation software software 0 [ p4 , last $ output outputC ] []
let outputE = operation software software 0 [ p5 , last $ output outputD ] []
last $ output outputE
operation :: [ Int ] -> [ Int ] -> Int -> [ Int ] -> [ Int ] -> Amplifier
2019-12-07 16:44:43 +01:00
operation ( 99 : _ ) state i input output =
Amplifier state i input []
operation ( op : x : y : z : _ ) state i input output
2019-12-07 08:40:38 +01:00
| last ( digits op ) == 1 = do
2019-12-07 16:44:43 +01:00
let newindex = i + 4
2019-12-07 08:40:38 +01:00
let newstate = add ( fillup ( revertdigs op ) 5 ) x y z state
operation ( drop newindex newstate ) newstate newindex input output
| last ( digits op ) == 2 = do
2019-12-07 16:44:43 +01:00
let newindex = i + 4
2019-12-07 08:40:38 +01:00
let newstate = mult ( fillup ( revertdigs op ) 5 ) x y z state
2019-12-07 16:44:43 +01:00
operation ( drop newindex newstate ) newstate newindex input output
2019-12-07 08:40:38 +01:00
| last ( digits op ) == 3 = do
2019-12-07 16:44:43 +01:00
if length input == 0
then Amplifier state i input output
else do
let newindex = i + 2
let newstate = put ( fillup ( revertdigs op ) 3 ) x ( head input ) state
let newinput = drop 1 input
operation ( drop newindex newstate ) newstate newindex newinput output
2019-12-07 08:40:38 +01:00
| last ( digits op ) == 4 = do
2019-12-07 16:44:43 +01:00
let newindex = i + 2
2019-12-07 08:40:38 +01:00
let newoutput = out ( fillup ( revertdigs op ) 3 ) output x state
2019-12-07 15:16:35 +01:00
let newinput = drop 1 input
2019-12-07 08:40:38 +01:00
operation ( drop newindex state ) state newindex input newoutput
| ( last ( digits op ) == 5 ) = do
2019-12-07 16:44:43 +01:00
let newindex = jumpif ( fillup ( revertdigs op ) 4 ) x y i state
2019-12-07 08:40:38 +01:00
operation ( drop newindex state ) state newindex input output
| ( last ( digits op ) == 6 ) = do
2019-12-07 16:44:43 +01:00
let newindex = jumpifnot ( fillup ( revertdigs op ) 4 ) x y i state
2019-12-07 08:40:38 +01:00
operation ( drop newindex state ) state newindex input output
| ( last ( digits op ) == 7 ) = do
2019-12-07 16:44:43 +01:00
let newindex = i + 4
2019-12-07 08:40:38 +01:00
let newstate = lessthan ( fillup ( revertdigs op ) 5 ) x y z state
operation ( drop newindex newstate ) newstate newindex input output
| ( last ( digits op ) == 8 ) = do
2019-12-07 16:44:43 +01:00
let newindex = i + 4
2019-12-07 08:40:38 +01:00
let newstate = equal ( fillup ( revertdigs op ) 5 ) x y z state
operation ( drop newindex newstate ) newstate newindex input output
add :: [ Int ] -> Int -> Int -> Int -> [ Int ] -> [ Int ]
add ( op1 : op2 : m1 : m2 : m3 : _ ) p1 p2 p3 state =
Main . insert state sum p3
where
sum = ( getValue m1 p1 state ) + ( getValue m2 p2 state )
mult :: [ Int ] -> Int -> Int -> Int -> [ Int ] -> [ Int ]
mult ( op1 : op2 : m1 : m2 : m3 : _ ) p1 p2 p3 state =
Main . insert state sum p3
where
sum = ( getValue m1 p1 state ) * ( getValue m2 p2 state )
put :: [ Int ] -> Int -> Int -> [ Int ] -> [ Int ]
put ( op1 : op2 : m1 : _ ) p1 input state =
Main . insert state input p1
out :: [ Int ] -> [ Int ] -> Int -> [ Int ] -> [ Int ]
out ( op1 : op2 : m1 : _ ) output p1 state =
output ++ [ ( getValue m1 p1 state ) ]
jumpif :: [ Int ] -> Int -> Int -> Int -> [ Int ] -> Int
jumpif ( op1 : op2 : m1 : m2 : _ ) p1 p2 index state
| ( getValue m1 p1 state ) /= 0 = getValue m2 p2 state
| otherwise = index + 3
jumpifnot :: [ Int ] -> Int -> Int -> Int -> [ Int ] -> Int
jumpifnot ( op1 : op2 : m1 : m2 : _ ) p1 p2 index state
| ( getValue m1 p1 state ) == 0 = getValue m2 p2 state
| otherwise = index + 3
lessthan :: [ Int ] -> Int -> Int -> Int -> [ Int ] -> [ Int ]
lessthan ( op1 : op2 : m1 : m2 : m3 : _ ) p1 p2 p3 state
| ( getValue m1 p1 state ) < ( getValue m2 p2 state ) = Main . insert state 1 p3
| otherwise = Main . insert state 0 p3
equal :: [ Int ] -> Int -> Int -> Int -> [ Int ] -> [ Int ]
equal ( op1 : op2 : m1 : m2 : m3 : _ ) p1 p2 p3 state
| ( getValue m1 p1 state ) == ( getValue m2 p2 state ) = Main . insert state 1 p3
| otherwise = Main . insert state 0 p3
insert :: [ Int ] -> Int -> Int -> [ Int ]
insert xs value index = do
let split = splitAt index xs
( fst split ) ++ [ value ] ++ ( drop 1 ( snd split ) )
digits :: Int -> [ Int ]
digits = map Char . digitToInt . show
revertdigs :: Int -> [ Int ]
revertdigs 0 = []
revertdigs x = x ` mod ` 10 : revertdigs ( x ` div ` 10 )
fillup :: [ Int ] -> Int -> [ Int ]
fillup array x = array ++ ( replicate ( x - ( length array ) ) 0 )
getValue :: Int -> Int -> [ Int ] -> Int
getValue 0 index array = array !! index
getValue 1 index array = index