Python 实现 五子棋小游戏【附源码】

news2024/11/20 0:45:17

引言

五子棋是一种古老而深受欢迎的策略游戏,它具有简单的规则和无穷的变化。作为一种传统的中国棋类游戏,五子棋已经在世界范围内流行起来,并成为智力挑战和休闲娱乐的优秀选择。

在这里插入图片描述

规则和玩法:

五子棋使用一个15x15的棋盘,玩家轮流在空白位置上放置黑色或白色的棋子。目标是在水平、垂直或对角线上先形成连续的五个棋子,即五子连珠。玩家可以利用自己的棋子来阻止对方取得胜利,并同时寻找自己的胜利机会。五子棋规则简单明了,但由于棋盘庞大的可能性,游戏变化多端,需要高度的战略和思考能力。

模块化分析

init():初始化数据,包括清空棋盘上的棋子和提示信息,重置游戏状态。

drawWin():绘制游戏窗口,包括棋盘和各种文本提示信息,使其显示在屏幕上。

inBoard(x, y):判断给定的点是否在棋盘范围内。通过检查坐标x和y是否在0到15之间来确定。

downOk(x, y):判断给定点是否可以落子,即是否在棋盘内且该位置没有被占据。如果位置合法且为空,则返回True,否则返回False。

manPlay():处理玩家下棋的逻辑。首先获取玩家鼠标点击事件的坐标,如果玩家点击了重新开始或退出按钮,则执行相应的操作。如果玩家点击的位置是合法的,则落下一颗棋子,否则重新调用该函数等待合法的点击。

go(x, y):将一颗棋子放置在给定的位置,并判断游戏是否结束。该函数会检查该位置是否合法,如果合法,则在图形界面上放置一个棋子,同时更新棋盘数组和计数器。接下来,该函数会检查是否有一方玩家获胜或棋盘已满,如果是,则设置游戏结束标志,并弹出相应的对话框。

whoStart(p):处理选择先手的逻辑。根据玩家点击的位置(先手按钮或后手按钮),确定玩家和AI的先后顺序,并更新相关的变量。

AI1():博弈树的第一层。该函数用于生成所有可能的下一步落子点,并调用AI2()函数评估每个点的得分。

AI2():博弈树的第二层。该函数遍历了AI1()生成的所有可能的下一步落子点,并调用AI3()函数评估每个点的得分。

AI3(p2):博弈树的第三层。该函数通过模拟对方的下一步落子,评估当前局面的得分。它会考虑到玩家和AI的不同策略,找出对AI最有利的下一步落子点。

完整代码:

from graphics import *
import time
###
num = [[0 for a in range(16)] for a in range(16)]
dx = [1,1,0,-1,-1,-1,0,1] #x,y方向向量
dy = [0,1,1,1,0,-1,-1,-1]
is_end = False
go_first = 1 #先手标志
start = 1 #轮换下棋标志
ai = 1 #AI下棋标志
L1_max=-100000 #剪枝阈值
L2_min=100000
list=[] #保存已画棋子
RESTART_FLAG = False
QUIT_FLAG = False
###
win = GraphWin("五子棋",550,451)
aiFirst = Text(Point(500,100),"")
manFirst = Text(Point(500,140),"")
notice = Text(Point(500,290),"") #提示轮到谁落子
notice.setFill('red')
last_ai = Text(Point(500,330),"") #AI最后落子点
last_man = Text(Point(500,370),"") #玩家最后落子点
QUIT = Text(Point(500,20),"退出")
QUIT.setFill('red')
RESTART = Text(Point(500,60),"重玩")
RESTART.setFill('red')
#数据初始化,把棋盘上的棋子和提示清空
def init():
    global is_end,start,go_first,RESTART_FLAG
    is_end=False
    start=1
    go_first=1
    RESTART_FLAG=False
    QUIT_FLAG=False
    for i in range(16):
        for j in range(16):
            if(num[i][j]!=0):
                num[i][j]=0
    for i in range(len(list)):
        list[-1].undraw()
        list.pop(-1)
    aiFirst.setText("AI 先手")
    manFirst.setText("我先手")
    notice.setText("")
    last_ai.setText("")
    last_man.setText("")
