[haskell] Day 7

This commit is contained in:
Akshay Mankar 2023-12-09 13:10:44 +01:00
parent a79cc27359
commit b281d8e39b
Signed by: axeman
GPG key ID: CA08F3AB62369B89
5 changed files with 1267 additions and 0 deletions

View file

@ -26,6 +26,7 @@ library
, Day4
, Day5
, Day6
, Day7
executable aoc2023
import: warnings

View file

@ -8,6 +8,7 @@ import Day3 qualified
import Day4 qualified
import Day5 qualified
import Day6 qualified
import Day7 qualified
import System.Environment (getArgs)
main :: IO ()
@ -19,4 +20,5 @@ main = do
["day4"] -> Day4.run
["day5"] -> Day5.run
["day6"] -> Day6.run
["day7"] -> Day7.run
args -> error $ "Invlaid args: " <> show args

259
haskell/src/Day7.hs Normal file
View 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

File diff suppressed because it is too large Load diff

5
input/day7-eg Normal file
View file

@ -0,0 +1,5 @@
32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483