diff --git a/2015/day-14.php b/2015/day-14.php new file mode 100644 index 0000000..bf40d96 --- /dev/null +++ b/2015/day-14.php @@ -0,0 +1,64 @@ +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"); diff --git a/2015/inputs/day-14-sample.input b/2015/inputs/day-14-sample.input new file mode 100644 index 0000000..706ceae --- /dev/null +++ b/2015/inputs/day-14-sample.input @@ -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. diff --git a/2015/inputs/day-14.input b/2015/inputs/day-14.input new file mode 100644 index 0000000..2af1170 --- /dev/null +++ b/2015/inputs/day-14.input @@ -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. diff --git a/2015/src/ReindeerOlympics.php b/2015/src/ReindeerOlympics.php new file mode 100644 index 0000000..46a9c01 --- /dev/null +++ b/2015/src/ReindeerOlympics.php @@ -0,0 +1,174 @@ +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, + ]; + } +} diff --git a/2015/tests/ReindeerOlympicsTest.php b/2015/tests/ReindeerOlympicsTest.php new file mode 100644 index 0000000..fab0f0c --- /dev/null +++ b/2015/tests/ReindeerOlympicsTest.php @@ -0,0 +1,42 @@ +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); + } +} diff --git a/README.md b/README.md index 40a64d3..32df125 100644 --- a/README.md +++ b/README.md @@ -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 | | | | | | | | | |