#画棋盘
def drawWin():
    win.setBackground('yellow')
    for i in range(0,451,30):
        line=Line(Point(i,0),Point(i,450))
        line.draw(win)
    for j in range(0,451,30):
        line=Line(Point(0,j),Point(450,j))
        line.draw(win)
    Rectangle(Point(460,5),Point(540,35)).draw(win)
    Rectangle(Point(460,45),Point(540,75)).draw(win)
    Rectangle(Point(460,85),Point(540,115)).draw(win)
    Rectangle(Point(460,125),Point(540,155)).draw(win)
    Rectangle(Point(452,275),Point(548,305)).draw(win)
    Rectangle(Point(452,307),Point(548,395)).draw(win)
    aiFirst.draw(win)
    manFirst.draw(win)
    notice.draw(win)
    last_ai.draw(win)
    last_man.draw(win)
    QUIT.draw(win)
    RESTART.draw(win)
#判断该点是否在棋盘范围内
def inBoard(x,y):
    if(x>=0 and x<=15 and y>=0 and y<=15): return True
    else: return False
#判断该点是否可落子,即是否在棋盘内且没有落子
def downOk(x,y):
    if(inBoard(x,y) and num[x][y]==0): return True
    else: return False
#该点值是否和i值相等,即该点棋子颜色和i相同
def sameColor(x,y,i):
    if(inBoard(x,y) and num[x][y]==i): return True
    else: return False
#在给定的方向v(v区分正负)上,和该点同色棋子的个数
def numInline(x,y,v):
    i=x+dx[v]; j=y+dy[v]
    s=0; ref=num[x][y]
    if(ref==0): return 0
    while(sameColor(i,j,ref)):
        s=s+1; i=i+dx[v]; j=j+dy[v]
    return s
#该点四个方向里(即v不区分正负),活四局势的个数
def liveFour(x,y):
    key=num[x][y]; s=0
    for u in range(4):
        samekey=1
        samekey,i=numofSamekey(x,y,u,1,key,samekey)
        if(not downOk(x+dx[u]*i,y+dy[u]*i)):
            continue
        samekey,i=numofSamekey(x,y,u,-1,key,samekey)
        if(not downOk(x+dx[u]*i,y+dy[u]*i)):
            continue
        if(samekey==4):
            s=s+1
    return s
#该点八个方向里(即v区分正负),冲四局势的个数
def chongFour(x,y):
    key=num[x][y]; s=0
    for u in range(8):
        samekey=0; flag=True; i=1
        while(sameColor(x+dx[u]*i,y+dy[u]*i,key) or flag):
            if(not sameColor(x+dx[u]*i,y+dy[u]*i,key)):
                if(flag and inBoard(x+dx[u]*i,y+dy[u]*i) and num[x+dx[u]*i][y+dy[u]*i]!=0):
                    samekey-=10
                flag=False
            samekey+=1
            i+=1
        i-=1
        if(not inBoard(x+dx[u]*i,y+dy[u]*i)):
            continue
        samekey,i=numofSamekey(x,y,u,-1,key,samekey)
        if(samekey==4):
            s+=1
    return s-liveFour(x,y)*2
