-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDay21.hs
More file actions
98 lines (77 loc) · 2.59 KB
/
Copy pathDay21.hs
File metadata and controls
98 lines (77 loc) · 2.59 KB
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
module Day21
( part1
, part2
) where
import Control.Monad (void)
import Data.ByteString (ByteString)
import Data.List (sortBy)
import Data.Maybe (catMaybes)
import Data.Ord (comparing)
import FlatParse.Basic (anyAsciiDecimalInt, isDigit,
runParser, skipSatisfy, some)
import Helpers.Parsers.FlatParse (Parser, extract)
data Weapon =
Weapon Cost Damage
deriving (Show, Eq, Ord)
data Armor =
Armor Cost Defense
deriving (Show, Eq, Ord)
data Ring =
Ring Cost Damage Defense
deriving (Show, Eq, Ord)
data Character =
Character HP Damage Defense
deriving (Show, Eq, Ord)
type Cost = Int
type Damage = Int
type Defense = Int
type HP = Int
weapons :: [Weapon]
weapons = [Weapon 8 4, Weapon 10 5, Weapon 25 6, Weapon 40 7, Weapon 74 8]
armors :: [Armor]
armors =
[Armor 0 0, Armor 13 1, Armor 31 2, Armor 53 3, Armor 75 4, Armor 102 5]
rings :: [Ring]
rings =
[ Ring 0 0 0
, Ring 25 1 0
, Ring 50 2 0
, Ring 100 3 0
, Ring 20 0 1
, Ring 40 0 2
, Ring 80 0 3
]
consume :: Parser ()
consume = void . some $ skipSatisfy (not . isDigit)
parseInput :: Parser Character
parseInput = do
consume
hp <- anyAsciiDecimalInt
consume
damage <- anyAsciiDecimalInt
consume
Character hp damage <$> anyAsciiDecimalInt
win :: Character -> Character -> Bool
win boss@(Character bossHP bossDamage bossArmor) me@(Character meHP meDamage meArmor) =
(bossHP `div` meHit) < meHP `div` bossHit
|| (bossHP `div` meHit == meHP `div` bossHit && meHP `mod` bossHit /= 0)
|| (bossHP `div` meHit == (meHP `div` bossHit) + 1
&& bossHP `mod` meHit == 0)
where
meHit = max 1 $ meDamage - bossArmor
bossHit = max 1 $ bossDamage - meArmor
makeMe :: Weapon -> Armor -> Ring -> Ring -> Maybe (Cost, Character)
makeMe (Weapon cw aw) (Armor ca ad) r1@(Ring cr1 ar1 dr1) r2@(Ring cr2 ar2 dr2)
| r1 == r2 = Nothing
| otherwise =
Just (cw + ca + cr1 + cr2, Character 100 (aw + ar1 + ar2) (ad + dr1 + dr2))
makeMes :: [(Int, Character)]
makeMes = catMaybes $ makeMe <$> weapons <*> armors <*> rings <*> rings
lowerWin :: Character -> Int
lowerWin boss = fst . minimum . filter (win boss . snd) $ makeMes
maxLose :: Character -> Int
maxLose boss = fst . maximum . filter (not . win boss . snd) $ makeMes
part1 :: Bool -> ByteString -> String
part1 _ = show . lowerWin . extract . runParser parseInput
part2 :: Bool -> ByteString -> String
part2 _ = show . maxLose . extract . runParser parseInput