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

<https://adventofcode.com/2019/day/11>

-}
module Main (main) where

import Advent.Format (format)
import Advent.Coord (Coord, turnLeft, turnRight, origin, north, drawPicture)
import Data.Map (Map)
import Data.Map qualified as Map
import Intcode (Effect(..), run, new)

-- |
-- >>> :main
-- 1876
-- ░░██░░░██░░███░░░░██░░██░░░██░░░██░░█░░░░░·
-- ·█░░█░█░░█░█░░█░░░░█░█░░█░█░░█░█░░█░█░░░░░░
-- ·█░░░░█░░░░█░░█░░░░█░█░░░░█░░░░█░░░░█░░░░░░
-- ░█░░░░█░██░███░░░░░█░█░░░░█░██░█░░░░█░░░░░·
-- ░█░░█░█░░█░█░░░░█░░█░█░░█░█░░█░█░░█░█░░░░··
-- ·░██░░░███░█░░░░░██░░░██░░░███░░██░░████░··
main :: IO ()
IO ()
main =
 do [Int]
inp <- [format|2019 11 %d&,%n|]
    let start :: Map Coord Int -> Map Coord Int
start  = Coord -> Coord -> Effect -> Map Coord Int -> Map Coord Int
robot Coord
origin Coord
north (Machine -> Effect
run ([Int] -> Machine
new [Int]
inp))
        run1 :: Map Coord Int
run1   = Map Coord Int -> Map Coord Int
start Map Coord Int
forall k a. Map k a
Map.empty
        run2 :: Map Coord Int
run2   = Map Coord Int -> Map Coord Int
start (Coord -> Int -> Map Coord Int
forall k a. k -> a -> Map k a
Map.singleton Coord
origin Int
1)
        render :: Map Coord Int -> IO ()
render = String -> IO ()
putStr (String -> IO ())
-> (Map Coord Int -> String) -> Map Coord Int -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map Coord Char -> String
drawPicture (Map Coord Char -> String)
-> (Map Coord Int -> Map Coord Char) -> Map Coord Int -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Char) -> Map Coord Int -> Map Coord Char
forall a b. (a -> b) -> Map Coord a -> Map Coord b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Int -> Char
paintChar
    Int -> IO ()
forall a. Show a => a -> IO ()
print (Map Coord Int -> Int
forall a. Map Coord a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length Map Coord Int
run1)
    Map Coord Int -> IO ()
render Map Coord Int
run2

-- | Run a painter robot to see what it paints.
robot ::
  Coord         {- ^ robot's location             -} ->
  Coord         {- ^ robot's direction            -} ->
  Effect        {- ^ control program effect       -} ->
  Map Coord Int {- ^ starting painted coordinates -} ->
  Map Coord Int {- ^ final painted coordinates    -}
robot :: Coord -> Coord -> Effect -> Map Coord Int -> Map Coord Int
robot Coord
here Coord
dir Effect
effect Map Coord Int
paint =
  case Effect
effect of
    Effect
Halt -> Map Coord Int
paint

    Input Int -> Effect
f -> Coord -> Coord -> Effect -> Map Coord Int -> Map Coord Int
robot Coord
here Coord
dir Effect
effect' Map Coord Int
paint
      where
        color :: Int
color   = Int -> Coord -> Map Coord Int -> Int
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault Int
0 Coord
here Map Coord Int
paint
        effect' :: Effect
effect' = Int -> Effect
f Int
color

    Output Int
color (Output Int
turn Effect
effect') -> Coord -> Coord -> Effect -> Map Coord Int -> Map Coord Int
robot Coord
here' Coord
dir' Effect
effect' Map Coord Int
paint'
      where
        paint' :: Map Coord Int
paint' = Coord -> Int -> Map Coord Int -> Map Coord Int
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Coord
here Int
color Map Coord Int
paint
        dir' :: Coord
dir'   = Int -> Coord -> Coord
turnFn Int
turn Coord
dir
        here' :: Coord
here'  = Coord
here Coord -> Coord -> Coord
forall a. Num a => a -> a -> a
+ Coord
dir'

    Effect
_ -> String -> Map Coord Int
forall a. HasCallStack => String -> a
error String
"Bad program"

-- | Compute the turn function given a robot's output.
turnFn :: Int {- ^ robot turn output -} -> Coord -> Coord
turnFn :: Int -> Coord -> Coord
turnFn Int
0 = Coord -> Coord
turnLeft
turnFn Int
1 = Coord -> Coord
turnRight
turnFn Int
x = String -> Coord -> Coord
forall a. HasCallStack => String -> a
error (String
"Unexpected turn command: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
x)

-- | Character representation of paint number.
paintChar :: Int -> Char
paintChar :: Int -> Char
paintChar Int
0 = Char
'░'
paintChar Int
1 = Char
'█'
paintChar Int
x = String -> Char
forall a. HasCallStack => String -> a
error (String
"Unexpected paint color: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
x)