#该点四个方向里活三,以及八个方向里断三的个数
def liveThree(x,y):
    key=num[x][y]; s=0
    for u in range(4):
        samekey=1
        samekey,i=numofSamekey(x,y,u,1,key,samekey)
        if(not downOk(x+dx[u]*i,y+dy[u]*i)):
            continue
        if(not downOk(x+dx[u]*(i+1),y+dy[u]*(i+1))):
            continue
        samekey,i=numofSamekey(x,y,u,-1,key,samekey)
        if(not downOk(x+dx[u]*i,y+dy[u]*i)):
            continue
        if(not downOk(x+dx[u]*(i-1),y+dy[u]*(i-1))):
            continue
        if(samekey==3):
            s+=1
    for u in range(8):
        samekey=0; flag=True; i=1
        while(sameColor(x+dx[u]*i,y+dy[u]*i,key) or flag):
            if(not sameColor(x+dx[u]*i,y+dy[u]*i,key)):
                if(flag and inBoard(x+dx[u]*i,y+dy[u]*i) and num[x+dx[u]*i][y+dy[u]*i]!=0):
                    samekey-=10
                flag=False
            samekey+=1
            i+=1
        if(not downOk(x+dx[u]*i,y+dy[u]*i)):
            continue
        if(inBoard(x+dx[u]*(i-1),y+dy[u]*(i-1)) and num[x+dx[u]*(i-1)][y+dy[u]*(i-1)]==0):
            continue
        samekey,i=numofSamekey(x,y,u,1,key,samekey)
        if(not downOk(x+dx[u]*i,y+dy[u]*i)):
            continue
        if(samekey==3):
            s+=1
    return s
#该点在四个方向里,是否有六子或以上连线
def overLine(x,y):
    flag=False
    for u in range(4):
        if((numInline(x,y,u)+numInline(x,y,u+4))>4):
            flag=True
    return flag
#该黑子点是否是禁手点,黑子禁手直接判输
def ban(x,y):
    if(sameColor(x,y,3-go_first)):
        return False
    flag=((liveThree(x,y)>1) or (overLine(x,y)) or ((liveFour(x,y)+chongFour(x,y))>1))
    return flag
#统计在u方向上,和key值相同的点的个数,即和key同色的连子个数
def numofSamekey(x,y,u,i,key,sk):
    if(i==1):
        while(sameColor(x+dx[u]*i,y+dy[u]*i,key)):
            sk+=1
            i+=1
    elif(i==-1):
        while(sameColor(x+dx[u]*i,y+dy[u]*i,key)):
            sk+=1
            i-=1
    return sk,i
#游戏是否结束,如果有五子连线或出现禁手
def gameOver(x,y):
    global is_end
    for u in range(4):
        if((numInline(x,y,u)+numInline(x,y,u+4))>=4):
            is_end=True
            return True
    return False
#对该点落子后的局势进行估分
def getScore(x,y):
    global is_end
    if(ban(x,y)):
        return 0
    if(gameOver(x,y)):
        is_end=False
        return 10000
    score=liveFour(x,y)*1000+(chongFour(x,y)+liveThree(x,y))*100
    for u in range(8):
        if(inBoard(x+dx[u],y+dy[u]) and num[x+dx[u]][y+dy[u]]!=0):
            score=score+1
    return score
#博弈树第一层
def AI1():
    global L1_max
    L1_max=-100000
    if(num[8][8]==0 and go_first==ai):
        return go(8,8)
    keyi=-1; keyj=-1
    for x in [8,7,9,6,10,5,11,4,12,3,13,2,14,1,15,0]:
        for y in [8,7,9,6,10,5,11,4,12,3,13,2,14,1,15,0]:
            if(not downOk(x,y)):
                continue
            num[x][y]=ai
            tempp=getScore(x,y)
            if(tempp==0):
                num[x][y]=0; continue
            if(tempp==10000):
                return go(x,y)
            tempp=AI2()
            num[x][y]=0
            if(tempp>L1_max): #取极大
                L1_max=tempp; keyi=x; keyj=y
    go(keyi,keyj)
#博弈树第二层
def AI2():
    global L2_min
    L2_min=100000
    for x in [8,7,9,6,10,5,11,4,12,3,13,2,14,1,15,0]:
        for y in [8,7,9,6,10,5,11,4,12,3,13,2,14,1,15,0]:
            if(not downOk(x,y)):
                continue
            num[x][y]=3-ai
            tempp=getScore(x,y)
            if(tempp==0):
                num[x][y]=0; continue
            if(tempp==10000):
                num[x][y]=0; return -10000
            tempp=AI3(tempp)
            if(tempp<L1_max): #L1层剪枝
                num[x][y]=0; return -10000
            num[x][y]=0
            if(tempp<L2_min): #取极小
                L2_min=tempp
    return L2_min
