{-# Language QuasiQuotes, TemplateHaskell, RankNTypes #-}
{-|
Module      : Main
Description : Day 23 solution
Copyright   : (c) Eric Mertens, 2017
License     : ISC
Maintainer  : emertens@gmail.com

<https://adventofcode.com/2017/day/23>

Part 1 is just a copy/paste of day 18

Part 2 was done with manual analysis. My program counted the number of
composite numbers in the range @[108100, 108117 .. 125100]@

-}
module Main where

import Advent        (format, stageTH)
import Data.Map      (Map)
import Text.Read     (readMaybe)
import qualified Data.Map as Map
import qualified Data.Vector as V

data C = Cset | Cjnz | Cmul | Csub
  deriving Int -> C -> ShowS
[C] -> ShowS
C -> String
(Int -> C -> ShowS) -> (C -> String) -> ([C] -> ShowS) -> Show C
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> C -> ShowS
showsPrec :: Int -> C -> ShowS
$cshow :: C -> String
show :: C -> String
$cshowList :: [C] -> ShowS
showList :: [C] -> ShowS
Show

stageTH

-- | >>> :main
-- 6241
main :: IO ()
IO ()
main =
  do Vector (C, Either Char Integer, Either Char Integer)
input <- [(C, Either Char Integer, Either Char Integer)]
-> Vector (C, Either Char Integer, Either Char Integer)
forall a. [a] -> Vector a
V.fromList ([(C, Either Char Integer, Either Char Integer)]
 -> Vector (C, Either Char Integer, Either Char Integer))
-> IO [(C, Either Char Integer, Either Char Integer)]
-> IO (Vector (C, Either Char Integer, Either Char Integer))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [format|2017 23 (@C (%a|%ld) (%a|%ld)%n)*|]
     let pgm :: p -> Int
pgm p
n = Vector (C, Either Char Integer, Either Char Integer) -> Int
runProgram Vector (C, Either Char Integer, Either Char Integer)
input
     Int -> IO ()
forall a. Show a => a -> IO ()
print (Integer -> Int
forall {p}. p -> Int
pgm Integer
0)

-- | Either lookup a register or return the value of a constant.
(!) ::
  Map Char Integer {- ^ registers          -} ->
  Either Char Integer {- ^ number or register -} ->
  Integer            {- ^ argument value     -}
Map Char Integer
m ! :: Map Char Integer -> Either Char Integer -> Integer
! Either Char Integer
k =
  case Either Char Integer
k of
    Right Integer
n -> Integer
n
    Left Char
v  -> Integer -> Char -> Map Char Integer -> Integer
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault Integer
0 Char
v Map Char Integer
m

runProgram ::
  V.Vector (C, Either Char Integer, Either Char Integer) {- ^ instructions -} ->
  Int               {- ^ multiplies   -}
runProgram :: Vector (C, Either Char Integer, Either Char Integer) -> Int
runProgram Vector (C, Either Char Integer, Either Char Integer)
cmds = Int -> Int -> Map Char Integer -> Int
forall {t}. Num t => t -> Int -> Map Char Integer -> t
step Int
0 Int
0 Map Char Integer
forall k a. Map k a
Map.empty
  where
    step :: t -> Int -> Map Char Integer -> t
step t
acc Int
pc Map Char Integer
regs =
      case Vector (C, Either Char Integer, Either Char Integer)
cmds Vector (C, Either Char Integer, Either Char Integer)
-> Int -> Maybe (C, Either Char Integer, Either Char Integer)
forall a. Vector a -> Int -> Maybe a
V.!? Int
pc of
        Maybe (C, Either Char Integer, Either Char Integer)
Nothing          -> t
acc
        Just (C
Cset,Left Char
x,Either Char Integer
y) -> t -> Int -> Map Char Integer -> t
step t
acc (Int
pcInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) (Char -> Integer -> Map Char Integer -> Map Char Integer
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Char
x (Map Char Integer
regsMap Char Integer -> Either Char Integer -> Integer
!Either Char Integer
y) Map Char Integer
regs)
        Just (C
Csub,Left Char
x,Either Char Integer
y) -> t -> Int -> Map Char Integer -> t
step t
acc (Int
pcInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) (Char -> Integer -> Map Char Integer -> Map Char Integer
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Char
x (Map Char Integer
regs Map Char Integer -> Char -> Integer
forall k a. Ord k => Map k a -> k -> a
Map.! Char
x Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Map Char Integer
regsMap Char Integer -> Either Char Integer -> Integer
!Either Char Integer
y) Map Char Integer
regs)
        Just (C
Cmul,Left Char
x,Either Char Integer
y) -> t -> Int -> Map Char Integer -> t
step (t
1t -> t -> t
forall a. Num a => a -> a -> a
+t
acc) (Int
pcInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) (Char -> Integer -> Map Char Integer -> Map Char Integer
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Char
x (Map Char Integer
regs Map Char Integer -> Char -> Integer
forall k a. Ord k => Map k a -> k -> a
Map.! Char
x Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Map Char Integer
regsMap Char Integer -> Either Char Integer -> Integer
!Either Char Integer
y) Map Char Integer
regs)
        Just (C
Cjnz,Either Char Integer
x,Either Char Integer
y) -> t -> Int -> Map Char Integer -> t
step t
acc (Int
pcInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
o) Map Char Integer
regs
          where
            o :: Int
o | Map Char Integer
regsMap Char Integer -> Either Char Integer -> Integer
!Either Char Integer
x Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= Integer
0 = Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Map Char Integer
regsMap Char Integer -> Either Char Integer -> Integer
!Either Char Integer
y)
              | Bool
otherwise  = Int
1