Skip to content

Commit

Permalink
Day 24: Crossed Wires (part 2)
Browse files Browse the repository at this point in the history
  • Loading branch information
ephemient committed Dec 24, 2024
1 parent d0cab07 commit 4a24664
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 23 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ Development occurs in language-specific directories:
|[Day21.hs](hs/src/Day21.hs)|[Day21.kt](kt/aoc2024-lib/src/jvmCodegen/kotlin/com/github/ephemient/aoc2024/codegen/Day21.kt)|[day21.py](py/aoc2024/day21.py)|[day21.rs](rs/src/day21.rs)|
|[Day22.hs](hs/src/Day22.hs)|[Day22.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day22.kt)|[day22.py](py/aoc2024/day22.py)|[day22.rs](rs/src/day22.rs)|
|[Day23.hs](hs/src/Day23.hs)|[Day23.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day23.kt)|[day23.py](py/aoc2024/day23.py)|[day23.rs](rs/src/day23.rs)|
|[Day24.hs](hs/src/Day24.hs) ½||||
|[Day24.hs](hs/src/Day24.hs)||||
6 changes: 4 additions & 2 deletions hs/app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import Day20 qualified (solve)
import Day21 qualified (solve)
import Day22 qualified (part1, part2)
import Day23 qualified (part1, part2)
import Day24 qualified (part1)
import Day24 qualified (part1, part2)
import Day3 qualified (part1, part2)
import Day4 qualified (part1, part2)
import Day5 qualified (part1, part2)
Expand Down Expand Up @@ -80,4 +80,6 @@ main = do
run 21 print [Day21.solve 2, Day21.solve 25]
run 22 (either fail print) [Day22.part1, Day22.part2]
run 23 putStrLn [show . Day23.part1, T.unpack . Day23.part2]
run 24 (either (fail . errorBundlePretty) (maybe (fail "error") print)) [Day24.part1]
run 24 (either (fail . errorBundlePretty) (maybe (fail "error") putStrLn)) [fmap2 show . Day24.part1, fmap2 T.unpack . Day24.part2]
where
fmap2 = fmap . fmap
5 changes: 3 additions & 2 deletions hs/bench/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import Day20 qualified (solve)
import Day21 qualified (solve)
import Day22 qualified (part1, part2)
import Day23 qualified (part1, part2)
import Day24 qualified (part1)
import Day24 qualified (part1, part2)
import Day3 qualified (part1, part2)
import Day4 qualified (part1, part2)
import Day5 qualified (part1, part2)
Expand Down Expand Up @@ -187,6 +187,7 @@ main =
env (getDayInput 24) $ \input ->
bgroup
"Day 24"
[ bench "part 1" $ nf Day24.part1 input
[ bench "part 1" $ nf Day24.part1 input,
bench "part 2" $ nf Day24.part2 input
]
]
77 changes: 62 additions & 15 deletions hs/src/Day24.hs
Original file line number Diff line number Diff line change
@@ -1,49 +1,96 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}

-- |
-- Module: Day24
-- Description: <https://adventofcode.com/2024/day/24 Day 24: Crossed Wires>
module Day24 (part1) where
module Day24 (part1, part2) where

import Control.Applicative ((<|>))
import Control.Monad (join, liftM2)
import Control.Monad (foldM, join, liftM2)
import Data.Bifunctor (bimap)
import Data.Bits ((.&.), (.^.), (.|.))
import Data.Bits (Bits, (.&.), (.^.), (.|.))
import Data.Char (isAlphaNum)
import Data.Functor (($>))
import Data.List (sort)
import Data.Map (Map)
import Data.Map qualified as Map (dropWhileAntitone, fromList, takeWhileAntitone, (!?))
import Data.Maybe (mapMaybe)
import Data.Set qualified as Set (empty, fromList, toList)
import Data.String (IsString)
import Data.Text (Text)
import Data.Text qualified as T (singleton)
import Data.Text (Text, pattern (:<))
import Data.Text qualified as T (intercalate, singleton, stripPrefix)
import Data.Tuple (swap)
import Data.Void (Void)
import Text.Megaparsec (MonadParsec, ParseErrorBundle, Token, Tokens, choice, parse, sepEndBy, takeWhile1P)
import Text.Megaparsec.Char (newline, string)
import Text.Megaparsec.Char.Lexer qualified as L (decimal)

data Expr a = Expr a :&: Expr a | Expr a :|: Expr a | Expr a :^: Expr a | Literal a
data Expr a = a :&: a | a :|: a | a :^: a deriving (Eq, Ord)

parser :: (MonadParsec e s m, IsString (Tokens s), Token s ~ Char, Num a) => m ([(Tokens s, a)], [(Tokens s, Expr (Tokens s))])
parser = (,) <$> (initial `sepEndBy` newline) <* newline <*> (wire `sepEndBy` newline)
where
initial = (,) <$> takeWhile1P Nothing isAlphaNum <* string ": " <*> L.decimal
wire =
flip (,)
<$> ( (flip ($) . Literal <$> takeWhile1P Nothing isAlphaNum)
<*> choice [string " AND " $> (:&:), string " OR " $> (:|:), string " XOR " $> (:^:)]
<*> (Literal <$> takeWhile1P Nothing isAlphaNum)
)
<$> do
lhs <- takeWhile1P Nothing isAlphaNum
op <- choice [string " AND " $> (:&:), string " OR " $> (:|:), string " XOR " $> (:^:)]
rhs <- takeWhile1P Nothing isAlphaNum
pure $ min lhs rhs `op` max lhs rhs
<* string " -> "
<*> takeWhile1P Nothing isAlphaNum

