summaryrefslogtreecommitdiff
path: root/13-1.hs
blob: e295598f1886f6a2988e389a0a0e6174e4471a1e (plain)
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
import Data.Array
import Data.Ratio (denominator, numerator)
import Text.Parsec

type Matrix = Array (Int,Int) Rational

type Pair = (Rational,Rational)

det :: Matrix -> Rational
det m = a*d - b*c
  where
    a = m ! (1,1)
    b = m ! (2,1)
    c = m ! (1,2)
    d = m ! (2,2)

matrix :: Pair -> Pair -> Matrix
matrix (a,c) (b,d) =
  array ((1,1),(2,2)) [((1,1),a), ((2,1),b), ((1,2),c), ((2,2),d)]

solve :: Pair -> Pair -> Pair -> Pair
solve target a b = (solA, solB)
  where
    coefficients = matrix a b
    solA = det (matrix target b) / det coefficients
    solB = det (matrix a target) / det coefficients

int :: Rational -> Bool
int = (== 1) . denominator

maybeSolve :: Pair -> Pair -> Pair -> Maybe Pair
maybeSolve target a b =
  let sol@(a',b') = solve target a b
  in if int a' && int b'
     then Just sol
     else Nothing

cost :: Pair -> Pair -> Pair -> Int
cost target a b = case maybeSolve target a b of
  Nothing -> 0
  Just (a',b') -> fromIntegral . numerator $ 3*a' + b'

cost' :: (Pair,Pair,Pair) -> Int
cost' (a, b, target) = cost target a b

number :: Parsec String () Rational
number = fromInteger . read <$> many digit

button :: Parsec String () Pair
button = (,)
  <$  string "Button "
  <*  anyChar
  <*  string ": X+"
  <*> number
  <*  string ", Y+"
  <*> number
  <*  newline

prize :: Parsec String () Pair
prize = (,)
  <$  string "Prize: X="
  <*> number
  <*  string ", Y="
  <*> number
  <*  newline

parseOne :: Parsec String () (Pair,Pair,Pair)
parseOne = (,,) <$> button <*> button <*> prize

parseInput :: Parsec String () [(Pair,Pair,Pair)]
parseInput = sepBy parseOne newline

doParse :: String -> [(Pair,Pair,Pair)]
doParse s = case parse parseInput "" s of
  Left e  -> error $ show e
  Right a -> a

main :: IO ()
main = getContents >>= print . sum . map cost' . doParse