As I wrote in the previous post, the Advent of Code is happening soon — it start on Tuesday, December 1st and runs all the way to December 25th. If you want to give your programming skills a good workout or test, you’ll want to try out Advent of Code’s challenges!
The previous post featured Python solutions to the day one challenges from the 2019 edition of Advent of Code. In this post, I’ll present solutions written in Swift.
Day one challenge, part one
Here’s the first part of day one’s challenge:
The Elves quickly load you into a spacecraft and prepare to launch.
At the first Go / No Go poll, every Elf is Go until the Fuel Counter-Upper. They haven’t determined the amount of fuel required yet.
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.
For example:
- For a mass of
12
, divide by 3 and round down to get4
, then subtract 2 to get2
.- For a mass of
14
, dividing by 3 and rounding down still yields4
, so the fuel required is also2
.- For a mass of
1969
, the fuel required is654
.- For a mass of
100756
, the fuel required is33583
.The Fuel Counter-Upper needs to know the total fuel requirement. To find it, individually calculate the fuel needed for the mass of each module (your puzzle input), then add together all the fuel values.
What is the sum of the fuel requirements for all of the modules on your spacecraft?
While the problems in the Advent of Code are the same for every participant, the data for each participant is different (there’s a sign-up process, which gives you an account, your own progress tracker, and your own data). This prevents participants from simply sharing the solution.
Here are the module masses that were provided for my account:
134492 88713 84405 148193 95951 63545 137840 65558 124836 95431 77622 91864 108677 116871 119496 97172 86115 105704 68613 77114 114013 52766 57048 80814 73888 58253 135934 97409 112439 98262 116047 57456 124261 83006 101495 133449 111372 56146 87818 92209 149259 124559 141838 147988 65703 125566 59650 139564 92430 126307 120406 147383 84362 51529 146366 131840 53270 71886 118767 104311 126181 76964 129430 95489 91098 54133 110057 107276 118226 96104 135382 85152 61697 143417 148879 126846 130205 111170 86687 113729 123330 56976 148470 66028 129715 75686 74964 148258 72669 88809 78173 92699 124806 67217 139066 136002 135730 145708 142054 135772
I used the Python REPL for my Python-based solution. For my Swift-based solution, I used the closest analogue: an Xcode Playground.
Swift, like Python uses the three quotes to denote multiline strings. I used them to define a multiline string constant, rawInput
, into which I pasted the data:
let rawInput = """ 134492 88713 84405 148193 95951 63545 137840 65558 124836 95431 77622 91864 108677 116871 119496 97172 86115 105704 68613 77114 114013 52766 57048 80814 73888 58253 135934 97409 112439 98262 116047 57456 124261 83006 101495 133449 111372 56146 87818 92209 149259 124559 141838 147988 65703 125566 59650 139564 92430 126307 120406 147383 84362 51529 146366 131840 53270 71886 118767 104311 126181 76964 129430 95489 91098 54133 110057 107276 118226 96104 135382 85152 61697 143417 148879 126846 130205 111170 86687 113729 123330 56976 148470 66028 129715 75686 74964 148258 72669 88809 78173 92699 124806 67217 139066 136002 135730 145708 142054 135772 """
With rawInput
defined, it’s time to convert it from a multiline string into an array of strings, with each line getting turned into its own array element. The String
class’ split
method does this quite easily, and the result was the splitInput
string array:
let splitInput = rawInput.split(separator: "\n")
The next step was to convert splitInput
’s numbers-in-string-form into actual numbers. This process would involve applying the same function — the Int
struct’s “init
from string” method — to all the elements in an array, which is exactly what the map
method is for:
let masses = splitInput.map {Int($0)!}
Swift’s map
method takes a closure containing a function as its argument and applies that function to every item in the given array, creating a new array as its result.
In this case, the function in question is:
Int($0)!
Parameters passed into the closure begin with the $
character, which is then followed by a number specifying which parameter it is. The first parameter is $0
, followed by the second parameter, $1
, followed by the third parameter, $2
, and so on.
Only one parameter is passed to the closure: $0
, which represents the current element of the splitInput
array. It’s fed into the init
method of Int
that takes a string and attempt to produce an integer. Since it’s possible that this method will be given a string that can’t be converted into an integer, the method’s return type is the optional type Int?
.
Since I’m quite certain that all the strings in the splitInput
array convert to integers, I used the ! operator to force unwrap the resulting Int?
values.
The end result is masses
, an array of integers. Each element in the array represents the mass of a component in the ship, and we need to calculate the fuel necessary to propel each component to the final destination.
This calculation involves applying a function to every element in masses
, and that function is:
- Divide the mass by 3, rounding down.
- Subtracting 2 from the result above.
Once again, I used map
:
let fuelRequirements = masses.map { mass in mass / 3 - 2 }
In the function above, mass
and 3
are both integers, so mass / 3
is an integer division, which automatically rounds down.
The result of this map
ping is fuelRequirements
, an array of integers containing the fuel requirements for each module.
The result is the sum of all the values in fuelRequirements
. Unfortunately, Swift doesn’t have a built in method for getting the sum of an array, so we’ll need to roll our own:
let totalFuel = fuelRequirements.reduce(0, +)
For my data, the result was 3454942. This turned out to be correct, so it was time to tackle part two.
Day one challenge, part two
Part two involved recalculating the fuel requirements when also taking into account the mass of the added fuel:
During the second Go / No Go poll, the Elf in charge of the Rocket Equation Double-Checker stops the launch sequence. Apparently, you forgot to include additional fuel for the fuel you just added.
Fuel itself requires fuel just like a module – take its mass, divide by three, round down, and subtract 2. However, that fuel also requires fuel, and that fuel requires fuel, and so on. Any mass that would require negative fuel should instead be treated as if it requires zero fuel; the remaining mass, if any, is instead handled by wishing really hard, which has no mass and is outside the scope of this calculation.
So, for each module mass, calculate its fuel and add it to the total. Then, treat the fuel amount you just calculated as the input mass and repeat the process, continuing until a fuel requirement is zero or negative. For example:
- A module of mass
14
requires2
fuel. This fuel requires no further fuel (2 divided by 3 and rounded down is0
, which would call for a negative fuel), so the total fuel required is still just2
.- At first, a module of mass
1969
requires654
fuel. Then, this fuel requires216
more fuel (654 / 3 - 2
).216
then requires70
more fuel, which requires21
fuel, which requires5
fuel, which requires no further fuel. So, the total fuel required for a module of mass1969
is654 + 216 + 70 + 21 + 5 = 966
.- The fuel required by a module of mass
100756
and its fuel is:33583 + 11192 + 3728 + 1240 + 411 + 135 + 43 + 12 + 2 = 50346
.What is the sum of the fuel requirements for all of the modules on your spacecraft when also taking into account the mass of the added fuel? (Calculate the fuel requirements for each module separately, then add them all up at the end.)
This called for a recursive function, the Swift code for which is below:
func fuelRequired(mass: Int) -> Int { let result = mass / 3 - 2 if result <= 0 { return 0 } else { return result + fuelRequired(mass: result) } }
I used this function to map
the values in the masses
array from part one onto a new array, updatedFuelRequirements
…
let updatedFuelRequirements = masses.map { fuelRequired(mass: $0) }
…and the sum of its the elements was the answer for part two:
let updatedTotalFuel = updatedFuelRequirements.reduce(0, +)
For my data, the answer was 5179544.