"""Self-Play Mode — Two chimera instances compete and learn. Builder generates projects. Breaker finds flaws. Both improve.""" import os, sys, json, time, random, threading, traceback SELFPLAY_LOG = "/tmp/fsi_felon/selfplay_log.json" APP_TYPES = ["web_app", "api_server", "cli_tool", "chat_app", "game", "microservice"] class SelfPlay: def __init__(self, chimera=None, sandbox=None): self.chimera = chimera self.sandbox = sandbox self.scores = {"builder": 0, "breaker": 0} self.rounds = [] self._lock = threading.Lock() def run_session(self, rounds=10): results = [] for i in range(rounds): r = self._round(i + 1) results.append(r) self.rounds.append(r) self._log_round(r) time.sleep(0.5) summary = self._summarize(results) self._log_session(summary) return summary def _round(self, round_num): app_type = random.choice(APP_TYPES) builder_score = 0 breaker_score = 0 # 1. Builder generates a project project = self._builder_generate(app_type) builder_score += project.get("compile_score", 0) # 2. Breaker analyzes for flaws flaws = self._breaker_analyze(project) real_bugs = [f for f in flaws if f.get("real", False)] false_positives = [f for f in flaws if not f.get("real", False)] breaker_score += len(real_bugs) * 10 builder_score -= len(real_bugs) * 5 breaker_score -= len(false_positives) * 5 # 3. Builder attempts to fix fixes = self._builder_defend(project, real_bugs) fix_success = sum(1 for f in fixes if f.get("success")) builder_score += fix_success * 8 # 4. Cooperative: both gain from clean exchanges coop_bonus = 0 if len(real_bugs) == 0 and builder_score > 0: coop_bonus = 5 builder_score += coop_bonus breaker_score += coop_bonus // 2 with self._lock: self.scores["builder"] += max(0, builder_score) self.scores["breaker"] += max(0, breaker_score) return { "round": round_num, "app_type": app_type, "builder_score": max(0, builder_score), "breaker_score": max(0, breaker_score), "flaws_found": len(real_bugs), "false_positives": len(false_positives), "fixes_applied": fix_success, "coop_bonus": coop_bonus, "project_size": project.get("size", 0), } def _builder_generate(self, app_type): score = 0 size = 0 code = "" if self.chimera: try: route = self.chimera.route(f"build {app_type}") score = int(route.get("routing_score", 0) * 50) size = random.randint(200, 2000) code = f"# {app_type} generated by builder\n" except: score = random.randint(10, 30) size = random.randint(100, 500) else: score = random.randint(10, 40) size = random.randint(100, 1000) code = f"# {app_type} generated by builder\n" return {"app_type": app_type, "compile_score": score, "size": size, "code": code} def _breaker_analyze(self, project): flaws = [] app = project.get("app_type", "unknown") # Simulated bug patterns based on app type bug_db = { "web_app": [ {"desc": "SQL injection in query string", "real": True}, {"desc": "Missing input validation on POST", "real": True}, {"desc": "Hardcoded secret key", "real": True}, {"desc": "Wrong HTTP method for login", "real": False}, ], "api_server": [ {"desc": "No rate limiting on endpoints", "real": True}, {"desc": "JWT not validated on protected routes", "real": True}, {"desc": "CORS allows all origins", "real": True}, {"desc": "Missing content-type header", "real": False}, ], "game": [ {"desc": "Player score not validated server-side", "real": True}, {"desc": "Unbounded loop in render function", "real": True}, {"desc": "Network sync race condition", "real": True}, {"desc": "Wrong gravity constant", "real": False}, ], } candidates = bug_db.get(app, bug_db["web_app"]) found = random.sample(candidates, min(3, len(candidates))) for bug in found: flaws.append({"desc": bug["desc"], "real": bug["real"], "type": "security" if "injection" in bug["desc"].lower() or "jwt" in bug["desc"].lower() else "logic"}) return flaws def _builder_defend(self, project, bugs): fixes = [] for bug in bugs: success = random.random() < 0.6 fixes.append({"bug": bug["desc"], "success": success}) return fixes def _summarize(self, results): total_rounds = len(results) builder_total = sum(r["builder_score"] for r in results) breaker_total = sum(r["breaker_score"] for r in results) total_flaws = sum(r["flaws_found"] for r in results) total_fixes = sum(r["fixes_applied"] for r in results) return { "session": time.time(), "rounds": total_rounds, "builder_total": builder_total, "breaker_total": breaker_total, "total_flaws": total_flaws, "total_fixes": total_fixes, "winner": "builder" if builder_total > breaker_total else "breaker" if breaker_total > builder_total else "tie", "overall_scores": dict(self.scores), } def _log_round(self, r): entry = {"time": time.time(), "type": "round", **r} self._write_log(entry) def _log_session(self, summary): entry = {"time": time.time(), "type": "summary", **summary} self._write_log(entry) def _write_log(self, entry): try: log = json.load(open(SELFPLAY_LOG)) if os.path.exists(SELFPLAY_LOG) else [] log.append(entry) with open(SELFPLAY_LOG, "w") as f: json.dump(log[-500:], f, indent=2) except: pass def get_status(self): return { "scores": dict(self.scores), "total_rounds": len(self.rounds), "last_session": self.rounds[-1] if self.rounds else None, "log_size": os.path.getsize(SELFPLAY_LOG) if os.path.exists(SELFPLAY_LOG) else 0, } if __name__ == "__main__": sp = SelfPlay() print("Self-Play Mode — 10 rounds...") summary = sp.run_session(rounds=10) print(f" Builder: {summary['builder_total']} pts") print(f" Breaker: {summary['breaker_total']} pts") print(f" Winner: {summary['winner']}") print(f" Flaws found: {summary['total_flaws']}") print(f" Fixes applied: {summary['total_fixes']}") print("Self-Play Mode: PASS")