diff --git a/solutions/src/2024/13.hs b/solutions/src/2024/13.hs index c460a38..7a57d9d 100644 --- a/solutions/src/2024/13.hs +++ b/solutions/src/2024/13.hs @@ -1,4 +1,4 @@ -{-# Language QuasiQuotes #-} +{-# Language QuasiQuotes, NumDecimals #-} {-| Module : Main Description : Day 13 solution @@ -37,6 +37,8 @@ Prize: X=18641, Y=10279 module Main (main) where import Advent (format) +import GHC.Num (integerGcde) +import Debug.Trace -- | >>> :main -- 29877 @@ -44,25 +46,40 @@ import Advent (format) main :: IO () main = do input <- [format|2024 13 - (Button A: X%+%u, Y%+%u%n - Button B: X%+%u, Y%+%u%n - Prize: X=%u, Y=%u%n)&%n|] - print (sum (map (cost 0) input)) - print (sum (map (cost 10000000000000) input)) + (Button A: X%+%lu, Y%+%lu%n + Button B: X%+%lu, Y%+%lu%n + Prize: X=%lu, Y=%lu%n)&%n|] + print (sum (map (cost 0) input)) + print (sum (map (cost 1e13) input)) -cost :: Int -> (Int, Int, Int, Int, Int, Int) -> Integer +cost :: Integer -> (Integer, Integer, Integer, Integer, Integer, Integer) -> Integer cost extra (ax, ay, bx, by, x, y) - | det == 0 = error "colinearity not supported" - | a >= 0, b >= 0, ar == 0, br == 0 = 3 * a + b + | det == 0 = colinear ax ay bx by x y + | (a, 0) <- (by * x' - bx * y') `quotRem` det, a >= 0 + , (b, 0) <- (ax * y' - ay * x') `quotRem` det, b >= 0 + = 3 * a + b | otherwise = 0 where - ax' = toInteger ax - ay' = toInteger ay - bx' = toInteger bx - by' = toInteger by - x' = toInteger (x + extra) - y' = toInteger (y + extra) - - det = ax' * by' - ay' * bx' - (a, ar) = (by' * x' - bx' * y') `quotRem` det - (b, br) = (ax' * y' - ay' * x') `quotRem` det + det = ax * by - ay * bx + x' = x + extra + y' = y + extra + +colinear :: Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer +colinear ax ay bx by x y + | ax*by == bx*ay -- buttons are colinear with each other + , ax*y == x*ay -- output is colinear with buttons + , let (d, e, f) = integerGcde ax bx + , (h, 0) <- x `quotRem` d -- target x is a multiple of the gcd of the two button +x + , let a = e * h -- prototypical (but potentially negative) solution + , let b = f * h -- prototypical (but potentially negative) solution + , let u = ax `quot` d + , let v = - (bx `quot` d) + , let klo = a `divCeil` v + , let khi = b `div` u + , klo <= khi + = minimum [3 * (a - k * v) + (b - k * u) | k <- [klo, khi]] -- the relation is linear so one of the extremes is the answer + | otherwise = 0 + +-- Division that rounds up +divCeil :: Integral a => a -> a -> a +x `divCeil` y = (x + y - 1) `div` y