# -*- encoding:utf-8 -*-
'''
模拟乒乓球竞技
@author: bpf
'''
# 比赛规则:
# 1. 一场比赛: 单打:采用七局四胜制
# 双打淘汰赛、团体赛:采用五局三胜制
# 2. 一局比赛: 先得11分为胜,10平后,多得2分为胜
# 3. 一局比赛: 每队发球2次后,接发球方即成为发球方,依此类推,直至该局比赛结束
# 或者到双方比分都达到10分时,发球和接发次序仍然不变,但每队只轮发一次球
from random import random
from pandas import DataFrame
from time import time
class SportCompetitionAnalyze:
def PrintInfo(self):
'''
function: 打印程序的介绍信息
'''
print("{:*^70}".format("产品简介"))
print("产品名称: 乒乓球竞技模拟分析器(采取单打淘汰赛制)")
print("产品概述: 通过输入多个队伍的能力值(0到1之间的小数表示),能够模拟多次多个队伍的乒乓球竞技比赛,从而得出各自的胜率!")
print("产品作者: 19统计 叶若琳 30")
print("{:*^70}".format("模拟开始"))
def GetInputs_for_Singal(self):
'''
function: 获得用户输入的参数 获得单打各队员的能力值
'''
self.n = eval(input("请输入需要模拟比赛的场数:"))
self.probAbilityList_Original = list(map(eval, input("(注:通过输入的次序进行两两配对, 即前两个分别为队员A和B;以此类推。)\n\
请输入各队员的能力值(0~1), 请用英文逗号隔开(输入个数为2的倍数): ").split(',')))
self.probAbilityList = self.probAbilityList_Original
self.probNum = len(self.probAbilityList)
def GetInputs_for_Double(self):
'''
function: 获得用户输入的参数 获得双打各队伍各队员的能力值
probAbilityList: 使用各队伍中两队员的平均能力值作为该队伍的能力值 --- 双打
probAbilityList_Original: 存储原生的各队员能力值, 后续可用
'''
self.probAbilityList, self.probAbilityList_Original = [], []
self.n = eval(input("请输入需要模拟比赛的场数:"))
Original = list(map(eval, input("(注:通过输入的次序进行四四配对, 即前四个为队伍A和B;以此类推。)\n\
请输入各队员的能力值(0~1), 请用英文逗号隔开(输入个数为4的倍数): ").split(',')))
for i in range(0, len(Original), 2):
self.probAbilityList.append((Original[i] + Original[i+1])/2)
self.probAbilityList_Original.append(list([Original[i], Original[i+1]]))
self.probNum = len(self.probAbilityList)
def PrintResult(self):
'''
function: 输出模拟比赛的结果
data: 存储每支的比赛信息
'''
print("{:*^70}".format("模拟结束"))
print("竞技分析结束,每组共模拟{}场比赛。".format(self.n))
data = []
for i in range(self.probNum):
tmplist = []
tmplist.append(self.probAbilityList_Original[i]) # 存储能力值
tmplist.append(self.probwinsList[i]) # 存储获胜的场数
tmplist.append(self.probwinsList[i]/self.n) # 存储胜率
data.append(tmplist)
dataSheet = DataFrame(data , index=list(range(1, self.probNum+1)), columns=list(["Ability", "wins", "rate"]))
#dataSheet.sort_values(by="wins", inplace=True) # 对比赛胜率rate进行排序, 会混乱比赛队伍的关系, 因此不采用
print(dataSheet)
def simNGames(self, GAMES, WINS):
'''
function: 模拟n场比赛
probwinsList: 存储每支队伍赢得比赛的场数 的列表
winA, winB: 队伍A和B在一场比赛中获胜的局数
winsA, winsB: 队伍A和B赢得比赛的场数,总共n场
'''
self.probwinsList = []
for i in range(0, self.probNum, 2):
print("队员:", i+1, 'VS' ,i+2, "比赛中...")
winsA, winsB = 0, 0
for _ in range(self.n):
winA, winB = self.simOneGame(self.probAbilityList[i], self.probAbilityList[i+1], GAMES, WINS)
if winA > winB:
winsA += 1
else:
winsB += 1
self.probwinsList.append(winsA)
self.probwinsList.append(winsB)
def simOneGame(self, probA, probB, GAMES, WINS):
'''
function: 模拟一场比赛 》》》 GAMES局 WINS胜
单打比赛,包括七局,采取七局四胜制
双打比赛,包括五局,采取五局三胜制
scoreA, scoreB: 分别为队伍A和B一局比赛的分数
winA, winB: 分别为队伍A和B一场比赛赢的局数
return: 返回双方赢的局数
'''
winA, winB = 0, 0
for _ in range(GAMES):
scoreA, scoreB = self.simAGame(probA, probB)
if scoreA > scoreB:
winA += 1
else:
winB += 1
if winA >=WINS or winB >= WINS:
break
return winA, winB
def simAGame(self, probA, probB):
'''
function: 模拟一局比赛
probA, probB: 分别为队伍A和B的能力值
return: 返回队伍A和B在本局比赛中获得的分数
'''
scoreA, scoreB = 0, 0
serving = 'A' # 发球方
servingNum = 2 # 每方的发球次数
while not self.GameOver(scoreA, scoreB):
if scoreA >= 10 and scoreB >= 10:
servingNum = 1
for _ in range(servingNum):
if random() > probA:
scoreB += 1
else:
scoreA += 1
serving = 'B'
for _ in range(servingNum):
if random() > probB:
scoreA += 1
else:
scoreB += 1
serving = 'A'
return scoreA, scoreB
def GameOver(self, scoreA, scoreB):
'''
function: 定义赢得一局的条件: 先得11分为胜,10平后,多得2分为胜
'''
if scoreA >= 11 or scoreB >= 11:
return (abs(scoreA-scoreB)>=2)
elif (scoreA == 10 and scoreB > 11) or (scoreB == 10 and scoreA > 11):
return (abs(scoreA-scoreB)>=2)
else:
return 0
def print_MENU():
print("程序简介:模拟乒乓球竞技")
print("程序功能:\n\t1. 模拟多队员进行单打比赛\n\t2. 模拟多队伍多队员进行双打比赛")
while 1:
choose = input("功能选择:NO.")
if choose in ['1', '2']:
return eval(choose)
else:
print("输入有误, 请重新输入!")
def simGameMenu():
choose = print_MENU()
if choose == 1:
pingpong = SportCompetitionAnalyze()
pingpong.PrintInfo()
pingpong.GetInputs_for_Singal()
Time = time()
pingpong.simNGames(7, 4)
print("模拟用时: {:.1f}s".format(time()-Time))
pingpong.PrintResult()
else:
pingpong = SportCompetitionAnalyze()
pingpong.PrintInfo()
pingpong.GetInputs_for_Double()
Time = time()
pingpong.simNGames(5, 3)
print("模拟用时: {:.1f}s".format(time()-Time))
pingpong.PrintResult()
if __name__ == "__main__":
simGameMenu()