Advent of Code 2019 Day 1 (Part 1) PHP Hints

— Day 1: The Tyranny of the Rocket Equation (Part 1) —

At last it’s time to dive in to some code – if you haven’t yet please check out the Getting Started post to make sure you have the same setup or similar to me if you want to follow along. The final code for this part can be found on GitHub here.

So reading through the puzzle description (which is great) we are building a FuelCounterUpper – essentially we’re trying to solve the rocket equation to calculate how much fuel is required to launch a given module is based on its mass.

We are given an explanation of how this should be done and also crucially 4 test scenarios with input and expected output. So the first thing to do is to add them to our test harness.

I created a new test file called Puzzle1Test.php. In this file I wanted to define the test outputs, loop through each of the test files, create a new FuelCounterUpper object using the input and test its output against the expected output.

/tests/Puzzle1Test.php

<?php

use PHPUnit\Framework\TestCase;
use PuzzleSolvers\Puzzle1\FuelCounterUpper;

final class Puzzle1Test extends TestCase
{
    public function testInputs(): void
    {
        $outputs = [2, 2, 654, 33583];
        foreach ($outputs as $inputFileNumber => $output) {
            $puzzleSolver = new FuelCounterUpper('puzzle1/' . $inputFileNumber . '.txt');
            $puzzleSolver->run();
            $this->assertSame($output, $puzzleSolver->getOutput());
        }
    }
}

Next I created 4 test input files with just the provided inputs as content.

/inputs/puzzle1/0.txt

12

/inputs/puzzle1/1.txt

14

/inputs/puzzle1/2.txt

1969

/inputs/puzzle1/3.txt

12

Using the files is a bit cumbersome for single inputs but reading ahead we will be provided with a larger more complex file for the actual final calculation. So I thought it’s a good idea to just always use files. Finally I created the FuelCounterUpper Class which extends the PuzzleSolver Class from Getting Started.

/src/FuelCounterUpper.php

<?php

namespace PuzzleSolvers;

class FuelCounterUpper extends PuzzleSolver
{
    public function run()
    {

    }
}

Now running make tests is providing an end-to-end test suite for us to implement our actual logic in the run function.

NOTE: After adding classes I needed to regenerate the composer autoload paths by running composer dump-autoload. I decided to add this to the Makefile before running the tests.

Makefile

