【问题标题】:Generating NBA play by plays per quarter通过每季度的比赛创造 NBA 比赛
【发布时间】:2021-01-14 18:55:05
【问题描述】:

我正在尝试生成每节 NBA 的逐场比赛。

我的做法是选择一支球队在四分之一内得分,然后根据这些分数进行比赛。

假设我选择 A 队在一个季度中得到 5 分。我做了一个算法来生成这个示例列表:

[ ['2 指针','SWITCH'], ['MISSED 2 指针', '进攻篮板', 'STAY'], ['3 指针', 'SWITCH']]]

(这个列表比实际生成的要简单得多。我添加了更多的特性,比如助攻和篮板,除了抢断和失误。)

'SWITCH' 表示切换控球权,而 'STAY' 表示当前球队保持控球权。每个“SWITCH”都需要在另一个团队的列表中至少有一个可用的操作,而每个“STAY”需要在当前团队的列表中至少有一个可用的操作。

现在,当我为 2 个不同的团队创建两个列表时,它们的列表长度可能会有所不同,因为(除其他外)一个团队的得分可能高于另一个。

当我尝试将两个列表连接在一起以创建逐个播放模拟时,这可能会导致一个团队在另一个团队中有“SWITCH”时没有任何动作。

我的问题是如何将额外的“SWITCH”或“STAY”添加到任何团队列表中,以便每个“SWITCH”都会在另一个团队的列表中执行操作,并且每个“STAY”都会在当前团队的列表中执行操作。

我的第一个想法是在列表中添加随机失误和抢断(导致“SWITCH”)并希望它会起作用,但我想不出办法来解决这个问题。

此外,只要列表满足所需的得分点数,初始团队列表的顺序无关紧要。

这是我目前所拥有的:

def join_steals_tov(teamA_list, teamB_list):

overall_list = []

possession = 'Team A'

print(len(teamA_list))
print(len(teamB_list))

while teamA_list and teamB_list:
    
    if possession == 'Team A':
        play = teamA_list.pop(0)
        play.insert(0,'Team A')
        overall_list.append(play)
        print(f"Team A: {play}")
        
        if play[-1] == 'SWITCH':
            possession = 'Team B'
    else:
        play = teamB_list.pop(0)
        play.insert(0,'Team B')
        overall_list.append(play)
        print(f"Team B: {play}")
        
        if play[-1] == 'SWITCH':
            possession = 'Team A'
    
return teamA_list, teamB_list, overall_list

【问题讨论】:

  • 其他球队得分多少重要吗?或者您只是想为 1 支球队逐场比赛?
  • 是的。所以最初,我为两个独立的团队创建了两个列表。无论我设置多少点,一个列表都会加起来。因此,例如,一个列表将加起来为 20 分,这意味着与该列表对应的团队将获得 20 分。然后,我尝试将这 2 个列表合并在一起以形成播放数据。
  • 我想我可以在每个列表中随机添加失误和抢断,但是 1) 我不知道它会如何工作 2) 这样做可能只会让它变得更复杂。也许我不能从所需的点数开始?我现在很困
  • 好的,我会考虑一下。我认为有几种方法可以使用概率来创建这些随机事件。

标签: python algorithm linked-list


【解决方案1】:

这是生成一个季度的逐个播放列表的一种可能方法,从两个得分事件列表开始,例如

teamA_scores = ['A2', 'A3']          
teamB_scores = ['B3', 'B3', 'B2']

步骤1:将列表组合成一个列表,并将组合列表打乱,例如

scores: ['A3', 'B3', 'A2', 'B3', 'B2']
          ^^ --- team A scores 3 points

第2步:当同一队连续两次得分时,必须有另一队的非得分事件。因此,创建一个新的播放列表,其中包括那些强制 SWITCH 播放,例如

switch: ['A3', 'B3', 'A2', 'B3', 'AS', 'B2']
                                  ^^ --- team A gives the ball back to team B without scoring

第 3 步:将季末比赛添加到列表中(只是为了让下一步更容易),例如

end Q:  ['A3', 'B3', 'A2', 'B3', 'AS', 'B2', 'AQ']
                                              ^^ --- time runs out with team A in possession

第 4 步:现在添加一些额外的非得分比赛来填写该季度。在示例代码中,播放列表被填充到任意长度 10。为了真实,您需要为每个播放分配一个时间,并填充列表直到总时间为 12 分钟。

plays:  ['AK', 'A3', 'B3', 'AS', 'BS', 'A2', 'B3', 'AS', 'B2', 'AQ']
          ^^ --- team A keeps the ball, e.g. missed shot, offensive rebound
                            ^^ --- team A loses the ball
                                  ^^ --- team B loses the ball

这里有一些示例代码,展示了如何从两个得分播放列表中创建 10 个播放列表:

from random import shuffle
from random import randrange
from random import randint

# helper functions
def getTeam(event):
    return event[0]

def makeSameTeamItem(event, item):
    if getTeam(event) == 'A':
        return 'A' + item
    else:
        return 'B' + item

def makeOtherTeamItem(event, item):
    if getTeam(event) == 'B':
        return 'A' + item
    else:
        return 'B' + item

