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

— Day 2: 1202 Program Alarm (Part 2) —

The second part of the Day 2 puzzle looks like it doesn’t require any changes to our ElfComputer itself – but requires another ElfComputerInputFinder to run multiple programs to find a specific output after trying different program inputs.

So for this part I’m just going to take the logic from solve.php from Day 2 Part 1 and modify it to brute force loop through all the possible inputs until we get the correct output. The value ranges seem small enough that this wouldn’t take very long…

Each of the two input values will be between 0 and 99, inclusive

So that means that there are 100 * 100 = 10,000 possible input combinations to test.

So first I created a new test class – this time I decided that the ElfComputerInputFinder should return an output true/false if it could successfully generate the desired output value by modifying it’s internal program. I appreciate what is an “input” and what is and “output” is a little muddled here… So my test file looked like this:

<?php

use PHPUnit\Framework\TestCase;
use PuzzleSolvers\Day2\ElfComputerInputFinder;

final class Day2Part2Test extends TestCase
{
    public function testInputs(): void
    {
        $outputs = [
            true,
        ];
        foreach ($outputs as $inputFileNumber => $output) {
            $puzzleSolver = new ElfComputerInputFinder('day2/part2/' . $inputFileNumber);
            $puzzleSolver->run();
            $this->assertSame($output, $puzzleSolver->getOutput());
        }
    }
}

The value which we are trying to get as an output from the ElfComputer is the input to the ElfComputerInputFinder. Your own value might be different?

/inputs/day2/part2/0

19690720

Next I created the ElfComputerInputFinder Class which as I mentioned was just using the code from solve.php in Day 2 Part 1 with some slight modifications. The most important being that I set the output using the following comparison (NOTE that we cast the input to an integer)

$this->output = ($elfComputerProgram[0] === (int) $this->inputs[0]);
<?php

namespace PuzzleSolvers\Day2;

use PuzzleSolvers\PuzzleSolver;
use PuzzleSolvers\Day2\ElfComputer;

class ElfComputerInputFinder extends PuzzleSolver
{
    public function run()
    {
        $elfComputer = new ElfComputer('day2/part1/input.txt');
        $elfComputer->initialise();
        $elfComputer->writeMemory(12, 1);
        $elfComputer->writeMemory(2, 2);
        $elfComputer->run();
        $elfComputerProgram = $elfComputer->getProgram();

        $this->output = ($elfComputerProgram[0] === (int) $this->inputs[0]);
    }
}

Running make tests shows that we have a failing test. So the next step it to loop through all the possible inputs (0-99) for the two writeMemory() calls and halt if we find a set of inputs that produces the correct outcome.

public function run()
{
    $elfComputer = new ElfComputer('day2/part1/input.txt');

    for ($input0 = 0; $input0 < 99; $input0++) {
        for ($input1 = 0; $input1 < 99; $input1++) {
            $elfComputer->initialise();
            $elfComputer->writeMemory($input0, 1);
            $elfComputer->writeMemory($input1, 2);
            $elfComputer->run();
            $elfComputerProgram = $elfComputer->getProgram();

            $this->output = ($elfComputerProgram[0] === (int) $this->inputs[0]);
        }
    }    
}

Now make tests still failed for the above… One mistake is that the output is always being overwritten based on the last comparison. So let’s make sure we exit the loop as soon as we’ve found a match.

