Skip to content

Commit 80d03ac

Browse files
committed
feat(2024/Day18)
1 parent 7cbe446 commit 80d03ac

File tree

5 files changed

+3626
-1
lines changed

5 files changed

+3626
-1
lines changed

2024/Day18/Day18.hs

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
module Main where
2+
3+
import Data.Bits
4+
import Data.List
5+
import Data.List.Split
6+
import Data.List.Unique
7+
import Data.Map (Map)
8+
import Data.Map qualified as M
9+
import Data.Matrix (Matrix, (!))
10+
import Data.Matrix qualified as Mat
11+
import Data.Set (Set)
12+
import Data.Set qualified as S
13+
import Data.Tuple.Extra
14+
import Debug.Trace
15+
import System.Environment
16+
import Text.Regex.TDFA ((=~))
17+
18+
-- TODO: Cleanup imports after day done
19+
20+
type Input = [(Int, Int)]
21+
22+
type Output = Int
23+
24+
parseInput :: String -> Input
25+
parseInput = map (swap . read) . map (\l -> "(" ++ l ++ ")") . lines
26+
27+
data Dir = UP | LEFT | RIGHT | DOWN deriving (Show, Eq, Ord)
28+
29+
move :: (Int, Int) -> Dir -> (Int, Int)
30+
move (y, x) UP = (y - 1, x)
31+
move (y, x) DOWN = (y + 1, x)
32+
move (y, x) LEFT = (y, x - 1)
33+
move (y, x) RIGHT = (y, x + 1)
34+
35+
isOut :: (Int, Int) -> (Int, Int) -> Bool
36+
isOut (width, height) (y, x) = x < 0 || y < 0 || x > width || y > height
37+
38+
smallestPathFind :: Set (Int, Int) -> Map (Int, Int) Int -> (Int, Int) -> (Int, Int) -> Int -> (Int, Set (Int, Int), Map (Int, Int) Int)
39+
smallestPathFind memory seen pos target nbMoves
40+
-- \| trace ("Pos: " ++ show pos) False = (-1, S.empty, seen)
41+
| isOut target pos = (-1, S.empty, seen)
42+
| pos `M.member` seen && seen M.! pos <= nbMoves = (-1, S.empty, seen) -- trace ("At " ++ show nbMoves ++ " but already seen at " ++ show (seen M.! pos))
43+
| pos `S.member` memory = (-1, S.empty, currSeen)
44+
| pos == target = (nbMoves, S.singleton target, currSeen)
45+
| length bestSol /= 0 = (bestMoves, S.insert pos bestPath, seenRight)
46+
| otherwise = (-1, S.empty, seenRight)
47+
where
48+
currSeen = M.insert pos nbMoves seen
49+
(movesUp, pathUp, seenUp) = smallestPathFind memory currSeen (move pos UP) target (nbMoves + 1)
50+
(movesLeft, pathLeft, seenLeft) = smallestPathFind memory seenUp (move pos LEFT) target (nbMoves + 1)
51+
(movesDown, pathDown, seenDown) = smallestPathFind memory seenLeft (move pos DOWN) target (nbMoves + 1)
52+
(movesRight, pathRight, seenRight) = smallestPathFind memory seenDown (move pos RIGHT) target (nbMoves + 1)
53+
54+
bestSol = sortOn fst . filter ((/= -1) . fst) $ [(movesUp, pathUp), (movesLeft, pathLeft), (movesDown, pathDown), (movesRight, pathRight)]
55+
(bestMoves, bestPath) = head bestSol
56+
57+
part1 :: Int -> Input -> Output
58+
part1 range input = fst3 $ smallestPathFind memory M.empty (0, 0) (range, range) 0
59+
where
60+
memory = foldl (\r e -> S.insert e r) S.empty $ take 1024 input
61+
62+
anyPathFind :: Set (Int, Int) -> Map (Int, Int) Int -> (Int, Int) -> (Int, Int) -> Int -> (Int, Set (Int, Int), Map (Int, Int) Int)
63+
anyPathFind memory seen pos target nbMoves
64+
-- \| trace ("Pos: " ++ show pos) False = (-1, S.empty, seen)
65+
| isOut target pos = (-1, S.empty, seen)
66+
| pos `M.member` seen && seen M.! pos <= nbMoves = (-1, S.empty, seen) -- trace ("At " ++ show nbMoves ++ " but already seen at " ++ show (seen M.! pos))
67+
| pos `S.member` memory = (-1, S.empty, currSeen)
68+
| pos == target = (nbMoves, S.singleton target, currSeen)
69+
| movesUp /= -1 = (movesUp, S.insert pos pathUp, seenUp)
70+
| movesLeft /= -1 = (movesLeft, S.insert pos pathLeft, seenLeft)
71+
| movesDown /= -1 = (movesDown, S.insert pos pathDown, seenDown)
72+
| movesRight /= -1 = (movesRight, S.insert pos pathRight, seenRight)
73+
| otherwise = (-1, S.empty, seenRight)
74+
where
75+
currSeen = M.insert pos nbMoves seen
76+
(movesUp, pathUp, seenUp) = anyPathFind memory currSeen (move pos UP) target (nbMoves + 1)
77+
(movesLeft, pathLeft, seenLeft) = anyPathFind memory seenUp (move pos LEFT) target (nbMoves + 1)
78+
(movesDown, pathDown, seenDown) = anyPathFind memory seenLeft (move pos DOWN) target (nbMoves + 1)
79+
(movesRight, pathRight, seenRight) = anyPathFind memory seenDown (move pos RIGHT) target (nbMoves + 1)
80+
81+
part2 :: Int -> Input -> (Int, Int)
82+
part2 range input = sub 1024 memory (drop 1024 input) (snd3 $ smallestPathFind memory M.empty (0, 0) (range, range) 0)
83+
where
84+
memory = foldl (\r e -> S.insert e r) S.empty $ take 1024 input
85+
sub n mem [] lastPath = (-1, -1)
86+
sub n mem (e : l) lastPath
87+
-- \| trace (show lastPath) False = (0, 0)
88+
| e `S.notMember` lastPath = sub (n + 1) newMem l lastPath
89+
| currRes == -1 = swap e
90+
| otherwise = sub (n + 1) newMem l currPath
91+
where
92+
newMem = S.insert e mem
93+
(currRes, currPath, _) = smallestPathFind newMem M.empty (0, 0) (range, range) 0
94+
95+
showMemory :: Int -> Set (Int, Int) -> [String]
96+
showMemory range memory = [[if S.member (y, x) memory then '#' else '.' | x <- [0 .. range]] | y <- [0 .. range]]
97+
98+
main :: IO ()
99+
main = do
100+
args <- getArgs
101+
content <- readFile (last args)
102+
let input = parseInput content
103+
let range = 70
104+
105+
-- print input
106+
107+
-- let memory = foldl (\r e -> S.insert e r) S.empty $ take 1024 input
108+
109+
-- putStrLn . unlines $ showMemory range memory
110+
111+
print $ part1 range input
112+
print $ part2 range input

2024/Day18/Makefile

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
CONTAINER_NAME=haskell-aoc
2+
SRC=./Day18.hs
3+
TARGET=Day18
4+
all: $(TARGET)
5+
6+
$(TARGET): $(SRC)
7+
ghc -O3 $(SRC)
8+
9+
profile: $(SRC)
10+
11+
12+
clean:
13+
$(RM) ./Day18 ./Day18.o ./Day18.hi
14+
15+
setup: $(SRC)
16+
docker cp $(SRC) $(CONTAINER_NAME):/home/haskell/Main.hs
17+
docker cp input.txt $(CONTAINER_NAME):/home/haskell/input.txt
18+
docker cp shortinput.txt $(CONTAINER_NAME):/home/haskell/shortinput.txt
19+
20+
profiling: setup
21+
docker exec -it $(CONTAINER_NAME) cabal build --enable-profiling
22+
docker exec -it $(CONTAINER_NAME) ./profile input.txt
23+
24+
run: setup
25+
docker exec -it $(CONTAINER_NAME) cabal build
26+
docker exec -it $(CONTAINER_NAME) ./run input.txt
27+
28+
.PHONY: all $(TARGET) clean setup profiling run

0 commit comments

Comments
 (0)