Skip to content
This repository has been archived by the owner on Nov 17, 2024. It is now read-only.

Commit

Permalink
clean up day 16
Browse files Browse the repository at this point in the history
  • Loading branch information
mstksg committed Dec 21, 2023
1 parent f6c905b commit 5f4032c
Showing 1 changed file with 25 additions and 135 deletions.
160 changes: 25 additions & 135 deletions src/AOC/Challenge/Day16.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
{-# OPTIONS_GHC -Wno-unused-imports #-}
{-# OPTIONS_GHC -Wno-unused-top-binds #-}

-- |
-- Module : AOC.Challenge.Day16
-- License : BSD3
Expand All @@ -9,43 +6,23 @@
-- Portability : non-portable
--
-- Day 16. See "AOC.Solver" for the types used in this module!
--
-- After completing the challenge, it is recommended to:
--
-- * Replace "AOC.Prelude" imports to specific modules (with explicit
-- imports) for readability.
-- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@
-- pragmas.
-- * Replace the partial type signatures underscores in the solution
-- types @_ :~> _@ with the actual types of inputs and outputs of the
-- solution. You can delete the type signatures completely and GHC
-- will recommend what should go in place of the underscores.
module AOC.Challenge.Day16
( day16a,
day16b,
)
where

import AOC.Prelude
import Data.Functor.Foldable hiding (fold)
import Data.Functor.Foldable.TH (MakeBaseFunctor (makeBaseFunctor))
import qualified Data.Graph.Inductive as G
import qualified Data.IntMap as IM
import qualified Data.IntSet as IS
import qualified Data.List.NonEmpty as NE
import qualified Data.List.PointedList as PL
import qualified Data.List.PointedList.Circular as PLC
import qualified Data.Map as M
import qualified Data.OrdPSQ as PSQ
import qualified Data.Sequence as Seq
import AOC.Common.Point (Dir (..), Point, boundingBox, dirPoint', inBoundingBox, parseAsciiMap)
import AOC.Solver (noFail, (:~>) (..))
import Control.DeepSeq (NFData)
import Data.Functor ((<&>))
import Data.Map.NonEmpty (NEMap)
import qualified Data.Map.NonEmpty as NEM
import Data.Set (Set)
import qualified Data.Set as S
import qualified Data.Set.NonEmpty as NES
import qualified Data.Text as T
import qualified Data.Vector as V
import qualified Linear as L
import qualified Text.Megaparsec as P
import qualified Text.Megaparsec.Char as P
import qualified Text.Megaparsec.Char.Lexer as PP
import GHC.Generics (Generic)
import Linear.V2 (V2 (..))
import Safe (maximumMay)

data Tile = SplitNS | SplitEW | MirrorNE | MirrorNW
deriving stock (Show, Generic, Eq, Ord)
Expand Down Expand Up @@ -85,127 +62,40 @@ process = \case
East -> South
West -> North

unProcess :: Tile -> Dir -> Set Dir
unProcess = \case
SplitNS ->
S.fromList . \case
North -> [North, East, West]
South -> [South, East, West]
East -> []
West -> []
SplitEW ->
S.fromList . \case
North -> []
South -> []
East -> [East, North, South]
West -> [West, North, South]
MirrorNE ->
S.singleton . \case
North -> East
South -> West
East -> North
West -> South
MirrorNW ->
S.singleton . \case
North -> West
South -> East
East -> South
West -> North

data PathTrie k v = PTNode !v (Map k (PathTrie k v))
deriving stock (Show, Generic, Eq, Ord)

instance (NFData v, NFData k) => NFData (PathTrie k v)

makeBaseFunctor ''PathTrie

stepTrie :: Map Point Tile -> (Set (Point, Dir), (Point, Dir)) -> PathTrieF Dir Point (Set (Point, Dir), (Point, Dir))
stepTrie mp = go
where
Just bb = boundingBox' (M.keys mp)
go (seen, (p, d))
| (p, d) `S.member` seen = PTNodeF p M.empty
| otherwise =
PTNodeF p $
M.fromList
[ (d', (S.insert (p, d) seen, (p', d')))
| (p', d') <- pd',
inBoundingBox bb p'
]
where
pd' = case M.lookup p mp of
Just t -> process t d <&> \newDir -> (p + dirPoint' newDir, newDir)
Nothing -> pure (p + dirPoint' d, d)

triePoints :: PathTrieF Dir Point (Set Point) -> Set Point
triePoints (PTNodeF p xs) = S.insert p $ fold xs

runMap :: Map Point Tile -> [[Point]]
runMap mp = go 0 East
where
Just bb = boundingBox' (M.keys mp)
go !p !d
| inBoundingBox bb p = map (p :) case M.lookup p mp of
Nothing -> go (p + dirPoint' d) d
Just t -> do
d' <- process t d
go (p + dirPoint' d') d'
| otherwise = pure []

-- | this would have been too good to be true lol
fillMap :: Map Point Tile -> Map Point (Map Dir Bool)
fillMap mp = res
where
res = M.fromSet go (fillBoundingBox' (M.keys mp))
go p = flip M.fromSet (NES.toSet allDirSet) \d ->
let q = p + dirPoint' (d <> South)
d' = case M.lookup q mp of
Nothing -> S.singleton d
Just t -> unProcess t d
in case M.lookup q res of
Nothing -> False
Just ds -> or $ ds `M.restrictKeys` d'

searchPoints :: Map Point Tile -> Point -> Dir -> Set Point
searchPoints :: NEMap Point Tile -> Point -> Dir -> Set Point
searchPoints mp p0 d0 = go S.empty (S.singleton (p0, d0))
where
Just bb = boundingBox' (M.keys mp)
bb = boundingBox (NEM.keys mp)
go seen queue = case S.minView queue of
Nothing -> S.map fst seen
Just ((p, d), queue') ->
let stepped = filter (inBoundingBox bb . fst) case M.lookup p mp of
let stepped = filter (inBoundingBox bb . fst) case NEM.lookup p mp of
Nothing -> [(p + dirPoint' d, d)]
Just t -> process t d <&> \d' -> (p + dirPoint' d', d')
newSteps = S.fromList stepped `S.difference` seen
in go (S.insert (p, d) seen) (newSteps <> queue')

day16a :: _ :~> _
day16a :: NEMap Point Tile :~> Int
day16a =
MkSol
{ sParse = Just . parseAsciiMap classify,
sShow = show,
-- sShow = ('\n' :) . displayAsciiSet '.' '#',
-- , sSolve = noFail $ fillMap
-- , sSolve = noFail $ \mp -> ana (stepTrie mp) (0, East) :: PathTrie Dir Point
-- , sSolve = noFail $ \mp -> S.size $ hylo triePoints (stepTrie mp) (S.empty, (0, East))
{ sParse = NEM.nonEmptyMap . parseAsciiMap classify,
sShow = show,
sSolve = noFail $ \mp -> S.size $ searchPoints mp 0 East
-- , sSolve = Just . S.size . foldMap S.fromList . runMap
}

day16b :: _ :~> _
day16b :: NEMap Point Tile :~> Int
day16b =
MkSol
{ sParse = sParse day16a,
sShow = show,
sSolve = \mp -> do
V2 (V2 xMin yMin) (V2 xMax yMax) <- boundingBox' (M.keys mp)
let allStarts = concat
[ (,South) . (`V2` yMin) <$> [xMin .. xMax]
, (,North) . (`V2` yMax) <$> [xMin .. xMax]
, (,East) . V2 xMin <$> [yMin .. yMax]
, (,West) . V2 xMax <$> [yMin .. yMax]
]
let V2 (V2 xMin yMin) (V2 xMax yMax) = boundingBox (NEM.keys mp)
allStarts =
concat
[ (,South) . (`V2` yMin) <$> [xMin .. xMax],
(,North) . (`V2` yMax) <$> [xMin .. xMax],
(,East) . V2 xMin <$> [yMin .. yMax],
(,West) . V2 xMax <$> [yMin .. yMax]
]
maximumMay $ S.size . uncurry (searchPoints mp) <$> allStarts


}

0 comments on commit 5f4032c

Please sign in to comment.