summaryrefslogtreecommitdiff
path: root/2019/25.hs
blob: 3494fab006dcb375cb89e2eab07ecf5291a202bf (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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
{-# LANGUAGE MultiWayIf #-}
module Main where

import Control.Monad
import Data.Char
import Data.List
import Data.Ord
import qualified Data.Set as Set
import qualified Data.Map.Strict as Map
import System.IO
import Text.Parsec hiding (getInput)

import Debug.Trace

import Input
import qualified IntCode as IC


cyan, blue :: String -> String
cyan s = "\x1B[36m" ++ s ++ "\x1B[0m"
blue s = "\x1B[34m" ++ s ++ "\x1B[0m"


type Name = String
type Item = String

data Meta = Killed | Taken Item | WTF String
  deriving (Show)

data Dir = North | East | West | South
  deriving (Enum, Eq, Ord)

instance Show Dir where
    show North = "north"
    show East = "east"
    show West = "west"
    show South = "south"

instance Read Dir where
    readsPrec _ ('n':'o':'r':'t':'h':s) = [(North, s)]
    readsPrec _ ('e':'a':'s':'t':s) = [(East, s)]
    readsPrec _ ('w':'e':'s':'t':s) = [(West, s)]
    readsPrec _ ('s':'o':'u':'t':'h':s) = [(South, s)]
    readsPrec _ _ = []

data RoomDesc
    = RoomDesc { rName :: Name
               , rDesc :: String
               , rDoors :: [Dir]
               , rItems :: [Item] }
  deriving (Show)

parseOutput :: String -> Either Meta RoomDesc
parseOutput = either (\e -> Left (WTF (show e))) id . parse pRoom ""
  where
    pRoom = do
        spaces
        choice [do
                    void $ string "You take the "
                    item <- many (satisfy (/= '.'))
                    void $ char '.'
                    void $ newline
                    return (Left (Taken item))
               ,do
                    name <- pName
                    desc <- pDesc
                    spaces
                    void $ string "Doors here lead:\n"
                    dirStrs <- pList
                    spaces
                    items <- choice [string "Items here:\n" >> pList
                                    ,return []]
                    spaces
                    void $ string "Command?\n"
                    return (Right (RoomDesc name desc (map read dirStrs) items))
               ]
    pName = do
        void $ string "== "
        name <- many (notFollowedBy (try (string " ==")) >> anyChar)
        void $ string " =="
        void $ newline
        return name
    pDesc = pRestOfLine
    pList = many (string "- " >> pRestOfLine <* newline)
    pRestOfLine = many (satisfy (/= '\n'))

data Explorer = Explorer (Map.Map Name (RoomDesc, Map.Map Dir Name)) (Maybe Name) (Maybe (Name, Dir))
  deriving (Show)

observe :: Explorer -> RoomDesc -> Explorer
observe (Explorer chart _ lastMove) rd =
    let chart' = case Map.lookup (rName rd) chart of
                     Nothing -> Map.insert (rName rd) (rd, mempty) chart
                     Just _ -> chart
    in case lastMove of
           Nothing -> Explorer chart' (Just (rName rd)) lastMove
           Just (from, dir) ->
               let Just (fromRD, dirmap) = Map.lookup from chart
                   chart'' = Map.insert from (fromRD, Map.insert dir (rName rd) dirmap) chart'
               in Explorer chart'' (Just (rName rd)) lastMove

chooseMove :: Explorer -> Maybe (Dir, Explorer)
chooseMove (Explorer _ Nothing _) = undefined
chooseMove (Explorer chart (Just current@"Security Checkpoint") _) =
    Just (South, Explorer chart (Just current) (Just (current, South)))
chooseMove (Explorer chart (Just current) _) =
    let Just (rd, dirmap) = Map.lookup current chart
        opts = [case Map.lookup dir dirmap of
                    Just room' -> (dir, distToUnex room' 1 (Set.singleton current))
                    Nothing -> (dir, 1)
               | dir <- rDoors rd]
    in traceShow opts $
       case minimumBy (comparing snd) ((undefined, maxBound) : opts) of
           (_, n) | n == maxBound -> Nothing
           (dir, _) -> Just (dir, Explorer chart (Just current) (Just (current, dir)))
  where
    distToUnex :: Name -> Int -> Set.Set Name -> Int
    distToUnex "Security Checkpoint" _ _ = maxBound
    distToUnex room dist visited =
        let Just (rd, dirmap) = Map.lookup room chart
        in if | room `Set.member` visited ->
                  maxBound
              | not . null $ rDoors rd \\ Map.keys dirmap ->
                  dist + 1
              | otherwise ->
                  minimum [distToUnex room' (dist + 1) (Set.insert room visited)
                          | room' <- Map.elems dirmap]

printDOT :: Explorer -> String
printDOT (Explorer chart _ _) = "digraph G {\n" ++ printRelations "Hull Breach" mempty ++ "}"
  where
    printRelations room seen
        | room `Set.member` seen = ""
        | otherwise =
            let Just (rd, dirmap) = Map.lookup room chart
            in concat ((show room ++ " [label=" ++ show (room ++ "\n" ++ show (rItems rd)) ++ "];\n")
                      : unlines [show room ++ " -> " ++ show to ++ " [label=" ++ show (show dir) ++ "];"
                                | (dir, to) <- Map.assocs dirmap]
                      : [printRelations to (Set.insert room seen) | to <- Map.elems dirmap])

exploreAndPrintMap :: [Integer] -> IO ()
exploreAndPrintMap program =
    let loop cont inp expl =
            case IC.runContinue cont inp of
                Left (cont', out) -> do
                    let out' = map (chr . fromIntegral) out
                    putStr (cyan out') >> hFlush stdout
                    case parseOutput out' of
                        Right rd -> 
                            case chooseMove (observe expl rd) of
                                Just (dir, expl') -> do
                                    putStrLn (blue ("Entering '" ++ show dir ++ "'"))
                                    loop cont' (map (fromIntegral . ord) (show dir ++ "\n")) expl'
                                Nothing -> do
                                    putStrLn "Explorer gave up!"
                                    putStrLn (printDOT expl)
                        Left err -> do
                            print err
                            putStrLn (printDOT expl)
                Right (_, out) -> do
                    putStr (cyan (map (chr . fromIntegral) out))
    in loop (IC.initialContinuation program) [] (Explorer mempty Nothing Nothing)

interactive :: [Integer] -> IO ()
interactive program =
    let loop cont inp =
            case IC.runContinue cont inp of
                Left (cont', out) -> do
                    putStr (map (chr . fromIntegral) out)
                    line <- getLine
                    loop cont' (map (fromIntegral . ord) (line ++ "\n"))
                Right (_, out) -> do
                    putStr (map (chr . fromIntegral) out)
    in loop (IC.initialContinuation program) []

tryCombinations :: [Integer] -> IO ()
tryCombinations program =
    let loop _ _ [] = putStrLn "Out of tries!"
        loop cont inp (current:nexts) =
            case IC.runContinue cont inp of
                Left (cont', out) -> do
                    let out' = map (chr . fromIntegral) out
                    -- putStrLn out'
                    if | "lighter" `isInfixOf` out' -> putStrLn "  -> heavy"
                       | "heavier" `isInfixOf` out' -> putStrLn "  -> light"
                       | otherwise                  -> return ()
                    print current
                    let input = concat [concat ["take " ++ i ++ "\n" | i <- current]
                                       ,"east\n"
                                       ,concat ["drop " ++ i ++ "\n" | i <- current]]
                    loop cont' (map (fromIntegral . ord) input) nexts
                Right (_, out) -> do
                    putStr (map (chr . fromIntegral) out)
        setupString = "east\ntake antenna\nnorth\nnorth\ntake asterisk\nsouth\nwest\nwest\ntake astronaut ice cream\neast\nsouth\ntake hologram\nnorth\neast\nsouth\neast\ntake ornament\nnorth\nwest\ntake fixed point\neast\nsouth\nwest\nwest\nsouth\nsouth\nsouth\ntake dark matter\nnorth\nwest\nnorth\ntake monolith\nnorth\nnorth\ndrop astronaut ice cream\ndrop hologram\ndrop fixed point\ndrop asterisk\ndrop ornament\ndrop dark matter\ndrop monolith\ndrop antenna\n"
        allItems = ["asterisk", "ornament", "dark matter", "monolith", "antenna", "astronaut ice cream", "hologram", "fixed point"]
        combinations [] = [[]]
        combinations (x:xs) = let l = combinations xs in map (x:) l ++ l
    in loop (IC.initialContinuation program) (map (fromIntegral . ord) setupString) (combinations allItems)

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

    -- exploreAndPrintMap program
    -- interactive program
    tryCombinations program

    -- Collection of items: ["asterisk","ornament","astronaut ice cream","fixed point"]
    -- Password: 134227456