专栏名称: 马哥Linux运维
马哥linux致力于linux运维培训,连续多年排名第一,订阅者可免费获得学习机会和相关Linux独家实战资料!
目录
相关文章推荐
运维  ·  再见,CDN 巨头:Akamai 宣布 ... ·  昨天  
51好读  ›  专栏  ›  马哥Linux运维

python 实现 2048 游戏 (二)

马哥Linux运维  · 公众号  · 运维  · 2019-06-28 21:00

正文


来源:Python高效编程

作者:flywind

我们之前梳理了实现简易版 2048 游戏的基本知识,这篇文章将介绍如何实现各个模块。换句话说,上一次我们确定了旅行的目的地,这一次就让我们自由畅行在山间田野。

主程序,即 game 函数按部就班地向下执行,该判断就判断,然后执行相应函数。 首先读取用户输入,第一个判断:是否移动数字,显然要移动数字要满足以下条件:

  • 用户输入小写的 w s a d 对应上下左右

  • 该移动方向上允许移动

具体来说,移动方向最前面有 空间或者有连续相同的数字。可以移动则执行 move 函数,并在棋盘上生成随机数字,否则原样输出。

其次判断:棋盘是否被填满。被填满时执行 fail 函数。

最后判断:是否胜利。如果获胜,打印获胜提示。

def game(board, stdscr, rscore):
   global score
   global change
   
   # curses.noecho()
   # 屏幕不显示用户输入的字符
   curses.noecho()
   while 1:
     
       # stdscr.getch()
       # 读取用户输入的字符
       order = stdscr.getch()

       # move()对用户移动的响应
       current_board, change = move(order, board)
       # change 为 1 随机产生 2 或 4
       if change:
           current_board = choice(board)

       # 打印棋盘
       print_board(stdscr, current_board, rscore)

       # 当棋盘被填满,判断是否游戏结束
       if (current_board != 0).all():
           fail(current_board)

       # win 为 1 打印获胜提示
       if win:
           stdscr.addstr('You win')


首先是移动模块:

basic 函数用来执行移动与碰撞的操作。 move_{up,down,right,left} 函数用来实现各个方向上的 basic 函数操作。 move 函数用来响应用户指令,实现各个方向上的移动。

棋盘由 矩阵组成,0 代表该位置上没有数字。 basic 函数就是基于矩阵的运算,且以右移为基础移动。

矩阵

向右滑动:

每一周期分为 4 轮,每一轮操作一行(共 4 行),从最左面的元素开始执行。设置 flag 用于提示这一轮是否发生了改变,如果发生了改变,这一轮就再进行一次循环,直到 flag 保持为 0 不变。对于循环的每一个元素,如果该元素不为 0 ,若下个元素为 0,就交换当前值与下个元素的值。若下个元素与当前元素相同,则当前元素置 0 ,且下一个元素增加一倍,分数还要增加 100 分。

举个例子:对于第一行 [2 2 0 4]

第一轮:

  • 4 与 0 不交换             [2 2 0 4]

  • 0 与 2 交换                 [2 0 2 4]

  • 0 与 2 交换                 [0 2 2 4]

  • flag = 1 且 score + = 0

第二轮:

  • 4 与 2 不交换             [0 2 2 4]

  • 双倍 置 0      [0 0 4 4]

  • 0 不变                        [0 0 4 4]

  • flag = 1 且 score += 100

第三轮:

  • 双倍 置 0     [0 0 0 8]

  • 不变                          [0 0 0 8]

  • 不变                          [0 0 0 8]

  • flag = 1 且 score += 100

第四轮:

  • 不变

  • 不变

  • 不变

  • flag = 0 且 score += 0

即第一轮最后输出结果 [0 0 0 8]。

以上就是向右移动的操作,而对于其他方向上的移动其实就是在此基础上进行矩阵的转置与逆置操作。

# A 为 4*4 的矩阵
# 转置操作
A.T
# 逆置操作
A[::-1,::-1]

下图为原矩阵:

向下滑动:

将原矩阵转置得到新矩阵,新矩阵向右滑动,相当于原矩阵向下滑动,再转置变回原矩阵。

向左滑动:

