Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion airsenal/framework/data_fetcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,9 @@ def get_num_free_transfers(self, fpl_team_id=None):
Requires login
"""
squad_data = self.get_current_squad_data(fpl_team_id)
return squad_data["transfers"]["limit"]
return max(
0, squad_data["transfers"]["limit"] - squad_data["transfers"]["made"]
)

def get_current_bank(self, fpl_team_id=None):
"""
Expand Down
19 changes: 12 additions & 7 deletions airsenal/framework/optimization_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ def next_week_transfers(
"""Given a previous strategy and some optimisation constraints, determine the valid
options for the number of transfers (or chip played) in the following gameweek.

strat is a tuple (free_transfers, hit_so_far, strat_dict)
strat is a tuple (free_transfers, total_points_hits, strat_dict)
strat_dict must have key chips_played, which is a dict indexed by gameweek with
possible values None, "wildcard", "free_hit", "bench_boost" or triple_captain"

Expand All @@ -446,7 +446,9 @@ def next_week_transfers(
max_free_transfers - maximum number of free transfers saved in the game rules
(2 before 2024/25, 5 from 2024/25 season)

Returns (new_transfers, new_ft_available, new_points_hits) tuples.
Returns (new_transfers, new_ft_available, total_points_hits, hit_this_gw) tuples.
- total_points_hits is the total points hit so far including this gw
- hit_this_gw is the points hit incurred this gameweek
"""
# check that the 'chips' dict we are given makes sense:
if chips is None:
Expand Down Expand Up @@ -524,17 +526,20 @@ def next_week_transfers(
if allow_triple_captain:
new_transfers += [f"T{nt}" for nt in ft_choices]

new_points_hits = [
hit_so_far + calc_points_hit(nt, ft_available) for nt in new_transfers
]
hit_this_gw = [calc_points_hit(nt, ft_available) for nt in new_transfers]
total_points_hits = [hit_so_far + hit for hit in hit_this_gw]
new_ft_available = [
calc_free_transfers(nt, ft_available, max_free_transfers)
for nt in new_transfers
]

# return list of (num_transfers, free_transfers, hit_so_far) tuples for each new
# strategy
return list(zip(new_transfers, new_ft_available, new_points_hits, strict=False))
return list(
zip(
new_transfers, new_ft_available, total_points_hits, hit_this_gw, strict=True
)
)


def count_expected_outputs(
Expand Down Expand Up @@ -590,7 +595,7 @@ def count_expected_outputs(
max_free_transfers=max_free_transfers,
)

for n_transfers, new_free_transfers, new_hit in possibilities:
for n_transfers, new_free_transfers, new_hit, _ in possibilities:
# make a copy of the strategy up to this point, then add on this gw
new_dict = deepcopy(s[2])

Expand Down
39 changes: 25 additions & 14 deletions airsenal/scripts/fill_transfersuggestion_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
from airsenal.framework.optimization_transfers import make_best_transfers
from airsenal.framework.optimization_utils import (
MAX_FREE_TRANSFERS,
calc_points_hit,
check_tag_valid,
count_expected_outputs,
fill_suggestion_table,
Expand Down Expand Up @@ -131,7 +130,15 @@ def optimize(
else:
profiler = None

num_transfers, free_transfers, hit_so_far, squad, strat_dict, sid = status
(
num_transfers,
free_transfers,
hit_so_far,
hit_this_gw,
squad,
strat_dict,
sid,
) = status
# num_transfers will be 0, 1, 2, OR 'W' or 'F', OR 'T0', T1', 'T2',
# OR 'B0', 'B1', or 'B2' (the latter six represent triple captain or
# bench boost along with 0, 1, or 2 transfers).
Expand Down Expand Up @@ -203,18 +210,16 @@ def optimize(
(updater, increment, pid) if updater is not None else None,
)

points_hit = calc_points_hit(num_transfers, free_transfers)
discount_factor = get_discount_factor(root_gw, gw)
points -= points_hit * discount_factor
points -= hit_this_gw * discount_factor
strat_dict["total_score"] += points
strat_dict["points_per_gw"][gw] = points
strat_dict["free_transfers"][gw] = free_transfers
strat_dict["num_transfers"][gw] = num_transfers
strat_dict["points_hit"][gw] = points_hit
strat_dict["points_hit"][gw] = hit_this_gw
strat_dict["discount_factor"][gw] = discount_factor
strat_dict["players_in"][gw] = transfers["in"]
strat_dict["players_out"][gw] = transfers["out"]

depth += 1

if depth >= len(gameweek_range):
Expand All @@ -240,16 +245,15 @@ def optimize(
chips=chips_gw_dict[gw + 1],
max_free_transfers=max_free_transfers,
)

for strat in strategies:
# strat: (num_transfers, free_transfers, hit_so_far)
num_transfers, free_transfers, hit_so_far = strat
num_transfers, free_transfers, hit_so_far, hit_this_gw = strat

queue.put(
(
num_transfers,
free_transfers,
hit_so_far,
hit_this_gw,
new_squad,
strat_dict,
sid,
Expand Down Expand Up @@ -319,7 +323,8 @@ def print_strat(strat: dict) -> None:
print(" ===============================================")
for gw in gameweeks_as_int:
print(f"\n =========== Gameweek {gw} ================\n")
print(f"Chips played: {strat['chips_played'][str(gw)]}\n")
print(f"Chips played: {strat['chips_played'][str(gw)]}")
print(f"Points Hits: {strat['points_hit'][str(gw)]}\n")
print("Players in:\t\t\tPlayers out:")
print("-----------\t\t\t------------")
for i in range(len(strat["players_in"][str(gw)])):
Expand Down Expand Up @@ -382,15 +387,20 @@ def discord_payload(strat: dict, lineup: list[str]) -> dict:


def print_team_for_next_gw(
strat: dict, season: str = CURRENT_SEASON, fpl_team_id: int | None = None
strat: dict,
season: str = CURRENT_SEASON,
fpl_team_id: int | None = None,
use_api: bool = False,
) -> Squad:
"""
Display the team (inc. subs and captain) for the next gameweek
"""
gameweeks_as_str = strat["points_per_gw"].keys()
gameweeks_as_int = sorted([int(gw) for gw in gameweeks_as_str])
next_gw = gameweeks_as_int[0]
t = get_starting_squad(next_gw=next_gw, season=season, fpl_team_id=fpl_team_id)
t = get_starting_squad(
next_gw=next_gw, season=season, fpl_team_id=fpl_team_id, use_api=use_api
)
for pidout in strat["players_out"][str(next_gw)]:
t.remove_player(pidout)
for pidin in strat["players_in"][str(next_gw)]:
Expand Down Expand Up @@ -488,6 +498,7 @@ def run_optimization(
apifetcher=fetcher,
is_replay=is_replay,
)
print(f"Starting with {num_free_transfers} free transfers")

# create the output directory for temporary json files
# giving the points prediction for each strategy
Expand Down Expand Up @@ -591,7 +602,7 @@ def update_progress(increment=1, index=None):
processor.start()
procs.append(processor)
# add starting node to the queue
squeue.put((0, num_free_transfers, 0, starting_squad, {}, "starting"))
squeue.put((0, num_free_transfers, 0, 0, starting_squad, {}, "starting"))

for i, p in enumerate(procs):
progress_bars[i].close()
Expand Down Expand Up @@ -620,7 +631,7 @@ def update_progress(increment=1, index=None):
print(f"Best score: {best_strategy['total_score']}")
print_strat(best_strategy)
best_squad = print_team_for_next_gw(
best_strategy, season=season, fpl_team_id=fpl_team_id
best_strategy, season=season, fpl_team_id=fpl_team_id, use_api=use_api
)

# If a valid discord webhook URL has been stored
Expand Down
Loading