diff --git a/src/AOC/Challenge/Day13.hs b/src/AOC/Challenge/Day13.hs index 2af6b07..8b2090c 100644 --- a/src/AOC/Challenge/Day13.hs +++ b/src/AOC/Challenge/Day13.hs @@ -1,6 +1,3 @@ -{-# OPTIONS_GHC -Wno-unused-imports #-} -{-# OPTIONS_GHC -Wno-unused-top-binds #-} - -- | -- Module : AOC.Challenge.Day13 -- License : BSD3 @@ -9,115 +6,71 @@ -- Portability : non-portable -- -- Day 13. 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.Day13 ( - day13a - , day13b - ) where +module AOC.Challenge.Day13 + ( day13a, + day13b, + ) +where -import AOC.Prelude - -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 qualified Data.Set as S -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 - --- negate (i-x-0.5) + x+0.5 --- -i+x+0.5+x+0.5 --- -i+x+x+1 +import AOC.Common (firstJust) +import AOC.Common.Point (Point, fillBoundingBox', parseAsciiSet) +import AOC.Solver (noFail, (:~>) (..)) +import Control.Lens (contains, over) +import Data.Foldable (toList) +import Data.IntMap (IntMap) +import qualified Data.IntMap as IM +import Data.IntSet (IntSet) +import qualified Data.IntSet as IS +import Data.List.Split (splitOn) +import Data.Set (Set) +import Linear.V2 (V2 (..)) findRefl :: Set Point -> IntSet -findRefl pts = IS.fromList findHoriz - <> IS.fromList ((*100) <$> findVert) +findRefl pts = + IS.fromList (findForMap cols) + <> IS.fromList ((* 100) <$> findForMap rows) where - Just (V2 (V2 xMin yMin) (V2 xMax yMax)) = boundingBox' pts cols :: IntMap IntSet - cols = IM.fromListWith (<>) - [ (x, IS.singleton y) - | V2 x y <- toList pts - ] + cols = + IM.fromListWith + (<>) + [ (x, IS.singleton y) + | V2 x y <- toList pts + ] rows :: IntMap IntSet - rows = IM.fromListWith (<>) - [ (y, IS.singleton x) - | V2 x y <- toList pts - ] - findHoriz = flip filter [xMin+1 .. xMax] $ \x -> - let (lt, gt) = IM.spanAntitone (< x) cols + rows = + IM.fromListWith + (<>) + [ (y, IS.singleton x) + | V2 x y <- toList pts + ] + findForMap :: IntMap IntSet -> [Int] + findForMap mp = flip filter [minKey + 1 .. maxKey] $ \i -> + let (lt, gt) = IM.spanAntitone (< i) mp in and $ zipWith (==) (snd <$> IM.toDescList lt) (snd <$> IM.toAscList gt) - -- let cols = IntMap IntSet - -- cols = IM.fromListWith (<>) $ toList pts - -- let (lt,gt) = S.partition ((< x) . view _x) pts - -- gt' = S.map (over _x ((+ (x-1)) . negate . subtract x)) gt - -- inters = lt `S.intersection` gt' - -- in inters == lt || inters == gt' - findVert = flip filter [yMin+1 .. yMax] $ \y -> - let (lt, gt) = IM.spanAntitone (< y) rows - in and $ zipWith (==) (snd <$> IM.toDescList lt) (snd <$> IM.toAscList gt) - -- findVert = flip find [yMin+1 .. yMax] $ \y -> - -- let (lt,gt) = S.partition ((< y) . view _y) pts - -- gt' = S.map (over _y ((+ (y-1)) . negate . subtract y)) gt - -- inters = lt `S.intersection` gt' - -- in inters == lt || inters == gt' - -- let flipped = S.map (\(V2 i j) -> V2 (-i + 2*x+1) j) pts - -- -- symDiff = (flipped S.// pts) <> (pts S.// flipped) - -- -- inters = S.intersection flipped pts - -- in shiftToZero' (flipped `S.difference` pts) == shiftToZero' (pts `S.difference` flipped) - -- x <$ guard (S.map (\(V2 i j) -> V2 (negate (i - x)) j) pts == S.map (\(V2 i j) -> V2 (i - x) j) pts) - -- findVert = flip find [yMin .. yMax] $ \y -> - -- let flipped = S.map (\(V2 i j) -> V2 i (-j + 2*y+1)) pts - -- -- symDiff = (flipped S.// pts) <> (pts S.// flipped) - -- -- inters = S.intersection flipped pts - -- in shiftToZero' (flipped `S.difference` pts) == shiftToZero' (pts `S.difference` flipped) - -- -- y <$ guard (S.map (\(V2 i j) -> V2 i (-j + 2*y)) pts == S.map (\(V2 i j) -> V2 i (j - y)) pts) - --- condenseRefl :: Maybe Int -> Maybe Int -> Maybe Int --- condenseRefl horiz vert = horiz <|> ((* 100) <$> vert) + where + (minKey, _) = IM.findMin mp + (maxKey, _) = IM.findMax mp -day13a :: _ :~> _ -day13a = MkSol - { sParse = noFail $ map (parseAsciiSet (== '#')) . splitOn "\n\n" - , sShow = show - -- , sSolve = noFail $ map findRefl - , sSolve = fmap sum . traverse (fmap fst . IS.minView . findRefl) +day13a :: [Set Point] :~> Int +day13a = + MkSol + { sParse = noFail $ map (parseAsciiSet (== '#')) . splitOn "\n\n", + sShow = show, + sSolve = fmap sum . traverse (fmap fst . IS.minView . findRefl) } findSmudge :: Set Point -> Maybe Int -findSmudge pts = flip firstJust (V2 <$> [xMin .. xMax] <*> [yMin .. yMax]) \pt -> - let newRefl = findRefl (over (contains pt) not pts) - isNew = newRefl `IS.difference` origRefl - in fst <$> IS.minView isNew - -- in guard (newRefl /= origRefl) *> (fst <$> IS.minView isNew) - -- mfilter (/= origRefl) $ findRefl (over (contains pt) not pts) +findSmudge pts = flip firstJust (fillBoundingBox' pts) \pt -> + let newRefl = findRefl (over (contains pt) not pts) + in fst <$> IS.minView (newRefl `IS.difference` origRefl) where origRefl = findRefl pts - Just (V2 (V2 xMin yMin) (V2 xMax yMax)) = boundingBox' pts -day13b :: _ :~> _ -day13b = MkSol - { sParse = sParse day13a - , sShow = show - , sSolve = fmap sum . traverse findSmudge +day13b :: [Set Point] :~> Int +day13b = + MkSol + { sParse = noFail $ map (parseAsciiSet (== '#')) . splitOn "\n\n", + sShow = show, + sSolve = fmap sum . traverse findSmudge } diff --git a/src/AOC/Common/Point.hs b/src/AOC/Common/Point.hs index 6659bdb..d880a53 100644 --- a/src/AOC/Common/Point.hs +++ b/src/AOC/Common/Point.hs @@ -37,6 +37,7 @@ module AOC.Common.Point ( , memoPoint , boundingBox , boundingBox' + , fillBoundingBox' , inBoundingBox , minCorner, minCorner' , contiguousRegions @@ -116,6 +117,13 @@ boundingBox = (\(T2 (Ap mn) (Ap mx)) -> V2 (getMin <$> mn) (getMax <$> mx)) boundingBox' :: (Foldable f, Applicative g, Ord a) => f (g a) -> Maybe (V2 (g a)) boundingBox' = fmap boundingBox . NE.nonEmpty . toList +fillBoundingBox' + :: (Foldable f, Applicative g, Num a, Ord a, Ord (g a), Traversable g, Enum a) + => f (g a) -> Set (g a) +fillBoundingBox' ps = case boundingBox' ps of + Nothing -> S.empty + Just (V2 mins maxs) -> S.fromList $ sequenceA $ liftA2 enumFromTo mins maxs + minCorner :: (Foldable1 f, Applicative g, Ord a) => f (g a) -> g a minCorner = fmap getMin . getAp . foldMap1 (Ap . fmap Min) @@ -138,7 +146,6 @@ shiftToZero' ps = case minCorner' ps of Nothing -> ps Just mn -> S.mapMonotonic (liftA2 subtract mn) ps - inBoundingBox :: (Applicative g, Foldable g, Ord a) => V2 (g a)