以下介绍的这个程序,用Python较为简单地模拟出多人石头剪刀布的所有情况以及输赢结果。
子函数编写
在编写主程序前,对需要使用到的几个函数先进行编写。
判断输赢的函数check()
在这里我们用 ‘a’ 表示石头,‘b’ 表示剪刀,‘c’ 表示布。
提供的参数是一个字典,键是出手势的人(用1、2、3等数字表示),值是手势(用a、b、c表示)。
如果出现了三种手势或只出现一种,则很显然是平局。
如果出现了两种手势,那么判断输赢。
最后返回所有获胜的人的号码列表。
代码如下:
# Check.py
def check(check_list):
'''a代表石头,b代表剪刀,c代表布,判断获胜的选手'''
result = list(check_list.values())
# 如果石头剪刀布都出现或只出现一个,就是平局
if len(set(result)) == 3 or len(set(result)) == 1:
return None
winners = []
# 石头对剪刀,石头赢
if set(result) == {'a','b'}:
# 找出所有出石头的人
for k,v in check_list.items():
if v == 'a':
winners.append(k)
# 石头对布,布赢
if set(result) == {'a','c'}:
# 找出所有出布的人
for k,v in check_list.items():
if v == 'c':
winners.append(k)
# 剪刀对布,剪刀赢
if set(result) == {'b','c'}:
# 找出所有出剪刀的人
for k,v in check_list.items():
if v == 'b':
winners.append(k)
return winners
列举所有情况的函数
原理:为了快速且正确的列举所有排列情况(这里其实就是a、b、c三个字母),我利用所有的三进制数来一一对应。有一个初始列表[‘a’,‘b’,‘c’],每一个三进制数的数位只有0、1、2,正好对应列表中a、b、c的索引。
例:三个人玩石头剪刀布:
三进制数12(即012)代表abc,即第一个人出石头,第二个人出剪刀,第三个人出布。
又如:5个人玩石头剪刀布:
三进制数12022代表bcacc,即第一个人出剪刀,第三个人出石头,剩下的人出布。
补齐数码的函数code()
这个函数解决的问题是数码不够的情况。
例如:5个人玩石头剪刀布,
三进制数是121,数码不足五位,需要补齐至00121。
函数接受一个三进制数,返回一个列表,每一项是一个数字(以字符串的形式),这个数字是0、1、2中的一个。
代码如下:
# Create_situation.py
def code(num,length):
'''补齐数字,将num补齐至length位,例如将1补齐至三位变成001'''
num = str(num)
# 如果位数相同,以列表形式返回数字
if len(num) == length:
return list(num)
# 如果位数不同,就在前面一直补0,直到补齐
elif len(num) < length:
while True:
num = '0' + num
if len(num) == length:
break
return list(num)
三进制数与十进制数的转换
因为三进制数较难列举,因此我们列举简单的十进制数,并使用函数将其转换为三进制数。
其中的原理和十进制与二进制的转换非常类似,感兴趣的读者可以查看我的另一篇文章《Python 二进制与十进制的研究》。
Python 二进制与十进制的研究
代码如下(三进制转十进制的函数在后续的程序中没有用到,只是我用来测试程序的):
# Change.py
def three_to_ten(num):
'''将三进制数转换为十进制数'''
# 主要原理:(abcde)3 = a*3**4 + b*3**3 + c*3**2 + d*3**1 + e*3**0
nums = list(str(num))
nums.reverse()
result = 0
for i in range(0,len(nums)):
# 每个数位乘上3的次方
delta = int(nums[i]) * (3 ** i)
result += delta
return result
def ten_to_three(num):
'''把十进制数转换成三进制数'''
# 主要原理:原数字一直除3,得到的余数反写
results = []
division = num
if division == 0:
return 0
# 反复除三,记录商和余数
while division != 0:
results.append(division % 3)
division = division // 3
results.reverse()
result = ''
for nums in results:
# 获得最终的结果
result += str(nums)
return int(result)
排列所有的情况create_situation()
前期的准备工作做好后,这个排列情况的函数不难编写出。
函数接收的参数是玩石头剪刀布人的数量。
如果有重复的就舍去,没有重复的就写入列表。
最后返回的列表是所有情况(用字典表示,例如{1:‘a’,2:‘a’,3,‘a’})
代码如下:
# Create_situation.py
from Change import ten_to_three, three_to_ten
def create_situation(num):
'''提供有多少人玩石头剪刀布,返回所有情况'''
# 用三进制数来列举每个人出石头、剪刀或布的情况
list_1 = ['a','b','c']
situation = {}
situations = []
nums = 3 ** num # 情况的数量
for i in range(0,nums):
i_1 = ten_to_three(i) # 将每个十进制数转为三进制数
i_2 = code(i_1,num) # 补齐位数
situation = {}
for y in range(1,len(i_2)+1):
# 根据三进制数位数0、1、2决定a,b,c
situation[y] = list_1[int(i_2[y-1])]
# 重复的情况要舍去
if situation in situations:
continue
else:
situations.append(situation) # 加入列表
return situations
主程序
运用input()函数获得玩石头剪刀布的人数,然后获得所有的情况,判断输赢或平局,然后对应打印出来。
代码如下:
# Main.py---提供玩石头剪刀布的人数,列举所有情况及获胜情况
# TonyLu 2024.4.13-2024.4.14
from Create_situation import create_situation as c_s
from Check import check
num = input('有几个人玩石头剪刀布?') # 获得人数
print('开始列举。a表示石头,b表示剪刀,c表示布')
list_1 = c_s(int(num))
results = {}
for i in list_1:
winners = check(i) # 判断输赢
if winners == None:
winners = '平局'
print(f'{i} {winners}') # 打印结果
运行成果
注意:由于程序中出现了调用其他文件的函数,请务必把几个子程序和主程序放在同一个文件夹里。
接下来看一下主程序的运行成果。
备注:最后一张图片是五个人玩,由于放不下,只取了一部分。
总结
通过几个函数,我们成功模拟了多人玩石头剪刀布的情况。
程序调试的时间不长,如有错误请帮忙指出,谢谢!