summaryrefslogtreecommitdiff
path: root/2019/13.hs
blob: 1f16a2c1e8877f347f5f9969a95c000d4f806c41 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
{-# LANGUAGE MultiWayIf #-}
module Main where

import Control.Concurrent
import Control.Monad
import Data.List
import qualified Data.Map.Strict as Map
import Data.Maybe
import System.IO

import Input
import IntCode


showGame :: Bool
showGame = False


blockBy :: Show a => Int -> [a] -> [[a]]
blockBy _ [] = []
blockBy n l = case splitAt n l of
                  (pre, post@(_:_)) -> pre : blockBy n post
                  (pre, []) -> [pre]

printMap :: (Integral i, Integral j) => Map.Map (i, i) j -> [String]
printMap mp =
    let keys = Map.keys mp
        (minx, maxx) = (minimum (map fst keys), maximum (map fst keys))
        (miny, maxy) = (minimum (map snd keys), maximum (map snd keys))
    in [[".#+=O" !! (fromIntegral (fromMaybe 0 (Map.lookup (x, y) mp)))
        | x <- [minx..maxx]]
       | y <- [miny..maxy]]

type Pos = (Int, Int)
data AI =
    AI { aiBall :: [Pos]
       , aiPad :: [Int]
       , aiScreen :: Map.Map Pos Int
       , aiScore :: Integer }

pos :: Integral i => (i, i) -> Pos
pos (a, b) = (fromIntegral a, fromIntegral b)

initAI :: AI
initAI = AI [] [] Map.empty (-1)

processOutput :: (Integer, Integer, Integer) -> AI -> AI
processOutput (-1, 0, score) ai = ai { aiScore = score }
processOutput (x, y, v) ai =
    let p = pos (x, y)
        removed = fromMaybe 0 (Map.lookup p (aiScreen ai))
        screen' = Map.insert (pos (x, y)) (fromIntegral v) (aiScreen ai)
        ball' = if removed == 4 then aiBall ai \\ [p] else aiBall ai
        ball'' = if v == 4 then p : ball' else ball'
        pad' = if removed == 3 then aiPad ai \\ [fromIntegral x] else aiPad ai
        pad'' = if v == 3 then fromIntegral x : pad' else pad'
    in ai { aiScreen = screen', aiBall = ball'', aiPad = pad'' }

provideInput :: AI -> Integer
provideInput ai =
    case (aiBall ai, aiPad ai) of
        ([(x, _)], [pad]) -> if | x < pad -> -1
                                | x > pad -> 1
                                | otherwise -> 0
        _ -> error "Ambiguous pad/ball!"

main :: IO ()
main = do
    program <- parse . head <$> getInput 13

    print $ let output = snd (run program [])
                bd = foldl (\mp [x,y,t] -> Map.insert (x, y) t mp)
                           Map.empty (blockBy 3 output)
            in length (filter (== 2) (Map.elems bd))

    when showGame $ putStr "\x1B[H\x1B[2J"

    let program' = 2 : tail program
        loop ai mcont output = do
            let ai1 = foldl' (\ai' [a,b,c] -> processOutput (a, b, c) ai') ai (blockBy 3 output)

            when showGame $ do
                -- putStr "\x1B[1;1H\x1B[2J"
                -- putStr (unlines (printMap (aiScreen ai1)))
                flip mapM_ (blockBy 3 output) $ \[x,y,v] ->
                    if x /= -1
                        then putStr ("\x1B[" ++ show (y + 1) ++ ";" ++ show (x + 1) ++ "H" ++ [".#+=O" !! fromIntegral v])
                        else return ()
                putStrLn ("\x1B[24;1H" ++ show (aiScore ai1))
                hFlush stdout
                threadDelay 2000

            case mcont of
                Just cont -> case runContinue cont [provideInput ai1] of
                                 Left (cont', out) -> loop ai1 (Just cont') out
                                 Right (_, out) -> loop ai1 Nothing out
                Nothing -> return (aiScore ai1)

    score <- case runInterruptible program' [] of
                 Left (cont, output) -> loop initAI (Just cont) output
                 Right (_, output) -> loop initAI Nothing output
    print score