-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrun.hs
83 lines (70 loc) · 2.37 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
{-# LANGUAGE TupleSections #-}
import Control.Monad (guard)
import Data.List (permutations, group, minimum, maximum, minimumBy, maximumBy)
import Data.Set (Set)
import qualified Data.Set as Set
import Text.Megaparsec hiding (State)
import Data.ByteString.Char8 (ByteString, pack, unpack)
import Crypto.Hash
md5 :: ByteString -> Digest MD5
md5 = hash
data Queue a = Queue [a] [a]
newQueue = Queue [] []
fromList = flip Queue []
toList (Queue front back) = front ++ reverse back
enq (Queue xs ys) y = Queue xs (y:ys)
deq (Queue [] []) = Nothing
deq (Queue (x:xs) ys) = Just (x, Queue xs ys)
deq (Queue [] ys) = deq (Queue (reverse ys) [])
-- TODO: Replace with AoC.Search.bfs
bfs :: (Ord a, Eq a) => a -> (a -> [a]) -> (a -> Bool) -> Maybe (Int, a)
bfs state neighbours done = bfs' Set.empty (enq newQueue (0, state))
where bfs' visited queue = do
((steps, current), queue') <- deq queue
let visited' = Set.insert current visited
valid s = not (Set.member s visited')
queue'' = foldr (flip enq) queue'
. map (steps+1,)
. filter valid
. neighbours $ current
if done current
then pure (steps, current)
else bfs' visited' queue''
type State = (String, (Int, Int))
type Input = String
woop :: [(a -> a)] -> (a, a) -> [(a, a)]
woop fs (x, y) = do
f <- fs
[(f x, y), (x, f y)]
done :: State -> Bool
done (path, (x, y))
| x == 3 && y == 3 = True
| otherwise = False
dirs :: Int -> Char
dirs 0 = 'U'
dirs 1 = 'D'
dirs 2 = 'L'
dirs 3 = 'R'
dirs x = error $ "Invalid dir: " ++ show x
neighboursOf :: Input -> State -> [State]
neighboursOf input (path, (x, y))
| (x, y) == (3, 3) = []
| otherwise = do
(i, (x', y')) <- zip [0..] [(x, y - 1), (x, y + 1), (x - 1, y), (x + 1, y)]
guard $ 0 <= x' && x' < 4 && 0 <= y' && y' < 4
let h = take 4 . show $ md5 (pack $ input ++ reverse path)
guard $ (h !! i) `elem` "bcdef"
pure (dirs i : path, (x', y'))
allDone input =
filter done
. concat
. takeWhile (not.null)
$ iterate (>>= neighboursOf input) [("", (0, 0))]
part1 input =
let Just (_, (path, _)) = bfs ("", (0, 0)) (neighboursOf input) done
in reverse path
part2 = length . fst . last . allDone
main = do
let input = "pslxynzg"
putStrLn (part1 input)
print (part2 input)