This repository has been archived by the owner on Nov 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathDay02.hs
75 lines (69 loc) · 2.77 KB
/
Day02.hs
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
-- |
-- Module : AOC.Challenge.Day02
-- Copyright : (c) Justin Le 2018
-- License : BSD3
--
-- Maintainer : justin@jle.im
-- Stability : experimental
-- Portability : non-portable
--
-- Day 2. See "AOC.Solver" for the types used in this module!
module AOC.Challenge.Day02 (
day02a
, day02b
) where
import AOC.Common (freqs, perturbations)
import AOC.Solver ((:~>)(..))
import Control.Monad (guard)
import Data.Containers.ListUtils (nubOrd)
import Data.List (find)
import Data.Witherable (catMaybes)
import qualified Data.Map as M
import qualified Data.Set as S
-- | We compute a frequency map of all of the characters in a string, and
-- then get all of the frequencies that happened for each line.
--
-- Then we build a frequency map of the frequencies!
day02a :: [String] :~> Int
day02a = MkSol
{ sParse = Just . lines
, sShow = show
, sSolve = mulTwoThree -- > lookup how many times
-- 2 and 3 occurred, and
-- multiply
. freqs -- > build a frequency map of
-- all seen frequencies
. concatMap -- > get the frequency map of
(nubOrd . M.elems . freqs) -- each string, and then
-- combine all of the
-- frequencies into a big
-- list of frequencies
}
where
mulTwoThree m = (*) <$> M.lookup 2 m <*> M.lookup 3 m
-- | The main work is in 'firstNeighbor', which looks thorugh a list of
-- items and finds the first item whose neighbor was already seen.
--
-- Then we take the two "almost matching" strings and filter out all of the
-- characters that aren't the same, using 'zipWith' and 'catMaybes'.
day02b :: [String] :~> String
day02b = MkSol
{ sParse = Just . lines
, sShow = id
, sSolve = fmap (uncurry onlySame)
. firstNeighbor
}
where
onlySame xs = catMaybes . zipWith (\x y -> x <$ guard (x == y)) xs
-- | Find the first string in a list that is a neighbor of a previous
-- string.
firstNeighbor :: [String] -> Maybe (String, String)
firstNeighbor = go S.empty
where
go seen (x:xs) = case find (`S.member` seen) (neighbors x) of
Just n -> Just (x, n)
Nothing -> go (x `S.insert` seen) xs
go _ [] = Nothing
-- | Get all one-character neighbors of a given string
neighbors :: String -> [String]
neighbors = perturbations (const ['a'..'z'])