Advent of Code: Day 14

Source

Part 1: Given a list of running patterns of the form Comet can fly 14 km/s for 10 seconds, but then must rest for 127 seconds., determine who will be in the lead after 2503 seconds.

simulation_time = int(sys.argv[1])
data = {}

for line in sys.stdin:
    name, speed, time, rest = re.match(
        r'(\w+) can fly (\d+) km/s for (\d+) seconds, but then must rest for (\d+) seconds.',
        line
    ).groups()

    data[name] = {
        'speed': int(speed),
        'running_time': int(time),
        'resting_time': int(rest),
        'distance': 0,
        'resting': False,
        'timer': int(time),
    }

for tick in range(1, simulation_time + 1):
    for key in data:
        if data[key]['timer'] == 0:
            data[key]['resting'] = not data[key]['resting']
            data[key]['timer'] = (
                data[key]['resting_time']
                if data[key]['resting']
                else data[key]['running_time']
            )

        if not data[key]['resting']:
            data[key]['distance'] += data[key]['speed']

        data[key]['timer'] -= 1

print(max(
    (data[key]['distance'], key)
    for key in data
))

Originally, I was going to use a priority queue for this one. That would have let me put each next event (switching from running to resting or vice versa) in the queue and always pull off the next one to happen. Unfortunately, that made the code path right around stopping a bit complicated, since if one was running at the moment the race ends, they only update partially.

Instead, we just have a simulation that once per second will update the state (when data[key]['timer'] == 0), the distance (when )not data[key]['resting']), and the timer. Relatively straight forward. The one remaining amusing part is the ordering of the two if statements. If they’re in the other order, you have an off-by-one problem where you should be running but have already changed state to resting.

Part 2: Run the same simulation, but after each round award one point to whoever is in first place (one to each in the case of ties). Determine who has the highest score.

For the most part, the code doesn’t change. Basically, we’re going to add one more field to each entry in data:

data[name] = {
      ...
      'score': 0,
  }

Then we can use the same code that we had earlier to find the highest distance. Unfortunately, we cannot just increment that one because of ties; instead we have to loop:

for tick in range(1, simulation_time + 1):
  ...

  max_distance, max_name = max((data[key]['distance'], key) for key in data)

  for key in data:
      if data[key]['distance'] == max_distance:
          data[key]['score'] += 1

Then at the end, we print out the maximum score rather than distance.