#博弈树第三层
def AI3(p2):
    keyp=-100000
    for x in [8,7,9,6,10,5,11,4,12,3,13,2,14,1,15,0]:
        for y in [8,7,9,6,10,5,11,4,12,3,13,2,14,1,15,0]:
            if(not downOk(x,y)):
                continue
            num[x][y]=ai
            tempp=getScore(x,y)
            if(tempp==0):
                num[x][y]=0; continue
            if(tempp==10000):
                num[x][y]=0; return 10000
            if(tempp-p2*2>L2_min): #L2层剪枝
                num[x][y]=0; return 10000
            num[x][y]=0
            if(tempp-p2*2>keyp): #取极大
                keyp=tempp-p2*2
    return keyp
#选手下棋
def manPlay():
    p=win.getMouse()
    if(Restart(p) or Quit(p)): return
    x=round(p.getX()/30)
    y=round(p.getY()/30)
    if(downOk(x,y)): go(x,y)
    else: manPlay()
#落下一子并且判断游戏是否结束
def go(x,y):
    global is_end
    c=Circle(Point(x*30,y*30),13)
    if(start==ai):
        num[x][y]=ai
        last_ai.setText("AI 落子:\n(x:y)=("+str(x)+":"+str(y)+")")
        if(go_first==ai): c.setFill('black')
        else: c.setFill('white')
    else:
        num[x][y]=3-ai
        last_man.setText("玩家落子:\n(x:y)=("+str(x)+":"+str(y)+")")
        if(go_first==ai): c.setFill('white')
        else: c.setFill('black')
    c.draw(win)
    list.append(c)
    if(ban(x,y)):
        if(start==ai):
            notice.setText("AI 禁手,玩家赢!\n点击重玩")
        else:
            notice.setText("玩家禁手,AI 赢!\n点击重玩")
        is_end=True
    elif(gameOver(x,y)):
        if(start==ai):
            notice.setText("AI 赢!\n点击重玩")
        else:
            notice.setText("玩家赢!\n点击重玩")
##是否重新开始游戏
def Restart(p):
    global RESTART_FLAG
    x=p.getX(); y=p.getY()
    if((abs(500-x)<40) and (abs(60-y)<15)): #restart
        init()
        RESTART_FLAG=True
        notice.setText("重新开始")
        time.sleep(1)
        return True
    else:
        return False
##是否退出游戏
def Quit(p):
    global QUIT_FLAG, is_end
    x=p.getX(); y=p.getY()
    if((abs(500-x)<40) and (abs(20-y)<15)): #quit
        init()
        QUIT_FLAG=True
        is_end=True
        notice.setText("退出")
        time.sleep(1)
        return True
    else:
        return False
##选择先后手
def whoStart(p):
    global start, go_first
    x=p.getX(); y=p.getY()
    if((abs(500-x)<40) and (abs(100-y)<15)): #AI 先手
        start=1
        go_first=1
        aiFirst.setText("AI 执黑")
        manFirst.setText("玩家执白")
        return True
    elif((abs(500-x)<40) and (abs(140-y)<15)): #玩家先手
        start=2
        go_first=2
        aiFirst.setText("AI 执白")
        manFirst.setText("玩家执黑")
        return True
    else:
        return False
##------------------------------------------------------------------##
#主程序入口
if __name__=='__main__':
    init()
    drawWin()
    notice.setText("请选择先手")
    p=win.getMouse()
    while(not whoStart(p) and not Quit(p)):
        p=win.getMouse()
    while(not is_end):
        RESTART_FLAG=False
        if(start==ai):
            notice.setText("AI 正在下棋...")
            AI1()
        else:
            notice.setText("请你下棋...")
            manPlay()
        start=3-start
        if(RESTART_FLAG):
            notice.setText("请选择先手")
            p=win.getMouse()
            while(not whoStart(p) and not Quit(p)):
                p=win.getMouse()
        elif(not QUIT_FLAG and is_end):
            p=win.getMouse()
            while(not Restart(p) and not Quit(p)):
                p=win.getMouse()
            if(RESTART_FLAG):
                notice.setText("请选择先手")
                p=win.getMouse()
                while(not whoStart(p) and not Quit(p)):
                    p=win.getMouse()

