-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday22.hs
88 lines (66 loc) · 2.68 KB
/
day22.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
module Main where
import Control.Applicative ((<|>))
import Control.Monad
import Data.Char (isDigit)
import Data.Foldable (foldl', minimumBy)
import Data.Function (on)
import Data.Functor (($>))
import Data.List (scanl', unfoldr)
import Data.List.Split (splitOn)
import Data.Map.Strict (Map)
import Data.Map.Strict qualified as M
import Data.Maybe (fromMaybe, isJust)
import Data.Tuple (swap)
import Debug.Trace (trace)
data Dir = R | D | L | U deriving (Show, Read, Eq, Bounded, Enum)
data PathN = RotateR | RotateL | Move Int deriving (Show, Eq)
type Board = Map Point Char
type Point = (Int, Int)
main = interact (unlines . sequence [part1] . parse)
part1 = ("Part 1: " ++) . show . score . uncurry follow
follow :: Board -> [PathN] -> (Dir, Point)
follow board = foldl' action (R, start $ M.keys board)
where
action (dir, point) RotateL = (prev dir, point)
action (dir, point) RotateR = (next dir, point)
action dp (Move n) = move n dp
move :: Int -> (Dir, Point) -> (Dir, Point)
move 0 dp = dp
move n dp = maybe dp (move (n - 1)) $ findNext dp
findNext (dir, point) = do
(tile, point) <- nextTile point dir <|> telepoint point dir
guard (tile == '.')
return (dir, point)
nextTile point dir = tile (add point dir)
telepoint point dir = last . takeWhile isJust . map tile $ iterate (`add` opposite dir) point
tile point = (,point) <$> board M.!? point
start = minimumBy (compare `on` swap)
add :: Point -> Dir -> Point
add (x, y) R = (x + 1, y)
add (x, y) L = (x - 1, y)
add (x, y) U = (x, y - 1)
add (x, y) D = (x, y + 1)
parse = ap ((,) . parseMap . lines . head) (parsePath . head . lines . (!! 1)) . splitOn "\n\n"
parseMap :: [String] -> Map (Int, Int) Char
parseMap input = M.fromList [((x, y), v) | (y, row) <- zip [1 ..] input, (x, v) <- zip [1 ..] row, v /= ' ']
parsePath :: String -> [PathN]
parsePath ('R' : xs) = RotateR : parsePath xs
parsePath ('L' : xs) = RotateL : parsePath xs
parsePath move@(d : _) = let (n, xs) = span isDigit move in Move (read n) : parsePath xs
parsePath _ = []
next dir
| dir == maxBound = minBound
| otherwise = succ dir
prev dir
| dir == minBound = maxBound
| otherwise = pred dir
opposite dir = next (next dir)
score (dir, (x, y)) = 1000 * y + 4 * x + length (takeWhile (/= dir) (iterate next R))
drawBoard :: Board -> [(Dir, Point)] -> Point -> String
drawBoard board path start = unlines [[drawTile (x, y) | x <- [1 .. mx]] | y <- [1 .. my]]
where
mx = maximum $ map fst $ M.keys board
my = maximum $ map snd $ M.keys board
pm = M.fromList $ map swap path
drawTile :: (Int, Int) -> Char
drawTile p = if p == start then 'S' else fromMaybe ' ' $ head . show <$> pm M.!? p <|> board M.!? p