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

<https://adventofcode.com/2020/day/8>

-}
module Main (main) where

import Advent (format, stageTH)
import Data.IntMap (IntMap)
import Data.IntMap qualified as IntMap

-- | Programs are expressed as control-flow graphs.
--
-- Nodes are instructions in the program.
--
-- Nodes are labeled with the accumulator-effect of executing that instruction.
--
-- Edges capture control flow between instructions.
--
-- Edges are labeled with the /cost/ of taking that edge. It costs @1@ to
-- take a control path generated from a toggled instruction.
data O = Onop | Ojmp | Oacc

stageTH

------------------------------------------------------------------------

-- |
-- >>> :main
-- 1200
-- 1023
main :: IO ()
IO ()
main =
  do [(O, Int)]
inp <- [format|2020 8 (@O (|%+)%d%n)*|]
     let pgm :: IntMap (O, Int)
pgm = [(Int, (O, Int))] -> IntMap (O, Int)
forall a. [(Int, a)] -> IntMap a
IntMap.fromList ([Int] -> [(O, Int)] -> [(Int, (O, Int))]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
0..] [(O, Int)]
inp)
     Int -> IO ()
forall a. Show a => a -> IO ()
print ((Int, Int) -> Int
forall a b. (a, b) -> b
snd (IntMap (O, Int) -> Int -> Int -> (Int, Int)
part1 IntMap (O, Int)
pgm Int
0 Int
0))
     Int -> IO ()
forall a. Show a => a -> IO ()
print (IntMap (O, Int) -> Int -> Int -> Int
part2 IntMap (O, Int)
pgm Int
0 Int
0)

part1 :: IntMap (O, Int) -> Int -> Int -> (Int,Int)
part1 :: IntMap (O, Int) -> Int -> Int -> (Int, Int)
part1 IntMap (O, Int)
pgm Int
ip Int
acc =
  let continue :: Int -> Int -> (Int, Int)
continue = IntMap (O, Int) -> Int -> Int -> (Int, Int)
part1 (Int -> IntMap (O, Int) -> IntMap (O, Int)
forall a. Int -> IntMap a -> IntMap a
IntMap.delete Int
ip IntMap (O, Int)
pgm) in
  case Int -> IntMap (O, Int) -> Maybe (O, Int)
forall a. Int -> IntMap a -> Maybe a
IntMap.lookup Int
ip IntMap (O, Int)
pgm of
    Maybe (O, Int)
Nothing        -> (Int
ip, Int
acc)
    Just (O
Onop, Int
_) -> Int -> Int -> (Int, Int)
continue (Int
ipInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) Int
acc
    Just (O
Oacc, Int
n) -> Int -> Int -> (Int, Int)
continue (Int
ipInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) (Int
accInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
n)
    Just (O
Ojmp, Int
n) -> Int -> Int -> (Int, Int)
continue (Int
ipInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
n) Int
acc

part2 :: IntMap (O, Int) -> Int -> Int -> Int
part2 :: IntMap (O, Int) -> Int -> Int -> Int
part2 IntMap (O, Int)
pgm Int
ip Int
acc =
  case IntMap (O, Int)
pgm IntMap (O, Int) -> Int -> (O, Int)
forall a. IntMap a -> Int -> a
IntMap.! Int
ip of
    (O
Onop, Int
n) -> (Int, Int) -> Int -> Int
forall {b}. (Int, b) -> b -> b
try (IntMap (O, Int) -> Int -> Int -> (Int, Int)
part1 IntMap (O, Int)
pgm (Int
ipInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
n) Int
acc) (IntMap (O, Int) -> Int -> Int -> Int
part2 IntMap (O, Int)
pgm (Int
ipInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) Int
acc)
    (O
Ojmp, Int
n) -> (Int, Int) -> Int -> Int
forall {b}. (Int, b) -> b -> b
try (IntMap (O, Int) -> Int -> Int -> (Int, Int)
part1 IntMap (O, Int)
pgm (Int
ipInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) Int
acc) (IntMap (O, Int) -> Int -> Int -> Int
part2 IntMap (O, Int)
pgm (Int
ipInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
n) Int
acc)
    (O
Oacc, Int
n) -> IntMap (O, Int) -> Int -> Int -> Int
part2 IntMap (O, Int)
pgm (Int
ipInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) (Int
accInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
n)
  where
    try :: (Int, b) -> b -> b
try (Int
ip', b
acc') b
e
      | Int
ip' Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== IntMap (O, Int) -> Int
forall a. IntMap a -> Int
IntMap.size IntMap (O, Int)
pgm = b
acc'
      | Bool
otherwise              = b
e