网络开发者 Gabriele Cirulli 在一个周末就发明了游戏 2048。它的灵感来自于 Veewo 工作室的 1024 游戏,而这个游戏的灵感又来自于 Threes!,开发团队 Sirvo 的一款游戏。2048,你必须在一个4 × 4的板上合并数字,才能把它们从屏幕上清除。两个 2 合并成一个 4,两个 4 合并成一个 8,以此类推。每次合并时,游戏都会在棋盘上添加一个新的 2。目标是在整个董事会填满之前达到 2048。
当您运行twentyfortyeight.py时,输出将如下所示:
Twenty Forty-Eight, by Al Sweigart email@protected
`--snip--`
+-----+-----+-----+-----+
| | | | |
| | | 2 | 16 |
| | | | |
+-----+-----+-----+-----+
| | | | |
| | 16 | 4 | 2 |
| | | | |
+-----+-----+-----+-----+
| | | | |
| 2 | | 4 | 32 |
| | | | |
+-----+-----+-----+-----+
| | | | |
| | | | 2 |
| | | | |
+-----+-----+-----+-----+
Score: 80
Enter move: (WASD or Q to quit)
`--snip--`这个程序使用“列”数据结构实现它的滑动行为,由四个字符串列表表示:BLANK(一个单空格字符串)、'2'、'4'、'8'等等。该列表中的第一个值表示列的底部,而最后一个值表示列的顶部。无论玩家向上、向下、向左或向右滑动牌,组合在一列中的数字总是向下滑动。想象一下重力将瓷砖拉向这些方向。例如,图 79-1 显示了一块向右滑动的棋盘。我们将创建四个列表来表示列:
['2', '4', '8', ' '][' ', ' ', ' ', '4'][' ', ' ', ' ', '2'][' ', ' ', ' ', ' ']
combineTilesInColumn()函数接受一个列列表并返回另一个列列表,匹配的数字被组合并向底部移动。调用combineTilesInColumn()的代码负责在适当的方向创建列列表,并用返回的列表更新游戏板。
:游戏板向右滑动时的列(高亮显示)
"""Twenty Forty-Eight, by Al Sweigart email@protected
A sliding tile game to combine exponentially-increasing numbers.
Inspired by Gabriele Cirulli's 2048, which is a clone of Veewo Studios'
1024, which in turn is a clone of the Threes! game.
More info at https://en.wikipedia.org/wiki/2048_(video_game)
This code is available at https://nostarch.com/big-book-small-python-programming
Tags: large, game, puzzle"""
import random, sys
# Set up the constants:
BLANK = '' # A value that represents a blank space on the board.
def main():
print('''Twenty Forty-Eight, by Al Sweigart email@protected
Slide all the tiles on the board in one of four directions. Tiles with
like numbers will combine into larger-numbered tiles. A new 2 tile is
added to the board on each move. You win if you can create a 2048 tile.
You lose if the board fills up the tiles before then.''')
input('Press Enter to begin...')
gameBoard = getNewBoard()
while True: # Main game loop.
drawBoard(gameBoard)
print('Score:', getScore(gameBoard))
playerMove = askForPlayerMove()
gameBoard = makeMove(gameBoard, playerMove)
addTwoToBoard(gameBoard)
if isFull(gameBoard):
drawBoard(gameBoard)
print('Game Over - Thanks for playing!')
sys.exit()
def getNewBoard():
"""Returns a new data structure that represents a board.
It's a dictionary with keys of (x, y) tuples and values of the tile
at that space. The tile is either a power-of-two integer or BLANK.
The coordinates are laid out as:
X0 1 2 3
Y+-+-+-+-+
0| | | | |
+-+-+-+-+
1| | | | |
+-+-+-+-+
2| | | | |
+-+-+-+-+
3| | | | |
+-+-+-+-+"""
newBoard = {} # Contains the board data structure to be returned.
# Loop over every possible space and set all the tiles to blank:
for x in range(4):
for y in range(4):
newBoard[(x, y)] = BLANK
# Pick two random spaces for the two starting 2's:
startingTwosPlaced = 0 # The number of starting spaces picked.
while startingTwosPlaced < 2: # Repeat for duplicate spaces.
randomSpace = (random.randint(0, 3), random.randint(0, 3))
# Make sure the randomly selected space isn't already taken:
if newBoard[randomSpace] == BLANK:
newBoard[randomSpace] = 2
startingTwosPlaced = startingTwosPlaced + 1
return newBoard
def drawBoard(board):
"""Draws the board data structure on the screen."""
# Go through each possible space left to right, top to bottom, and
# create a list of what each space's label should be.
labels = [] # A list of strings for the number/blank for that tile.
for y in range(4):
for x in range(4):
tile = board[(x, y)] # Get the tile at this space.
# Make sure the label is 5 spaces long:
labelForThisTile = str(tile).center(5)
labels.append(labelForThisTile)
# The {} are replaced with the label for that tile:
print("""
+-----+-----+-----+-----+
| | | | |
|{}|{}|{}|{}|
| | | | |
+-----+-----+-----+-----+
| | | | |
|{}|{}|{}|{}|
| | | | |
+-----+-----+-----+-----+
| | | | |
|{}|{}|{}|{}|
| | | | |
+-----+-----+-----+-----+
| | | | |
|{}|{}|{}|{}|
| | | | |
+-----+-----+-----+-----+
""".format(*labels))
def getScore(board):
"""Returns the sum of all the tiles on the board data structure."""
score = 0
# Loop over every space and add the tile to the score:
for x in range(4):
for y in range(4):
# Only add non-blank tiles to the score:
if board[(x, y)] != BLANK:
score = score + board[(x, y)]
return score
def combineTilesInColumn(column):
"""The column is a list of four tile. Index 0 is the "bottom" of
the column, and tiles are pulled "down" and combine if they are the
same. For example, combineTilesInColumn([2, BLANK, 2, BLANK])
returns [4, BLANK, BLANK, BLANK]."""
# Copy only the numbers (not blanks) from column to combinedTiles
combinedTiles = [] # A list of the non-blank tiles in column.
for i in range(4):
if column[i] != BLANK:
combinedTiles.append(column[i])
# Keep adding blanks until there are 4 tiles:
while len(combinedTiles) < 4:
combinedTiles.append(BLANK)
# Combine numbers if the one "above" it is the same, and double it.
for i in range(3): # Skip index 3: it's the topmost space.
if combinedTiles[i] == combinedTiles[i + 1]:
combinedTiles[i] *= 2 # Double the number in the tile.
# Move the tiles above it down one space:
for aboveIndex in range(i + 1, 3):
combinedTiles[aboveIndex] = combinedTiles[aboveIndex + 1]
combinedTiles[3] = BLANK # Topmost space is always BLANK.
return combinedTiles
def makeMove(board, move):
"""Carries out the move on the board.
The move argument is either 'W', 'A', 'S', or 'D' and the function
returns the resulting board data structure."""
# The board is split up into four columns, which are different
# depending on the direction of the move:
if move == 'W':
allColumnsSpaces = [[(0, 0), (0, 1), (0, 2), (0, 3)],
[(1, 0), (1, 1), (1, 2), (1, 3)],
[(2, 0), (2, 1), (2, 2), (2, 3)],
[(3, 0), (3, 1), (3, 2), (3, 3)]]
elif move == 'A':
allColumnsSpaces = [[(0, 0), (1, 0), (2, 0), (3, 0)],
[(0, 1), (1, 1), (2, 1), (3, 1)],
[(0, 2), (1, 2), (2, 2), (3, 2)],
[(0, 3), (1, 3), (2, 3), (3, 3)]]
elif move == 'S':
allColumnsSpaces = [[(0, 3), (0, 2), (0, 1), (0, 0)],
[(1, 3), (1, 2), (1, 1), (1, 0)],
[(2, 3), (2, 2), (2, 1), (2, 0)],
[(3, 3), (3, 2), (3, 1), (3, 0)]]
elif move == 'D':
allColumnsSpaces = [[(3, 0), (2, 0), (1, 0), (0, 0)],
[(3, 1), (2, 1), (1, 1), (0, 1)],
[(3, 2), (2, 2), (1, 2), (0, 2)],
[(3, 3), (2, 3), (1, 3), (0, 3)]]
# The board data structure after making the move:
boardAfterMove = {}
for columnSpaces in allColumnsSpaces: # Loop over all 4 columns.
# Get the tiles of this column (The first tile is the "bottom"
# of the column):
firstTileSpace = columnSpaces[0]
secondTileSpace = columnSpaces[1]
thirdTileSpace = columnSpaces[2]
fourthTileSpace = columnSpaces[3]
firstTile = board[firstTileSpace]
secondTile = board[secondTileSpace]
thirdTile = board[thirdTileSpace]
fourthTile = board[fourthTileSpace]
# Form the column and combine the tiles in it:
column = [firstTile, secondTile, thirdTile, fourthTile]
combinedTilesColumn = combineTilesInColumn(column)
# Set up the new board data structure with the combined tiles:
boardAfterMove[firstTileSpace] = combinedTilesColumn[0]
boardAfterMove[secondTileSpace] = combinedTilesColumn[1]
boardAfterMove[thirdTileSpace] = combinedTilesColumn[2]
boardAfterMove[fourthTileSpace] = combinedTilesColumn[3]
return boardAfterMove
def askForPlayerMove():
"""Asks the player for the direction of their next move (or quit).
Ensures they enter a valid move: either 'W', 'A', 'S' or 'D'."""
print('Enter move: (WASD or Q to quit)')
while True: # Keep looping until they enter a valid move.
move = input('> ').upper()
if move == 'Q':
# End the program:
print('Thanks for playing!')
sys.exit()
# Either return the valid move, or loop back and ask again:
if move in ('W', 'A', 'S', 'D'):
return move
else:
print('Enter one of "W", "A", "S", "D", or "Q".')
def addTwoToBoard(board):
"""Adds a new 2 tile randomly to the board."""
while True:
randomSpace = (random.randint(0, 3), random.randint(0, 3))
if board[randomSpace] == BLANK:
board[randomSpace] = 2
return # Return after finding one non-blank tile.
def isFull(board):
"""Returns True if the board data structure has no blanks."""
# Loop over every space on the board:
for x in range(4):
for y in range(4):
# If a space is blank, return False:
if board[(x, y)] == BLANK:
return False
return True # No space is blank, so return True.
# If this program was run (instead of imported), run the game:
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
sys.exit() # When Ctrl-C is pressed, end the program. 试着找出下列问题的答案。尝试对代码进行一些修改,然后重新运行程序,看看这些修改有什么影响。
- 如果把 118 行的
return score改成return 9999会怎么样? - 如果把 229 行的
board[randomSpace] = 2改成board[randomSpace] = 256会怎么样?