总结:

五子棋作为一种古老而精彩的策略游戏,在不同的文化和地区都有着广泛的影响力。它不仅是一种休闲娱乐方式,还是一种锻炼智力和战略思维的有效方法。通过挑战五子棋,我们可以探索无限的可能性,并提高我们的思考能力和决策技巧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1442675.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HiveSQL——设计一张最近180天的注册、活跃留存表

0 问题描述 现有一个用户活跃表user_active(user_id,active_date)、 用户注册表user_regist(user_id,regist_date)&#xff0c;表中分区字段都为dt(yyyy-MM-dd)&#xff0c;用户字段均为user_id; 设计一张 1-180天的注册活跃留存表&#xff1b;表结构如下&#xff1a; 1 数据分…

【学网攻】 第(24)节 -- 帧中继(点对点)

系列文章目录 目录 系列文章目录 文章目录 前言 一、帧中继是什么&#xff1f; 二、实验 1.引入 实验拓扑图 实验配置 在帧中继中配置通信链路​编辑 实验验证 文章目录 【学网攻】 第(1)节 -- 认识网络【学网攻】 第(2)节 -- 交换机认识及使用【学网攻】 第(3)节 --…

【Django】Django中间件

Django中间件 1 中间件的定义 中间件是Django请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系统&#xff0c;用于全局改变Django的输入或输出。 中间件以类的形式体现。 每个中间件组件负责做一些特定的功能。例如&#xff0c;Django包含一个中间件组件Authen…

知到答案在哪搜? #微信#笔记#其他

学习工具是我们的得力助手&#xff0c;帮助我们更好地组织学习内容和时间。 1.试题猪 这是一个公众号 总体来说还是很不错的&#xff0c;题库虽然不是特别全&#xff0c;但是大部分网课答案能够查询到&#xff0c;最重要的是免费的 下方附上一些测试的试题及答案 1、实验室…

【java】简单的Java语言控制台程序

一、用于文本文件处理的Java语言控制台程序示例 以下是一份简单的Java语言控制台程序示例&#xff0c;用于文本文件的处理。本例中我们将会创建一个程序&#xff0c;它会读取一个文本文件&#xff0c;显示其内容&#xff0c;并且对内容进行计数&#xff0c;然后将结果输出到控…

在 Next 中, ORM 框架 Prisma 使用

Prisma 介绍 Prisma 是一个 ORM 框架&#xff0c;主要用于 Node.js 或 TypeScript 作为后端开发的应用&#xff0c;主要有三部分组成&#xff1a; Prisma Client&#xff1a;自动生成且类型安全的查询构建器&#xff0c;适用于 Nodex.js 和 TS&#xff1b;Prisma Migrate: 迁…

2024.2.5 vscode连不上虚拟机,始终waiting for server log

昨天还好好的&#xff0c;吃着火锅&#xff0c;做着毕设&#xff0c;突然就被vscode给劫了。 起初&#xff0c;哥们跟着网上教程有模有样地删除了安装包缓存&#xff0c;还删除了.vscode-server&#xff0c;发现没卵用&#xff0c;之前都是搜那个弹窗报错。 后来发现原来是vsco…

Java:Arrays类、Lambda表达式、JDK新特性(方法引用) --黑马笔记

一、Arrays类 1.1 Arrays基本使用 Arrays是操作数组的工具类&#xff0c;它可以很方便的对数组中的元素进行遍历、拷贝、排序等操作。 下面我们用代码来演示一下&#xff1a;遍历、拷贝、排序等操作。需要用到的方法如下&#xff1a; public class ArraysTest1 {public stat…

【linux开发工具】vim详解

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 “学如逆水行舟&#xff0…

Git远程仓库的使用(Gitee)及相关指令

