Choose the best F1 Fantasy team based on an optimization problem.

Peder Ward
CodeX
Published in
9 min readMar 30, 2022
Formel 1 in Malaysia 2010. By Morio. CC BY SA 3.0

Finally it’s that time of year again! This weekend Formula 1 kickstarted the season of 2022 with some exciting results at Gulf Air Bahrain Grand Prix. This also means that the first round of F1 Fantasy is finished.

For those of you unfamiliar with Formula 1, it is the highest class of international racing for open-wheel single-seater formula racing cars. A Formula 1 season consists of a series of races, known as Grands Prix, that take place worldwide. The number of races in one season may vary, and this year it is a record-breaking 23 races in F1. This year’s season consists of 10 constructors participating and each constructor has two drivers. The list below show the teams and their drivers.

Constructors and drivers:

  • Mercedes: Lewis Hamilton #44 George Russell #63
  • Red Bull: Max Verstappen #1 Sergio Perez #11
  • Ferrari: Charles Leclerc #16 Carlos Sainz #55
  • McLaren: Lando Norris #3 Daniel Ricciardo #4
  • Alpine: Fernando Alonso #14 Esteban Ocon #31
  • AlphaTauri: Pierre Gasly #10 Yuki Tsunoda #22
  • Aston Martin: Sebastian Vettel #5 Lance Stroll #18
  • Williams: Nicholas Latifi #6 Alex Albon #23
  • Alfa Romeo: Zhou Guanyu #24 Valtteri Bottas #77
  • Haas: Kevin Magnussen #20 Mick Schumacher #47

With the ongoing F1 season there is a game called F1 Fantasy that makes you choose a team with drivers and a constructor to compete with other Fantasy F1 players. Your selected drivers and constructor score points based on their performances in real-world Formula 1 sessions. Before the season starts you have to choose five drivers and one constructor within a budget of 100mill. When the season starts the team is locked. However, you are allowed to make some substitutions prior to the next race week. More about the rules for F1 Fantasy here. The way in which you can obtain points during one race weekend are shown in the points scoring table here. There are numerous ways your team can earn points, but mostly it is based upon the real-world Formula 1 point system. Moving on, let’s look at the optimization problem.

Photo by mikeshen from Unsplash

As described in the previous section you need to choose a team with five drivers and one constructor. You have a budget of 100 mill and each driver and constructor cost between 9–35 mill. You want to optimize the choosing in order to get the drivers and constructor that will obtain most points through the season. Sounds familiar? If you have done some optimization before you may have heard about the Knapsack problem. Wikipedia describes the Knapsack problem as:

The knapsack problem is a problem in combinatorial optimization: Given a set of items, each with a weight and a value, determine the number of each item to include in a collection so that the total weight is less than or equal to a given limit and the total value is as large as possible.

The most common problem being solved is the 0–1 knapsack problem, which restricts the number x_{i} of copies of each kind of item to zero or one. Given a set of n items numbered from 1 up to n, each with a weight w_{i} and a value v_{i}, along with a maximum weight capacity W,

Defining the 0–1 knapsack problem. [1]

Here x_{i} represents the number of instances of item i to include in the knapsack. Informally, the problem is to maximize the sum of the values of the items in the knapsack so that the sum of the weights is less than or equal to the knapsack’s capacity. [1]

Simplified, you want to pick items with the highest price without exceeding the weight constraint. Which item would you take to maximize the price and still have under or equal to 15kg in your bag? There are many algorithms to solve this problem.

Photo by Zyousefi. CC-BY-SA-4.0

Imagine the bag in the picture above as the budget limit in F1 Fantasy and each brick as a driver or constructor. In our optimization problem the ‘kg’ resembles the price on each driver or constructor and price($) will be replaced with points on each driver or constructor. The price and budget limit are known to us, but how can we be informed about the points prior to the season if the season has not yet begun. A possible solution would be to use the F1 results for 2021 as it is expected that the drivers or constructors haven’t changed too much since last year’s final results. If we include all the drivers one can choose in F1 Fantasy, their price and last season’s score, we would get the following list.

List of drivers. Code by author.

Before we look more into the problem it should be mentioned that there is a huge difference between the different cars in F1. In recent years, Mercedes has dominated the F1. There is no doubt that they have great drivers, but it is also well known that Mercede’s cars have been faster than their competitors. This is part of the competition, to have the best cars. Between seasons it is not unusual to change drivers. If we look at the list we can see that Russel has a high price considering the low score he had last season. This is because last season Russel was a driver for Williams where their cars are considered to be among the slowest cars in F1. This year Russel earned a seat as a Mercedes driver and therefore it is expected that Russel will be fighting at the top of the rankings. This describes the difference between the slowest and fastest cars in F1. For this reason, for some drivers, last season’s score may not reflect the predicted score for this season.

