Script to read saved setup files from Motorsport Manager and calculate the optimum setup based on these
A toy project for up-skilling Python - finds the optimal setup for a driver's race weekend in the PC game Motorsport Manager.
Once I had played the setup minigame a few times, I could clearly see the rule mechanics behind it, which spoiled my enjoyment:
The game pre-determines the ideal setup for a driver's weekend. This ideal setup is
made up of 3 aspects, each of which is a float between -1
and 1
:
Downforce
Handling
Speed Balance
To get each of these 3 numbers as close as possible to their ideal target, the player uses 6 input sliders. Each of these sliders represents a setting for a different car component:
Front Wing
Rear Wing
Pressure
Camber
Gearing
Suspension
The effect of each slider on Downforce
/Handling
/Speed Balance
varies in direction
and intensity. E.g. Pressure
has no effect on Downforce
, but increasing Pressure
will slightly decrease the Speed Balance
number. Each slider also moves in different
increments. See components.yml
for the more details.
Knowing the above, I now know that there are over 300 million combinations of settings, each of which producing a different combination of the 3 aspects. With the different setting affects and increments, this is designed to provide some fun trying to reach the best compromise. For me, knowing that there was one answer that is closer to ideal than all the others, and that this could be predicted numerically, just became a source of frustration.
Enumerating all 300 million possible combinations - with nested loops or using
itertools
- took too much
computation and was therefore unworkably slow (the setup minigame occurs multiple
times during one play session).
I explored using NumPy, but this needed to represent all 300 million combinations at once and therefore hit memory limitations. Since NumPy does not label dimensions, I also found it very difficult to work out the correct incantations to link aspect combinations to their original settings.
Learning to use [Dask Array}(https://docs.dask.org/en/stable/array.html) potentially solved the memory problem, but the difficulty with unlabelled arrays remained.
The solution finally became apparent when I attended my first
SciPy conference (2020, virtual attendance), which
included a workshop on learning Xarray.
Xarray is all about labelled arrays, and can operate via Dask. It was very intuitive
for this self-contained problem, and I was able to put together the solution that
evening. I could represent each combination of settings as a 6-dimensional Dataset
of the difference from the ideal, 3 times over - once for each aspect. I could then
look for the minimum value across all 3, and access the settings as the dimension
labels. See purple.py
. On a mid-range laptop the operation takes less
than 30 seconds - well within a tolerable wait.