尼姆游戏是一种两个人玩的回合制数学战略游戏。游戏者轮流从一堆棋子(一共有好几堆,一次只能从其中一堆拿。)(或者任何道具)中取走一个或者多个,最后不能再取的就是输家。当指定相应数量时,一堆这样的棋子称作一个尼姆堆。
本文中的尼姆游戏是传统尼姆游戏的一个变形,即:只有一堆棋子,每次从尼姆堆中拿走的棋子数量不能超过尼姆堆中棋子数量的一半,但至少取走一个,最后不能再取的就是输家。
首先用Python实现这个游戏:
from random import randint
def nimu(n):
while n > 1 :
# 玩家走
print("\nNow it's your turn, and we have {0} left.".format(n))
# 确保玩家输入的是合法整数值
while True :
try:
num = int(input("How many would you to take:"))
assert 1<= num <= n//2
break
except:
print("\nThe number you can take must between 1 and {0}".format(n//2))
n -= num
if n == 1:
return "You lose!"
# 机器人随意拿走一些
n -= randint(1,n//2)
else:
return "You win!"
print(nimu(randint(1, 1000)))
上述代码初值由计算机从1-1000中随机产生。
运行结果如下图:
下表从数列角度逆推游戏的机理,发现一个机器人必胜模式:
分析数列[2,5,11,23, ……],得出其表达式为(3*2n-2-1),只要玩家出现任意一步尼姆堆中数量为该数值时,就可确保机器人必胜。
进一步对机器人必胜模式进行分析:
在玩家做出选择后,要根据尼姆堆中剩余元素个数N(3*2**n <= N <= 3*2**(n+1)-2)确定机器人应选择的值:首先确定其所在范围的n值,n=math.floor(log2(N/3));尔后,根据n值确定机器人应从尼姆堆中拿走的数量为N-(3*2**n-1)。
利用Python来实现这个必胜机器人,代码如下:
from math import log2, floor
from random import randint
def answer_n(x):
return floor(log2(x/3))
def dead_num(n):
# 定义根据任意元素数确定机器人所需要选择的数值,以使玩家必输
if log2((n+1)/3)%1 == 0:
# 无法得到使玩家必输的数值,则在规定范围内随机使用一个数值
return randint(1,n//2)
else:
# 按玩家必输模式计算出机器人应拿走的元素数量
return n-(3*2**answer_n(n)-1)
def nimu(n):
while n > 1 :
# 玩家走
print("\nNow it's your turn, and we have {0} left.".format(n))
# 确保玩家输入的是合法整数值
while True :
try:
num = int(input("How many would you to take:"))
assert 1<= num <= n//2
break
except:
print("\nThe number you can take must between 1 and {0}".format(n//2))
n -= num
if n == 1:
return "You lose!"
# 机器人拿走使玩家进入必输模式的数值
n -= dead_num(n)
else:
return "You win!"
print(nimu(randint(1, 1000)))
其运行结果如下:
结果一
结果二
从运行结果可以看出,机器人每次都会拿走一定的值使玩家选择时尼姆堆中的元素数量为其必输值,所以只要玩家一步错,就会陷入万劫不复。
但是也有的玩家在先手的情况下步步为营,不给机器人任何破绽,这样机器人也不能赢,例如:
当然我们可以给玩家一个时间限定,在有限的时间内,如果尼姆堆中元素数量足够大,人类玩家难以通过手算或心算方式得出让机器人必输的取值,所以机器人的计算速度使得其在比赛中占有极大优势。
最终,我们用Python实现了一个尼姆游戏“无敌”机器人(之所以打个引号,是因为在你“先手”&“掌握规则”&“能在规定时间内计算出应该拿走的数量”时,你也可以打败机器人)
PS:初学Python语言,格式或者算法上有什么问题还请大牛指教!