The scores missing from last season are highlighted with red question marks in the picture above. The reason why some drives don’t have points from last season is because they are new drivers in F1 this season or they have made a comeback from previous seasons. The following bullet list describes how I replaced some of the scores in the list to estimate this season’s score better. I would not describe myself as an F1 expert, so I might have made some unprecise conclusions. Solving this optimization problem however, would still require the same procedure.

  • Russel: Russel became the new driver for team Mercedes. Replace Russel’s score with the person who had Mercedes seat last year, Bottas.
  • Hulkenberg: Hulkenberg is replacing Vettel in the first races due to Covid-19. Set Hulkenberg’s score as Vettel’s last season score.
  • Bottas: Bottas got a new seat at Alfa Romeo. Set Bottas’ score as last year’s driver for Alfa Romeo, Kimi Räikkönen.
  • Guanyu: Guanyu is a new driver joining Alfa Romeo. Set his score as the other driver from last season at Alfa Romeo, Antonio Giovinazzi.
  • Albon: Albon has a comeback to F1 for the Williams team. Albon was earlier driving for Red Bull, which is considered a faster car. Will therefore not use Albons score as a Red Bull driver. Set his score as the last year driver at Williams, Russel.
  • Magnussen: Magnussen also has a comeback to F1 this season as a driver for Haas. Magnussen did also drive for Haas season 2020. Therefore I will use Magnussen’s score from 2020.

After replacing and inserting new scores, the list now looks like the picture below. I also inserted the constructors and their score from last season.

Drivers and constructors and the score from last season. Code by author.

If we now go back to the problem we can see some differences by comparing this problem with the original Knapsack problem. First of all in the original Knapsack problem you can choose just one item, if this item gives the highest price and is under the weight constraint. In this problem we need to choose a total of six items (drivers and constructor), but we can only choose five drivers and one constructor. This indicates that we have a multiple constrained knapsack problem. Also, some Knapsack problems allow you to take one item multiple times, but this is not allowed in our problem.

To solve this multi-constrained problem we will use the CP-SAT solver given by OR-Tools which is an open source software for combinatorial optimization. CP-SAT solver is a constraint programming solver that uses SAT (satisfiability) methods. [2]

First we need to declare the number of items. In this case, the number of drivers and constructors. We also define the first constraints:

# Preprocessing 
capacity = 6
budget = 100 #mill
n_drivers_constructors = len(set([entry['name'] for entry in drivers_constructors]))

Now it is time to define the model we are going to use to solve this problem.

# Define model
model = cp_model.CpModel()
x_select = [model.NewBoolVar('') for i in range(n_drivers_constructors)]

After defining the model we have to set the constraints. Since some of the values have decimals and this solver only allows problems with integer numbers we multiply values by 10.

# Capacity constrain
model.Add(sum(x_select) == capacity)
# Budget constrain
model.Add(sum([x_select[i] * int(round(drivers_constructors[i]['price']*10)) for i in range(n_drivers_constructors)]) <= budget * 10)
# Constructor constrain
model.Add(sum([x_select[i] for i in range(n_drivers_constructors) if drivers_constructors[i]['type'] == 'constructors']) == 1)

Before we can solve the problem, we have to let the model know which variable we want to maximize.

# Maximize sum of scores selected model.Maximize(sum([x_select[i] * int(round(drivers_constructors[i]['score']*10)) for i in range(n_drivers_constructors)]))

Then it is time to solve the problem. We also need to scale back the values. The last lines help us print some results.

#Solve the problemsolver = cp_model.CpSolver()
solver.parameters.log_search_progress = True
model.Proto().objective.scaling_factor = -1./10
status = solver.Solve(model)
if status == cp_model.OPTIMAL:
selected = [i for i in range(n_drivers_constructors) if solver.Value(x_select[i]) == 1]
print("\n".join([str(drivers_constructors[i]) for i in selected]))

If we take a look at the results it looks like it is going to be a RedBull team. The solver also prints status: OPTIMAL. This means an optimal feasible solution was found. If we sum the price tags from this team we get 100 mill. By optimize after points from last year it looks like it is better to have a combination of the best and worst drivers (with car), than a whole team with mid good drivers.

Results:
{'name': 'Verstappen', 'type': 'driver', 'price': 30.5, 'score': 395.5}
{'name': 'Perez', 'type': 'driver', 'price': 17.5, 'score': 190}{'name': 'Albon', 'type': 'driver', 'price': 7.5, 'score': 16}{'name': 'Schumacker', 'type': 'driver', 'price': 6.5, 'score': 0}{'name': 'Magnussen', 'type': 'driver', 'price': 5.5, 'score': 1}{'name': 'RED BULL RACING HONDA', 'type': 'constructors', 'price': 32.5, 'score': 585.5}

Conclusion

This article demonstrates that it is possible to find the best F1 Fantasy team based on last season points by defining the problem as an optimization problem. As previously mentioned, the point system in F1 Fantasy is not entirely based on the points in real-world F1. For instance, you get extra points for streaks and negative points for disqualification. This was not taken into account in this problem. After some races this solution will be outdated, but you can always solve it again with the same method using some other variables instead of last season points.

LinkedIn: https://www.linkedin.com/in/peder-ward-158b87b4/

Github: https://github.com/pederw455/Formula-1-Fantasy-team-optimization

Picture from Piqsels.

[1] Wikipedia — Knapsack problem

[2] Google OR-Tools — CP-SAT Solver

--

--

Peder Ward
CodeX
Writer for

Data Scientist, MSc Cybernetics and robotics. LinkedIn Peder Ward