#!/usr/bin/env python3
"""Strategy Games house-bot exhibition ladder (labelled house bots, rule-based, deterministic). No stakes, no money: a rating only.
Round robin, every pair plays tic-tac-toe and Connect Four with both colours. Each game's random choices come from
seed = sha256("arena-games|<date>|<game>|<x_bot>|<o_bot>"), so anyone can replay every game from the published move list.
Elo: start 1200, K=24, games processed in published order. Output: site/data/games/ladder.json and site/games.html (built by build_leagues)."""
import hashlib, json, os, random, sys, datetime as dt
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))); sys.path.insert(0, ROOT)
from games.game_engine import ttt_best_moves, ttt_winner, ttt_turn, c4_empty, c4_drop, c4_legal, c4_winner, c4_wins_now, c4_negamax
BOTS = [  # (id, name, character, rule)
 ("house-perfect-pia", "Perfect Pia", "Calm and exact. Searches every line she can see before she moves.", "Tic-tac-toe: full minimax. Connect Four: alpha-beta search 4 plies with a line-count score."),
 ("house-centre-cy", "Centre Cy", "Believes the middle of the board is always the answer.", "Takes a win, blocks a loss, otherwise the free cell or column nearest the centre."),
 ("house-greedy-gus", "Greedy Gus", "Grabs any win in sight, plugs any hole, then guesses.", "Takes a win, blocks a loss, otherwise a seeded random legal move."),
 ("house-random-rae", "Random Rae", "Plays for fun. Every move is a seeded coin flip.", "Seeded random legal move."),
 ("house-corner-cora", "Corner Cora", "Loves edges and corners and distrusts the middle.", "Takes a win, blocks a loss, otherwise the free corner/edge-most cell or column."),
]
def ttt_move(bot, b, rng):
    p = ttt_turn(b); o = "O" if p == "X" else "X"; free = [i for i in range(9) if b[i] == "."]
    if bot == "house-perfect-pia": return ttt_best_moves(b)[0][0] - 1
    if bot == "house-random-rae": return rng.choice(free)
    for who in (p, o):
        for i in free:
            if ttt_winner(b[:i] + who + b[i+1:]) == who: return i
    if bot == "house-centre-cy": return min(free, key=lambda i: (abs(i // 3 - 1) + abs(i % 3 - 1), i))
    if bot == "house-corner-cora": return min(free, key=lambda i: (-(abs(i // 3 - 1) + abs(i % 3 - 1)), i))
    return rng.choice(free)
def c4_move(bot, b, p, rng):
    o = "O" if p == "X" else "X"; L = c4_legal(b)
    if bot == "house-perfect-pia":
        return max(sorted(L, key=lambda c: abs(c - 3)), key=lambda c: -c4_negamax(c4_drop(b, c, p), o, 3))
    if bot == "house-random-rae": return rng.choice(L)
    w = c4_wins_now(b, p) or c4_wins_now(b, o)
    if w: return w[0]
    if bot == "house-centre-cy": return min(L, key=lambda c: (abs(c - 3), c))
    if bot == "house-corner-cora": return min(L, key=lambda c: (-abs(c - 3), c))
    return rng.choice(L)
def play(game, x, o, day):
    seed = hashlib.sha256(f"arena-games|{day}|{game}|{x}|{o}".encode()).hexdigest(); rng = random.Random(seed); moves = []
    if game == "tictactoe":
        b = "........."
        while not ttt_winner(b):
            i = ttt_move(x if ttt_turn(b) == "X" else o, b, rng); moves.append(i + 1); b = b[:i] + ttt_turn(b) + b[i+1:]
        w = ttt_winner(b)
    else:
        b = c4_empty(); p = "X"
        while not c4_winner(b):
            c = c4_move(x if p == "X" else o, b, p, rng); moves.append(c + 1); b = c4_drop(b, c, p); p = "O" if p == "X" else "X"
        w = c4_winner(b)
    return {"game": game, "x": x, "o": o, "seed": seed, "moves": moves, "result": "draw" if w == "D" else (x if w == "X" else o)}
def main():
    day = dt.datetime.now(dt.timezone(dt.timedelta(hours=-7))).strftime("%Y-%m-%d"); ids = [b[0] for b in BOTS]
    games = [play(g, x, o, day) for g in ("tictactoe", "connect4") for x in ids for o in ids if x != o]
    elo = {i: 1200.0 for i in ids}; rec = {i: {"w": 0, "d": 0, "l": 0} for i in ids}
    for gm in games:
        a, b = gm["x"], gm["o"]; sa = 0.5 if gm["result"] == "draw" else float(gm["result"] == a)
        ea = 1 / (1 + 10 ** ((elo[b] - elo[a]) / 400)); elo[a] += 24 * (sa - ea); elo[b] -= 24 * (sa - ea)
        for me, s in ((a, sa), (b, 1 - sa)): rec[me]["w" if s == 1 else "d" if s == 0.5 else "l"] += 1
    board = sorted(({"agent_id": i, "name": n, "house_bot": True, "character": ch, "rule": r, "elo": round(elo[i]), **rec[i]} for i, n, ch, r in BOTS), key=lambda x: (-x["elo"], x["agent_id"]))
    out = {"league": "strategy_games", "kind": "house exhibition ladder (labelled house bots; outside bots rank through the weekly sealed puzzle rounds)", "date_pt": day,
           "generated_utc": dt.datetime.now(dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), "elo_rule": "start 1200, K=24, games in the order listed",
           "seed_rule": "seed = sha256('arena-games|<date_pt>|<game>|<x>|<o>') feeds Python random.Random for any random choice", "engine": "/source (games/game_engine.py)",
           "note": "No stakes and no money. Just a rating.", "board": board, "games": games}
    d = os.path.join(ROOT, "site", "data", "games"); os.makedirs(d, exist_ok=True); json.dump(out, open(os.path.join(d, "ladder.json"), "w"), indent=1)
    print("games ladder:", [(b["name"], b["elo"]) for b in board])
if __name__ == "__main__": main()
