-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay07.hs
138 lines (111 loc) · 2.94 KB
/
Day07.hs
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ViewPatterns #-}
module Day07 where
import Control.Arrow ((&&&))
import Control.Monad
import Data.Char
import Data.Function (on)
import Text.ParserCombinators.ReadP
import Harness
import ParseHelper
import Data.List qualified as L
main :: IO ()
main = getInputAndSolve (parseInput parseHand) calcTotalWinnings (calcTotalWinnings . subJokers)
-- SOLVE
calcTotalWinnings :: [Hand] -> Int
calcTotalWinnings =
sum
. zipWith calcWinnings [1 ..]
. concatMap (L.sortOn (.cards))
. sortAndGroupOn (jokerCardsToType . (.cards))
where
calcWinnings :: Int -> Hand -> Int
calcWinnings rank hand = rank * hand.bet
sortAndGroupOn :: (Ord b, Eq b) => (a -> b) -> [a] -> [[a]]
sortAndGroupOn f =
map (map snd)
. L.groupBy ((==) `on` fst)
. L.sortOn fst
. map (f &&& id)
-- HELPERS
data HandType
= HighCard
| OnePair
| TwoPair
| ThreeOfKind
| FullHouse
| FourOfKind
| FiveOfKind
deriving (Show, Eq, Ord, Enum, Bounded)
cardsToType :: [Card] -> HandType
cardsToType (L.group . L.sort -> gs) =
if
| anyLengthIs 5 -> FiveOfKind
| anyLengthIs 4 -> FourOfKind
| anyLengthIs 3 && anyLengthIs 2 -> FullHouse
| anyLengthIs 3 -> ThreeOfKind
| length (filter ((== 2) . length) gs) == 2 -> TwoPair
| anyLengthIs 2 -> OnePair
| otherwise -> HighCard
where
anyLengthIs :: Int -> Bool
anyLengthIs i = any ((== i) . length) gs
jokerCardsToType :: [Card] -> HandType
jokerCardsToType cards =
let (jokers, nonJokers) = L.partition (== Joker) cards
in foldr
(\_ handType -> increment handType)
(cardsToType nonJokers)
jokers
where
increment :: HandType -> HandType
increment = \case
FiveOfKind -> FiveOfKind
FourOfKind -> FiveOfKind
FullHouse -> FourOfKind
ThreeOfKind -> FourOfKind
TwoPair -> FullHouse
OnePair -> ThreeOfKind
HighCard -> OnePair
subJokers :: [Hand] -> [Hand]
subJokers = map (\h -> h {cards = map substituteJoker h.cards})
where
substituteJoker :: Card -> Card
substituteJoker = \case
Jack -> Joker
c -> c
-- PARSE
data Hand = Hand
{ cards :: [Card]
, bet :: Int
}
deriving (Show)
parseHand :: ReadP Hand
parseHand = do
cards <- many1 parseCard
void $ char ' '
bet <- parseInt
return Hand {..}
data Card
= Joker
| Numbered Int
| Ten
| Jack
| Queen
| King
| Ace
deriving (Show, Ord, Eq)
parseCard :: ReadP Card
parseCard =
choice
[ Numbered . read . (: []) <$> satisfy isNumber
, Ten <$ char 'T'
, Jack <$ char 'J'
, Queen <$ char 'Q'
, King <$ char 'K'
, Ace <$ char 'A'
]