Using bounded
[advent-of-code-22.git] / advent02 / Main.hs
1 -- Writeup at https://work.njae.me.uk/2022/12/02/advent-of-code-2022-day-2/
2
3 import System.Environment
4 import Data.Text ()
5 import qualified Data.Text.IO as TIO
6 import Data.Attoparsec.Text hiding (Result)
7 import Control.Applicative
8
9 data Shape = Rock | Paper | Scissors deriving (Show, Eq, Ord, Enum, Bounded)
10 data Result = Loss | Draw | Win deriving (Show, Eq, Ord, Enum)
11 data Round = Round Shape Shape deriving (Eq, Show)
12 data ShapeResult = ShapeResult Shape Result deriving (Eq, Show)
13
14 main :: IO ()
15 main =
16 do dataFileName <- getDataFileName
17 text <- TIO.readFile dataFileName
18 let match1 = successfulParse1 text
19 print $ part1 match1
20 let match2 = successfulParse2 text
21 print $ part2 match2
22
23 getDataFileName :: IO String
24 getDataFileName =
25 do args <- getArgs
26 progName <- getProgName
27 let baseDataName = if null args
28 then progName
29 else head args
30 let dataFileName = "data/" ++ baseDataName ++ ".txt"
31 return dataFileName
32
33 part1 :: [Round] -> Int
34 part1 = sum . fmap scoreRound
35
36 part2 :: [ShapeResult] -> Int
37 part2 = sum . fmap (scoreRound . roundFromResult)
38
39 player2Result :: Round -> Result
40 player2Result (Round Rock Paper) = Win
41 player2Result (Round Paper Scissors) = Win
42 player2Result (Round Scissors Rock) = Win
43 player2Result (Round x y) | x == y = Draw
44 player2Result _ = Loss
45
46 scoreRound :: Round -> Int
47 scoreRound r@(Round _ y) = scoreShape y + scoreResult (player2Result r)
48
49 scoreShape :: Shape -> Int
50 scoreShape s = 1 + fromEnum s
51
52 scoreResult :: Result -> Int
53 scoreResult r = 3 * fromEnum r
54
55 roundFromResult :: ShapeResult -> Round
56 roundFromResult (ShapeResult shape result) = Round shape p2s
57 where p2s = head [ p2Shape
58 -- | p2Shape <- [Rock .. Scissors]
59 | p2Shape <- [minBound .. maxBound]
60 , player2Result (Round shape p2Shape) == result
61 ]
62
63 -- Parse the input file
64
65 match1P = roundP `sepBy` endOfLine
66 roundP = Round <$> p1ShapeP <*> (" " *> p2ShapeP)
67
68 match2P = shapeResultP `sepBy` endOfLine
69 shapeResultP = ShapeResult <$> p1ShapeP <*> (" " *> resultP)
70
71 p1ShapeP = aP <|> bP <|> cP
72 aP = Rock <$ "A"
73 bP = Paper <$ "B"
74 cP = Scissors <$ "C"
75
76 p2ShapeP = xP <|> yP <|> zP
77 xP = Rock <$ "X"
78 yP = Paper <$ "Y"
79 zP = Scissors <$ "Z"
80
81 resultP = xrP <|> yrP <|> zrP
82 xrP = Loss <$ "X"
83 yrP = Draw <$ "Y"
84 zrP = Win <$ "Z"
85
86 -- successfulParse :: Text -> (Integer, [Maybe Integer])
87 successfulParse1 input =
88 case parseOnly match1P input of
89 Left _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
90 Right match -> match
91
92 successfulParse2 input =
93 case parseOnly match2P input of
94 Left _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
95 Right match -> match