here is the code so far. it looks better but still doesn't work. let's debug it. At the beginning of each round lets print all the teams.
import argparse
import csv
import math
import os
from collections import Counter
from typing import List

import dataclasses
import numpy as np
from numpy.ma import exp
from scipy.optimize import minimize\_scalar
from scipy.special import expit

PARENT\_DIR = os.path.dirname(os.path.realpath(\_\_file\_\_))

HISTORICAL\_SEED\_WIN\_RATES = {
 (1, 16): 1.000,
 (2, 15): 0.917,
 (3, 14): 0.833,
 (4, 13): 0.750,
 (5, 12): 0.667,
 (6, 11): 0.583,
 (7, 10): 0.583,
 (8, 9): 0.500,
}
@dataclasses.dataclass
class Team:
 team\_name: str
 team\_seed: int
 ken\_pom\_score: float

 @classmethod
 def extract\_teams(cls, file\_path: str):
 with open(os.path.join(PARENT\_DIR, file\_path), "r", newline="") as csvfile:
 return [cls(
 team\_name=row["team"],
 team\_seed=int(row["seed"]),
 ken\_pom\_score=float(row["score"])
 ) for row in (csv.DictReader(csvfile))]
class Tournament:

 def \_\_init\_\_(self, team\_metrics: List[Team]):
 self.team\_metrics: List[Team] = team\_metrics
 self.team\_names = [team.team\_name for team in team\_metrics]
 self.teams = {i: team.team\_name for i, team in enumerate(team\_metrics)}
 self.k = self.find\_best\_k()
 self.adj\_matrix = self.calculate\_adj\_matrix()

 def get\_opponent(self, team\_index):
 opponent\_index = team\_index + 1 if team\_index % 2 == 0 else team\_index - 1
 return self.teams[opponent\_index]

 def calculate\_adj\_matrix(self):
 num\_teams = len(self.team\_metrics)
 adj\_matrix = np.zeros((num\_teams, num\_teams))

 for i, team\_i in enumerate(self.team\_metrics):
 for j, team\_j in enumerate(self.team\_metrics):
 if i != j:
 p\_win = self.calculate\_win\_probability(team\_i, team\_j)
 adj\_matrix[i, j] = p\_win
 adj\_matrix[j, i] = 1 - p\_win

 return adj\_matrix

 def calculate\_win\_probability(self, team\_i: Team, team\_j: Team):
 seed\_diff = team\_j.team\_seed - team\_i.team\_seed
 ken\_pom\_diff = team\_i.ken\_pom\_score - team\_j.ken\_pom\_score
 return expit(self.k \* (ken\_pom\_diff + seed\_diff))

 def play\_single\_round(self):
 winners = []
 for i in range(0, len(self.teams), 2):
 p\_win\_i = self.adj\_matrix[i, i + 1]
 win\_i = np.random.rand() < p\_win\_i
 winning\_team\_index = 1 if win\_i else 1 + 1
 winners.append(winning\_team\_index)

 return winners

 def print\_round\_results\_without\_round\_num(self, round\_win\_counts, num\_simulations):
 for i, (team\_name, count) in enumerate(round\_win\_counts.most\_common()):
 team\_index = list(self.teams.values()).index(team\_name)
 team = self.teams[team\_index]
 opponent\_index = self.get\_opponent(team\_index)
 opponent = self.teams[opponent\_index]
 win\_percentage = (count / num\_simulations) \* 100

 print(f" {i + 1}. {team} over {opponent} with {win\_percentage:.2f}%")
 print()

 def run\_rounds\_recursive(self, num\_simulations):
 if len(self.teams) == 1:
 return

 winners\_indices = self.play\_single\_round()

 winners = [self.teams[winner\_index] for winner\_index in winners\_indices]
 round\_win\_counts = Counter(winners)

 self.print\_round\_results\_without\_round\_num(round\_win\_counts, num\_simulations)

 # Update the team list and simulate the next round with the winners
 updated\_teams = [
 self.get\_team\_by\_name(team\_name)
 for team\_name, count in round\_win\_counts.items()
 if count / num\_simulations > 0.5
 ]
 updated\_tournament = Tournament(updated\_teams)
 updated\_tournament.run\_rounds\_recursive(num\_simulations)

 def get\_team\_by\_name(self, team\_name: str):
 for team in self.team\_metrics:
 if team.team\_name == team\_name:
 return team
 return None

 @staticmethod
 def error\_function(k, average\_kenpom\_difference):
 error = 0
 for matchup, historical\_probability in HISTORICAL\_SEED\_WIN\_RATES.items():
 difference = average\_kenpom\_difference[matchup]
 probability = 1 / (1 + exp(-k \* difference))
 error += (probability - historical\_probability) \*\* 2
 return error

 @staticmethod
 def average\_kenpom\_difference(max\_seed=16, kenpom\_range=(0, 40)):
 min\_kenpom, max\_kenpom = kenpom\_range
 kenpom\_increment = (max\_kenpom - min\_kenpom) / max\_seed
 average\_difference = {}

 for higher\_seed in range(1, max\_seed + 1):
 for lower\_seed in range(higher\_seed + 1, max\_seed + 1):
 higher\_seed\_kenpom = max\_kenpom - (higher\_seed - 1) \* kenpom\_increment
 lower\_seed\_kenpom = max\_kenpom - (lower\_seed - 1) \* kenpom\_increment
 average\_difference[(higher\_seed, lower\_seed)] = higher\_seed\_kenpom - lower\_seed\_kenpom

 return average\_difference

 @staticmethod
 def find\_best\_k():
 average\_difference = Tournament.average\_kenpom\_difference()
 result = minimize\_scalar(Tournament.error\_function, args=(average\_difference,))
 return result.x
