summaryrefslogtreecommitdiffstats
path: root/day02/app/Main.hs
diff options
context:
space:
mode:
authorShivesh Mandalia <mail@shivesh.org>2023-01-21 18:35:06 +0000
committerShivesh Mandalia <mail@shivesh.org>2023-01-21 18:35:06 +0000
commit5e1a6e19bc0a40795d39e541abf2f6bba39d1065 (patch)
treec612b4f4b75f1a748ef6ad7e6107a586081a07a7 /day02/app/Main.hs
parent1750a9dbd6b6c62da90e5e1da431c20e83c30dc8 (diff)
downloadAOC_2022_haskell-5e1a6e19bc0a40795d39e541abf2f6bba39d1065.tar.gz
AOC_2022_haskell-5e1a6e19bc0a40795d39e541abf2f6bba39d1065.zip
complete day 2
Diffstat (limited to 'day02/app/Main.hs')
-rw-r--r--day02/app/Main.hs204
1 files changed, 204 insertions, 0 deletions
diff --git a/day02/app/Main.hs b/day02/app/Main.hs
new file mode 100644
index 0000000..c16fc53
--- /dev/null
+++ b/day02/app/Main.hs
@@ -0,0 +1,204 @@
+{-
+--- Day 2: Rock Paper Scissors ---
+
+The Elves begin to set up camp on the beach. To decide whose tent gets to be closest to the
+snack storage, a giant Rock Paper Scissors tournament is already in progress.
+
+Rock Paper Scissors is a game between two players. Each game contains many rounds; in each
+round, the players each simultaneously choose one of Rock, Paper, or Scissors using a hand
+shape. Then, a winner for that round is selected: Rock defeats Scissors, Scissors defeats
+Paper, and Paper defeats Rock. If both players choose the same shape, the round instead ends
+in a draw.
+
+Appreciative of your help yesterday, one Elf gives you an encrypted strategy guide (your puzzle
+input) that they say will be sure to help you win. "The first column is what your opponent is
+going to play: A for Rock, B for Paper, and C for Scissors. The second column--" Suddenly, the
+Elf is called away to help with someone's tent.
+
+The second column, you reason, must be what you should play in response: X for Rock, Y for
+Paper, and Z for Scissors. Winning every time would be suspicious, so the responses must have
+been carefully chosen.
+
+The winner of the whole tournament is the player with the highest score. Your total score is
+the sum of your scores for each round. The score for a single round is the score for the shape
+you selected (1 for Rock, 2 for Paper, and 3 for Scissors) plus the score for the outcome of
+the round (0 if you lost, 3 if the round was a draw, and 6 if you won).
+
+Since you can't be sure if the Elf is trying to help you or trick you, you should calculate the
+score you would get if you were to follow the strategy guide.
+
+For example, suppose you were given the following strategy guide:
+
+```
+A Y
+B X
+C Z
+```
+
+This strategy guide predicts and recommends the following:
+
+ In the first round, your opponent will choose Rock (A), and you should choose Paper (Y).
+ This ends in a win for you with a score of 8 (2 because you chose Paper + 6 because you
+ won).
+ In the second round, your opponent will choose Paper (B), and you should choose Rock (X).
+ This ends in a loss for you with a score of 1 (1 + 0).
+ The third round is a draw with both players choosing Scissors, giving you a score of 3 + 3
+ = 6.
+
+In this example, if you were to follow the strategy guide, you would get a total score of 15 (8
++ 1 + 6).
+
+What would your total score be if everything goes exactly according to your strategy guide?
+
+--- Part Two ---
+
+The Elf finishes helping with the tent and sneaks back over to you. "Anyway, the second column
+says how the round needs to end: X means you need to lose, Y means you need to end the round in
+a draw, and Z means you need to win. Good luck!"
+
+The total score is still calculated in the same way, but now you need to figure out what shape
+to choose so the round ends as indicated. The example above now goes like this:
+
+ In the first round, your opponent will choose Rock (A), and you need the round to end in a
+ draw (Y), so you also choose Rock. This gives you a score of 1 + 3 = 4.
+ In the second round, your opponent will choose Paper (B), and you choose Rock so you lose
+ (X) with a score of 1 + 0 = 1.
+ In the third round, you will defeat your opponent's Scissors with Rock for a score of 1 + 6
+ = 7.
+
+Now that you're correctly decrypting the ultra top secret strategy guide, you would get a total
+score of 12.
+
+Following the Elf's instructions for the second column, what would your total score be if
+everything goes exactly according to your strategy guide?
+-}
+{-# LANGUAGE DerivingStrategies #-}
+
+module Main (main) where
+
+import Data.ByteString.Lazy (ByteString)
+import Options.Applicative (Parser, ParserInfo, argument, execParser, fullDesc, help, helper, info, metavar, str)
+import Relude hiding (ByteString, readFile)
+import Text.Parsec (ParseError, parse, (<?>))
+import Text.Parsec.ByteString.Lazy (GenParser)
+import Text.Parsec.Char (oneOf, space, string)
+import Text.Parsec.Combinator (eof, many1)
+import Text.Parsec.Prim (parsecMap, try)
+
+type Opts :: Type
+newtype Opts = Opts {_filename :: Text}
+
+type RockPaperScissorsKind :: Type
+data RockPaperScissorsKind = Rock | Paper | Scissors
+ deriving stock (Show)
+
+instance Enum RockPaperScissorsKind where
+ fromEnum Rock = 1
+ fromEnum Paper = 2
+ fromEnum Scissors = 3
+ toEnum _ = error "Unsupported"
+
+type GameOutcome :: Type
+data GameOutcome = Win | Loss | Draw deriving stock (Show)
+
+instance Enum GameOutcome where
+ fromEnum Win = 6
+ fromEnum Loss = 0
+ fromEnum Draw = 3
+ toEnum _ = error "Unsupported"
+
+type GamePrediction :: Type
+data GamePrediction = GamePrediction {player :: RockPaperScissorsKind, opponent :: RockPaperScissorsKind} deriving stock (Show)
+
+options :: Parser Opts
+options = Opts <$> filename
+ where
+ filename :: Parser Text
+ filename = argument str $ metavar "filename" <> help "Input file"
+
+opts :: ParserInfo Opts
+opts = info (helper <*> options) fullDesc
+
+parseInput :: FilePath -> ByteString -> Either ParseError [(Char, Char)]
+parseInput = parse parser
+
+eol :: GenParser t st ()
+eol =
+ parsecMap
+ (const ())
+ ( try (string "\n\r")
+ <|> try (string "\r\n")
+ <|> string "\n"
+ <|> string "\r"
+ <?> "end of line"
+ )
+
+parser :: GenParser t st [(Char, Char)]
+parser = many1 block <* eof
+
+block :: GenParser t st (Char, Char)
+block = (,) <$> (oneOf "ABC" <* space) <*> oneOf "XYZ" <* (eol <|> eof)
+
+parseOpp :: Char -> RockPaperScissorsKind
+parseOpp 'A' = Rock
+parseOpp 'B' = Paper
+parseOpp 'C' = Scissors
+parseOpp _ = error "invalid char"
+
+parse1 :: Char -> RockPaperScissorsKind
+parse1 'X' = Rock
+parse1 'Y' = Paper
+parse1 'Z' = Scissors
+parse1 _ = error "invalid char"
+
+parse2 :: Char -> GameOutcome
+parse2 'X' = Loss
+parse2 'Y' = Draw
+parse2 'Z' = Win
+parse2 _ = error "invalid char"
+
+outcome :: GamePrediction -> GameOutcome
+outcome (GamePrediction Rock Scissors) = Win
+outcome (GamePrediction Paper Rock) = Win
+outcome (GamePrediction Scissors Paper) = Win
+outcome (GamePrediction Rock Paper) = Loss
+outcome (GamePrediction Paper Scissors) = Loss
+outcome (GamePrediction Scissors Rock) = Loss
+outcome _ = Draw
+
+fromOutcome :: GameOutcome -> RockPaperScissorsKind -> RockPaperScissorsKind
+fromOutcome Win Rock = Paper
+fromOutcome Win Paper = Scissors
+fromOutcome Win Scissors = Rock
+fromOutcome Loss Rock = Scissors
+fromOutcome Loss Paper = Rock
+fromOutcome Loss Scissors = Paper
+fromOutcome Draw a = a
+
+score :: GamePrediction -> Int
+score a = (fromEnum . player) a + (fromEnum . outcome) a
+
+runPart1 :: [(Char, Char)] -> Int
+runPart1 = sum . map (score . prediction)
+ where
+ prediction (a, b) = GamePrediction (parse1 b) (parseOpp a)
+
+prediction2 :: (Char, Char) -> GamePrediction
+prediction2 (a, b) = GamePrediction (fromOutcome (parse2 b) opp) opp
+ where
+ opp = parseOpp a
+
+runPart2 :: [(Char, Char)] -> Int
+runPart2 = sum . map (score . prediction2)
+
+main :: IO ()
+main = do
+ fileName <- toString . _filename <$> execParser opts
+ rawInput <- readFileLBS fileName
+ case parseInput fileName rawInput of
+ Left e -> do
+ putTextLn "Error parsing input:"
+ print e
+ Right r -> do
+ print $ runPart1 r
+ print $ runPart2 r