# -*- coding: utf-8 -*- """ 季后赛对阵图生成器 ================== 从 games 表按赛季提取季后赛比赛,按系列分组统计胜负, 结合 standings 种子排名,输出标准 bracket 结构(东部/西部/总决赛)。 系列规则:同轮次内共享同一对球队的比赛归为一个系列;胜者 = 胜场多者(平局取最后一场胜者)。 """ import re from collections import OrderedDict from db import query ROUND_ORDER = ["季后赛首轮", "季后赛次轮", "东部决赛", "西部决赛", "总决赛"] ROUND_CN = {"季后赛首轮": "首轮", "季后赛次轮": "半决赛", "东部决赛": "东部决赛", "西部决赛": "西部决赛", "总决赛": "总决赛"} ROUND_STEP = {"季后赛首轮": 0, "季后赛次轮": 1, "东部决赛": 2, "西部决赛": 2, "总决赛": 3} ROUND_INDEX = {r: i for i, r in enumerate(ROUND_ORDER)} def _load_teams(): return {r["id"]: r for r in query("SELECT id, name, code FROM teams")} def _load_standings(season): rows = query("""SELECT s.*, t.name, t.code FROM standings s JOIN teams t ON s.team_id=t.id WHERE s.season=? ORDER BY s.conference DESC, s.rank""", (season,)) by_conf = {"东部": [], "西部": []} for r in rows: if r["conference"] in by_conf: by_conf[r["conference"]].append(r) return by_conf def _series_key(home_id, away_id): return tuple(sorted((home_id, away_id))) def _collect_series(games): """按 (轮次, 球队对) 分组:返回 {(round_name, pair): [(game, home_team, away_team), ...]}""" series = OrderedDict() for g in games: key = (g["round_name"], _series_key(g["home_team_id"], g["away_team_id"])) series.setdefault(key, []).append(g) return series def _match_result(match_games, teams): """统计一个系列:返回 {home:{team,team_id,wins,advance}, away:{...}, games:[game_ids]} 主客方向以第一场为准;每场胜者按实际主客归属计数(主客互换不影响胜场归属)。""" g0 = match_games[0] h_id, a_id = g0["home_team_id"], g0["away_team_id"] h_wins = a_wins = 0 last_winner = None gids = [] for g in match_games: gids.append(g["id"]) if g["status"] != "finished": continue if g["home_score"] is None or g["away_score"] is None: continue winner = g["home_team_id"] if g["home_score"] > g["away_score"] else g["away_team_id"] last_winner = winner if winner == h_id: h_wins += 1 else: a_wins += 1 if h_wins == a_wins: h_wins, a_wins = (1, 0) if last_winner == h_id else (0, 1) h_team, a_team = teams[h_id], teams[a_id] return { "home": {"team": h_team["name"], "team_id": h_id, "code": h_team["code"], "wins": h_wins, "advance": h_wins > a_wins}, "away": {"team": a_team["name"], "team_id": a_id, "code": a_team["code"], "wins": a_wins, "advance": a_wins > h_wins}, "games": gids, } def _winner(m): return m["home"] if m["home"]["advance"] else m["away"] def build_bracket(season): """主入口:返回该赛季对阵图结构 { season, rounds: {first: {east:[m..], west:[m..]}, semi: {...}, conf: {east:m, west:m}, final: m} } """ teams = _load_teams() standings = _load_standings(season) games = query("""SELECT * FROM games WHERE season=? AND (round_name LIKE '季后赛%' OR round_name IN ('东部决赛','西部决赛','总决赛'))""", (season,)) series = _collect_series(games) def find_match(round_name, home_id, away_id): key = (round_name, _series_key(home_id, away_id)) if key in series: return _match_result(series[key], teams) # 无数据系列:按种子高者晋级(占位) return { "home": {"team": teams[home_id]["name"], "team_id": home_id, "code": teams[home_id]["code"], "wins": 0, "advance": True}, "away": {"team": teams[away_id]["name"], "team_id": away_id, "code": teams[away_id]["code"], "wins": 0, "advance": False}, "games": [], "placeholder": True, } out = {"season": season, "rounds": {}} for conf in ("东部", "西部"): ranked = standings.get(conf, []) first, semi = [], [] if len(ranked) >= 8: pairs = [(ranked[0], ranked[7]), (ranked[3], ranked[4]), # 1v8, 4v5(上半区) (ranked[1], ranked[6]), (ranked[2], ranked[5])] # 2v7, 3v6(下半区) first = [find_match("季后赛首轮", p[0]["team_id"], p[1]["team_id"]) for p in pairs] # 次轮:上半区胜者 vs 下半区胜者(1/8胜 vs 4/5胜,2/7胜 vs 3/6胜) w1, w2 = _winner(first[0]), _winner(first[1]) w3, w4 = _winner(first[2]), _winner(first[3]) semi = [find_match("季后赛次轮", w1["team_id"], w2["team_id"]), find_match("季后赛次轮", w3["team_id"], w4["team_id"])] out["rounds"]["first"] = out["rounds"].get("first", {}) out["rounds"]["semi"] = out["rounds"].get("semi", {}) out["rounds"]["first"][conf] = first out["rounds"]["semi"][conf] = semi # 分区决赛 if len(semi) >= 2: c1, c2 = _winner(semi[0]), _winner(semi[1]) out["rounds"].setdefault("conf", {})[conf] = find_match( "东部决赛" if conf == "东部" else "西部决赛", c1["team_id"], c2["team_id"]) # 总决赛 east_c, west_c = out["rounds"].get("conf", {}).get("东部"), out["rounds"].get("conf", {}).get("西部") if east_c and west_c: e, w = _winner(east_c), _winner(west_c) out["rounds"]["final"] = find_match("总决赛", e["team_id"], w["team_id"]) return out def seasons(): """可用赛季列表(standings 有数据的),降序""" rows = query("SELECT DISTINCT season FROM standings ORDER BY season DESC") return [r["season"] for r in rows]