import random
# ==== 확률 계산 유틸 ====
def clamp(prob):
return max(0.05, min(prob, 0.95))
def stat(v): return v / 100 # 0~1 변환
# ==== Player 클래스 ====
class Player:
def __init__(self, name, pos, shoot=70, handle=70, agility=70, physical=70, sense=70):
self.name = name
self.pos = pos
self.shoot = shoot
self.handle = handle
self.agility = agility
self.physical = physical
self.sense = sense
self.points = 0
# ==== Team 클래스 ====
class Team:
def __init__(self, name, players):
self.name = name
self.players = players
self.score = 0
def get(self, pos):
for p in self.players:
if p.pos == pos:
return p
return self.players[0]
def select_rebounder(team):
weights = []
for p in team.players:
# rebounding 스탯 대신, physical + agility + sense의 가중치로만 계산
weight = p.physical * 0.6 + p.agility * 0.2 + p.sense * 0.2 + random.uniform(0, 10)
weights.append(weight)
idx = weights.index(max(weights))
return team.players[idx]
def offensive_rebound_prob(team, rebound_count, team_off_avg=0, team_def_avg=0):
base = 0.5 if rebound_count < 3 else 0.3
# rebounding 스탯은 없으니 PF/C의 physical, sense, agility 평균치로 대체
bigs = [p for p in team.players if p.pos in ["C", "PF"]]
if bigs:
bigs_reb = (sum([p.physical * 0.5 + p.agility * 0.2 + p.sense * 0.3 for p in bigs]) / len(bigs)) / 100
else:
bigs_reb = 0
prob = base + (team_off_avg - team_def_avg) * 0.05 + (bigs_reb * 0.10)
return clamp(prob)
# ==== 덩크/레이업/풋백 확률 ====
def dunk_prob(player):
prob = 0.45 + stat(player.physical)*0.15 + stat(player.agility)*0.05
return clamp(prob)
def layup_prob(player):
prob = 0.30 + stat(player.physical)*0.10 + stat(player.agility)*0.10 + stat(player.sense)*0.05
return clamp(prob)
# ==== 슈팅 확률 ====
def pullup_2pt_prob(player):
prob = 0.20 + stat(player.shoot)*0.25 + stat(player.handle)*0.05 + stat(player.agility)*0.02
return clamp(prob)
def pullup_3pt_prob(player):
prob = 0.15 + stat(player.shoot)*0.30 + stat(player.handle)*0.05 + stat(player.agility)*0.02
return clamp(prob)
def catch_and_shoot_prob(player, pass_count=0):
base_prob = 0.20 + stat(player.shoot)*0.30 + stat(player.agility)*0.02
base_prob += min(pass_count * 0.02, 0.06)
return clamp(base_prob)
def post_up_prob(player):
prob = 0.30 + stat(player.physical)*0.15 + stat(player.sense)*0.05
return clamp(prob)
# ==== 패스 성공 확률 ====
def pass_prob(handler, pass_count=0):
prob = 0.85 + stat(handler.sense)*0.02 - min(pass_count*0.02, 0.06)
return clamp(prob)
def kickout_pass_prob(handler):
prob = 0.70 + stat(handler.handle)*0.05 + stat(handler.sense)*0.05
return clamp(prob)
# ==== Pick&Roll 확률 ====
def screen_prob(screener):
prob = 0.55 + stat(screener.physical)*0.10 + stat(screener.sense)*0.05
return clamp(prob)
def rollman_pass_prob(handler):
prob = 0.65 + stat(handler.handle)*0.05 + stat(handler.sense)*0.05
return clamp(prob)
# ==== 온더볼 메인 ====
def onball(team, opp, pos, pass_count=0, rebound_count=1, log=None):
if log is None: log = []
player = team.get(pos)
if pos == "PG":
branch = random.choices(['pass', 'iso'], [0.55, 0.45])[0]
if branch == 'pass':
t, w = ['SG','C','PF','SF'], [0.36,0.27,0.18,0.19]
target_pos = random.choices(t, w)[0]
handler = player
receiver = team.get(target_pos)
log.append(f"PG 패스 → {receiver.name} ({target_pos})")
if random.random() < pass_prob(handler, pass_count):
made = random.random() < catch_and_shoot_prob(receiver, pass_count+1)
if made:
receiver.points += 2
team.score += 2
log.append(f"{receiver.name} 캐치&슛 성공! +2점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
log.append(f"{receiver.name} 캐치&슛 실패 → 리바운드 싸움")
rebound(team, opp, log, rebound_count)
else:
log.append(f"{receiver.name}에게 패스 실패 → 턴오버/속공")
else:
iso_branch = random.choices(
['pullup2','pullup3','drive_kick','drive_layup','pnr'],
[0.25,0.20,0.25,0.20,0.10])[0]
if iso_branch == 'pullup2':
prob = pullup_2pt_prob(player)
made = random.random() < prob
if made:
player.points += 2
team.score += 2
log.append(f"PG 2점 풀업 ({prob:.2f}) 성공! +2점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
log.append(f"PG 2점 풀업 ({prob:.2f}) 실패 → 리바운드")
rebound(team, opp, log, rebound_count)
elif iso_branch == 'pullup3':
prob = pullup_3pt_prob(player)
made = random.random() < prob
if made:
player.points += 3
team.score += 3
log.append(f"PG 3점 풀업 ({prob:.2f}) 성공! +3점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
log.append(f"PG 3점 풀업 ({prob:.2f}) 실패 → 리바운드")
rebound(team, opp, log, rebound_count)
elif iso_branch == 'drive_kick':
prob = 0.55 + stat(player.handle)*0.20 + stat(player.agility)*0.15 + stat(player.sense)*0.05
if random.random() < prob:
sg = team.get('SG')
if random.random() < kickout_pass_prob(player):
made = random.random() < catch_and_shoot_prob(sg)
if made:
sg.points += 3
team.score += 3
log.append(f"PG Drive&Kick → {sg.name} 오픈 3점 성공! +3점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
log.append(f"PG Drive&Kick → {sg.name} 오픈 3점 실패 → 리바운드")
rebound(team, opp, log, rebound_count)
else:
log.append(f"PG 킥아웃 패스 실패 → 턴오버/속공")
else:
log.append("PG 드라이브 실패 → 리바운드 싸움")
rebound(team, opp, log, rebound_count)
elif iso_branch == 'drive_layup':
prob = 0.45 + stat(player.handle)*0.20 + stat(player.physical)*0.20 + stat(player.agility)*0.10
if random.random() < prob:
if random.random() < dunk_prob(player):
player.points += 2
team.score += 2
log.append("PG 돌파 → 덩크 성공! +2점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
if random.random() < layup_prob(player):
player.points += 2
team.score += 2
log.append("PG 돌파 → 레이업 성공! +2점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
log.append("PG 돌파 실패 → 리바운드")
rebound(team, opp, log, rebound_count)
else:
log.append("PG 돌파 실패 → 리바운드")
rebound(team, opp, log, rebound_count)
elif iso_branch == 'pnr':
screener = team.get(random.choices(['PF','C'], [0.6,0.4])[0])
if random.random() < screen_prob(screener):
if random.random() < 0.4:
prob = 0.55 + stat(player.handle)*0.20 + stat(player.agility)*0.15 + stat(player.sense)*0.05 + 0.10
if random.random() < prob:
if random.random() < dunk_prob(player):
player.points += 2
team.score += 2
log.append("PG PnR 돌파 → 덩크 성공! +2점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
if random.random() < layup_prob(player):
player.points += 2
team.score += 2
log.append("PG PnR 돌파 → 레이업 성공! +2점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
log.append("PG PnR 돌파 실패 → 리바운드")
rebound(team, opp, log, rebound_count)
else:
log.append("PG PnR 돌파 실패 → 리바운드")
rebound(team, opp, log, rebound_count)
else:
if random.random() < rollman_pass_prob(player):
if random.random() < dunk_prob(screener):
screener.points += 2
team.score += 2
log.append(f"PnR 롤맨({screener.name}) 덩크 성공! +2점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
if random.random() < layup_prob(screener):
screener.points += 2
team.score += 2
log.append(f"PnR 롤맨({screener.name}) 레이업 성공! +2점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
log.append(f"PnR 롤맨({screener.name}) 마무리 실패 → 리바운드")
rebound(team, opp, log, rebound_count)
else:
log.append(f"PnR 롤맨 패스 실패 → 턴오버/속공")
else:
log.append("스크린 실패 → PG 드라이브")
prob = 0.45 + stat(player.handle)*0.20 + stat(player.physical)*0.20 + stat(player.agility)*0.10
if random.random() < prob:
if random.random() < dunk_prob(player):
player.points += 2
team.score += 2
log.append("스크린 실패 후 PG 드라이브 → 덩크 성공! +2점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
if random.random() < layup_prob(player):
player.points += 2
team.score += 2
log.append("스크린 실패 후 PG 드라이브 → 레이업 성공! +2점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
log.append("스크린 실패 후 PG 드라이브 실패 → 리바운드")
rebound(team, opp, log, rebound_count)
else:
log.append("스크린 실패 후 PG 드라이브 실패 → 리바운드")
rebound(team, opp, log, rebound_count)
def rebound(team, opp, log, rebound_count):
off_prob = offensive_rebound_prob(team, rebound_count)
if random.random() < off_prob:
rebounder = select_rebounder(team)
log.append(f"공격 리바운드! ({rebounder.name}) Putback 시도")
if random.random() < dunk_prob(rebounder):
rebounder.points += 2
team.score += 2
log.append(f"{rebounder.name} Putback 덩크 성공! +2점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
if random.random() < layup_prob(rebounder):
rebounder.points += 2
team.score += 2
log.append(f"{rebounder.name} Putback 레이업 성공! +2점")
log.append(f"현재 스코어: {team.name} {team.score} : {opp.name} {opp.score}")
else:
log.append(f"{rebounder.name} Putback 실패 → 리바운드 루프")
rebound(team, opp, log, rebound_count+1)
else:
log.append("수비 리바운드! 공격권 전환")
# ==== 샘플 Cavs/Warriors 라인업 ====
cavs = Team("Cavs", [
Player("Kyrie", "PG", 78, 85, 80, 65, 80),
Player("JR Smith", "SG", 77, 65, 75, 65, 68),
Player("LeBron", "SF", 79, 82, 88, 95, 94),
Player("Love", "PF", 80, 58, 68, 83, 75),
Player("Thompson", "C", 66, 54, 70, 90, 70)
])
warriors = Team("Warriors", [
Player("Curry", "PG", 87, 95, 92, 60, 85),
Player("Klay", "SG", 84, 74, 75, 65, 75),
Player("Barnes", "SF", 76, 65, 78, 84, 74),
Player("Green", "PF", 71, 75, 76, 86, 81),
Player("Bogut", "C", 55, 58, 58, 92, 70)
])
# ==== 시뮬레이션 실행 ====
for i in range(10): # 10포제션
log = []
onball(cavs, warriors, "PG", log=log)
print(f"[Cavs 포제션 #{i+1}]")
for l in log: print(l)
print(f"점수: {cavs.score}\n")
log = []
onball(warriors, cavs, "PG", log=log)
print(f"[Warriors 포제션 #{i+1}]")
for l in log: print(l)
print(f"점수: {warriors.score}\n")
To embed this project on your website, copy the following code and paste it into your website's HTML: