"""Perfiles de jitter intercambiables. El compromiso de fondo no tiene truco: Timenet solo cuenta dentro de la ventana teorica, asi que solo se puede variar hacia el lado seguro, y alejarse cuesta presencia real. Mas variedad visible = mas minutos ahi. Cada perfil elige un punto de ese compromiso. Un perfil puede afinar por tramo con sufijo `_w`: `entry_median_w0` gana a `entry_median` para el tramo 0. Sirve para repartir el coste donde menos molesta: adelantar la entrada de la mañana ALARGA el dia, mientras que adelantar la vuelta de comer solo ACORTA la comida y se sale a la misma hora. """ from __future__ import annotations # Valores comunes. Un perfil solo declara lo que cambia. BASE = { "entry_floor": 12, "entry_median": 58, "entry_sigma": 0.95, "entry_cap": 330, "exit_floor": 10, "exit_median": 42, "exit_sigma": 0.95, "exit_cap": 300, "drift_sigma": 0.30, "drift_clamp": 0.75, "entry_guard": 12, } PERFILES: dict[str, dict] = { "discreto": { "titulo": "Discreto", "resumen": "Lo más barato. Casi siempre justo antes de la hora.", "stats": {'coste': 2.82, 'madrugon': 1.29, 'mins': 2, 'moda': 75}, "params": { "entry_median": 42, "entry_sigma": 0.80, "entry_cap": 240, "exit_median": 30, "exit_sigma": 0.80, "exit_cap": 210, }, }, "natural": { "titulo": "Natural", "resumen": "Equilibrio entre variedad y minutos de más.", "stats": {'coste': 4.14, 'madrugon': 1.81, 'mins': 5, 'moda': 51}, "params": { "entry_median": 58, "entry_sigma": 0.95, "entry_cap": 330, "exit_median": 42, "exit_sigma": 0.95, "exit_cap": 300, }, }, "camuflado": { "titulo": "Camuflado", "resumen": "Se parece a tu histórico real. Cuesta algo más.", "stats": {'coste': 5.47, 'madrugon': 2.08, 'mins': 4, 'moda': 43}, "params": { "entry_median": 75, "entry_sigma": 1.05, "entry_cap": 450, "exit_median": 55, "exit_sigma": 1.05, "exit_cap": 420, }, }, "disperso": { "titulo": "Disperso", "resumen": "Máxima variedad. Paga bastantes minutos por ella.", "stats": {'coste': 7.87, 'madrugon': 2.99, 'mins': 6, 'moda': 29}, "params": { "entry_median": 110, "entry_sigma": 1.15, "entry_cap": 600, "exit_median": 80, "exit_sigma": 1.15, "exit_cap": 540, }, }, "sin_madrugar": { "titulo": "Sin madrugar", "resumen": "Entrada de la mañana pegada a las 9. La variedad se la come " "la pausa de comer y la salida, así no te levantas antes.", "stats": {'coste': 5.68, 'madrugon': 1.26, 'mins': 2, 'moda': 77}, "params": { # tramo 0 = mañana: lo mas ajustado posible, no alarga el dia "entry_median_w0": 35, "entry_sigma_w0": 0.70, "entry_cap_w0": 150, # tramo 1 = vuelta de comer: generoso, solo acorta la comida "entry_median_w1": 120, "entry_sigma_w1": 1.15, "entry_cap_w1": 600, # salidas: generosas, no obligan a madrugar "exit_median": 75, "exit_sigma": 1.10, "exit_cap": 480, }, }, } DEFECTO = "natural" def existe(nombre: str) -> bool: return nombre in PERFILES def nombres() -> list[str]: return list(PERFILES) def params(nombre: str) -> dict: """Parametros efectivos del perfil, con BASE por debajo.""" p = dict(BASE) p.update(PERFILES.get(nombre, PERFILES[DEFECTO])["params"]) return p def titulo(nombre: str) -> str: return PERFILES.get(nombre, {}).get("titulo", nombre) def resumen(nombre: str) -> str: return PERFILES.get(nombre, {}).get("resumen", "") def stats(nombre: str) -> dict: """Medidas sobre los 226 dias laborables del año. Ver tools/perfiles.py. coste = presencia total de mas, min/dia madrugon = de esos, los que te levantas antes por la entrada de la mañana mins = minutos distintos visibles en la entrada de las 09:00 moda = cuota del minuto mas frecuente """ return PERFILES.get(nombre, {}).get("stats", {}) def valor(p: dict, clase: str, campo: str, wi: int): """`entry_median_w0` gana a `entry_median` para el tramo 0.""" return p.get(f"{clase}_{campo}_w{wi}", p[f"{clase}_{campo}"])