-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay24.hs
96 lines (89 loc) · 4.01 KB
/
Day24.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
-- |
-- Module: Day24
-- Description: <https://adventofcode.com/2024/day/24 Day 24: Crossed Wires>
module Day24 (part1, part2) where
import Control.Applicative ((<|>))
import Control.Monad (foldM, join, liftM2)
import Data.Bifunctor (bimap)
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, 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 = 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 (,)
<$> 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 = 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
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