Skip to content

Commit

Permalink
Day 22: Sand Slabs
Browse files Browse the repository at this point in the history
  • Loading branch information
ephemient committed Dec 22, 2023
1 parent c5fefa2 commit f876f78
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ Development occurs in language-specific directories:
|[Day19.hs](hs/src/Day19.hs)|[Day19.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day19.kt)|[day19.py](py/aoc2023/day19.py)|[day19.rs](rs/src/day19.rs)|
|[Day20.hs](hs/src/Day20.hs)|[Day20.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day20.kt)|[day20.py](py/aoc2023/day20.py)|[day20.rs](rs/src/day20.rs)|
|[Day21.hs](hs/src/Day21.hs)|[Day21.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day21.kt)|||
|[Day22.hs](hs/src/Day22.hs)||||
6 changes: 4 additions & 2 deletions hs/aoc2023.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ library
Day18,
Day19,
Day20,
Day21
Day21,
Day22

-- Modules included in this library but not exported.
other-modules:
Expand Down Expand Up @@ -113,7 +114,8 @@ test-suite aoc2023-test
Day18Spec,
Day19Spec,
Day20Spec,
Day21Spec
Day21Spec,
Day22Spec
build-depends:
aoc2023,
base ^>=4.17.2.0,
Expand Down
2 changes: 2 additions & 0 deletions hs/app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import qualified Day18 (part1, part2)
import qualified Day19 (part1, part2)
import qualified Day20 (part1, part2)
import qualified Day21 (solve)
import qualified Day22 (solve)

import Control.Monad (ap, when)
import Data.Foldable (find)
Expand Down Expand Up @@ -73,3 +74,4 @@ main = do
run 19 (either (fail . errorBundlePretty) print) [Day19.part1, Day19.part2]
run 20 (either (fail . errorBundlePretty) $ maybe (fail "error") print) [fmap Just . Day20.part1, Day20.part2]
run 21 print [Day21.solve 64]
run 22 (either fail $ uncurry ((>>) `on` print)) [Day22.solve]
4 changes: 4 additions & 0 deletions hs/bench/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import qualified Day18 (part1, part2)
import qualified Day19 (part1, part2)
import qualified Day20 (part1, part2)
import qualified Day21 (solve)
import qualified Day22 (solve)
import System.Environment.Blank (getEnv, setEnv, unsetEnv)
import System.FilePath (combine)

Expand Down Expand Up @@ -126,5 +127,8 @@ main = defaultMain
]
, env (getDayInput 20) $ \input -> bgroup "Day 21"
[ bench "part 1" $ nf (Day21.solve 64) input
, env (getDayInput 22) $ \input -> bgroup "Day 22"
[ bench "part 1" $ nf (fmap fst . Day22.solve) input
, bench "part 2" $ nf (fmap snd . Day22.solve) input
]
]
64 changes: 64 additions & 0 deletions hs/src/Day22.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{-|
Module: Day22
Description: <https://adventofcode.com/2023/day/22 Day 22: Sand Slabs>
-}
{-# LANGUAGE OverloadedStrings #-}
module Day22 (solve) where

import Common (readEntire)
import Control.Arrow ((***), first, second)
import Control.Parallel.Strategies (parMap, rseq)
import Data.List (foldl', mapAccumL, sortOn, tails)
import qualified Data.Map as Map ((!?), empty, fromDistinctAscList, fromListWith, keys, partition, toList, unionWith, update)
import Data.Maybe (fromMaybe)
import Data.Semigroup (Max(Max))
import qualified Data.Set as Set (delete, fromList, minView, null, singleton, size)
import Data.Text (Text)
import qualified Data.Text as T (lines, stripPrefix)
import qualified Data.Text.Read as T (decimal)
import Data.Text.Read (Reader)

parseLine :: (Integral a) => Reader ((a, a, a), (a, a, a))
parseLine text = do
(x0, text) <- T.decimal text
(y0, text) <- maybe (Left "expected ,") pure (T.stripPrefix "," text) >>= T.decimal
(z0, text) <- maybe (Left "expected ,") pure (T.stripPrefix "," text) >>= T.decimal
(x1, text) <- maybe (Left "expected ~") pure (T.stripPrefix "~" text) >>= T.decimal
(y1, text) <- maybe (Left "expected ,") pure (T.stripPrefix "," text) >>= T.decimal
(z1, text) <- maybe (Left "expected ,") pure (T.stripPrefix "," text) >>= T.decimal
pure (((x0, y0, z0), (x1, y1, z1)), text)

settle :: (Integral a) => [((a, a, a), (a, a, a))] -> [((a, a, a), (a, a, a))]
settle = sortOn bottom . snd . mapAccumL f Map.empty . sortOn bottom where
f zs ((x0, y0, z0), (x1, y1, z1)) = (zs', ((x0, y0, z + 1), (x1, y1, z'))) where
Max z = fromMaybe (Max 0) $
mconcat [Max <$> zs Map.!? (x, y) | x <- [x0..x1], y <- [y0..y1]]
z' = z + 1 - z0 + z1
zs' = Map.unionWith max zs $
Map.fromDistinctAscList [((x, y), z') | x <- [x0..x1], y <- [y0..y1]]
bottom ((_, _, z), _) = z

solve :: Text -> Either String (Int, Int)
solve input = do
bricks <- fmap settle . mapM (readEntire (parseLine @Int)) $ T.lines input
let (rdeps, deps) = (Map.fromListWith (<>) *** Map.fromListWith (<>)) $ unzip
[ ((below, Set.singleton above), (above, Set.singleton below))
| (below, ((x0, y0, _), (x1, y1, z))):rest <- tails $ zip @Int [0..] bricks
, (above, ((x2, y2, _), (x3, y3, _))) <-
takeWhile (\(_, ((_, _, z'), (_, _, _))) -> z' == z + 1) $
dropWhile (\(_, ((_, _, z'), (_, _, _))) -> z' <= z) rest
, x0 <= x3 && x2 <= x1 && y0 <= y3 && y2 <= y1
]
unsafe = Set.fromList
[ below
| (above, Just (below, below')) <- second Set.minView <$> Map.toList deps
, Set.null below'
]
countFalls = fst . accumulateFalls' (0, deps)
accumulateFalls' (k, deps') below = case rdeps Map.!? below of
Just above ->
let (below', deps'') = first Map.keys $ Map.partition Set.null $
foldl' (flip $ Map.update (pure . Set.delete below)) deps' above
in foldl' accumulateFalls' (k + length below', deps'') below'
Nothing -> (k, deps')
pure (length bricks - Set.size unsafe, sum . parMap rseq countFalls $ Map.keys rdeps)
27 changes: 27 additions & 0 deletions hs/test/Day22Spec.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{-# LANGUAGE OverloadedStrings #-}
module Day22Spec (spec) where

import Data.Text (Text)
import qualified Data.Text as T (unlines)
import Day22 (solve)
import Test.Hspec (Spec, describe, it, shouldBe, xit)

example :: Text
example = T.unlines
[ "1,0,1~1,2,1"
, "0,0,2~2,0,2"
, "0,2,3~2,2,3"
, "0,0,4~0,2,4"
, "2,0,5~2,2,5"
, "0,1,6~2,1,6"
, "1,1,8~1,1,9"
]

spec :: Spec
spec = do
describe "part 1" $ do
it "examples" $ do
fst <$> solve example `shouldBe` Right 5
describe "part 2" $ do
it "examples" $ do
snd <$> solve example `shouldBe` Right 7

0 comments on commit f876f78

Please sign in to comment.