将原矩阵逆置得到新矩阵,新矩阵向右滑动,相当于原矩阵向左滑动,再逆置变回原矩阵。

向上滑动:

将原矩阵转置加逆置得到新矩阵,新矩阵向右滑动,相当于原矩阵向上滑动,再通过转置加逆置变回原矩阵。


# 基础移动
def basic(board):
   global score
   global win
   # 以右移为基础移动

   for i in range(4):
       flag = 1
       while flag:
           flag = 0
           j = 2
           while j >= 0:
               if board[i, j] != 0:
                   if board[i, j + 1] == board[i, j]:
                       board[i, j + 1] = 2 * board[i, j]
                       if board[i, j + 1] == 2048:
                           win = 1
                       board[i, j] = 0
                       score += 100
                       flag = 1

                   elif board[i, j + 1] == 0:
                       temp = board[i, j]
                       board[i, j] = board[i, j + 1]
                       board[i, j + 1] = temp
                       flag = 1

               j -= 1
   return board


# 右移
def move_right(board):
   return basic(board)


# 上移
def move_up(board):
   # 逆置 + 转置
   board = board[::-1, ::-1].T
   board = basic(board)
   board = board[::-1, ::-1].T
   return board


# 左移
def move_left(board):
   # 逆置
   board = board[::-1, ::-1]
   board = basic(board)
   board = board[::-1, ::-1]
   return board


# 下移
def move_down(board):
   # 转置
   board = board.T
   board = basic(board)
   board = board.T
   return board


# 移动
def move(order, board):
   # ord 求码值
   global score
   global win
   change = 1
   tempboard = copy.deepcopy(board)

   # 退出游戏
   if order == ord('q'):
       save_score(score)
       exit()
   # 重置游戏
   elif order == ord('r'):
       win = 0
       save_score(score)
       score = 0
       stdscr.clear()
       wrapper(main)
   # 胜利后,只有退出和重置游戏
   elif win:
       change = 0
       newboard = tempboard
       return newboard, change
   # 上下左右移动
   elif order == ord('w'):
       newboard = move_up(board)
   elif order == ord('s'):
       newboard = move_down(board)
   elif order == ord('a'):
       newboard = move_left(board)
   elif order == ord('d'):
       newboard = move_right(board)

   # 按其他键程序不响应
   else:
       newboard = board

   if (newboard == tempboard).all():
       change = 0

   return newboard, change

接下来,我们讲 choice 模块:首先获取值为 0 的矩阵元素的位置,并储存在字典里,以序号( 最大值为 count ) 为索引。其次产生 [0,count) 范围内的随机数(随机抽取值为 0 的元素),并且产生随机数 2 或 4 (概率为 75% 与 25%)。最后将随机抽取的元素更改为生成的随机数(2 或 4)。

# 随机产生 2 或 4
def choice(board):
   udict = {}
   # 统计0的个数
   count = 0
   for i in range(4):
       for j in range(4):
           # board[i,j] 为 0
           # eg. {0:(1,3),1:(2,1),3:(3,2)}
           # 根据 key 可以获得元素 0 在棋盘上的位置
           if not board[i, j]:
               udict[count] = (i, j)
               count += 1
   # np.random.randint(0, count)
   # 产生 [0,count) 范围内的随机数
   random_number = np.random.randint(0, count)
   # np.random.choice([2,2,2,4])
   # 随机选取列表 [2,2,2,4] 中的元素
   two_or_four = np.random.choice([2, 2, 2, 4])
   # 更改棋盘上 0 元素为随机数
   board[udict[random_number]] = two_or_four
   return board

然后是生成分数:

首先游戏开始时加载一次分数(历史最高分),游戏结束时保存最高分。每次打印棋盘前,都比较当前分数与当前最高分,并更改当前最高分数。

# 加载最高分
def load_score():
   rank_score = np.load(FILENAME)
   return rank_score


# 保存最高分
def save_score(score):
   rscore = load_score()
   if score > rscore:
       np.save(FILENAME, score)

# 比较当前分数与当前最高分
def compare_score(score, rscore):
   if score > rscore:
       rscore = score
   return rscore

其次是打印模块:







请到「今天看啥」查看全文