Skip to content

Commit 1875db3

Browse files
committed
Day 22: Monkey Map
1 parent d2eb691 commit 1875db3

File tree

6 files changed

+152
-0
lines changed

6 files changed

+152
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ Development occurs in language-specific directories:
2727
|[Day19.hs](hs/src/Day19.hs)|[Day19.kt](kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day19.kt)|[day19.py](py/aoc2022/day19.py)|[day19.rs](rs/src/day19.rs)|
2828
|[Day20.hs](hs/src/Day20.hs)|[Day20.kt](kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day20.kt)|[day20.py](py/aoc2022/day20.py)|[day20.rs](rs/src/day20.rs)|
2929
|[Day21.hs](hs/src/Day21.hs)|[Day21.kt](kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day21.kt)|[day21.py](py/aoc2022/day21.py)|[day21.rs](rs/src/day21.rs)|
30+
|[Day22.hs](hs/src/Day22.hs)|

hs/aoc2022.cabal

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ data-files:
3232
, day19.txt
3333
, day20.txt
3434
, day21.txt
35+
, day22.txt
3536

3637
extra-source-files:
3738
README.md
@@ -60,6 +61,7 @@ library
6061
, Day19
6162
, Day20
6263
, Day21
64+
, Day22
6365
build-depends:
6466
array ^>=0.5.4.0
6567
, base ^>=4.16.0.0
@@ -123,6 +125,7 @@ test-suite aoc2022-test
123125
, Day19Spec
124126
, Day20Spec
125127
, Day21Spec
128+
, Day22Spec
126129
hs-source-dirs: test
127130
default-language: GHC2021
128131
build-tool-depends:

hs/app/Main.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import Day18 (day18a, day18b)
2323
import Day19 (day19a, day19b)
2424
import Day20 (day20a, day20b)
2525
import Day21 (day21a, day21b)
26+
import Day22 (day22a, day22b)
2627

2728
import Control.Monad ((<=<), ap, when)
2829
import Data.Function (on)
@@ -73,3 +74,4 @@ main = do
7374
run 19 (either (fail . errorBundlePretty) print) [day19a, day19b]
7475
run 20 (either fail print) [day20a, day20b]
7576
run 21 (either (fail . errorBundlePretty) print) [day21a, day21b]
77+
run 22 print [day22a, day22b]

hs/bench/Main.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import Day18 (day18a, day18b)
2626
import Day19 (day19a, day19b)
2727
import Day20 (day20a, day20b)
2828
import Day21 (day21a, day21b)
29+
import Day22 (day22a, day22b)
2930
import Paths_aoc2022 (getDataFileName)
3031
import System.Environment.Blank (getEnv, setEnv, unsetEnv)
3132

@@ -126,4 +127,8 @@ main = defaultMain
126127
[ bench "part 1" $ nf day21a input
127128
, bench "part 2" $ nf day21b input
128129
]
130+
, env (getDayInput 22) $ \input -> bgroup "Day 22"
131+
[ bench "part 1" $ nf day22a input
132+
, bench "part 2" $ nf day22b input
133+
]
129134
]

