国庆期间闲不住,用python把经典俄罗斯方块实现了一遍,找到了些儿时的乐趣。因此突发奇想,正统编程之余也给自己找点儿乐趣,换个角度写程序。
原计划是写篇完整的博文对程序算法和函数模块做个说明,但是在整理程序的时候发现自己给程序加的注释已经相当详细,程序以外的文字显得很多余。正所谓大道至简,直接将程序代码贴上来,大家就着代码、伴着注解,相信对程序的理解应该很容易。

配置文件 elsfk.cfg,定义了单一方向的原始方块形状组合,具体的格式说明请参见getConf中的注解。

;1,1,1,1;;
1,1,1,0;1,0,0,0;;
1,1,1,0;0,0,1,0;;
0,1,0,0;1,1,1,0;;
1,1,0,0;1,1,0,0;;
1,1,0,0;0,1,1,0;;
0,1,1,0;1,1,0,0;;

完整程序代码:

# -*- coding:utf-8 -*-
'''
经典俄罗斯方块
游戏基于python2.7、pygame1.9.2b8编写。游戏注解中出现的术语解释:
舞台:整个游戏界面,包括堆叠区、成绩等显示区,下个出现方块预告区。
堆叠区:游戏方块和活动方块形状堆放区域,游戏中主要互动区。
方块(基础方块):这里的方块是对基础的小四方形统称,每个方块就是一个正方形。
方块形状:指一组以特定方式组合在一起的方块,也就是大家常说的下落方块形状,比如长条,方形,L形等。
固实方块:特指堆叠区中不能再进行移动,可被消除的基础方块集合。version:1.0
author:lykyl
createdate:2016.9.29
'''
import sys
import random,copy
import pygame as pg
from pygame.locals import *'''
常量声明
'''
EMPTY_CELL=0        #空区标识,表示没有方块
FALLING_BLOCK=1     #下落中的方块标识,也就是活动方块。
STATIC_BLOCK=2      #固实方块标识'''
全局变量声明
变量值以sysInit函数中初始化后的结果为准
'''
defaultFont=None        #默认字体
screen=None     #屏幕输出对象
backSurface=None        #图像输出缓冲画板
score=0     #玩家得分记录
clearLineScore=0        #玩家清除的方块行数
level=1     #关卡等级
clock=None      #游戏时钟
nowBlock=None       #当前下落中的方块
nextBlock=None      #下一个将出现的方块
fallSpeed=10        #当前方块下落速度
beginFallSpeed=fallSpeed        #游戏初始时方块下落速度
speedBuff=0     #下落速度缓冲变量
keyBuff=None        #上一次按键记录
maxBlockWidth=10        #舞台堆叠区X轴最大可容纳基础方块数
maxBlockHeight=18       #舞台堆叠区Y轴最大可容纳基础方块数
blockWidth=30       #以像素为单位的基础方块宽度
blockHeight=30      #以像素为单位的基础方块高度
blocks=[]       #方块形状矩阵四维列表。第一维为不同的方块形状,第二维为每个方块形状不同的方向(以0下标起始,一共四个方向),第三维为Y轴方块形状占用情况,第四维为X轴方块形状占用情况。矩阵中0表示没有方块,1表示有方块。
stage=[]        #舞台堆叠区矩阵二维列表,第一维为Y轴方块占用情况,第二维为X轴方块占用情况。矩阵中0表示没有方块,1表示有固实方块,2表示有活动方块。
gameOver=False      #游戏结束标志
pause=False     #游戏暂停标志def printTxt(content,x,y,font,screen,color=(255,255,255)):'''显示文本args:content:待显示文本内容x,y:显示坐标font:字体screen:输出的screencolor:颜色'''imgTxt=font.render(content,True,color)screen.blit(imgTxt,(x,y))class point(object):'''平面坐标点类attributes:x,y:坐标值'''def __init__(self,x,y):self.__x=xself.__y=ydef getx(self):return self.__xdef setx(self,x):self.__x=xx=property(getx,setx)def gety(self):return self.__ydef sety(self,y):self.__y=yy=property(gety,sety)    def __str__(self):return "{x:"+"{:.0f}".format(self.__x)+",y:"+"{:.0f}".format(self.__y)+"}"class blockSprite(object):'''方块形状精灵类下落方块的定义全靠它了。attributes:shape:方块形状编号direction:方块方向编号xy,方块形状左上角方块坐标block:方块形状矩阵'''def __init__(self,shape,direction,xy):self.shape=shapeself.direction=directionself.xy=xydef chgDirection(self,direction):'''改变方块的方向args:direction:1为向右转,0为向左转。'''dirNumb=len(blocks[self.shape])-1if direction==1:self.direction+=1if self.direction>dirNumb:self.direction=0else:self.direction-=1if self.direction<0:self.direction=dirNumbdef clone(self):'''克隆本体return:返回自身的克隆'''return blockSprite(self.shape,self.direction,point(self.xy.x,self.xy.y))def _getBlock(self):return blocks[self.shape][self.direction]block = property(_getBlock)def getConf(fileName):'''从配置文件中读取方块形状数据每个方块以4*4矩阵表示形状,配置文件每行代表一个方块,用分号分隔矩阵行,用逗号分隔矩阵列,0表示没有方块,1表示有方块。因为此程序只针对俄罗斯方块的经典版,所以方块矩阵大小以硬编码的形式写死为4*4。args:fileName:配置文件名'''global blocks   #blocks记录方块形状。with open(fileName,'rt') as fp:for temp in fp.readlines():blocks.append([])blocksNumb=len(blocks)-1blocks[blocksNumb]=[]#每种方块形状有四个方向,以0~3表示。配置文件中只记录一个方向形状,另外三个方向的矩阵排列在sysInit中通过调用transform计算出来。blocks[blocksNumb].append([])row=temp.split(";")for r in range(len(row)):col=[]ct=row[r].split(",")#对矩阵列数据做规整,首先将非“1”的值全修正成“0”以过滤空字串或回车符。for c in range(len(ct)):if ct[c]!="1":col.append(0)else:col.append(1)#将不足4列的矩阵通过补“0”的方式,补足4列。for c in range(len(ct)-1,3):col.append(0)blocks[blocksNumb][0].append(col)#如果矩阵某行没有方块,则配置文件中可以省略此行,程序会在末尾补上空行数据。for r in range(len(row)-1,3):blocks[blocksNumb][0].append([0,0,0,0])blocks[blocksNumb][0]=formatBlock(blocks[blocksNumb][0])def sysInit():'''系统初始化包括pygame环境初始化,全局变量赋值,生成每个方块形状的四个方向矩阵。'''global defaultFont,screen,backSurface,clock,blocks,stage,gameOver,fallSpeed,beginFallSpeed,nowBlock,nextBlock,score,level,clearLineScore,pause#pygame运行环境初始化pg.init()screen=pg.display.set_mode((500,550))backSurface=pg.Surface((screen.get_rect().width,screen.get_rect().height))pg.display.set_caption("block")clock=pg.time.Clock()pg.mouse.set_visible(False)#游戏全局变量初始化defaultFont=pg.font.Font("res/font/yh.ttf",16)        #yh.ttf这个字体文件请自行上网搜索下载,如果找不到就随便用个ttf格式字体文件替换一下。nowBlock=NonenextBlock=NonegameOver=Falsepause=Falsescore=0level=1clearLineScore=0beginFallSpeed=20fallSpeed=beginFallSpeed-level*2#初始化游戏舞台stage=[]for y in range(maxBlockHeight):stage.append([])for x in range(maxBlockWidth):stage[y].append(EMPTY_CELL)#生成每个方块形状4个方向的矩阵数据for x in range(len(blocks)):#因为重新开始游戏时会调用sysinit对系统所有参数重新初始化,为了避免方向矩阵数据重新生成,需要在此判断是否已经生成,如果已经生成则跳过。if len(blocks[x])<2:t=blocks[x][0]for i in range(3):t=transform(t,1)blocks[x].append(formatBlock(t))#transform,removeTopBlank,formatBlock这三个函数只为生成方块形状4个方向矩阵使用,在游戏其他环节无作用,在阅读程序时可以先跳过。
def transform(block,direction=0):  '''生成指定方块形状转换方向后的矩阵数据args:block:方块形状矩阵参数direction:转换的方向,0代表向左,1代表向右return:变换方向后的方块形状矩阵参数'''result=[]for y in range(4):result.append([])for x in range(4):if direction==0:result[y].append(block[x][3-y])else:result[y].append(block[3-x][y])return result def removeTopBlank(block):'''清除方块矩阵顶部空行数据args:block:方块开关矩阵return:整理后的方块矩阵数据'''result=copy.deepcopy(block)blankNumb=0while sum(result[0])<1 and blankNumb<4:del result[0]result.append([0,0,0,0])blankNumb+=1return resultdef formatBlock(block):'''整理方块矩阵数据,使方块在矩阵中处于左上角的位置args:block:方块开关矩阵return:整理后的方块矩阵数据'''result=removeTopBlank(block)#将矩阵右转,用于计算左侧X轴线空行,计算完成后再转回result=transform(result, 1)result=removeTopBlank(result)result=transform(result,0)return resultdef checkDeany(sprite):'''检查下落方块是否与舞台堆叠区中固实方块发生碰撞args:sprite:下落方块return:如果发生碰撞则返回True'''topX=sprite.xy.xtopY=sprite.xy.yfor y in range(len(sprite.block)):for x in range(len(sprite.block[y])):if sprite.block[y][x]==1:yInStage=topY+yxInStage=topX+xif yInStage>maxBlockHeight-1 or yInStage<0:return Trueif xInStage>maxBlockWidth-1 or xInStage<0:return Trueif stage[yInStage][xInStage]==STATIC_BLOCK:return True                return Falsedef checkLine():'''检测堆叠区是否有可消除的整行固实方块根据检测结果重新生成堆叠区矩阵数据,调用updateScore函数更新玩家积分等数据。return:本轮下落周期消除的固实方块行数'''global stageclearCount=0    #本轮下落周期消除的固实方块行数tmpStage=[]     #根据消除情况新生成的堆叠区矩阵,在有更新的情况下会替换全局的堆叠区矩阵。for y in stage:#因为固实方块在堆叠矩阵里以2表示,所以判断方块是否已经满一整行只要计算矩阵行数值合计是否等于堆叠区X轴最大方块数*2就可以。if sum(y)>=maxBlockWidth*2:tmpStage.insert(0,maxBlockWidth*[0])clearCount+=1else:tmpStage.append(y)if clearCount>0:stage=tmpStageupdateScore(clearCount)return clearCountdef updateStage(sprite,updateType=1):'''将下落方块坐标数据更新到堆叠区数据中。下落方块涉及的坐标在堆叠区中用数字1标识,固实方块在堆叠区中用数字2标识。args:sprite:下落方块形状updateType:更新方式,0代表清除,1代表动态加入,2代表固实加入。'''global stagetopX=sprite.xy.xtopY=sprite.xy.yfor y in range(len(sprite.block)):for x in range(len(sprite.block[y])):if sprite.block[y][x]==1:if updateType==0:if stage[topY+y][topX+x]==FALLING_BLOCK:stage[topY+y][topX+x]=EMPTY_CELLelif updateType==1:if stage[topY+y][topX+x]==EMPTY_CELL:stage[topY+y][topX+x]=FALLING_BLOCKelse:stage[topY+y][topX+x]=STATIC_BLOCKdef updateScore(clearCount):'''更新玩家游戏记录,包括积分、关卡、消除方块行数,并且根据关卡数更新方块下落速度。args:clearCount:本轮下落周期内清除的方块行数。return:当前游戏的最新积分'''global score,fallSpeed,level,clearLineScoreprizePoint=0    #额外奖励分数,同时消除的行数越多,奖励分值越高。if clearCount>1:if clearCount<4:prizePoint=clearCount**clearCountelse:prizePoint=clearCount*5score+=(clearCount+prizePoint)*level#玩得再牛又有何用? :)if score>99999999:score=0clearLineScore+=clearCountif clearLineScore>100:clearLineScore=0level+=1if level>(beginFallSpeed/2):level=1fallSpeed=beginFallSpeedfallSpeed=beginFallSpeed-level*2return scoredef drawStage(drawScreen):'''在给定的画布上绘制舞台args:drawScreen:待绘制的画布'''staticColor=30,102,76       #固实方块颜色activeColor=255,239,0       #方块形状颜色fontColor=200,10,120        #文字颜色baseRect=0,0,blockWidth*maxBlockWidth+1,blockHeight*maxBlockHeight+1        #堆叠区方框#绘制堆叠区外框drawScreen.fill((180,200,170))pg.draw.rect(drawScreen, staticColor, baseRect,1)#绘制堆叠区内的所有方块,包括下落方块形状for y in range(len(stage)):for x in range(len(stage[y])):baseRect=x*blockWidth,y*blockHeight,blockWidth,blockHeightif stage[y][x]==2:pg.draw.rect(drawScreen, staticColor, baseRect)elif stage[y][x]==1:pg.draw.rect(drawScreen, activeColor, baseRect)#绘制下一个登场的下落方块形状printTxt("Next:",320,350,defaultFont,backSurface,fontColor)if nextBlock!=None:for y in range(len(nextBlock.block)):for x in range(len(nextBlock.block[y])):baseRect=320+x*blockWidth,380+y*blockHeight,blockWidth,blockHeightif nextBlock.block[y][x]==1:pg.draw.rect(drawScreen, activeColor, baseRect)#绘制关卡、积分、当前关卡消除整行数printTxt("Level:%d" % level,320,40,defaultFont,backSurface,fontColor)printTxt("Score:%d" % score,320,70,defaultFont,backSurface,fontColor)printTxt("Clear:%d" % clearLineScore,320,100,defaultFont,backSurface,fontColor)#特殊游戏状态的输出if gameOver:printTxt("GAME OVER",230,200,defaultFont,backSurface,fontColor)   printTxt("<PRESS RETURN TO REPLAY>",200,260,defaultFont,backSurface,fontColor)   if pause:printTxt("Game pausing",230,200,defaultFont,backSurface,fontColor)   printTxt("<PRESS RETURN TO CONTINUE>",200,260,defaultFont,backSurface,fontColor)   def process():'''游戏控制及逻辑处理'''global gameOver,nowBlock,nextBlock,speedBuff,backSurface,keyBuff,pauseif nextBlock is None:nextBlock=blockSprite(random.randint(0,len(blocks)-1),random.randint(0,3),point(maxBlockWidth+4,maxBlockHeight))if nowBlock is None:nowBlock=nextBlock.clone()nowBlock.xy=point(maxBlockWidth//2,0)nextBlock=blockSprite(random.randint(0,len(blocks)-1),random.randint(0,3),point(maxBlockWidth+4,maxBlockHeight))#每次生成新的下落方块形状时检测碰撞,如果新的方块形状一出现就发生碰撞,则显然玩家已经没有机会了。gameOver=checkDeany(nowBlock)#游戏失败后,要将活动方块形状做固实处理if gameOver:updateStage(nowBlock,2)'''对于下落方块形状操控以及移动,采用影子形状进行预判断。如果没有碰撞则将变化应用到下落方块形状上,否则不变化。'''tmpBlock=nowBlock.clone()       #影子方块形状'''处理用户输入对于用户输入分为两部分处理。第一部分,将退出、暂停、重新开始以及形状变换的操作以敲击事件处理。这样做的好处是只对敲击一次键盘做出处理,避免用户按住单一按键后程序反复处理影响操控,特别是形状变换操作,敲击一次键盘换变一次方向,玩家很容易控制。'''for event in pg.event.get():if event.type== pg.QUIT:sys.exit()pg.quit()elif event.type==pg.KEYDOWN:if event.key==pg.K_ESCAPE:sys.exit()pg.quit()elif event.key==pg.K_RETURN:if gameOver:sysInit()returnelif pause:pause=Falseelse:pause=Truereturnelif not gameOver and not pause:if event.key==pg.K_SPACE:tmpBlock.chgDirection(1)elif event.key==pg.K_UP:tmpBlock.chgDirection(0)if not gameOver and not pause:'''用户输入处理第二部分,将左右移动和快速下落的操作以按下事件处理。这样做的好处是不需要玩家反复敲击键盘进行操作,保证了操作的连贯性。由于连续移动的速度太快,不利于定位。所以在程序中采用了简单的输入减缓处理,即通过keyBuff保存上一次操作按键,如果此次按键与上一次按键相同,则跳过此轮按键处理。'''keys=pg.key.get_pressed()if keys[K_DOWN]:tmpBlock.xy=point(tmpBlock.xy.x,tmpBlock.xy.y+1)keyBuff=Noneelif keys[K_LEFT]:if keyBuff!=pg.K_LEFT:tmpBlock.xy=point(tmpBlock.xy.x-1,tmpBlock.xy.y)keyBuff=pg.K_LEFTelse:keyBuff=Noneelif keys[K_RIGHT]:if keyBuff!=pg.K_RIGHT:tmpBlock.xy=point(tmpBlock.xy.x+1,tmpBlock.xy.y)keyBuff=pg.K_RIGHTelse:keyBuff=Noneif not checkDeany(tmpBlock):updateStage(nowBlock,0)nowBlock=tmpBlock.clone()#处理自动下落speedBuff+=1if speedBuff>=fallSpeed:speedBuff=0tmpBlock=nowBlock.clone()tmpBlock.xy=point(nowBlock.xy.x,nowBlock.xy.y+1)if not checkDeany(tmpBlock):updateStage(nowBlock,0)nowBlock=tmpBlock.clone()updateStage(nowBlock,1)else:#在自动下落过程中一但发生活动方块形状的碰撞,则将活动方块形状做固实处理,并检测是否有可消除的整行方块updateStage(nowBlock,2)checkLine()nowBlock=Noneelse:updateStage(nowBlock,1)drawStage(backSurface)screen.blit(backSurface,(0,0))pg.display.update()clock.tick(40)def main():'''主程序'''getConf("elsfk.cfg")sysInit()while True:process() if __name__ == "__main__":main()

程序运行截图:

转载于:https://www.cnblogs.com/lykyl/p/5946102.html

趣味python编程之经典俄罗斯方块相关推荐

  1. python编程方块_趣味Python编程之经典俄罗斯方块

    本帖最后由 柠檬守护 于 2016-10-23 14:14 编辑 转载:http://www.linuxidc.com/Linux/2016-10/136205.htm 用python把经典俄罗斯方块 ...

  2. python编程入门经典教程-2020年5个经典python编程入门视频教程推荐学习

    近三年,python在一二线城市,越来越火热了!企业的需求也是python编程学习者的动力!python学习网整理了适合于零基础朋友的5个python编程入门视频课程,这些都是python编程入门经典 ...

  3. python编程入门经典实例-总算明了python编程入门经典实例

    跟Java语言一样,python语言也有类的概念,直接使用class关键字定义python类.在python类,定义类的方法.然后直接使用类的初始化调用自身,获取相应的属性.以下是小编为你整理的pyt ...

  4. python编程入门经典教程-python编程入门经典

    第七章主要是文件操作相关,这么看用python---便,是一个不错的工具. <Python编程入门经典>主要内容:介绍用于存储和操作数据的变量的概念:讨论用于读写数据的文件和输入/输出:总 ...

  5. python三本经典书籍-《python编程入门经典》python之父推荐这三本书让你更快入门...

    在学习python的时候,我们不得不看一些书籍来辅助.有哪些书籍是比较经典的呢?python之父给大家推荐这3本书,其中又2本是属于一个系列,即<Python核心编程>第二版和第三版,一本 ...

  6. python编程入门经典-总算理解python编程入门经典教程

    为了提高模块加载的速度,每个模块都会在__pycache__文件夹中放置该模块的预编译模块,命名为module.version.pyc,version是模块的预编译版本编码,一般都包含Python的版 ...

  7. python编程入门经典百度云-python电子书学习资料打包分享百度云资源下载

    [300dpi高清版] Python基砒教程(第2版)LHD,pdf Head. First. Python中文版pdf [ Python3程序开发指南第二版pdf [ thon编程第4版)]( Pr ...

  8. python编程入门-Python编程入门经典pdf(Python编程入门教程) 高清中文版

    Python编程入门经典pdf(Python编程入门教程)下载.Python编程入门经典pdf高清版帮助各位更好的进行Python编程的学习以及理解,最经典的课题,最深入的概念,让你在Python编程 ...

  9. python编程入门经典-Python编程入门经典

    第七章主要是文件操作相关,这么看用python---便,是一个不错的工具. <Python编程入门经典>主要内容:介绍用于存储和操作数据的变量的概念:讨论用于读写数据的文件和输入/输出:总 ...

  10. python编程入门经典-Python编程入门经典PDF文档免费下载

    作为一门面向对象的开源编程语言,python易于理解和扩展,并且使用起来非常方便.<python编程入门经典>涵盖了python的方方面面,通过学习本书,读者可以立即使用python编写程 ...

最新文章

  1. U盘安装Centos6.3 续
  2. django——会话追踪技术
  3. Java判断字符串的开头和结尾
  4. IWMS实现频道页面的方法
  5. 【Servlet】Servlet显示时间和IP等信息
  6. C#编程指南:使用属性
  7. redis DB操作
  8. opencv转单通道python_在OpenCv(Python)中查找单通道图像的目录
  9. 进程间通信---共享内存 ftok shmat shmget shmdt shmctl
  10. 微信扫码支付开发流程
  11. 穿孔发光字/外露发光字制作流程步骤
  12. 中国传统四大菜系之:鲁菜
  13. 中国电信修改光猫路由模式为桥接模式
  14. 1143 Lowest Common Ancestor (30分) 附测试点分析
  15. 嵌入式软件的调试工具
  16. 【CSS】css 获取从第n个元素开始,之后的所有元素 :nth-of-type(n)与:nth-child(n)
  17. 艾永亮:为什么国内品牌很难在市场中生存下去
  18. Rot加密and逆序函数(废话版)
  19. REVV Racing 联手 SuperPlastic,为您带来 Chunder 迷宫锦标赛
  20. 网络上的优美感人句子

热门文章

  1. 保持简单----纪念丹尼斯•里奇
  2. 新编计算机基础教程考试知识点,新编计算机基础教程
  3. 包工协议书样本_个人承包协议书范本
  4. 老师给我推荐的经典管理书籍
  5. ASP.NET 入门基础--搭建网站开发环境
  6. 常用数学建模知识点及方法总结(1)(2021-8-10)
  7. 武汉大学计算机网络安全学院,消息︱武汉大学计算机学院(新)与国家网络安全学院正式组建...
  8. js 金额格式化 和 转成人民币大写金额形式
  9. [高项]项目工作说明书VS采购工作说明书
  10. 大话USB驱动之基础概念