5364浏览
查看: 5364|回复: 2

[资源] 基于python的五指棋游戏制作

[复制链接]
本帖最后由 DFSH_Cranberry 于 2020-11-2 18:43 编辑

今天有点手痒,想自己撸几把游戏,于是去网上找了一个五子棋的示例,便和朋友一起fight了。

如果没有安装树pygame的库,请执行这个代码
[mw_shl_code=python,false]pip install pygame[/mw_shl_code]
如果是使用的树莓派,系统里面可能会包含这个库文件,所以如果这一步没有执行成功,也可以尝试直接运行这个里面的代码。

整个代码:
[mw_shl_code=python,false]# -*- coding=utf-8 -*-
import random
import pygame
from pygame.locals import MOUSEBUTTONUP
pygame.init()

space = 60 # 四周留下的边距
cell_size = 40 # 每个格子大小
cell_num = 15
grid_size = cell_size * (cell_num - 1) + space * 2 # 棋盘的大小
screencaption = pygame.display.set_caption('FIR')
screen = pygame.display.set_mode((grid_size,grid_size)) #设置窗口长宽

chess_arr = []
flag = 1 # 1黑 2白
game_state = 1 # 游戏状态1.表示正常进行 2.表示黑胜 3.表示白胜

def get_one_dire_num(lx, ly, dx, dy, m):
    tx = lx
    ty = ly
    s = 0
    while True:
        tx += dx
        ty += dy
        if tx < 0 or tx >= cell_num or ty < 0 or ty >= cell_num or m[ty][tx] == 0: return s
        s+=1

def check_win(chess_arr, flag):
    m = [[0]*cell_num for i in range(cell_num)] # 先定义一个15*15的全0的数组,不能用[[0]*cell_num]*cell_num的方式去定义因为一位数组会被重复引用
    for x, y, c in chess_arr:
        if c == flag:
            m[y][x] = 1 # 上面有棋则标1
    lx = chess_arr[-1][0] # 最后一个子的x
    ly = chess_arr[-1][1] # 最后一个子的y
    dire_arr = [[(-1,0),(1,0)],[(0,-1),(0,1)],[(-1,-1),(1,1)],[(-1,1),(1,-1)]] # 4个方向数组,往左+往右、往上+往下、往左上+往右下、往左下+往右上,4组判断方向
   
    for dire1,dire2 in dire_arr:
        dx, dy = dire1
        num1 = get_one_dire_num(lx, ly, dx, dy, m)
        dx, dy = dire2
        num2 = get_one_dire_num(lx, ly, dx, dy, m)
        if num1 + num2 + 1 >= 5: return True

    return False

while True:
    for event in pygame.event.get():
         if event.type == pygame.QUIT:
             pygame.quit()
             exit()
   
         if game_state == 1 and event.type == pygame.MOUSEBUTTONUP: # 鼠标弹起
             x, y = pygame.mouse.get_pos() # 获取鼠标位置
             xi = int(round((x - space)*1.0/cell_size)) # 获取到x方向上取整的序号
             yi = int(round((y - space)*1.0/cell_size)) # 获取到y方向上取整的序号
             if xi>=0 and xi<cell_num and yi>=0 and yi<cell_num and (xi,yi,1) not in chess_arr and (xi,yi,2) not in chess_arr:
                 chess_arr.append((xi,yi,flag))
                 if check_win(chess_arr, flag):
                     game_state = 2 if flag == 1 else 3
                 else:
                     flag = 2 if flag == 1 else 1

    screen.fill((0,0,150)) # 将界面设置为蓝色

    for x in range(0,cell_size*cell_num,cell_size):
        pygame.draw.line(screen,(200,200,200),(x+space,0+space),(x+space,cell_size*(cell_num-1)+space),1)
    for y in range(0,cell_size*cell_num,cell_size):
        pygame.draw.line(screen,(200,200,200),(0+space,y+space),(cell_size*(cell_num-1)+space,y+space),1)

    for x, y, c in chess_arr:
        chess_color = (30,30,30) if c == 1 else (225,225,225)
        pygame.draw.circle(screen, chess_color, [x*cell_size+space, y*cell_size+space], 16,16)

    if game_state != 1:
        myfont = pygame.font.Font(None,60)
        white = 210,210,0
        win_text = "%s win"%('black' if game_state == 2 else 'white')
        textImage = myfont.render(win_text, True, white)
        screen.blit(textImage, (260,320))
   
    pygame.display.update() # 必须调用update才能看到绘图显示[/mw_shl_code]

