-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrun.hs
139 lines (120 loc) · 4.3 KB
/
run.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
{-# LANGUAGE BangPatterns #-}
import Control.Monad (guard)
import Data.List ( intercalate
, maximum
, minimumBy
, subsequences)
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.PriorityQueue.FingerTree (PQueue)
import qualified Data.PriorityQueue.FingerTree as PQueue
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Void (Void)
import Text.Megaparsec hiding (State, empty)
import Text.Megaparsec.Char
import Debug.Trace (trace)
data Node = Node { size :: {-# UNPACK #-} !Int
, used :: {-# UNPACK #-} !Int
, avail :: {-# UNPACK #-} !Int }
deriving (Show, Eq, Ord)
numP = read <$> some digitChar
nodeP = Node <$> (some spaceChar *> numP <* string "T")
<*> (some spaceChar *> numP <* string "T")
<*> (some spaceChar *> numP <* string "T")
nodeNameP = (,) <$> (string "/dev/grid/node-x" *> numP)
<*> (string "-y" *> numP)
lineP :: Parsec Void String ((Int, Int), Node)
lineP = (,) <$> nodeNameP <*> nodeP
unsafeRight (Right x) = x
parseAll = HashMap.fromList . map unsafeRight .
map (parse lineP "") . drop 2 . lines
viable :: Node -> Node -> Bool
viable a b =
used a > 0 && avail b >= used a
part1 nodes = length $ do
n1 <- HashMap.toList nodes
n2 <- HashMap.toList nodes
guard $ fst n1 /= fst n2
guard $ viable (snd n1) (snd n2)
pure $ (n1, n2)
data Reduced = NEmpty | NGoal | NBig | NNormal
deriving (Show, Eq, Ord)
data State = State { goal :: {-# UNPACK #-} !(Int, Int)
, empty :: {-# UNPACK #-} !(Int, Int)
, nodes :: HashMap (Int, Int) Reduced }
deriving (Show, Eq, Ord)
solve :: State -> (Int, State)
solve initial = solve' Set.empty (PQueue.singleton 0 (0, initial))
where solve' :: Set State -> PQueue Int (Int, State) -> (Int, State)
solve' visited queue =
let Just ((!steps, !current), !queue') = PQueue.minView queue
visited' = Set.insert current visited
valid s = not (Set.member s visited')
queue'' = foldr (uncurry PQueue.insert) queue'
. map (\s -> (h s + steps + 1, (steps + 1, s)))
. filter valid $ neighboursOf current
in
if done current
then (steps, current)
else solve' visited' queue''
done s = goal s == (0, 0)
d (x0, y0) (x1, y1) =
abs (x1 - x0) + abs (y1 - y0)
h s = 5 * (d (goal s) (0, 0))
+ d (empty s) (goal s)
viable' NNormal = True
viable' NGoal = True
viable' _ = False
neighboursOf :: State -> [State]
neighboursOf s = do
let (x, y) = empty s
Just en = HashMap.lookup (empty s) (nodes s)
(x', y') <- [(x, y-1), (x-1, y), (x, y+1), (x+1, y)]
Just bn <- pure $ HashMap.lookup (x', y') (nodes s)
guard $ viable' bn
let g' = if (x', y') == goal s
then (x, y)
else goal s
e' = (x', y')
m = HashMap.insert (x', y') en $
HashMap.insert (x, y) bn $ nodes s
pure $ State { goal = g'
, empty = e'
, nodes = m }
part2 = fst . solve . initialState
reduceGrid g = HashMap.mapWithKey f
where f pos n
| used n == 0 = NEmpty
| pos == g = NGoal
| used n >= 100 = NBig
| otherwise = NNormal
initialState grid =
let mx = maximum . map (fst . fst) . HashMap.toList $ grid
g = (mx, 0)
grid' = reduceGrid g grid
(epos, _) = head . filter ((==NEmpty).snd) . HashMap.toList $ grid'
in
State { goal = g
, empty = epos
, nodes = grid' }
pstate s =
putStrLn $ "Goal: " ++ show (goal s)
++ "\nEmpty: " ++ show (empty s)
++ "\nh: " ++ show (h s)
pgrid s =
let xm = maximum . map (fst . fst) . HashMap.toList $ nodes s
ym = maximum . map (snd . fst) . HashMap.toList $ nodes s
c pos =
let Just v = HashMap.lookup pos (nodes s)
in case v of
NEmpty -> '_'
NNormal -> '.'
NBig -> 'X'
NGoal -> 'G'
in
intercalate "\n" [[c (x, y) | x <- [0..xm]] | y <- [0..ym]]
main = do
input <- parseAll <$> readFile "input.txt"
print (part1 input)
print (part2 input)