数字拼图游戏与拼图游戏原理一致,把打乱了的数字或图片经移动,拼成给定的目标数字或图片,其中总有一个空的地方,让相邻(上下左右)的方块移动,直至达到目标。
游戏代码由浙江温州永嘉县教师发展中心应根球老师提供,我略做修改和优化。
代码有点长,用手机阅读可能不太方便,可以复制地址到电脑上用浏览器查看。
import random
#显示数字拼图
def disp(s, d):
#s和d是两个数字字符串,把0换成空格,把数字摆放到指定位置
s = ''.join(s).replace('0', ' ')
d = ''.join(d).replace('0', ' ')
print('''
+---+---+---+ +---+---+---+
| {0[0]} | {0[1]} | {0[2]} | | {1[0]} | {1[1]} | {1[2]} |
|---+---+---| |---+---+---|
| {0[3]} | {0[4]} | {0[5]} | ==> | {1[3]} | {1[4]} | {1[5]} |
|---+---+---| |---+---+---|
| {0[6]} | {0[7]} | {0[8]} | | {1[6]} | {1[7]} | {1[8]} |
|---+---+---| |---+---+---|
'''.format(s, d))
#移动数字
def move(s, numstr):
if (numstr not in "12345678") or (not numstr):
return
t1 = s.index('0')
t2 = s.index(numstr)
#字符在字符串中的位置除3的商对应游戏图中的行下标
#除3的余数对应游戏图中的列下标
t = zip(divmod(t1,3), divmod(t2,3))
t = ''.join([str(abs(i-j)) for i,j in t])
#如果输入的数字与空格相邻则移动
if t in ('01','10'):
s[t1], s[t2] = s[t2], s[t1]
#获取空格周边可移动数字
def getMoveable(s):
#空格位置的行、列坐标
p, q = divmod(s.index('0'), 3)
#空格上下的位置坐标
ls = [(p, i) for i in (q+1, q-1) if i in range(3)]
#空给左右的位置坐标
ls += [(i, q) for i in (p+1, p-1) if i in range(3)]
return ls
#爬山算法状态计算函数,从当前状态s到终态d所需要的总步数
def getInstance(s,d):
#依次计算s和d中相同数字的距离,并求所有距离之和
sumi = 0
for n in '12345678':
t1 = divmod(s.index(n), 3)
t2 = divmod(d.index(n), 3)
sumi += abs(t1[0]-t2[0]) + abs(t1[1]-t2[1])
return sumi
#让机器根据与目标各数字差距之和的策略选定一个移动数字
def choiceNum(s, moveable, d):
tmp = [100,'0']
for i in moveable:
s1 = s[::]
s1[s1.index('0')], s1[s1.index(i)] = i, '0'
getI = getInstance(s1, d)
if getI < tmp[0]:
tmp = getI, i
elif getI == tmp[0]:
tmp = getI, random.choice([i, tmp[1]])
return tmp[1]
#打乱数字拼图顺序,以得到初始状态
def shufflemove(d, times):
s2 = d[::]
mov = '0'
for i in range(times):
mov1 = mov
mov = [s2[x[0]*3+x[1]] for x in getMoveable(s2)]
if mov1 in mov:
mov.remove(mov1)
mov = random.choice(mov)
move(s2, mov)
return s2
#做题,当who为computer时为计算机解题
#当who为human,即非computer时,人工解题
def do(s, d, who):
s1 = s[::]
num = ''
bushu = 0
while True:
if who != 'computer':
disp(s1,d)
#已成功、步数太大或人工放弃时返回
if s1==d or bushu>=1000 or num=='0':
return bushu
#确定可输入的数字
n_of_m = [s1[x[0]*3+x[1]] for x in getMoveable(s1)]
if who == 'computer':
#机器答题不允许后退
if num in n_of_m:
n_of_m.remove(num)
num = choiceNum(s, n_of_m, d)
else:
#人工答题允许后退
prompt = '第{0}步{1}(输入0退出)=>'.format(bushu, n_of_m)
num = input(prompt)[0]
#输入0步数设为999,视为主动放弃并退出
if num == '0':
bushu = 999
#移动数字
move(s1,num)
bushu += 1
#主程序开始
print('-'*34)
print('拼图游戏'.center(34,'*'))
print('''
**玩法:左边的图通过移动空格相邻的数字到
空格处,最终得到右边的图,游戏即完成。只
要输入空格相邻的数字,该数字即被移到空格
处。=>左边的数字为你已经移动的步数及你可
移动的数字。完成任务的步数越少,你的游戏
成绩越高。祝你幸运!
''')
print('-'*34)
#为简化,设定固定数字目标,其中0显示为空方块
d = list('123804765')
#为简化,让机器从目标开始随机逆移动,先只移动6步
#也可以设置难度,移动步数越多,恢复越难。
s = shufflemove(d,6)
#先让计算机用简易爬山算法去解题,由于爬山算法本身的原因,不一定能得到最优解
cpstep = do(s, d, 'computer')
#显示开始与结束状态及机器解题情况
disp(s, d)
print('这个题目机器用了{0}步!\n'.format(cpstep))
if cpstep > 1000:
print("机器用了1000步还没解出,看你的了!\n")
#用于计人工移动的步数
bushu = 0
#人工解题开始
hmstep = do(s, d, 'human')
#显示游戏结果
if hmstep < cpstep:
print("你胜利,真了不起!")
elif hmstep == cpstep:
print("你与机器持平局,加油!")
else:
print("哈,你输给了机器,这可是用爬山算法哦!")
某次玩游戏的过程如下: