1.实验要求
编写程序设计和实现聪明的尼姆游戏(人机对战)。尼姆游戏是个著名的游戏,有很多变种玩法。两个玩家轮流从一堆物品中拿走一部分。在每一步中,玩家可以自由选择拿走多少物品,但是必须至少拿走一个并且最多只能拿走一半物品,然后轮到下一个玩家。拿走最后一个物品的玩家输掉游戏。
在聪明模式中,计算机每次拿走足够多的物品使得堆的大小是2 的幂次方减1——也就是3,7,15,31 或63。除了堆的大小已经是2 的幂次方减1,在其他情况下这样走都是符合游戏规则的。如果已是2的幂次方减1,计算机就按游戏规则随机拿走一些。
2.实验原理
2.1玩家回合
要确保玩家拿取的数要大于等于1,并且小于等于当前物品数的一半,可以使用while循环来完成。不过如果当前物品数为1时,会进入死循环,所以需要专门考虑:使用if语句来进行判断。拿取后,把更新的物品数返回即可。
其中用到的函数有player_turn。
def player_turn(heap_number):
print(f'当前堆的大小为{heap_number},', end='')
take_number = 0
while take_number < 1 or take_number > heap_number // 2:
take_number = eval(input('请输入你要拿取的数量(至少为1,至多为当前堆大小的一半):'))
if heap_number == 1 and take_number == 1: # 边界问题
break
return heap_number - take_number
2.2电脑回合
根据传入的参数,来判断当前的模式。
如果是傻瓜模式,就随机拿取[1,当前堆的大小的一半],不过要注意这个区间有可能是非法的,当堆大小为1时,当前堆大小的一半为0,所以非法区间为[1,0]。
如果是聪明模式,就先找到比此时物品数小的最大2的幂次方减1。如果这个2的幂次方减1恰好等于此时的物品数,就进行随机拿取;如果不等于,就拿取***此时物品数-2的幂次方减1***。
记得最后更新一下堆大小(返回当前的物品数)
其中用到的函数有computer_turn。
def computer_turn(heap_number, game_mode):
print(f'堆的大小为{heap_number},', end='')
if game_mode == 1: # 傻瓜模式:随机拿取
if heap_number > 1:
take_number = random.randint(1, heap_number // 2) # 如果heap_number <= 1,那么随机区间非法
else:
take_number = 1
print(f'对手拿取了{take_number}个物品,还剩下{heap_number - take_number}个物品。')
return heap_number - take_number
else:
x = 0
while True: # 找到此时比物品数小的 最大的2的幂次方减1
if pow(2, x) <= heap_number:
x = x + 1
else:
break
x = x - 1
if pow(2, x) == heap_number: # 恰好等于物品数
if heap_number > 1: # 如果heap_number <= 1,那么随机区间非法
take_number = random.randint(1, heap_number // 2)
else:
take_number = 1
print(f'对手拿取了{take_number}个物品,还剩下{heap_number - take_number}个物品。')
return heap_number - take_number
else:
take_number = heap_number - (pow(2, x) - 1)
print(f'对手拿取了{take_number}个物品,还剩下{heap_number - take_number}个物品。')
return heap_number - take_number
2.3实现交替拿取
仅需定义一个变量now_play,用它来表示当前拿取的是谁,0代表电脑,1代表玩家,然后每有人拿取后,把now_play更新为表示对手的值就行了,这样就可以进行轮流进行拿取物品。
其中用到的函数有change_play。
def change_play(now_play): # 改变玩家回合
if now_play == 0:
return 1
return 0
2.4判断游戏结束
仅需判断当前物品数是否为0即可,一个玩家拿取后,如果此时物品数为0,那么游戏就可以结束了,这个玩家输了。
用到的函数有is_over。
def is_over(heap_number): # 判断游戏是否结束
if heap_number == 0:
return True
else:
return False
3.全部代码
import random
def game():
heap_number = eval(input('请输入堆的初始大小:'))
game_mode = eval(input("请选择游戏模式:1.傻瓜模式 2.聪明模式"))
now_play = eval(input('请输入谁先进行拿取:1.自己 0.对手')) # 此时轮到谁进行游戏
while True:
if now_play == 0:
heap_number = computer_turn(heap_number, game_mode)
now_play = change_play(now_play)
if is_over(heap_number):
print('恭喜你,赢得了比赛!')
break
else:
heap_number = player_turn(heap_number)
now_play = change_play(now_play)
if is_over(heap_number):
print('很可惜呢,希望下一次能胜利!')
break
def player_turn(heap_number):
print(f'当前堆的大小为{heap_number},', end='')
take_number = 0
while take_number < 1 or take_number > heap_number // 2:
take_number = eval(input('请输入你要拿取的数量(至少为1,至多为当前堆大小的一半):'))
if heap_number == 1 and take_number == 1: # 边界问题
break
return heap_number - take_number
def computer_turn(heap_number, game_mode):
print(f'堆的大小为{heap_number},', end='')
if game_mode == 1: # 傻瓜模式:随机拿取
if heap_number > 1:
take_number = random.randint(1, heap_number // 2) # 如果heap_number <= 1,那么随机区间非法
else:
take_number = 1
print(f'对手拿取了{take_number}个物品,还剩下{heap_number - take_number}个物品。')
return heap_number - take_number
else:
x = 0
while True: # 找到此时比物品数小的 最大的2的幂次方减1
if pow(2, x) <= heap_number:
x = x + 1
else:
break
x = x - 1
if pow(2, x) == heap_number:
if heap_number > 1: # 如果heap_number <= 1,那么随机区间非法
take_number = random.randint(1, heap_number // 2)
else:
take_number = 1
print(f'对手拿取了{take_number}个物品,还剩下{heap_number - take_number}个物品。')
return heap_number - take_number
else:
take_number = heap_number - (pow(2, x) - 1)
print(f'对手拿取了{take_number}个物品,还剩下{heap_number - take_number}个物品。')
return heap_number - take_number
def change_play(now_play): # 改变玩家回合
if now_play == 0:
return 1
return 0
def is_over(heap_number): # 判断游戏是否结束
if heap_number == 0:
return True
else:
return False
if __name__ == '__main__':
game()