下面我们就来读读代码吧

1.设置棋盘

五子棋标准棋盘是15x15的,如果我们每个格子的大小是40x40的话,棋盘应该是40x(15-1)=560的宽度,我们在四面各保留60的边距,那么窗口的长宽各是40x(15-1)+60x2

[mw_shl_code=python,false]# -*- coding=utf-8 -*-
import random
import pygame
pygame.init()

space = 60 # 四周留下的边距
cell_size = 40 # 每个格子大小
cell_num = 15
grid_size = cell_size * (cell_num - 1) + space * 2 # 棋盘的大小
screencaption = pygame.display.set_caption('FIR')
screen = pygame.display.set_mode((grid_size,grid_size)) #设置窗口长宽

while True:
    for event in pygame.event.get():
         if event.type == pygame.QUIT:
             pygame.quit()
             exit()

    screen.fill((0,0,150)) # 将界面设置为蓝色

    for x in range(0,cell_size*cell_num,cell_size):
        pygame.draw.line(screen,(200,200,200),(x+space,0+space),(x+space,cell_size*(cell_num-1)+space),1)
    for y in range(0,cell_size*cell_num,cell_size):
        pygame.draw.line(screen,(200,200,200),(0+space,y+space),(cell_size*(cell_num-1)+space,y+space),1)
   
    pygame.display.update() # 必须调用update才能看到绘图显示[/mw_shl_code]

基于python的五指棋游戏制作图1

2.落子

首先我们定义一个chess_arr数组用于存储落到棋盘上的棋子

[mw_shl_code=python,false]chess_arr = [][/mw_shl_code]

然后在游戏主循环监听下鼠标弹起事件,然后在捕捉到鼠标弹起事件时获取鼠标位置并把位置添加进chess_arr[mw_shl_code=python,false]for event in pygame.event.get():
        ……
   
         if event.type == pygame.MOUSEBUTTONUP: # 鼠标弹起
             x, y = pygame.mouse.get_pos() # 获取鼠标位置
             chess_arr.append((x,y))[/mw_shl_code]
最后我们在pygame.display.update()前将棋子绘制出来看看效果

基于python的五指棋游戏制作图2

可以看到,现在已经能点出棋子了,但是棋子的位置不是纵横线的交叉点,所以我们必须对鼠标位置进行取整,不能把x,y这个位置加的这么随意,处理下x,y位置的代码如下


[mw_shl_code=python,false]for event in pygame.event.get():
        ……
         if event.type == pygame.MOUSEBUTTONUP: # 鼠标弹起
             x, y = pygame.mouse.get_pos() # 获取鼠标位置
             xi = int(round((x - space)*1.0/cell_size)) # 获取到x方向上取整的序号
             yi = int(round((y - space)*1.0/cell_size)) # 获取到y方向上取整的序号
             if xi>=0 and xi<cell_num and yi>=0 and yi<cell_num:
                 chess_arr.append((xi*cell_size+space,yi*cell_size+space))[/mw_shl_code]
现在发现落子位置靠谱多了
基于python的五指棋游戏制作图3


