Guest User

Untitled

a guest
Oct 28th, 2026
10
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.21 KB | None | 0 0
  1. import csv
  2. import glob
  3. import json
  4. import math
  5. import os
  6. import subprocess
  7. import sys
  8. import time
  9.  
  10. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  11. HARNESS = os.path.join(BASE_DIR, "fm24ctl.exe")
  12. FIXTURES_DIR = os.path.join(BASE_DIR, "fixtures")
  13. RESULTS_DIR = os.path.join(BASE_DIR, "resultats")
  14. TEMP_DIR = os.path.join(BASE_DIR, "temp")
  15.  
  16. for d in [FIXTURES_DIR, RESULTS_DIR, TEMP_DIR]:
  17. os.makedirs(d, exist_ok=True)
  18.  
  19. FIXTURES = [
  20. {"name": "Crystal Palace (Median)", "file": os.path.join(FIXTURES_DIR, "aston-villa-v-crystal-palace-1792853408.json")},
  21. {"name": "Man City (Elite)", "file": os.path.join(FIXTURES_DIR, "astonvscity.json")},
  22. {"name": "Luton Town (Low)", "file": os.path.join(FIXTURES_DIR, "astonvsluton.json")}
  23. ]
  24.  
  25. TEMP_JSON = os.path.join(TEMP_DIR, "temp_fixture_stable.json")
  26. CSV_OUT = os.path.join(TEMP_DIR, "temp_sim_out_stable.csv")
  27.  
  28. OUTPUT_JSON_SEGMENTS = os.path.join(RESULTS_DIR, "segments_calibres_fmss.json")
  29. OUTPUT_GLOBAL_CSV = os.path.join(RESULTS_DIR, "bilan_statistique_hp.csv")
  30.  
  31. # 1,500 matches x 3 = 4,500 matches per level (22,500 / attr) -> ~1h per attribute
  32. MATCHES_PER_LEVEL = 1500
  33. TIERS = [1, 8, 12, 16, 20]
  34.  
  35. # OUTFIELD PLAYERS QUEUE
  36. ACTIVE_QUEUE = [
  37. ("reflexes", True),
  38. ("agility", True),
  39. ("aerial_reach", True),
  40. ("command_of_area", True),
  41. ("handling", True),
  42. ("one_on_ones", True),
  43. ("positioning", True),
  44. ("pace", False),
  45. ("acceleration", False),
  46. ("jumping_reach", False),
  47. ("work_rate", False),
  48. ("dribbling", False),
  49. ("tackling", False),
  50. ("passing", False),
  51. ("finishing", False),
  52. ("stamina", False),
  53. ("balance", False),
  54. ("anticipation", False),
  55. ("concentration", False)
  56. ]
  57.  
  58. def cleanup():
  59. """Remove temporary binary job files left behind by the harness."""
  60. for f in glob.glob(os.path.join(BASE_DIR, "_job_input*.bin")):
  61. try:
  62. os.remove(f)
  63. except OSError:
  64. pass
  65.  
  66. def simulate(fixture_path, attr_name, val_out_of_20, is_gk):
  67. """Run simulation batch for a given fixture, attribute value, and player target."""
  68. val_fm = int(val_out_of_20 * 5)
  69. raw_attr = attr_name[3:] if attr_name.startswith("gk_") else attr_name
  70.  
  71. with open(fixture_path, "r", encoding="utf-8") as f:
  72. data = json.load(f)
  73.  
  74. entries = data.get("fields", []) if isinstance(data, dict) else data
  75. targets = [0] if is_gk else list(range(1, 11))
  76. target_names = {f"home.player[{idx}].ratings" for idx in targets}
  77.  
  78. modified_count = 0
  79. for entry in entries:
  80. if isinstance(entry, dict) and entry.get("name") in target_names:
  81. u8map = entry.get("u8map", {})
  82. if raw_attr in u8map:
  83. u8map[raw_attr] = val_fm
  84. modified_count += 1
  85.  
  86. if modified_count == 0:
  87. return 0.0, 0.0
  88.  
  89. with open(TEMP_JSON, "w", encoding="utf-8") as f:
  90. json.dump(data, f)
  91.  
  92. cmd = [HARNESS, "run", TEMP_JSON, f"1-{MATCHES_PER_LEVEL}", CSV_OUT, "--keep-build"]
  93. subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
  94.  
  95. csv_res = CSV_OUT if os.path.exists(CSV_OUT) else TEMP_JSON.replace(".json", ".csv")
  96. gds = []
  97.  
  98. if os.path.exists(csv_res):
  99. with open(csv_res, "r", encoding="utf-8") as f:
  100. reader = csv.DictReader(f)
  101. for row in reader:
  102. gds.append(int(row["home_goals"]) - int(row["away_goals"]))
  103. try:
  104. os.remove(csv_res)
  105. except OSError:
  106. pass
  107.  
  108. cleanup()
  109.  
  110. if not gds:
  111. return 0.0, 0.0
  112.  
  113. mean_gd = sum(gds) / len(gds)
  114. variance = sum((x - mean_gd) ** 2 for x in gds) / len(gds)
  115. std_err = math.sqrt(variance / len(gds))
  116. return mean_gd, std_err
  117.  
  118. def test_attribute(attr_name, is_gk):
  119. """Evaluate performance across all defined tiers and compute confidence intervals."""
  120. tier_means = {}
  121. tier_errors = {}
  122.  
  123. for p in TIERS:
  124. t_tier = time.time()
  125. gd_tot = 0.0
  126. err_sq_tot = 0.0
  127. for fix in FIXTURES:
  128. gd, s_err = simulate(fix["file"], attr_name, p, is_gk)
  129. gd_tot += gd
  130. err_sq_tot += s_err ** 2
  131.  
  132. tier_means[p] = gd_tot / len(FIXTURES)
  133. tier_errors[p] = math.sqrt(err_sq_tot) / len(FIXTURES)
  134. print(f" [P{p}: {(time.time() - t_tier) / 60:.1f}m]", end="", flush=True)
  135.  
  136. base_gd = tier_means[8]
  137. deltas = {p: (tier_means[p] - base_gd) * 38 for p in TIERS}
  138. ci_95 = {p: (tier_errors[p] * 1.96) * 38 for p in TIERS}
  139.  
  140. segments = [
  141. {"from": 16, "to": 20, "pts": round(deltas[20] - deltas[16], 2)},
  142. {"from": 12, "to": 16, "pts": round(deltas[16] - deltas[12], 2)},
  143. {"from": 8, "to": 12, "pts": round(deltas[12] - deltas[8], 2)},
  144. {"from": 1, "to": 8, "pts": round(deltas[8] - deltas[1], 2)}
  145. ]
  146. amplitude = round(deltas[20] - deltas[1], 2)
  147. return deltas, ci_95, segments, amplitude
  148.  
  149. def save_results(attr_name, is_gk, deltas, ci_95, segments, amplitude):
  150. """Save calibrated segments to JSON and append statistical summary to CSV."""
  151. key_camel = "".join(x.capitalize() for x in attr_name.split("_"))
  152.  
  153. structure = {"position_weights": {"GK": {}, "OUTFIELD": {}}}
  154. if os.path.exists(OUTPUT_JSON_SEGMENTS):
  155. try:
  156. with open(OUTPUT_JSON_SEGMENTS, "r", encoding="utf-8") as f:
  157. structure = json.load(f)
  158. except Exception:
  159. pass
  160.  
  161. section = "GK" if is_gk else "OUTFIELD"
  162. structure["position_weights"][section][key_camel] = segments
  163.  
  164. with open(OUTPUT_JSON_SEGMENTS, "w", encoding="utf-8") as f:
  165. json.dump(structure, f, indent=2)
  166.  
  167. file_exists = os.path.exists(OUTPUT_GLOBAL_CSV)
  168. with open(OUTPUT_GLOBAL_CSV, "a", newline="", encoding="utf-8") as f:
  169. writer = csv.writer(f)
  170. if not file_exists:
  171. writer.writerow(["Attribute", "Target", "P1", "P8", "P12", "P16", "P20", "Amplitude_38m", "CI95_Margin"])
  172. writer.writerow([
  173. attr_name, "Goalkeeper" if is_gk else "Outfield",
  174. f"{deltas[1]:+.2f}", "0.0", f"{deltas[12]:+.2f}", f"{deltas[16]:+.2f}", f"{deltas[20]:+.2f}",
  175. f"{amplitude:+.2f}", f"±{ci_95[20]:.2f} pts"
  176. ])
  177.  
  178. def main():
  179. total = len(ACTIVE_QUEUE)
  180. print("=" * 65)
  181. print(f" SAFE SEQUENTIAL BENCHMARK (N = {MATCHES_PER_LEVEL * 3} matches / tier)")
  182. print(" Estimated throughput: ~1h per attribute | No DLL conflict risk")
  183. print("=" * 65)
  184.  
  185. t0_global = time.time()
  186.  
  187. for idx, (attr, is_gk) in enumerate(ACTIVE_QUEUE, 1):
  188. t0 = time.time()
  189. target_str = "GK" if is_gk else "OUTFIELD"
  190. print(f"\n[{idx}/{total}] {attr.upper()} ({target_str})...", end="", flush=True)
  191.  
  192. deltas, ci_95, segments, amp = test_attribute(attr, is_gk)
  193. save_results(attr, is_gk, deltas, ci_95, segments, amp)
  194.  
  195. duration = (time.time() - t0) / 60
  196. print(f"\n -> Validated in {duration:.1f} min | Amplitude: {amp:+.2f} pts (CI95: ±{ci_95[20]:.2f})")
  197.  
  198. print("\n" + "=" * 65)
  199. print(f" SESSION COMPLETED IN {(time.time() - t0_global) / 3600:.2f} HOURS")
  200. print("=" * 65)
  201.  
  202. if __name__ == "__main__":
  203. main()
  204.  
Advertisement
Add Comment
Please, Sign In to add comment