all / 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
Day 2, not too bad for Haskell either :)
There is some fun in parsing here:
data Policy = P
{ pIx1 :: Int
, pIx2 :: Int
, pChar :: Char
, pPass :: String
}
parsePolicy :: String -> Maybe Policy
parsePolicy str = do
[ixes,c:_,pwd] <- pure $ words str
[ix1,ix2] <- pure $ splitOn "-" ixes
P <$> readMaybe ix1
<*> readMaybe ix2
<*> pure c
<*> pure pwd
I used one of my more regular do-block tricks: if you pattern match in a
Maybe
do-block, then failed pattern matches will turn the whole thing into a
Nothing
. So if any of those list literal pattern matches failed, the whole
block will return Nothing
.
In any case, we just need to write a function to check if a given policy is valid for either criteria:
countTrue :: (a -> Bool) -> [a] -> Int
countTrue p = length . filter p
validate1 :: Policy -> Bool
validate1 P{..} = n >= pIx1 && n <= pIx2
where
n = countTrue (== pChar) pPass
validate2 :: Policy -> Bool
validate2 P{..} = n == 1
where
n = countTrue (== pChar) [pPass !! (pIx1 - 1), pPass !! (pIx2 - 1)]
And so parts 1 and 2 are just a count of how many policies are true :)
part1 :: [Policy] -> Int
part1 = countTrue validate1
part2 :: [Policy] -> Int
part2 = countTrue validate2
Back to all reflections for 2020
>> Day 02a
benchmarking...
time 55.69 μs (55.61 μs .. 55.78 μs)
1.000 R² (1.000 R² .. 1.000 R²)
mean 55.89 μs (55.82 μs .. 56.03 μs)
std dev 323.1 ns (232.5 ns .. 422.3 ns)
* parsing and formatting times excluded
>> Day 02b
benchmarking...
time 42.96 μs (42.88 μs .. 43.06 μs)
1.000 R² (1.000 R² .. 1.000 R²)
mean 43.11 μs (43.07 μs .. 43.19 μs)
std dev 196.8 ns (94.94 ns .. 332.4 ns)
* parsing and formatting times excluded