def print_team_for_next_gw(strat): """ Display the team (inc. subs and captain) for the next gameweek """ t = get_starting_squad() 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] for pidout in strat["players_out"][str(next_gw)]: t.remove_player(pidout, gameweek=next_gw) for pidin in strat["players_in"][str(next_gw)]: t.add_player(pidin, gameweek=next_gw) tag = get_latest_prediction_tag() expected_points = t.get_expected_points(next_gw, tag) print(t)
def get_sell_price(team_id, player_id): squad = get_starting_squad(team_id) for p in squad.players: if p.player_id == player_id: return squad.get_sell_price_for_player(p)
def run_optimization( gameweeks, tag, season=CURRENT_SEASON, fpl_team_id=None, chip_gameweeks={}, num_free_transfers=None, max_total_hit=None, allow_unused_transfers=True, max_transfers=2, num_iterations=100, num_thread=4, profile=False, ): """ This is the actual main function that sets up the multiprocessing and calls the optimize function for every num_transfers/gameweek combination, to find the best strategy. The chip-related variables e.g. wildcard_week are -1 if that chip is not to be played, 0 for 'play it any week', or the gw in which it should be played. """ discord_webhook = fetcher.DISCORD_WEBHOOK if fpl_team_id is None: fpl_team_id = fetcher.FPL_TEAM_ID # give the user the option to login fetcher.login() print("Running optimization with fpl_team_id {}".format(fpl_team_id)) # How many free transfers are we starting with? if not num_free_transfers: num_free_transfers = get_free_transfers(fpl_team_id, gameweeks[0], apifetcher=fetcher) # create the output directory for temporary json files # giving the points prediction for each strategy shutil.rmtree(OUTPUT_DIR, ignore_errors=True) os.makedirs(OUTPUT_DIR, exist_ok=True) # first get a baseline prediction # baseline_score, baseline_dict = get_baseline_prediction(num_weeks_ahead, tag) # Get a dict of what chips we definitely or possibly will play # in each gw chip_gw_dict = construct_chip_dict(gameweeks, chip_gameweeks) # create a queue that we will add nodes to, and some processes to take # things off it squeue = CustomQueue() procs = [] # create one progress bar for each thread progress_bars = [] for i in range(num_thread): progress_bars.append(tqdm(total=100)) # number of nodes in tree will be something like 3^num_weeks unless we allow # a "chip" such as wildcard or free hit, in which case it gets complicated num_weeks = len(gameweeks) num_expected_outputs = count_expected_outputs( num_weeks, next_gw=gameweeks[0], free_transfers=num_free_transfers, max_total_hit=max_total_hit, allow_unused_transfers=allow_unused_transfers, max_transfers=max_transfers, chip_gw_dict=chip_gw_dict, ) total_progress = tqdm(total=num_expected_outputs, desc="Total progress") # functions to be passed to subprocess to update or reset progress bars def reset_progress(index, strategy_string): if strategy_string == "DONE": progress_bars[index].close() else: progress_bars[index].n = 0 progress_bars[index].desc = "strategy: " + strategy_string progress_bars[index].refresh() def update_progress(increment=1, index=None): if index is None: # outer progress bar nfiles = len(os.listdir(OUTPUT_DIR)) total_progress.n = nfiles total_progress.refresh() if nfiles == num_expected_outputs: total_progress.close() for pb in progress_bars: pb.close() else: progress_bars[index].update(increment) progress_bars[index].refresh() use_api = fetcher.logged_in starting_squad = get_starting_squad(fpl_team_id=fpl_team_id, use_api=use_api, apifetcher=fetcher) if not allow_unused_transfers and (num_weeks > 1 or (num_weeks == 1 and num_free_transfers == 2)): # if we are excluding unused transfers the tree may not include the baseline # strategy. In those cases quickly calculate and save it here first. save_baseline_score(starting_squad, gameweeks, tag, season=season) update_progress() # Add Processes to run the the target 'optimize' function. # This target function needs to know: # num_transfers # current_team (list of player_ids) # transfer_dict {"gw":<gw>,"in":[],"out":[]} # total_score # num_free_transfers # budget for i in range(num_thread): processor = Process( target=optimize, args=( squeue, i, num_expected_outputs, gameweeks, season, tag, chip_gw_dict, max_total_hit, allow_unused_transfers, max_transfers, num_iterations, update_progress, reset_progress, profile, ), ) processor.daemon = True processor.start() procs.append(processor) # add starting node to the queue squeue.put((0, num_free_transfers, 0, starting_squad, {}, "starting")) for i, p in enumerate(procs): progress_bars[i].close() progress_bars[i] = None p.join() # find the best from all the strategies tried best_strategy = find_best_strat_from_json(tag) baseline_score = find_baseline_score_from_json(tag, num_weeks) fill_suggestion_table(baseline_score, best_strategy, season, fpl_team_id) for i in range(len(procs)): print("\n") print("\n====================================\n") print("Strategy for Team ID: {}".format(fpl_team_id)) print("Baseline score: {}".format(baseline_score)) print("Best score: {}".format(best_strategy["total_score"])) print_strat(best_strategy) t = print_team_for_next_gw(best_strategy, fpl_team_id) # If a valid discord webhook URL has been stored # in env variables, send a webhook message if discord_webhook != "MISSING_ID": # Use regex to check the discord webhook url is correctly formatted if re.match( r"^.*(discord|discordapp)\.com\/api\/webhooks\/([\d]+)\/([a-zA-Z0-9_-]+)$", discord_webhook, ): # create a formatted team lineup message for the discord webhook lineup_strings = [ "__Strategy for Team ID: **{}**__".format(fpl_team_id), "Baseline score: *{}*".format(int(baseline_score)), "Best score: *{}*".format(int(best_strategy["total_score"])), "\n__starting 11__", ] for position in ["GK", "DEF", "MID", "FWD"]: lineup_strings.append("== **{}** ==\n```".format(position)) for p in t.players: if p.position == position and p.is_starting: player_line = "{} ({})".format(p.name, p.team) if p.is_captain: player_line += "(C)" elif p.is_vice_captain: player_line += "(VC)" lineup_strings.append(player_line) lineup_strings.append("```\n") lineup_strings.append("__subs__") lineup_strings.append("```") subs = [p for p in t.players if not p.is_starting] subs.sort(key=lambda p: p.sub_position) for p in subs: lineup_strings.append("{} ({})".format(p.name, p.team)) lineup_strings.append("```\n") # generate a discord embed json and send to webhook payload = discord_payload(best_strategy, lineup_strings) result = requests.post(discord_webhook, json=payload) if 200 <= result.status_code < 300: print( f"Discord webhook sent, status code: {result.status_code}") else: print( f"Not sent with {result.status_code}, response:\n{result.json()}" ) else: print("Warning: Discord webhook url is malformed!\n", discord_webhook) shutil.rmtree(OUTPUT_DIR, ignore_errors=True) return
def run_optimization( gameweeks, tag, season=CURRENT_SEASON, fpl_team_id=None, chip_gameweeks={}, num_free_transfers=None, max_total_hit=None, allow_unused_transfers=True, max_transfers=2, num_iterations=100, num_thread=4, profile=False, ): """ This is the actual main function that sets up the multiprocessing and calls the optimize function for every num_transfers/gameweek combination, to find the best strategy. The chip-related variables e.g. wildcard_week are -1 if that chip is not to be played, 0 for 'play it any week', or the gw in which it should be played. """ if fpl_team_id is None: fpl_team_id = fetcher.FPL_TEAM_ID print("Running optimization with fpl_team_id {}".format(fpl_team_id)) # How many free transfers are we starting with? if not num_free_transfers: num_free_transfers = get_free_transfers(gameweeks[0], fpl_team_id) # create the output directory for temporary json files # giving the points prediction for each strategy shutil.rmtree(OUTPUT_DIR, ignore_errors=True) os.makedirs(OUTPUT_DIR, exist_ok=True) # first get a baseline prediction # baseline_score, baseline_dict = get_baseline_prediction(num_weeks_ahead, tag) # Get a dict of what chips we definitely or possibly will play # in each gw chip_gw_dict = construct_chip_dict(gameweeks, chip_gameweeks) # create a queue that we will add nodes to, and some processes to take # things off it squeue = CustomQueue() procs = [] # create one progress bar for each thread progress_bars = [] for i in range(num_thread): progress_bars.append(tqdm(total=100)) # number of nodes in tree will be something like 3^num_weeks unless we allow # a "chip" such as wildcard or free hit, in which case it gets complicated num_weeks = len(gameweeks) num_expected_outputs = count_expected_outputs( num_weeks, next_gw=gameweeks[0], free_transfers=num_free_transfers, max_total_hit=max_total_hit, allow_unused_transfers=allow_unused_transfers, max_transfers=max_transfers, chip_gw_dict=chip_gw_dict, ) total_progress = tqdm(total=num_expected_outputs, desc="Total progress") # functions to be passed to subprocess to update or reset progress bars def reset_progress(index, strategy_string): if strategy_string == "DONE": progress_bars[index].close() else: progress_bars[index].n = 0 progress_bars[index].desc = "strategy: " + strategy_string progress_bars[index].refresh() def update_progress(increment=1, index=None): if index is None: # outer progress bar nfiles = len(os.listdir(OUTPUT_DIR)) total_progress.n = nfiles total_progress.refresh() if nfiles == num_expected_outputs: total_progress.close() for pb in progress_bars: pb.close() else: progress_bars[index].update(increment) progress_bars[index].refresh() starting_squad = get_starting_squad(fpl_team_id=fpl_team_id) if not allow_unused_transfers and ( num_weeks > 1 or (num_weeks == 1 and num_free_transfers == 2) ): # if we are excluding unused transfers the tree may not include the baseline # strategy. In those cases quickly calculate and save it here first. save_baseline_score(starting_squad, gameweeks, tag, season=season) update_progress() # Add Processes to run the the target 'optimize' function. # This target function needs to know: # num_transfers # current_team (list of player_ids) # transfer_dict {"gw":<gw>,"in":[],"out":[]} # total_score # num_free_transfers # budget for i in range(num_thread): processor = Process( target=optimize, args=( squeue, i, num_expected_outputs, gameweeks, season, tag, chip_gw_dict, max_total_hit, allow_unused_transfers, max_transfers, num_iterations, update_progress, reset_progress, profile, ), ) processor.daemon = True processor.start() procs.append(processor) # add starting node to the queue squeue.put((0, num_free_transfers, 0, starting_squad, {}, "starting")) for i, p in enumerate(procs): progress_bars[i].close() progress_bars[i] = None p.join() # find the best from all the strategies tried best_strategy = find_best_strat_from_json(tag) baseline_score = find_baseline_score_from_json(tag, num_weeks) fill_suggestion_table(baseline_score, best_strategy, season, fpl_team_id) for i in range(len(procs)): print("\n") print("\n====================================\n") print("Strategy for Team ID: {}".format(fpl_team_id)) print("Baseline score: {}".format(baseline_score)) print("Best score: {}".format(best_strategy["total_score"])) print_strat(best_strategy) print_team_for_next_gw(best_strategy, fpl_team_id) shutil.rmtree(OUTPUT_DIR, ignore_errors=True) return
def run_optimization( gameweeks, tag, season=CURRENT_SEASON, wildcard=False, free_hit=False, triple_captain=False, bench_boost=False, num_free_transfers=None, num_iterations=100, num_thread=4, profile=False, ): """ This is the actual main function that sets up the multiprocessing and calls the optimize function for every num_transfers/gameweek combination, to find the best strategy. """ ## How many free transfers are we starting with? if not num_free_transfers: num_free_transfers = get_free_transfers(gameweeks[0]) ## create the output directory for temporary json files ## giving the points prediction for each strategy shutil.rmtree(OUTPUT_DIR, ignore_errors=True) os.makedirs(OUTPUT_DIR, exist_ok=True) ## first get a baseline prediction # baseline_score, baseline_dict = get_baseline_prediction(num_weeks_ahead, tag) ## create a queue that we will add nodes to, and some processes to take ## things off it squeue = CustomQueue() procs = [] ## create one progress bar for each thread progress_bars = [] for i in range(num_thread): progress_bars.append(tqdm(total=100)) ## number of nodes in tree will be something like 3^num_weeks unless we allow ## a "card" such as wildcard or free hit, in which case it gets complicated num_weeks = len(gameweeks) expected_num = count_expected_outputs(0, num_weeks, wildcard, free_hit, triple_captain, bench_boost) total_progress = tqdm(total=expected_num, desc="Total progress") ## functions to be passed to subprocess to update or reset progress bars def reset_progress(index, strategy_string): if strategy_string == "DONE": progress_bars[index].close() else: progress_bars[index].n = 0 progress_bars[index].desc = "strategy: " + strategy_string progress_bars[index].refresh() def update_progress(increment=1, index=None): if index == None: ## outer progress bar nfiles = len(os.listdir(OUTPUT_DIR)) total_progress.n = nfiles total_progress.refresh() if nfiles == expected_num: total_progress.close() for pb in progress_bars: pb.close() else: progress_bars[index].update(increment) progress_bars[index].refresh() ## Add Processes to run the the target 'optimize' function. ## This target function needs to know: ## num_transfers ## current_team (list of player_ids) ## transfer_dict {"gw":<gw>,"in":[],"out":[]} ## total_score ## num_free_transfers ## budget cards = [] if wildcard: cards.append("wildcard") if free_hit: cards.append("free_hit") if triple_captain: cards.append("triple_captain") if bench_boost: cards.append("bench_boost") for i in range(num_thread): processor = Process( target=optimize, args=( squeue, i, gameweeks, season, tag, cards, num_iterations, update_progress, reset_progress, profile, ), ) processor.daemon = True processor.start() procs.append(processor) ## add starting node to the queue starting_squad = get_starting_squad() squeue.put((0, num_free_transfers, starting_squad, {}, "starting")) for i, p in enumerate(procs): progress_bars[i].close() progress_bars[i] = None p.join() ### find the best from all the strategies tried best_strategy = find_best_strat_from_json(tag) baseline_score = find_baseline_score_from_json(tag, num_weeks) fill_suggestion_table(baseline_score, best_strategy, season) for i in range(len(procs)): print("\n") print("\n====================================\n") print("Baseline score: {}".format(baseline_score)) print("Best score: {}".format(best_strategy["total_score"])) print_strat(best_strategy) print_team_for_next_gw(best_strategy) shutil.rmtree(OUTPUT_DIR, ignore_errors=True) return