eval :: (Ord k, Bits a) => Map k (Expr k) -> (k -> Maybe a) -> Map k (Maybe a)
eval wires f = values'
where
values' = fmap eval' wires
eval' (a :&: b) = (.&.) <$> eval'' a <*> eval'' b
eval' (a :|: b) = (.|.) <$> eval'' a <*> eval'' b
eval' (a :^: b) = (.^.) <$> eval'' a <*> eval'' b
eval'' a = f a <|> join (values' Map.!? a)

part1 :: Text -> Either (ParseErrorBundle Text Void) (Maybe Int)
part1 input = do
(givens, wires) <- bimap Map.fromList Map.fromList <$> parse parser "" input
let values = fmap eval wires
eval (a :&: b) = (.&.) <$> eval a <*> eval b
eval (a :|: b) = (.|.) <$> eval a <*> eval b
eval (a :^: b) = (.^.) <$> eval a <*> eval b
eval (Literal a) = givens Map.!? a <|> join (values Map.!? a)
let values = eval wires (givens Map.!?)
pure $
foldr (liftM2 . flip $ (+) . (*) 2) (Just 0) $
Map.takeWhileAntitone (< T.singleton (succ 'z')) $
Map.dropWhileAntitone (< "z") values

part2 :: Text -> Either (ParseErrorBundle Text Void) (Maybe Text)
part2 input = do
(_ :: [(Text, Int)], wires) <- parse parser "" input
let wires' = Map.fromList $ swap <$> wires
pure $ fmap finish $ foldM go (Set.empty, Nothing, wires') $ sort $ mapMaybe (T.stripPrefix "z") $ fst <$> wires

Check warning on line 67 in hs/src/Day24.hs

View workflow job for this annotation

GitHub Actions / lint

Warning in part2 in module Day24: Redundant <$> ▫︎ Found: "mapMaybe (T.stripPrefix \"z\") $ fst <$> wires" ▫︎ Perhaps: "mapMaybe (T.stripPrefix \"z\" . fst) wires"
where
go (acc, carry, wires) suffix
| Just carry' <- carry = case wires Map.!? (x :^: y) of
Nothing -> if carry' /= z then swizzle carry' z else pure (acc, Nothing, wires)
Just halfAdd -> case wires Map.!? (min halfAdd carry' :^: max halfAdd carry') of
Nothing -> do
halfAdd' <- wires Map.!? (x :&: y)
swizzle halfAdd halfAdd'
Just fullAdd ->
if fullAdd /= z
then swizzle fullAdd z
else
let carry'' = do
overflow1 <- wires Map.!? (x :&: y)
overflow2 <- wires Map.!? (min halfAdd carry' :&: max halfAdd carry')
wires Map.!? (min overflow1 overflow2 :|: max overflow1 overflow2)
in pure (acc, carry'', wires)
| "z00" <- z = do
add <- wires Map.!? (x :^: y)
if add /= z then swizzle add z else pure (acc, wires Map.!? (x :&: y), wires)
| otherwise = Nothing
where
x = 'x' :< suffix
y = 'y' :< suffix
z = 'z' :< suffix
swizzle a b = go (acc <> Set.fromList [a, b], carry, fmap f wires) suffix
where
f c | c == a = b | c == b = a | otherwise = c
finish (acc, _, _) = T.intercalate "," $ Set.toList acc
31 changes: 28 additions & 3 deletions hs/test/Day24Spec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ module Day24Spec (spec) where

import Data.Text (Text)
import Data.Text qualified as T (unlines)
import Day24 (part1)
import Test.Hspec (Spec, describe, it, shouldBe)
import Day24 (part1, part2)
import Test.Hspec (Spec, describe, it, shouldBe, xit)

example1, example2 :: Text
example1, example2, example3 :: Text
example1 =
T.unlines
[ "x00: 1",
Expand Down Expand Up @@ -71,10 +71,35 @@ example2 =
"tgd XOR rvg -> z12",
"tnw OR pbm -> gnj"
]
example3 =
T.unlines
[ "x00: 0",
"x01: 1",
"x02: 0",
"x03: 1",
"x04: 0",
"x05: 1",
"y00: 0",
"y01: 0",
"y02: 1",
"y03: 1",
"y04: 0",
"y05: 1",
"",
"x00 AND y00 -> z05",
"x01 AND y01 -> z02",
"x02 AND y02 -> z01",
"x03 AND y03 -> z03",
"x04 AND y04 -> z04",
"x05 AND y05 -> z00"
]

spec :: Spec
spec = do
describe "part 1" $ do
it "examples" $ do
part1 example1 `shouldBe` Right (Just 4)
part1 example2 `shouldBe` Right (Just 2024)
describe "part 1" $ do
xit "examples" $ do
part2 example3 `shouldBe` Right (Just "z00,z01,z02,z05")

0 comments on commit 4a24664

Please sign in to comment.