# start with a list of scores for each team
teamA_scores = ['A2', 'A3']
teamB_scores = ['B3', 'B3', 'B2']

# step 1: combine and shuffle to create an overall list of scores
scores = teamA_scores + teamB_scores
shuffle(scores)
print 'scores:', scores

# step 2: create list of plays including mandatory SWITCH plays
plays = []
plays.append(scores[0])
for i in range(1, len(scores)):
   if getTeam(scores[i]) == getTeam(scores[i-1]):
      plays.append(makeOtherTeamItem(scores[i], 'S'))
   plays.append(scores[i])
print 'switch:', plays

# step 3: add the end of quarter play
plays.append(makeOtherTeamItem(scores[-1], 'Q'))
print 'end Q: ', plays

# step 4: insert additional non-scoring plays to fill the quarter
quarter = 10
while len(plays) < quarter:
   index = randrange(0, len(plays))
   if quarter - len(plays) == 1 or randint(1,2) == 1:
      plays.insert(index, makeSameTeamItem(plays[index], 'K'))
   else:
      plays.insert(index, makeOtherTeamItem(plays[index], 'S'))
      plays.insert(index, makeOtherTeamItem(plays[index], 'S'))
print 'plays: ', plays

【讨论】:

  • 这是一个很好的方法。先把计分项记下来,然后用非计分的比赛填写同一队的连续计分项。这不是我的问题,但如果我还试图添加某个非得分事件的“窄范围”怎么办?例如,如果 NBA 球队的 2 分球命中率为 50%,则错过的 2 分球数必须“大致”(给予或接受 1 或 2)与命中的 2 分球数相同。以及如何进一步增加助攻、进攻篮板、防守篮板?我会从你的方法开始尝试自己解决这个问题,所以我会保持更新,但是如果你
  • (续...)在我弄明白之前就知道该怎么做,那太好了!
  • @theyeinkye 如果列表中有很多非得分事件,这很容易。例如,如果有 10 个“A2”事件和 20 个非评分事件(“AS”或“AK”),那么您可以在 9 到 11 个非评分事件之间随机选择,并将它们转换为“AM2”事件( A 队 2 分不中)。你必须为 3 个指针做同样的事情。其余的 'AS' 和 'AK' 被转换成球出界、盖帽、外带、进攻点球等。
  • 最好将事件分为得分和不得分,并根据事件百分比对它们进行分区。我试试看,谢谢。
【解决方案2】:

我认为这是使用马尔可夫链的好方法。看看here

基本思想是,对于每个当前状态,都有可能进入下一个状态。您可以创建当前状态是什么的概率矩阵,以及下一个状态可能是什么的概率。然后使用这些权重从random.choices 中拉出。你可以让它变得像你想要的那样复杂。关键是从简单开始,让它发挥作用,然后你可以添加更多并修饰它(例如,我要添加 3 分未命中和 3 分取得的状态,(你还必须包括丢 2 分、投 2 分的概率……或者你可以把它作为投篮投丢和投篮命中的延伸),但我想我会把它留给你处理并在那里添加。你也可以添加在进攻和防守犯规中,如果是防守投篮犯规,投篮是否有助攻的概率等)

这只是设置一些功能来跟踪一切并“发挥”控球权的问题。

这是我的例子:

1.为每个团队创建概率矩阵。索引将是当前状态,列是下一个状态。

注意:您希望每行的总和为 1

import pandas as pd
import random

prob_matrix_A = pd.DataFrame([
[0, 0.05,   0.1,    0.375,  0.475,  0,  0,  0],
[0, 0,  0,  0,  0,  0,  0,  1],
[0, 0,  0,  0,  0,  0,  0,  1],
[0, 0.02,   0,  0,  0,  0.19,   0.79,   0],
[0, 0,  0,  0,  0,  0,  0,  1],
[1, 0,  0,  0,  0,  0,  0,  0],
[0, 0,  0,  0,  0,  0,  0,  1],
[0, 0, 0, 0, 0, 0, 0, 0]],
columns = ['Possession', 'Unforced Turnover','Steal','Miss Shot','Made Shot','Off Reb','Def Rebound', 'Change Possession'],
index = ['Possession', 'Unforced Turnover','Steal','Miss Shot','Made Shot','Off Reb','Def Rebound', 'Change Possession'])


prob_matrix_B = pd.DataFrame([
[0, 0.05,   0.1,    0.475,  0.375,  0,  0,  0],
[0, 0,  0,  0,  0,  0,  0,  1],
[0, 0,  0,  0,  0,  0,  0,  1],
[0, 0.02,   0,  0,  0,  0.09,   0.89,   0],
[0, 0,  0,  0,  0,  0,  0,  1],
[1, 0,  0,  0,  0,  0,  0,  0],
[0, 0,  0,  0,  0,  0,  0,  1],
[0, 0, 0, 0, 0, 0, 0, 0]],
columns = ['Possession', 'Unforced Turnover','Steal','Miss Shot','Made Shot','Off Reb','Def Rebound', 'Change Possession'],
index = ['Possession', 'Unforced Turnover','Steal','Miss Shot','Made Shot','Off Reb','Def Rebound', 'Change Possession'])

