2024-24 part 2 in haskell and a bit of manual execution
This commit is contained in:
parent
ade8a22794
commit
cfaf4e0111
2 changed files with 146 additions and 0 deletions
4
2024/24-Crossed_Wires/adder.drawio.svg
Normal file
4
2024/24-Crossed_Wires/adder.drawio.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 115 KiB |
142
2024/24-Crossed_Wires/second.hs
Normal file
142
2024/24-Crossed_Wires/second.hs
Normal file
|
@ -0,0 +1,142 @@
|
|||
-- requires cabal install --lib megaparsec parser-combinators heap vector
|
||||
module Main (main) where
|
||||
|
||||
import Control.Monad (void, when)
|
||||
import Data.Functor
|
||||
import qualified Data.List as L
|
||||
import qualified Data.Map as M
|
||||
import Data.Maybe
|
||||
import Data.Ord (comparing)
|
||||
import qualified Data.Set as S
|
||||
import Data.Void (Void)
|
||||
import Text.Megaparsec
|
||||
import Text.Megaparsec.Char
|
||||
|
||||
import Debug.Trace
|
||||
|
||||
type In = (String, Bool)
|
||||
data Op = And | Or | Xor deriving (Eq, Ord, Show)
|
||||
type Gate = (String, Op, String, String)
|
||||
data Input = Input [In] [Gate] deriving Show
|
||||
|
||||
type Parser = Parsec Void String
|
||||
|
||||
parseIn :: Parser (String, Bool)
|
||||
parseIn = (,) <$> some alphaNumChar <* string ": "
|
||||
<*> (char '1' $> True <|> char '0' $> False)
|
||||
|
||||
parseOp :: Parser Op
|
||||
parseOp = string "AND" $> And
|
||||
<|> string "OR" $> Or
|
||||
<|> string "XOR" $> Xor
|
||||
|
||||
parseGate :: Parser Gate
|
||||
parseGate = (,,,) <$> some alphaNumChar <* space
|
||||
<*> parseOp <* space
|
||||
<*> some alphaNumChar <* string " -> "
|
||||
<*> some alphaNumChar
|
||||
|
||||
parseInput' :: Parser Input
|
||||
parseInput' = Input <$> some (parseIn <* eol) <* eol
|
||||
<*> some (parseGate <* eol) <* eof
|
||||
|
||||
parseInput :: String -> IO Input
|
||||
parseInput filename = do
|
||||
input <- readFile filename
|
||||
case runParser parseInput' filename input of
|
||||
Left bundle -> error $ errorBundlePretty bundle
|
||||
Right input' -> return input'
|
||||
|
||||
exec And = ( && )
|
||||
exec Or = ( || )
|
||||
exec Xor = ( /= )
|
||||
|
||||
intToStringPad2 :: Int -> String
|
||||
intToStringPad2 i | i < 10 = '0':show i
|
||||
| otherwise = show i
|
||||
|
||||
--type GatesMap = M.Map String (String, Op, String)
|
||||
type GatesMap = M.Map (String, Op, String) String
|
||||
|
||||
-- The gates could be in the wrong order around the operation
|
||||
get :: GatesMap -> (String, Op, String) -> (GatesMap, String)
|
||||
get gates g@(a, op, b) = case M.lookup g gates of
|
||||
Just o -> (M.delete g gates, o)
|
||||
Nothing -> let g' = (b, op, a) in case M.lookup g' gates of
|
||||
Just o -> (M.delete g' gates, o)
|
||||
Nothing -> trace ("gate not found: " ++ show g) undefined
|
||||
|
||||
type SignalsMap = M.Map String String
|
||||
|
||||
getXYs :: String -> Op -> (GatesMap, SignalsMap) -> (GatesMap, SignalsMap)
|
||||
getXYs prefix op acc = L.foldl' getXY acc [0..44]
|
||||
where
|
||||
getXY :: (GatesMap, SignalsMap) -> Int -> (GatesMap, SignalsMap)
|
||||
getXY (gates, signals) i = let is = intToStringPad2 i
|
||||
(gates', s) = get gates ('x':is, op, 'y':is)
|
||||
in (gates', M.insert (prefix ++ is) s signals)
|
||||
|
||||
getSmbs :: (GatesMap, SignalsMap) -> (GatesMap, SignalsMap)
|
||||
getSmbs = getXYs "smb" Xor
|
||||
|
||||
getCarryBit0s :: (GatesMap, SignalsMap) -> (GatesMap, SignalsMap)
|
||||
getCarryBit0s = getXYs "cb0" And
|
||||
|
||||
solve :: (GatesMap, SignalsMap) -> (GatesMap, SignalsMap)
|
||||
solve acc = L.foldl' solveOne acc [1..44]
|
||||
where
|
||||
solveOne :: (GatesMap, SignalsMap) -> Int -> (GatesMap, SignalsMap)
|
||||
solveOne (gates, signals) i = let is = intToStringPad2 i
|
||||
js = intToStringPad2 $ i - 1
|
||||
cbp = signals M.! ("cb" ++ js)
|
||||
smb = signals M.! ("smb" ++ is)
|
||||
cb0 = signals M.! ("cb0" ++ is)
|
||||
(gates', cb1) = get gates (cbp, And, smb)
|
||||
signals' = M.insert ("cb1" ++ is) cb1 signals
|
||||
(gates'', cbn) = get gates' (cb1, Or, cb0)
|
||||
signals'' = M.insert ("cb" ++ is) cbn signals'
|
||||
in (gates'', signals'')
|
||||
|
||||
swapSignals :: [Gate] -> String -> String -> [Gate]
|
||||
swapSignals gates x y = let gm = M.fromList $ map (\(a, op, b, out) -> (out, (a, op, b))) gates
|
||||
xo = gm M.! x
|
||||
yo = gm M.! y
|
||||
gm' = M.insert x yo $ M.insert y xo gm
|
||||
in map (\(out, (a, op, b)) -> (a, op, b, out)) $ M.toList gm'
|
||||
|
||||
-- I build the adder from the available gates, following the structure in the
|
||||
-- svg file along this code. The swapSignals arguments have been determined by
|
||||
-- rerunning manually this program 4 times.
|
||||
--
|
||||
-- When I got a 'gate not found: ("z11",Or,"dpf")' error, I ran:
|
||||
-- ```
|
||||
-- grep z11 input (base)
|
||||
-- gkc AND qqw -> z11
|
||||
-- ```
|
||||
-- followed by
|
||||
-- ```
|
||||
-- grep dpf input (base)
|
||||
-- wpd OR dpf -> dtq
|
||||
-- y11 AND x11 -> dpf
|
||||
-- ```
|
||||
--
|
||||
-- The only OR operation is the oen between wpd and dpf, with dpf as a common
|
||||
-- signal. The two signals to swap are therefore z11 and wpd which I add bellow.
|
||||
-- Rince and repeat until all four swaps are identified.
|
||||
compute :: Input -> String
|
||||
compute (Input _ gates) = trace (show $ gs''') $ L.intercalate "," $ L.sort ["z11", "wpd", "skh", "jqf", "z19", "mdd", "z37", "wts"]
|
||||
where
|
||||
gates' = swapSignals gates "z11" "wpd"
|
||||
gates'' = swapSignals gates' "skh" "jqf"
|
||||
gates''' = swapSignals gates'' "z19" "mdd"
|
||||
gates'''' = swapSignals gates''' "z37" "wts"
|
||||
gatesMap = M.fromList $ map (\(a, op, b, out) -> ((a, op, b), out)) gates''''
|
||||
(gm, gs) = getSmbs (gatesMap, M.empty)
|
||||
(gm', gs') = getCarryBit0s (gm, gs)
|
||||
gs'' = M.insert "cb00" (gs' M.! "cb000") gs'
|
||||
(gm'', gs''') = solve (gm', gs'')
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
input <- parseInput "input"
|
||||
print $ compute input
|
Loading…
Add table
Add a link
Reference in a new issue