Day13 with 2 solution2 one naive, the other one faster
This commit is contained in:
parent
5467cee5e8
commit
ffe0f250d6
6 changed files with 506 additions and 4 deletions
|
@ -2,7 +2,7 @@
|
|||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
--
|
||||
-- hash: 3f3cbe111f1f469760a75a30c1b54e4cf67cf5ca5a968c511d1e0fe4bac65a32
|
||||
-- hash: 454d3692225cbc9c59993888515dbf7f9c46a2d886ffebc91072c08157888c4d
|
||||
|
||||
name: adventofcode
|
||||
version: 0.1.0.0
|
||||
|
@ -42,6 +42,7 @@ library
|
|||
Day10
|
||||
Day11
|
||||
Day12
|
||||
Day13
|
||||
other-modules:
|
||||
Paths_adventofcode
|
||||
build-depends:
|
||||
|
@ -58,7 +59,7 @@ executable adventofcode-exe
|
|||
hs-source-dirs:
|
||||
app
|
||||
main-is: Main.hs
|
||||
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
||||
ghc-options: -O2 -threaded -rtsopts -with-rtsopts=-N
|
||||
build-depends:
|
||||
adventofcode
|
||||
, base
|
||||
|
@ -82,5 +83,5 @@ test-suite adventofcode-test
|
|||
, tasty-hunit
|
||||
other-modules:
|
||||
Paths_adventofcode
|
||||
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
||||
ghc-options: -O2 -threaded -rtsopts -with-rtsopts=-N
|
||||
default-language: Haskell2010
|
||||
|
|
12
app/Main.hs
12
app/Main.hs
|
@ -18,6 +18,7 @@ import qualified Day09
|
|||
import qualified Day10
|
||||
import qualified Day11
|
||||
import qualified Day12
|
||||
import qualified Day13
|
||||
|
||||
showSol :: [Char] -> Doc -> IO ()
|
||||
showSol txt d = putText . toS . render $
|
||||
|
@ -41,6 +42,7 @@ solutions = Map.fromList [(["01"], day01)
|
|||
,(["10"], day10)
|
||||
,(["11"], day11)
|
||||
,(["12"], day12)
|
||||
,(["13"], day13)
|
||||
]
|
||||
|
||||
day01 :: IO ()
|
||||
|
@ -125,3 +127,13 @@ day12 = do
|
|||
showSol "Solution 1" (int (fromMaybe 0 sol1))
|
||||
let sol2 = fmap Day12.solution2 input
|
||||
showSol "Solution 2" (int (fromMaybe 0 sol2))
|
||||
|
||||
day13 :: IO ()
|
||||
day13 = do
|
||||
putText "Day 13:"
|
||||
input <- Day13.input
|
||||
let sol1 = fmap Day13.solution1 input
|
||||
showSol "Solution 1" (int (fromMaybe 0 sol1))
|
||||
input2 <- Day13.parseInput
|
||||
let sol2 = fmap Day13.solution2 input2
|
||||
showSol "Solution 2" (int (fromMaybe 0 sol2))
|
||||
|
|
44
inputs/day13.txt
Normal file
44
inputs/day13.txt
Normal file
|
@ -0,0 +1,44 @@
|
|||
0: 3
|
||||
1: 2
|
||||
2: 4
|
||||
4: 4
|
||||
6: 5
|
||||
8: 6
|
||||
10: 8
|
||||
12: 8
|
||||
14: 6
|
||||
16: 6
|
||||
18: 8
|
||||
20: 8
|
||||
22: 6
|
||||
24: 12
|
||||
26: 9
|
||||
28: 12
|
||||
30: 8
|
||||
32: 14
|
||||
34: 12
|
||||
36: 8
|
||||
38: 14
|
||||
40: 12
|
||||
42: 12
|
||||
44: 12
|
||||
46: 14
|
||||
48: 12
|
||||
50: 14
|
||||
52: 12
|
||||
54: 10
|
||||
56: 14
|
||||
58: 12
|
||||
60: 14
|
||||
62: 14
|
||||
66: 10
|
||||
68: 14
|
||||
74: 14
|
||||
76: 12
|
||||
78: 14
|
||||
80: 20
|
||||
86: 18
|
||||
92: 14
|
||||
94: 20
|
||||
96: 18
|
||||
98: 17
|
|
@ -25,6 +25,7 @@ library:
|
|||
- Day10
|
||||
- Day11
|
||||
- Day12
|
||||
- Day13
|
||||
dependencies:
|
||||
- base >=4.7 && <5
|
||||
- protolude
|
||||
|
@ -38,6 +39,7 @@ executables:
|
|||
main: Main.hs
|
||||
source-dirs: app
|
||||
ghc-options:
|
||||
- -O2
|
||||
- -threaded
|
||||
- -rtsopts
|
||||
- -with-rtsopts=-N
|
||||
|
@ -52,6 +54,7 @@ tests:
|
|||
main: Spec.hs
|
||||
source-dirs: test
|
||||
ghc-options:
|
||||
- -O2
|
||||
- -threaded
|
||||
- -rtsopts
|
||||
- -with-rtsopts=-N
|
||||
|
|
429
src/Day13.hs
Normal file
429
src/Day13.hs
Normal file
|
@ -0,0 +1,429 @@
|
|||
{-# LANGUAGE NoImplicitPrelude #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-|
|
||||
description:
|
||||
|
||||
You need to cross a vast firewall. The firewall consists of several layers, each
|
||||
with a security scanner that moves back and forth across the layer. To succeed,
|
||||
you must not be detected by a scanner.
|
||||
|
||||
By studying the firewall briefly, you are able to record (in your puzzle input)
|
||||
the depth of each layer and the range of the scanning area for the scanner
|
||||
within it, written as depth: range. Each layer has a thickness of exactly 1. A
|
||||
layer at depth 0 begins immediately inside the firewall; a layer at depth 1
|
||||
would start immediately after that.
|
||||
|
||||
For example, suppose you've recorded the following:
|
||||
|
||||
0: 3
|
||||
1: 2
|
||||
4: 4
|
||||
6: 4
|
||||
|
||||
This means that there is a layer immediately inside the firewall (with range 3),
|
||||
a second layer immediately after that (with range 2), a third layer which begins
|
||||
at depth 4 (with range 4), and a fourth layer which begins at depth 6 (also with
|
||||
range 4). Visually, it might look like this:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [ ] ... ... [ ] ... [ ]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
Within each layer, a security scanner moves back and forth within its range.
|
||||
Each security scanner starts at the top and moves down until it reaches the
|
||||
bottom, then moves up until it reaches the top, and repeats. A security scanner
|
||||
takes one picosecond to move one step. Drawing scanners as S, the first few
|
||||
picoseconds look like this:
|
||||
|
||||
|
||||
Picosecond 0:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[S] [S] ... ... [S] ... [S]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
Picosecond 1:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [ ] ... ... [ ] ... [ ]
|
||||
[S] [S] [S] [S]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
Picosecond 2:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [S] ... ... [ ] ... [ ]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[S] [S] [S]
|
||||
[ ] [ ]
|
||||
|
||||
Picosecond 3:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [ ] ... ... [ ] ... [ ]
|
||||
[S] [S] [ ] [ ]
|
||||
[ ] [ ] [ ]
|
||||
[S] [S]
|
||||
|
||||
Your plan is to hitch a ride on a packet about to move through the firewall. The
|
||||
packet will travel along the top of each layer, and it moves at one layer per
|
||||
picosecond. Each picosecond, the packet moves one layer forward (its first move
|
||||
takes it into layer 0), and then the scanners move one step. If there is a
|
||||
scanner at the top of the layer as your packet enters it, you are caught. (If a
|
||||
scanner moves into the top of its layer while you are there, you are not caught:
|
||||
it doesn't have time to notice you before you leave.) If you were to do this in
|
||||
the configuration above, marking your current position with parentheses, your
|
||||
passage through the firewall would look like this:
|
||||
|
||||
Initial state:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[S] [S] ... ... [S] ... [S]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
Picosecond 0:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
(S) [S] ... ... [S] ... [S]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
( ) [ ] ... ... [ ] ... [ ]
|
||||
[S] [S] [S] [S]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
|
||||
Picosecond 1:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] ( ) ... ... [ ] ... [ ]
|
||||
[S] [S] [S] [S]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] (S) ... ... [ ] ... [ ]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[S] [S] [S]
|
||||
[ ] [ ]
|
||||
|
||||
|
||||
Picosecond 2:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [S] (.) ... [ ] ... [ ]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[S] [S] [S]
|
||||
[ ] [ ]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
|
||||
[ ] [ ] (.) ... [ ] ... [ ]
|
||||
[S] [S] [ ] [ ]
|
||||
[ ] [ ] [ ]
|
||||
[S] [S]
|
||||
|
||||
|
||||
Picosecond 3:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [ ] ... (.) [ ] ... [ ]
|
||||
[S] [S] [ ] [ ]
|
||||
[ ] [ ] [ ]
|
||||
[S] [S]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[S] [S] ... (.) [ ] ... [ ]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[ ] [S] [S]
|
||||
[ ] [ ]
|
||||
|
||||
|
||||
Picosecond 4:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[S] [S] ... ... ( ) ... [ ]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[ ] [S] [S]
|
||||
[ ] [ ]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [ ] ... ... ( ) ... [ ]
|
||||
[S] [S] [S] [S]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
|
||||
Picosecond 5:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [ ] ... ... [ ] (.) [ ]
|
||||
[S] [S] [S] [S]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [S] ... ... [S] (.) [S]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[S] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
|
||||
Picosecond 6:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [S] ... ... [S] ... (S)
|
||||
[ ] [ ] [ ] [ ]
|
||||
[S] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [ ] ... ... [ ] ... ( )
|
||||
[S] [S] [S] [S]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
In this situation, you are caught in layers 0 and 6, because your packet entered
|
||||
the layer when its scanner was at the top when you entered it. You are not
|
||||
caught in layer 1, since the scanner moved into the top of the layer once you
|
||||
were already there.
|
||||
|
||||
The severity of getting caught on a layer is equal to its depth multiplied by
|
||||
its range. (Ignore layers in which you do not get caught.) The severity of the
|
||||
whole trip is the sum of these values. In the example above, the trip severity
|
||||
is 0*3 + 6*4 = 24.
|
||||
|
||||
Given the details of the firewall you've recorded, if you leave immediately,
|
||||
what is the severity of your whole trip?
|
||||
|
||||
--- Part Two ---
|
||||
|
||||
Now, you need to pass through the firewall without being caught - easier said
|
||||
than done.
|
||||
|
||||
You can't control the speed of the packet, but you can delay it any number of
|
||||
picoseconds. For each picosecond you delay the packet before beginning your
|
||||
trip, all security scanners move one step. You're not in the firewall during
|
||||
this time; you don't enter layer 0 until you stop delaying the packet.
|
||||
|
||||
In the example above, if you delay 10 picoseconds (picoseconds 0 - 9), you won't
|
||||
get caught:
|
||||
|
||||
State after delaying:
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [S] ... ... [ ] ... [ ]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[S] [S] [S]
|
||||
[ ] [ ]
|
||||
|
||||
Picosecond 10:
|
||||
0 1 2 3 4 5 6
|
||||
( ) [S] ... ... [ ] ... [ ]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[S] [S] [S]
|
||||
[ ] [ ]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
( ) [ ] ... ... [ ] ... [ ]
|
||||
[S] [S] [S] [S]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
|
||||
Picosecond 11:
|
||||
0 1 2 3 4 5 6
|
||||
[ ] ( ) ... ... [ ] ... [ ]
|
||||
[S] [S] [S] [S]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[S] (S) ... ... [S] ... [S]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
|
||||
Picosecond 12:
|
||||
0 1 2 3 4 5 6
|
||||
[S] [S] (.) ... [S] ... [S]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [ ] (.) ... [ ] ... [ ]
|
||||
[S] [S] [S] [S]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
|
||||
Picosecond 13:
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [ ] ... (.) [ ] ... [ ]
|
||||
[S] [S] [S] [S]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [S] ... (.) [ ] ... [ ]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[S] [S] [S]
|
||||
[ ] [ ]
|
||||
|
||||
|
||||
Picosecond 14:
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [S] ... ... ( ) ... [ ]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[S] [S] [S]
|
||||
[ ] [ ]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [ ] ... ... ( ) ... [ ]
|
||||
[S] [S] [ ] [ ]
|
||||
[ ] [ ] [ ]
|
||||
[S] [S]
|
||||
|
||||
|
||||
Picosecond 15:
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [ ] ... ... [ ] (.) [ ]
|
||||
[S] [S] [ ] [ ]
|
||||
[ ] [ ] [ ]
|
||||
[S] [S]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[S] [S] ... ... [ ] (.) [ ]
|
||||
[ ] [ ] [ ] [ ]
|
||||
[ ] [S] [S]
|
||||
[ ] [ ]
|
||||
|
||||
|
||||
Picosecond 16:
|
||||
0 1 2 3 4 5 6
|
||||
[S] [S] ... ... [ ] ... ( )
|
||||
[ ] [ ] [ ] [ ]
|
||||
[ ] [S] [S]
|
||||
[ ] [ ]
|
||||
|
||||
0 1 2 3 4 5 6
|
||||
[ ] [ ] ... ... [ ] ... ( )
|
||||
[S] [S] [S] [S]
|
||||
[ ] [ ] [ ]
|
||||
[ ] [ ]
|
||||
|
||||
Because all smaller delays would get you caught, the fewest number of
|
||||
picoseconds you would need to delay to get through safely is 10.
|
||||
|
||||
What is the fewest number of picoseconds that you need to delay the packet to
|
||||
pass through the firewall without being caught?
|
||||
|
||||
|-}
|
||||
module Day13 where
|
||||
|
||||
import Protolude
|
||||
|
||||
import Data.Map.Strict (Map)
|
||||
import qualified Data.Map.Strict as Map
|
||||
import qualified Data.Text as T
|
||||
|
||||
testInput :: Text
|
||||
testInput = "0: 3\n\
|
||||
\1: 2\n\
|
||||
\4: 4\n\
|
||||
\6: 4\n"
|
||||
|
||||
parseInput :: IO (Maybe (Map Int Int))
|
||||
parseInput = parseTxt <$> readFile "inputs/day13.txt"
|
||||
|
||||
input :: IO (Maybe AppState)
|
||||
input = fmap mkAppState <$> parseInput
|
||||
|
||||
parseTxt :: Text -> Maybe (Map Int Int)
|
||||
parseTxt txt =
|
||||
txt & T.lines & map T.words & traverse parseLine & fmap Map.fromList
|
||||
where
|
||||
parseLine :: [Text] -> Maybe (Int,Int)
|
||||
parseLine (n:d:_) = (,) <$> txtToInt n <*> txtToInt d
|
||||
|
||||
txtToInt :: Text -> Maybe Int
|
||||
txtToInt = fmap fst . head . reads . toS
|
||||
|
||||
data AppState = AppState { position :: Int
|
||||
, severity :: Int
|
||||
, caughted :: Bool -- ^ Just for solution2 naive
|
||||
, fwState :: Map Int (Int,Int)
|
||||
, fwDepth :: Map Int Int
|
||||
} deriving (Show)
|
||||
|
||||
mkAppState :: Map Int Int -> AppState
|
||||
mkAppState depths = AppState 0 0 False (fmap (const (0,1)) depths) depths
|
||||
|
||||
oneStep :: AppState -> AppState
|
||||
oneStep st =
|
||||
let caught = (== Just 0) (fmap fst (Map.lookup (position st) (fwState st)))
|
||||
newpos = (+1) (position st)
|
||||
newfwst = Map.mapWithKey
|
||||
(\i (v,dir) -> let d = fromMaybe 0 (Map.lookup i (fwDepth st)) in
|
||||
if (v + dir) >= d || (v+dir) < 0
|
||||
then (v - dir,-dir)
|
||||
else (v+dir,dir))
|
||||
(fwState st)
|
||||
newseverity = if caught
|
||||
then severity st
|
||||
+ position st * fromMaybe 0 (Map.lookup (position st) (fwDepth st))
|
||||
else severity st
|
||||
in AppState newpos newseverity (caught || caughted st) newfwst (fwDepth st)
|
||||
|
||||
solution1Debug :: Maybe AppState -> IO Int
|
||||
solution1Debug (Just st) = do
|
||||
print st
|
||||
if position st > maximum (Map.keys (fwDepth st))
|
||||
then return $ severity st
|
||||
else solution1Debug (Just (oneStep st))
|
||||
|
||||
solution1 :: AppState -> Int
|
||||
solution1 st =
|
||||
if position st > maximum (Map.keys (fwDepth st))
|
||||
then severity st
|
||||
else solution1 (oneStep st)
|
||||
|
||||
|
||||
passes :: AppState -> Bool
|
||||
passes st =
|
||||
if caughted st || position st > maximum (Map.keys (fwDepth st))
|
||||
then not (caughted st)
|
||||
else passes (oneStep st)
|
||||
|
||||
-- | TOO LONG
|
||||
solution2naive :: AppState -> Int
|
||||
solution2naive st =
|
||||
if passes st
|
||||
then - (position st)
|
||||
else solution2naive (st { position = position st - 1 })
|
||||
|
||||
caughtToken :: Int -> Int -> Int -> Bool
|
||||
caughtToken delay x depth = (x + delay) `rem` (2*(depth - 1)) == 0
|
||||
|
||||
collisions :: Int -> Map Int Int -> [Bool]
|
||||
collisions delay depths = Map.mapWithKey (caughtToken delay) depths & Map.elems
|
||||
|
||||
solution2 :: Map Int Int -> Int
|
||||
solution2 depths = go depths 0
|
||||
where
|
||||
go depths delay =
|
||||
if not (or (collisions delay depths))
|
||||
then delay
|
||||
else go depths (delay + 1)
|
15
test/Spec.hs
15
test/Spec.hs
|
@ -13,6 +13,7 @@ import qualified Day08
|
|||
import qualified Day10
|
||||
import qualified Day11
|
||||
import qualified Day12
|
||||
import qualified Day13
|
||||
|
||||
main :: IO ()
|
||||
main = defaultMain $
|
||||
|
@ -116,4 +117,16 @@ main = defaultMain $
|
|||
fmap Day12.solution2 (Day12.parseTxt Day12.testTxt) @?= Just 2
|
||||
]
|
||||
]
|
||||
]
|
||||
, testGroup "Day 13"
|
||||
[ testGroup "Solution 1"
|
||||
[ testCase "Example" $
|
||||
fmap Day13.solution1
|
||||
(fmap Day13.mkAppState
|
||||
(Day13.parseTxt Day13.testInput)) @?= Just 24
|
||||
]
|
||||
, testGroup "Solution 2"
|
||||
[ testCase "Example" $
|
||||
fmap Day13.solution2 (Day13.parseTxt Day13.testInput) @?= Just 10
|
||||
]
|
||||
]
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue