-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathDay15.hs
125 lines (100 loc) · 3.12 KB
/
Day15.hs
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
-- alternative solution using depth first search with cloned droids
module Main where
import Utilities
import Graph
import Geometry
import Intcode
import Data.Map (Map)
import qualified Data.Map as Map
-- Input processing
type Input = Memory
parse :: String -> Input
parse = readMemory
-- Part One
data Move = N | S | W | E
deriving (Bounded, Enum, Eq, Ord, Show)
fromMove :: Move -> Value
fromMove d = toValue d + 1
startPoint :: Position
startPoint = zero
move :: Move -> Position
move N = Position 0 (-1)
move S = Position 0 1
move W = Position (-1) 0
move E = Position 1 0
data Response = Blocked | Moved | Found
deriving (Enum, Show)
toResponse :: Value -> Response
toResponse = fromValue
data Cell = Wall | Space
deriving (Eq, Show)
showCell :: Cell -> Char
showCell Wall = '#'
showCell Space = '.'
data Maze = Maze {
target :: Maybe Position,
maze_map :: Map Position Cell
}
initMaze :: Maze
initMaze = Maze {
target = Nothing,
maze_map = Map.singleton startPoint Space
}
showMaze :: Maze -> String
showMaze maze = showGrid ' ' $
target_map `Map.union`
Map.singleton startPoint '0' `Map.union`
fmap showCell (maze_map maze)
where
target_map = case target maze of
Just p -> Map.singleton p '*'
Nothing -> Map.empty
-- map the maze using the droid program
mapMaze :: Memory -> Maze
mapMaze mem = searchMaze startPoint (automaton mem) initMaze
-- search from p with a droid positioned at p
searchMaze :: Position -> Automaton -> Maze -> Maze
searchMaze p droid m =
foldl (moveTo droid) m [(d, p .+. move d) | d <- allValues]
-- consider droid moves to adjacent points
moveTo :: Automaton -> Maze -> (Move, Position) -> Maze
moveTo droid maze (d, p)
| Map.member p m = maze
| otherwise = case r of
Blocked ->
maze { maze_map = Map.insert p Wall m }
Moved -> searchMaze p droid' $
maze { maze_map = Map.insert p Space m }
Found -> searchMaze p droid' $
maze { maze_map = Map.insert p Space m, target = Just p }
where
(r, droid') = moveDroid droid d
m = maze_map maze
-- interact with the droid, attempting to make the move
moveDroid :: Automaton -> Move -> (Response, Automaton)
moveDroid (ReadValue k) d = case k (fromMove d) of
WriteValue v droid -> (toResponse v, droid)
_ -> error "Droid failed to respond"
moveDroid _ _ = error "Droid not accepting input"
-- destinations reachable in one step from p
neighbours :: Map Position Cell -> Position -> [Position]
neighbours m p =
[p' | d <- allValues, let p' = p .+. move d, Map.lookup p' m == Just Space]
solve1 :: Maze -> Int
solve1 maze = case target maze of
Nothing -> error "Target not found"
Just t ->
length $ takeWhile (t `notElem`) $
bfs (neighbours (maze_map maze)) [startPoint]
-- Part Two
solve2 :: Maze -> Int
solve2 maze = case target maze of
Nothing -> error "Target not found"
Just t -> length (bfs (neighbours (maze_map maze)) [t]) - 1
main :: IO ()
main = do
s <- readFile "input/15.txt"
let maze = mapMaze (parse s)
-- putStr (showMaze maze)
print (solve1 maze)
print (solve2 maze)