-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathadvent14.py
128 lines (103 loc) · 4.1 KB
/
advent14.py
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
import re
class Reindeer(object):
def __init__(self, name, speed, travel_time, rest_time):
self.name = name
self.speed = speed
self.travel_time = travel_time
self.rest_time = rest_time
self.reset()
def __eq__(self, other):
if not isinstance(other, Reindeer):
return NotImplemented
return self.name == other.name
def __hash__(self):
return hash(self.name)
def distance_travelled(self, total_time):
periods = total_time // (self.travel_time + self.rest_time)
travelled = min(
total_time - (self.travel_time + self.rest_time) * periods,
self.travel_time) + periods * self.travel_time
return self.speed * travelled
def reset(self):
self.distance = 0
self.score = 0
self.running = True
self.duration = self.travel_time
def tick(self):
if self.running:
self.distance += self.speed
self.duration -= 1
if self.duration == 0:
self.running = not self.running
self.duration = (
self.travel_time if self.running else self.rest_time)
def run_race(reindeer, race_duration):
furthest_distance = 0
reindeer_in_lead = set()
datapoints = {r.name: [] for r in reindeer}
for second in range(race_duration):
for racing_reindeer in reindeer:
racing_reindeer.tick()
if racing_reindeer.distance > furthest_distance:
furthest_distance = racing_reindeer.distance
reindeer_in_lead = {racing_reindeer}
elif racing_reindeer.distance == furthest_distance:
reindeer_in_lead.add(racing_reindeer)
for leading_reindeer in reindeer_in_lead:
leading_reindeer.score += 1
for racing_reindeer in reindeer:
datapoints[racing_reindeer.name].append((
racing_reindeer.distance, racing_reindeer.score))
return max(r.score for r in reindeer), datapoints
def read_file(fileobj):
line_pattern = re.compile(
r'(\w+) can fly (\d+) km/s for (\d+) seconds, '
r'but then must rest for (\d+) seconds.')
return [
Reindeer(name, int(speed), int(tt), int(rt))
for line in fileobj if line.strip()
for name, speed, tt, rt in (line_pattern.search(line).groups(),)
]
def plot_race(datapoints, max_distance, max_score, filename):
import matplotlib.pyplot as plt
plt.ioff()
colours = [
'#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#c49c94',
'#f7b6d2', '#c7c7c7', '#bcbd22']
fig, ax = plt.subplots(1, 1, figsize=(14, 12))
ax.spines['top'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.get_xaxis().tick_bottom()
ax.get_yaxis().tick_left()
# plt.xlim(0, max_score)
# plt.ylim(0, max_distance)
plt.xticks(range(0, 1000, 200), fontsize=14)
plt.yticks(range(0, 3000, 500), fontsize=14)
for colour, (reindeer, data) in zip(colours, datapoints.items()):
plt.plot(
[distance for distance, score in data],
[score for distance, score in data],
lw=2.5, color=colour)
y_pos = data[-1][0] - 0.5 # at same height as score
plt.text(max_distance, y_pos, reindeer, fontsize=14,
color=colour)
plt.savefig(filename, bbox_inches='tight')
if __name__ == '__main__':
import sys
import os.path
filename = sys.argv[-2]
race_duration = int(sys.argv[-1])
with open(filename) as inf:
reindeer = read_file(inf)
max_distance = max(r.distance_travelled(race_duration)
for r in reindeer)
print('Part 1:', max_distance)
max_score, datapoints = run_race(reindeer, race_duration)
print('Part 2:', max_score)
if '--graph' in sys.argv:
dirname, basename = os.path.split(filename)
output = os.path.join(dirname, os.path.splitext(basename)[0] + '.png')
plot_race(datapoints, max_distance, max_score, output)
print('Saved graph to', output)