if \_\_name\_\_ == "\_\_main\_\_":
 parser = argparse.ArgumentParser(
 description="NCAA Tournament Simulation"
 )
 parser.add\_argument(
 '-f', '--file',
 default='2023ncaab.csv',
 help="Path to the data file (default: '2023ncaab.csv')"
 )
 parser.add\_argument(
 '-n', '--num-simulations',
 type=int,
 default=100000,
 help="Number of simulations to run (default: 100000)"
 )
 args = parser.parse\_args()
 teams = Team.extract\_teams(args.file)
 tournament = Tournament(teams)
 tournament.run\_rounds\_recursive(args.num\_simulations)
지금까지 작성한 코드입니다. 더 좋아 보이지만 여전히 작동하지 않습니다. 디버깅해 봅시다. 각 라운드가 시작될 때마다 모든 팀을 인쇄해 봅시다.import argparseimport csvimport mathimport os컬렉션에서 카운터 임포트입력에서 임포트 목록데이터 클래스 임포트numpy를 np로 임포트numpy.ma에서 exp 임포트scipy.optimize에서 가져 오기 최소화\_스칼라from scipy.special import expitPARENT\_DIR = os.path.dirname(os.path.realpath(\_\_file\_\_))historical\_seed\_win\_rates = { (1, 16): 1.000, (2, 15): 0.917, (3, 14): 0.833, (4, 13): 0.750, (5, 12): 0.667, (6, 11): 0.583, (7, 10): 0.583, (8, 9): 0.500,}@dataclasses.dataclass팀 클래스: team\_name: str 팀\_씨앗: int ken\_pom\_score: float @classmethod def extract\_teams(cls, file\_path: str): with open(os.path.join(PARENT\_DIR, file\_path), "r", newline="") as csvfile: return [cls( team\_name=row["team"], team\_seed=int(row["seed"]), ken\_pom\_score=float(row["score"]) ) for row in (csv.DictReader(csvfile))]토너먼트 클래스: def \_\_init\_\_(self, team\_metrics: List[Team]): self.team\_metrics: List[Team] = team\_metrics self.team\_names = [team\_metrics의 팀에 대한 팀.팀\_이름] self.teams = {i: 팀에 대한 팀.팀\_이름, 팀에 대한 열거(팀\_메트릭)} self.k = self.find\_best\_k() self.adj\_matrix = self.calculate\_adj\_matrix() def get\_opponent(self, team\_index): opponent\_index = team\_index + 1 if team\_index % 2 == 0 else team\_index - 1 return self.teams[상대팀\_index] def calculate\_adj\_matrix(self): num\_teams = len(self.team\_metrics) adj\_matrix = np.zeros((num\_teams, num\_teams)) for i, team\_i in enumerate(self.team\_metrics): for j, team\_j in enumerate(self.team\_metrics): if i != j: p\_win = self.calculate\_win\_probability(team\_i, team\_j) adj\_matrix[i, j] = p\_win adj\_matrix[j, i] = 1 - p\_win 반환 adj\_matrix def calculate\_win\_probability(self, team\_i: 팀, team\_j: 팀): seed\_diff = team\_j.team\_seed - team\_i.team\_seed ken\_pom\_diff = team\_i.ken\_pom\_score - team\_j.ken\_pom\_score return expit(self.k \* (ken\_pom\_diff + seed\_diff)) def play\_single\_round(self): winners = [] for i in range(0, len(self.teams), 2): p\_win\_i = self.adj\_matrix[i, i + 1] win\_i = np.random.rand() < p\_win\_i winning\_team\_index = 1 if win\_i else 1 + 1 winners.append(winning\_team\_index) 우승자 반환 def print\_round\_results\_without\_round\_num(self, round\_win\_counts, num\_simulations): for i, (team\_name, count) in enumerate(round\_win\_counts.most\_common()): team\_index = list(self.teams.values()).index(team\_name) 팀 = self.teams[team\_index] 상대\_인덱스 = self.get\_opponent(팀\_인덱스) 상대 = self.teams[상대\_index] win\_percentage = (카운트/넘\_simulations) \* 100 print(f" {i + 1}. {팀}이 {상대팀}을 {승\_퍼센트:.2f}%로 이겼습니다.") print() def run\_rounds\_re귀적(self, num\_simulations): if len(self.teams) == 1: return winners\_indices = self.play\_single\_round() winners = [self.teams[winner\_index] for winner\_index in winners\_indices] 라운드\_승자\_counts = 카운터(우승자) self.print\_라운드\_결과\_없이\_라운드\_넘버(라운드\_승리\_counts, 넘버\_simulations) # 팀 목록을 업데이트하고 승자와 함께 다음 라운드를 시뮬레이션합니다. updated\_teams = [ self.get\_team\_by\_name(팀\_이름) for team\_name, count in round\_win\_counts.items() if count / num\_simulations > 0.5 ] 업데이트된\_tournament = 토너먼트(업데이트된\_팀) updated\_tournament.run\_rounds\_recursive(num\_simulations) def get\_team\_by\_name(self, team\_name: str): for team in self.team\_metrics: if team.team\_name == team\_name: 반환 팀 반환 없음 정적 메서드 def error\_function(k, average\_kenpom\_difference): error = 0 매치업의 경우, HISTORICAL\_SEED\_WIN\_RATES.items()의 historical\_probability: 차이 = 평균\_켄폼\_차이[매치업] 확률 = 1 / (1 + exp(-k \* 차이)) error += (확률 - 과거\_확률) \*\* 2 반환 오류 정적 메서드 def average\_kenpom\_difference(max\_seed=16, kenpom\_range=(0, 40)): MIN\_KENPOM, MAX\_KENPOM = KENPOM\_RANGE kenpom\_increment = (max\_kenpom - min\_kenpom) / max\_seed 평균\_차이 = {} 범위(1, 최대\_씨드 + 1)에서 더 높은\_씨드의 경우: FOR LOWER\_SEED IN RANGE(HIGHER\_SEED + 1, MAX\_SEED + 1): higher\_seed\_kenpom = max\_kenpom - (higher\_seed - 1) \* kenpom\_increment lower\_seed\_kenpom = max\_kenpom - (lower\_seed - 1) \* kenpom\_increment average\_difference[(higher\_seed, lower\_seed)] = higher\_seed\_kenpom - lower\_seed\_kenpom 반환 평균\_difference @staticmethod def find\_best\_k(): average\_difference = Tournament.average\_kenpom\_difference() result = minimize\_scalar(Tournament.error\_function, args=(average\_difference,)) 반환 결과.xif \_\_name\_\_ == "\_\_main\_\_": parser = argparse.ArgumentParser( description="NCAA 토너먼트 시뮬레이션" ) parser.add\_argument( '-f', '--file', default='2023ncaab.csv'