Skip to content

Commit

Permalink
2015 Day 14 part 1 & 2
Browse files Browse the repository at this point in the history
  • Loading branch information
sprak3000 committed Oct 27, 2024
1 parent 11f865f commit cafcfcc
Show file tree
Hide file tree
Showing 6 changed files with 292 additions and 1 deletion.
64 changes: 64 additions & 0 deletions 2015/day-14.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

ini_set("memory_limit", "-1");

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

use adventofcode\Year2015\ReindeerOlympics;

/**
* --- Day 14: Reindeer Olympics ---
*
* This year is the Reindeer Olympics! Reindeer can fly at high speeds, but must rest occasionally to recover their
* energy. Santa would like to know which of his reindeer is fastest, and so he has them race.
*
* Reindeer can only either be flying (always at their top speed) or resting (not moving at all), and always spend
* whole seconds in either state.
*
* For example, suppose you have the following Reindeer:
*
* - Comet can fly 14 km/s for 10 seconds, but then must rest for 127 seconds.
* - Dancer can fly 16 km/s for 11 seconds, but then must rest for 162 seconds.
*
* After one second, Comet has gone 14 km, while Dancer has gone 16 km. After ten seconds, Comet has gone 140 km,
* while Dancer has gone 160 km. On the eleventh second, Comet begins resting (staying at 140 km), and Dancer
* continues on for a total distance of 176 km. On the 12th second, both reindeer are resting. They continue to
* rest until the 138th second, when Comet flies for another ten seconds. On the 174th second, Dancer flies
* for another 11 seconds.
*
* In this example, after the 1000th second, both reindeer are resting, and Comet is in the lead at 1120 km
* (poor Dancer has only gotten 1056 km by that point). So, in this situation, Comet would win
* (if the race ended at 1000 seconds).
*
* Given the descriptions of each reindeer (in your puzzle input), after exactly 2503 seconds, what distance
* has the winning reindeer traveled?
*/

$reindeerOlympics = new ReindeerOlympics();
$input = file(__DIR__ . '/inputs/day-14.input', FILE_IGNORE_NEW_LINES);
$result = $reindeerOlympics->determineFlightWinnerDistance($input, 2503);
print('The winning reindeer flew ' . $result . " kilometers.\n");

/**
* --- Part Two ---
*
* Seeing how reindeer move in bursts, Santa decides he's not pleased with the old scoring system.
*
* Instead, at the end of each second, he awards one point to the reindeer currently in the lead. (If there are
* multiple reindeer tied for the lead, they each get one point.) He keeps the traditional 2503 second time limit,
* of course, as doing otherwise would be entirely ridiculous.
*
* Given the example reindeer from above, after the first second, Dancer is in the lead and gets one point. He stays
* in the lead until several seconds into Comet's second burst: after the 140th second, Comet pulls into the lead
* and gets his first point. Of course, since Dancer had been in the lead for the 139 seconds before that, he has
* accumulated 139 points by the 140th second.
*
* After the 1000th second, Dancer has accumulated 689 points, while poor Comet, our old champion, only has 312.
* So, with the new scoring system, Dancer would win (if the race ended at 1000 seconds).
*
* Again given the descriptions of each reindeer (in your puzzle input), after exactly 2503 seconds, how many
* points does the winning reindeer have?
*/

$result = $reindeerOlympics->determineFlightWinnerPoints($input, 2503);
print('The winning reindeer accumulated ' . $result . " points.\n");
2 changes: 2 additions & 0 deletions 2015/inputs/day-14-sample.input
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Comet can fly 14 km/s for 10 seconds, but then must rest for 127 seconds.
Dancer can fly 16 km/s for 11 seconds, but then must rest for 162 seconds.
9 changes: 9 additions & 0 deletions 2015/inputs/day-14.input
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Vixen can fly 19 km/s for 7 seconds, but then must rest for 124 seconds.
Rudolph can fly 3 km/s for 15 seconds, but then must rest for 28 seconds.
Donner can fly 19 km/s for 9 seconds, but then must rest for 164 seconds.
Blitzen can fly 19 km/s for 9 seconds, but then must rest for 158 seconds.
Comet can fly 13 km/s for 7 seconds, but then must rest for 82 seconds.
Cupid can fly 25 km/s for 6 seconds, but then must rest for 145 seconds.
Dasher can fly 14 km/s for 3 seconds, but then must rest for 38 seconds.
Dancer can fly 3 km/s for 16 seconds, but then must rest for 37 seconds.
Prancer can fly 25 km/s for 6 seconds, but then must rest for 143 seconds.
174 changes: 174 additions & 0 deletions 2015/src/ReindeerOlympics.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<?php

namespace adventofcode\Year2015;