为了代码的可读性更好点,以后一些棋盘计算更方便,我们把放入chess_arr数组的绝对坐标改成放入的是格子的序号,也就是把
[mw_shl_code=python,false]chess_arr.append((xi*cell_size+space,yi*cell_size+space))[/mw_shl_code]
改成
[mw_shl_code=python,false]chess_arr.append((xi,yi))[/mw_shl_code]
然后在画棋子的地方也稍作修改,把[mw_shl_code=python,false]pygame.draw.circle(screen,(205,205,205), [x, y], 16,16)[/mw_shl_code]
改成
[mw_shl_code=python,false]pygame.draw.circle(screen,(205,205,205), [x*cell_size+space, y*cell_size+space], 16,16)[/mw_shl_code]
接下来还有个问题,因为进到chess_arr数组前并没有判断某一位置是否已经有棋子,存在重复落子的情况,所以这边还要多加个判断,因为python语言够强大,可以直接判断是否包含tuple或者数组,所以只要多加一个(xi,yi) not in chess_arr的判断就好了,开不开森~
[mw_shl_code=python,false]    for event in pygame.event.get():
         ……
         if event.type == pygame.MOUSEBUTTONUP: # 鼠标弹起
             ……
             if xi>=0 and xi<cell_num and yi>=0 and yi<cell_num and (xi,yi) not in chess_arr:
                     chess_arr.append((xi,yi))[/mw_shl_code]
为免代码偏差,先更新下目前的完整代码
[mw_shl_code=python,false]# -*- coding=utf-8 -*-
import random
import pygame
from pygame.locals import MOUSEBUTTONUP
pygame.init()

space = 60 # 四周留下的边距
cell_size = 40 # 每个格子大小
cell_num = 15
grid_size = cell_size * (cell_num - 1) + space * 2 # 棋盘的大小
screencaption = pygame.display.set_caption('FIR')
screen = pygame.display.set_mode((grid_size,grid_size)) #设置窗口长宽

chess_arr = []

while True:
    for event in pygame.event.get():
         if event.type == pygame.QUIT:
             pygame.quit()
             exit()
   
         if event.type == pygame.MOUSEBUTTONUP: # 鼠标弹起
             x, y = pygame.mouse.get_pos() # 获取鼠标位置
             xi = int(round((x - space)*1.0/cell_size)) # 获取到x方向上取整的序号
             yi = int(round((y - space)*1.0/cell_size)) # 获取到y方向上取整的序号
             if xi>=0 and xi<cell_num and yi>=0 and yi<cell_num and (xi,yi) not in chess_arr:
                 chess_arr.append((xi,yi))

    screen.fill((0,0,150)) # 将界面设置为蓝色

    for x in range(0,cell_size*cell_num,cell_size):
        pygame.draw.line(screen,(200,200,200),(x+space,0+space),(x+space,cell_size*(cell_num-1)+space),1)
    for y in range(0,cell_size*cell_num,cell_size):
        pygame.draw.line(screen,(200,200,200),(0+space,y+space),(cell_size*(cell_num-1)+space,y+space),1)

    for x, y in chess_arr:
        pygame.draw.circle(screen,(205,205,205), [x*cell_size+space, y*cell_size+space], 16,16)

    pygame.display.update() # 必须调用update才能看到绘图显示[/mw_shl_code]
3.区分黑白子

这里常规的想法可能有这么两种:1.chess_arr理论应该是黑白相间的,一个隔一个不同颜色画就好了(这种在不考虑正规比赛五手两打或者让子的情况下是没问题的) 2.往chess_arr里填(x,y)时多填一个黑白标记改成(x,y,flag) ,这里我们选择第二种方案
首先,我们全局定义个flag变量

[mw_shl_code=python,false]flag = 1 # 1黑 2白[/mw_shl_code]


我们把

[mw_shl_code=python,false]if xi>=0 and xi<cell_num and yi>=0 and yi<cell_num and (xi,yi) not in chess_arr:
                 chess_arr.append((xi,yi))[/mw_shl_code]
这里改成
[mw_shl_code=python,false]if xi>=0 and xi<cell_num and yi>=0 and yi<cell_num and (xi,yi,1) not in chess_arr and (xi,yi,2) not in chess_arr:
                 chess_arr.append((xi,yi,flag))
                 flag = 2 if flag == 1 else 2[/mw_shl_code]
再把画棋的
[mw_shl_code=python,false]for x, y in chess_arr:
        pygame.draw.circle(screen,(205,205,205), [x*cell_size+space, y*cell_size+space], 16,16)[/mw_shl_code]
改成
[mw_shl_code=python,false]for x, y, c in chess_arr:
        chess_color = (30,30,30) if c == 1 else (225,225,225)
        pygame.draw.circle(screen, chess_color, [x*cell_size+space, y*cell_size+space], 16,16)[/mw_shl_code]
