Worst Practice

Advent of Code - Day 9

Posted on December  9, 2022 @ 13:45

Posted under the Backend category

Level: expert

Posted with the following tags: #Advent, #PHP

The day I failed.

Advent of Code - Day 9
Calendar icon by Kevin Sanderson from Pixabay

The input data

A very basic data file:

  • each row contains a character and a number separated by space
  • the characters represent directions (up, right, down, left): U, R, D, L
  • the numbers represent the distance/step

Task: we pull a rope in the given direction for the given distance, and we need to figure out,

Part one

I will be honest: it was tough. I went down to a super primitive level, and wrote a low-performing code just to do what needs to be done and get the right result. Currently, I am not even sure, whether my code is actually working well or just accidentally gives the right result.

Should I share the code? Well, it’s definitely the worst practice.

Part two

I’m not proud of myself. This is the day I have to give up. I could pass part one, but this one requires skills that I don’t have yet. I failed. I tried, but couldn’t figure out the algorithm. I tried to reuse the functions from the part one, but it was a complete failure as well. I’m too tired, sad and disappointed now to try again. I give up…

This is how it should look like visualized.

When I was thinking about the solution, I thought if I treat every knot as a rope-head for the next knot, it will work. But I couldn’t even reproduce the example outputs of the task.

Maybe later I will re-try and update this article… But not today.

The next day

I was very upset that I couldn’t figure out the solution, so I spent my whole Saturday (10th of December) to find a solution. I analyzed the example in the puzzle description and watched that reddit video of the working solution a million times to understand the logic behind it. And then I just found it.

The solution I made for the part one was a total dead end, no wonder why lead me to nowhere. Yes, it accidentally gave the right result, but it was a disaster.

The new, working code covers both part one and two, you need only change the value of the ROPE_LENGTH constant. I also added a print function to be able to visualize the result. For the puzzle example, it will look something like this:

Output sample of the rope pull puzzle.

The Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
const ROPE_LENGTH = 10;

$knotPositions = array_fill(0, ROPE_LENGTH, [0,0]);
$moves = [];
$headX = 0;
$headY = 0;
$visitedByTail = [
    '0,0' => 1,
];

$minY = 0;
$maxY = 0;
$row = 1;

if ($fn = fopen(__DIR__ . '/input.txt', 'r')) {
    while (($line = fgets($fn, 1024)) !== false) {
        $line = trim($line);
        [$direction, $steps] = explode(' ', $line);

        for ($i = 0; $i < $steps; $i++) {
            switch ($direction) {
                case 'U':
                    $headY++;
                    break;
                case 'R':
                    $headX++;
                    break;
                case 'D':
                    $headY--;
                    break;
                case 'L':
                    $headX--;
            }

            $knotPositions[0] = [$headX, $headY];
            $minY = min($minY, $headY);
            $maxY = max($maxY, $headY);

            for ($j = 1; $j < ROPE_LENGTH; $j++) {
                moveKnot($j, $knotPositions);
            }

            $coordinate = $knotPositions[ROPE_LENGTH - 1][0].','.$knotPositions[ROPE_LENGTH - 1][1];

            if (!isset($visitedByTail[$coordinate])) {
                $visitedByTail[$coordinate] = 0;
            }

            $visitedByTail[$coordinate]++;

            deleteLines($minY, $maxY);
            printStep(
                moveIndex: $row,
                stepIndex: $i + 1,
                direction: $direction,
                knotPositions: $knotPositions,
                visitedByTail: $visitedByTail,
                minY: $minY,
                maxY: $maxY
            );
        }
        $row++;
    }
    fclose($fn);
}
function moveKnot(int $knotIndex, array &$knotPositions): void
{
    [$currentTailX, $currentTailY] = $knotPositions[$knotIndex];
    [$previousTailX, $previousTailY] = $knotPositions[$knotIndex - 1];

    $diffX = abs($previousTailX - $currentTailX);
    $diffY = abs($previousTailY - $currentTailY);

    if ($diffX < 2 && $diffY < 2) {
        return;
    }

    $currentTailX += ($diffX === 0 && $diffY > 1)
        ? 0
        : ($previousTailX - $currentTailX > 0 ? 1 : -1);

    $currentTailY += ($diffX > 1 && $diffY === 0)
        ? 0
        : ($previousTailY - $currentTailY > 0 ? 1 : -1);

    $knotPositions[$knotIndex] = [$currentTailX, $currentTailY];
}

function deleteLines(int $minY, int $maxY): void
{
    for ($y = max($maxY +6, 20); $y >= min($minY -5, -20) -2; $y--) {
        echo "\r\x1b[K"; // remove this line
        echo "\033[1A\033[K";
    }
}

function printStep(
    int $moveIndex,
    int $stepIndex,
    string $direction,
    array $knotPositions,
    array $visitedByTail,
    int $minY,
    int $maxY
): void {
    $matrix = [];
    // Prepare matrix
    for ($y = max($maxY +5, 20); $y >= min($minY -5, -20); $y--) {
        for ($x = -180; $x <= 40; $x++) {
            $matrix[$y][$x] = ' ';

            if ($y === 0) {
                $matrix[$y][$x] = '─';
            }

            if ($x === 0) {
                $matrix[$y][$x] = '|';
            }
        }
    }

    foreach ($visitedByTail as $coordinate => $times) {
        [$x, $y] = explode(',', $coordinate);
        $matrix[$y][$x] = '.';
    }

    // Place markers
    for ($i = ROPE_LENGTH - 1; $i >= 0; $i--) {
        $sign = $i === 0 ? 'H' : $i;
        $matrix[$knotPositions[$i][1]][$knotPositions[$i][0]] = $sign;
    }

    echo "Move #$moveIndex | Step #$stepIndex into $direction".PHP_EOL;

    foreach ($matrix as $index => $row) {
        echo str_pad($index, 4, ' ', STR_PAD_LEFT).'. '.implode($row).PHP_EOL;
    }

    echo '      ';
    for ($x = -180; $x <= 40; $x++) {
        echo ($x % 5 === 0) ? '|' : "'";
    }
    echo PHP_EOL;

    usleep(100000);
}

echo count($visitedByTail).PHP_EOL;
Gábor Iván