体育模拟竞技

# -*- 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()

posted @ 2020-11-21 16:54  小松可可奈  阅读(106)  评论(0)    收藏  举报