def test_count_expected_outputs_no_chips_zero_hit():
    count = count_expected_outputs(
        3,
        free_transfers=1,
        max_total_hit=0,
        next_gw=1,
        max_transfers=2,
        chip_gw_dict={},
    )
    assert count == 13
def test_count_expected_outputs_no_chips_2ft_no_unused():
    count = count_expected_outputs(
        3,
        free_transfers=2,
        max_total_hit=None,
        allow_unused_transfers=False,
        next_gw=1,
        max_transfers=2,
    )
    assert count == 14
def test_count_expected_outputs_no_chips_no_constraints():
    # No constraints or chips, expect 3**num_gameweeks strategies
    count = count_expected_outputs(
        3,
        free_transfers=1,
        max_total_hit=None,
        allow_unused_transfers=True,
        next_gw=1,
        max_transfers=2,
        chip_gw_dict={},
    )
    assert count == 3**3
Esempio n. 4
0
def count_expected_play_wildcard_no_constraints():
    count = count_expected_outputs(
        2,
        free_transfers=1,
        max_total_hit=None,
        allow_unused_transfers=True,
        next_gw=1,
        max_transfers=2,
        chip_gw_dict={
            1: {"chip_to_play": "wildcard", "chips_allowed": []},
            2: {"chip_to_play": None, "chips_allowed": []},
        },
    )
    assert count == 3
Esempio n. 5
0
def count_expected_play_free_hit_no_unused():
    count = count_expected_outputs(
        2,
        free_transfers=2,
        max_total_hit=None,
        allow_unused_transfers=False,
        next_gw=1,
        max_transfers=2,
        chip_gw_dict={
            1: {"chip_to_play": "free_hit", "chips_allowed": []},
            2: {"chip_to_play": None, "chips_allowed": []},
        },
    )
    assert count == 3
Esempio n. 6
0
def count_expected_bench_boost_allowed_no_constraints():
    count = count_expected_outputs(
        2,
        free_transfers=1,
        max_total_hit=None,
        allow_unused_transfers=True,
        next_gw=1,
        max_transfers=2,
        chip_gw_dict={
            1: {"chips_allowed": ["bench_boost"]},
            2: {"chips_allowed": ["bench_boost"]},
            3: {"chips_allowed": ["bench_boost"]},
        },
    )
    assert count == 27
Esempio n. 7
0
def test_count_expected_wildcard_allowed_no_constraints():
    count = count_expected_outputs(
        2,
        free_transfers=1,
        max_total_hit=None,
        allow_unused_transfers=True,
        next_gw=1,
        max_transfers=2,
        chip_gw_dict={
            1: {"chips_allowed": ["wildcard"]},
            2: {"chips_allowed": ["wildcard"]},
            3: {"chips_allowed": ["wildcard"]},
        },
    )
    assert count == 15
Esempio n. 8
0
def is_finished(
    num_gameweeks,
    wildcard=False,
    free_hit=False,
    triple_captain=False,
    bench_boost=False,
):
    """
    Count the number of json files in the output directory, and see if the number
    matches the final expected number, calculated based on number of gameweeks,
    and cards played
    Return True if output files are all there, False otherwise.
    """
    final_expected_num = count_expected_outputs(0, num_gameweeks, wildcard,
                                                free_hit, triple_captain,
                                                bench_boost)
    # count the json files in the output dir
    json_count = len(os.listdir(OUTPUT_DIR))
    if json_count == final_expected_num:
        return True
    return False
Esempio n. 9
0
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
Esempio n. 10
0
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
Esempio n. 11
0
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