First off, I want to give credit to Martin Andersen and his awesome post from last year's F# Advent Calendar, without which, I wouldn't have found The EA SPORTS FUT Database which I use for this post.
What I'm going to do in this blog post, is create a simple program, which, given a valid football formation (442, 451, 352 etc) will query the EA SPORTS FUT Database and return the best 11 players given a set of player characteristics. While this isn't particularly tricky, it does cover a lot of the basic F# types and idioms such as Record Types, Discriminated Unions, Pattern Matching and Partial Application.
I have an F# file called
Domain.fs that has my Discriminated Union and Record Types which contains all the information about my players, team, possible formations and player positions:
The main logic of this application lives in a file called
FantasyFootball.fs. What we will do is work through the code in logical steps until we have all the pieces needed to put together the program.
I suppose the first step when deciding a fantasy football team is choosing the formation you would like. I'm quite a traditionalist when it comes to football, so for this blog post I'm going to pick
The first 2 functions that we'll look at are:
Firstly, we call the
pickTeam function passing in a string such as "442" which then uses pattern matching to determine which named case to pass to the
The reason the
pickTeamfunction is under the
findBestTeamfunction is that in F# functions have to be defined in code before they can be used.
Once we are in the
findBestTeam function, we just call the
createTeam function passing in the result of the
getPlayers function which gets all the players from the database and the formation picked by the user. The result of that is piped to the
printTeam function which is a function in
Helper.fs that just prints the team to the console.
The next step is to look at the
The first line of the
createTeam function pattern matches over the
pickedFormation which is passed in and then returns a tuple containing the number of players that are needed in each position which is deconstructed using a let binding. Once we have the number of players needed for each position we can call
getStartingPlayers to get the players best suited for our team (based on our chosen characteristics which we'll see later) and then we just return the
Team record type, which is then printed to the console using a method in our
getStartingPlayers function then takes in the players in the correct position, a function to sort the players by called
findBest and then the number of players to take:
The next part of code that we'll look through is in charge of actually finding the best players for each position,
The reason that I'm not using FSharp.Data and the JSON Type Provider is becuase this is a .NET Core project and Type Providers don't currently work in the FSI
The EA SPORTS FUT Database contains retired and classic players which they call "Icon players". As nice as it would be to include Pelé or Maradona into our fantasy team, the Icon players don't play much football nowadays so they wouldn't score us many points!
removeIconPlayers function in the section above is very simple and is just passed to
Array.filter to remove Icon players because they are given the club name 'Icons' by FIFA and the
removeDuplicatePlayers function is passed to
Array.distinctBy to remove duplicate players.
The next function:
getAllPlayersInPosition is the function that we are going to use partial application on. To do this we make
position:Position the first argument so that when we call the function with 1 argument (instead of the 2 that it's declared with) we get back a function that requires 1 argument
(players:JsonValue) but will use the implementation from
getAllPlayersInPosition. I know that can sound a bit much if you've never used partial application before, so I'd really recommend Scott Wlaschin's article to understand this fully.
The next function we'll take a look at is the
getPlayers function below that we use to call the EA SPORTS FUT Database API and retrieve the players:
getPlayers the first function we define is
getPage which makes an asynchronus call to The EA SPORTS FUT Database to retrieve 1 page of players which it then returns as an array (each page has 24 players). The next 2 lines work out how many pages in total are in the database and then the last expression actually calls the
getPage function the required amount of times in parallel and then concatenates the arrays and returns one array of all players.
As pointed out to me, I could do with throttling and/or batching these requests to the API. There is a library called FSharp.Control.AsyncSeq that I intend to add.
The other functions that we've touched on but not looked at yet are the functions that sum up the total of our players characteristics and determine the players that are 'picked' for our team (
findBestAttackers) which are found in
Helper.fs, one of which looks like this:
All these functions do is sum up the values of the player characteristics that we're interested in and return the total to be sorted by. You can change these characteristics by picking the values for each player that can be found in the database - but these are the characteristics that I chose that I value for each position. The rest of the values that I chose can be found in the
Helper.fs file: https://github.com/iwasdavid/fsharp-fantasy-football/blob/master/Helper.fs
For example if all you were worried about was having quick players, you could change all the of functions to be like this:
The functions below are just a few little functions to help with finding player positions and then the
createPlayerFromJson function takes a
JsonValue type and creates a record type of our
Player type so that it can be added to our
Team type which is what is eventually printed to the console.
Running the following program gives us the following results which are very interesting, as they were not the players that I would have expected to be returned!
GOALKEEPERS Name: Manuel Neuer. Team: FC Bayern München. Position: Goalkeeper. Rating: 91 DEFENDERS Name: Marcelo Vieira da Silva. Team: Real Madrid. Position: Left Back. Rating: 89 Name: Juan Francisco Torres Belén. Team: Atlético Madrid. Position: Right Back. Rating: 87 Name: Sergio Ramos García. Team: Real Madrid. Position: Centre Back. Rating: 92 Name: Alex Nicolao Telles. Team: FC Porto. Position: Left Back. Rating: 86 MIDFIELDERS Name: Luka Modric. Team: Real Madrid. Position: Centre Midfield. Rating: 92 Name: Kevin De Bruyne. Team: Manchester City. Position: Centre Atacking Midfielder. Rating: 92 Name: Radja Nainggolan. Team: Inter. Position: Centre Midfield. Rating: 86 Name: N'Golo Kanté. Team: Chelsea. Position: Centre Defensive Midfielder. Rating: 89 ATTACKERS Name: Lionel Messi. Team: FC Barcelona. Position: Centre Forward. Rating: 95 Name: Neymar da Silva Santos Jr.. Team: Paris Saint-Germain. Position: Left Forward. Rating: 93
And that is it. There's a lot of things I could do to improve and add to this script, but I hope it's been helpful for at least 1 person :)
Source code is available on Github: https://github.com/iwasdavid/fsharp-fantasy-football
Merry Christmas and a Happy New Year!