.PHONY: tests
tests:
	composer dump-autoload
	./vendor/phpunit/phpunit/phpunit tests/*

Solution

This is really where we get to think about the specific problem at hand – hopefully the setup so far is useful in getting started and you might want to just take it from here.

For me the next step was to look at the specific mass to fuel calculation and create a new function in the FuelCounterUpper Class.

public function calculateFuelForMass(int $mass)
{
}

From the instructions we are told

to find the fuel required for a module, take its mass, divide by three, round down, and subtract 2.

So this seems reasonably simple to implement. First let’s try something like this:

public function calculateFuelForMass(int $mass)
{
    return $mass / 3 - 2;
}

And modify the run() function to loop through the inputs and pass them as arguments to the new function:

public function run()
{
    foreach ($this->inputs as $mass) {
        $this->output = $this->calculateFuelForMass($mass);
    }
}

Now if we run make tests again we see we have 1 passing assertion! But followed by a failing assertion.

There was 1 failure:

1) Puzzle1Test::testInputs
Failed asserting that 2.666666666666667 is identical to 2.

/tests/Puzzle1Test.php:14

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.

So our calculation is correct for input file 0.txt but not for 1.txt. What’s the missing piece?

The missing piece is the rounding down part of the equation

to find the fuel required for a module, take its mass, divide by three, round down, and subtract 2.

In PHP there is a handy floor() function to round numbers down. So our function becomes:

public function calculateFuelForMass(int $mass)
{
    return floor($mass / 3) - 2;
}

Running make tests again we still get an interesting failure

Failed asserting that 2.0 is identical to 2

Because my tests are using the stricter assertSame function and PHP plays fast and loose with types – the values are not technically identical. Our calculated value was converted to a float when calling the floor() function. From php.net

The return value of floor() is still of type float because the value range of float is usually bigger than that of integer.

https://www.php.net/manual/en/function.floor.php

So we just need to ensure we cast the value back to an integer.

public function calculateFuelForMass(int $mass)
{
    return (int) floor($mass / 3) - 2;
}

Now if we run make tests again we have all 4 assertions passing! So we can be pretty confident that our FuelCounterUpper is doing the right thing. If you want you could create your own test inputs and expected outputs to test other scenarios and edge cases. But for the purposes of this blog I’m generally ignoring that unless it is clearly needed.

Ok so next it’s time to get your live input. Click on the link in the puzzle description and it should open a new page. Copy and Paste the contents into a new file in your /inputs/puzzle1 folder. I believe that every person will get a unique input file – and will require a unique answer. Here was the input I received:
<strong>/inputs/puzzle1/real.txt</strong>

63455
147371
83071
57460
74392
145303
130181
53102
120073
93111
144471
105327
116466
67222
122845
146097
92014
114428
96796
131140
101481
87953
101415
75739
64263
94257
140426
62387
84464
104547
103581
89121
123301
64993
143555
55246
120986
67596
146173
149707
60285
83517
73782
103464
140506
78400
140672
141638
84470
116879
100701
63976
135748
65021
120086
147249
55441
135315
147426
93676
91384
110918
123368
102430
144807
82761
134357
62990
85171
134886
69166
119744
80648
96752
89379
136178
95175
124306
51990
57564
111347
79317
95357
85765
137827
105014
110742
105014
149330
78437
107908
139044
143304
90614
52119
147113
119815
125634
104335
138295

So we can now see that rather than a single mass value in the file there is a a long list. In the last part of the puzzle we’re told:

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.

So we need to modify our program to add each of the outputs together rather than just a single fuel calculation. Now it’s time to create my own test file using what we’ve learned so far – I combined all of the inputs from the original tests into /inputs/puzzle1/4.txt and then added the sum of the outputs as the new expected output (2 + 2 + 654 + 33583).

<strong>/inputs/puzzle1/4.txt</strong>

12
14
1969
100756

/tests/Puzzle1Test.php

<?php

use PHPUnit\Framework\TestCase;
use PuzzleSolvers\Puzzle1\FuelCounterUpper;

final class Puzzle1Test extends TestCase
{
    public function testInputs(): void
    {
        $outputs = [2, 2, 654, 33583, (2 + 2 + 654 + 33583)];
        foreach ($outputs as $inputFileNumber => $output) {
            $puzzleSolver = new FuelCounterUpper('puzzle1/' . $inputFileNumber . '.txt');
            $puzzleSolver->run();
            $this->assertSame($output, $puzzleSolver->getOutput());
        }
    }
}

After running make tests to see that we had a failing assertion again the last change was to sum the output after each calculation.

public function run()
{
    $this->output = 0;

    foreach ($this->inputs as $mass) {
        $this->output += $this->calculateFuelForMass($mass);
    }
}

Now all assertions are passing – we still need a way to just run the real puzzle input file through our class. I created a simple solve.php script:

<strong>src/puzzle1/solve.php</strong>

<?php

require_once __DIR__ . '/../../vendor/autoload.php';

use PuzzleSolvers\Puzzle1\FuelCounterUpper;

$fuelCounterUpper = new FuelCounterUpper('puzzle1/input.txt');
$fuelCounterUpper->run();
echo "answer: " . $fuelCounterUpper->getOutput() . "\n";

Let’s get that gold star!

$ php src/puzzle1/solve.php
answer: 3464458

The final code for this part can be found on GitHub here. Remember my answer will not be the same as yours for your real input. Make sure to replace your /inputs/puzzle1/input.txt with your own file. Did it work? Let me know! In the next part I’ll take a look at getting that 2nd gold star.

Leave a comment

Your email address will not be published. Required fields are marked *