Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AdventOfCode2020.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ library
Day12.Solution
Day13.Solution
Day14.Solution
Day15.Solution
Day16.Parser
Day16.Solution
Day16.Utils
Expand Down Expand Up @@ -93,6 +94,7 @@ test-suite AdventOfCode2020-test
Day12.SolutionSpec
Day13.SolutionSpec
Day14.SolutionSpec
Day15.SolutionSpec
Day16.SolutionSpec
Day16.UtilsSpec
Day17.SolutionSpec
Expand Down
60 changes: 60 additions & 0 deletions src/Day15/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
## Day 15: Rambunctious Recitation

You catch the airport shuttle and try to book a new flight to your vacation island. Due to the storm, all direct flights have been cancelled, but a route is available to get around the storm. You take it.

While you wait for your flight, you decide to check in with the Elves back at the North Pole. They're playing a _memory game_ and are ever so excited to explain the rules!

In this game, the players take turns saying _numbers_ . They begin by taking turns reading from a list of _starting numbers_ (your puzzle input). Then, each turn consists of considering the _most recently spoken number_ :

- If that was the _first_ time the number has been spoken, the current player says _`0`_ .
- Otherwise, the number had been spoken before; the current player announces _how many turns apart_ the number is from when it was previously spoken.

So, after the starting numbers, each turn results in that player speaking aloud either _`0`_ (if the last number is new) or an _age_ (if the last number is a repeat).

For example, suppose the starting numbers are `0,3,6` :

- _Turn 1_ : The `1` st number spoken is a starting number, _`0`_ .
- _Turn 2_ : The `2` nd number spoken is a starting number, _`3`_ .
- _Turn 3_ : The `3` rd number spoken is a starting number, _`6`_ .
- _Turn 4_ : Now, consider the last number spoken, `6` . Since that was the first time the number had been spoken, the `4` th number spoken is _`0`_ .
- _Turn 5_ : Next, again consider the last number spoken, `0` . Since it _had_ been spoken before, the next number to speak is the difference between the turn number when it was last spoken (the previous turn, `4` ) and the turn number of the time it was most recently spoken before then (turn `1` ). Thus, the `5` th number spoken is `4 - 1` , _`3`_ .
- _Turn 6_ : The last number spoken, `3` had also been spoken before, most recently on turns `5` and `2` . So, the `6` th number spoken is `5 - 2` , _`3`_ .
- _Turn 7_ : Since `3` was just spoken twice in a row, and the last two turns are `1` turn apart, the `7` th number spoken is _`1`_ .
- _Turn 8_ : Since `1` is new, the `8` th number spoken is _`0`_ .
- _Turn 9_ : `0` was last spoken on turns `8` and `4` , so the `9` th number spoken is the difference between them, _`4`_ .
- _Turn 10_ : `4` is new, so the `10` th number spoken is _`0`_ .

(The game ends when the Elves get sick of playing or dinner is ready, whichever comes first.)

Their question for you is: what will be the _`2020` th_ number spoken? In the example above, the `2020` th number spoken will be `436` .

Here are a few more examples:

- Given the starting numbers `1,3,2` , the `2020` th number spoken is `1` .
- Given the starting numbers `2,1,3` , the `2020` th number spoken is `10` .
- Given the starting numbers `1,2,3` , the `2020` th number spoken is `27` .
- Given the starting numbers `2,3,1` , the `2020` th number spoken is `78` .
- Given the starting numbers `3,2,1` , the `2020` th number spoken is `438` .
- Given the starting numbers `3,1,2` , the `2020` th number spoken is `1836` .

Given your starting numbers, _what will be the `2020` th number spoken?_

## Part Two

Impressed, the Elves issue you a challenge: determine the `30000000` th number spoken. For example, given the same starting numbers as above:

- Given `0,3,6` , the `30000000` th number spoken is `175594` .
- Given `1,3,2` , the `30000000` th number spoken is `2578` .
- Given `2,1,3` , the `30000000` th number spoken is `3544142` .
- Given `1,2,3` , the `30000000` th number spoken is `261214` .
- Given `2,3,1` , the `30000000` th number spoken is `6895259` .
- Given `3,2,1` , the `30000000` th number spoken is `18` .
- Given `3,1,2` , the `30000000` th number spoken is `362` .

Given your starting numbers, _what will be the `30000000` th number spoken?_

## Link

[https://adventofcode.com/2020/day/15][1]

[1]: https://adventofcode.com/2020/day/15
43 changes: 43 additions & 0 deletions src/Day15/Solution.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module Day15.Solution (memoryGame, part1, part2) where

import Advent.Utils (fromRightOrShowError, readInt)
import Control.Monad (liftM3)
import qualified Data.IntMap.Lazy as IntMap
import Text.Parsec
( ParseError,
Parsec,
char,
digit,
endOfLine,
many1,
parse,
sepBy,
)

part1 :: String -> String
part1 = show . memoryGame 2020 . fromRightOrShowError . parseInts

part2 :: String -> String
part2 = show . memoryGame 30000000 . fromRightOrShowError . parseInts

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: This runs too slowly to check.


parseInts :: String -> Either ParseError [Int]
parseInts = parse (intsParser <* endOfLine) ""
where
intsParser :: Parsec String () [Int]
intsParser = (readInt <$> many1 digit) `sepBy` char ','

type State = IntMap.IntMap Int

memoryGame :: Int -> [Int] -> Int
memoryGame target = liftM3 go length last initialState
where
go :: Int -> Int -> State -> Int
go turn prev state
| turn == target = prev
| otherwise = go (succ turn) next nextState
where
next = (maybe 0 (turn -) . IntMap.lookup prev) state
nextState = IntMap.insert prev turn state

initialState :: [Int] -> State
initialState = IntMap.fromList . flip zip [1 ..] . init
73 changes: 73 additions & 0 deletions test/Day15/SolutionSpec.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
module Day15.SolutionSpec (spec) where

import Data.Foldable (for_)
import Day15.Solution (memoryGame, part1, part2)
import Test.Hspec

spec :: Spec
spec = parallel $ do
it "solves Part 1" $ do
input <- readFile "./test/Day15/input.txt"
part1 input `shouldBe` "694"
-- TODO: this runs in ~40 seconds with production optimizations
xit "solves Part 2" $ do
input <- readFile "./test/Day15/input.txt"
part2 input `shouldBe` "21768614"
describe "memoryGame" $ do
context "when looking at the 2020th result" $ do
let n = 2020
let cases =
[ ([0, 3, 6], 436),
([1, 3, 2], 1),
([2, 1, 3], 10),
([1, 2, 3], 27),
([2, 3, 1], 78),
([3, 2, 1], 438),
([3, 1, 2], 1836)
]
let test (input, expected) = it ("is " ++ show expected ++ " for input " ++ show input) $ do
memoryGame n input `shouldBe` expected

for_ cases test

-- TODO: figure out how to make this not take 10 minutes
xcontext "when looking at the 30000000th result" $ do
let n = 30000000
let cases =
[ ([0, 3, 6], 175594),
([1, 3, 2], 2578),
([2, 1, 3], 3544142),
([1, 2, 3], 261214),
([2, 3, 1], 6895259),
([3, 2, 1], 18),
([3, 1, 2], 362)
]
let test (input, expected) = it ("is " ++ show expected ++ " for input " ++ show input) $ do
memoryGame n input `shouldBe` expected

for_ cases test

-- 300
-- Finished in 0.0017 seconds
-- Finished in 0.0011 seconds

-- 3000
-- Finished in 0.0061 seconds
-- Finished in 0.0052 seconds

-- 30000
-- Finished in 0.1399 seconds
-- Finished in 0.1418 seconds

-- 300000
-- Finished in 1.7987 seconds
-- Finished in 1.8572 seconds
-- Finished in 1.7550 seconds

-- 3000000
-- Finished in 30.7137 seconds
-- Finished in 26.4237 seconds
-- Finished in 25.2911 seconds

-- 30000000
-- TODO: Need to get here!
1 change: 1 addition & 0 deletions test/Day15/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
17,1,3,16,19,0