Done day 15
authorNeil Smith <NeilNjae@users.noreply.github.com>
Thu, 15 Dec 2022 19:11:33 +0000 (19:11 +0000)
committerNeil Smith <NeilNjae@users.noreply.github.com>
Thu, 15 Dec 2022 19:11:33 +0000 (19:11 +0000)
advent-of-code22.cabal
advent15/Main.hs [new file with mode: 0644]
data/advent15.txt [new file with mode: 0644]
data/advent15a.txt [new file with mode: 0644]
problems/day15.html [new file with mode: 0644]

index 6859245ad6977d1e2193bdeca547025c513cc7e1..94190f578d9549a60299dd9733c9c58cafe504ec 100644 (file)
@@ -170,3 +170,8 @@ executable advent14
   import: common-extensions, build-directives
   main-is: advent14/Main.hs
   build-depends: text, attoparsec, containers, linear, lens
+
+executable advent15
+  import: common-extensions, build-directives
+  main-is: advent15/Main.hs
+  build-depends: text, attoparsec, containers, linear, lens
diff --git a/advent15/Main.hs b/advent15/Main.hs
new file mode 100644 (file)
index 0000000..9ed5d86
--- /dev/null
@@ -0,0 +1,93 @@
+-- Writeup at https://work.njae.me.uk/2022/12/15/advent-of-code-2022-day-15/
+
+import AoC
+import Data.Text (Text)
+import qualified Data.Text.IO as TIO
+import Data.Attoparsec.Text hiding (take, D)
+import Control.Applicative
+import Data.List
+import Data.Ix
+import qualified Data.Set as S
+import Linear hiding (Trace, trace, distance)
+import Control.Lens
+
+type Position = V2 Int
+
+data Sensor = Sensor Position Position -- sensor position, beacon position
+  deriving (Eq, Show)
+
+newtype Region = Region { getRegion :: Position -> Bool }  
+
+instance Semigroup Region where
+  r1 <> r2 = Region (\p -> getRegion r1 p || getRegion r2 p)
+
+instance Monoid Region where
+  mempty = Region (\p -> False)
+
+main :: IO ()
+main = 
+  do  dataFileName <- getDataFileName
+      text <- TIO.readFile dataFileName
+      let sensors = successfulParse text
+      -- print sensors
+      print $ part1 sensors
+      print $ part2 sensors
+
+thisY :: Int
+-- thisY = 10
+thisY = 2000000
+
+searchRange :: (Position, Position)
+-- searchRange = ((V2 0 0), (V2 20 20))
+searchRange = ((V2 0 0), (V2 4000000 4000000))
+
+part1, part2 :: [Sensor] -> Int
+part1 sensors = length $ filter (\p -> p `notElem` occupied) $ filter (getRegion coverage) rowCoords
+  where coverage = mconcat $ fmap nearby sensors
+        rowCoords = range ((V2 (globalMinX sensors) thisY), (V2 (globalMaxX sensors) thisY))
+        occupied = concatMap (\(Sensor s b) -> [s, b]) sensors
+
+part2 sensors = x * 4000000 + y
+  where coverage = mconcat $ fmap nearby sensors
+        boundaries = S.filter (inRange searchRange) $ S.unions $ fmap justOutside sensors
+        V2 x y = S.findMin $ S.filter (\p -> not $ getRegion coverage p) boundaries
+
+manhattan :: Position -> Position -> Int
+manhattan p1 p2 = (abs dx) + (abs dy)
+  where V2 dx dy = p1 ^-^ p2
+
+nearby :: Sensor -> Region
+nearby (Sensor s b) = Region (\p -> manhattan s p <= dist)
+  where dist = manhattan s b
+
+minX, maxX :: Sensor -> Int
+minX (Sensor s@(V2 sx _) b) = sx - (manhattan s b)
+maxX (Sensor s@(V2 sx _) b) = sx + (manhattan s b)
+
+globalMinX, globalMaxX :: [Sensor] -> Int
+globalMinX = minimum . fmap minX
+globalMaxX = maximum . fmap maxX
+
+justOutside :: Sensor -> S.Set Position
+justOutside (Sensor s@(V2 sx sy) b) = S.fromList (topLeft ++ topRight ++ bottomLeft ++ bottomRight)
+  where d = 1 + manhattan s b
+        topLeft = [V2 x y | (x, y) <- zip [(sx - d)..sx] [sy..(sy + d)] ]
+        topRight = [V2 x y | (x, y) <- zip [(sx + d), (sx + d - 1)..sx] [sy..(sy + d)] ]
+        bottomLeft = [V2 x y | (x, y) <- zip [(sx - d)..sx] [sy, (sy - 1)..(sy - d)] ]
+        bottomRight = [V2 x y | (x, y) <- zip [(sx + d), (sx + d - 1)..sx] [sy, (sy - 1)..(sy - d)] ]
+
+-- Parse the input file
+
+sensorsP :: Parser [Sensor]
+sensorP :: Parser Sensor
+positionP :: Parser Position
+
+sensorsP = sensorP `sepBy` endOfLine
+sensorP = Sensor <$> ("Sensor at " *> positionP) <*> (": closest beacon is at " *> positionP)
+positionP = V2 <$> (("x=" *> signed decimal) <* ", ") <*> ("y=" *> signed decimal)
+
+successfulParse :: Text -> [Sensor]
+successfulParse input = 
+  case parseOnly sensorsP input of
+    Left  _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
+    Right sensors -> sensors
diff --git a/data/advent15.txt b/data/advent15.txt
new file mode 100644 (file)
index 0000000..dc19ef2
--- /dev/null
@@ -0,0 +1,27 @@
+Sensor at x=1326566, y=3575946: closest beacon is at x=1374835, y=2000000
+Sensor at x=2681168, y=3951549: closest beacon is at x=3184941, y=3924923
+Sensor at x=3959984, y=1095746: closest beacon is at x=3621412, y=2239432
+Sensor at x=3150886, y=2479946: closest beacon is at x=3621412, y=2239432
+Sensor at x=3983027, y=2972336: closest beacon is at x=4012908, y=3083616
+Sensor at x=3371601, y=3853300: closest beacon is at x=3184941, y=3924923
+Sensor at x=3174612, y=3992719: closest beacon is at x=3184941, y=3924923
+Sensor at x=3316368, y=1503688: closest beacon is at x=3621412, y=2239432
+Sensor at x=3818181, y=2331216: closest beacon is at x=3621412, y=2239432
+Sensor at x=3960526, y=3229321: closest beacon is at x=4012908, y=3083616
+Sensor at x=61030, y=3045273: closest beacon is at x=-467419, y=2369316
+Sensor at x=3635583, y=3121524: closest beacon is at x=4012908, y=3083616
+Sensor at x=2813357, y=5535: closest beacon is at x=3595763, y=-77322
+Sensor at x=382745, y=1566522: closest beacon is at x=1374835, y=2000000
+Sensor at x=3585664, y=538632: closest beacon is at x=3595763, y=-77322
+Sensor at x=3979654, y=2158646: closest beacon is at x=3621412, y=2239432
+Sensor at x=3996588, y=2833167: closest beacon is at x=4012908, y=3083616
+Sensor at x=3249383, y=141800: closest beacon is at x=3595763, y=-77322
+Sensor at x=3847114, y=225529: closest beacon is at x=3595763, y=-77322
+Sensor at x=3668737, y=3720078: closest beacon is at x=3184941, y=3924923
+Sensor at x=1761961, y=680560: closest beacon is at x=1374835, y=2000000
+Sensor at x=2556636, y=2213691: closest beacon is at x=3621412, y=2239432
+Sensor at x=65365, y=215977: closest beacon is at x=346716, y=-573228
+Sensor at x=709928, y=2270200: closest beacon is at x=1374835, y=2000000
+Sensor at x=3673956, y=2670437: closest beacon is at x=4029651, y=2547743
+Sensor at x=3250958, y=3999227: closest beacon is at x=3184941, y=3924923
+Sensor at x=3009537, y=3292368: closest beacon is at x=3184941, y=3924923
\ No newline at end of file
diff --git a/data/advent15a.txt b/data/advent15a.txt
new file mode 100644 (file)
index 0000000..652e631
--- /dev/null
@@ -0,0 +1,14 @@
+Sensor at x=2, y=18: closest beacon is at x=-2, y=15
+Sensor at x=9, y=16: closest beacon is at x=10, y=16
+Sensor at x=13, y=2: closest beacon is at x=15, y=3
+Sensor at x=12, y=14: closest beacon is at x=10, y=16
+Sensor at x=10, y=20: closest beacon is at x=10, y=16
+Sensor at x=14, y=17: closest beacon is at x=10, y=16
+Sensor at x=8, y=7: closest beacon is at x=2, y=10
+Sensor at x=2, y=0: closest beacon is at x=2, y=10
+Sensor at x=0, y=11: closest beacon is at x=2, y=10
+Sensor at x=20, y=14: closest beacon is at x=25, y=17
+Sensor at x=17, y=20: closest beacon is at x=21, y=22
+Sensor at x=16, y=7: closest beacon is at x=15, y=3
+Sensor at x=14, y=3: closest beacon is at x=15, y=3
+Sensor at x=20, y=1: closest beacon is at x=15, y=3
\ No newline at end of file
diff --git a/problems/day15.html b/problems/day15.html
new file mode 100644 (file)
index 0000000..c0ac144
--- /dev/null
@@ -0,0 +1,212 @@
+<!DOCTYPE html>
+<html lang="en-us">
+<head>
+<meta charset="utf-8"/>
+<title>Day 15 - Advent of Code 2022</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?30"/>
+<link rel="stylesheet alternate" type="text/css" href="/static/highcontrast.css?0" title="High Contrast"/>
+<link rel="shortcut icon" href="/favicon.png"/>
+<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>
+</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="/2022/about">[About]</a></li><li><a href="/2022/events">[Events]</a></li><li><a href="https://teespring.com/stores/advent-of-code" target="_blank">[Shop]</a></li><li><a href="/2022/settings">[Settings]</a></li><li><a href="/2022/auth/logout">[Log Out]</a></li></ul></nav><div class="user">Neil Smith <a href="/2022/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">int y=</span><a href="/2022">2022</a><span class="title-event-wrap">;</span></h1><nav><ul><li><a href="/2022">[Calendar]</a></li><li><a href="/2022/support">[AoC++]</a></li><li><a href="/2022/sponsors">[Sponsors]</a></li><li><a href="/2022/leaderboard">[Leaderboard]</a></li><li><a href="/2022/stats">[Stats]</a></li></ul></nav></div></header>
+
+<div id="sidebar">
+<div id="sponsor"><div class="quiet">Our <a href="/2022/sponsors">sponsors</a> help make Advent of Code possible:</div><div class="sponsor"><a href="https://kotlinlang.org/" target="_blank" onclick="if(ga)ga('send','event','sponsor','sidebar',this.href);" rel="noopener">Kotlin by JetBrains</a> - Trees, lists, packages - it&apos;s Advent of Code time! Get ready to solve puzzles in Kotlin. Watch us livestream our discussions about the solutions for the first few puzzles, join our leaderboard, win prizes. Happy holidays!</div></div>
+</div><!--/sidebar-->
+
+<main>
+<article class="day-desc"><h2>--- Day 15: Beacon Exclusion Zone ---</h2><p>You feel the ground rumble again as the distress signal leads you to a large network of subterranean tunnels. You don't have time to search them all, but you don't need to: your pack contains a set of deployable <em>sensors</em> that you imagine were originally built to locate lost Elves.</p>
+<p>The sensors aren't very powerful, but that's okay; your handheld device indicates that you're close enough to the source of the distress signal to use them. You pull the emergency sensor system out of your pack, hit the big button on top, and the sensors zoom off down the tunnels.</p>
+<p>Once a sensor finds a spot it thinks will give it a good reading, it attaches itself to a hard surface and begins monitoring for the nearest signal source <em>beacon</em>. Sensors and beacons always exist at integer coordinates. Each sensor knows its own position and can <em>determine the position of a beacon precisely</em>; however, sensors can only lock on to the one beacon <em>closest to the sensor</em> as measured by the <a href="https://en.wikipedia.org/wiki/Taxicab_geometry" target="_blank">Manhattan distance</a>. (There is never a tie where two beacons are the same distance to a sensor.)</p>
+<p>It doesn't take long for the sensors to report back their positions and closest beacons (your puzzle input). For example:</p>
+<pre><code>Sensor at x=2, y=18: closest beacon is at x=-2, y=15
+Sensor at x=9, y=16: closest beacon is at x=10, y=16
+Sensor at x=13, y=2: closest beacon is at x=15, y=3
+Sensor at x=12, y=14: closest beacon is at x=10, y=16
+Sensor at x=10, y=20: closest beacon is at x=10, y=16
+Sensor at x=14, y=17: closest beacon is at x=10, y=16
+Sensor at x=8, y=7: closest beacon is at x=2, y=10
+Sensor at x=2, y=0: closest beacon is at x=2, y=10
+Sensor at x=0, y=11: closest beacon is at x=2, y=10
+Sensor at x=20, y=14: closest beacon is at x=25, y=17
+Sensor at x=17, y=20: closest beacon is at x=21, y=22
+Sensor at x=16, y=7: closest beacon is at x=15, y=3
+Sensor at x=14, y=3: closest beacon is at x=15, y=3
+Sensor at x=20, y=1: closest beacon is at x=15, y=3
+</code></pre>
+<p>So, consider the sensor at <code>2,18</code>; the closest beacon to it is at <code>-2,15</code>. For the sensor at <code>9,16</code>, the closest beacon to it is at <code>10,16</code>.</p>
+<p>Drawing sensors as <code>S</code> and beacons as <code>B</code>, the above arrangement of sensors and beacons looks like this:</p>
+<pre><code>               1    1    2    2
+     0    5    0    5    0    5
+ 0 ....S.......................
+ 1 ......................S.....
+ 2 ...............S............
+ 3 ................SB..........
+ 4 ............................
+ 5 ............................
+ 6 ............................
+ 7 ..........S.......S.........
+ 8 ............................
+ 9 ............................
+10 ....B.......................
+11 ..S.........................
+12 ............................
+13 ............................
+14 ..............S.......S.....
+15 B...........................
+16 ...........SB...............
+17 ................S..........B
+18 ....S.......................
+19 ............................
+20 ............S......S........
+21 ............................
+22 .......................B....
+</code></pre>
+<p>This isn't necessarily a comprehensive map of all beacons in the area, though. Because each sensor only identifies its closest beacon, if a sensor detects a beacon, you know there are no other beacons that close or closer to that sensor. There could still be beacons that just happen to not be the closest beacon to any sensor. Consider the sensor at <code>8,7</code>:</p>
+<pre><code>               1    1    2    2
+     0    5    0    5    0    5
+-2 ..........#.................
+-1 .........###................
+ 0 ....S...#####...............
+ 1 .......#######........S.....
+ 2 ......#########S............
+ 3 .....###########SB..........
+ 4 ....#############...........
+ 5 ...###############..........
+ 6 ..#################.........
+ 7 .#########<em>S</em>#######S#........
+ 8 ..#################.........
+ 9 ...###############..........
+10 ....<em>B</em>############...........
+11 ..S..###########............
+12 ......#########.............
+13 .......#######..............
+14 ........#####.S.......S.....
+15 B........###................
+16 ..........#SB...............
+17 ................S..........B
+18 ....S.......................
+19 ............................
+20 ............S......S........
+21 ............................
+22 .......................B....
+</code></pre>
+<p>This sensor's closest beacon is at <code>2,10</code>, and so you know there are no beacons that close or closer (in any positions marked <code>#</code>).</p>
+<p>None of the detected beacons seem to be producing the distress signal, so you'll need to <span title="&quot;When you have eliminated all which is impossible, then whatever remains, however improbable, must be where the missing beacon is.&quot; - Sherlock Holmes">work out</span> where the distress beacon is by working out where it <em>isn't</em>. For now, keep things simple by counting the positions where a beacon cannot possibly be along just a single row.</p>
+<p>So, suppose you have an arrangement of beacons and sensors like in the example above and, just in the row where <code>y=10</code>, you'd like to count the number of positions a beacon cannot possibly exist. The coverage from all sensors near that row looks like this:</p>
+<pre><code>                 1    1    2    2
+       0    5    0    5    0    5
+ 9 ...#########################...
+<em>10 ..####B######################..</em>
+11 .###S#############.###########.
+</code></pre>
+<p>In this example, in the row where <code>y=10</code>, there are <code><em>26</em></code> positions where a beacon cannot be present.</p>
+<p>Consult the report from the sensors you just deployed. <em>In the row where <code>y=2000000</code>, how many positions cannot contain a beacon?</em></p>
+</article>
+<p>Your puzzle answer was <code>5147333</code>.</p><article class="day-desc"><h2 id="part2">--- Part Two ---</h2><p>Your handheld device indicates that the distress signal is coming from a beacon nearby. The distress beacon is not detected by any sensor, but the distress beacon must have <code>x</code> and <code>y</code> coordinates each no lower than <code>0</code> and no larger than <code>4000000</code>.</p>
+<p>To isolate the distress beacon's signal, you need to determine its <em>tuning frequency</em>, which can be found by multiplying its <code>x</code> coordinate by <code>4000000</code> and then adding its <code>y</code> coordinate.</p>
+<p>In the example above, the search space is smaller: instead, the <code>x</code> and <code>y</code> coordinates can each be at most <code>20</code>. With this reduced search area, there is only a single position that could have a beacon: <code>x=14, y=11</code>. The tuning frequency for this distress beacon is <code><em>56000011</em></code>.</p>
+<p>Find the only possible position for the distress beacon. <em>What is its tuning frequency?</em></p>
+</article>
+<p>Your puzzle answer was <code>13734006908372</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="/2022">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+%22Beacon+Exclusion+Zone%22+%2D+Day+15+%2D+Advent+of+Code+2022&amp;url=https%3A%2F%2Fadventofcode%2Ecom%2F2022%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+%22Beacon+Exclusion+Zone%22+%2D+Day+15+%2D+Advent+of+Code+2022+%23AdventOfCode+https%3A%2F%2Fadventofcode%2Ecom%2F2022%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