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 13
Browse files Browse the repository at this point in the history
  • Loading branch information
mstksg committed Dec 13, 2023
1 parent 0e59757 commit 9d189f0
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 101 deletions.
153 changes: 53 additions & 100 deletions src/AOC/Challenge/Day13.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.Day13
-- License : BSD3
Expand All @@ -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
}
9 changes: 8 additions & 1 deletion src/AOC/Common/Point.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ module AOC.Common.Point (
, memoPoint
, boundingBox
, boundingBox'
, fillBoundingBox'
, inBoundingBox
, minCorner, minCorner'
, contiguousRegions
Expand Down Expand Up @@ -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)

Expand All @@ -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)
Expand Down

0 comments on commit 9d189f0

Please sign in to comment.