import React, { useState } from "react"; import { X, Power, Info, RotateCcw } from "lucide-react"; // The absolute baseline FPL defaults as defined in your comprehensive_settings.json export const DEFAULT_SETTINGS = { enabled: false, secs: 600, hit_limit: 0, no_transfer_last_gws: 0, keep_top_ev_percent: 7, ft_use_penalty: 0.1, itb_loss_per_transfer: 0.0, vcap_weight: 0.1, opposing_play_penalty: 0.5, use_ft_value_list: false, // Sub-toggle for FT behavior ft_value: 1.5, xmin_lb: 45, ft_value_list: { "2": 2, "3": 1.6, "4": 1.3, "5": 1.1 }, bench_weights: { "0": 0.03, "1": 0.21, "2": 0.06, "3": 0.002 }, randomization_strength: 1.0, iteration_criteria: "this_gw_transfer_in_out", iteration_diff: 2, }; export function AdvancedSettingsModal({ setShowAdvancedSettings, comprehensiveSettings, setComprehensiveSettings, }) { const [isEnabled, setIsEnabled] = useState( comprehensiveSettings.enabled ?? false ); const handleToggle = () => { const newState = !isEnabled; setIsEnabled(newState); setComprehensiveSettings((prev) => ({ ...prev, enabled: newState })); }; const handleResetToDefaults = () => { if (window.confirm("Are you sure you want to restore all advanced settings to their recommended defaults?")) { // Restore all defaults, but preserve whatever the current toggle state is! setComprehensiveSettings({ ...DEFAULT_SETTINGS, enabled: isEnabled }); } }; const handleChange = (key, value, nestedKey = null) => { setComprehensiveSettings((prev) => { if (nestedKey !== null) { return { ...prev, [key]: { ...(prev[key] || {}), [nestedKey]: value, }, }; } return { ...prev, [key]: value }; }); }; // Check if we are using the dynamic list or the flat value const isUsingFtList = comprehensiveSettings.use_ft_value_list ?? DEFAULT_SETTINGS.use_ft_value_list; const SETTINGS_GROUPS = [ { title: "Solver Constraints", items: [ { key: "secs", label: "Solve Time Limit (secs)", type: "number", step: "1", default: DEFAULT_SETTINGS.secs, desc: "Maximum time in seconds allowed for the solver per iteration." }, { key: "hit_limit", label: "Max Horizon Hits", type: "number", step: "1", default: DEFAULT_SETTINGS.hit_limit, desc: "Maximum total hits allowed over the entire horizon. Leave blank for infinite." }, { key: "no_transfer_last_gws", label: "No Transfers Last X GWs", type: "number", step: "1", default: DEFAULT_SETTINGS.no_transfer_last_gws, desc: "Prevent transfers in the final X gameweeks of the horizon." }, { key: "xmin_lb", label: "Min xMins (Per GW)", type: "number", step: "1", default: DEFAULT_SETTINGS.xmin_lb, desc: "Minimum expected minutes per GW. Multiplied by the horizon length to filter out non-playing players before solving." }, { key: "keep_top_ev_percent", label: "Keep Top EV (%)", type: "number", step: "1", default: DEFAULT_SETTINGS.keep_top_ev_percent, desc: "Percentage of top EV players to keep for the solve." }, ] }, { title: "Penalties & Weights", items: [ { key: "ft_use_penalty", label: "FT Use Penalty", type: "number", step: "0.01", default: DEFAULT_SETTINGS.ft_use_penalty, desc: "Penalty applied for using a free transfer (encourages rolling)." }, { key: "itb_loss_per_transfer", label: "ITB Loss per Transfer", type: "number", step: "0.01", default: DEFAULT_SETTINGS.itb_loss_per_transfer, desc: "Artificial cost deducted from ITB per transfer to prefer cheaper identical-EV moves." }, { key: "vcap_weight", label: "Vice-Captain Weight", type: "number", step: "0.01", default: DEFAULT_SETTINGS.vcap_weight, desc: "Fractional EV added to the Vice Captain in case the main captain does not play." }, { key: "opposing_play_penalty", label: "Opposing Play Penalty", type: "number", step: "0.01", default: DEFAULT_SETTINGS.opposing_play_penalty, desc: "Penalty applied when attacking players face defensive players in your lineup." }, ] }, { title: "Bench Weights", desc: "Fractional EV added to bench players based on their bench order.", isNested: true, parentKey: "bench_weights", items: [ { nestedKey: "0", label: "Goalkeeper", type: "number", step: "0.01", default: DEFAULT_SETTINGS.bench_weights["0"] }, { nestedKey: "1", label: "Outfield 1st", type: "number", step: "0.01", default: DEFAULT_SETTINGS.bench_weights["1"] }, { nestedKey: "2", label: "Outfield 2nd", type: "number", step: "0.01", default: DEFAULT_SETTINGS.bench_weights["2"] }, { nestedKey: "3", label: "Outfield 3rd", type: "number", step: "0.01", default: DEFAULT_SETTINGS.bench_weights["3"] }, ] }, { title: "Iterations & Simulations", items: [ { key: "randomization_strength", label: "Randomization Strength", type: "number", step: "0.01", default: DEFAULT_SETTINGS.randomization_strength, desc: "Multiplier/Strength for adding noise to EVs during Sensitivity Analysis." }, { key: "iteration_criteria", label: "Iteration Criteria", type: "select", default: DEFAULT_SETTINGS.iteration_criteria, desc: "Rule to generate alternative solutions in subsequent iterations.", options: [ { value: "this_gw_transfer_in_out", label: "Transfers In & Out (Current GW)" }, { value: "this_gw_transfer_in", label: "Transfers In (Current GW)" }, { value: "this_gw_transfer_out", label: "Transfers Out (Current GW)" }, ] }, { key: "iteration_diff", label: "Iteration Difference", type: "number", step: "1", default: DEFAULT_SETTINGS.iteration_diff, desc: "Minimum number of transfers that must change to find an alternate optimal solution." } ] } ]; return (
Configure comprehensive internal MILP parameters and weights.
When enabled, these parameters will be injected into the solver payload.
Intrinsic EV value assigned for holding/rolling free transfers.
Using standard flat FT Value from normal settings.
{group.desc}
}