Skip to content

Commit

Permalink
handle colinear case
Browse files Browse the repository at this point in the history
  • Loading branch information
glguy committed Dec 13, 2024
1 parent c9f3bed commit 4dd8a09
Showing 1 changed file with 36 additions and 19 deletions.
55 changes: 36 additions & 19 deletions solutions/src/2024/13.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{-# Language QuasiQuotes #-}
{-# Language QuasiQuotes, NumDecimals #-}
{-|
Module : Main
Description : Day 13 solution
Expand Down Expand Up @@ -37,32 +37,49 @@ Prize: X=18641, Y=10279
module Main (main) where

import Advent (format)
import GHC.Num (integerGcde)
import Debug.Trace

-- | >>> :main
-- 29877
-- 99423413811305
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

0 comments on commit 4dd8a09

Please sign in to comment.