-
Notifications
You must be signed in to change notification settings - Fork 0
/
Day06.hs
102 lines (81 loc) · 3.38 KB
/
Day06.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
{-# LANGUAGE RecordWildCards, DeriveGeneric #-}
module Day06 where
import Control.Applicative
import Data.Attoparsec.ByteString.Char8
import qualified Data.Set as S
import Control.Parallel.Strategies
import Day
import AocTools.Grid
import AocTools.Wastl (direction)
task :: Day (Grid Objects) Int
task = Day { parser = gridParser cell $ Objects mempty mempty
, solvers = [ part 1 $ length . S.fromList . map toLocation . \a -> travel a $ singlePatrol a
, part 2 $ length . filter id .
withStrategy (parListChunk 12 rseq) .
map gridHasLoop . addObsts
]
}
data Patrol = Patrol { patrolX :: !Int
, patrolY :: !Int
, patrolDir :: !Coord
} deriving (Show, Eq, Ord, Generic)
instance ToJSON Patrol
data Objects = Objects { obst :: S.Set (Int, Int) -- 🍐🍎
, patrols :: ![Patrol]
} deriving (Show, Generic)
instance ToJSON Objects
-- Parsing
cell :: Grid Objects -> Parser Objects
cell g = (addPatrol g <$> direction) <|>
(anyChar >>= obstacle >>= addObstacle g)
addObstacle :: Applicative f => Grid Objects -> Bool -> f Objects
addObstacle Grid{..} True = pure stuff{obst = S.insert (trail, rows) $ obst stuff}
addObstacle Grid{..} False = pure stuff
addPatrol :: Grid Objects -> Coord -> Objects
addPatrol Grid{..} d = stuff{patrols = Patrol trail rows d : patrols stuff}
obstacle :: Alternative f => Char -> f Bool
obstacle c = case c of
'#' -> pure True
'.' -> pure False
_ -> empty
-- Now the implementation
patrolFwd :: Patrol -> Patrol
patrolFwd Patrol{..} = f patrolDir
where f (dx, dy) = Patrol { patrolX = patrolX + dx
, patrolY = patrolY + dy
, ..
}
movePatrol :: Grid Objects -> Patrol -> Patrol
movePatrol Grid{..} origP = if S.member (patrolX movedP, patrolY movedP) $ obst stuff
then origP{ patrolDir = turnRight (patrolDir origP) }
else movedP
where movedP = patrolFwd origP
travel :: Grid Objects -> Patrol -> [Patrol]
travel grid pat = if isIn
then next : travel grid next
else []
where next = movePatrol grid pat
isIn = bounds grid (patrolX next, patrolY next)
toLocation :: Patrol -> (Int, Int)
toLocation Patrol{..} = (patrolX, patrolY)
-- |A loop is something where the guard travels to the same direction
-- at the same location.
hasLoop :: Ord a => S.Set a -> [a] -> Bool
hasLoop s (x:xs) = if S.member x s
then True
else hasLoop (S.insert x s) xs
hasLoop _ [] = False -- Guard walked out
gridHasLoop :: Grid Objects -> Bool
gridHasLoop grid = hasLoop mempty $ travel grid $ singlePatrol grid
singlePatrol :: Grid Objects -> Patrol
singlePatrol g = case patrols (stuff g) of
[a] -> a
_ -> error "Expecting exactly 1 patrol"
addObsts :: Grid Objects -> [Grid Objects]
addObsts Grid{..} = [ Grid{stuff = stuff{obst = S.insert (x, y) (obst stuff)}, ..}
| y <- [0..rows-1]
, x <- [0..cols-1]
, not $ any (isStart x y) $ patrols stuff
, S.notMember (x, y) $ obst stuff
]
where isStart x y Patrol{..} = patrolX == x && patrolY == y