class ReindeerOlympics
{
/**
* Determine the maximum distance flown from a given set of reindeer.
*
* @param array $reindeerData
* @param int $interval
* @return float
*/
public function determineFlightWinnerDistance(array $reindeerData, int $interval): float
{
$winnersDistance = 0;

foreach ($reindeerData as $reindeer) {
$data = $this->parseReindeerData($reindeer);
$distance = $this->calculateDistance(
$data['speed'],
$data['flightTime'],
$data['oneSecondFlightDistance'],
$data['restDuration'],
$interval
);
if ($distance > $winnersDistance) {
$winnersDistance = $distance;
}
}

return $winnersDistance;
}

/**
* Determine the winning points from a given set of reindeer.
*
* @param array $reindeerData
* @param int $interval
* @return int
*/
public function determineFlightWinnerPoints(array $reindeerData, int $interval): int
{
$contestants = [];

foreach ($reindeerData as $reindeer) {
$data = $this->parseReindeerData($reindeer);
$contestants[] = $data;
}

for ($i = 0; $i < $interval; $i++) {
// Determine each reindeer's current distance.
foreach ($contestants as $index => $contestant) {
if (!$contestant['isResting']) {
$contestants[$index]['currentDistance'] += $contestant['speed'];

if ($contestants[$index]['currentLeg'] + 1 === $contestant['flightTime']) {
$contestants[$index]['isResting'] = true;
$contestants[$index]['currentLeg'] = 0;

continue;
}

$contestants[$index]['currentLeg']++;
}

if ($contestant['isResting'] && $contestant['currentRestTime'] + 1 === $contestant['restDuration']) {
$contestants[$index]['isResting'] = false;
$contestants[$index]['currentRestTime'] = 0;

continue;
}

if ($contestant['isResting']) {
$contestants[$index]['currentRestTime']++;
}
}

// Find the largest current distance
$largestDistance = 0;

foreach ($contestants as $contestant) {
if ($contestant['currentDistance'] > $largestDistance) {
$largestDistance = $contestant['currentDistance'];
}
}

// Update the scores for those in the lead.
foreach ($contestants as $index => $contestant) {
if ($contestant['currentDistance'] === $largestDistance) {
$contestants[$index]['currentPoints']++;
}
}
}

$winnersPoints = 0;

foreach ($contestants as $contestant) {
if ($contestant['currentPoints'] > $winnersPoints) {
$winnersPoints = $contestant['currentPoints'];
}
}

return $winnersPoints;
}

/**
* Find the distance covered over a given interval based on the given speed, flight time, and rest duration.
* All values are in seconds.
*
* @param int $speed
* @param int $flightTime
* @param float $oneSecondFlightDistance
* @param int $restDuration
* @param int $interval
* @return float
*/
protected function calculateDistance(
int $speed,
int $flightTime,
float $oneSecondFlightDistance,
int $restDuration,
int $interval
): float {
$distance = 0;

$maxFlightTime = $speed * $flightTime;

$currentTime = 0;
while ($currentTime < $interval) {
if ($maxFlightTime > $interval) {
$distance += $oneSecondFlightDistance * ($interval - $currentTime);
$currentTime = $interval;
continue;
}

if ($maxFlightTime + $restDuration > $interval) {
$distance += $maxFlightTime + ($interval - $currentTime);
$currentTime = $interval;
continue;
}

$distance += $maxFlightTime;
$currentTime += $flightTime + $restDuration;
}

return $distance;
}

/**
* Parse a string of reindeer flight data.
*
* @param string $reindeer
* @return array
*/
protected function parseReindeerData(string $reindeer): array
{
preg_match('/(.*) can fly (\d+) km\/s for (\d+).*for (\d+).*/', $reindeer, $matches);

return [
'name' => $matches[1],
'speed' => intval($matches[2]),
'flightTime' => intval($matches[3]),
'oneSecondFlightDistance' => $matches[2] / $matches[3],
'maxFlightTime' => $matches[2] * $matches[3],
'restDuration' => intval($matches[4]),
'currentDistance' => 0,
'currentPoints' => 0,
'currentLeg' => 0,
'isResting' => false,
'currentRestTime' => 0,
];
}
}
42 changes: 42 additions & 0 deletions 2015/tests/ReindeerOlympicsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace adventofcode\Year2015\Test;

use adventofcode\Year2015\ReindeerOlympics;
use PHPUnit\Framework\TestCase;

class ReindeerOlympicsTest extends TestCase
{
protected ReindeerOlympics $reindeerOlympics;
protected array $sampleInput;
protected array $input;

protected function setup(): void
{
$this->reindeerOlympics = new ReindeerOlympics();
$this->sampleInput = file(__DIR__ . '/../inputs/day-14-sample.input', FILE_IGNORE_NEW_LINES);
$this->input = file(__DIR__ . '/../inputs/day-14.input', FILE_IGNORE_NEW_LINES);
}

public function testDetermineFlightWinnerDistance()
{
// Day 14 sample input
$result = $this->reindeerOlympics->determineFlightWinnerDistance($this->sampleInput, 1000);
$this->assertEquals(1120, $result);

// Day 14 input
$result = $this->reindeerOlympics->determineFlightWinnerDistance($this->input, 2503);
$this->assertEquals(2660, $result);
}

public function testDetermineFlightWinnerPoints()
{
// Day 14 sample input
$result = $this->reindeerOlympics->determineFlightWinnerPoints($this->sampleInput, 1000);
$this->assertEquals(689, $result);

// Day 14 input
$result = $this->reindeerOlympics->determineFlightWinnerPoints($this->input, 2503);
$this->assertEquals(1256, $result);
}
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ of changes and trends in that community.
| Day 11 | | | | | | | | | |
| Day 12 | | | | | | | | | |
| Day 13 | | | | | | | | | |
| Day 14 | | | | | | | | | |
| Day 14 | | | | | | | | | :star: :star: |
| Day 15 | | | | | | | | | |
| Day 16 | | | | | | | | | |
| Day 17 | | | | | | | | | |
Expand Down

0 comments on commit cafcfcc

Please sign in to comment.