Skip to content

Latest commit

 

History

History
61 lines (46 loc) · 1.67 KB

day07.md

File metadata and controls

61 lines (46 loc) · 1.67 KB

This one works out well as a list monad based search. Essentially you are picking operations where:

targ == (x ? y) ? z

and if those ? operations induce a list monad split, you can then search all of the possible choices:

checkEquation :: [Int -> Int -> Int] -> Int -> [Int] -> Bool
checkEquation ops targ xs = targ `elem` foldl1M branchOnOp xs
  where
    branchOnOp a b = map (\f -> f a b) ops

Then you can do checkEquation [(+),(*)] for part 1 and checkEquation [(+),(*),cat] for part 2.

However, it is kind of helpful to work backwards from the target to see if you can get the initial number. For example, in 292: 11 6 16 20, you can eliminate * as an option for the final operation right off the bat.

So really, you can rephrase the problem as:

x == y ? (z ? targ)

where ? are the inverse operations, but you have some way to easily eliminate operations that don't make sense.

checkBackEquation :: [Int -> Int -> Maybe Int] -> Int -> [Int] -> Bool
checkBackEquation unOps targ (x:xs) = x `elem` foldrM branchOnUnOp targ xs
  where
    branchOnUnOp a b = mapMaybe (\f -> f a b) unOPs

And our un-ops are:

unAdd :: Int -> Int -> Maybe Int
unAdd x y = [y - x | y >= x]

unMul :: Int -> Int -> Maybe Int
unMul x y = [y `div` x | y `mod` x == 0]

unCat :: Int -> Int -> Maybe Int
unCat x y = [d | m == x]
  where
    pow = length . takeWhile (< x) $ iterate (* 10) 1
    (d, m) = y `divMod` (10 ^ pow)

So part 1 is checkBackEquation [unAdd, unMul] and part 2 is checkBackEquation [unAdd, unMul, unCat].

Timing-wise, moving from forwards to backwards brought my times for part 2 from 380ms to 1.2ms.