目录 1 远程仓库的创建和配置 1.1 创建远程仓库 1.2 设置SSH公钥 2 指令 2.1 git remote add 远端名称(一般为origin) 仓库路径 2.2 git remote 2.3 git push [-f] [--set-upstream] [远端名称 [本地分支名][:远端分支名]] 2.3 git clone url 2.4 git fetch 2.5 git p…

js库和js框架你还分不清吗?一句话就讲明白了。

一、JS库 JS库&#xff08;JavaScript Library&#xff09;是一组封装了常用功能和工具的JavaScript代码集合。它们提供了一系列的函数和方法&#xff0c;使得开发者能够更便捷地进行常见的操作和处理。JS库通常是轻量级的&#xff0c;只关注某个特定的功能或问题领域。 一些常…

VUE学习之路——列表渲染

<p v-for"item in items">{{ item }}</p>使用v-for进行列表的渲染。 这仅仅是一个简单的demo&#xff0c;使用v-for可以用来遍历数组和对象&#xff0c;具体如下&#xff1a; 注意&#xff1a;遍历数组或对象的时候&#xff0c;&#xff08;&#xff09;…

国产光耦2024:发展机遇与挑战全面解析

随着科技的不断进步&#xff0c;国产光耦在2024年正面临着前所未有的机遇与挑战。本文将深入分析国产光耦行业的发展现状&#xff0c;揭示其在技术创新、市场需求等方面的机遇和挑战。 国产光耦技术创新的机遇&#xff1a; 国产光耦作为光电器件的重要组成部分&#xff0c;其技…

STM32——中断

1 什么是中断 中断&#xff1a;打断CPU执行正常的程序&#xff0c;转而处理紧急程序&#xff0c;然后返回原暂停的程序继续运行&#xff1b; 对于单片机来说&#xff0c;中断是指CPU正在处理某个事件A&#xff0c;发生了另一件事件B&#xff0c;请求CPU迅速去处理&#xff08;…

Elasticsearch 通信模块的分析

Elasticsearch 通信模块的分析 - 知乎 Elasticsearch是一个基于Lucene的分布式实时搜索框架&#xff0c;它本身能够接受用户发来的http 请求&#xff0c; 集群节点之间也会有相关的通信。 通信模块的简介 Elasticsearch 中的通信相关的配置都是由NetworkModule 这个类完成的…

机器学习系列——(二十二)结语

随着我们的机器学习系列的探索画上句号&#xff0c;我们不禁感慨于这一领域的广阔和深邃。从最初的基础概念到复杂的算法&#xff0c;从理论的探讨到实际应用的示例&#xff0c;我们一起经历了一段非凡的旅程。机器学习不仅是当前技术创新的核心驱动力之一&#xff0c;也是塑造…

leetcode:63.不同路径二

dp数组含义&#xff1a;由初始位置到最终位置路径个数 递推公式&#xff1a;如果没有障碍再进行递推公式 初始化&#xff1a;1.若起始位置和终止位置有障碍路径个数为0 2.dp[i][0] 1和dp[0][j] 1的for循环条件都需要加上一个and dp[i][0] 0和and dp[0][j] 0. 3.遍历顺序…

springboot175图书管理系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

Java:常用API接上篇 --黑马笔记

一、 StringBuilder类 StringBuilder代表可变字符串对象&#xff0c;相当于是一个容器&#xff0c;它里面的字符串是可以改变的&#xff0c;就是用来操作字符串的。 好处&#xff1a;StringBuilder比String更合适做字符串的修改操作&#xff0c;效率更高&#xff0c;代码也更…

微服务OAuth 2.1扩展额外信息到JWT并解析(Spring Security 6)

文章目录 一、简介二、重写UserDetailsService三、Controller解析JWT获取用户信息四、后记 一、简介 VersionJava17SpringCloud2023.0.0SpringBoot3.2.1Spring Authorization Server1.2.1Spring Security6.2.1mysql8.2.0 Spring Authorization Server 使用JWT时&#xff0c;前…