Day 18
authorNeil Smith <neil.git@njae.me.uk>
Sun, 23 Dec 2018 14:53:01 +0000 (14:53 +0000)
committerNeil Smith <neil.git@njae.me.uk>
Sun, 23 Dec 2018 14:53:01 +0000 (14:53 +0000)
advent-of-code.cabal
data/advent18-small.txt [new file with mode: 0644]
data/advent18.txt [new file with mode: 0644]
problems/day18.html [new file with mode: 0644]
src/advent18/advent18.hs [new file with mode: 0644]

index 22868e72760423b3aa7f621cc13173cd78db51d0..3e787cfedd7ad617791eeabb90b11215f37d6978 100644 (file)
@@ -208,3 +208,22 @@ executable advent17
                      , text
                      , megaparsec
                      , containers
+
+executable advent18
+  hs-source-dirs:      src/advent18
+  main-is:             advent18.hs
+  default-language:    Haskell2010
+  build-depends:       base >= 4.7 && < 5
+                     , text
+                     , megaparsec
+                     , containers
+
+executable advent19
+  hs-source-dirs:      src/advent19
+  main-is:             advent19.hs
+  default-language:    Haskell2010
+  build-depends:       base >= 4.7 && < 5
+                     , containers
+                     , mtl
+                     , text
+                     , megaparsec   
\ No newline at end of file
diff --git a/data/advent18-small.txt b/data/advent18-small.txt
new file mode 100644 (file)
index 0000000..13d299d
--- /dev/null
@@ -0,0 +1,10 @@
+.#.#...|#.
+.....#|##|
+.|..|...#.
+..|#.....#
+#.#|||#|#|
+...#.||...
+.|....|...
+||...#|.#|
+|.||||..|.
+...#.|..|.
diff --git a/data/advent18.txt b/data/advent18.txt
new file mode 100644 (file)
index 0000000..77f7e29
--- /dev/null
@@ -0,0 +1,50 @@
+...##.....|..|#.##|#.......#...#|.|..#..#|#||#..##
+|#.#...||.#|.....|#|.#.|....#......|#.|....#....#.
+..#..#.#..#..||.#.##|#|.|..|........|#...#|.|.||#.
+|#....|.|.#.|....||###|.|#|..........#...#....#|.#
+#.....|....#...#|||#.......|#.........|#..###|.#|#
+..#.|.|...||#...||.#|...#.|...###|....|||.#|.#....
+.|..#..#.##|...|##...#.##.|.|..|.#...#|..|.#|.....
+#||.|||.#..###|....|.|...|||...|||.|...||||#...#|.
+#|..||.#..#..#|.|##.......#|#....#...##.|#...#.#||
+..#....#...|....||.#||.##|#.#|.....#.#|.|#...#.#.|
+.|#|.||......|#....#..|#|#.##.|...|....|#.|.#.#.#.
+.....||##...||..#.||......#|.||..|.|..|..........|
+#|#..#||.|....||.|..|...||....|..|...##........|..
+.|#.|#|..|##...||.|##.#...#|#..|.#.|.|#..|..#...#.
+##.......#.|....|.|#.|.#..#.#|.#|..###...#...#..|.
+#..#.....##.....#...##.|..#..##.|.#.##.....|.|..##
+....#.||....|.....|...|..#...|.|.....#.##||....#..
+|#.#..#..#.........||#..|.|#...........|.|#......#
+|#..#.|..#..|.#..||.##..|.#.|..||...||.|.#|....#.|
+#.#|.....#..||#....#.|..#..|.#|....|..|....|#....|
+......||....||||.........##|....#..||.|.......|...
+#.....|..#.|.##|..|.|.|#.#..|.....|..|#.|..|.|..#.
+###|.|.#..##.#.|#...##||.#|...##|#....##...#....#|
+..|...##....#.....##......|.#|..|...#|#.||#..|.|..
+|.||#..||....#..|......#|.#.##.#..|.#|#.|...##||#.
+#.#.|..|.....|#.#|#.##||...||.||.||.||......|....#
+..#|.......|#|.|....|...#.#|..||.#.#||.#..#|.....|
+...|#.##|...#.|...#.|....#.##...|.#.#..#.....#|...
+.....#.....|...#.##.#.|......|...#.#|.....|.#.|.#.
+..#....#..|...|||..#..#.......#|..|..#|.|...|#..#|
+..||||........#|.#.##...|..|||#...|...#|.|###...||
+||#.##.||.....#..||...........|....|.....|#|||#...
+..|||#..|.||..|#.|#.#...#|#...|.###.#...|...#....#
+...........|||..|||#.#...#|....####.##||...|..|...
+.##.#|##|....#..|.#|#|.......|.|.....#..|...|||...
+..#...|.|..#..#..|###.#........|......#.#....##|..
+#.|..|#|.#.#..||...#.#.....|#.|#......#||.#.|.#...
+...#.....##.|##...#..#|.#.#.#..||..|#....#####..|.
+.....#...#.#|........##|#.#.#....|.|........#|||..
+|..##|#.|.|...||#..#.|..#..##..#|.......#|#.#..|.|
+|..|....#..|..|....#.|...##....|#...|...|.|#.|...|
+..|.|#|#...|.##..##|||..#..|...#..#||...#|.|.#|.|.
+.|...||.##.#...|.#.#...###||.|###|......|#....####
+.|.#..#.|..|##.....|.|.#.#||.#|.......|#..|##.||..
+...|..||...#|.|..|.....|.#.##.#..#...|##.|..##..#.
+##.|.#...#|#|...##|#.....|..|#.#.#.##.#|...||..#..
+##|.#..|....#..#|.#|..||...#......#.#|.#.#|..#|#..
+|....|.#|#......|....#.#.....|..#.|...|##....|.||#
+|.#.|...#...||#.##...|.|..#||...|..##|#......#.|..
+...|#.|......#....#......#.....#.#|..||..|#..#...#
diff --git a/problems/day18.html b/problems/day18.html
new file mode 100644 (file)
index 0000000..d675249
--- /dev/null
@@ -0,0 +1,267 @@
+<!DOCTYPE html>
+<html lang="en-us">
+<head>
+<meta charset="utf-8"/>
+<title>Day 18 - Advent of Code 2018</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?19"/>
+<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 Google, 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, ads, 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; the easiest ones take 3-4 hours each, but the
+harder ones take 6-8 hours, and a few even longer than that. 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="/2018/about">[About]</a></li><li><a href="/2018/events">[Events]</a></li><li><a href="https://teespring.com/adventofcode" target="_blank">[Shop]</a></li><li><a href="/2018/settings">[Settings]</a></li><li><a href="/2018/auth/logout">[Log Out]</a></li></ul></nav><div class="user">Neil Smith <a href="/2018/support" class="supporter-badge" title="Advent of Code Supporter">(AoC++)</a> <span class="star-count">37*</span></div></div><div><h1 class="title-event">&nbsp;&nbsp;&nbsp;<span class="title-event-wrap">0x0000|</span><a href="/2018">2018</a><span class="title-event-wrap"></span></h1><nav><ul><li><a href="/2018">[Calendar]</a></li><li><a href="/2018/support">[AoC++]</a></li><li><a href="/2018/sponsors">[Sponsors]</a></li><li><a href="/2018/leaderboard">[Leaderboard]</a></li><li><a href="/2018/stats">[Stats]</a></li></ul></nav></div></header>
+
+<div id="sidebar">
+<div id="sponsor"><div class="quiet">Our <a href="/2018/sponsors">sponsors</a> help make Advent of Code possible:</div><div class="sponsor"><a href="https://www.honeypot.io/" target="_blank" onclick="if(ga)ga('send','event','sponsor','click',this.href);" rel="noopener">Honeypot.io</a> - Europe's Tech Job Platform where companies apply to you with salary and tech stack upfront!</div></div>
+</div><!--/sidebar-->
+
+<main>
+<article class="day-desc"><h2>--- Day 18: Settlers of The North Pole ---</h2><p>On the outskirts of the North Pole base construction project, many Elves are collecting <span title="Trade wood for sheep?">lumber</span>.</p>
+<p>The lumber collection area is 50 acres by 50 acres; each acre can be either <em>open ground</em> (<code>.</code>), <em>trees</em> (<code>|</code>), or a <em>lumberyard</em> (<code>#</code>). You take a scan of the area (your puzzle input).</p>
+<p>Strange magic is at work here: each minute, the landscape looks entirely different. In exactly <em>one minute</em>, an open acre can fill with trees, a wooded acre can be converted to a lumberyard, or a lumberyard can be cleared to open ground (the lumber having been sent to other projects).</p>
+<p>The change to each acre is based entirely on <em>the contents of that acre</em> as well as <em>the number of open, wooded, or lumberyard acres adjacent to it</em> at the start of each minute. Here, "adjacent" means any of the eight acres surrounding that acre. (Acres on the edges of the lumber collection area might have fewer than eight adjacent acres; the missing acres aren't counted.)</p>
+<p>In particular:</p>
+<ul>
+<li>An <em>open</em> acre will become filled with <em>trees</em> if <em>three or more</em> adjacent acres contained trees. Otherwise, nothing happens.</li>
+<li>An acre filled with <em>trees</em> will become a <em>lumberyard</em> if <em>three or more</em> adjacent acres were lumberyards. Otherwise, nothing happens.</li>
+<li>An acre containing a <em>lumberyard</em> will remain a <em>lumberyard</em> if it was adjacent to <em>at least one other lumberyard and at least one acre containing trees</em>. Otherwise, it becomes <em>open</em>.</li>
+</ul>
+<p>These changes happen across all acres <em>simultaneously</em>, each of them using the state of all acres at the beginning of the minute and changing to their new form by the end of that same minute. Changes that happen during the minute don't affect each other.</p>
+<p>For example, suppose the lumber collection area is instead only 10 by 10 acres with this initial configuration:</p>
+<pre><code>Initial state:
+.#.#...|#.
+.....#|##|
+.|..|...#.
+..|#.....#
+#.#|||#|#|
+...#.||...
+.|....|...
+||...#|.#|
+|.||||..|.
+...#.|..|.
+
+After 1 minute:
+.......##.
+......|###
+.|..|...#.
+..|#||...#
+..##||.|#|
+...#||||..
+||...|||..
+|||||.||.|
+||||||||||
+....||..|.
+
+After 2 minutes:
+.......#..
+......|#..
+.|.|||....
+..##|||..#
+..###|||#|
+...#|||||.
+|||||||||.
+||||||||||
+||||||||||
+.|||||||||
+
+After 3 minutes:
+.......#..
+....|||#..
+.|.||||...
+..###|||.#
+...##|||#|
+.||##|||||
+||||||||||
+||||||||||
+||||||||||
+||||||||||
+
+After 4 minutes:
+.....|.#..
+...||||#..
+.|.#||||..
+..###||||#
+...###||#|
+|||##|||||
+||||||||||
+||||||||||
+||||||||||
+||||||||||
+
+After 5 minutes:
+....|||#..
+...||||#..
+.|.##||||.
+..####|||#
+.|.###||#|
+|||###||||
+||||||||||
+||||||||||
+||||||||||
+||||||||||
+
+After 6 minutes:
+...||||#..
+...||||#..
+.|.###|||.
+..#.##|||#
+|||#.##|#|
+|||###||||
+||||#|||||
+||||||||||
+||||||||||
+||||||||||
+
+After 7 minutes:
+...||||#..
+..||#|##..
+.|.####||.
+||#..##||#
+||##.##|#|
+|||####|||
+|||###||||
+||||||||||
+||||||||||
+||||||||||
+
+After 8 minutes:
+..||||##..
+..|#####..
+|||#####|.
+||#...##|#
+||##..###|
+||##.###||
+|||####|||
+||||#|||||
+||||||||||
+||||||||||
+
+After 9 minutes:
+..||###...
+.||#####..
+||##...##.
+||#....###
+|##....##|
+||##..###|
+||######||
+|||###||||
+||||||||||
+||||||||||
+
+After 10 minutes:
+.||##.....
+||###.....
+||##......
+|##.....##
+|##.....##
+|##....##|
+||##.####|
+||#####|||
+||||#|||||
+||||||||||
+</code></pre>
+<p>After 10 minutes, there are <code>37</code> wooded acres and <code>31</code> lumberyards.  Multiplying the number of wooded acres by the number of lumberyards gives the total <em>resource value</em> after ten minutes: <code>37 * 31 = <em>1147</em></code>.</p>
+<p><em>What will the total resource value of the lumber collection area be after 10 minutes?</em></p>
+</article>
+<p>Your puzzle answer was <code>384480</code>.</p><article class="day-desc"><h2 id="part2">--- Part Two ---</h2><p>This important natural resource will need to last for at least thousands of years.  Are the Elves collecting this lumber sustainably?</p>
+<p><em>What will the total resource value of the lumber collection area be after 1000000000 minutes?</em></p>
+</article>
+<p>Your puzzle answer was <code>177004</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="/2018">return to your advent calendar</a> and try another puzzle.</p>
+<p>If you still want to see it, you can <a href="18/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+%22Settlers+of+The+North+Pole%22+%2D+Day+18+%2D+Advent+of+Code+2018&amp;url=https%3A%2F%2Fadventofcode%2Ecom%2F2018%2Fday%2F18&amp;related=ericwastl&amp;hashtags=AdventOfCode" target="_blank">Twitter</a>
+  <a href="http://www.reddit.com/submit?url=https%3A%2F%2Fadventofcode%2Ecom%2F2018%2Fday%2F18&amp;title=I%27ve+completed+%22Settlers+of+The+North+Pole%22+%2D+Day+18+%2D+Advent+of+Code+2018" target="_blank">Reddit</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('send', 'pageview');
+</script>
+<!-- /ga -->
+</body>
+</html>
\ No newline at end of file
diff --git a/src/advent18/advent18.hs b/src/advent18/advent18.hs
new file mode 100644 (file)
index 0000000..39da4c5
--- /dev/null
@@ -0,0 +1,139 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+-- import Debug.Trace
+
+import Data.Text (Text)
+-- import qualified Data.Text as T
+import qualified Data.Text.IO as TIO
+
+import Data.Void (Void)
+
+import Text.Megaparsec
+import Text.Megaparsec.Char
+import qualified Text.Megaparsec.Char.Lexer as L
+import qualified Control.Applicative as CA
+
+import Data.List
+-- import qualified Data.Set as S
+
+import qualified Data.Map.Strict as M
+import Data.Map.Strict ((!))
+-- import Data.Tuple (swap)
+
+type Coord = (Int, Int) -- row, col
+data Cell = Open | Trees | Lumberyard deriving (Eq, Enum, Bounded, Ord)
+type World = M.Map Coord Cell
+type Cache = M.Map World Int
+
+instance Show Cell where
+    show Open = "."
+    show Trees = "|"
+    show Lumberyard = "#"
+
+main :: IO ()
+main = do 
+    text <- TIO.readFile "data/advent18.txt"
+    let worldSpec = successfulParse text
+    let world = makeWorld worldSpec
+    -- print $ neighbours (1, 1) world
+    -- putStrLn $ showWorld world
+    -- putStrLn $ showWorld $ generation world
+    -- putStrLn $ showWorld $ (iterate generation world)!!10
+    print $ part1 world
+    print $ part2 world
+
+part1 :: World -> Int
+part1 world = score ((iterate generation world)!!10)
+
+part2 :: World -> Int
+part2 world = score usedWorld
+    where (worlds, repeated) = cacheWorlds world
+          lastMinute = M.size worlds
+          prevMinute = worlds!repeated
+          final = 1000000000
+          cycleLength = lastMinute - prevMinute
+          nCycles = (final - lastMinute) `div` cycleLength
+          usedIteration = final - (lastMinute + nCycles * cycleLength) + prevMinute
+          usedWorld = head $ M.keys $ M.filter (== usedIteration) worlds
+
+
+score :: World -> Int
+score world = nTrees * nLumber
+    where nTrees = M.size $ M.filter (== Trees) world
+          nLumber = M.size $ M.filter (== Lumberyard) world
+
+makeWorld :: [[Cell]] -> World
+makeWorld rows = M.unions $ [makeWorldRow r row | (r, row) <- zip [1..] rows]
+
+makeWorldRow :: Int -> [Cell] -> World
+makeWorldRow r row = M.fromList [((r, c), cell) | (c, cell) <- zip [1..] row]
+
+neighbours :: Coord -> World -> World
+neighbours here world = M.filterWithKey isNeighbour world
+    where isNeighbour c _ = c `elem` neighbourCoords here
+
+neighbourCoords :: Coord -> [Coord]
+neighbourCoords (r, c) = [(r', c') | r' <- [(r - 1)..(r + 1)]
+                                   , c' <- [(c - 1)..(c + 1)]
+                                   , ((r' /= r) || (c' /= c))
+                                   ]
+
+showWorld world = unlines $ [showWorldRow r world | r <-[minR..maxR]]
+    where ((minR, _), _) = M.findMin world
+          ((maxR, _), _) = M.findMax world
+
+showWorldRow r world = concat [show (lookupCell (r, c) world) | c <- [minC..maxC]]
+    where ((_, minC), _) = M.findMin world
+          ((_, maxC), _) = M.findMax world
+
+
+lookupCell :: Coord -> World -> Cell
+lookupCell coord world = M.findWithDefault Open coord world
+
+generation :: World -> World
+generation world = M.mapWithKey generationCell world
+    where generationCell here _ = propogateCell here world
+
+propogateCell :: Coord -> World -> Cell
+propogateCell here world = propogateCell' (world!here)
+    where propogateCell' Open = if nTrees >= 3 then Trees else Open
+          propogateCell' Trees = if nLumber >= 3 then Lumberyard else Trees
+          propogateCell' Lumberyard = if (nLumber >= 1) && (nTrees >= 1) then Lumberyard else Open
+          ns = neighbours here world
+          nTrees = M.size $ M.filter (== Trees) ns
+          nLumber = M.size $ M.filter (== Lumberyard) ns
+
+cacheWorlds :: World -> (Cache, World)
+cacheWorlds world = go (M.empty, world, 0) (drop 1 $ iterate generation world)
+    where go (cache, prev, minute) [] = (cache, prev)
+          go (cache, prev, minute) (w:ws) = 
+            if w `M.member` cache
+            then (cache', w)
+            else go (cache', w, minute + 1) ws
+            where cache' = M.insert prev minute cache
+
+-- Parse the input file
+
+type Parser = Parsec Void Text
+
+sc :: Parser ()
+sc = L.space (skipSome (char ' ')) CA.empty CA.empty
+
+-- lexeme  = L.lexeme sc
+-- integer = lexeme L.decimal
+symb = L.symbol sc
+
+openP = (symb "." *> pure Open)
+treesP = (symb "|" *> pure Trees)
+lumberP = (symb "#" *> pure Lumberyard)
+cellP = openP <|> treesP <|> lumberP
+
+fileP =  rowP `sepEndBy` (char '\n')
+
+rowP = many cellP
+
+successfulParse :: Text -> [[Cell]]
+successfulParse input = 
+        case parse fileP "input" input of
+                Left  _error -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
+                Right world -> world
\ No newline at end of file