hs/src/Day22.hs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
{-|
2+
Module: Day22
3+
Description: <https://adventofcode.com/2022/day/22 Day 22: Monkey Map>
4+
-}
5+
{-# LANGUAGE LambdaCase, MultiWayIf, NondecreasingIndentation, OverloadedStrings, ParallelListComp, RecordWildCards, ScopedTypeVariables, TransformListComp, TypeFamilies, ViewPatterns #-}
6+
module Day22 (day22a, day22b) where
7+
8+
import Control.Arrow (first)
9+
import Data.Either (partitionEithers)
10+
import Data.Function (on)
11+
import Data.Ix (inRange)
12+
import Data.List (find, foldl', foldl1', groupBy)
13+
import Data.List.Split (chunksOf)
14+
import Data.Map (Map)
15+
import qualified Data.Map as Map ((!?), fromList)
16+
import Data.Maybe (fromJust, maybeToList)
17+
import Data.Text (Text)
18+
import qualified Data.Text as T (findIndex, index, length, lines, null, uncons)
19+
import qualified Data.Text.Read as T (decimal)
20+
import Data.Vector (Vector)
21+
import qualified Data.Vector as V ((!), (!?), fromList, head, length)
22+
23+
data Move a = Move a | TurnL | TurnR
24+
data Dir = R | D | L | U deriving (Enum, Eq, Ord)
25+
26+
readsPath :: (Integral a) => Text -> ([Move a], Text)
27+
readsPath (T.decimal -> Right (n, s)) = let ~(path, s') = readsPath s in (Move n:path, s')
28+
readsPath (T.uncons -> Just ('L', s)) = let ~(path, s') = readsPath s in (TurnL:path, s')
29+
readsPath (T.uncons -> Just ('R', s)) = let ~(path, s') = readsPath s in (TurnR:path, s')
30+
readsPath s = ([], s)
31+
32+
turnRight, turn180, turnLeft :: Dir -> Dir
33+
turnRight d = toEnum $ (fromEnum d + 1) `mod` 4
34+
turn180 d = toEnum $ (fromEnum d + 2) `mod` 4
35+
turnLeft d = toEnum $ (fromEnum d + 3) `mod` 4
36+
37+
step :: (Num a) => Dir -> (a, a) -> (a, a)
38+
step R (x, y) = (x + 1, y)
39+
step D (x, y) = (x, y + 1)
40+
step L (x, y) = (x - 1, y)
41+
step U (x, y) = (x, y - 1)
42+
43+
get :: Vector Text -> (Int, Int) -> Char
44+
get maze (x, y)
45+
| Just line <- maze V.!? y, inRange (0, T.length line - 1) x = T.index line x
46+
| otherwise = ' '
47+
48+
mazePerimeter :: Vector Text -> [((Int, Int), Dir)]
49+
mazePerimeter maze
50+
| Just x0 <- maze V.!? 0 >>= T.findIndex (== '.')
51+
= let initial:rest = iterate step' ((x0, 0), R) in initial:takeWhile (/= initial) rest where
52+
step' (p, d)
53+
| ' ' <- get maze p' = (p, turnRight d)
54+
| ' ' <- get maze p'' = (p', d)
55+
| otherwise = (p'', turnLeft d)
56+
where
57+
p' = step d p
58+
p'' = step (turnLeft d) p'
59+
mazePerimeter _ = []
60+
61+
mazeEdges2D, mazeEdges3D :: Vector Text -> Map ((Int, Int), Dir) ((Int, Int), Dir)
62+
mazeEdges2D maze = Map.fromList
63+
[ ((p, d'), (wrap d' p, d'))
64+
| x0 <- maybeToList $ maze V.!? 0 >>= T.findIndex (== '.')
65+
, (p, d) <- mazePerimeter maze
66+
, let d' = turnLeft d
67+
] where
68+
wrap d (x, y) = fromJust . find ((/= ' ') . get maze) $ case d of
69+
R -> [(x', y) | x' <- [0..x]]
70+
D -> [(x, y') | y' <- [0..y]]
71+
L -> [(x', y) | let line = maze V.! y, x' <- [T.length line - 1, T.length line - 2..x]]
72+
U -> [(x, y') | y' <- [V.length maze - 1, V.length maze - 2..y]]
73+
mazeEdges3D maze = Map.fromList $ concat
74+
[ [((p, turnLeft d), (q, turnRight e)), ((q, turnLeft e), (p, turnRight d))]
75+
| (edge1, edge2) <- joinedEdges
76+
, ((p, d), (q, e)) <- zip edge1 $ reverse edge2
77+
] where
78+
perimeter = mazePerimeter maze
79+
sideLength = foldl1' gcd . map length $ groupBy ((==) `on` snd) perimeter
80+
joinEdges [] = []
81+
joinEdges edges
82+
| [] <- joined = error "loop"
83+
| otherwise = joined ++ joinEdges remaining where
84+
(joined, remaining) = partitionEithers $ joinEdges' edges
85+
joinEdges' ((d1, e1):(d2, e2):edges)
86+
| turnLeft d1 == d2 = Left (e1, e2) : joinEdges' (first turnLeft <$> edges)
87+
joinEdges' (edge:edges) = Right edge : joinEdges' edges
88+
joinEdges' [] = []
89+
joinedEdges = joinEdges [(dir, edge) | edge@((_, dir):_) <- chunksOf sideLength perimeter]
90+
91+
day22 :: (Vector Text -> Map ((Int, Int), Dir) ((Int, Int), Dir)) -> Text -> Int
92+
day22 mazeEdges input = 1000 * (y + 1) + 4 * (x + 1) + fromEnum d where
93+
(V.fromList -> maze, [_, readsPath -> (path, "")]) = break T.null $ T.lines input
94+
Just x0 = T.findIndex (== '.') $ V.head maze
95+
edges = mazeEdges maze
96+
step' s | Just s' <- edges Map.!? s = s'
97+
step' (p, d) = (step d p, d)
98+
go s (Move n) = last . takeWhile ((== '.') . get maze . fst) . take (n + 1) $ iterate step' s
99+
go (p, d) TurnL = (p, turnLeft d)
100+
go (p, d) TurnR = (p, turnRight d)
101+
((x, y), d) = foldl' go ((x0, 0), R) path
102+
103+
day22a :: Text -> Int
104+
day22a = day22 mazeEdges2D
105+
106+
day22b :: Text -> Int
107+
day22b = day22 mazeEdges3D

hs/test/Day22Spec.hs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
module Day22Spec (spec) where
3+
4+
import Data.Text (Text)
5+
import qualified Data.Text as T (unlines)
6+
import Day22 (day22a, day22b)
7+
import Test.Hspec (Spec, describe, it, shouldBe)
8+
9+
example :: Text
10+
example = T.unlines
11+
[ " ...#"
12+
, " .#.."
13+
, " #..."
14+
, " ...."
15+
, "...#.......#"
16+
, "........#..."
17+
, "..#....#...."
18+
, "..........#."
19+
, " ...#...."
20+
, " .....#.."
21+
, " .#......"
22+
, " ......#."
23+
, ""
24+
, "10R5L5R10L4R5L5"
25+
]
26+
27+
spec :: Spec
28+
spec = do
29+
describe "part 1" $ do
30+
it "examples" $ do
31+
day22a example `shouldBe` 6032
32+
describe "part 2" $ do
33+
it "examples" $ do
34+
day22b example `shouldBe` 5031

0 commit comments

Comments
 (0)