Done day 15
authorNeil Smith <neil.git@njae.me.uk>
Wed, 15 Dec 2021 13:15:17 +0000 (13:15 +0000)
committerNeil Smith <neil.git@njae.me.uk>
Wed, 15 Dec 2021 13:15:17 +0000 (13:15 +0000)
advent-of-code21.cabal
advent14/Main.hs [new file with mode: 0644]
advent15/Main.hs [new file with mode: 0644]
data/advent14.txt [new file with mode: 0644]
data/advent14a.txt [new file with mode: 0644]
data/advent15.txt [new file with mode: 0644]
data/advent15a.txt [new file with mode: 0644]
problems/day14.html [new file with mode: 0644]
problems/day15.html [new file with mode: 0644]

index 90d342a8a95431b892ca8e871614cd95ebf70f25..050473f80e19eea8b438de41a3099036633dcf1d 100644 (file)
@@ -149,3 +149,13 @@ executable advent13
   import: common-extensions, build-directives
   main-is: advent13/Main.hs
   build-depends: text, attoparsec, containers, linear
+
+executable advent14
+  import: common-extensions, build-directives
+  main-is: advent14/Main.hs
+  build-depends: text, attoparsec, containers, multiset
+
+executable advent15
+  import: common-extensions, build-directives
+  main-is: advent15/Main.hs
+  build-depends: text, containers, linear, array, pqueue, mtl, lens
diff --git a/advent14/Main.hs b/advent14/Main.hs
new file mode 100644 (file)
index 0000000..c6ade97
--- /dev/null
@@ -0,0 +1,92 @@
+-- Writeup at https://work.njae.me.uk/2021/12/13/advent-of-code-2021-day-13/
+
+import Data.Text ()
+import qualified Data.Text.IO as TIO
+
+import Data.Attoparsec.Text
+import Control.Applicative
+
+import Data.List
+import qualified Data.Map as M
+import Data.Map ((!))
+import qualified Data.MultiSet as MS
+import qualified Data.Set as S
+
+type RuleSet = M.Map String String
+type PolyPairs = MS.MultiSet String
+
+
+main :: IO ()
+main = 
+  do  text <- TIO.readFile "data/advent14.txt"
+      let (template, rules) = successfulParse text
+      print $ part1 rules template
+      print $ part2 rules template
+
+part1 :: RuleSet -> String -> Int
+part1 rules template = (last counts) - (head counts)
+  where result = (simulateNaive rules template) !! 10
+        counts = sort $ map snd $ MS.toOccurList $ MS.fromList result
+
+simulateNaive :: RuleSet -> String -> [String]
+simulateNaive rules polymer = iterate (stepNaive rules) polymer
+
+stepNaive :: RuleSet -> String -> String
+stepNaive rules polymer = merge polymer $ concatMap (rules !) $ mkPairs polymer
+
+part2 :: RuleSet -> String -> Int
+part2 rules template = (last counts) - (head counts)
+  where pairs = MS.fromList $ mkPairs template
+        result = (simulate rules pairs) !! 40
+        elementCounts = countElements result
+        -- counts = sort $ map snd $ MS.toOccurList elementCounts
+        counts = sort $ M.elems elementCounts
+
+simulate :: RuleSet -> PolyPairs -> [PolyPairs]
+simulate rules polymer = iterate (step rules) polymer
+
+step :: RuleSet -> PolyPairs -> PolyPairs
+step rules polymer = MS.union firsts seconds
+  where firsts = MS.map (addFirst rules) polymer
+        seconds = MS.map (addSecond rules) polymer
+
+addFirst :: RuleSet -> String -> String
+addFirst rules pair = a : c
+  where a = pair!!0
+        c = rules ! pair
+
+addSecond :: RuleSet -> String -> String
+addSecond rules pair = c ++ [a]
+  where a = pair!!1
+        c = rules ! pair
+
+-- countElements :: PolyPairs -> MS.MultiSet Char
+countElements :: PolyPairs -> M.Map Char Int
+countElements pairs = counts
+  where firsts = MS.map (!!0) pairs
+        seconds = MS.map (!!1) pairs
+        elems = S.union (MS.toSet firsts) (MS.toSet seconds)
+        -- counts = MS.fromMap $ M.map ((`div` 2) . (+ 1)) $ MS.toMap $ MS.union firsts seconds
+        counts = M.map ((`div` 2) . (+ 1)) $ MS.toMap $ MS.union firsts seconds
+
+
+mkPairs :: String -> [String]
+mkPairs polymer = map stringify $ zip polymer $ tail polymer
+stringify (a, b) = [a, b]
+
+merge :: [a] -> [a] -> [a]
+merge [] ys = ys
+merge (x:xs) ys = x : (merge ys xs)
+
+-- Parse the input file
+
+inputP = (,) <$> (many1 letter) <* many1 endOfLine <*> rulesP
+
+rulesP = M.fromList <$> ruleP `sepBy` endOfLine
+ruleP = (,) <$> many1 letter <* " -> " <*> many1 letter
+
+-- successfulParse :: Text -> (Integer, [Maybe Integer])
+successfulParse input = 
+  case parseOnly inputP input of
+    Left  _err -> ("", M.empty) -- TIO.putStr $ T.pack $ parseErrorPretty err
+    Right indata -> indata
diff --git a/advent15/Main.hs b/advent15/Main.hs
new file mode 100644 (file)
index 0000000..1186a52
--- /dev/null
@@ -0,0 +1,229 @@
+-- Writeup at https://work.njae.me.uk/2021/12/13/advent-of-code-2021-day-13/
+
+
+import Debug.Trace
+
+-- import qualified Data.Text.IO as TIO
+
+-- import qualified Data.Map.Strict as M
+-- import Data.Map.Strict ((!))
+import qualified Data.PQueue.Prio.Min as P
+import qualified Data.Set as S
+import qualified Data.Sequence as Q
+import Data.Sequence ((<|), (|>), (><)) --, ViewR( (:>) ), ViewL( (:<) ))
+import Data.Foldable (foldl', sum) -- (toList, foldr', foldl', all)
+import Data.Char
+import Control.Monad.Reader
+import Control.Lens hiding ((<|), (|>), (:>), (:<))
+import Data.Maybe (fromMaybe)
+import Linear (V2(..), (^+^), (^-^), (*^), (^*))
+import Data.Array.IArray
+
+
+pattern Empty   <- (Q.viewl -> Q.EmptyL)  where Empty = Q.empty
+pattern x :< xs <- (Q.viewl -> x Q.:< xs) where (:<)  = (Q.<|) 
+pattern xs :> x <- (Q.viewr -> xs Q.:> x) where (:>)  = (Q.|>) 
+
+type BasePosition = V2 Int -- r, c
+newtype Position = Position BasePosition -- r, c
+  deriving (Eq, Ord, Show)
+newtype TiledPosition = TiledPosition BasePosition -- r, c
+  deriving (Eq, Ord, Show)
+type Grid = Array BasePosition Int
+
+data Cave = Cave 
+  { _grid :: Grid
+  , _goal :: BasePosition
+  } deriving (Eq, Ord, Show)
+makeLenses ''Cave
+
+type CaveContext = Reader Cave
+
+
+data Agendum s = 
+    Agendum { _current :: s
+            , _trail :: Q.Seq s
+            , _cost :: Int
+            } deriving (Show, Eq)
+makeLenses ''Agendum                       
+
+type Agenda s = P.MinPQueue Int (Agendum s)
+
+type ExploredStates s = S.Set s
+
+class (Eq s, Ord s, Show s) => SearchState s where
+    unwrapPos :: s -> BasePosition
+    successors :: s -> CaveContext (Q.Seq s)
+    estimateCost :: s -> CaveContext Int
+    emptySearchState :: s
+    isGoal :: s -> CaveContext Bool
+    entryCost :: s -> CaveContext Int
+
+
+instance SearchState Position where
+
+  unwrapPos (Position p) = p
+
+  emptySearchState = Position (V2 0 0)
+
+  -- successors :: Position -> CaveContext (Q.Seq Position)
+  successors here = 
+    do grid <- asks _grid
+       let neighbours = 
+            filter (inRange (bounds grid))  
+              [ (unwrapPos here) ^+^ delta
+              | delta <- [V2 -1 0, V2 1 0, V2 0 -1, V2 0 1]
+              ]
+       let succs = Q.fromList $ map Position neighbours
+       return succs
+
+  -- estimateCost :: Position -> CaveContext Int
+  estimateCost here = 
+    do goal <- asks _goal
+       let (V2 dr dc) = (unwrapPos here) ^-^ goal
+       return $ (abs dr) + (abs dc)
+
+  -- isGoal :: here -> CaveContext Bool
+  isGoal here = 
+    do goal <- asks _goal
+       return $ (unwrapPos here) == goal
+
+  entryCost here = 
+    do grid <- asks _grid
+       return $ grid ! (unwrapPos here)
+
+instance SearchState TiledPosition where
+
+  emptySearchState = TiledPosition (V2 0 0)
+
+  unwrapPos (TiledPosition p) = p
+
+  -- successors :: Position -> CaveContext (Q.Seq Position)
+  successors (TiledPosition here) = 
+    do grid <- asks _grid
+       let (lowBound, highBound) = bounds grid
+       let extendedBounds = ( lowBound
+                            , tileScale highBound
+                            )
+       let neighbours = 
+            filter (inRange extendedBounds)  
+              [ here ^+^ delta
+              | delta <- [V2 -1 0, V2 1 0, V2 0 -1, V2 0 1]
+              ]
+       let succs = Q.fromList $ map TiledPosition neighbours
+       return succs
+
+  -- estimateCost :: Position -> CaveContext Int
+  estimateCost (TiledPosition here) = 
+    do goal <- asks _goal
+       let (V2 dr dc) = here ^-^ (tileScale goal)
+       return $ (abs dr) + (abs dc)
+
+  -- isGoal :: here -> CaveContext Bool
+  isGoal (TiledPosition here) = 
+    do goal <- asks _goal
+       return $ here == (tileScale goal)
+
+  entryCost (TiledPosition (V2 r c)) = 
+    do grid <- asks _grid
+       let (_, V2 maxR maxC) = bounds grid
+       let (tileR, gridR) = r `divMod` (maxR + 1)
+       let (tileC, gridC) = c `divMod` (maxC + 1)
+       let gridCost = grid ! (V2 gridR gridC)
+       let cost = (gridCost - 1 + tileR + tileC) `mod` 9 + 1
+       return cost
+
+tileScale :: BasePosition -> BasePosition
+tileScale (V2 r c) = V2 (ts r) (ts c)
+  where ts n = (n + 1) * 5 - 1
+
+-- enTilePosition :: Position -> TiledPosition
+-- enTilePosition (V2 a b) = V2 a b
+
+------------------------------
+
+main :: IO ()
+main = 
+  do  text <- readFile "data/advent15.txt"
+      let cave = mkCave text
+      print $ part1 cave
+      print $ part2 cave
+      -- print $ part2 grid
+
+mkCave :: String -> Cave
+mkCave text = Cave { _grid = grid, _goal = V2 r c }
+  where rows = lines text
+        r = length rows - 1
+        c = (length $ head rows) - 1
+        grid = listArray ((V2 0 0), (V2 r c)) $ map mkCell $ concat rows
+        mkCell e = digitToInt e
+
+  
+part1 :: Cave -> Int
+-- part1 :: Maze -> Maybe (Agendum Portal)
+part1 cave = maybe 0 _cost result
+    where result = runReader searchCave cave :: Maybe (Agendum Position)
+
+part2 :: Cave -> Int
+-- part1 :: Maze -> Maybe (Agendum Portal)
+part2 cave = maybe 0 _cost result
+    where result = runReader searchCave cave :: Maybe (Agendum TiledPosition)
+
+-- part2 :: Maze -> Int
+-- -- part2 :: Maze -> Maybe (Agendum LevelledSearchState)
+-- part2 maze = maybe 0 _cost result
+--     where result = runReader searchMaze maze :: Maybe (Agendum LevelledSearchState)
+
+
+searchCave ::  SearchState s => CaveContext (Maybe (Agendum s))
+searchCave = 
+    do agenda <- initAgenda
+       aStar agenda S.empty
+
+initAgenda ::  SearchState s => CaveContext (Agenda s)
+initAgenda = 
+    do let ss = emptySearchState
+       c <- estimateCost ss
+       return $ P.singleton c Agendum { _current = ss, _trail = Q.empty, _cost = c}
+
+
+aStar ::  SearchState s => Agenda s -> ExploredStates s -> CaveContext (Maybe (Agendum s))
+aStar agenda closed 
+    -- | trace ("Peeping " ++ (show $ fst $ P.findMin agenda) ++ ": " ++ (show reached) ++ " <- " ++ (show $ toList $ Q.take 1 $ _trail $ currentAgendum) ++ " :: " ++ (show newAgenda)) False = undefined
+    -- | trace ("Peeping " ++ (show $ _current $ snd $ P.findMin agenda) ) False = undefined
+    | P.null agenda = return Nothing
+    | otherwise = 
+        do  let (_, currentAgendum) = P.findMin agenda
+            let reached = currentAgendum ^. current
+            nexts <- candidates currentAgendum closed
+            let newAgenda = foldl' (\q a -> P.insert (_cost a) a q) (P.deleteMin agenda) nexts
+            reachedGoal <- isGoal reached
+            if reachedGoal
+            then return (Just currentAgendum)
+            else if reached `S.member` closed
+                 then aStar (P.deleteMin agenda) closed
+                 else aStar newAgenda (S.insert reached closed)
+
+
+candidates ::  SearchState s => Agendum s -> ExploredStates s -> CaveContext (Q.Seq (Agendum s))
+candidates agendum closed = 
+    do  let candidate = agendum ^. current
+        let previous = agendum ^. trail
+        -- let prevCost = agendum ^. cost
+        succs <- successors candidate
+        let nonloops = Q.filter (\s -> s `S.notMember` closed) succs
+        mapM (makeAgendum previous) nonloops
+
+makeAgendum ::  SearchState s => (Q.Seq s) -> s -> CaveContext (Agendum s)
+makeAgendum previous newPosition = 
+    do predicted <- estimateCost newPosition
+       grid <- asks _grid
+       let newTrail = previous |> newPosition
+       let _ :< entered = newTrail
+       -- let incurred = foldr (+) 0 $ mapM entryCost entered
+       incurredQ <- mapM entryCost newTrail
+       let incurred = foldr (+) 0 incurredQ
+       return Agendum { _current = newPosition
+                      , _trail = newTrail
+                      , _cost = incurred + predicted
+                      }
diff --git a/data/advent14.txt b/data/advent14.txt
new file mode 100644 (file)
index 0000000..7ce2054
--- /dev/null
@@ -0,0 +1,102 @@
+SCVHKHVSHPVCNBKBPVHV
+
+SB -> B
+HH -> P
+VF -> N
+BS -> S
+NC -> C
+BF -> H
+BN -> H
+SP -> H
+BK -> H
+FF -> N
+VN -> B
+FN -> C
+FS -> S
+PP -> F
+ON -> H
+FV -> F
+KO -> F
+PK -> H
+VB -> S
+HS -> B
+NV -> O
+PN -> S
+VH -> B
+OS -> P
+BP -> H
+OV -> B
+HK -> S
+NN -> K
+SV -> C
+PB -> F
+SK -> F
+FB -> S
+NB -> K
+HF -> P
+FK -> K
+KV -> P
+PV -> F
+BC -> S
+FO -> N
+HC -> F
+CP -> B
+KK -> F
+PC -> S
+HN -> O
+SH -> H
+CK -> P
+CO -> F
+HP -> K
+PS -> C
+KP -> F
+OF -> K
+KS -> F
+NO -> V
+CB -> K
+NF -> N
+SF -> F
+SC -> P
+FC -> V
+BV -> B
+SS -> O
+KC -> K
+FH -> C
+OP -> C
+CF -> K
+VO -> V
+VK -> H
+KH -> O
+NP -> V
+NH -> O
+NS -> V
+BH -> C
+CH -> S
+CC -> F
+CS -> P
+SN -> F
+BO -> S
+NK -> S
+OO -> P
+VV -> F
+FP -> V
+OK -> C
+SO -> H
+KN -> P
+HO -> O
+PO -> H
+VS -> N
+PF -> N
+CV -> F
+BB -> H
+VC -> H
+HV -> B
+CN -> S
+OH -> K
+KF -> K
+HB -> S
+OC -> H
+KB -> P
+OB -> C
+VP -> C
+PH -> K
\ No newline at end of file
diff --git a/data/advent14a.txt b/data/advent14a.txt
new file mode 100644 (file)
index 0000000..6c1c3a1
--- /dev/null
@@ -0,0 +1,18 @@
+NNCB
+
+CH -> B
+HH -> N
+CB -> H
+NH -> C
+HB -> C
+HC -> B
+HN -> C
+NN -> C
+BH -> H
+NC -> B
+NB -> B
+BN -> B
+BB -> N
+BC -> B
+CC -> N
+CN -> C
\ No newline at end of file
diff --git a/data/advent15.txt b/data/advent15.txt
new file mode 100644 (file)
index 0000000..2fdd133
--- /dev/null
@@ -0,0 +1,100 @@
+7123177913871491389589264788547148381988811673752241637816163827448692499239515115894381588375396471
+9738878597866183278612514475755598861117949965829826949819696599841511616198177483157219211238226665
+2728314738118818417596585299898141515484167717378811771172771455459817575879182681543225671796579564
+3698237941798921857499612323641798179312519816114669628793538557419995793394792976116175183983211218
+8931299558895122919849817111991912299694619511272212888417418196496976619283339474157935296969177921
+4876399196561616888211818411392552865916811686918294139639998168582233818894993882378164973177419292
+1292826913925292993394627812472966298114458928424398988196171891981314199973375171711499591999295722
+2612819791685974812429441111697818261281422279834379563115293558312533728942295586148171358952659947
+6638822314594315236886818282759119972499915998333742951291758918511191291981139924297194471198819992
+3212921351399439782291441141194213794861268171719226573929478185198972881798292879922296597111267985
+9694955531925421216621667171387812673256762869119116982546712788121611188865459428311479913923252812
+8993899649958218893492947198596622311414594129991613199694121881963973616778199437361417251114898896
+2932137594986313811563891831516429822264187117794718732832322275923584989344938612756788851319215445
+9923942842169247311971329991517267154685897333913756132933889376946781927392178755261819729642288132
+9142173591893199149829423314381331583328694313941266171113924219211681916313986812131311533142191693
+5848192685188599944816146242793127138949121256118119414186816441591293151999119971396296221276131241
+3495527991317138191168853316591298662515462388318695459314259431427199827112814475291998894533661719
+6815167261798913879534189564249399392672145779914816489949341922439166128967131283857229364832939232
+9715263568753956232885237118388651333374435831664275153715817339997179993814287431396622272616271133
+1812949719568129216833815893498895318898151127318887918774267731151122617626267391916721894187735879
+6194936292994131619884424828772115321296933111248146743138951133525259921388132674296978191595215421
+8977231295463954488743917723843199269161928319992799113233196719627994185231913636481128347191888993
+7191387136716153811135921391495919122211583111131242512186177359857642411281598176173477158148461742
+8748994183994891161849125494181651122719172611112829351936539911981374881952184859949776981967135889
+4818995199799799317796797181385776752888232638392141897612916871898458673919399565456391616359619722
+2324149221529527952295121615332529396955513926313268682677391936267663467637119911666968839741927288
+9387191819291784198317392415819191724129835871495311699431299514299912942877459422153796598154195936
+3187152295157488239629224167299278792979374188163598271919325679669943944631641114319613632913941867
+4187553758199893917166292177621116363872642297713977519579918916911132498628192671316693138923217738
+7896983716218397299912126791897926852272193176651193815853749813676172192991933991241531915358592943
+2348247919145847514999915288812971366894736367322416127123839211819819893938238144595466824813581688
+6911579174885666196116496835334513924129158549472491355911727571419193999691849595418142654987162596
+8233311258291312123922594216362361184417111671134933993719988681111993994811784424145912713728546148
+1162139841389997711783335749151324349829189455223249558153435151167312198161429992286971283355217762
+9499631991542729216797153991489111797686945155575494822971821485199834897292889255296899325832659372
+9919722366494721121916812991347712311123354812314431341835584535911911183475929155451117133113518354
+3299321782846455776178299242914282826128713775681296642569791332836884518333197171822922357552638257
+4614911396711984669911199281145369229899421149212339819959928831788384791918432316191251591691214353
+4369289134597955589999495348592539727279475199412997118117152888153787932811981712348789921597223884
+1921278987992168289199159228511681682851987138959823165149863424871954671997219162181639788134671239
+8539367723644163113958998812319181651829319522919271727372655732991298341353642799614887987116119991
+9819237823912311318512271116896112568693473129616312762194271883382198451421259591659131149739567431
+3913629226255984248923677172369227529268378689129311591493392989499398541156998151934538421544988198
+9874118273932233256784989122831831438444699999969125597386734339278292411842292935966924611973117536
+3297942819139177814771683635499686478938123535294382195953689425918122743452127119188392213214162719
+6428369731511597131697241921814427613712519849617997822241919837334471918169985497447845215111571111
+4717882166389311599121985911815127671481262152181961791322981241713128381283932879815396748715326116
+9446657145378538332649933726924339585982712752416989637831889841999171924591539949915281695348616119
+1542914874298812536216998196883784913621263443517371816311128619246117922299562938819614239587919693
+1445469798118719191614518343323122521442292174917967445253456563977662989479257751894581593719739991
+7772142417741935423174485817123889139399291273428692734431984124117191994123498115727598161392677699
+6988945923714311219114921397868498885113257552168928846919896189191694261437512499791222278916169288
+7851211433643663591554743152113696322971779195631762592311915771859233266466849696548914971193499364
+4919396661388921921994122948951598619526848887659791912391223398827566916838432249598749774916496711
+1196599655895393416289389977637236641318279616416994991439226589111757355629849933169854118249997919
+5114211499998844843174919884423467219162277687929161558775139511929631962658267678221272328914953732
+8216171512426485781351969231952198433381329983288317413326512138574464114453937223541765367971856788
+2213177288792193388519618281868911182281428936911325912397967192287711833111797518143218673821192899
+2784153192416189911216612291791983219622166389621996117219781639919612667199999129821528411964639199
+4187898219537683365988849129793392557283572786889441274612197286178112735995692535456157587812218711
+3289961986387137451576181267292891928699191488241997579896141161217814198431161774438724446513817347
+1579149591461935819116247565121622891413133538179461264778198149199793117163932219237419521332475723
+8511871311713679914269882644473751984369996191729198493285714245762879942883717594273668937984612859
+1299519281323389918993242297332143893749252414799491177636152119414457637112995194255822953441323767
+5956297531138129799432717521922827212567131222732852611291511239992997934392819583522981699162914417
+9941713968132521114282728119911171371767252117572477196141318371891319144171114944838884899991129662
+3289334178322912999218672177961313951181389283788115263914991671385991338168197891735411426621896437
+3517399198911891116419161659893219997361251111462415147245636698911119144122948941611999537914127921
+7391621997684963324417791261394821117179111599877845791691691999758491717192891377392662426922381649
+9539641387391158465314288999171521516996119112739729425799782424458612492799866313395268329813142998
+7159761151187488213272297981256985319892792222143284319128613282931987743335641992228248391313519531
+3138612956672911883119197936151783665186495653914713496188952389722272421535138125838382521963591562
+2722391699271222281179116691629372616974311231381924211686611666919731221727399213415714891415614991
+7198919263899918891485598237399963318129522299496599371183799311118199698219813639932192422592379521
+7368622164517278632222981928929692926994317935549341915121242459537164841789531795135523197651979291
+1926148892518314965269213317792781877993491381788416765975716755185279549699712281281784581861751169
+9249985391769615964945784311129911697198267954993689957729719133276621353583195921579141925315925166
+6822113216689392191133553173276588849489996679488414961829594191993816192923712718939527186629116941
+5929189959684777622871367188254399766777395794834448572421243984636768429891326273999953635499781823
+3968641911437319438596991911526912992916952278875428199179942114261598893262717938299221167273799979
+3581738249268299998729188711271131183463978189544984397511934495858934421812219983183226727177137114
+1631458422911129912835999399394913599312249165919497857822112997138599391899427114543933588882749375
+7751959887233838373994262343677972499854568863234916814662714417494468417237214895924711139913939931
+4591349111791721157991319331898365223999499119349939111219419624872719169431985811875181468395293861
+9792291732129325192193663689331995249571212989662591947284119882841111636726819362215311819211795897
+1488399771452298411953629967461723138977649994883655382185266168282297595892929374412297962677511168
+7924861111421119789911192623416874981293118241238722492254626547593951768791776297212239248219618114
+8735129436119198495916414425995513415961489114319948429491748935119857139517121738987938966891125783
+9669648319182921264115692232751318121116373481894266491823915963139471491859149397216775362939414969
+9956144986618983299822988922918999922211819753624127131799113139524343936443128596821789648269963896
+7991139371219143999647978711473188557939866849119391117191869921928812871649882975753469934691399149
+5512138553215161592622431858481393373361674599955358483792512195323411931199662281891856487591112893
+9417295659949511519279658459111995961867134373999356883483499622281215996711111747445985881745648997
+5869918917951981844765591591191412765331147258751421475964719757299891817418117775918693188123993417
+1624257425257726748957618898694796968997367176999981891437592613316142593927894818699625341211118521
+2691266599149129279888396797262149311989598668888453732682612511116669997393343711966114278812466911
+3871919914394816781582584188751221559169429281577559622382565992458117893269798921374515671374711269
+2937696914977492589611351118178169911891394771517293921914627713178322419232244298725129511451733882
+8914885347357182842138897239419948884639745551712216898133998524382831475347911516291799198921935134
+5527717416681927222852118761365169418317189819727214551585891949313611155917969911112533919284572421
\ No newline at end of file
diff --git a/data/advent15a.txt b/data/advent15a.txt
new file mode 100644 (file)
index 0000000..7d9d562
--- /dev/null
@@ -0,0 +1,10 @@
+1163751742
+1381373672
+2136511328
+3694931569
+7463417111
+1319128137
+1359912421
+3125421639
+1293138521
+2311944581
\ No newline at end of file
diff --git a/problems/day14.html b/problems/day14.html
new file mode 100644 (file)
index 0000000..a3e0d04
--- /dev/null
@@ -0,0 +1,165 @@
+<!DOCTYPE html>
+<html lang="en-us">
+<head>
+<meta charset="utf-8"/>
+<title>Day 14 - Advent of Code 2021</title>
+<!--[if lt IE 9]><script src="/static/html5.js"></script><![endif]-->
+<link href='//fonts.googleapis.com/css?family=Source+Code+Pro:300&subset=latin,latin-ext' rel='stylesheet' type='text/css'/>
+<link rel="stylesheet" type="text/css" href="/static/style.css?26"/>
+<link rel="stylesheet alternate" type="text/css" href="/static/highcontrast.css?0" title="High Contrast"/>
+<link rel="shortcut icon" href="/favicon.png"/>
+</head><!--
+
+
+
+
+Oh, hello!  Funny seeing you here.
+
+I appreciate your enthusiasm, but you aren't going to find much down here.
+There certainly aren't clues to any of the puzzles.  The best surprises don't
+even appear in the source until you unlock them for real.
+
+Please be careful with automated requests; I'm not a massive company, and I can
+only take so much traffic.  Please be considerate so that everyone gets to play.
+
+If you're curious about how Advent of Code works, it's running on some custom
+Perl code. Other than a few integrations (auth, analytics, social media), I
+built the whole thing myself, including the design, animations, prose, and all
+of the puzzles.
+
+The puzzles are most of the work; preparing a new calendar and a new set of
+puzzles each year takes all of my free time for 4-5 months. A lot of effort
+went into building this thing - I hope you're enjoying playing it as much as I
+enjoyed making it for you!
+
+If you'd like to hang out, I'm @ericwastl on Twitter.
+
+- Eric Wastl
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+-->
+<body>
+<header><div><h1 class="title-global"><a href="/">Advent of Code</a></h1><nav><ul><li><a href="/2021/about">[About]</a></li><li><a href="/2021/events">[Events]</a></li><li><a href="https://teespring.com/stores/advent-of-code" target="_blank">[Shop]</a></li><li><a href="/2021/settings">[Settings]</a></li><li><a href="/2021/auth/logout">[Log Out]</a></li></ul></nav><div class="user">Neil Smith <a href="/2021/support" class="supporter-badge" title="Advent of Code Supporter">(AoC++)</a> <span class="star-count">30*</span></div></div><div><h1 class="title-event">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="title-event-wrap"></span><a href="/2021">2021</a><span class="title-event-wrap"></span></h1><nav><ul><li><a href="/2021">[Calendar]</a></li><li><a href="/2021/support">[AoC++]</a></li><li><a href="/2021/sponsors">[Sponsors]</a></li><li><a href="/2021/leaderboard">[Leaderboard]</a></li><li><a href="/2021/stats">[Stats]</a></li></ul></nav></div></header>
+
+<div id="sidebar">
+<div id="sponsor"><div class="quiet">Our <a href="/2021/sponsors">sponsors</a> help make Advent of Code possible:</div><div class="sponsor"><a href="https://www.tcgplayer.com/adventofcode/?utm_campaign=aoc&amp;utm_source=adventOfCode&amp;utm_medium=aocPromo" target="_blank" onclick="if(ga)ga('send','event','sponsor','sidebar',this.href);" rel="noopener">TCGplayer.com</a> - - . Hiring .NET &amp; . . JS engineers. . . Help us serve . . our community . . of game stores. . &amp; hobbyists!! . ---- JOIN US ----</div></div>
+</div><!--/sidebar-->
+
+<main>
+<script>window.addEventListener('click', function(e,s,r){if(e.target.nodeName==='CODE'&&e.detail===3){s=window.getSelection();s.removeAllRanges();r=document.createRange();r.selectNodeContents(e.target);s.addRange(r);}});</script>
+<article class="day-desc"><h2>--- Day 14: Extended Polymerization ---</h2><p>The incredible pressures at this depth are starting to put a strain on your submarine. The submarine has <a href="https://en.wikipedia.org/wiki/Polymerization" target="_blank">polymerization</a> equipment that would produce suitable materials to reinforce the submarine, and the nearby volcanically-active caves should even have the necessary input elements in sufficient quantities.</p>
+<p>The submarine manual contains <span title="HO&#xa;&#xa;HO -&gt; OH">instructions</span> for finding the optimal polymer formula; specifically, it offers a <em>polymer template</em> and a list of <em>pair insertion</em> rules (your puzzle input). You just need to work out what polymer would result after repeating the pair insertion process a few times.</p>
+<p>For example:</p>
+<pre><code>NNCB
+
+CH -&gt; B
+HH -&gt; N
+CB -&gt; H
+NH -&gt; C
+HB -&gt; C
+HC -&gt; B
+HN -&gt; C
+NN -&gt; C
+BH -&gt; H
+NC -&gt; B
+NB -&gt; B
+BN -&gt; B
+BB -&gt; N
+BC -&gt; B
+CC -&gt; N
+CN -&gt; C
+</code></pre>
+<p>The first line is the <em>polymer template</em> - this is the starting point of the process.</p>
+<p>The following section defines the <em>pair insertion</em> rules. A rule like <code>AB -&gt; C</code> means that when elements <code>A</code> and <code>B</code> are immediately adjacent, element <code>C</code> should be inserted between them. These insertions all happen simultaneously.</p>
+<p>So, starting with the polymer template <code>NNCB</code>, the first step simultaneously considers all three pairs:</p>
+<ul>
+<li>The first pair (<code>NN</code>) matches the rule <code>NN -&gt; C</code>, so element <code><em>C</em></code> is inserted between the first <code>N</code> and the second <code>N</code>.</li>
+<li>The second pair (<code>NC</code>) matches the rule <code>NC -&gt; B</code>, so element <code><em>B</em></code> is inserted between the <code>N</code> and the <code>C</code>.</li>
+<li>The third pair (<code>CB</code>) matches the rule <code>CB -&gt; H</code>, so element <code><em>H</em></code> is inserted between the <code>C</code> and the <code>B</code>.</li>
+</ul>
+<p>Note that these pairs overlap: the second element of one pair is the first element of the next pair. Also, because all pairs are considered simultaneously, inserted elements are not considered to be part of a pair until the next step.</p>
+<p>After the first step of this process, the polymer becomes <code>N<em>C</em>N<em>B</em>C<em>H</em>B</code>.</p>
+<p>Here are the results of a few steps using the above rules:</p>
+<pre><code>Template:     NNCB
+After step 1: NCNBCHB
+After step 2: NBCCNBBBCBHCB
+After step 3: NBBBCNCCNBBNBNBBCHBHHBCHB
+After step 4: NBBNBNBBCCNBCNCCNBBNBBNBBBNBBNBBCBHCBHHNHCBBCBHCB
+</code></pre>
+<p>This polymer grows quickly. After step 5, it has length 97; After step 10, it has length 3073. After step 10, <code>B</code> occurs 1749 times, <code>C</code> occurs 298 times, <code>H</code> occurs 161 times, and <code>N</code> occurs 865 times; taking the quantity of the most common element (<code>B</code>, 1749) and subtracting the quantity of the least common element (<code>H</code>, 161) produces <code>1749 - 161 = <em>1588</em></code>.</p>
+<p>Apply 10 steps of pair insertion to the polymer template and find the most and least common elements in the result. <em>What do you get if you take the quantity of the most common element and subtract the quantity of the least common element?</em></p>
+</article>
+<p>Your puzzle answer was <code>2712</code>.</p><article class="day-desc"><h2 id="part2">--- Part Two ---</h2><p>The resulting polymer isn't nearly strong enough to reinforce the submarine. You'll need to run more steps of the pair insertion process; a total of <em>40 steps</em> should do it.</p>
+<p>In the above example, the most common element is <code>B</code> (occurring <code>2192039569602</code> times) and the least common element is <code>H</code> (occurring <code>3849876073</code> times); subtracting these produces <code><em>2188189693529</em></code>.</p>
+<p>Apply <em>40</em> steps of pair insertion to the polymer template and find the most and least common elements in the result. <em>What do you get if you take the quantity of the most common element and subtract the quantity of the least common element?</em></p>
+</article>
+<p>Your puzzle answer was <code>8336623059567</code>.</p><p class="day-success">Both parts of this puzzle are complete! They provide two gold stars: **</p>
+<p>At this point, you should <a href="/2021">return to your Advent calendar</a> and try another puzzle.</p>
+<p>If you still want to see it, you can <a href="14/input" target="_blank">get your puzzle input</a>.</p>
+<p>You can also <span class="share">[Share<span class="share-content">on
+  <a href="https://twitter.com/intent/tweet?text=I%27ve+completed+%22Extended+Polymerization%22+%2D+Day+14+%2D+Advent+of+Code+2021&amp;url=https%3A%2F%2Fadventofcode%2Ecom%2F2021%2Fday%2F14&amp;related=ericwastl&amp;hashtags=AdventOfCode" target="_blank">Twitter</a>
+  <a href="javascript:void(0);" onclick="var mastodon_instance=prompt('Mastodon Instance / Server Name?'); if(typeof mastodon_instance==='string' && mastodon_instance.length){this.href='https://'+mastodon_instance+'/share?text=I%27ve+completed+%22Extended+Polymerization%22+%2D+Day+14+%2D+Advent+of+Code+2021+%23AdventOfCode+https%3A%2F%2Fadventofcode%2Ecom%2F2021%2Fday%2F14'}else{return false;}" target="_blank">Mastodon</a
+></span>]</span> this puzzle.</p>
+</main>
+
+<!-- ga -->
+<script>
+(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+ga('create', 'UA-69522494-1', 'auto');
+ga('set', 'anonymizeIp', true);
+ga('send', 'pageview');
+</script>
+<!-- /ga -->
+</body>
+</html>
\ No newline at end of file
diff --git a/problems/day15.html b/problems/day15.html
new file mode 100644 (file)
index 0000000..b61e18f
--- /dev/null
@@ -0,0 +1,264 @@
+<!DOCTYPE html>
+<html lang="en-us">
+<head>
+<meta charset="utf-8"/>
+<title>Day 15 - Advent of Code 2021</title>
+<!--[if lt IE 9]><script src="/static/html5.js"></script><![endif]-->
+<link href='//fonts.googleapis.com/css?family=Source+Code+Pro:300&subset=latin,latin-ext' rel='stylesheet' type='text/css'/>
+<link rel="stylesheet" type="text/css" href="/static/style.css?26"/>
+<link rel="stylesheet alternate" type="text/css" href="/static/highcontrast.css?0" title="High Contrast"/>
+<link rel="shortcut icon" href="/favicon.png"/>
+</head><!--
+
+
+
+
+Oh, hello!  Funny seeing you here.
+
+I appreciate your enthusiasm, but you aren't going to find much down here.
+There certainly aren't clues to any of the puzzles.  The best surprises don't
+even appear in the source until you unlock them for real.
+
+Please be careful with automated requests; I'm not a massive company, and I can
+only take so much traffic.  Please be considerate so that everyone gets to play.
+
+If you're curious about how Advent of Code works, it's running on some custom
+Perl code. Other than a few integrations (auth, analytics, social media), I
+built the whole thing myself, including the design, animations, prose, and all
+of the puzzles.
+
+The puzzles are most of the work; preparing a new calendar and a new set of
+puzzles each year takes all of my free time for 4-5 months. A lot of effort
+went into building this thing - I hope you're enjoying playing it as much as I
+enjoyed making it for you!
+
+If you'd like to hang out, I'm @ericwastl on Twitter.
+
+- Eric Wastl
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+-->
+<body>
+<header><div><h1 class="title-global"><a href="/">Advent of Code</a></h1><nav><ul><li><a href="/2021/about">[About]</a></li><li><a href="/2021/events">[Events]</a></li><li><a href="https://teespring.com/stores/advent-of-code" target="_blank">[Shop]</a></li><li><a href="/2021/settings">[Settings]</a></li><li><a href="/2021/auth/logout">[Log Out]</a></li></ul></nav><div class="user">Neil Smith <a href="/2021/support" class="supporter-badge" title="Advent of Code Supporter">(AoC++)</a> <span class="star-count">30*</span></div></div><div><h1 class="title-event">&nbsp;&nbsp;&nbsp;<span class="title-event-wrap">sub y{</span><a href="/2021">2021</a><span class="title-event-wrap">}</span></h1><nav><ul><li><a href="/2021">[Calendar]</a></li><li><a href="/2021/support">[AoC++]</a></li><li><a href="/2021/sponsors">[Sponsors]</a></li><li><a href="/2021/leaderboard">[Leaderboard]</a></li><li><a href="/2021/stats">[Stats]</a></li></ul></nav></div></header>
+
+<div id="sidebar">
+<div id="sponsor"><div class="quiet">Our <a href="/2021/sponsors">sponsors</a> help make Advent of Code possible:</div><div class="sponsor"><a href="https://rick379856.typeform.com/to/oQ0e2jpi?utm_source=event&amp;utm_medium=ad&amp;utm_campaign=adventofcode2021" target="_blank" onclick="if(ga)ga('send','event','sponsor','sidebar',this.href);" rel="noopener">Honeycomb</a> - You like performant, correct code. So do we. Distributed systems should be easy to understand. Use Honeycomb for free to debug your distributed systems and get a free shirt. Download our white papers and watch our demo.</div></div>
+</div><!--/sidebar-->
+
+<main>
+<script>window.addEventListener('click', function(e,s,r){if(e.target.nodeName==='CODE'&&e.detail===3){s=window.getSelection();s.removeAllRanges();r=document.createRange();r.selectNodeContents(e.target);s.addRange(r);}});</script>
+<article class="day-desc"><h2>--- Day 15: Chiton ---</h2><p>You've almost reached the exit of the cave, but the walls are getting closer together. Your submarine can barely still fit, though; the main problem is that the walls of the cave are covered in <a href="https://en.wikipedia.org/wiki/Chiton" target="_blank">chitons</a>, and it would be best not to bump any of them.</p>
+<p>The cavern is large, but has a very low ceiling, restricting your motion to two dimensions. The shape of the cavern resembles a square; a quick scan of chiton density produces a map of <em>risk level</em> throughout the cave (your puzzle input). For example:</p>
+<pre><code>1163751742
+1381373672
+2136511328
+3694931569
+7463417111
+1319128137
+1359912421
+3125421639
+1293138521
+2311944581
+</code></pre>
+<p>You start in the top left position, your destination is the bottom right position, and you <span title="Can't go diagonal until we can repair the caterpillar unit. Could be the liquid helium or the superconductors.">cannot move diagonally</span>. The number at each position is its <em>risk level</em>; to determine the total risk of an entire path, add up the risk levels of each position you <em>enter</em> (that is, don't count the risk level of your starting position unless you enter it; leaving it adds no risk to your total).</p>
+<p>Your goal is to find a path with the <em>lowest total risk</em>. In this example, a path with the lowest total risk is highlighted here:</p>
+<pre><code><em>1</em>163751742
+<em>1</em>381373672
+<em>2136511</em>328
+369493<em>15</em>69
+7463417<em>1</em>11
+1319128<em>13</em>7
+13599124<em>2</em>1
+31254216<em>3</em>9
+12931385<em>21</em>
+231194458<em>1</em>
+</code></pre>
+<p>The total risk of this path is <code><em>40</em></code> (the starting position is never entered, so its risk is not counted).</p>
+<p><em>What is the lowest total risk of any path from the top left to the bottom right?</em></p>
+</article>
+<p>Your puzzle answer was <code>503</code>.</p><article class="day-desc"><h2 id="part2">--- Part Two ---</h2><p>Now that you know how to find low-risk paths in the cave, you can try to find your way out.</p>
+<p>The entire cave is actually <em>five times larger in both dimensions</em> than you thought; the area you originally scanned is just one tile in a 5x5 tile area that forms the full map. Your original map tile repeats to the right and downward; each time the tile repeats to the right or downward, all of its risk levels <em>are 1 higher</em> than the tile immediately up or left of it. However, risk levels above <code>9</code> wrap back around to <code>1</code>. So, if your original map had some position with a risk level of <code>8</code>, then that same position on each of the 25 total tiles would be as follows:</p>
+<pre><code>8 9 1 2 3
+9 1 2 3 4
+1 2 3 4 5
+2 3 4 5 6
+3 4 5 6 7
+</code></pre>
+<p>Each single digit above corresponds to the example position with a value of <code>8</code> on the top-left tile. Because the full map is actually five times larger in both dimensions, that position appears a total of 25 times, once in each duplicated tile, with the values shown above.</p>
+<p>Here is the full five-times-as-large version of the first example above, with the original map in the top left corner highlighted:</p>
+<pre><code><em>1163751742</em>2274862853338597396444961841755517295286
+<em>1381373672</em>2492484783351359589446246169155735727126
+<em>2136511328</em>3247622439435873354154698446526571955763
+<em>3694931569</em>4715142671582625378269373648937148475914
+<em>7463417111</em>8574528222968563933317967414442817852555
+<em>1319128137</em>2421239248353234135946434524615754563572
+<em>1359912421</em>2461123532357223464346833457545794456865
+<em>3125421639</em>4236532741534764385264587549637569865174
+<em>1293138521</em>2314249632342535174345364628545647573965
+<em>2311944581</em>3422155692453326671356443778246755488935
+22748628533385973964449618417555172952866628316397
+24924847833513595894462461691557357271266846838237
+32476224394358733541546984465265719557637682166874
+47151426715826253782693736489371484759148259586125
+85745282229685639333179674144428178525553928963666
+24212392483532341359464345246157545635726865674683
+24611235323572234643468334575457944568656815567976
+42365327415347643852645875496375698651748671976285
+23142496323425351743453646285456475739656758684176
+34221556924533266713564437782467554889357866599146
+33859739644496184175551729528666283163977739427418
+35135958944624616915573572712668468382377957949348
+43587335415469844652657195576376821668748793277985
+58262537826937364893714847591482595861259361697236
+96856393331796741444281785255539289636664139174777
+35323413594643452461575456357268656746837976785794
+35722346434683345754579445686568155679767926678187
+53476438526458754963756986517486719762859782187396
+34253517434536462854564757396567586841767869795287
+45332667135644377824675548893578665991468977611257
+44961841755517295286662831639777394274188841538529
+46246169155735727126684683823779579493488168151459
+54698446526571955763768216687487932779859814388196
+69373648937148475914825958612593616972361472718347
+17967414442817852555392896366641391747775241285888
+46434524615754563572686567468379767857948187896815
+46833457545794456865681556797679266781878137789298
+64587549637569865174867197628597821873961893298417
+45364628545647573965675868417678697952878971816398
+56443778246755488935786659914689776112579188722368
+55172952866628316397773942741888415385299952649631
+57357271266846838237795794934881681514599279262561
+65719557637682166874879327798598143881961925499217
+71484759148259586125936169723614727183472583829458
+28178525553928963666413917477752412858886352396999
+57545635726865674683797678579481878968159298917926
+57944568656815567976792667818781377892989248891319
+75698651748671976285978218739618932984172914319528
+56475739656758684176786979528789718163989182927419
+67554889357866599146897761125791887223681299833479
+</code></pre>
+<p>Equipped with the full map, you can now find a path from the top left corner to the bottom right corner with the lowest total risk:</p>
+<pre><code><em>1</em>1637517422274862853338597396444961841755517295286
+<em>1</em>3813736722492484783351359589446246169155735727126
+<em>2</em>1365113283247622439435873354154698446526571955763
+<em>3</em>6949315694715142671582625378269373648937148475914
+<em>7</em>4634171118574528222968563933317967414442817852555
+<em>1</em>3191281372421239248353234135946434524615754563572
+<em>1</em>3599124212461123532357223464346833457545794456865
+<em>3</em>1254216394236532741534764385264587549637569865174
+<em>1</em>2931385212314249632342535174345364628545647573965
+<em>2</em>3119445813422155692453326671356443778246755488935
+<em>2</em>2748628533385973964449618417555172952866628316397
+<em>2</em>4924847833513595894462461691557357271266846838237
+<em>324</em>76224394358733541546984465265719557637682166874
+47<em>15</em>1426715826253782693736489371484759148259586125
+857<em>4</em>5282229685639333179674144428178525553928963666
+242<em>1</em>2392483532341359464345246157545635726865674683
+246<em>1123532</em>3572234643468334575457944568656815567976
+423653274<em>1</em>5347643852645875496375698651748671976285
+231424963<em>2342</em>5351743453646285456475739656758684176
+342215569245<em>332</em>66713564437782467554889357866599146
+33859739644496<em>1</em>84175551729528666283163977739427418
+35135958944624<em>61</em>6915573572712668468382377957949348
+435873354154698<em>44</em>652657195576376821668748793277985
+5826253782693736<em>4</em>893714847591482595861259361697236
+9685639333179674<em>1</em>444281785255539289636664139174777
+3532341359464345<em>2461</em>575456357268656746837976785794
+3572234643468334575<em>4</em>579445686568155679767926678187
+5347643852645875496<em>3</em>756986517486719762859782187396
+3425351743453646285<em>4564</em>757396567586841767869795287
+4533266713564437782467<em>554</em>8893578665991468977611257
+449618417555172952866628<em>3163</em>9777394274188841538529
+462461691557357271266846838<em>2</em>3779579493488168151459
+546984465265719557637682166<em>8</em>7487932779859814388196
+693736489371484759148259586<em>125</em>93616972361472718347
+17967414442817852555392896366<em>6413</em>91747775241285888
+46434524615754563572686567468379<em>7</em>67857948187896815
+46833457545794456865681556797679<em>26</em>6781878137789298
+645875496375698651748671976285978<em>21</em>873961893298417
+4536462854564757396567586841767869<em>7</em>952878971816398
+5644377824675548893578665991468977<em>6112</em>579188722368
+5517295286662831639777394274188841538<em>5</em>299952649631
+5735727126684683823779579493488168151<em>4</em>599279262561
+6571955763768216687487932779859814388<em>1</em>961925499217
+7148475914825958612593616972361472718<em>34725</em>83829458
+28178525553928963666413917477752412858886<em>3</em>52396999
+57545635726865674683797678579481878968159<em>2</em>98917926
+57944568656815567976792667818781377892989<em>24</em>8891319
+756986517486719762859782187396189329841729<em>1431</em>9528
+564757396567586841767869795287897181639891829<em>2</em>7419
+675548893578665991468977611257918872236812998<em>33479</em>
+</code></pre>
+<p>The total risk of this path is <code><em>315</em></code> (the starting position is still never entered, so its risk is not counted).</p>
+<p>Using the full map, <em>what is the lowest total risk of any path from the top left to the bottom right?</em></p>
+</article>
+<p>Your puzzle answer was <code>2853</code>.</p><p class="day-success">Both parts of this puzzle are complete! They provide two gold stars: **</p>
+<p>At this point, you should <a href="/2021">return to your Advent calendar</a> and try another puzzle.</p>
+<p>If you still want to see it, you can <a href="15/input" target="_blank">get your puzzle input</a>.</p>
+<p>You can also <span class="share">[Share<span class="share-content">on
+  <a href="https://twitter.com/intent/tweet?text=I%27ve+completed+%22Chiton%22+%2D+Day+15+%2D+Advent+of+Code+2021&amp;url=https%3A%2F%2Fadventofcode%2Ecom%2F2021%2Fday%2F15&amp;related=ericwastl&amp;hashtags=AdventOfCode" target="_blank">Twitter</a>
+  <a href="javascript:void(0);" onclick="var mastodon_instance=prompt('Mastodon Instance / Server Name?'); if(typeof mastodon_instance==='string' && mastodon_instance.length){this.href='https://'+mastodon_instance+'/share?text=I%27ve+completed+%22Chiton%22+%2D+Day+15+%2D+Advent+of+Code+2021+%23AdventOfCode+https%3A%2F%2Fadventofcode%2Ecom%2F2021%2Fday%2F15'}else{return false;}" target="_blank">Mastodon</a
+></span>]</span> this puzzle.</p>
+</main>
+
+<!-- ga -->
+<script>
+(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+ga('create', 'UA-69522494-1', 'auto');
+ga('set', 'anonymizeIp', true);
+ga('send', 'pageview');
+</script>
+<!-- /ga -->
+</body>
+</html>
\ No newline at end of file