随机是大部分游戏的重要组成部分之一
大量的随机可以保持游戏的可玩性
363. def getNewPiece():
364. # return a random new piece in a random rotation and color
365. shape = random.choice(list(SHAPES.keys()))
366. newPiece = {'shape': shape,
367. 'rotation': random.randint(0, len(SHAPES[shape]) - 1),
368. 'x': int(BOARDWIDTH / 2) - int(TEMPLATEWIDTH / 2),
369. 'y': -2, # start it above the board (i.e. less than 0)
370. 'color': random.randint(0, len(COLORS)-1)}
371. return newPiece
getNewPiece()函数制造位于底板顶部的随机积木。 首先,要随机选择该积木的形状,我们通过在线365上调用列表(SHAPES.keys())创建所有可能形状的列表。keys()库方法返回数据类型“dict_keys”的值, ,它必须在传递给random.choice()之前转换为list()函数的列表值。 这是因为random.choice()函数只接受其参数的列表值。 random.choice()函数随后从列表中返回一个项目的值。
积木数据结构只是具有键“形状”,“旋转”,“x”,“y”和“颜色”的简单的数据库。
“旋转”键的值是一个0到1之间的随机整数,而不是该形状的许多可能的旋转。 形状的旋转数可以从表达式len(SHAPES [shape])中找到。
请注意,我们不会在每个积木数据结构中存储字符串值列表(如存储在S_SHAPE_TEMPLATE中的常量),以表示每个片段的框。 相反,我们只是存储指向PIECES常数的形状和旋转的索引。
“x”键的值始终设置为底板的中间(也称为片段本身的宽度,从我们的TEMPLATEWIDTHconstant中可以看出)。 “y”键的值始终设置为-2,将其略高于底板。 (底板的顶行是第0行)
由于COLORS常数是不同颜色的元组,因此从0到COLORS(减1)的长度中选择一个随机数将为我们的积木颜色提供一个随机的索引值。
一旦newPiece字库中的所有值都被设置,getNewPiece()函数返回newPiece。
374. def addToBoard(board, piece):
375. # fill in the board based on piece's location, shape, and rotation
376. for x in range(TEMPLATEWIDTH):
377. for y in range(TEMPLATEHEIGHT):
378. if SHAPES[piece['shape']][piece['rotation']][y][x] != BLANK:
379. board[x piece['x']][y piece['y']] = piece['color']
底板数据结构是用于跟踪先前着陆的积木的矩形空格的数据表示。 正在将降落的积木没有被底板数据库所标记。 addToBoard()函数做的是采用一个积木的数据结构,在底板数据结构中增加方块数据。 这是积木落地后发生的。
376和377行上的嵌套for循环经过没个积木数据结构中的每个空格,如果在空格(行378)中找到一个方块,则将其添加到底板中(第379行)。
382. def getBlankBoard():
383. # create and return a new blank board data structure
384. board = []
385. for i in range(BOARDWIDTH):
386. board.append([BLANK] * BOARDHEIGHT)
387. return board
用于该板的数据结构相当简单:它是值列表的列表。 如果值与BLANK中的值相同,那么它是一个空的空间。 如果该值为整数,则表示一个方块,它是COLORS常量列表中整数索引的颜色。 也就是说,0是蓝色,1是绿色,2是红色,3是黄色。
为了创建一个空白底板,列表分隔符用于创建表示列表的BLANK值。 这是在第386行完成的。这些列表之一是为底板中的每个纵行创建的(这是第385行的for循环)。
390. def isOnBoard(x, y):
391. return x >= 0 and x < BOARDWIDTH and y < BOARDHEIGHT
isOnBoard()是一个简单的函数,它检查传递的XY坐标表示板上存在的有效值。 只要XY坐标不小于0或大于或等于BOARDWIDTH和BOARDHEIGHT常数,则函数返回True。
394. def isValidPosition(board, piece, adjX=0, adjY=0):
395. # Return True if the piece is within the board and not colliding
396. for x in range(TEMPLATEWIDTH):
397. for y in range(TEMPLATEHEIGHT):
398. isAboveBoard = y piece['y'] adjY < 0
399. if isAboveBoard or SHAPES[piece['shape']][piece['rotation']][y][x] == BLANK:
400. continue
isValidPosition()函数给出了一个底板数据结构和一个积木数据结构,如果该积木中的所有方块都在底板上并且不与底板上的任何方块重叠,则返回True。 这是通过拍摄片段的XY坐标(实际上是5x5方块上右上方框的坐标),并在积木数据结构中添加坐标。 这是几张照片,以帮助说明这一点:
在左边的底板子上,掉落的积木(即落下的积木的左上角)XY坐标是底板上的(2,3)。 但是落下的部分坐标系中的方块有自己的坐标。 要找到这些作品的“底板”坐标,我们只需要添加掉落积木左上方的“底板”坐标和方块的“积木”坐标。
在左侧底板,下落积木中的方块在下列积木的坐标
(2, 2) (3, 2) (1, 3) (2, 3)
当我们将坐标(2,3)(底板上积木的坐标)添加到上述坐标时,它看起来像这样:
(2 2, 2 3) (3 2, 2 3) (1 2, 3 3) (2 2, 3 3)
添加坐标(2,3)后,方块位于以下“底板”的坐标:
(4, 5) (5, 5) (3, 6) (4, 6)
现在我们可以弄清楚下落块的框是哪个板坐标,我们可以看到它们是否与已经在底板上的着陆的方块重叠。 行396和397上的嵌套for循计算所有下落积木上的每个可能的坐标。
我们想检查一个下降的方块是否在底板子上,或者与底板上的一个方块重叠。 (虽然一个例外是如果方块在底板上方,当下降的部分刚刚开始下降时,它是可能的)。行398创建一个名为isAboveBoard的变量,如果在坐标上的下降方块部分上的方块 指出x和y在底板上显示为True。 否则设置为False。
行399上的if语句检查该片上的空格是否在板上或空白。 如果其中任何一个为True,则代码执行一个连续运行并转到下一个继续重复运行。 (注意,行399的结尾具有[y] [x]而不是[x] [y],这是因为PIECES数据结构中的坐标相反,参见上一节“设置积木模板”)。
401. if not isOnBoard(x piece['x'] adjX, y piece['y'] adjY):
402. return False
403. if board[x piece['x'] adjX][y piece['y'] adjY] != BLANK:
404. return False
405. return True
行401上的if语句检查该积木上的方块不在底板上。行403上的if语句检查该方块的位置的底板空间不为空。 如果这些条件之一为True,则isValidPosition()函数将反馈为False。请注意,这些if语句还会调整传递给函数的adjX和adjY参数的坐标。
如果代码经过嵌套的for循环,并且没有找到反馈为False的原因,则该积木的位置必须是有效的,因此函数在第405行反馈为True。
407. def isCompleteLine(board, y):
408. # Return True if the line filled with boxes with no gaps.
409. for x in range(BOARDWIDTH):
410. if board[x][y] == BLANK:
411. return False
412. return True
isCompleteLine在y参数指定的行中进行简单的检查。 当每个空间都被一个方块填满时,板上的一行被认为是“完整的”。 行409上的for循环遍历行中的每个空格。 如果一个空格是空白的(它是由与BLANK常量相同的值引起的),那么函数返回False。
415. def removeCompleteLines(board):
416. # Remove any completed lines on the board, move everything above them down, and return the number of complete lines.
417. numLinesRemoved = 0
418. y = BOARDHEIGHT - 1 # start y at the bottom of the board
419. while y >= 0:
removeCompleteLines()函数将在传递的板数据结构中找到任何完整的行,删除行,然后将板上的所有框移动到该行的下一行。 该函数将反馈已删除的行数(由numLinesRemoved变量跟踪),以便将其添加到分数中。
该函数的工作方式是运行在从第419行开始的循环中,y变量从最下面的行开始(这是BOARDHEIGHT - 1)。 每当y指定的行不完整时,y将递减到下一个最高的行。 一旦y达到-1,循环终止。
420. if isCompleteLine(board, y):
421. # Remove the line and pull boxes down by one line.
422. for pullDownY in range(y, 0, -1):
423. for x in range(BOARDWIDTH):
424. board[x][pullDownY] = board[x][pullDownY-1]
425. # Set very top line to blank.
426. for x in range(BOARDWIDTH):
427. board[x][0] = BLANK
428. numLinesRemoved = 1
429. # Note on the next iteration of the loop, y is the same.
430. # This is so that if the line that was pulled down is also
431. # complete, it will be removed.
432. else:
433. y -= 1 # move on to check next row up
434. return numLinesRemoved
如果y引用的行完成,则isCompleteLine()函数将返回True。 在这种情况下,程序需要将删除的行上方的每行的值复制到下一个最下行。 这就是第422行的for循环(这就是为什么它调用range()函数从y开始,而不是0.另外注意它使用range()的三个参数形式,以便它返回的列表 从y开始,以0结束,每次迭代之后“增加”为-1)
我们来看下面的例子。 为了节省空间,只显示底板的前五行。 行3是一条完整的行,这意味着它上面的所有行(行2,1和0)必须“下拉”。 首先,第2行被复制到第3行。右侧的板显示完成之后,底的外观如何:
这个“下拉”实际上只是将更高的行的值复制到第424行下面的行。将行2复制到行3后,将行1复制到行2,然后将行0复制到行1:
完整的行被删除后,执行到达行419开始的while循环的结尾,因此执行将跳回循环的开头。 请注意,当行被删除并且行被拉下时,y变量根本没有改变。 所以在下一次迭代中,y变量指向与之前相同的行。
这是需要的,因为如果有两条完整的线,那么第二条完整的线将被拉下来,也必须被消除。 然后代码将消除此完整的行,然后转到下一个重复。 只有当没有完成的行,y变量在行433上递减。一旦y变量一直递减到0,执行将退出while循环。
(未完待续)
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved