day14: Complete second puzzle
This commit is contained in:
parent
99ec5911b7
commit
7de9869d1c
14
14/Itertools.hs
Normal file
14
14/Itertools.hs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
module Itertools (
|
||||||
|
flatmap,
|
||||||
|
windows
|
||||||
|
) where
|
||||||
|
|
||||||
|
windows :: Int -> [a] -> [[a]]
|
||||||
|
windows _ [] = []
|
||||||
|
windows i (x:xs)
|
||||||
|
| length xs < i-1 = []
|
||||||
|
| otherwise = (x:take (i-1) xs):windows i xs
|
||||||
|
|
||||||
|
flatmap :: (t -> [a]) -> [t] -> [a]
|
||||||
|
flatmap _ [] = []
|
||||||
|
flatmap f (x:xs) = f x ++ flatmap f xs
|
|
@ -1,5 +1,8 @@
|
||||||
|
import Data.List (maximumBy, minimumBy)
|
||||||
import Data.Map.Strict (Map, (!))
|
import Data.Map.Strict (Map, (!))
|
||||||
import qualified Data.Map.Strict as Map
|
import qualified Data.Map.Strict as Map
|
||||||
|
import Data.Ord (comparing)
|
||||||
|
import Itertools (flatmap, windows)
|
||||||
import Parsing (splitByString)
|
import Parsing (splitByString)
|
||||||
import Reducers (leastMostOcc)
|
import Reducers (leastMostOcc)
|
||||||
|
|
||||||
|
@ -9,6 +12,7 @@ main = do
|
||||||
let
|
let
|
||||||
polymer = parsePolymer input
|
polymer = parsePolymer input
|
||||||
(putStrLn . show . solution1) polymer
|
(putStrLn . show . solution1) polymer
|
||||||
|
(putStrLn . show . solution2) polymer
|
||||||
|
|
||||||
parsePolymer :: String -> ([Char], Map [Char] Char)
|
parsePolymer :: String -> ([Char], Map [Char] Char)
|
||||||
parsePolymer input = (template input, rules input)
|
parsePolymer input = (template input, rules input)
|
||||||
|
@ -21,13 +25,18 @@ parsePolymer input = (template input, rules input)
|
||||||
tupelize untupled = (head untupled, (head . last) untupled)
|
tupelize untupled = (head untupled, (head . last) untupled)
|
||||||
|
|
||||||
solution1 :: ([Char], Map [Char] Char) -> Int
|
solution1 :: ([Char], Map [Char] Char) -> Int
|
||||||
solution1 (template, rules) = uncurry (-) (leastMostOcc polymer)
|
solution1 (template, rules) = uncurry (-) (leastMostOcc (polymerizeN rules template 10))
|
||||||
where
|
|
||||||
polymer :: [Char]
|
|
||||||
polymer = iterate (polymerize rules) template !! 10
|
|
||||||
|
|
||||||
solution2 :: ([Char], Map [Char] Char) -> Int
|
solution2 :: ([Char], Map [Char] Char) -> Int
|
||||||
solution2 (template, rules) = undefined
|
solution2 (template, rules) = ((snd . most) polyCounts) - ((snd . least) polyCounts)
|
||||||
|
where
|
||||||
|
polyCounts :: Map Char Int
|
||||||
|
polyCounts = polyCounter rules template 40
|
||||||
|
least = minimumBy (comparing snd) . Map.assocs
|
||||||
|
most = maximumBy (comparing snd) . Map.assocs
|
||||||
|
|
||||||
|
polymerizeN :: Map [Char] Char -> [Char] -> Int -> [Char]
|
||||||
|
polymerizeN rules template i = iterate (polymerize rules) template !! i
|
||||||
|
|
||||||
polymerize :: Map [Char] Char -> [Char] -> [Char]
|
polymerize :: Map [Char] Char -> [Char] -> [Char]
|
||||||
polymerize rules template = step template
|
polymerize rules template = step template
|
||||||
|
@ -37,6 +46,54 @@ polymerize rules template = step template
|
||||||
step [final] = [final]
|
step [final] = [final]
|
||||||
step (x:xs) = x:(rules ! [x, head xs]):(step xs)
|
step (x:xs) = x:(rules ! [x, head xs]):(step xs)
|
||||||
|
|
||||||
|
-- Since we don't actually need to synthesize the polymer, but only
|
||||||
|
-- need to know how many of each elements occur, we can get around
|
||||||
|
-- having to create a ridiculously large list by operating on the rule
|
||||||
|
-- pairs instead.
|
||||||
|
--
|
||||||
|
-- E.g. NNCB -> NCNBCHB (see testInput1), in this case we turn:
|
||||||
|
--
|
||||||
|
-- - NN -> NC, CN
|
||||||
|
-- - NC -> NB, BC
|
||||||
|
-- - CB -> CH, HB
|
||||||
|
--
|
||||||
|
-- All we need to do is keep track of the counts of these
|
||||||
|
-- sub-sequences, not of the full polymer.
|
||||||
|
--
|
||||||
|
-- To get back to the individual elements, we then just need to
|
||||||
|
-- de-window it all again.
|
||||||
|
polyCounter :: Map [Char] Char -> [Char] -> Int -> Map Char Int
|
||||||
|
polyCounter rules template iterations = elCounts
|
||||||
|
where
|
||||||
|
initial :: Map [Char] Int
|
||||||
|
initial = (Map.fromListWith (+) . map (\e -> (e, 1)) . windows 2) template
|
||||||
|
step :: Map [Char] Int -> Map [Char] Int
|
||||||
|
step = Map.fromListWith (+) . flatmap resultingPolies . Map.assocs
|
||||||
|
resultingPolies :: ([Char], Int) -> [([Char], Int)]
|
||||||
|
resultingPolies (p, i) = [([head p, c], i), ([c, last p], i)]
|
||||||
|
where c = rules ! p
|
||||||
|
polyCounts :: Map [Char] Int
|
||||||
|
polyCounts = iterate step initial !! iterations
|
||||||
|
|
||||||
|
-- Because the windows are size 2, every element of a sub-binding is
|
||||||
|
-- counted twice (e.g. NBCBC -> NB, BC, CB, BC), except the first and
|
||||||
|
-- last element. We know the first and last elements (because we
|
||||||
|
-- operated on windows, they'll match the first and last of the
|
||||||
|
-- template), so we can un-window the whole batch by dividing their
|
||||||
|
-- number of occurrences by two, except for the first and last, where
|
||||||
|
-- we subtract the number by 1 first and add 1 back later.
|
||||||
|
elCounts :: Map Char Int
|
||||||
|
elCounts = Map.mapWithKey deWindow naiveSum
|
||||||
|
where
|
||||||
|
els :: ([Char], Int) -> [(Char, Int)]
|
||||||
|
els (p, i) = [(head p, i), (last p, i)]
|
||||||
|
naiveSum :: Map Char Int
|
||||||
|
naiveSum = (Map.fromListWith (+) . flatmap els . Map.assocs) polyCounts
|
||||||
|
deWindow :: Char -> Int -> Int
|
||||||
|
deWindow c i
|
||||||
|
| c == head template || c == last template = (i - 1) `quot` 2 + 1
|
||||||
|
| otherwise = i `quot` 2
|
||||||
|
|
||||||
-- Tests
|
-- Tests
|
||||||
|
|
||||||
testInput1 = unlines [
|
testInput1 = unlines [
|
||||||
|
@ -59,6 +116,6 @@ testInput1 = unlines [
|
||||||
"CC -> N",
|
"CC -> N",
|
||||||
"CN -> C"]
|
"CN -> C"]
|
||||||
|
|
||||||
testInputParsed1 = parsePolymer testInput1
|
parsedTestInput1 = parsePolymer testInput1
|
||||||
test1 = solution1 testInputParsed1 -- 1588
|
test1 = solution1 parsedTestInput1 -- 1588
|
||||||
test2 = solution2 testInputParsed1 -- 2188189693529
|
test2 = solution2 parsedTestInput1 -- 2188189693529
|
||||||
|
|
Loading…
Reference in a new issue