[haskell] Day 7
This commit is contained in:
parent
a79cc27359
commit
b281d8e39b
|
@ -26,6 +26,7 @@ library
|
||||||
, Day4
|
, Day4
|
||||||
, Day5
|
, Day5
|
||||||
, Day6
|
, Day6
|
||||||
|
, Day7
|
||||||
|
|
||||||
executable aoc2023
|
executable aoc2023
|
||||||
import: warnings
|
import: warnings
|
||||||
|
|
|
@ -8,6 +8,7 @@ import Day3 qualified
|
||||||
import Day4 qualified
|
import Day4 qualified
|
||||||
import Day5 qualified
|
import Day5 qualified
|
||||||
import Day6 qualified
|
import Day6 qualified
|
||||||
|
import Day7 qualified
|
||||||
import System.Environment (getArgs)
|
import System.Environment (getArgs)
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
|
@ -19,4 +20,5 @@ main = do
|
||||||
["day4"] -> Day4.run
|
["day4"] -> Day4.run
|
||||||
["day5"] -> Day5.run
|
["day5"] -> Day5.run
|
||||||
["day6"] -> Day6.run
|
["day6"] -> Day6.run
|
||||||
|
["day7"] -> Day7.run
|
||||||
args -> error $ "Invlaid args: " <> show args
|
args -> error $ "Invlaid args: " <> show args
|
||||||
|
|
259
haskell/src/Day7.hs
Normal file
259
haskell/src/Day7.hs
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
|
||||||
|
module Day7 where
|
||||||
|
|
||||||
|
import Control.Monad (replicateM)
|
||||||
|
import Data.Char (isDigit)
|
||||||
|
import Data.Functor ((<&>))
|
||||||
|
import Data.List (sort, sortBy)
|
||||||
|
import Data.Map.Lazy qualified as Map
|
||||||
|
import Data.Ord (comparing)
|
||||||
|
import Text.ParserCombinators.ReadP
|
||||||
|
import Data.Function (on)
|
||||||
|
|
||||||
|
run :: IO ()
|
||||||
|
run = do
|
||||||
|
input <- readInput
|
||||||
|
putStrLn $ "Part 1 Answer: " <> show (part1 input)
|
||||||
|
putStrLn $ "Part 2 Answer: " <> show (part2 input)
|
||||||
|
|
||||||
|
part1 :: [(Hand, Bid)] -> Int
|
||||||
|
part1 handsWithBids =
|
||||||
|
let sortedHandsWithBids = sort handsWithBids
|
||||||
|
in sum $ zipWith (\(_, bid) rank -> bid * rank) sortedHandsWithBids [1 ..]
|
||||||
|
|
||||||
|
part2 :: [(Hand, Bid)] -> Int
|
||||||
|
part2 handsWithBids =
|
||||||
|
let sortedHandsWithBids = sortBy (jokerCompare `on` fst) handsWithBids
|
||||||
|
in sum $ zipWith (\(_, bid) rank -> bid * rank) sortedHandsWithBids [1 ..]
|
||||||
|
|
||||||
|
-- | Order of the constructors is important
|
||||||
|
--
|
||||||
|
-- >>> HighCard `compare` FiveOfAKind
|
||||||
|
-- LT
|
||||||
|
--
|
||||||
|
-- >>> FullHouse `compare` ThreeOfAKind
|
||||||
|
-- GT
|
||||||
|
data HandType
|
||||||
|
= HighCard
|
||||||
|
| OnePair
|
||||||
|
| TwoPair
|
||||||
|
| ThreeOfAKind
|
||||||
|
| FullHouse
|
||||||
|
| FourOfAKind
|
||||||
|
| FiveOfAKind
|
||||||
|
deriving (Eq, Ord, Show)
|
||||||
|
|
||||||
|
-- |
|
||||||
|
-- >>> handType $ Hand [K,K,K,K,K]
|
||||||
|
-- FiveOfAKind
|
||||||
|
--
|
||||||
|
-- >>> handType $ Hand [C2,C2,C2,C2,K]
|
||||||
|
-- FourOfAKind
|
||||||
|
--
|
||||||
|
-- >>> handType $ Hand [K,J,K,C2,J]
|
||||||
|
-- TwoPair
|
||||||
|
--
|
||||||
|
-- >>> handType $ Hand [K,A,J,Q,A]
|
||||||
|
-- OnePair
|
||||||
|
--
|
||||||
|
-- >>> handType $ Hand [A,J,A,C2,A]
|
||||||
|
-- ThreeOfAKind
|
||||||
|
--
|
||||||
|
-- >>> handType $ Hand [A,A,A,K,K]
|
||||||
|
-- FullHouse
|
||||||
|
handType :: [Card] -> HandType
|
||||||
|
handType cards =
|
||||||
|
let counts = foldr (\c -> Map.insertWith (+) c (1 :: Int)) mempty cards
|
||||||
|
in case maximum counts of
|
||||||
|
1 -> HighCard
|
||||||
|
2
|
||||||
|
| length (Map.filter (== 2) counts) == 2 -> TwoPair
|
||||||
|
| otherwise -> OnePair
|
||||||
|
3
|
||||||
|
| 2 `elem` counts -> FullHouse
|
||||||
|
| otherwise -> ThreeOfAKind
|
||||||
|
4 -> FourOfAKind
|
||||||
|
5 -> FiveOfAKind
|
||||||
|
_ -> error "Too many cards"
|
||||||
|
|
||||||
|
-- |
|
||||||
|
-- >>> jokerHandType [J,J,J,J,J]
|
||||||
|
-- FiveOfAKind
|
||||||
|
--
|
||||||
|
-- >>> jokerHandType [Q,Q,Q,Q,Q]
|
||||||
|
-- FiveOfAKind
|
||||||
|
--
|
||||||
|
-- >>> jokerHandType [Q,Q,Q,Q,J]
|
||||||
|
-- FiveOfAKind
|
||||||
|
--
|
||||||
|
-- >>> jokerHandType [Q,Q,Q,J,J]
|
||||||
|
-- FiveOfAKind
|
||||||
|
--
|
||||||
|
-- >>> jokerHandType [Q,Q,Q,K,J]
|
||||||
|
-- FourOfAKind
|
||||||
|
--
|
||||||
|
-- >>> jokerHandType [Q,Q,A,K,J]
|
||||||
|
-- ThreeOfAKind
|
||||||
|
--
|
||||||
|
-- >>> jokerHandType [Q,Q,A,A,J]
|
||||||
|
-- FullHouse
|
||||||
|
--
|
||||||
|
-- >>> jokerHandType [Q,Q,Q,A,A]
|
||||||
|
-- FullHouse
|
||||||
|
--
|
||||||
|
-- >>> jokerHandType [Q,Q,A,K,K]
|
||||||
|
-- TwoPair
|
||||||
|
--
|
||||||
|
-- >>> jokerHandType [T,J,Q,K,A]
|
||||||
|
-- OnePair
|
||||||
|
--
|
||||||
|
-- >>> jokerHandType [T,J,J,K,A]
|
||||||
|
-- ThreeOfAKind
|
||||||
|
jokerHandType :: [Card] -> HandType
|
||||||
|
jokerHandType cards =
|
||||||
|
let jokers = length $ filter (== J) cards
|
||||||
|
withoutJokers = filter (/= J) cards
|
||||||
|
in case length withoutJokers of
|
||||||
|
0 -> FiveOfAKind
|
||||||
|
5 -> handType withoutJokers
|
||||||
|
_ ->
|
||||||
|
let countsWithoutJoker = foldr (\c -> Map.insertWith (+) c (1 :: Int)) mempty withoutJokers
|
||||||
|
pairsCount = Map.size (Map.filter (== 2) countsWithoutJoker)
|
||||||
|
in case maximum countsWithoutJoker of
|
||||||
|
5 -> FiveOfAKind
|
||||||
|
4
|
||||||
|
| jokers == 1 -> FiveOfAKind
|
||||||
|
| otherwise -> FourOfAKind
|
||||||
|
3
|
||||||
|
| jokers == 2 -> FiveOfAKind
|
||||||
|
| otherwise -> FourOfAKind
|
||||||
|
2
|
||||||
|
| jokers == 3 -> FiveOfAKind
|
||||||
|
| jokers == 2 -> FourOfAKind
|
||||||
|
| jokers == 1 && pairsCount == 2 -> FullHouse
|
||||||
|
| otherwise -> ThreeOfAKind
|
||||||
|
1
|
||||||
|
| jokers == 4 -> FiveOfAKind
|
||||||
|
| jokers == 3 -> FourOfAKind
|
||||||
|
| jokers == 2 -> ThreeOfAKind
|
||||||
|
| otherwise -> OnePair
|
||||||
|
_ -> error "too many cards"
|
||||||
|
|
||||||
|
-- Order of the constructors is important
|
||||||
|
data Card
|
||||||
|
= C2
|
||||||
|
| C3
|
||||||
|
| C4
|
||||||
|
| C5
|
||||||
|
| C6
|
||||||
|
| C7
|
||||||
|
| C8
|
||||||
|
| C9
|
||||||
|
| T
|
||||||
|
| J
|
||||||
|
| Q
|
||||||
|
| K
|
||||||
|
| A
|
||||||
|
deriving (Eq, Ord, Show)
|
||||||
|
|
||||||
|
instance JokerOrd Card where
|
||||||
|
jokerCompare :: Card -> Card -> Ordering
|
||||||
|
jokerCompare J J = EQ
|
||||||
|
jokerCompare J _ = LT
|
||||||
|
jokerCompare _ J = GT
|
||||||
|
jokerCompare c1 c2 = c1 `compare` c2
|
||||||
|
|
||||||
|
newtype Hand = Hand [Card]
|
||||||
|
deriving (Eq)
|
||||||
|
|
||||||
|
instance Show Hand where
|
||||||
|
show :: Hand -> String
|
||||||
|
show (Hand cardOrder) =
|
||||||
|
foldMap
|
||||||
|
( \c ->
|
||||||
|
case show c of
|
||||||
|
['C', x] -> [x]
|
||||||
|
xs -> xs
|
||||||
|
)
|
||||||
|
cardOrder
|
||||||
|
|
||||||
|
class JokerOrd a where
|
||||||
|
jokerCompare :: a -> a -> Ordering
|
||||||
|
|
||||||
|
instance (JokerOrd a) => JokerOrd [a] where
|
||||||
|
jokerCompare [] [] = EQ
|
||||||
|
jokerCompare [] _ = LT
|
||||||
|
jokerCompare _ [] = GT
|
||||||
|
jokerCompare (x : xs) (y : ys) =
|
||||||
|
let comparison = jokerCompare x y
|
||||||
|
in if comparison == EQ
|
||||||
|
then jokerCompare xs ys
|
||||||
|
else comparison
|
||||||
|
|
||||||
|
-- |
|
||||||
|
-- >>> Hand [K,K,K,K,K] `compare` Hand [A,A,A,A,A]
|
||||||
|
-- LT
|
||||||
|
--
|
||||||
|
-- >>> Hand [K,K,K,K,A] `compare` Hand [T,T,T,T,T]
|
||||||
|
-- LT
|
||||||
|
instance Ord Hand where
|
||||||
|
compare :: Hand -> Hand -> Ordering
|
||||||
|
compare (Hand cards1) (Hand cards2) =
|
||||||
|
case comparing handType cards1 cards2 of
|
||||||
|
EQ -> cards1 `compare` cards2
|
||||||
|
x -> x
|
||||||
|
|
||||||
|
instance JokerOrd Hand where
|
||||||
|
jokerCompare :: Hand -> Hand -> Ordering
|
||||||
|
jokerCompare (Hand cards1) (Hand cards2) =
|
||||||
|
case comparing jokerHandType cards1 cards2 of
|
||||||
|
EQ -> jokerCompare cards1 cards2
|
||||||
|
x -> x
|
||||||
|
|
||||||
|
type Bid = Int
|
||||||
|
|
||||||
|
readInput :: IO [(Hand, Bid)]
|
||||||
|
readInput = do
|
||||||
|
input <- getContents
|
||||||
|
case readP_to_S inputParser input of
|
||||||
|
[] -> error "Parser failed"
|
||||||
|
[(parsed, "")] -> pure parsed
|
||||||
|
xs -> error $ "Parser failed: " <> show xs
|
||||||
|
|
||||||
|
cardParser :: ReadP Card
|
||||||
|
cardParser =
|
||||||
|
satisfy (`elem` "23456789TJQKA") <&> \case
|
||||||
|
'2' -> C2
|
||||||
|
'3' -> C3
|
||||||
|
'4' -> C4
|
||||||
|
'5' -> C5
|
||||||
|
'6' -> C6
|
||||||
|
'7' -> C7
|
||||||
|
'8' -> C8
|
||||||
|
'9' -> C9
|
||||||
|
'T' -> T
|
||||||
|
'J' -> J
|
||||||
|
'Q' -> Q
|
||||||
|
'K' -> K
|
||||||
|
'A' -> A
|
||||||
|
c -> error $ "impossible card: " <> [c]
|
||||||
|
|
||||||
|
handParser :: ReadP Hand
|
||||||
|
handParser =
|
||||||
|
Hand <$> replicateM 5 cardParser
|
||||||
|
|
||||||
|
bidParser :: ReadP Bid
|
||||||
|
bidParser = do
|
||||||
|
read <$> munch1 isDigit
|
||||||
|
|
||||||
|
lineParser :: ReadP (Hand, Int)
|
||||||
|
lineParser = do
|
||||||
|
h <- handParser
|
||||||
|
skipSpaces
|
||||||
|
b <- bidParser
|
||||||
|
pure (h, b)
|
||||||
|
|
||||||
|
inputParser :: ReadP [(Hand, Int)]
|
||||||
|
inputParser = do
|
||||||
|
sepBy1 lineParser (char '\n') <* eof
|
1000
input/day7
Normal file
1000
input/day7
Normal file
File diff suppressed because it is too large
Load diff
5
input/day7-eg
Normal file
5
input/day7-eg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
32T3K 765
|
||||||
|
T55J5 684
|
||||||
|
KK677 28
|
||||||
|
KTJJT 220
|
||||||
|
QQQJA 483
|
Loading…
Reference in a new issue