Found a simplification.
The code originally stored boxcar locations on two independent axes (x and y). This is too complicated and was causing bugs as I introduced acceleration and speed.
I was able to greatly simplify when I realized the train track is essentially a number line wrapped into a circle. I can store a car’s location with a single number on this line and then later “unwrap” the location based on where along the track it is.
This made for very clean code that leaves no room for bugs. The location is a single value, not some arbitrary pixel location.
Next: I want to look into making the “chug” sound match frequency with the train’s speed.