当前位置: 首页 > 后端技术 > Python

Python游戏:Snake&&Minesweeper

时间:2023-03-26 11:02:25 Python

1.贪吃蛇游戏介绍贪吃蛇是一款非常简单的游戏,适合练习。先来看看我的游戏截图:玩法:回车键:开始游戏空格键:暂停/继续↑↓←→方向键或WSAD键:控制移动方向。食物分为红色、绿色、蓝色三种,分别对应10分、20分、30分。每吃一种食物,就会增加相应的点数。每增加100点,速度就会提升一个等级。没有设置检查点。很快,然后GAMEOVER。2.游戏分析SnakeSnake的游戏非常简单。屏幕上随机出现一个点,表示“食物”。控制“蛇”的上下左右移动。到边界或到他自己的身体,游戏就结束了。我们先来分析一下写这个游戏需要注意哪些点。1、蛇代表什么?我们可以把整个页游区划分成小格子。一组小格子连成一条“蛇”。我们可以用不同的颜色来表示它。如上图所示,我用深色,较深的颜色代表背景,较浅的颜色代表“蛇”。我们可以用坐标来表示每个小方块,X轴和Y轴的范围是可以设置的。用一个列表来存储“蛇身”的坐标,然后一条“蛇”就出来了,最后只需要用不同的颜色显示即可。2.蛇是怎样移动的?第一反应是像蚯蚓一样让每个小方块向前移动一个空间,但是实现起来很麻烦。它从一开始就卡在这里。想象一下我们玩过的贪吃蛇。“蛇”的每一次动作,都感觉它是整体向前的。在脑海中排除“蛇”的“动作”,仔细想想“蛇”移动前后。其实除了头部和尾部,其他部分完全没有变化。这很容易,将下一个空格的坐标添加到列表的开头并删除列表的最后一个元素相当于将蛇向前移动一个空间。3.如何判断游戏结束?如果“蛇”移动到游戏区域范围之外或触及自身,则视为输。轴坐标的范围是预先确定的,如果超出范围很容易判断。那么遇到自己时如何判断呢?如果你脑海中“蛇”移动的画面真的很难,但是在代码中,我们的“蛇”是一个列表,那么我们只需要判断下一个格子的坐标是否包含在“蛇”中就可以了它在名单上吗?解决了这些问题,我们就可以开始编码了。3、代码展示由于程序需要频繁的增删“蛇”的头尾,为了更好的性能,我们使用deque代替list。首先,需要对“蛇”进行初始化。“蛇”的初始长度为3,位置位于左上角。#游戏区域坐标范围SCOPE_X=(0,SCREEN_WIDTH//SIZE-1)SCOPE_Y=(2,SCREEN_HEIGHT//SIZE-1)snake=deque()def_init_snake():snake.clear()snake.append((2,scope_y[0]))snake.append((1,scope_y[0]))snake.append((0,scope_y[0]))创建“食物”,在屏幕中随机选择一个点作为“食物”,但要确保“食??物”不在“蛇”身上。defcreate_food(snake):food_x=random.randint(SCOPE_X[0],SCOPE_X[1])food_y=random.randint(SCOPE_Y[0],SCOPE_Y[1])while(food_x,food_y)insnake:#if如果出现在一条蛇身上,重新开始food_x=random.randint(SCOPE_X[0],SCOPE_X[1])food_y=random.randint(SCOPE_Y[0],SCOPE_Y[1])returnfood_x,food_y“蛇”的运动可以有4个方向,用一个元组表示移动的方向,每按一次方向键,赋值对应的值#Directionpos=(1,0)foreventinpygame.event.get():ifevent.type==QUIT:sys.exit()elifevent.type==KEYDOWN:ifevent.keyin(K_w,K_UP):#这个判断是为了防止蛇向上移动按下下键,导致直接GAMEOVERifpOS[1]:POS=(0,-1)ElifEvent.Keyin(K_S,K_DOWN):IfPOS[1]:POS=(0,1)ELIFEVENT.KEYIN(K_a,K_left):ifpos[0]:pos=(-1,0)elifevent.keyin(K_d,K_RIGHT):ifpos[0]:pos=(1,0)而“蛇”的移动可以表示为:(next_s=snake[0][0]+pos[0],snake[0][1]+pos[1])ifnext_s==food:#eatfoodsnake.appendleft(next_s)food=create_food(snake)else:ifSCOPE_X[0]<=next_s[0]<=SCOPE_X[1]andSCOPE_Y[0]<=next_s[1]<=SCOPE_Y[1]andnext_snotinsnake:snake.appendleft(next_s)snake.pop()else:game_over=TrueMinesweeper这次我们在XP上模仿扫雷,感受下XP上的风格比看起来好多了win7上的那个。原谅我的残疾。扫雷网页游戏我基本没赢过。测试的时候我偷偷把地雷的数量从99个改成50个才算赢。..下面是我的实现逻辑。首先,如何表示地雷和非地雷。一开始想的是创建一个二维数组来表示整个区域,0表示非地雷,1表示地雷。后来觉得不对,还有标记为地雷的,标记为问号的,还有表示周围地雷数量的数字,这么多状态,搞个类就可以了classBlockStatus(Enum):normal=1#Notclickedopened=2#Alreadyclickmine=3#mineflag=4#markasmineask=5#markasquestionmarkbomb=6#steponminehint=7#arounddoubleclickdouble=8#正在被双击鼠标左右键类__Mine:(self,x,y,value=0):self._x=xself._y=yself._value=0self._around_mine_count=-1self._status=BlockStatus.normaldeself__setlfrevalue(value():returnstr(self._value)#returnf'({self._x},{self._y})={self._value},status={self.status}'defget_x(self):返回self._xdefset_x(self,x):self._x=xx=property(fget=get_x,fset=set_x)defget_y(self):返回self._ydefset_y(self,y):yyy=self.=property(fget=get_y,fset=set_y)defget_value(self):返回self._valuedefset_value(self,value):如果值:self._value=1其他:self._value=0value=property(fget=get_value,fset=set_value,doc='0:非地雷1:雷')defget_around_mine_count(self):returnself._around_mine_count):self._around_mine_count=aight_mine_count_mine_count=property(fget=get_around_mine_count,fset=set_around_mine_count,doc='四周四周数量')defget_status(self)status=property(fget=get_status,fset=set_status,doc='BlockStatus')我的很简单,随机选99个数字,从上到下依次排列classMineBlock:def__init__(self):self._block=[[Mine(i,j)foriinrange(BLOCK_WIDTH)]forjinrange(BLOCK_HEIGHT)]#mineforiinrandom.sample(range(BLOCK_WIDTH*BLOCK_HEIGHT),MINE_COUNT):锁定i[selfb_COUNT]//BLOCK_WIDTH][i%BLOCK_WIDTH].value=1当我们点击一??个网格时,我们只需要找到co根据点击的坐标对应地雷,看它的值就知道我们有没有踩到地雷了。如果没有踩到地雷,则需要计算周围8个位置有多少个地雷,从而显示相应的数字。如果周围有雷,就显示数字,这个简单,但是如果周围没有地雷,就显示一个区域,直到有雷,如下图,我只点了中间,就出现了这样的大面积图片来源:页游www.laoshoucun.com页游其实很容易计算,用递归就可以了。如果计算出周围的地雷数量为0,则递归计算周围8个位置的地雷数量,直到地雷数量不为0。classMineBlock:  defopen_mine(self,x,y):#踩到我的ifself._block[y][x].value:self._block[y][x].status=BlockStatus.bombreturnFalse#先把状态改成openedself._block[y][x].status=BlockStatus.openedaround=_get_around(x,y)_sum=0_sum=0fori,jinaround:jlfse[x][if].value:_sum+=1self._block[y][x].around_mine_count=_sum#如果周围没有地雷,则递归统计周围8个未点击和未打开的#这样可以实现大面积打开的效果if_sum==0:ForI,JinAround:ifself._block[j][i].Round_mine_count==-1:Self.open_mine(i,J)ReturnTrueDef_get_around(x,y):"""return(x,y)周围点的坐标"""#这里注意range的末端是一个开区间,所以加1return[(i,j)foriinrange(max(0,x-1),min(BLOCK_WIDTH-1,x+1)+1)forjinrange(max(0,y-1),min(BLOCK_HEIGHT-1,y+1)+1)ifi!=xorj!=y]接下来,还有一个比较麻烦的地方。我们经常同时按下鼠标的左右键。如果全部标记,它会立即打开所有周围的格子。如果有错误标记,那么抱歉,GAMEOVER如果没有完全标记,会有一个效果显示一圈没有打开的格子和标记类MineBlock:  defdouble_mouse_button_down(self,x,y):ifself._block[y][x].around_mine_count==0:returnTrueself._block[y][x].status=BlockStatus.doublearound=_get_around(x,y)sumflag=0#xy周围标记的地雷数量fori,_jarin()ifself._block[j][i].status==BlockStatus.flag:sumflag+=1#周围所有地雷都被标记result=Trueifsumflag==self._block[y][x].foraround_mine_count:I,jinartnd:ifself._block[j][i].status==blockstatus.normal:ifnotself.open_mine(i,j):result=falseelse:forI,jinartnd:ifseld._block[j][i].status==BlockStatus.normal:self._block[j][i].status=BlockStatus.hint返回结果defdouble_mouse_button_up(self,x,y):lockx_blocky[self].].status=BlockStatus.openedfori,jin_get_around(x,y):如果self._block[j][i].status==BlockStatus.hint:self._block[j][i].status=BlockStatus。正常扫雷的主要逻辑就这么多,剩下的就是一些杂项事件