for ($input0 = 0; $input0 < 99 && !$this->output; $input0++) {
    for ($input1 = 0; $input1 < 99 && !$this->output; $input1++) {

Now we are exiting the loop, the current values for $input0 and $input1 should be the correct inputs for the desired output. So lets read ahead in the puzzle it looks like we still have to do something with those values.

Find the input noun and verb that cause the program to produce the output 19690720What is 100 * noun + verb? (For example, if noun=12 and verb=2, the answer would be 1202.)

Ok so lets write a test that assumes we have set a noun and verb and produces the correct output

public function testOutputForVerbNoun()
{
    $elfComputerInputFinder = new ElfComputerInputFinder('day2/part2/0');
    $elfComputerInputFinder->SetNoun(12);
    $elfComputerInputFinder->SetVerb(2);

    $output = $elfComputerInputFinder->getOutput();

    $this->assertSame(1202, $output);
}

Now lets make the test pass – add the properties and the required functions

private $noun;
private $verb;

...

public function setNoun(int $value)
{
    $this->noun = $value;
}

public function setVerb(int $value)
{
    $this->verb = $value;
}

public function getOutput()
{
    return 100 * $this->noun + $this->verb;
}

Running make tests shows that this is passing – but we’ve now broken the initial test as we’ve modified what getOutput() returns.

Instead let’s use something like isMatchFound() in that test

public function testInputs(): void
{
    $outputs = [
        true,
    ];
    foreach ($outputs as $inputFileNumber => $output) {
        $puzzleSolver = new ElfComputerInputFinder('day2/part2/' . $inputFileNumber);
        $puzzleSolver->run();
        $this->assertSame($output, $puzzleSolver->isMatchFound());
    }
}

and make it pass

class ElfComputerInputFinder extends PuzzleSolver
{

...

    private $isMatchFound;

...

        for ($input0 = 0; $input0 < 99 && !$this->isMatchFound; $input0++) {
            for ($input1 = 0; $input1 < 99 && !$this->isMatchFound; $input1++) {

...

                $this->isMatchFound = ($elfComputerProgram[0] === (int) $this->inputs[0]);
 
...

    public function isMatchFound()
    {
        return $this->isMatchFound;
    }

...

We also need to actually set the noun and verb if we find a match

        if ($this->isMatchFound) {
            $this->noun = $input0;
            $this->verb = $input1;
        }

So here is the final class

/src/day2/ElfComputerInputFinder.php

<?php

namespace PuzzleSolvers\Day2;

use PuzzleSolvers\PuzzleSolver;
use PuzzleSolvers\Day2\ElfComputer;

class ElfComputerInputFinder extends PuzzleSolver
{
    private $noun;
    private $verb;
    private $isMatchFound;

    public function run()
    {
        $elfComputer = new ElfComputer('day2/part1/input.txt');

        for ($input0 = 0; $input0 < 99 && !$this->isMatchFound; $input0++) {
            for ($input1 = 0; $input1 < 99 && !$this->isMatchFound; $input1++) {
                $elfComputer->initialise();
                $elfComputer->writeMemory($input0, 1);
                $elfComputer->writeMemory($input1, 2);
                $elfComputer->run();
                $elfComputerProgram = $elfComputer->getProgram();

                $this->isMatchFound = ($elfComputerProgram[0] === (int) $this->inputs[0]);
                if ($this->isMatchFound) {
                    $this->noun = $input0;
                    $this->verb = $input1;
                }
            }
        }
    }

    public function setNoun(int $value)
    {
        $this->noun = $value;
    }

    public function isMatchFound()
    {
        return $this->isMatchFound;
    }

    public function setVerb(int $value)
    {
        $this->verb = $value;
    }

    public function getOutput()
    {
        return 100 * $this->noun + $this->verb;
    }
}

OK now our tests are passing! I can see quite a few holes in the code in terms of edge cases but I think it’s good enough to have a go at getting that star. So let’s update solve.php to run it.

/src/day2/solve.php

<?php

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

use PuzzleSolvers\Day2\ElfComputer;
use PuzzleSolvers\Day2\ElfComputerInputFinder;

$elfComputer = new ElfComputer('day2/part1/input.txt');
$elfComputer->initialise();
$elfComputer->writeMemory(12, 1);
$elfComputer->writeMemory(2, 2);
$elfComputer->run();
$output = $elfComputer->getProgram();

echo "Day 2 Part 1 answer: " . $output[0] . "\n";

$elfComputerInputFinder = new ElfComputerInputFinder('day2/part2/0');
$elfComputerInputFinder->run();
$output = $elfComputerInputFinder->getOutput();

echo "Day 2 Part 2 answer: " . $output . "\n";

Let’s get that gold star!

$ php src/day2/solve.php 
Day 2 Part 1 answer: 4138687
Day 2 Part 2 answer: 6635

The final code for this part can be found on GitHub here. Remember my answer may not be the same as yours for your real input. Make sure to replace your inputs/day2/part2/0 with your own file. Did it work? Let me know!

Next up: Day 3 Part 1

Leave a comment

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