Thun/docs/Advent of Code 2017 Decembe...

555 lines
15 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Advent of Code 2017\n",
"\n",
"## December 2nd\n",
"\n",
"For each row, determine the difference between the largest value and the smallest value; the checksum is the sum of all of these differences.\n",
"\n",
"For example, given the following spreadsheet:\n",
"\n",
" 5 1 9 5\n",
" 7 5 3\n",
" 2 4 6 8\n",
"\n",
"* The first row's largest and smallest values are 9 and 1, and their difference is 8.\n",
"* The second row's largest and smallest values are 7 and 3, and their difference is 4.\n",
"* The third row's difference is 6.\n",
"\n",
"In this example, the spreadsheet's checksum would be 8 + 4 + 6 = 18."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from notebook_preamble import J, V, define"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"I'll assume the input is a Joy sequence of sequences of integers.\n",
"\n",
" [[5 1 9 5]\n",
" [7 5 3]\n",
" [2 4 6 8]]\n",
"\n",
"So, obviously, the initial form will be a `step` function:\n",
"\n",
" AoC2017.2 == 0 swap [F +] step"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This function `F` must get the `max` and `min` of a row of numbers and subtract. We can define a helper function `maxmin` which does this:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"define('maxmin == [max] [min] cleave')"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3 1\n"
]
}
],
"source": [
"J('[1 2 3] maxmin')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Then `F` just does that then subtracts the min from the max:\n",
"\n",
" F == maxmin -\n",
"\n",
"So:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"define('AoC2017.2 == [maxmin - +] step_zero')"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"18\n"
]
}
],
"source": [
"J('''\n",
"\n",
"[[5 1 9 5]\n",
" [7 5 3]\n",
" [2 4 6 8]] AoC2017.2\n",
"\n",
"''')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"...find the only two numbers in each row where one evenly divides the other - that is, where the result of the division operation is a whole number. They would like you to find those numbers on each line, divide them, and add up each line's result.\n",
"\n",
"For example, given the following spreadsheet:\n",
"\n",
" 5 9 2 8\n",
" 9 4 7 3\n",
" 3 8 6 5\n",
"\n",
"* In the first row, the only two numbers that evenly divide are 8 and 2; the result of this division is 4.\n",
"* In the second row, the two numbers are 9 and 3; the result is 3.\n",
"* In the third row, the result is 2.\n",
"\n",
"In this example, the sum of the results would be 4 + 3 + 2 = 9.\n",
"\n",
"What is the sum of each row's result in your puzzle input?"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[9 8 5 2]\n"
]
}
],
"source": [
"J('[5 9 2 8] sort reverse')"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[8 5 2] [9 divmod] [8 5 2]\n"
]
}
],
"source": [
"J('[9 8 5 2] uncons [swap [divmod] cons] dupdip')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
" [9 8 5 2] uncons [swap [divmod] cons F] dupdip G\n",
" [8 5 2] [9 divmod] F [8 5 2] G\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" . [8 5 2] [9 divmod] [uncons swap] dip dup [i not] dip\n",
" [8 5 2] . [9 divmod] [uncons swap] dip dup [i not] dip\n",
" [8 5 2] [9 divmod] . [uncons swap] dip dup [i not] dip\n",
" [8 5 2] [9 divmod] [uncons swap] . dip dup [i not] dip\n",
" [8 5 2] . uncons swap [9 divmod] dup [i not] dip\n",
" 8 [5 2] . swap [9 divmod] dup [i not] dip\n",
" [5 2] 8 . [9 divmod] dup [i not] dip\n",
" [5 2] 8 [9 divmod] . dup [i not] dip\n",
" [5 2] 8 [9 divmod] [9 divmod] . [i not] dip\n",
"[5 2] 8 [9 divmod] [9 divmod] [i not] . dip\n",
" [5 2] 8 [9 divmod] . i not [9 divmod]\n",
" [5 2] 8 . 9 divmod not [9 divmod]\n",
" [5 2] 8 9 . divmod not [9 divmod]\n",
" [5 2] 1 1 . not [9 divmod]\n",
" [5 2] 1 False . [9 divmod]\n",
" [5 2] 1 False [9 divmod] . \n"
]
}
],
"source": [
"V('[8 5 2] [9 divmod] [uncons swap] dip dup [i not] dip')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Tricky\n",
"\n",
"Let's think.\n",
"\n",
"Given a *sorted* sequence (from highest to lowest) we want to \n",
"* for head, tail in sequence\n",
" * for term in tail:\n",
" * check if the head % term == 0\n",
" * if so compute head / term and terminate loop\n",
" * else continue"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### So we want a `loop` I think\n",
"\n",
" [a b c d] True [Q] loop\n",
" [a b c d] Q [Q] loop\n",
"\n",
"`Q` should either leave the result and False, or the `rest` and True.\n",
"\n",
" [a b c d] Q\n",
" -----------------\n",
" result 0\n",
"\n",
" [a b c d] Q\n",
" -----------------\n",
" [b c d] 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This suggests that `Q` should start with:\n",
"\n",
" [a b c d] uncons dup roll<\n",
" [b c d] [b c d] a\n",
"\n",
"Now we just have to `pop` it if we don't need it.\n",
"\n",
" [b c d] [b c d] a [P] [T] [cons] app2 popdd [E] primrec\n",
" [b c d] [b c d] [a P] [a T] [E] primrec\n",
"\n",
"-------------------\n",
"\n",
" w/ Q == [% not] [T] [F] primrec\n",
"\n",
" [a b c d] uncons\n",
" a [b c d] tuck\n",
" [b c d] a [b c d] uncons\n",
" [b c d] a b [c d] roll>\n",
" [b c d] [c d] a b Q\n",
" [b c d] [c d] a b [% not] [T] [F] primrec\n",
"\n",
" [b c d] [c d] a b T\n",
" [b c d] [c d] a b / roll> popop 0\n",
"\n",
" [b c d] [c d] a b F Q\n",
" [b c d] [c d] a b pop swap uncons ... Q\n",
" [b c d] [c d] a swap uncons ... Q\n",
" [b c d] a [c d] uncons ... Q\n",
" [b c d] a c [d] roll> Q\n",
" [b c d] [d] a c Q\n",
"\n",
" Q == [% not] [/ roll> popop 0] [pop swap uncons roll>] primrec\n",
" \n",
" uncons tuck uncons roll> Q"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[8 5 3 2] [9 swap] [9 % not]\n"
]
}
],
"source": [
"J('[8 5 3 2] 9 [swap] [% not] [cons] app2 popdd')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"-------------------\n",
"\n",
" [a b c d] uncons\n",
" a [b c d] tuck\n",
" [b c d] a [b c d] [not] [popop 1] [Q] ifte\n",
"\n",
" [b c d] a [] popop 1\n",
" [b c d] 1\n",
"\n",
" [b c d] a [b c d] Q \n",
"\n",
"\n",
" a [...] Q\n",
" ---------------\n",
" result 0\n",
"\n",
" a [...] Q\n",
" ---------------\n",
" 1\n",
"\n",
"\n",
" w/ Q == [first % not] [first / 0] [rest [not] [popop 1]] [ifte]\n",
"\n",
"\n",
"\n",
" a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte]\n",
" a [b c d] first % not\n",
" a b % not\n",
" a%b not\n",
" bool(a%b)\n",
"\n",
" a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte]\n",
" a [b c d] first / 0\n",
" a b / 0\n",
" a/b 0\n",
"\n",
" a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte]\n",
" a [b c d] rest [not] [popop 1] [Q] ifte\n",
" a [c d] [not] [popop 1] [Q] ifte\n",
" a [c d] [not] [popop 1] [Q] ifte\n",
"\n",
" a [c d] [not] [popop 1] [Q] ifte\n",
" a [c d] not\n",
"\n",
" a [] popop 1\n",
" 1\n",
"\n",
" a [c d] Q\n",
"\n",
"\n",
" uncons tuck [first % not] [first / 0] [rest [not] [popop 1]] [ifte]\n",
" \n",
" \n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### I finally sat down with a piece of paper and blocked it out.\n",
"\n",
"First, I made a function `G` that expects a number and a sequence of candidates and return the result or zero:\n",
"\n",
" n [...] G\n",
" ---------------\n",
" result\n",
"\n",
" n [...] G\n",
" ---------------\n",
" 0\n",
"\n",
"It's a recursive function that conditionally executes the recursive part of its recursive branch\n",
"\n",
" [Pg] [E] [R1 [Pi] [T]] [ifte] genrec\n",
"\n",
"The recursive branch is the else-part of the inner `ifte`:\n",
"\n",
" G == [Pg] [E] [R1 [Pi] [T]] [ifte] genrec\n",
" == [Pg] [E] [R1 [Pi] [T] [G] ifte] ifte\n",
"\n",
"But this is in hindsight. Going forward I derived:\n",
"\n",
" G == [first % not]\n",
" [first /]\n",
" [rest [not] [popop 0]]\n",
" [ifte] genrec\n",
"\n",
"The predicate detects if the `n` can be evenly divided by the `first` item in the list. If so, the then-part returns the result. Otherwise, we have:\n",
"\n",
" n [m ...] rest [not] [popop 0] [G] ifte\n",
" n [...] [not] [popop 0] [G] ifte\n",
"\n",
"This `ifte` guards against empty sequences and returns zero in that case, otherwise it executes `G`."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"define('G == [first % not] [first /] [rest [not] [popop 0]] [ifte] genrec')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we need a word that uses `G` on each (head, tail) pair of a sequence until it finds a (non-zero) result. It's going to be designed to work on a stack that has some candidate `n`, a sequence of possible divisors, and a result that is zero to signal to continue (a non-zero value implies that it is the discovered result):\n",
"\n",
" n [...] p find-result\n",
" ---------------------------\n",
" result\n",
"\n",
"It applies `G` using `nullary` because if it fails with one candidate it needs the list to get the next one (the list is otherwise consumed by `G`.)\n",
"\n",
" find-result == [0 >] [roll> popop] [roll< popop uncons [G] nullary] primrec\n",
"\n",
" n [...] p [0 >] [roll> popop] [roll< popop uncons [G] nullary] primrec\n",
"\n",
"The base-case is trivial, return the (non-zero) result. The recursive branch...\n",
"\n",
" n [...] p roll< popop uncons [G] nullary find-result\n",
" [...] p n popop uncons [G] nullary find-result\n",
" [...] uncons [G] nullary find-result\n",
" m [..] [G] nullary find-result\n",
" m [..] p find-result\n",
"\n",
"The puzzle states that the input is well-formed, meaning that we can expect a result before the row sequence empties and so do not need to guard the `uncons`."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"define('find-result == [0 >] [roll> popop] [roll< popop uncons [G] nullary] primrec')"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3.0\n"
]
}
],
"source": [
"J('[11 9 8 7 3 2] 0 tuck find-result')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In order to get the thing started, we need to `sort` the list in descending order, then prime the `find-result` function with a dummy candidate value and zero (\"continue\") flag."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"define('prep-row == sort reverse 0 tuck')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we can define our program."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"define('AoC20017.2.extra == [prep-row find-result +] step_zero')"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"9.0\n"
]
}
],
"source": [
"J('''\n",
"\n",
"[[5 9 2 8]\n",
" [9 4 7 3]\n",
" [3 8 6 5]] AoC20017.2.extra\n",
"\n",
"''')"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 2
}