基于python的五指棋游戏制作图4

现在看起来有点像那么回事了

4.判断输赢

判断输赢的关键当然还是使用chess_arr这个数组,这个数组用来判断胜利并不太方便,我们把它转一个15*15的二维数组来计算,转换代码如下


[mw_shl_code=python,false]m = [[0]*15 for i in range(15)] # 先定义一个15*15的全0数组
for x, y, c in chess_arr:
    m[y][x] = 1 # 上面有棋则标1[/mw_shl_code]
我们把这代码一起放到一个check_win(chess_arr, flag)函数里,用于判断某一方是否胜利,基本流程是分别判断最后一颗落下的子的横线、竖线、斜线上是不是有5个以上子,有则返回True,函数代码如下:
[mw_shl_code=python,false]def get_one_dire_num(lx, ly, dx, dy, m):
    tx = lx
    ty = ly
    s = 0
    while True:
        tx += dx
        ty += dy
        if tx < 0 or tx >= cell_num or ty < 0 or ty >= cell_num or m[ty][tx] == 0: return s
        s+=1

def check_win(chess_arr, flag):
    m = [[0]*cell_num for i in range(cell_num)] # 先定义一个15*15的全0的数组,不能用[[0]*cell_num]*cell_num的方式去定义因为一位数组会被重复引用
    for x, y, c in chess_arr:
        if c == flag:
            m[y][x] = 1 # 上面有棋则标1
    lx = chess_arr[-1][0] # 最后一个子的x
    ly = chess_arr[-1][1] # 最后一个子的y
    dire_arr = [[(-1,0),(1,0)],[(0,-1),(0,1)],[(-1,-1),(1,1)],[(-1,1),(1,-1)]] # 4个方向数组,往左+往右、往上+往下、往左上+往右下、往左下+往右上,4组判断方向
   
    for dire1,dire2 in dire_arr:
        dx, dy = dire1
        num1 = get_one_dire_num(lx, ly, dx, dy, m)
        dx, dy = dire2
        num2 = get_one_dire_num(lx, ly, dx, dy, m)
        if num1 + num2 + 1 >= 5: return True

    return False[/mw_shl_code]
判断函数完成了,我们再定一个全局变量用于保存游戏状态
[mw_shl_code=python,false]game_state = 1 # 游戏状态1.表示正常进行 2.表示黑胜 3.表示白胜[/mw_shl_code]
我们在鼠标添加棋子的代码后面做下修改,调用判断胜利的函数
[mw_shl_code=python,false]                 if check_win(chess_arr, flag):
                     game_state = 2 if flag == 1 else 3
                 else:
                     flag = 2 if flag == 1 else 1[/mw_shl_code]
最后在pygame.display.update()前加个游戏状态判断,用于显示获胜文字
[mw_shl_code=python,false]    if game_state != 1:
        myfont = pygame.font.Font(None,60)
        white = 210,210,0
        win_text = "%s win"%('black' if game_state == 2 else 'white')
        textImage = myfont.render(win_text, True, white)
        screen.blit(textImage, (260,320))[/mw_shl_code]
另外,鼠标事件判断处也要做下修改,判断下游戏状态是不是游戏中
[mw_shl_code=python,false]if game_state == 1 and event.type == pygame.MOUSEBUTTONUP: # 鼠标弹起[/mw_shl_code]
至此主要的代码都完整了,下面是效果图
基于python的五指棋游戏制作图5
运行视频

基于python的五指棋游戏制作图6



simpleDemo.zip

1.57 KB, 下载次数: 3566

pATAq  版主

发表于 2020-11-2 00:07:58

最近 MagPi 发布了新的小册子,好像就讲的图形编程与游戏
回复

使用道具 举报

DFSH_Cranberry  中级技师
 楼主|

发表于 2020-11-2 18:21:36

pATAq 发表于 2020-11-2 00:07
最近 MagPi 发布了新的小册子,好像就讲的图形编程与游戏

我发现我自己学python也是东一点西一点的
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail