Multi-Agent Collision Simulator (Unicycle Kinematics)
Company: Applied
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question tests practical coding ability in robotics simulation, specifically implementing unicycle kinematics and discrete-time modeling via forward Euler integration. It evaluates numerical methods, geometric collision detection, and object-oriented design — competencies commonly assessed for robotics and systems software engineering roles.
Constraints
- 1 <= N <= 500
- dt = 0.01 s (fixed); T_max = 100.0 s, so at most 10,000 simulation steps
- Coordinates, speeds, and yaw rates are finite reals; radii are strictly positive
- Touching counts as a collision (use <=, compare squared distance to (r_i + r_j)^2)
- Pre-existing overlap at t = 0 must be reported as 'Collision occurred at 0.00 seconds'
- Clock time at step k is k * dt (multiply, do not accumulate, to avoid float drift)
Examples
Input: ([(0.0, 0.0, 0.0, 1.0, 0.0, 0.5), (9.15, 0.0, 3.14159265, 1.0, 0.0, 0.5)],)
Expected Output: Collision occurred at 4.08 seconds
Explanation: Two agents on the x-axis closing head-on at a combined 2 m/s. Centers start 9.15 m apart; radii sum to 1.0 m, so they touch after the gap closes by 8.15 m. At step 407 (t=4.07) the gap is 1.01 m (no), at step 408 (t=4.08) it is 0.99 m <= 1.0 m (collision).
Input: ([(0.0, 0.0, 0.0, 1.0, 0.0, 0.5), (0.5, 0.0, 0.0, 1.0, 0.0, 0.5)],)
Expected Output: Collision occurred at 0.00 seconds
Explanation: Centers are 0.5 m apart while radii sum to 1.0 m, so the agents already overlap at t=0. The pre-loop / step-0 check must report 0.00 seconds before any integration happens.
Input: ([(0.0, 0.0, 0.0, 1.0, 0.0, 0.5)],)
Expected Output: No collision
Explanation: A single agent has no pairs to check, so no collision can ever occur and the simulator runs to the horizon and returns 'No collision'.
Input: ([(0.0, 0.0, 0.0, 0.0, 0.0, 0.5), (10.0, 0.0, 0.0, 0.0, 0.0, 0.5)],)
Expected Output: No collision
Explanation: Both agents are stationary (v=0, omega=0) and stay 10 m apart, far beyond the 1.0 m touching distance, for the entire 100 s horizon.
Input: ([(0.0, 0.0, 0.0, 1.0, 0.0, 0.5), (3.0, 0.0, 3.14159265, 1.0, 0.0, 0.5)],)
Expected Output: Collision occurred at 1.01 seconds
Explanation: Head-on closing over a 3 m gap at 2 m/s with radii sum 1.0 m. The continuous contact time is 1.00 s, but the discrete Euler check first satisfies distance <= 1.0 m at step 101 (t=1.01) — illustrating that the stepwise simulation, not the analytic formula, decides the reported tick.
Input: ([(0.0, 0.0, 1.5707963267948966, 1.0, 0.0, 0.5), (0.0, 9.15, -1.5707963267948966, 1.0, 0.0, 0.5)],)
Expected Output: Collision occurred at 4.08 seconds
Explanation: Same geometry as the canonical example but rotated onto the y-axis (headings +pi/2 and -pi/2). Verifies that cos/sin integration handles non-axis-aligned headings: collision again at 4.08 s.
Input: ([(-5.0, 0.0, 0.0, 2.0, 0.0, 1.0), (5.0, 0.0, 3.14159265, 2.0, 0.0, 1.0)],)
Expected Output: Collision occurred at 2.01 seconds
Explanation: Faster agents (v=2) with larger radii (sum 2.0 m) closing a 10 m gap at 4 m/s. Analytic contact is 2.00 s; the discrete simulator first registers touching at step 201 (t=2.01).
Hints
- Check collisions BEFORE advancing, so step k = 0 catches a pre-existing overlap at t = 0.00. Loop k from 0 to max_steps inclusive: test the current configuration, return k * dt if any pair collides, otherwise advance every agent one Euler step.
- Avoid the sqrt: a pair collides when (dx*dx + dy*dy) <= (r_i + r_j)^2. This is exact for the '<=' (touching) rule and faster.
- Compute the reported time as k * dt, not by adding 0.01 repeatedly — accumulation drifts and can mis-round the formatted value. Advance all agents together using the SAME pre-step heading (forward Euler), then increment k.
- Because the simulation is discrete, the first step at which the touching condition holds can be one 0.01 s tick later than the continuous analytic contact time. Trust the stepwise check rather than a closed-form time.