本文是1小时入门Python的“课后习题”。
完整代码请移步Python实现命令行版2048。
按理说看到这里,并写到这里,就应该算是完成了入门,那么接下来就要用入门学会的知识,写一个命令行版的2048小游戏。
2048的逻辑无非是操作4x4的方格,每个方格中有一个数,我们可以操作这些数字进行移动,如果两个相同的数字在我们的操作下相撞了,那么它们就可以合并了。
而这个4x4的方格,无非是一个矩阵。
我们的操作可以理解为输入字符,用wsad代表上下左右,y代表确定,n代表取消。
python接收字符的函数是input,例如
>>> x = input("input a number")
input a number5
>>> x
'5'
而创建矩阵,可以用np.zeros([4,4]).astype(int),表示创建一个 4 × 4 4times4 4×4的全0矩阵,并化为整形。
矩阵中只有16个元素,尽管循环效率低下,但足以满足人的操作速度了。
操作逻辑
2048只有四个手势动作,即上下左右,这四个动作所引发的结果都可以归结为对单行或者单列的操作,进而归结为对一个列表的操作。
例如对于[0,2,2,0],如果向右合并,则输出为[0,0,0,4],向左合并则输出为[4,0,0,0]。
其实现方法如下
def addZeros(lst:list,flag:bool=True):
zeros = [0]*(4-len(lst))
return zeros+lst if flag else lst+zeros
# flat为Ture时把0补在左边;否则补右边
def rmZero(lst, flag=True):
lst = [x for x in lst if x]
end = len(lst)-1
if end < 1:
return addZeros(lst,flag)
index = range(end) if flag else range(end,-1,-1)
iter = 1 if flag else -1
for i in index:
if lst[i] == lst[i+iter]:
lst[i] *= 2
lst[i+iter] = 0
lst = [x for x in lst if x]
return addZeros(lst,flag)
然后,针对行和列均需遍历执行rmZero。其中,wsad分别表示上下左右
# w,s,a,d分别为上下左右
def updateMat(mat,op):
flag = op in "sd"
if op in "ws":
for i in range(4):
mat[:,i] = rmZero(mat[:,i],flag)
else:
for i in range(4):
mat[i,:] = rmZero(mat[i,:],flag)
return mat
这三个函数已经完成了2048的交互逻辑,接下来就是将updateMat这个函数嵌入到游戏的流程之中。
初始化
2048游戏在开始之前,需要初始化一个4x4的矩阵,然后每次操作之前,需要在矩阵中为0的位置随机生成一个数。随机生成的数的取值范围决定了游戏的难度,所以生成方式也比较灵活,下面给出一种普通的生成方法
def addNew(mat):
i,j = randint(4),randint(4)
while(mat[i,j]!=0):
i,j = randint(4),randint(4)
else:
x = randint(1,100)
x = 7 - np.floor(np.log2(x))
mat[i,j] = int(2**x)
print(mat)
交互操作
然后是交互操作,asdw表示操作,q表示退出。
def InputNew(mat):
op = input("input operator:")
if op in "asdw":
newMat = updateMat(mat*1,op)
if np.max(np.abs(mat-newMat))==0:
print("Invalid operation")
return mat,"Error"
else:
return newMat,"True"
if op == 'q':
return mat, "Exit"
print("Invalid Input")
return mat, "Error"
main
if __name__ == "__main__":
while(1):
addNew(mat)
newMat,flag = InputNew(mat)
while flag=="Error":
newMat,flag = InputNew(mat)
if flag == "Exit":
break
mat = newMat
print(mat)
print('-'*30)
if np.max(mat)==2048: flag == "win"
if np.min(mat)!=0: flag == "lose"
if flag in ["win","lose"]:
if input(f"you {flag}, play again? ")=="y":
mat *= 0
else: break



