Skip to content

Commit

Permalink
docs(2015.25-haskell): write some prose
Browse files Browse the repository at this point in the history
  • Loading branch information
yurrriq committed May 18, 2024
1 parent 57badd1 commit 4085820
Show file tree
Hide file tree
Showing 13 changed files with 547 additions and 8 deletions.
11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ $(wildcard _src/gap/$(1)/$(2).nw) \
$(wildcard _src/haskell/$(1)/$(2).nw)

years := \
2018 2019 2021
2015 2018 2019 2021

days := \
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Expand All @@ -25,6 +25,7 @@ NW_SRCS := $(foreach year,${years},$(call year_srcs,${year}))
GAP_SRCS := $(patsubst _src/gap/%.nw,src/Day%.g,$(filter _src/gap/%.nw,${NW_SRCS}))

HS_SRCS := \
$(patsubst _src/2015/haskell/%.nw,src/AdventOfCode/Year2015/Day%.hs,$(filter _src/2015/haskell/%.nw,${NW_SRCS})) \
$(patsubst _src/2018/haskell/%.nw,src/AdventOfCode/Year2018/Day%.hs,$(filter _src/2018/haskell/%.nw,${NW_SRCS})) \
$(patsubst _src/2019/haskell/%.nw,src/AdventOfCode/Year2019/Day%.hs,$(filter _src/2019/haskell/%.nw,${NW_SRCS})) \
$(patsubst _src/2021/haskell/%.nw,src/AdventOfCode/Year2021/Day%.hs,$(filter _src/2021/haskell/%.nw,${NW_SRCS}))
Expand Down Expand Up @@ -52,6 +53,9 @@ build: flake.nix $(wildcard src/**.hs) $(wildcard app/**.hs)

escape_underscores := 'sed "/^@use /s/_/\\\\_/g;/^@defn /s/_/\\\\_/g"'

_src/tex/2015.tex: $(call year_srcs,2015)
noweave -filter ${escape_underscores} -n -index $^ ${cpif} $@

_src/tex/2018.tex: $(call year_srcs,2018)
noweave -filter ${escape_underscores} -n -index $^ ${cpif} $@

Expand All @@ -76,6 +80,11 @@ src/Day%.g: _src/gap/%.nw
notangle -R'$(@F)' $< ${cpif} $@


src/AdventOfCode/Year2015/Day%.hs: _src/2015/haskell/%.nw
@ mkdir -p $(@D)
notangle -R'$(@F)' $< ${cpif} $@


src/AdventOfCode/Year2018/Day%.hs: _src/2018/haskell/%.nw
@ mkdir -p $(@D)
notangle -R'$(@F)' $< ${cpif} $@
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2023.6.2.12
2023.6.2.13
97 changes: 97 additions & 0 deletions _src/2015/day/25.nw
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
\newpage
\section{Day 25: Let It Snow}
\marginnote{\url{https://adventofcode.com/2015/day/25}}

Merry Christmas! Santa is booting up his weather machine; looks like you might
get a \href{https://adventofcode.com/2015/day/1}{white Christmas} after all.

The weather machine beeps! On the console of the machine is a copy protection
message asking you to
\href{https://en.wikipedia.org/wiki/Copy_protection\#Early_video_games}{enter a
code from the instruction manual}. Apparently, it refuses to run unless you
give it that code. No problem; you'll just look up the code in the--

``Ho ho ho'', Santa ponders aloud. ``I can't seem to find the manual.''

You look up the support number for the manufacturer and give them a call. Good
thing, too - that 49th star wasn't going to earn itself.

``Oh, that machine is quite old!'', they tell you. ``That model went out of
support six minutes ago, and we just finished shredding all of the manuals. I
bet we can find you the code generation algorithm, though.''

After putting you on hold for twenty minutes (your call is \textbf{very}
important to them, it reminded you repeatedly), they finally find an engineer
that remembers how the code system works.

The codes are printed on an infinite sheet of paper\marginnote{The paper is very
thin so it can be folded up neatly into the manual.}, starting in the top-left
corner. The codes are filled in by diagonals: starting with the first row with
an empty first box, the codes are filled in diagonally up and to the right. This
process repeats until the
\href{https://en.wikipedia.org/wiki/Cantor's_diagonal_argument}{infinite paper
is covered}. So, the first few codes are filled in in this order:

\begin{table}[h]
\centering
\begin{tabular}{r|rrrrrr}
& 1 & 2 & 3 & 4 & 5 & 6 \\
\midrule
1 & 1 & 3 & 6 & 10 & 15 & 21 \\
2 & 2 & 5 & 9 & 14 & 20 \\
3 & 4 & 8 & 13 & 19 \\
4 & 7 & 12 & 18 \\
5 & 11 & 17 \\
6 & 16
\end{tabular}
\end{table}

For example, the 12th code would be written to row \hs{4}, column \hs{2}; the
15th code would be written to row \hs{1}, column \hs{5}.

The voice on the other end of the phone continues with how the codes are
actually generated. The first code is \hs{20151125}. After that, each code is
generated by taking the previous one, multiplying it by \hs{252533}, and then
keeping the remainder from dividing that value by \hs{33554393}.

So, to find the second code (which ends up in row \hs{2}, column \hs{1}), start
with the previous value, \hs{20151125}. Multiply it by \hs{252533} to get
\hs{5088824049625}. Then, divide that by \hs{33554393}, which leaves a remainder
of \hs{31916031}. That remainder is the second code.

``Oh!'', says the voice. ``It looks like we missed a scrap from one of the
manuals. Let me read it to you.'' You write down his numbers:

\begin{table}[h]
\centering
\begin{tabular}{r|rrrrrr}
& 1 & 2 & 3 & 4 & 5 & 6 \\
\midrule
1 & 20151125 & 18749137 & 17289845 & 30943339 & 10071777 & 33511524 \\
2 & 31916031 & 21629792 & 16929656 & 7726640 & 15514188 & 4041754 \\
3 &16080970 & 8057251 & 1601130 & 7981243 & 11661866 & 16474243 \\
4 & 24592653 & 32451966 & 21345942 & 9380097 & 10600672 & 31527494 \\
5 & 77061 & 17552253 & 28094349 & 6899651 & 9250759 & 31663883 \\
6 & 33071741 & 6796745 & 25397450 & 24659492 & 1534922 & 27995004
\end{tabular}
\end{table}

\newpage

``Now remember'', the voice continues, ``that's not even all of the first few
numbers; for example, you're missing the one at 7,1 that would come before
6,2. But, it should be enough to let your-- oh, it's time for lunch! Bye!'' The
call disconnects.

Santa looks nervous. Your puzzle input contains the message on the machine's
console. \textbf{What code do you give the machine?}

\newthought{Part Two}

The machine springs to life, then falls silent again. It beeps. ``Insufficient
fuel'', the console reads. ``\textbf{Fifty stars} are required before
proceeding. \textbf{One star} is available.''

...``one star is available''? You check the fuel tank; sure enough, a lone star
sits at the bottom, awaiting its friends. Looks like you need to provide 49
yourself.
155 changes: 155 additions & 0 deletions _src/2015/haskell/25.nw
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
\subsection{Haskell solution}

Rather than try to generate the codes sequentially, which can quickly result in
a stack overflow, make some observations.

\newthought{The positions in first column} are the
\hrefootnote{https://en.wikipedia.org/wiki/Lazy_caterer's_sequence}{lazy
caterer's sequence}, i.e. \hrefootnote{https://oeis.org/A000124}{A000124 in
The Online Encyclopedia of Integer Sequences}.

\begin{equation}
a(n) = n \times (n - 1) / 2 + 1
\end{equation}

Or equivalently in Haskell:

<<Define a000124>>=
a000124 :: (Integral a) => a -> a
a000124 n = n * (n - 1) `div` 2 + 1
@ %def a000124

Since this problem might involve some rather large numbers, specialize the
polymorphic [[a000124]] to Haskell's arbitrary precision \hs{Integer}, and
ensure the compiler inlines it.

<<Define a000124>>=
{-# SPECIALIZE INLINE a000124 :: Integer -> Integer #-}
@

\newthought{Then to calculate the distance} from that position to that of
another column $c$ in the same row $r$, simply subtract the $r$th
\hrefootnote{https://en.wikipedia.org/wiki/Triangular_number}{triangular number}
from the $(c + r - 1)$th. The triangular numbers are known as
\hrefootnote{https://oeis.org/A000217}{A000217 in The Online Encyclopedia of
Integer Sequences}.

\begin{equation}
T(n) = n \times (n + 1) / 2
\end{equation}

Or equivalently in Haskell:

<<Define a000217>>=
a000217 :: (Integral a) => a -> a
a000217 n = n * (n + 1) `div` 2
@ %def a000217

Just as before, specialize to \hs{Integer} and inline.

<<Define a000217>>=
{-# SPECIALIZE INLINE a000217 :: Integer -> Integer #-}
@

So, to find the position in the sequence of codes from a given column and row:

<<Find the position>>=
position = a000124 row + a000217 (column + row - 1) - a000217 row
@

\newthought{The generating function} for the sequence is as follows, where $n
\in \mathbb{N}$ is the position.

\begin{equation}
f(n) =
\begin{cases}
20151125, & \text{for } n=0 \\
f(n - 1) \times 252533 \pmod{33554393}, & \text{otherwise}
\end{cases}
\end{equation}

That recursive definition can be rewritten as a closed formula.

\begin{equation}
f(n) = 20151125 \times 252533^{n-1} \pmod{33554393}
\end{equation}

Or equivalently in Haskell:

<<Find the code at a given position>>=
20151125 * (252533 ^ (position - 1)) `mod` 33554393
@

Thus, Part One can be solved by implementing a function that takes a coordinate
pair and returns the specified code.

<<Define partOne>>=
partOne :: Coordinates -> Integer
partOne (column, row) = <<Find the code at a given position>>
where
<<Find the position>>
@ %def partOne

There is nothing to solve for Part Two.

\newthought{To parse the input}, write a silly, overly explicit \hs{Parser}.

<<Import tools for parsing the input>>=
import Control.Monad (void)
import Text.Trifecta (Parser, comma, natural, symbol)
@

<<Define coordinates>>=
coordinates :: Parser Coordinates
coordinates =
do
void $ symbol "To continue, please consult the code grid in the manual."
row <- symbol "Enter the code at row" *> natural <* comma
column <- symbol "column" *> natural
pure (column, row)
@ %def coordinates

[[Coordinates]] is just a two-tuple of \hs{Integer}s.

<<Define Coordinates>>=
type Coordinates = (Integer, Integer)
@ %def Coordinates

Define the usual [[getInput]].

<<Import some common utilities>>=
import AdventOfCode.Input (parseInput)
import AdventOfCode.TH (inputFilePath)
@

<<Define getInput>>=
getInput :: IO Coordinates
getInput = parseInput coordinates $(inputFilePath)
@ %def getInput

\newthought{Bring it} all together.

<<Day25.hs>>=
module AdventOfCode.Year2015.Day25 where

<<Import some common utilities>>
<<Import tools for parsing the input>>

<<Define Coordinates>>

main :: IO ()
main =
do
putStr "Part One: "
print . partOne =<< getInput

<<Define partOne>>

<<Define getInput>>

<<Define coordinates>>

<<Define a000124>>

<<Define a000217>>
@
Loading

0 comments on commit 4085820

Please sign in to comment.