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 8.56 KB | None | 0 0
  1. import csv
  2. import glob
  3. import json
  4. import os
  5. import subprocess
  6. import sys
  7. import time
  8.  
  9. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  10. HARNESS = os.path.join(BASE_DIR, "fm24ctl.exe")
  11. FIXTURES_DIR = os.path.join(BASE_DIR, "fixtures")
  12. RESULTS_DIR = os.path.join(BASE_DIR, "resultats")
  13. TEMP_DIR = os.path.join(BASE_DIR, "temp")
  14.  
  15. for d in [FIXTURES_DIR, RESULTS_DIR, TEMP_DIR]:
  16. os.makedirs(d, exist_ok=True)
  17.  
  18. # THE 3 FIXTURES OF THE "GOLDEN TRIPTYCH"
  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 Block)", "file": os.path.join(FIXTURES_DIR, "astonvsluton.json")}
  23. ]
  24.  
  25. TEMP_JSON = os.path.join(TEMP_DIR, "temp_fixture_strict.json")
  26. CSV_OUT = os.path.join(TEMP_DIR, "temp_sim_out_strict.csv")
  27.  
  28. # Existing unified output files to be enriched
  29. OUTPUT_JSON_SEGMENTS = os.path.join(RESULTS_DIR, "mes_segments_overnight.json")
  30. OUTPUT_GLOBAL_CSV = os.path.join(RESULTS_DIR, "bilan_general_overnight.csv")
  31.  
  32. # HIGH PRECISION (500 matches per tier x 3 fixtures = 1,500 matches per tier)
  33. MATCHES_PER_LEVEL = 500
  34. TIERS = [1, 8, 12, 16, 20]
  35.  
  36. # Personality attributes (Stored under home.player[X].personality.[attr], direct 1-20 scale)
  37. PERSONALITY_ATTRIBUTES = {
  38. "sportsmanship", "professionalism", "loyalty", "temperament",
  39. "pressure", "ambition", "adaptability", "controversy"
  40. }
  41.  
  42. # 1. REMAINING ATTRIBUTES FOR OUTFIELD PLAYERS
  43. REMAINING_OUTFIELD_ATTRIBUTES = [
  44. "marking", "corners", "free_kicks", "long_throws", "penalty_taking",
  45. "flair", "off_the_ball",
  46. "consistency", "important_matches", "injury_proneness",
  47. "pressure", "professionalism", "ambition", "loyalty",
  48. "temperament", "sportsmanship", "adaptability"
  49. ]
  50.  
  51. # 2. REMAINING ATTRIBUTES FOR GOALKEEPERS
  52. REMAINING_GK_ATTRIBUTES = [
  53. "dirtiness", "first_touch", "passing", "technique", "throwing",
  54. "flair", "off_the_ball",
  55. "consistency", "important_matches", "injury_proneness",
  56. "pressure", "professionalism", "ambition", "loyalty",
  57. "temperament", "sportsmanship", "adaptability"
  58. ]
  59.  
  60. # Combined queue: (Attribute, is_gk)
  61. QUEUE_COMPLEMENT = [(attr, False) for attr in REMAINING_OUTFIELD_ATTRIBUTES] + [(attr, True) for attr in REMAINING_GK_ATTRIBUTES]
  62.  
  63. def cleanup():
  64. """Remove temporary binary job files left by the harness."""
  65. for f in glob.glob(os.path.join(BASE_DIR, "_job_input*.bin")):
  66. try:
  67. os.remove(f)
  68. except OSError:
  69. pass
  70.  
  71. def simulate(fixture_path, attr_name, val_out_of_20, is_gk):
  72. """Run batch simulation for a fixture, attribute value, and player target."""
  73. targets = [0] if is_gk else list(range(1, 11))
  74. is_personality = attr_name in PERSONALITY_ATTRIBUTES
  75.  
  76. if is_personality:
  77. val_fm = int(val_out_of_20) # Direct 1-20 scale
  78. target_names = {f"home.player[{idx}].personality.{attr_name}" for idx in targets}
  79. else:
  80. val_fm = int(val_out_of_20 * 5) # 1-100 scale for u8map
  81. target_names = {f"home.player[{idx}].ratings" for idx in targets}
  82.  
  83. with open(fixture_path, "r", encoding="utf-8") as f:
  84. data = json.load(f)
  85.  
  86. entries = data.get("fields", []) if isinstance(data, dict) else data
  87. modified_count = 0
  88.  
  89. for entry in entries:
  90. if isinstance(entry, dict):
  91. name = entry.get("name")
  92. if is_personality:
  93. if name in target_names and "u8" in entry:
  94. entry["u8"] = val_fm
  95. modified_count += 1
  96. else:
  97. if name in target_names:
  98. u8map = entry.get("u8map", {})
  99. if attr_name in u8map:
  100. u8map[attr_name] = val_fm
  101. modified_count += 1
  102.  
  103. if modified_count == 0:
  104. return None # Attribute missing from file for this target, gracefully ignored
  105.  
  106. with open(TEMP_JSON, "w", encoding="utf-8") as f:
  107. json.dump(data, f)
  108.  
  109. cmd = [HARNESS, "run", TEMP_JSON, f"1-{MATCHES_PER_LEVEL}", CSV_OUT, "--keep-build"]
  110. subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
  111.  
  112. csv_res = CSV_OUT if os.path.exists(CSV_OUT) else TEMP_JSON.replace(".json", ".csv")
  113. gd = 0.0
  114.  
  115. if os.path.exists(csv_res):
  116. with open(csv_res, "r", encoding="utf-8") as f:
  117. reader = csv.DictReader(f)
  118. for row in reader:
  119. gd += (int(row["home_goals"]) - int(row["away_goals"]))
  120. try:
  121. os.remove(csv_res)
  122. except OSError:
  123. pass
  124.  
  125. cleanup()
  126. return gd / MATCHES_PER_LEVEL
  127.  
  128. def test_full_attribute(attr_name, is_gk):
  129. """Evaluate performance deltas across all tiers."""
  130. tier_means = {}
  131.  
  132. for p in TIERS:
  133. gd_total = 0.0
  134. for fix in FIXTURES:
  135. gd = simulate(fix["file"], attr_name, p, is_gk)
  136. if gd is None:
  137. return None # Attribute not found in data, aborting this test
  138. gd_total += gd
  139. tier_means[p] = gd_total / len(FIXTURES)
  140.  
  141. base_gd = tier_means[8]
  142. deltas = {p: (tier_means[p] - base_gd) * 38 for p in TIERS}
  143.  
  144. segments = [
  145. {"from": 16, "to": 20, "pts": round(deltas[20] - deltas[16], 1)},
  146. {"from": 12, "to": 16, "pts": round(deltas[16] - deltas[12], 1)},
  147. {"from": 8, "to": 12, "pts": round(deltas[12] - deltas[8], 1)},
  148. {"from": 1, "to": 8, "pts": round(deltas[8] - deltas[1], 1)}
  149. ]
  150. amplitude = round(deltas[20] - deltas[1], 1)
  151. return deltas, segments, amplitude
  152.  
  153. def enrich_results(attr_name, is_gk, deltas, segments, amplitude):
  154. """Append calibrated segments to JSON and add stats to CSV."""
  155. key_camel = "".join(x.capitalize() for x in attr_name.split("_"))
  156.  
  157. # 1. Update existing JSON structure
  158. structure = {"position_weights": {"GK": {}, "OUTFIELD": {}}}
  159. if os.path.exists(OUTPUT_JSON_SEGMENTS):
  160. try:
  161. with open(OUTPUT_JSON_SEGMENTS, "r", encoding="utf-8") as f:
  162. structure = json.load(f)
  163. except Exception:
  164. pass
  165.  
  166. if "position_weights" not in structure:
  167. structure["position_weights"] = {"GK": {}, "OUTFIELD": {}}
  168.  
  169. section = "GK" if is_gk else "OUTFIELD"
  170. structure["position_weights"][section][key_camel] = segments
  171.  
  172. with open(OUTPUT_JSON_SEGMENTS, "w", encoding="utf-8") as f:
  173. json.dump(structure, f, indent=2)
  174.  
  175. # 2. Append to existing CSV summary
  176. file_exists = os.path.exists(OUTPUT_GLOBAL_CSV)
  177. with open(OUTPUT_GLOBAL_CSV, "a", newline="", encoding="utf-8") as f:
  178. writer = csv.writer(f)
  179. if not file_exists:
  180. writer.writerow(["Attribute", "Target", "P1", "P8 (0.0)", "P12", "P16", "P20", "Triangulated_38m_Amplitude"])
  181. writer.writerow([
  182. attr_name, "Goalkeeper" if is_gk else "Outfield",
  183. f"{deltas[1]:+.1f}", "0.0", f"{deltas[12]:+.1f}", f"{deltas[16]:+.1f}", f"{deltas[20]:+.1f}", f"{amplitude:+.1f}"
  184. ])
  185.  
  186. def main():
  187. total = len(QUEUE_COMPLEMENT)
  188. print("\n=======================================================")
  189. print(" STRICT COMPLEMENTARY BENCHMARK (OUTFIELD + GOALKEEPER)")
  190. print(f" {total} tests scheduled | {MATCHES_PER_LEVEL} matches/tier")
  191. print(f" Automatic injection into: {OUTPUT_JSON_SEGMENTS}")
  192. print("=======================================================\n")
  193.  
  194. for fix in FIXTURES:
  195. if not os.path.exists(fix["file"]):
  196. print(f"[ERROR] File not found: {fix['file']}")
  197. return
  198.  
  199. t0_global = time.time()
  200.  
  201. for idx, (attr, is_gk) in enumerate(QUEUE_COMPLEMENT, 1):
  202. t0 = time.time()
  203. target_label = "GOALKEEPER (P0)" if is_gk else "OUTFIELD (P1-10)"
  204. print(f"[{idx:2d}/{total}] {attr.upper()} ({target_label})...", end="", flush=True)
  205.  
  206. result = test_full_attribute(attr, is_gk)
  207. if result is None:
  208. print(" SKIPPED (Missing from JSON data)")
  209. continue
  210.  
  211. deltas, segments, amp = result
  212. enrich_results(attr, is_gk, deltas, segments, amp)
  213. print(f" OK ({time.time() - t0:.1f}s) | Impact: {amp:+.1f} pts")
  214.  
  215. print("\n=======================================================")
  216. print(f" COMPLETION FINISHED IN {(time.time() - t0_global) / 3600:.2f} HOURS!")
  217. print(" -> Master files enriched successfully!")
  218. print("=======================================================")
  219.  
  220. if __name__ == "__main__":
  221. main()
Advertisement
Add Comment
Please, Sign In to add comment