Skip to content

Commit c511e41

Browse files
committed
refacto(2024/Day06): Improved today's solution to run faster and multithreaded
1 parent 34068bf commit c511e41

File tree

2 files changed

+36
-35
lines changed

2 files changed

+36
-35
lines changed

2024/Day06/Day06.hs

+34-35
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
11
module Main where
22

3+
import Control.Parallel.Strategies
34
import Data.List
45
import System.Environment
6+
import Data.Matrix (Matrix, (!))
7+
import Data.Matrix qualified as Mat
58
import Data.Set (Set)
69
import Data.Set qualified as S
710
-- TODO: Cleanup imports after day done
811

912

1013
data Heading = UP | RIGHT | DOWN | LEFT deriving(Show, Eq, Ord)
11-
type Input = ([String], (Int, Int), (Int, Int), Heading)
14+
type Input = (Matrix Char, (Int, Int), (Int, Int), Heading)
1215
type Output = Int
1316

1417
parseInput :: String -> Input
15-
parseInput s = (lines s, (width, height), pos, UP)
18+
parseInput s = (mat, (width, height), pos, UP)
1619
where
17-
height = length $ lines s
18-
width = length . head $ lines s
19-
pos = head . filter (\(x, y) -> (== '^') . (!! x) . (!! y) $ lines s) $ [(x, y) | x<-[0..width-1], y<-[0..height-1]]
20+
mat = Mat.fromLists $ lines s
21+
height = Mat.nrows mat
22+
width = Mat.ncols mat
23+
24+
pos = head . filter ((== '^') . (mat !)) $ [(y, x) | x<-[1..width], y<-[1..height]]
2025

2126

2227
turn :: Heading -> Heading
@@ -26,53 +31,47 @@ turn DOWN = LEFT
2631
turn LEFT = UP
2732

2833
move :: Heading -> (Int, Int) -> (Int, Int)
29-
move UP (x, y) = (x, y-1)
30-
move RIGHT (x, y) = (x+1, y)
31-
move DOWN (x, y) = (x, y+1)
32-
move LEFT (x, y) = (x-1, y)
34+
move UP (y, x) = (y-1, x)
35+
move RIGHT (y, x) = (y, x+1)
36+
move DOWN (y, x) = (y+1, x)
37+
move LEFT (y, x) = (y, x-1)
3338

3439
isOut :: (Int, Int) -> (Int, Int) -> Bool
35-
isOut (width, height) (x, y) = x < 0 || y < 0 || x >= width || y >= height
36-
37-
cellAt :: [String] -> (Int, Int) -> Char
38-
cellAt grid (x, y) = (grid !! y) !! x
40+
isOut (width, height) (y, x) = x <= 0 || y <= 0 || x > width || y > height
3941

4042
visitedCells :: Input -> Set (Int, Int)
4143
visitedCells (grid, dim, pos, dir) = moveOut pos dir S.empty
4244
where
43-
moveOut xy dir positions
44-
| isOut dim (move dir xy) = newPoss
45-
| cellAt grid (move dir xy) == '#' = moveOut (move newDir xy) newDir newPoss
46-
| otherwise = moveOut (move dir xy) dir newPoss
45+
moveOut yx dir positions
46+
| isOut dim (move dir yx) = newPoss
47+
| grid ! (move dir yx) == '#' = moveOut yx newDir newPoss
48+
| otherwise = moveOut (move dir yx) dir newPoss
4749
where
48-
newPoss = (S.insert xy positions)
50+
newPoss = (S.insert yx positions)
4951
newDir = turn dir
5052

5153

5254
part1 :: Input -> Output
5355
part1 = S.size . visitedCells
5456

5557
part2 :: Input -> Output
56-
part2 (grid, (dim@(width, height)), pos, dir) = length . filter (isLoop pos dir S.empty) . map insertObstacle $ wallsPos
58+
part2 (grid, (dim@(width, height)), pos, dir) = length . filter id . parMap rseq (isLoop pos dir S.empty) . map insertObstacle $ wallsPos
5759
where
5860
visited = visitedCells (grid, dim, pos, dir)
59-
wallsPos = [(x, y) | x<-[0..width-1], y<-[0..height-1], ((x, y) `S.member` visited) && cellAt grid (x, y) /= '#']
60-
61-
insertObstacle :: (Int, Int) -> [String]
62-
insertObstacle (x, y) = linesBefore ++ [elemsBefore ++ "#" ++ elemsAfter] ++ linesAfter
63-
where
64-
(linesBefore, lineAt:linesAfter) = splitAt y grid
65-
(elemsBefore, elemAt:elemsAfter) = splitAt x lineAt
66-
67-
isLoop :: (Int, Int) -> Heading -> Set ((Int, Int), Heading) -> [String] -> Bool
68-
isLoop xy dir positions grid
69-
| (xy, dir) `S.member` positions = True
70-
| isOut dim xy = False
71-
| isOut dim (move dir xy) = False
72-
| cellAt grid (move dir xy) == '#' = isLoop xy newDir newPos grid
73-
| otherwise = isLoop (move dir xy) dir newPos grid
61+
wallsPos = [(y, x) | y<-[1..height], x<-[1..width], ((y, x) `S.member` visited) && grid ! (y, x) == '.']
62+
63+
insertObstacle :: (Int, Int) -> Matrix Char
64+
insertObstacle yx = Mat.setElem '#' yx grid
65+
66+
isLoop :: (Int, Int) -> Heading -> Set ((Int, Int), Heading) -> Matrix Char -> Bool
67+
isLoop yx dir positions grid
68+
| (yx, dir) `S.member` positions = True
69+
| isOut dim yx = False
70+
| isOut dim (move dir yx) = False
71+
| grid ! (move dir yx) == '#' = isLoop yx newDir newPos grid
72+
| otherwise = isLoop (move dir yx) dir newPos grid
7473
where
75-
newPos = (S.insert (xy, dir) positions)
74+
newPos = (S.insert (yx, dir) positions)
7675
newDir = turn dir
7776

7877

2024/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,5 @@ For the part 2 I managed to speed up my solution :
9191
- Changing the memory list to a Set memory => 3mins
9292
- Filtering the positions to only places where the guard passes in the their usual path => 30sec
9393
- Adding -O2 => ~10sec
94+
- Switching to Matrices instead of raw `[String]` => ~5.5s
95+
- Multithreading => ~6s (runs slightly slower but at least I have a reference to use multithreading)

0 commit comments

Comments
 (0)