c03xp 发表于 2020-9-3 12:57

PYTHON版贪吃蛇

本帖最后由 c03xp 于 2020-9-7 12:50 编辑

我看别人发的小游戏挺有意思,自己也跃跃欲试,写了个PYTHON版贪吃蛇。原创亮点是调用win32函数随意移动光标位置,并且隐藏光标的显示。
理论上还可以继续优化,减小抖动,那样的话代码量可能会大一点。

已更新为彩色版

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import threading, os, msvcrt, ctypes, time, sys, random, math, copy


class COORD(ctypes.Structure):
    _fields_ = [("X",ctypes.c_short),("Y",ctypes.c_short)]
       
class CURSOR_INFO(ctypes.Structure):
    _fields_ = [("dwSize",ctypes.c_uint),("bVisible",ctypes.c_uint)]
   

ww = 9          #列数
wh = 16         #行数
interval = 0.15#帧间隔
invincible = False#无敌模式:off

xboundary = 2
yboundary = 2
title = "\u3000"*int( (ww + xboundary/2 - 3)/2) + "贪吃蛇\n"
ceiling = "\u3000" + "▁"*ww + "\u3000\n"
floor = "\u3000" + "▔"*ww + "\u3000\n"
middle = ("▕"+"\u3000"*ww + "▏\n")*wh
bkground = title + ceiling + middle + floor + "\u3000"*30   #背景数据,最后是信息提示区
nexts = {b"d":1,b"a":-1,b"s":ww,b"w":-ww}#按键-蛇头位移 映射表
snake = food = nx = None
foods = []
for i in range(ww*wh):
    foods.append(i)

def writeCh(idx,ch,color=7):   
    '''写入一个字符。写屏范围是一个矩形空间,可用一维坐标idx或二维坐标x,y定位'''
    if(0 <= idx < ww*wh):
      dll.SetConsoleCursorPosition(hStdOut,COORD( (idx % ww)*2 + xboundary,int( idx / ww ) + yboundary))    #定位光标时,将矩形空间坐标转换成命令行坐标
      dll.SetConsoleTextAttribute(hStdOut,color)
      print(ch,end="")
      sys.stdout.flush()
      dll.SetConsoleTextAttribute(hStdOut,7)
def clearScreen():   
    '''清除内容并重画背景'''
    dll.SetConsoleCursorPosition(hStdOut,COORD(0,0))
    print(bkground)
def gameInit():         
    '''初始化游戏数据'''
    global snake,food,nx
    clearScreen()
    snake =
    food = list(set(foods) - set(snake))
    nx = nexts
    while(msvcrt.kbhit()):        #清空缓冲区
      msvcrt.getch()       
def collision():      
    '''碰撞检测'''
    if(snake < 0 or snake >= ww*wh):      #上下出界
      return not invincible
    if(nx == 1 and snake%ww == 0):         #撞右墙
      return not invincible
    if(nx == -1 and (snake + 1)%ww == 0):    #撞左墙
      return not invincible
    if(snake in snake):                  #撞蛇身
      return not invincible
    return False
def run():            
    '''主循环'''
    global snake,food,nx
    '''计算当前帧'''   
    if(msvcrt.kbhit()):
      while(msvcrt.kbhit()):        #获取按键输入
            ch = msvcrt.getch()
      if(ch in nexts.keys()):
            #if(nx + nexts != 0 or len(snake) < 2): #注释掉这条限制是为了无敌模式时畅行无阻
            nx = nexts#按键输入映射到蛇头位移
    snake.insert(0,snake + nx)
    if(snake == food):
      food = list(set(foods) - set(snake))
    else:
      snake.pop()      
    '''绘制当前帧'''
    if(collision()):
      dll.SetConsoleCursorPosition(hStdOut,COORD(0,wh + 3))
      print("Game Over")
      time.sleep(2)
      gameInit()
    clearScreen()   
    for el in snake:   #绘制蛇身
      writeCh(el,"\u25a0",12)
    writeCh(snake,"◆",12)   #绘制蛇头
    writeCh(food,"\u2605",10)   #绘制食物   
    threading.Timer(interval,run).start()
   
'''''''''''''''''主程序'''''''''''''''''''''
dll = ctypes.WinDLL("Kernel32.dll")
hStdOut = dll.GetStdHandle(-11)                #GetStdHandle(STD_OUTPUT_HANDLE)
dll.SetConsoleCursorPosition.argtypes =
dll.SetConsoleCursorInfo.argtypes =
ci = CURSOR_INFO(1,0)
dll.SetConsoleCursorInfo(hStdOut,id(ci))

os.system("cls")
gameInit()
run()




c03xp 发表于 2020-9-3 17:23

涛之雨 发表于 2020-9-3 17:11
就是贪吃蛇向右移动的时候按左键应该是没有效果的,
而这个会直接死亡。
应该是少了一个判断吧?


明白了,多谢提醒,可以这样改一下:

      if(ch in nexts.keys()):
            if(nx + nexts != 0 or len(snake) < 2):
                nx = nexts#按键输入映射到蛇头位移

涛之雨 发表于 2020-9-3 16:53

运行了一下,挺好的,
有个bug(算是吧)

这种不可能存在的情况也会出现
(向右走的时候按左键,其他同理)

此外可以用
os.system("mode con:cols=23 lines=22")
设置窗口大小

当然主要是前来学习算法(哈哈哈哈哈)

nmgnclxd 发表于 2020-9-3 13:15

666,已收藏,正需要这个

落雨碎江南 发表于 2020-9-3 13:16

厉害了楼主{:301_993:}

辰迷星海 发表于 2020-9-3 13:16

感谢分享

弗由 发表于 2020-9-3 13:21

这个好,感谢分享。

guide 发表于 2020-9-3 13:24

感谢分享

小木剑 发表于 2020-9-3 13:45

哈哈,很厉害

刀大喵 发表于 2020-9-3 13:48

贪吃蛇 编程学习常遇到的例子

fengling375 发表于 2020-9-3 13:51

100行,厉害

xm3834 发表于 2020-9-3 14:00

感谢你的分享支持一下
页: [1] 2 3
查看完整版本: PYTHON版贪吃蛇