这是 A 队的概率矩阵:

print (prob_matrix_A.to_string())
                   Possession  Unforced Turnover  Steal  Miss Shot  Made Shot  Off Reb  Def Rebound  Change Possession
Possession                  0               0.05    0.1      0.375      0.475     0.00         0.00                  0
Unforced Turnover           0               0.00    0.0      0.000      0.000     0.00         0.00                  1
Steal                       0               0.00    0.0      0.000      0.000     0.00         0.00                  1
Miss Shot                   0               0.02    0.0      0.000      0.000     0.19         0.79                  0
Made Shot                   0               0.00    0.0      0.000      0.000     0.00         0.00                  1
Off Reb                     1               0.00    0.0      0.000      0.000     0.00         0.00                  0
Def Rebound                 0               0.00    0.0      0.000      0.000     0.00         0.00                  1
Change Possession           0               0.00    0.0      0.000      0.000     0.00         0.00                  0

2。设置如何记录比分以及哪支球队拥有球权:

team_score_limit = 25


# game setup
game_dict = {'team_a':{'score':0, 'possession':False, 'prob_matrix': prob_matrix_A},
             'team_b':{'score':0, 'possession':False, 'prob_matrix': prob_matrix_B},
             'score_limit':team_score_limit}

# Do a jump ball to see who starts with ball
# Lets say team_a has 55% of getting the jump, and team_b 45%
wins_jump = random.choices(['team_a','team_b'], weights=[.55,.45], k=1)[0]
game_dict[wins_jump]['possession'] = True
print('%s: Wins the jump ball' %wins_jump)

3.创建函数来确定谁拥有球权(即谁在进攻、谁在防守):

# Function to return who has possession
def possession(game_dict):
    for k, v in game_dict.items():
        if type(v) == dict:
            if v['possession'] == True:
                offense_team = k
            if v['possession'] == False:
                defense_team = k
    return offense_team, defense_team
        

4.创建函数以根据概率进行控球:

def play(current_state, game_dict):
    # Determine who has possession and get their prob matrix
    offense, defense = possession(game_dict)
    prob_matrix = game_dict[offense]['prob_matrix']
    states_list = prob_matrix.columns.tolist()
    weights = prob_matrix.loc[current_state,:].tolist()
    
    # Based on current state weights, get the next state
    new_state = random.choices(states_list, weights=weights, k=1)[0]
    
    if 'Possession' not in new_state:
        if new_state in ['Def Rebound', 'Steal']:
            print ('%s: %s by %s' %(offense, new_state, defense))
        else:
            print ('%s: %s' %(offense, new_state))
    current_state = new_state
    

    if current_state == 'Made Shot':
        game_dict[offense]['score'] += 2
        
        # if team_a score reached the limit, we'll return False to stop the game
        if game_dict['team_a']['score'] >= game_dict['score_limit']:
            return False
 
    # if it's a change of possession, we'll update our game_dict
    if new_state == 'Change Possession':
        game_dict[offense]['possession'] = False
        game_dict[defense]['possession'] = True
    
    # else the team who currently possess the ball didn't change from the previous outcome (Ie. Offensive rebound), then they get another possession play 
    else:
        play(current_state, game_dict)

5.设置初始current_state 开始“游戏”并进行游戏:

# Play the first quarter 
current_state = 'Possession'   
continue_play = True   
while continue_play != False:    
    continue_play = play(current_state, game_dict)    
    
    
    
print ('\nEnd of 1st Quarter!\n')
print ('**************************')
print ('team_a: %s\tteam_b: %s' %(game_dict['team_a']['score'],game_dict['team_b']['score']))

输出:

runfile('test.py', wdir='C:/test')
team_a: Wins the jump ball
team_a: Made Shot
team_b: Miss Shot
team_b: Off Reb
team_b: Made Shot
team_a: Made Shot
team_b: Made Shot
team_a: Miss Shot
team_a: Def Rebound by team_b
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Steal by team_b
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Miss Shot
team_a: Def Rebound by team_b
team_b: Made Shot
team_a: Miss Shot
team_a: Def Rebound by team_b
team_b: Steal by team_a
team_a: Made Shot
team_b: Made Shot
team_a: Made Shot
team_b: Made Shot
team_a: Made Shot
team_b: Steal by team_a
team_a: Made Shot
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Unforced Turnover
team_b: Made Shot
team_a: Steal by team_b
team_b: Made Shot
team_a: Unforced Turnover
team_b: Steal by team_a
team_a: Made Shot
team_b: Unforced Turnover
team_a: Miss Shot
team_a: Off Reb
team_a: Made Shot
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Made Shot
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Made Shot
team_b: Made Shot
team_a: Made Shot
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Miss Shot
team_a: Off Reb
team_a: Made Shot
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Made Shot

End of 1st Quarter!

**************************
team_a: 26      team_b: 16

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 2015-10-07
    • 1970-01-01
    • 1970-01-01
    • 2015-09-16
    • 2017-07-14
    相关资源
    最近更新 更多