游戏玩法:
- 2048游戏共有16个格子,初始时初始数字由2或者4构成。
- 1、方向键向一个方向滑动,所有格子会向那个方向运动。
2、相同数字的两个格子,相撞时数字会相加,这是一个难点
3、每次滑动时,空白处会随机刷新出一个数字的格子。
4、当界面不可运动时(当界面全部被数字填满时),游戏结束;当界面中最大数字是2048时,游戏胜利。
基于上述需求,我们简单建立一下逻辑和需要的功能函数:
'''上下左右移动'''
def up():
pass
def down():
pass
def left():
pass
def right():
pass
'''游戏引擎'''
class Game():
def print_screen(self):
'''
这里绘制棋盘
:return:
'''
def rnd_field(self):
'''
这里做到每次移动棋盘,空白格子刷新一个数字
:return:
'''
def logic(self, control):
'''
处理键盘输入和更新棋盘
移动后的棋盘要么可以继续游戏,要么达到2048胜利,要么失败
有两个返回值,一个是状态码,一个是提示语是否重来
:param control:
:return:
'''
def main_loop(self):'''主函数 将所有组件运行'''
self.grid = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
self.rnd_field()
self.rnd_field()'''调用两次 产生两个随机数'''
while True:'''一直运行,不停绘制棋盘简体输入'''
self.print_screen()
control = input('input w/a/s/d:') '''控制输入参数为必须为wasd,并且将其传入logic()'''
if control in self.controls:
status, info = self.logic(control)
if status:'''状态码为1代表胜利'''
print(info)
if input("start anoter game?[y/n]") == "y":
'''选择重来,跳出循环,继续运行,再次self.main_loop()'''
break
else:
sys.exit(0)
self.main_loop()
接下来细扣每一个函数:
- Print_screen()绘制棋盘
grid = [[0,0,0,0], [0,0,0,0],[0,0,0,0],[0,0,0,0]]
'''绘制一个4x4表格'''
def print_screen(self):
#os.system('clear')
print('-'*21)
for row in self.grid:
print('|{}|'.format("|".join([str(col).center(4)])))
print('-'*21)
- Gird定义一个4x4的棋盘,row取出每一行; col取出每一行的每一列;center()的意思是将数字居中,“|”.join()的意思是以|为分隔符进行分割
- 绘制的棋盘如下
- 绘制的棋盘如下
- 可能你不懂这棋盘怎么绘制的 接下来我们将每一部分print看一下打印的结果,不懂的语句咋们来调试
grid = [[0,0,0,0], [0,0,0,0],[0,0,0,0],[0,0,0,0]]
#os.system('clear')
print('-' * 21)
for row in grid:
print(row)
for col in row:
print(col)
print("|".join([str(col).center(4) for col in row]))
print('|{}|'.format("|".join([str(col).center(4) for col in row])))
- 这四个箭头分别对应四个print 你就可以对比代码和执行效果去理解
.cn/20200304183035108.png) - Rnd_field()实现空格随机赋值
def rnd_field(self):
number = random.choice([4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2])'''随机抽取2和4'''
x, y = random.choice([(x, y) for x, y in itertools.product([0, 1, 2, 3], [0, 1, 2, 3]) if self.grid[x][y] == 0])'''随机抽取值不为0的坐标 '''
self.grid[x][y] = number
- 接下来就可以试试这两个功能是否运行正常
import itertools
import random
class Game:
controls = ['w','a','s','d']
grid = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
#打印屏幕
def print_screen(self):
#os.system('clear')
print('-'*21)
for row in self.grid:
print('|{}|'.format("|".join([str(col or ' ').center(4) for col in row])))
print('-'*21)
#随机生成数字
def rnd_field(self):
number = random.choice([4,2,4,2,4,2,4,2,4,2,4,2])
x, y = random.choice([(x, y) for x, y in itertools.product([0,1,2,3],[0,1,2,3]) if self.grid[x][y]==0])
self.grid[x][y]=number
def main_loop(self):
self.rnd_field()
self.rnd_field()
#while True:
self.print_screen()
self.main_loop
a = Game()
a.main_loop()
- 运行结果如图所示,多次运行可以看到随机生成了数字
- logic(),其目的是处理每次移动后的棋盘赋给grid,与本来的self.grid比较判断是否产生变化,产生变化的话将进行棋盘更新,并且调用rnd_field()添加数字,不改变的话再上下左右移动一下判断是否真的数字满了移动不了即游戏结束,还能动的话啥也不干
def logic(self,control):
grid = {"w": up,"a": left,"s": down,"d": right}[control]([[c for c in r] for r in self.grid])
'''深度拷贝 目的是生成新的数组进行上下左右移动 为了不影响原来的数组,比较移动后的数组要和原来的进行对比,改变的更新一下棋盘'''
if grid != self.grid:
del self.grid[:]
self.grid.extend(grid)'''棋盘更新'''
if [n for n in itertools.chain(*grid) if n>=2048]:
return 1,"You win!"
self.rnd_field()
else:
if not [1 for g in[f(grid) for f in [up,down,left,right]] if g != self.grid]:
return -1,"You lost!"
return 0, ''
if [n for n in itertools.chain(*grid) if n>=2048]:
return 1,"You win!"
- 这个代码的意思是itertools.chain将二维列表拍扁为一位数组,遍历每一个元素,如果存在大于2048的就达到胜利条件,并且返回一个状态码和一段话
- 可以看出用了很多列表推导式,对于列表推导式不熟悉的同学 下面可以举一个例子来理解
print([n for n in range(10) if n >= 6])
输出为[6, 7, 8, 9]
- 接下来就是上下左右移动了
- 移动逻辑: [2 0 2 0]向左移动后会变成 [2 2 0 0],所以这里将功能分两部分,一个是提取有效数字,一个是有效数字相加
- 提取有效数字:[0 2 2 0] 首先将非零元素提取出来[2 2] 往左移动添0:[2 2]+[0 0 0 0 ]=[2 2 0 0 0 0]
再截取为[2 2 0 0] 往右移动[0 0 0 0 ]+[2 2]=[0 0 0 0 2 2]再截取[2 2 0 0] - 数字相加:[2 2 0 0]相加为[0 4 0 0],数组位置两两之间进行判断,如果相同其中一个加倍,另外一个变成0,为了避免相加后的数字再次运算,如[4 2 2 0 ]变成[8 0 0 0]显然不符合规则 增加两个标志位
- 定义两个函数trim(),sum_seq()分别用于提取和相加
def trim(seqs, direction=0):
return ([0,0,0,0]+[n for n in seqs if n])[-4:]if direction else ([n for n in seqs if n]+[0,0,0,0])[:4]
def sum_seqs(seqs, direction=0):
'''四个位置,两两比较,相同相加'''
a=True;b=True
if seqs[0] and seqs[1] and seqs[1] == seqs[0]:
seqs[0]=0;seqs[1] = seqs[1] * 2;a =False '''相加后生成的数字不再参与运算'''
if seqs[1] and seqs[2] and seqs[2] == seqs[1] and a==True:
seqs[1]=0;seqs[2]=seqs[2] * 2;b = False
if seqs[2] and seqs[3] and seqs[3] == seqs[2] and b==True:
seqs[2]=0;seqs[3]=seqs[3] * 2
return trim(seqs, direction=direction)
- 先解决最简单的向左向右移动,按照提取数字-相加-提取数字顺序处理
def left(grid):
return [sum_seqs(trim(row)) for row in grid]
def right(grid):
return [sum_seqs(trim(row, direction=1), direction=1) for row in grid]
- 稍难一点的向上向下移动,二维数组提取出每一列,按照提取-相加-提取顺序处理,本质上就是当做行在处理,再放回原来的位置
def up(grid):
for col in [0, 1, 2, 3]:
for _idx, n in enumerate(sum_seqs(trim([row[col] for row in grid]))):
grid[_idx][col] = n
return grid
''' enumerate()用于生成带索引的列表,取出每一个放回原来的位置'''
def down(grid):
for col in [0, 1, 2, 3]:
for _idx, n in enumerate(sum_seqs(trim([row[col] for row in grid], direction=1), direction=1)):
grid[_idx][col] = n
return grid
将所有代码组装,游戏完成