Spoilers for Advent of Code 2019 follow.
Fuel required to launch a given module is based on its mass. Specifically, to find the fuel required for a module, take its mass, divide by three, round down, and subtract 2.
This describes a simple function. There seems to be an oversight in the problem statement that modules with very low mass have a negative fuel requirement. I'm going to assume that's not right, and that instead of integer subtraction, we want natural number subtraction (sometimes called "monus"). In Unison, we can use the Nat
type instead of integers, so we don't have to consider negatives. The subtraction operation is called drop
:
fuelRequirement : Nat -> Nat
fuelRequirement mass = mass / 3 `drop` 2
Let's add that to the codebase:
☝️ The namespace .advent2019.day1 is empty.
.advent2019.day1> add
⍟ I've added these definitions:
fuelRequirement : Nat -> Nat
We're given some test cases. I'm just going to represent these as pairs where the first element is the input and the second is the expected output.
day1.testCases = [(12, 2), (14, 2), (1969, 654), (100756, 33583)]
We can add those:
.advent2019> add
⍟ I've added these definitions:
day1.testCases : [(Nat, Nat)]
Let's add a test that checks that our fuelRequirement
function passes these cases. First, I want to add a helper function that checks a given test case, turning it into a Boolean:
checkCase f tc = case tc of (i, o) -> f i == o
.advent2019> add
⍟ I've added these definitions:
checkCase : (i ->{𝕖} o) -> (i, o) ->{𝕖} Boolean
And then a function that checks a whole list of cases. To do this, I use the forAll
function from base.test
. This takes a Nat
, which is the maximum number of test cases to run, and a Domain
, which is the set of values from which to generate test cases. A domain can be Small
, in which case it's just an exhaustive list of cases. So I just make a Small
domain from the cases we were given:
use .base.test.internals.v1.Test forAll
checkAll : (a -> b) -> [(a, b)] -> [Result]
checkAll f cases = forAll (List.size cases) (Small cases) (checkCase f)
.advent2019> add
⍟ I've added these definitions:
checkAll : (a ->{𝕖} b) -> [(a, b)] ->{𝕖} [Result]
Now we can write the actual test very simply:
test> day1.test = checkAll fuelRequirement testCases
1 | test> day1.test = checkAll fuelRequirement testCases
✅ Passed : Proved.
The test seems to pass. Let's add it:
.advent2019> add
⍟ I've added these definitions:
day1.test : [Result]
Now that we have a working fuelRequirement
function, we can add together the results of applying it to every value in the test input. First let's add that test input (I just pasted it into my text editor and added commas):
day1.data =
[ 68958,
82218,
54333,
59177,
51874,
100259,
95468,
124006,
75078,
113631,
90315,
147580,
68233,
81025,
133084,
90959,
81196,
92443,
124832,
65871,
57704,
140203,
113053,
76337,
72195,
115917,
87843,
131768,
105816,
131153,
59714,
94107,
50933,
139545,
94969,
149463,
60042,
66028,
111190,
63257,
50020,
88783,
81428,
73977,
149240,
137152,
74738,
55067,
128829,
56465,
81962,
67242,
94121,
92303,
68477,
88595,
64324,
82527,
134717,
140344,
132026,
137558,
95643,
79010,
146346,
86246,
52341,
147564,
89159,
66456,
83190,
128675,
130658,
122857,
134538,
122151,
133900,
117462,
117791,
139254,
86366,
66165,
92897,
121218,
135962,
143061,
129220,
114623,
98257,
76722,
121014,
77713,
137858,
133282,
103595,
118981,
149137,
101141,
70765,
141113 ]
.advent2019> add
⍟ I've added these definitions:
day1.data : [Nat]
The puzzle asks for the sum of the fuelRequirement
for all these inputs. That's easy:
answer = foldl (acc x -> fuelRequirement x + acc) 0 data
> answer
3 | > answer
⧩
3337766
We can store the answer in the codebsase if we like:
.advent2019.day1> add
⍟ I've added these definitions:
answer : Nat