Debug and extend obstacle-run statistics classes
Company: Headway
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
## Problem: Obstacle course run statistics (debug + implement)
You are building software to track how long racers take to complete an obstacle course.
### Domain model
- A **Course** has:
- `title` (string)
- `obstacle_count` (int): number of obstacles in the course
- A **Run** represents one attempt on a Course:
- `obstacle_times`: list of per-obstacle completion times (seconds)
- `complete`: boolean that becomes `True` when `len(obstacle_times) == course.obstacle_count`
- `add_obstacle_time(t)`: appends a time for the next obstacle; throws if run is already complete
- `get_run_time()`: sum of the recorded times so far (for incomplete runs, sum of completed obstacles)
- A **RunCollection** stores many runs for the *same* course:
- `add_run(run)`: adds a Run; rejects runs from a different Course
- `personal_best()`: the minimum total time among **complete** runs
### Tasks
1. **Debugging:**
- There is a bug in the provided `RunCollection` implementation that causes an existing unit test to fail.
- Fix `RunCollection` so that the provided test suite passes.
2. **Implement `best_of_bests()` in `RunCollection`:**
- Definition: the fastest possible run time if everything went perfectly.
- Compute it by taking, for each obstacle index `i`, the minimum observed time for obstacle `i` across **all runs** that reached that obstacle (including incomplete runs), and summing these per-obstacle minima.
- Add (or update) a unit test verifying correctness.
3. **Implement `chance_of_personal_best(test_run)` using simulation:**
- Input: an **in-progress** `Run` (may have completed some obstacles but not all).
- Assume that for each remaining obstacle `i` not yet completed in `test_run`, the time to complete obstacle `i` is drawn **uniformly at random** from the set of historical times recorded for obstacle `i` in the run collection (including obstacle times from incomplete runs, as long as that obstacle time exists).
- Run **10,000 trials**:
- For each trial, sample times for the missing obstacles and add them to `test_run`’s current elapsed time.
- Count the fraction of trials where the completed simulated total time is **<=** the current `personal_best()`.
- Return that fraction as a float.
- Accuracy requirement: the returned value should be within **±0.02** of the true probability.
4. **Testing & code quality discussion:**
- What additional tests would you add (beyond the provided ones)?
- After implementing `best_of_bests()` and `chance_of_personal_best()`, what improvements would you make to the codebase (structure, naming, performance, reliability, determinism of tests, etc.)?
### Notes / edge cases to consider
- Some runs may be incomplete.
- `personal_best()` is defined only over complete runs.
- The run collection is assumed to contain runs for exactly one course.
- Sampling for obstacle `i` requires there to be at least one historical time recorded for that obstacle.
Overview: This question evaluates object-oriented design, debugging, algorithmic reasoning, and probabilistic simulation competencies by requiring fixes to class behavior, implementation of aggregate statistics, and a Monte Carlo-based probability estimator.
Community answers
Answer by UsualWinter
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Random;
public class Main {
public static void main(String[] args) {
// The course has three obstacles.
Course course = new Course("Training Course", 3);
RunCollection collection = new RunCollection(course);
// Three complete historical runs.
collection.addRun(new Run(course, Arrays.asList(10.0, 12.0, 9.0)));
collection.addRun(new Run(course, Arrays.asList(11.0, 10.0, 10.0)));
collection.addRun(new Run(course, Arrays.asList(9.0, 13.0, 8.0)));
// Two incomplete historical runs.
collection.addRun(new Run(course, Arrays.asList(8.0, 11.0)));
collection.addRun(new Run(course, Arrays.asList(12.0, 9.0)));
// Current run:
// Obstacle 1 = 10 seconds
// Obstacle 2 = 12 seconds
// Obstacle 3 has not been completed.
Run testRun = new Run(course, Arrays.asList(10.0, 12.0));
double personalBest = collection.personalBest();
double bestOfBests = collection.bestOfBests();
double chance = collection.chanceOfPersonalBest(testRun);
System.out.println("Personal best: " + personalBest);
System.out.println("Best of bests: " + bestOfBests);
System.out.println("Current test-run time: " + testRun.getRunTime());
System.out.printf(
"Chance of personal best: %.4f (%.2f%%)%n",
chance,
chance * 100
);
}
}
class Course {
private final String title;
private final int obstacleCount;
public Course(String title, int obstacleCount) {
this.title = title;
this.obstacleCount = obstacleCount;
}
publ