diff --git a/2015/14/14.md b/2015/14/14.md new file mode 100644 index 0000000..4ed847d --- /dev/null +++ b/2015/14/14.md @@ -0,0 +1,73 @@ +## \-\-- 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*? + +Your puzzle answer was `2660`. + +## \-\-- Part Two \-\-- {#part2} + +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]{title="It also risks choosing a duration that isn't coprime with the cycle times of each reindeer."}. + +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*? + +Your puzzle answer was `1256`. + +Both parts of this puzzle are complete! They provide two gold stars: +\*\* + +At this point, you should [return to your Advent calendar](/2015) and +try another puzzle. + +If you still want to see it, you can [get your puzzle +input](14/input). + diff --git a/2015/14/solution.py b/2015/14/solution.py new file mode 100644 index 0000000..49688dd --- /dev/null +++ b/2015/14/solution.py @@ -0,0 +1,92 @@ +#!/bin/python3 +import sys,time,re +from pprint import pprint +sys.path.insert(0, '../../') +from fred import list2int,get_re,nprint,lprint,loadFile +start_time = time.time() + +input_f = 'input' + +def loadRaindeers(input_f): + """Load raindeer info from file into a dict + + Args: + input_f (str): filename + + Returns: + dict: Returns a dict of raindeer into + """ + raindeers = {} + with open(input_f) as file: + for line in file: + match = get_re(r"^(\w+).*fly (\d+).*for (\d+).* for (\d+) seconds.$",line.rstrip()) + raindeers[match.group(1)] = { + 'speed': int(match.group(2)), + 'time': int(match.group(3)), + 'rest': int(match.group(4)), + 'distance' : 0, + 'score': 0 + } + return raindeers + +######################################### +# # +# Part 1 # +# # +######################################### +def part1(): + raindeers = loadRaindeers(input_f) + print(raindeers) + seconds = 2503 + max_dist = 0 + for r in raindeers: + speed = raindeers[r]['speed'] + time = raindeers[r]['time'] + rest = raindeers[r]['rest'] + + cycle = (time+rest) + number_cycles = seconds//cycle + remainder = seconds%cycle + distance = speed * (number_cycles*time+min(remainder,time)) + if distance > max_dist: + max_dist = distance + return max_dist + +start_time = time.time() +print('Part 1:',part1(), '\t\t', round((time.time() - start_time)*1000), 'ms') + + +######################################### +# # +# Part 2 # +# # +######################################### +def part2(): + raindeers = loadRaindeers(input_f) + + seconds = 2503 + for i in range(1,seconds+1): + + for r in raindeers: + speed = raindeers[r]['speed'] + time = raindeers[r]['time'] + rest = raindeers[r]['rest'] + + cycle = (time+rest) + + # Cycle 0 to time+rest, but distance is only updated while + # the cycle is less than time. + if i % cycle <= time and i % cycle != 0: + raindeers[r]['distance'] += speed + + current_winner = max(raindeers[r]['distance'] for r in raindeers) + ties = [k for k, r in raindeers.items() if r['distance'] == current_winner] + + for t in ties: + + raindeers[t]['score'] += 1 + + return max(raindeers[r]['score'] for r in raindeers) + +start_time = time.time() +print('Part 2:',part2(), '\t\t', round((time.time() - start_time)*1000), 'ms') \ No newline at end of file