在本文中,我们将用 python 代码和逻辑来设计一款你经常在智能手机上玩的 2048 游戏。如果您对该游戏不熟悉,强烈建议您先玩一下该游戏,以便了解其基本功能。
如何玩 2048 .NET?
1.有一个 4*4 的网格,可以填入任何数字。一开始,随机填入两个 2 的单元格。其余单元格为空。
- 我们必须按下四个键中的任意一个,才能向上、向下、向左或向右移动。当我们按下任何一个键时,单元格中的元素就会向该方向移动,如果该行(向左或向右移动时)或该列(向上和向下移动时)中包含两个相同的数字,它们就会相加,然后该方向上的极端单元格就会填入该数字,其余单元格就会再次变空。‘
3.网格压缩后,任何随机空格都会被填上 2。
4.按照上述过程,我们必须通过加法将元素翻倍,并在任意一个单元格中组成 2048。如果我们能做到这一点,我们就赢了。
5.但是,如果在游戏过程中没有空格可以填入新的 2,那么游戏就结束了。
在上述过程中,您可以看到 2048 游戏图形用户界面的快照。但所有的逻辑都在主代码中。因此,为了完全理解背后的逻辑,我们可以假设上述网格是一个 4*4 矩阵(一个有四行四列的列表)。下面是上述游戏在没有图形用户界面的情况下进行输入和输出的方法。
例如
Commands are as follows :
'W' or 'w' : Move Up
'S' or 's' : Move Down
'A' or 'a' : Move Left
'D' or 'd' : Move Right
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 2, 0]
Press the command : a
GAME NOT OVER
[0, 0, 0, 2]
[0, 0, 0, 0]
[0, 0, 0, 0]
[2, 0, 0, 0]
Press the command : s
GAME NOT OVER
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 2, 0]
[2, 0, 0, 2]
Press the command : d
GAME NOT OVER
[0, 0, 0, 0]
[0, 0, 0, 0]
[2, 0, 0, 2]
[0, 0, 0, 4]
Press the command : a
GAME NOT OVER
[0, 2, 0, 0]
[0, 0, 0, 0]
[4, 0, 0, 0]
[4, 0, 0, 0]
Press the command : s
GAME NOT OVER
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[8, 2, 0, 2]
.
.
.
And the series of input output will go on till we lose or win!
编程方法 :
- 我们将设计每个逻辑函数,例如,我们正在执行左扫,然后通过反转矩阵并执行左扫,将其用于右扫。
- 向上移动可以通过转置然后向左移动来完成。
- 向下移动可以通过转置和向右移动来实现。
- 注释中详细解释了程序中的所有逻辑。强烈建议阅读所有注释。
下面有两个 python 文件,一个是包含主驱动代码的 2048.py,另一个是包含所有函数的 logic.py。要使用这些函数,应将 logic.py 导入 2048.py。只需将这两个文件放在同一文件夹中,然后运行 2048.py,即可完美运行。
logic.py
# logic.py to be
# imported in the 2048.py file
# importing random package
# for methods to generate random
# numbers.
import random
# function to initialize game / grid
# at the start
def start_game():
# declaring an empty list then
# appending 4 list each with four
# elements as 0.
mat =[]
for i in range(4):
mat.append([0] * 4)
# printing controls for user
print("Commands are as follows : ")
print("'W' or 'w' : Move Up")
print("'S' or 's' : Move Down")
print("'A' or 'a' : Move Left")
print("'D' or 'd' : Move Right")
# calling the function to add
# a new 2 in grid after every step
add_new_2(mat)
return mat
# function to add a new 2 in
# grid at any random empty cell
def add_new_2(mat):
# choosing a random index for
# row and column.
r = random.randint(0, 3)
c = random.randint(0, 3)
# while loop will break as the
# random cell chosen will be empty
# (or contains zero)
while(mat[r] != 0):
r = random.randint(0, 3)
c = random.randint(0, 3)
# we will place a 2 at that empty
# random cell.
mat[r] = 2
# function to get the current
# state of game
def get_current_state(mat):
# if any cell contains
# 2048 we have won
for i in range(4):
for j in range(4):
if(mat[i][j]== 2048):
return 'WON'
# if we are still left with
# atleast one empty cell
# game is not yet over
for i in range(4):
for j in range(4):
if(mat[i][j]== 0):
return 'GAME NOT OVER'
# or if no cell is empty now
# but if after any move left, right,
# up or down, if any two cells
# gets merged and create an empty
# cell then also game is not yet over
for i in range(3):
for j in range(3):
if(mat[i][j]== mat[i + 1][j] or mat[i][j]== mat[i][j + 1]):
return 'GAME NOT OVER'
for j in range(3):
if(mat[3][j]== mat[3][j + 1]):
return 'GAME NOT OVER'
for i in range(3):
if(mat[i][3]== mat[i + 1][3]):
return 'GAME NOT OVER'
# else we have lost the game
return 'LOST'
# all the functions defined below
# are for left swap initially.
# function to compress the grid
# after every step before and
# after merging cells.
def compress(mat):
# bool variable to determine
# any change happened or not
changed = False
# empty grid
new_mat = []
# with all cells empty
for i in range(4):
new_mat.append([0] * 4)
# here we will shift entries
# of each cell to it's extreme
# left row by row
# loop to traverse rows
for i in range(4):
pos = 0
# loop to traverse each column
# in respective row
for j in range(4):
if(mat[i][j] != 0):
# if cell is non empty then
# we will shift it's number to
# previous empty cell in that row
# denoted by pos variable
new_mat[i][pos] = mat[i][j]
if(j != pos):
changed = True
pos += 1
# returning new compressed matrix
# and the flag variable.
return new_mat, changed
# function to merge the cells
# in matrix after compressing
def merge(mat):
changed = False
for i in range(4):
for j in range(3):
# if current cell has same value as
# next cell in the row and they
# are non empty then
if(mat[i][j] == mat[i][j + 1] and mat[i][j] != 0):
# double current cell value and
# empty the next cell
mat[i][j] = mat[i][j] * 2
mat[i][j + 1] = 0
# make bool variable True indicating
# the new grid after merging is
# different.
changed = True
return mat, changed
# function to reverse the matrix
# means reversing the content of
# each row (reversing the sequence)
def reverse(mat):
new_mat =[]
for i in range(4):
new_mat.append([])
for j in range(4):
new_mat[i].append(mat[i][3 - j])
return new_mat
# function to get the transpose
# of matrix means interchanging
# rows and column
def transpose(mat):
new_mat = []
for i in range(4):
new_mat.append([])
for j in range(4):
new_mat[i].append(mat[j][i])
return new_mat
# function to update the matrix
# if we move / swipe left
def move_left(grid):
# first compress the grid
new_grid, changed1 = compress(grid)
# then merge the cells.
new_grid, changed2 = merge(new_grid)
changed = changed1 or changed2
# again compress after merging.
new_grid, temp = compress(new_grid)
# return new matrix and bool changed
# telling whether the grid is same
# or different
return new_grid, changed
# function to update the matrix
# if we move / swipe right
def move_right(grid):
# to move right we just reverse
# the matrix
new_grid = reverse(grid)
# then move left
new_grid, changed = move_left(new_grid)
# then again reverse matrix will
# give us desired result
new_grid = reverse(new_grid)
return new_grid, changed
# function to update the matrix
# if we move / swipe up
def move_up(grid):
# to move up we just take
# transpose of matrix
new_grid = transpose(grid)
# then move left (calling all
# included functions) then
new_grid, changed = move_left(new_grid)
# again take transpose will give
# desired results
new_grid = transpose(new_grid)
return new_grid, changed
# function to update the matrix
# if we move / swipe down
def move_down(grid):
# to move down we take transpose
new_grid = transpose(grid)
# move right and then again
new_grid, changed = move_right(new_grid)
# take transpose will give desired
# results.
new_grid = transpose(new_grid)
return new_grid, changed
# this file only contains all the logic
# functions to be called in main function
# present in the other file
代码解释:
- 代码首先导入随机软件包。
- 该软件包提供了生成随机数的方法。
- 接着,声明 start_game() 函数。
- 该函数将用于在程序开始时初始化游戏/网格。
- 该函数首先声明一个名为 mat 的空 list。
- 然后,它附加了四个列表,每个列表有四个元素,每个元素的值为 0。
- 这些列表代表游戏/网格上的单元格。
- 第一个列表(mat[0] )代表 0 号单元格,以此类推。
- 接下来,for 循环遍历 4 个值(i 在 range(4) 中)。
- 每个值都会生成一个包含 4 个元素([0] * 4)的新列表。
- 这些列表分别代表游戏/网格上 4 个可能的位置。
- 代码初始化了一个空列表,然后追加了四个列表,每个列表有四个元素。
- 第一个列表有 0 个元素,第二个列表有 1 个元素,第三个列表有 2 个元素,以此类推。
- 代码首先声明了两个变量 r 和 c,这两个变量将保存新 2 插入网格的行数和列数。
- 接下来,代码会调用一个名为 add_new_2() 的函数。
- 该函数有一个参数,即 mat。
- mat 是一个 Python 列表对象(一种存储多个项目的数据结构)。
- add_new_2() 函数首先选择两个随机数 r 和 c,然后使用这两个随机数指定新 2 插入网格的行和列。
- 最后,函数返回 mat 作为结果。
- 在计算机上运行这段代码时,您会看到类似下面的内容:W’或’w’:向上移动 S’或’s’:向下移动 A’ 或 ‘a’ : 向左移动 D’ 或 ‘d’ : 向右移动
- 代码首先随机选择行和列索引。
- 然后,它使用这些值在网格中选择一个新的空白单元格来添加新的 2。
- 最后,以新选择的单元格为参数调用 add_new_2 函数。
- 代码首先声明了两个变量。
- 第一个变量 mat 是一个包含四个整数的数组。
- 第二个变量 r 是一个介于 0 和 3 之间的随机数。
- 然后,代码循环遍历 mat 数组中的每个整数。
- 它会检查存储在 mat 数组中该位置的值是否与 2048 匹配(这是本游戏的获胜条件)。
- 如果符合,代码将返回 “WON”。
- 如果匹配数组中仍有尚未检查的单元格,代码将继续循环检查这些单元格。
- 对于每个尚未检查的单元格,代码都会检查其值是否与 2048 匹配。
- 如果不匹配,代码就会宣布玩家获胜并结束程序执行。
- 如果 mat 中的所有单元格都已检查过,或者其中一个单元格包含 2048(获胜条件),则不能宣布获胜,控制权将传回 get_current_state(),以便开始新一轮检查。
- 代码将检查矩阵 (mat) 中的每个单元格,看其是否包含 2048 的值。
- 如果有单元格包含 2048,代码将返回 “WON”。
- 如果在循环过程中的任何时候,mat 中所有四个单元格的值都为 0,则游戏尚未结束,代码将继续循环检查 mat 中的剩余单元格。
- 代码首先会检查游戏是否已经结束。
- 如果尚未结束,代码会检查是否有单元格被合并。
- 如果两个单元格已经合并,那么游戏结束,代码返回 “GAME NOT OVER”。
- 否则,代码将继续检查棋子的移动,直到有一个单元格是空的或者游戏结束。
- 如果出现平局,我们将宣布游戏失败。
- 代码将检查给定坐标处的单元格是否相等。
- 如果相等,则返回 “游戏未结束”。
- 如果不相等,则返回 “输了”。
- 代码通过将每个单元格的值复制到一个新列表来压缩网格。
- 代码首先创建一个空列表,然后循环矩阵中的所有单元格。
- 对于每个单元格,代码会计算新列表中所有单元格值的总和。
- 然后将该总和赋值给 i 变量。
- 下一行将创建一个名为 changed 的 bool 变量。
- 该变量将跟踪自上次调用 compress() 以来是否发生了任何变化。
- 如果没有变化,那么 changed 将被设置为 False。
- 否则,changed 变为 True。
- 接下来,代码会将每个单元格的值复制到一个新的列表中,从而压缩网格。
- 具体做法是循环遍历 mat 中的所有单元格,并将每个单元格的值乘以 4 。
- 最后,代码将这些列表相加,创建 new_mat。
- 代码在合并单元格前后的每一步都会压缩网格。
- bool 变量 changed 用于确定是否发生了变化。
- 如果没有变化,代码就会创建一个空网格。
- 代码首先声明了两个变量:changed 和 new_mat。
- changed 变量将记录矩阵中的单元格是否被修改。
- new_mat 变量将保存压缩矩阵向左移动一行后乘以 2 的结果。
- 接下来,代码会依次循环每一列。
- 对于该列中的每个单元格,如果其值等于下一个单元格的值,并且它们不是空的,那么就会重复检1. 查以确保它们仍然相等。
- 如果相等,则将它们的值设置为原始值的 2 倍,并清空该列中的下一个单元格,以便为将来的计算保留一个新值。
- 下一步将调用 merge() 函数。
- 该函数将一个 4×4 单元的矩阵作为输入,并根据单元格的值将其中的所有单元格合并在一起。
- 如果有任何单元格被修改,那么在将其返回给调用者之前,将在该函数中更新它们的值。
- 代码首先声明一个变量 i 代表行号,j 代表列号。
- 然后循环查看矩阵中的每个单元格,检查当前单元格的值是否与该行的下一个单元格一致,并确保两个单元格都不是空的。
- 如果这两个条件都满足,那么当前单元格的值就会加倍,并在该行的下一个单元格中设置为 0。
- 矩阵中的每一行都会重复这一过程。
- 代码首先创建一个布尔变量 “changed”,用于表示合并后的新网格是否不同。
- 如果网格不同,代码就会执行 reverse() 函数将矩阵反转,使其按原来的顺序显示。
- 然后使用 transpose() 函数交换行和列。
- 最后,update_mat() 函数将使用这两个函数来更改 mat 的内容。
- 代码首先创建一个名为 changed 的布尔变量,并将其设置为 True。
- 然后调用 reverse() 函数反转矩阵。
- 然后调用 transpose() 函数交错行和列。
- 最后,以这两个函数为参数调用 update_mat(),以更改 mat 的内容。
- 代码首先定义了两个变量:changed 和 mat。
- 一旦矩阵被合并,changed 变量将被设置为 True,因此代表新的网格。
- mat 变量将保持不变,因为它并不代表新网格。
- 下一个代码块定义了一个函数 reverse,该函数将反转 mat 变量中的行序列。
- 方法是在每一行添加一个空列表,然后引用该行中的各个列表项。
- 最后,还定义了 transpose 函数,用于交换 mat 中的行和列。
- 代码首先会压缩网格,从而缩小网格。
- 接下来,代码会合并新网格中的单元格,然后返回新矩阵和已更改的 bool 值。
- 最后,代码再次压缩新矩阵。
- 压缩的第一步是通过删除重复值来缩小每一行和每一列的大小。
- 第二步是将相邻的单元格合并在一起,使它们形成一个具有完整原始值的单元格。
- 最后,代码会再次压缩合并单元格,以再次创建一个更小的网格。
- 代码首先压缩网格,然后合并单元格并返回新的压缩网格。
- 然后,再次压缩新网格,并比较两个结果。
- 最后,返回新矩阵和已更改的 bool。
- 本节代码用于更新屏幕上的网格。
- 代码首先会检查用户是否向右或向左移动了手指(或轻扫)。
- 如果用户向右移动了手指(或轻扫),代码就会反向更新网格。
- 接下来,如果用户向上移动了手指(或轻扫),那么代码就不会反转矩阵,而是取其转置值并相应1地更新网格。
- 代码首先反转网格矩阵。
- 这对于向右或向上移动是必要的。
- 然后,根据输入的方向更新网格矩阵。
- 最后,返回更新后的网格和更改后的值。
- 代码首先创建两个新变量 new_grid 和 changed。
- 然后,代码使用 move_left 函数将网格向左移动。
- 接着,代码对新网格进行转置,创建一个新矩阵。
- 最后,代码同时返回原始网格和转置矩阵。
- move_down 函数的工作方式与此类似。
- 首先,它会创建两个新变量 new_grid 和 changed。
- 然后使用 move_down 函数向下移动。
- 再次,使用 transpose 创建一个新矩阵。
- 最后,原始网格和转置矩阵都会返回。
- 代码首先创建一个新的 2×2 网格。
- 然后,将新网格最左边的一列向下移动一行,最右边的一列向上移动一行。
- 最后,代码将新创建的网格转置,使其恢复到原来的形式。
- 如果在一个 3×3 矩阵上运行这段代码,它会将矩阵的左上角向下移动一行,将矩阵的右下角向上移动一行。
2048.py
# 2048.py
# importing the logic.py file
# where we have written all the
# logic functions used.
import logic
# Driver code
if __name__ == '__main__':
# calling start_game function
# to initialize the matrix
mat = logic.start_game()
while(True):
# taking the user input
# for next step
x = input("Press the command : ")
# we have to move up
if(x == 'W' or x == 'w'):
# call the move_up function
mat, flag = logic.move_up(mat)
# get the current state and print it
status = logic.get_current_state(mat)
print(status)
# if game not over then continue
# and add a new two
if(status == 'GAME NOT OVER'):
logic.add_new_2(mat)
# else break the loop
else:
break
# the above process will be followed
# in case of each type of move
# below
# to move down
elif(x == 'S' or x == 's'):
mat, flag = logic.move_down(mat)
status = logic.get_current_state(mat)
print(status)
if(status == 'GAME NOT OVER'):
logic.add_new_2(mat)
else:
break
# to move left
elif(x == 'A' or x == 'a'):
mat, flag = logic.move_left(mat)
status = logic.get_current_state(mat)
print(status)
if(status == 'GAME NOT OVER'):
logic.add_new_2(mat)
else:
break
# to move right
elif(x == 'D' or x == 'd'):
mat, flag = logic.move_right(mat)
status = logic.get_current_state(mat)
print(status)
if(status == 'GAME NOT OVER'):
logic.add_new_2(mat)
else:
break
else:
print("Invalid Key Pressed")
# print the matrix after each
# move.
print(mat)
代码解释:
- 代码首先导入逻辑模块。
- 该模块包含我们将在程序中使用的所有函数。
- 接下来,我们需要一个函数来初始化矩阵。
- 这是通过调用 start_game() 函数完成的。
- while 循环一直运行到用户按下键盘上的任意键(W、S、A、D)为止。
- 每按一次键,我们都会调用逻辑中的一个函数。
- 这些函数是 move_up()、move_down() 和 move_left()。
- 我们还需要调用 get_current_state(),以获取矩阵当前状态的信息。
- logic 中的每个函数都需要两个参数:mat 和 flag。
- mat 是矩阵对象,flag 是表示向上移动的 "W "或表示向下移动的 “S”。
- 如果你还记得本章前面的内容,这些都是对变量的引用,用于存储有关游戏棋盘的数据。
- 调用每个函数后,我们会打印出结果,然后使用状态变量检查游戏是否结束。
- 如果还没有结束,我们就使用 add_new_2() 在矩阵中添加新的一行。
- 否则,我们就跳出循环,因为在这个代码块中已经没有其他事情可做了!
- 最后,在
- 代码首先导入 logic.py 文件。
- 该文件包含本项目中使用的所有函数。
- 接着,我们编写了驱动程序代码。
- 在这段代码中,我们将检查按键的输入,并根据输入情况调用 logic.py 文件中的某个函数。
- 我们将在本文稍后部分详细讨论这些函数。
- while 循环用于跟踪用户输入并执行其中的相应代码。
- 该循环中的代码将一直执行,直到用户按下任何其他键或游戏结束。
- 在 if 语句中,我们将检查不同的按键,并根据输入情况调用 logic.py 中的一个函数