{-# Language QuasiQuotes #-}
module Main where
import Advent.Format (format)
import Data.List ( maximumBy, minimumBy )
import Data.Ord ( comparing )
data Item = Item { Item -> String
itemName :: String, Item -> Int
itemCost, Item -> Int
itemDamage, Item -> Int
itemArmor :: Int }
deriving (Int -> Item -> ShowS
[Item] -> ShowS
Item -> String
(Int -> Item -> ShowS)
-> (Item -> String) -> ([Item] -> ShowS) -> Show Item
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Item -> ShowS
showsPrec :: Int -> Item -> ShowS
$cshow :: Item -> String
show :: Item -> String
$cshowList :: [Item] -> ShowS
showList :: [Item] -> ShowS
Show, ReadPrec [Item]
ReadPrec Item
Int -> ReadS Item
ReadS [Item]
(Int -> ReadS Item)
-> ReadS [Item] -> ReadPrec Item -> ReadPrec [Item] -> Read Item
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: Int -> ReadS Item
readsPrec :: Int -> ReadS Item
$creadList :: ReadS [Item]
readList :: ReadS [Item]
$creadPrec :: ReadPrec Item
readPrec :: ReadPrec Item
$creadListPrec :: ReadPrec [Item]
readListPrec :: ReadPrec [Item]
Read)
main :: IO ()
IO ()
main =
do (Int
hp,Int
dmg,Int
armor) <- [format|2015 21 Hit Points: %u%nDamage: %u%nArmor: %u%n|]
let win :: Item -> Bool
win = Int -> Int -> Int -> Item -> Bool
fight Int
hp Int
dmg Int
armor
Int -> IO ()
forall a. Show a => a -> IO ()
print (Int -> IO ()) -> Int -> IO ()
forall a b. (a -> b) -> a -> b
$ [Int] -> Int
forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
minimum ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ (Item -> Int) -> [Item] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map Item -> Int
itemCost
([Item] -> [Int]) -> [Item] -> [Int]
forall a b. (a -> b) -> a -> b
$ (Item -> Bool) -> [Item] -> [Item]
forall a. (a -> Bool) -> [a] -> [a]
filter Item -> Bool
win
([Item] -> [Item]) -> [Item] -> [Item]
forall a b. (a -> b) -> a -> b
$ [Item]
gearOptions
Int -> IO ()
forall a. Show a => a -> IO ()
print (Int -> IO ()) -> Int -> IO ()
forall a b. (a -> b) -> a -> b
$ [Int] -> Int
forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ (Item -> Int) -> [Item] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map Item -> Int
itemCost
([Item] -> [Int]) -> [Item] -> [Int]
forall a b. (a -> b) -> a -> b
$ (Item -> Bool) -> [Item] -> [Item]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (Item -> Bool) -> Item -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Item -> Bool
win)
([Item] -> [Item]) -> [Item] -> [Item]
forall a b. (a -> b) -> a -> b
$ [Item]
gearOptions
emptyItem :: String -> Item
emptyItem :: String -> Item
emptyItem String
name = String -> Int -> Int -> Int -> Item
Item String
name Int
0 Int
0 Int
0
weapons :: [Item]
weapons :: [Item]
weapons =
[ String -> Int -> Int -> Int -> Item
Item String
"Dagger" Int
8 Int
4 Int
0
, String -> Int -> Int -> Int -> Item
Item String
"Shortsword" Int
10 Int
5 Int
0
, String -> Int -> Int -> Int -> Item
Item String
"Warhammer" Int
25 Int
6 Int
0
, String -> Int -> Int -> Int -> Item
Item String
"Longsword" Int
40 Int
7 Int
0
, String -> Int -> Int -> Int -> Item
Item String
"Greataxe" Int
74 Int
8 Int
0
]
armors :: [Item]
armors :: [Item]
armors =
[ String -> Int -> Int -> Int -> Item
Item String
"Leather" Int
13 Int
0 Int
1
, String -> Int -> Int -> Int -> Item
Item String
"Chainmail" Int
31 Int
0 Int
2
, String -> Int -> Int -> Int -> Item
Item String
"Splintmail" Int
53 Int
0 Int
3
, String -> Int -> Int -> Int -> Item
Item String
"Bandedmail" Int
75 Int
0 Int
4
, String -> Int -> Int -> Int -> Item
Item String
"Platemail" Int
102 Int
0 Int
5
]
rings :: [Item]
rings :: [Item]
rings =
[ String -> Int -> Int -> Int -> Item
Item String
"Damage +1" Int
25 Int
1 Int
0
, String -> Int -> Int -> Int -> Item
Item String
"Damage +2" Int
50 Int
2 Int
0
, String -> Int -> Int -> Int -> Item
Item String
"Damage +3" Int
100 Int
3 Int
0
, String -> Int -> Int -> Int -> Item
Item String
"Defense +1" Int
20 Int
0 Int
1
, String -> Int -> Int -> Int -> Item
Item String
"Defense +2" Int
40 Int
0 Int
2
, String -> Int -> Int -> Int -> Item
Item String
"Defense +3" Int
80 Int
0 Int
3
]
combine :: Item -> Item -> Item
combine :: Item -> Item -> Item
combine Item
x Item
y = Item
{ itemName :: String
itemName = Item -> String
itemName Item
x String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" and " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Item -> String
itemName Item
y
, itemCost :: Int
itemCost = Item -> Int
itemCost Item
x Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Item -> Int
itemCost Item
y
, itemDamage :: Int
itemDamage = Item -> Int
itemDamage Item
x Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Item -> Int
itemDamage Item
y
, itemArmor :: Int
itemArmor = Item -> Int
itemArmor Item
x Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Item -> Int
itemArmor Item
y
}
gearOptions :: [Item]
gearOptions :: [Item]
gearOptions =
do Item
weapon <- [Item]
weapons
Item
armor <- String -> Item
emptyItem String
"unarmored" Item -> [Item] -> [Item]
forall a. a -> [a] -> [a]
: [Item]
armors
[Item]
ring <- Int -> [Item] -> [[Item]]
forall a. Int -> [a] -> [[a]]
chooseUpTo Int
2 [Item]
rings
Item -> [Item]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return ((Item -> Item -> Item) -> [Item] -> Item
forall a. (a -> a -> a) -> [a] -> a
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldl1 Item -> Item -> Item
combine (Item
weapon Item -> [Item] -> [Item]
forall a. a -> [a] -> [a]
: Item
armor Item -> [Item] -> [Item]
forall a. a -> [a] -> [a]
: [Item]
ring))
chooseUpTo :: Int -> [a] -> [[a]]
chooseUpTo :: forall a. Int -> [a] -> [[a]]
chooseUpTo Int
0 [a]
_ = [[]]
chooseUpTo Int
_ [] = [[]]
chooseUpTo Int
n (a
x:[a]
xs) = ([a] -> [a]) -> [[a]] -> [[a]]
forall a b. (a -> b) -> [a] -> [b]
map (a
xa -> [a] -> [a]
forall a. a -> [a] -> [a]
:) (Int -> [a] -> [[a]]
forall a. Int -> [a] -> [[a]]
chooseUpTo (Int
nInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1) [a]
xs) [[a]] -> [[a]] -> [[a]]
forall a. [a] -> [a] -> [a]
++ Int -> [a] -> [[a]]
forall a. Int -> [a] -> [[a]]
chooseUpTo Int
n [a]
xs
fight ::
Int ->
Int ->
Int ->
Item ->
Bool
fight :: Int -> Int -> Int -> Item -> Bool
fight Int
hp Int
dmg Int
armor Item
gear = Int -> Int -> Int -> Int -> Bool
outcome Int
100 (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 (Int
dmg Int -> Int -> Int
forall a. Num a => a -> a -> a
- Item -> Int
itemArmor Item
gear)) Int
hp (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 (Item -> Int
itemDamage Item
gear Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
armor))
outcome ::
Int -> Int ->
Int -> Int ->
Bool
outcome :: Int -> Int -> Int -> Int -> Bool
outcome Int
hp1 Int
dec1 Int
hp2 Int
dec2 = (Int
hp1Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1)Int -> Int -> Int
forall a. Integral a => a -> a -> a
`quot`Int
dec1 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= (Int
hp2Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1)Int -> Int -> Int
forall a. Integral a => a -> a -> a
`quot`Int
dec2