Spaces:
Running
Running
Commit ·
21b054e
1
Parent(s): 1f058dc
solver updates
Browse files- solver.py +62 -5
- solver_engine.py +30 -0
solver.py
CHANGED
|
@@ -15,6 +15,7 @@ def run_milp_model(data: dict):
|
|
| 15 |
current_squad = data["current_squad"]
|
| 16 |
start_itb = data["itb"]
|
| 17 |
start_ft = data["ft"]
|
|
|
|
| 18 |
|
| 19 |
settings = data.get("settings", {})
|
| 20 |
|
|
@@ -141,6 +142,48 @@ def run_milp_model(data: dict):
|
|
| 141 |
prev_w = gws[i - 1]
|
| 142 |
gw_ft_gain[w] = gw_ft_value[w] - gw_ft_value[prev_w]
|
| 143 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
# --- OBJECTIVE FUNCTION (Decayed to match original) ---
|
| 145 |
obj_parts = []
|
| 146 |
for i, w in enumerate(gws):
|
|
@@ -173,6 +216,9 @@ def run_milp_model(data: dict):
|
|
| 173 |
for p in players:
|
| 174 |
obj_parts.append(-transfer_in[p][w] * ft_use_penalty * decay_factor)
|
| 175 |
|
|
|
|
|
|
|
|
|
|
| 176 |
prob += pulp.lpSum(obj_parts), "Total_EV_Objective"
|
| 177 |
|
| 178 |
# --- ADVANCED HORIZON-LEVEL CONSTRAINTS ---
|
|
@@ -419,7 +465,13 @@ def run_milp_model(data: dict):
|
|
| 419 |
|
| 420 |
# THE FIX: Boot up the HiGHS engine instead of CBC
|
| 421 |
try:
|
| 422 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 423 |
except pulp.PulpSolverError:
|
| 424 |
print("HiGHS not found! Falling back to CBC...")
|
| 425 |
solver = pulp.PULP_CBC_CMD(msg=False, timeLimit=time_limit_sec)
|
|
@@ -530,14 +582,19 @@ def run_milp_model(data: dict):
|
|
| 530 |
if chip == "bb":
|
| 531 |
gw_pure_ev += sum(raw_ev_matrix[p].get(w, 0) for p in gw_bench_sorted)
|
| 532 |
else:
|
| 533 |
-
# THE FIX 2:
|
| 534 |
-
of_bench_ws = [0.17, 0.05, 0.02]
|
| 535 |
of_idx = 0
|
| 536 |
for p in gw_bench_sorted:
|
| 537 |
if positions.get(p, "M") == "G":
|
| 538 |
-
|
|
|
|
| 539 |
else:
|
| 540 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 541 |
gw_pure_ev += raw_ev_matrix[p].get(w, 0) * bw
|
| 542 |
of_idx += 1
|
| 543 |
|
|
|
|
| 15 |
current_squad = data["current_squad"]
|
| 16 |
start_itb = data["itb"]
|
| 17 |
start_ft = data["ft"]
|
| 18 |
+
gw_opp_teams = data.get("gw_opp_teams", {})
|
| 19 |
|
| 20 |
settings = data.get("settings", {})
|
| 21 |
|
|
|
|
| 142 |
prev_w = gws[i - 1]
|
| 143 |
gw_ft_gain[w] = gw_ft_value[w] - gw_ft_value[prev_w]
|
| 144 |
|
| 145 |
+
# --- CROSS-PLAY PENALTY LOGIC ---
|
| 146 |
+
cp_penalty_expr = {w: 0 for w in gws}
|
| 147 |
+
|
| 148 |
+
if settings.get("no_opposing_play") and gw_opp_teams:
|
| 149 |
+
opp_group = settings.get("opposing_play_group", "all")
|
| 150 |
+
pen_val = float(settings.get("opposing_play_penalty", 0.5))
|
| 151 |
+
|
| 152 |
+
opp_pos = [
|
| 153 |
+
("G", "F"),
|
| 154 |
+
("G", "M"),
|
| 155 |
+
("D", "F"),
|
| 156 |
+
("D", "M"),
|
| 157 |
+
("F", "G"),
|
| 158 |
+
("M", "G"),
|
| 159 |
+
("F", "D"),
|
| 160 |
+
("M", "D"),
|
| 161 |
+
]
|
| 162 |
+
|
| 163 |
+
cp_list = []
|
| 164 |
+
for w in gws:
|
| 165 |
+
for p1 in players:
|
| 166 |
+
for p2 in players:
|
| 167 |
+
# Look up the pre-calculated dictionary!
|
| 168 |
+
if p1 != p2 and (teams[p1], teams[p2]) in gw_opp_teams.get(w, []):
|
| 169 |
+
if (
|
| 170 |
+
opp_group == "all"
|
| 171 |
+
or (positions[p1], positions[p2]) in opp_pos
|
| 172 |
+
):
|
| 173 |
+
cp_list.append((p1, p2, w))
|
| 174 |
+
|
| 175 |
+
if cp_list:
|
| 176 |
+
cp_vars = pulp.LpVariable.dicts("cp", cp_list, cat="Binary")
|
| 177 |
+
for p1, p2, w in cp_list:
|
| 178 |
+
prob += lineup[p1][w] + lineup[p2][w] <= 1 + cp_vars[(p1, p2, w)]
|
| 179 |
+
prob += cp_vars[(p1, p2, w)] <= lineup[p1][w]
|
| 180 |
+
prob += cp_vars[(p1, p2, w)] <= lineup[p2][w]
|
| 181 |
+
|
| 182 |
+
for w in gws:
|
| 183 |
+
cp_penalty_expr[w] = pen_val * pulp.lpSum(
|
| 184 |
+
cp_vars[(p1, p2, gw)] for (p1, p2, gw) in cp_list if gw == w
|
| 185 |
+
)
|
| 186 |
+
|
| 187 |
# --- OBJECTIVE FUNCTION (Decayed to match original) ---
|
| 188 |
obj_parts = []
|
| 189 |
for i, w in enumerate(gws):
|
|
|
|
| 216 |
for p in players:
|
| 217 |
obj_parts.append(-transfer_in[p][w] * ft_use_penalty * decay_factor)
|
| 218 |
|
| 219 |
+
if cp_penalty_expr[w] != 0:
|
| 220 |
+
obj_parts.append(-cp_penalty_expr[w] * decay_factor)
|
| 221 |
+
|
| 222 |
prob += pulp.lpSum(obj_parts), "Total_EV_Objective"
|
| 223 |
|
| 224 |
# --- ADVANCED HORIZON-LEVEL CONSTRAINTS ---
|
|
|
|
| 465 |
|
| 466 |
# THE FIX: Boot up the HiGHS engine instead of CBC
|
| 467 |
try:
|
| 468 |
+
gap = float(settings.get("gap", 0.005))
|
| 469 |
+
solver = pulp.getSolver(
|
| 470 |
+
"HiGHS",
|
| 471 |
+
msg=False,
|
| 472 |
+
timeLimit=time_limit_sec,
|
| 473 |
+
options=["presolve=on", f"mip_rel_gap={gap}"],
|
| 474 |
+
)
|
| 475 |
except pulp.PulpSolverError:
|
| 476 |
print("HiGHS not found! Falling back to CBC...")
|
| 477 |
solver = pulp.PULP_CBC_CMD(msg=False, timeLimit=time_limit_sec)
|
|
|
|
| 582 |
if chip == "bb":
|
| 583 |
gw_pure_ev += sum(raw_ev_matrix[p].get(w, 0) for p in gw_bench_sorted)
|
| 584 |
else:
|
| 585 |
+
# THE FIX 2: Stop hardcoding! Use the dynamic weights extracted from React at the top of the file!
|
|
|
|
| 586 |
of_idx = 0
|
| 587 |
for p in gw_bench_sorted:
|
| 588 |
if positions.get(p, "M") == "G":
|
| 589 |
+
# Uses the gk_bench_w parsed at line 46
|
| 590 |
+
gw_pure_ev += raw_ev_matrix[p].get(w, 0) * gk_bench_w
|
| 591 |
else:
|
| 592 |
+
# Uses the of_bench_ws array parsed at line 47
|
| 593 |
+
bw = (
|
| 594 |
+
of_bench_ws[of_idx]
|
| 595 |
+
if of_idx < len(of_bench_ws)
|
| 596 |
+
else avg_of_bench_w
|
| 597 |
+
)
|
| 598 |
gw_pure_ev += raw_ev_matrix[p].get(w, 0) * bw
|
| 599 |
of_idx += 1
|
| 600 |
|
solver_engine.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
|
|
| 1 |
from typing import Any
|
| 2 |
|
|
|
|
|
|
|
| 3 |
|
| 4 |
def _norm_id_list(raw) -> list[int]:
|
| 5 |
if not raw:
|
|
@@ -180,6 +183,32 @@ def prep_solver_data(payload_data: dict):
|
|
| 180 |
ev_matrix[pid][gw] = float(raw_ev) * decay_factor
|
| 181 |
raw_ev_matrix[pid][gw] = float(raw_ev)
|
| 182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
return {
|
| 184 |
"players": list(buy_prices.keys()),
|
| 185 |
"gws": horizon_gws,
|
|
@@ -192,4 +221,5 @@ def prep_solver_data(payload_data: dict):
|
|
| 192 |
"itb": float(payload_data["in_the_bank"]),
|
| 193 |
"ft": int(payload_data["free_transfers"]),
|
| 194 |
"settings": settings,
|
|
|
|
| 195 |
}
|
|
|
|
| 1 |
+
import requests
|
| 2 |
from typing import Any
|
| 3 |
|
| 4 |
+
_FIXTURE_CACHE = []
|
| 5 |
+
|
| 6 |
|
| 7 |
def _norm_id_list(raw) -> list[int]:
|
| 8 |
if not raw:
|
|
|
|
| 183 |
ev_matrix[pid][gw] = float(raw_ev) * decay_factor
|
| 184 |
raw_ev_matrix[pid][gw] = float(raw_ev)
|
| 185 |
|
| 186 |
+
# --- CROSS-PLAY FIXTURE PREP ---
|
| 187 |
+
global _FIXTURE_CACHE
|
| 188 |
+
gw_opp_teams = {w: [] for w in horizon_gws}
|
| 189 |
+
|
| 190 |
+
if settings.get("no_opposing_play"):
|
| 191 |
+
if not _FIXTURE_CACHE:
|
| 192 |
+
try:
|
| 193 |
+
# Same fast cache logic as solver_the_real_one
|
| 194 |
+
_FIXTURE_CACHE = requests.get(
|
| 195 |
+
"https://fantasy.premierleague.com/api/fixtures/"
|
| 196 |
+
).json()
|
| 197 |
+
except: # noqa: E722
|
| 198 |
+
pass
|
| 199 |
+
|
| 200 |
+
# Map the opponent matchups for the solver
|
| 201 |
+
team_mapping = {v: k for k, v in teams.items()} # Reverse lookup
|
| 202 |
+
for f in _FIXTURE_CACHE:
|
| 203 |
+
w = f.get("event")
|
| 204 |
+
if w in horizon_gws:
|
| 205 |
+
home_team = team_mapping.get(f.get("team_h"))
|
| 206 |
+
away_team = team_mapping.get(f.get("team_a"))
|
| 207 |
+
if home_team and away_team:
|
| 208 |
+
gw_opp_teams[w].extend(
|
| 209 |
+
[(home_team, away_team), (away_team, home_team)]
|
| 210 |
+
)
|
| 211 |
+
|
| 212 |
return {
|
| 213 |
"players": list(buy_prices.keys()),
|
| 214 |
"gws": horizon_gws,
|
|
|
|
| 221 |
"itb": float(payload_data["in_the_bank"]),
|
| 222 |
"ft": int(payload_data["free_transfers"]),
|
| 223 |
"settings": settings,
|
| 224 |
+
"gw_opp_teams": gw_opp_teams,
|
| 225 |
}
|