Python如何实现自动玩贪吃蛇程序(python,开发技术)

时间:2024-05-04 22:25:29 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

实现效果

先看看效果

这比我手动的快多了,而且是单机的,自动玩没惹骂我,哈哈 ,多人游戏整个自动玩会被骂死~

Python如何实现自动玩贪吃蛇程序

代码

没装软件的先安装一下软件,没装模块的安装一下pygame模块。

pipinstallpygame

导入模块

importpygame,sys,time,randomfrompygame.localsimport*

定义颜色变量

redColour=pygame.Color(255,0,0)blackColour=pygame.Color(0,0,0)whiteColour=pygame.Color(255,255,255)greenColour=pygame.Color(0,255,0)headColour=pygame.Color(0,119,255)

在所有后续的除法中,为预防pygame输出出现偏差,必须取除数(//)而不是单纯除法(/)

程序界面

第0行,HEIGHT行,第0列,WIDTH列为围墙,所以实际大小是13*13

IGHT=15WIDTH=15FIELD_SIZE=HEIGHT*WIDTH#蛇头位于snake数组的第一个元素HEAD=0

用数字代表不同的对象,因为运动时矩阵上每个格子会处理成到达食物的路径长度,因此这三个变量间需要有足够大的间隔(>HEIGHT*WIDTH)来互相区分,小写一般是坐标,大写代表常量。

FOOD=0UNDEFINED=(HEIGHT+1)*(WIDTH+1)SNAKE=2*UNDEFINED

snake是一维数组,对应元素直接加上以下值就表示向四个方向移动。

LEFT=-1RIGHT=1UP=-WIDTH#一维数组,所以需要整个宽度都加上才能表示上下移动。DOWN=WIDTH

错误码

ERR=-2333

用一维数组来表示二维的东西,board表示蛇运动的矩形场地,初始化蛇头在(1,1)的地方,初始蛇长度为1。

board=[0]*FIELD_SIZE#[0,0,0,……]snake=[0]*(FIELD_SIZE+1)snake[HEAD]=1*WIDTH+1snake_size=1

与上面变量对应的临时变量,蛇试探性地移动时使用。

tmpboard=[0]*FIELD_SIZEtmpsnake=[0]*(FIELD_SIZE+1)tmpsnake[HEAD]=1*WIDTH+1tmpsnake_size=1

food:食物位置初始在(4, 7),best_move: 运动方向。

food=4*WIDTH+7best_move=ERR

运动方向数组,游戏分数(蛇长)

mov=[LEFT,RIGHT,UP,DOWN]score=1

检查一个cell有没有被蛇身覆盖,没有覆盖则为free,返回true 。

defis_cell_free(idx,psize,psnake):returnnot(idxinpsnake[:psize])

检查某个位置idx是否可向move方向运动

defis_move_possible(idx,move):flag=Falseifmove==LEFT:#因为实际范围是13*13,[1,13]*[1,13],所以idx为1时不能往左跑,此时取余为1所以>1flag=Trueifidx%WIDTH>1elseFalseelifmove==RIGHT:#这里的<WIDTH-2跟上面是一样的道理flag=Trueifidx%WIDTH<(WIDTH-2)elseFalseelifmove==UP:#这里向上的判断画图很好理解,因为在[1,13]*[1,13]的实际运动范围外,还有个#大框是围墙,就是之前说的那几个行列,下面判断向下运动的条件也是类似的flag=Trueifidx>(2*WIDTH-1)elseFalseelifmove==DOWN:flag=Trueifidx<(FIELD_SIZE-2*WIDTH)elseFalsereturnflag

重置board

board_BFS后,UNDEFINED值都变为了到达食物的路径长度。

如需要还原,则要重置它。

defboard_reset(psnake,psize,pboard):foriinrange(FIELD_SIZE):ifi==food:pboard[i]=FOODelifis_cell_free(i,psize,psnake):#该位置为空pboard[i]=UNDEFINEDelse:#该位置为蛇身pboard[i]=SNAKE

广度优先搜索遍历整个board,计算出board中每个非SNAKE元素到达食物的路径长度。

defboard_BFS(pfood,psnake,pboard):queue=[]queue.append(pfood)inqueue=[0]*FIELD_SIZEfound=False#while循环结束后,除了蛇的身体,#其它每个方格中的数字为从它到食物的曼哈顿间距whilelen(queue)!=0:idx=queue.pop(0)#初始时idx是食物的坐标ifinqueue[idx]==1:continueinqueue[idx]=1foriinrange(4):#左右上下ifis_move_possible(idx,mov[i]):ifidx+mov[i]==psnake[HEAD]:found=Trueifpboard[idx+mov[i]]<SNAKE:#如果该点不是蛇的身体ifpboard[idx+mov[i]]>pboard[idx]+1:#小于的时候不管,不然会覆盖已有的路径数据。pboard[idx+mov[i]]=pboard[idx]+1ifinqueue[idx+mov[i]]==0:queue.append(idx+mov[i])returnfound

从蛇头开始,根据board中元素值,从蛇头周围4个领域点中选择最短路径。

defchoose_shortest_safe_move(psnake,pboard):best_move=ERRmin=SNAKEforiinrange(4):ifis_move_possible(psnake[HEAD],mov[i])andpboard[psnake[HEAD]+mov[i]]<min:#这里判断最小和下面的函数判断最大,都是先赋值,再循环互相比较min=pboard[psnake[HEAD]+mov[i]]best_move=mov[i]returnbest_move

检查是否可以追着蛇尾运动,即蛇头和蛇尾间是有路径的,为的是避免蛇头陷入死路。虚拟操作,在tmpboard,tmpsnake中进行。

defis_tail_inside():globaltmpboard,tmpsnake,food,tmpsnake_sizetmpboard[tmpsnake[tmpsnake_size-1]]=0#虚拟地将蛇尾变为食物(因为是虚拟的,所以在tmpsnake,tmpboard中进行)tmpboard[food]=SNAKE#放置食物的地方,看成蛇身result=board_BFS(tmpsnake[tmpsnake_size-1],tmpsnake,tmpboard)#求得每个位置到蛇尾的路径长度foriinrange(4):#如果蛇头和蛇尾紧挨着,则返回False。即不能follow_tail,追着蛇尾运动了ifis_move_possible(tmpsnake[HEAD],mov[i])andtmpsnake[HEAD]+mov[i]==tmpsnake[tmpsnake_size-1]andtmpsnake_size>3:result=Falsereturnresult

让蛇头朝着蛇尾运行一步,不管蛇身阻挡,朝蛇尾方向运行。

deffollow_tail():globaltmpboard,tmpsnake,food,tmpsnake_sizetmpsnake_size=snake_sizetmpsnake=snake[:]board_reset(tmpsnake,tmpsnake_size,tmpboard)#重置虚拟boardtmpboard[tmpsnake[tmpsnake_size-1]]=FOOD#让蛇尾成为食物tmpboard[food]=SNAKE#让食物的地方变成蛇身board_BFS(tmpsnake[tmpsnake_size-1],tmpsnake,tmpboard)#求得各个位置到达蛇尾的路径长度tmpboard[tmpsnake[tmpsnake_size-1]]=SNAKE#还原蛇尾returnchoose_longest_safe_move(tmpsnake,tmpboard)#返回运行方向(让蛇头运动1步)

在各种方案都不行时,随便找一个可行的方向来走(1步)

defany_possible_move():globalfood,snake,snake_size,boardbest_move=ERRboard_reset(snake,snake_size,board)board_BFS(food,snake,board)min=SNAKEforiinrange(4):ifis_move_possible(snake[HEAD],mov[i])andboard[snake[HEAD]+mov[i]]<min:min=board[snake[HEAD]+mov[i]]best_move=mov[i]returnbest_move

转换数组函数

defshift_array(arr,size):foriinrange(size,0,-1):arr[i]=arr[i-1]defnew_food():#随机函数生成新的食物globalfood,snake_sizecell_free=Falsewhilenotcell_free:w=random.randint(1,WIDTH-2)h=random.randint(1,HEIGHT-2)food=WIDTH*h+wcell_free=is_cell_free(food,snake_size,snake)pygame.draw.rect(playSurface,redColour,Rect(18*(food//WIDTH),18*(food%WIDTH),18,18))

真正的蛇在这个函数中,朝pbest_move走1步。

defmake_move(pbest_move):globalsnake,board,snake_size,scoreshift_array(snake,snake_size)snake[HEAD]+=pbest_movep=snake[HEAD]forbodyinsnake:#画蛇,身体,头,尾pygame.draw.rect(playSurface,whiteColour,Rect(18*(body//WIDTH),18*(body%WIDTH),18,18))pygame.draw.rect(playSurface,greenColour,Rect(18*(snake[snake_size-1]//WIDTH),18*(snake[snake_size-1]%WIDTH),18,18))pygame.draw.rect(playSurface,headColour,Rect(18*(p//WIDTH),18*(p%WIDTH),18,18))#下面一行是把初始情况会出现的第一个白块bug填掉pygame.draw.rect(playSurface,(255,255,0),Rect(0,0,18,18))#刷新pygame显示层pygame.display.flip()#如果新加入的蛇头就是食物的位置#蛇长加1,产生新的食物,重置board(因为原来那些路径长度已经用不上了)ifsnake[HEAD]==food:board[snake[HEAD]]=SNAKE#新的蛇头snake_size+=1score+=1ifsnake_size<FIELD_SIZE:new_food()else:#如果新加入的蛇头不是食物的位置board[snake[HEAD]]=SNAKE#新的蛇头board[snake[snake_size]]=UNDEFINED#蛇尾变为UNDEFINED,黑色pygame.draw.rect(playSurface,blackColour,Rect(18*(snake[snake_size]//WIDTH),18*(snake[snake_size]%WIDTH),18,18))#刷新pygame显示层pygame.display.flip()

虚拟地运行一次,然后在调用处检查这次运行可否可行,可行才真实运行。

虚拟运行吃到食物后,得到虚拟下蛇在board的位置。

defvirtual_shortest_move():globalsnake,board,snake_size,tmpsnake,tmpboard,tmpsnake_size,foodtmpsnake_size=snake_sizetmpsnake=snake[:]#如果直接tmpsnake=snake,则两者指向同一处内存tmpboard=board[:]#board中已经是各位置到达食物的路径长度了,不用再计算board_reset(tmpsnake,tmpsnake_size,tmpboard)food_eated=Falsewhilenotfood_eated:board_BFS(food,tmpsnake,tmpboard)move=choose_shortest_safe_move(tmpsnake,tmpboard)shift_array(tmpsnake,tmpsnake_size)tmpsnake[HEAD]+=move#在蛇头前加入一个新的位置#如果新加入的蛇头的位置正好是食物的位置#则长度加1,重置board,食物那个位置变为蛇的一部分(SNAKE)iftmpsnake[HEAD]==food:tmpsnake_size+=1board_reset(tmpsnake,tmpsnake_size,tmpboard)#虚拟运行后,蛇在board的位置tmpboard[food]=SNAKEfood_eated=Trueelse:#如果蛇头不是食物的位置,则新加入的位置为蛇头,最后一个变为空格tmpboard[tmpsnake[HEAD]]=SNAKEtmpboard[tmpsnake[tmpsnake_size]]=UNDEFINED

如果蛇与食物间有路径,则调用本函数。

deffind_safe_way():globalsnake,boardsafe_move=ERR#虚拟地运行一次,因为已经确保蛇与食物间有路径,所以执行有效#运行后得到虚拟下蛇在board中的位置,即tmpboardvirtual_shortest_move()#该函数唯一调用处ifis_tail_inside():#如果虚拟运行后,蛇头蛇尾间有通路,则选最短路运行(1步)returnchoose_shortest_safe_move(snake,board)safe_move=follow_tail()#否则虚拟地follow_tail1步,如果可以做到,返回truereturnsafe_move

初始化pygame 模块

pygame.init()

定义一个变量用来控制游戏速度

fpsClock=pygame.time.Clock()

创建pygame显示层

playSurface=pygame.display.set_mode((270,270))pygame.display.set_caption('贪吃蛇')

绘制pygame显示层

playSurface.fill(blackColour)

初始化食物

pygame.draw.rect(playSurface,redColour,Rect(18*(food//WIDTH),18*(food%WIDTH),18,18))whileTrue:foreventinpygame.event.get():#循环监听键盘和退出事件ifevent.type==QUIT:#如果点了关闭print(score)#游戏结束后打印分数pygame.quit()sys.exit()elifevent.type==KEYDOWN:#如果esc键被按下ifevent.key==K_ESCAPE:print(score)#游戏结束后打印分数pygame.quit()sys.exit()#刷新pygame显示层pygame.display.flip()#画围墙,255,255,0是黄色,边框是36是因为,pygame矩形是以边为初始,向四周填充边框pygame.draw.rect(playSurface,(255,255,0),Rect(0,0,270,270),36)#重置距离board_reset(snake,snake_size,board)#如果蛇可以吃到食物,board_BFS返回true#并且board中除了蛇身(=SNAKE),其它的元素值表示从该点运动到食物的最短路径长ifboard_BFS(food,snake,board):best_move=find_safe_way()#find_safe_way的唯一调用处else:best_move=follow_tail()ifbest_move==ERR:best_move=any_possible_move()#上面一次思考,只得出一个方向,运行一步ifbest_move!=ERR:make_move(best_move)else:print(score)#游戏结束后打印分数break#控制游戏速度fpsClock.tick(20)#20看上去速度正好
 </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
本文:Python如何实现自动玩贪吃蛇程序的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:Java代理模式怎么理解下一篇:

9 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18