前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python 五子棋 编程

Python 五子棋 编程

作者头像
用户6021899
发布2019-08-14 17:19:44
2K0
发布2019-08-14 17:19:44
举报
文章被收录于专栏:Python编程 pyqt matplotlib

N年前用python写的五子棋游戏,电脑执白子,只能判断一步,新手级别(可以再添加几层循环让它厉害一点) 。程序的UI界面是用python的标准库 Tkinter 写的,有点乱,现在已然看不懂。

代码如下,可以在py2.7 下运行:

代码语言:javascript
复制
#-*-coding:utf-8-*-
from tkinter import *
import tkMessageBox
#以下为AI部分:
N=17 #棋盘规格 N*N
class Chessboard:
    def __init__(self,n=N):
        self.N = n
        self.count=0   #棋盘上双方棋子总数
        self.info={}                #棋子信息
        for i in range(-(n//2),n//2+1):
            for j in range(-(n//2),n//2+1):
                self.info.setdefault((i,j))         #info的键的格式为(x,y)。(0,0)处为天元
       
    def add_chess(self, x,y,camp): #x,y为棋子的逻辑坐标,camp(阵营)取值为1或-1。
        self.info[(x,y)]=camp
        self.count+=1
       
    def cancel_add(self):      #退回前一状态。
        if self.count>0:
            self.count -=1
            self.info[self.savedfile[1][-1]]=None
           
    def restart(self):#?
        self.__init__(N)
        
chessboard=Chessboard()#N=19 #棋盘规格 N*N
#
def dead(L):    #是否为“死N”,是返回1。!!!别弄反了!!!
    if len(L)<5:return 1
    return 0
def  five(L):  #是否为成五,是返回1,否返回0
    c=0
    for item in L:
        if item==0:c=0
        else :c+=1
        if c==5:return 1
    return 0
def four(L):   #是否冲四,双四,分别返回1,2,无则0
    c=0
    index=[]
    for i in range(len(L)):
        if L[i]==0:
            index.append(i)    #index记录L中项为0处的索引
    for i in range(len(index)):
        if i==0:
            L[index[0]]=1
            if five(L[:index[0]+5])==1:
                c=1
        else:
            L[index[i-1]]=0
            L[index[i]]=1
            if five(L[index[i-1]+1:index[i]+5])==1:#稍微提高效率,且不会重复,不会遗漏
                c+=1
        if c==2:
            L[index[i]]=0
            return 2
    if c==1:
        L[index[-1]]=0
        return 1
    L[index[-1]]=0# somewhere wrong?
    return 0
def three(L):  #是否双活三、活三、眠三,分别返回3,2,1,无则0
    c1=c2=0    #c1为活三个数,c2为眠三数(上限1),无则0
    index=[]
    for i in range(len(L)):
        if L[i]==0:
            index.append(i)    #index记录L中项为0处的索引
    for i in range(len(index)):
        if i==0:
            L[index[0]]=1
            temp=four(L[:index[0]+5])#提升效率
            if temp==2:
                c1=1
            elif temp==1:
                c2=1
        else:
            L[index[i-1]]=0
            L[index[i]]=1
            temp=four(L[(index[i]-4 if index[i]>=4 else 0):index[i]+5]) #应用条件表达式。避免遗漏
            if temp==2:
                c1+=1
                if c1==2:
                    L[index[i]]=0
                    return 3 #双活三
            elif temp==1:
                c2=1
    if c1==1:
        L[index[-1]]=0
        return 2    #活三
    if c2==1:
        L[index[-1]]=0
        return 1 #眠三
    L[index[-1]]=0
    return 0
def two(L): #双活二返回3,活二返回2,眠二返回1, 无则记为0
    index=[]
    for i in range(len(L)):
        if L[i]==1:
            index.append(i) #列表index记录L中值为1的项的索引,len(index) 为L中1的个数
        if len(index)==3 and index[2]-index[0] <5 or len(index)>3:
            return 0                #无活二或眠二
    if len(index)==1:return 0 #无
    if len(L)==5:return 1 #眠二
    if len(index)==2:
        if index[0]==0 or index[1]==len(L)-1 :return 1 #眠二(10001形暗含在其中)
        else:   return 2 #活二
    if len(index)==3 and len(L)>=8: #and 后面的条件取消也可。不知道效率哪个高
        double_livetwo=([0,1,0,0,1,0,0,1,0],[0,1,0,0,1,0,1,0,0],[0,0,1,0,1,0,0,1,0],\
                        [0,0,1,0,0,1,0,1,0],[0,1,0,1,0,0,1,0,0]) #单线双活二
        if L in double_livetwo:
            return 3            #双活二
    if len(index)==3:
        if index[0]==0 and index[2]==len(L)-1:
            return 1 # 两个组合皆为眠二,返回眠二
        else:   return 2                       #否则,返回活二
def one(L): #活一返回2,眠一返回1,无返回0。
    lenth=len(L)
    c=0                             #c 记录1的个数,c至少会为1(评估点)。
    for i in range(lenth):
        if L[i]==1:
            c+=1
            index=i                 #index记录1的索引         
        if c==2:return 0
    if lenth==5 or index in (0,lenth-1):return 1 #眠一
    else:   return 2                                         #活一
#
V_five=9999000000          # 成五
V_db_four=333000000     #活四或双冲四
V_s_four=62500000         #冲四
V_dbl_three=2500000       #双活三
V_live_three=1250000       #活三
V_sleep_three=125000      #眠三
V_dbl_two=5000               #双活二
V_live_two=2500              #活二
V_sleep_two=250             #眠二
V_live_one=10                   #活一
V_sleep_one=1                  #眠一
V_dead=0                         #死N
#列表评估函数,计算出数值结果
def L_eval(L):
    if dead(L)==1:  return 0
    if five(L)==1:  return V_five
    result=four(L)
    if result==2:   return V_db_four
    elif result==1:   return V_s_four
    result=three(L)
    if result==3:   return V_dbl_three
    elif result==2: return V_live_three
    elif result==1: return V_sleep_three
    result=two(L)
    if result==3:   return V_dbl_two
    elif result==2: return V_live_two
    elif result==1: return V_sleep_two
    result=one(L)
    if result==2:   return V_live_one
    elif result==1:  return V_sleep_one
#
#
def Line_eval(camp_1,camp_2,L1,L2,camp): #deal with two list on a line
    if camp_1==camp:
        if camp_2 ==camp or camp_2=="not found":
            L1.pop()
            return L_eval(L1+L2)
        else: return L_eval(L1)+L_eval(L2)/5.0
    elif camp_1=="not found":
        if camp_2==camp:
            L1.pop()
            return L_eval(L1+L2)
        elif camp_2 == "not found":
            L1.pop()
            return 1.2 * L_eval(L1+L2) #L_eval(L1+L2)+L_eval(L1+L2)/5.0
        else:
            L1.pop()
            return L_eval(L1+L2) / 5.0
    else:
        if camp_2==camp:
            return L_eval(L1) / 5.0+L_eval(L2)
        else :
            L1.pop()
            return L_eval(L1+L2)/5.0
   
def evaluate(x,y,camp,info):#评估camp方在(x,y)处落子的价值
    #L1为一边评估列表,L2为另一边边评估列表。
    # 0度水平阳线上:
    L1,L2,camp_1,camp_2=[1],[1],"not found","not found"
    for i in range(x-1,x-5,-1):
        if i<-(N//2):break #不能越出棋盘边界
        if info[(i,y)]==None:L1.insert(0,0)
        else:
            index=i  #index 记录向一端搜索出的第一个非空闲点的坐标分量
            camp_1=info[(index,y)] #向一端搜索出的第一个非空闲点的阵营
            L1.insert(0,1)
            break
    if camp_1!="not found":
        for i in range(index-1,x-5,-1):
            if i<-(N//2):break #不能越出棋盘边界
            if info[(i,y)]==camp_1: L1.insert(0,1)
            elif info[(i,y)]==None:  L1.insert(0,0)
            else:   break                   #此端方向上的搜索完毕。
    for i in range(x+1,x+5):
        if i>(N//2):break #不能越出棋盘边界
        if info[(i,y)]==None:L2.append(0)
        else:
            index=i  #index记录向另一端搜索出的第一个非空闲点的坐标分量
            camp_2=info[(index,y)] #向另一端搜索出的第一个非空闲点的阵营
            L2.append(1)
            break
    if camp_2!="not found":
        for i in range(index+1,x+5):
            if i>(N//2):break #不能越出棋盘边界
            if info[(i,y)]==camp_2: L2.append(1)
            elif info[(i,y)]==None:  L2.append(0)
            else:   break                   #此线上的搜索完毕。
    V0=Line_eval(camp_1,camp_2,L1,L2,camp)
    if V0>=V_five:  return V_five
    #90度阳线
    L1,L2,camp_1,camp_2=[1],[1],"not found","not found"
    for i in range(y-1,y-5,-1):
        if i<-(N//2):break #不能越出棋盘边界
        if info[(x,i)]==None:L1.insert(0,0)
        else:
            index=i  #index 记录向一端搜索出的第一个非空闲点的坐标分量
            camp_1=info[(x,index)] #向一端搜索出的第一个非空闲点的阵营
            L1.insert(0,1)
            break
    if camp_1!="not found":
        for i in range(index-1,y-5,-1):
            if i<-(N//2):break #不能越出棋盘边界
            if info[(x,i)]==camp_1: L1.insert(0,1)
            elif info[(x,i)]==None:  L1.insert(0,0)
            else:   break                   #此端方向上的搜索完毕。
    for i in range(y+1,y+5):
        if i>(N//2):break #不能越出棋盘边界
        if info[(x,i)]==None:L2.append(0)
        else:
            index=i  #index记录向另一端搜索出的第一个非空闲点的坐标分量
            camp_2=info[(x,index)] #向另一端搜索出的第一个非空闲点的阵营
            L2.append(1)
            break
    if camp_2!="not found":
        for i in range(index+1,y+5):
            if i>(N//2):break #不能越出棋盘边界
            if info[(x,i)]==camp_2: L2.append(1)
            elif info[(x,i)]==None:  L2.append(0)
            else:   break                   #此线上的搜索完毕。
    #print (L1,L2)
    V90=Line_eval(camp_1,camp_2,L1,L2,camp)
    if V90>=V_five:  return V_five
    #45度阴线
    L1,L2,camp_1,camp_2=[1],[1],"not found","not found"
    for (i,j) in zip(range(x-1,x-5,-1),range(y-1,y-5,-1)):
        if i<-(N//2) or j<-(N//2):break #不能越出棋盘边界
        if info[(i,j)]==None:L1.insert(0,0)
        else:
            index=(i,j) #index 记录向一端搜索出的第一个非空闲点的坐标分量
            camp_1=info[index] #向一端搜索出的第一个非空闲点的阵营
            L1.insert(0,1)
            break
    if camp_1!="not found":
        for (i,j) in zip(range(index[0]-1,x-5,-1),range(index[1]-1,y-5,-1)):
            if i<-(N//2) or j<-(N//2):break #不能越出棋盘边界
            if info[(i,j)]==camp_1: L1.insert(0,1)
            elif info[(i,j)]==None:  L1.insert(0,0)
            else:   break                   #此端方向上的搜索完毕。
    for (i,j) in zip(range(x+1,x+5,1),range(y+1,y+5,1)):
        if i>(N//2) or j>(N//2):break #不能越出棋盘边界
        if info[(i,j)]==None:L2.append(0)
        else:
            index=(i,j) #index记录向另一端搜索出的第一个非空闲点的坐标分量
            camp_2=info[index] #向另一端搜索出的第一个非空闲点的阵营
            L2.append(1)
            break
    if camp_2!="not found":
        for (i,j) in zip(range(index[0]+1,x+5,1),range(index[1]+1,y+5,1)):
            if i>(N//2) or j>(N//2):break #不能越出棋盘边界
            if info[(i,j)]==camp_2: L2.append(1)
            elif info[(i,j)]==None:  L2.append(0)
            else:   break                   #此线上的搜索完毕。
    #print (L1,L2)
    V45=Line_eval(camp_1,camp_2,L1,L2,camp)
    if V45>=V_five:  return V_five
    #135度阴线
    L1,L2,camp_1,camp_2=[1],[1],"not found","not found"
    for (i,j) in zip(range(x-1,x-5,-1),range(y+1,y+5,1)):
        if i<-(N//2) or j>(N//2):break #不能越出棋盘边界
        if info[(i,j)]==None:L1.insert(0,0)
        else:
            index=(i,j) #index 记录向一端搜索出的第一个非空闲点的坐标分量
            camp_1=info[index] #向一端搜索出的第一个非空闲点的阵营
            L1.insert(0,1)
            break
    if camp_1!="not found":
        for (i,j) in zip(range(index[0]-1,x-5,-1),range(index[1]+1,y+5,1)):
            if i<-(N//2) or j>(N//2):break #不能越出棋盘边界
            if info[(i,j)]==camp_1: L1.insert(0,1)
            elif info[(i,j)]==None:  L1.insert(0,0)
            else:   break                   #此端方向上的搜索完毕。
    for (i,j) in zip(range(x+1,x+5,1),range(y-1,y-5,-1)):
        if i>(N//2) or j<-(N//2):break #不能越出棋盘边界
        if info[(i,j)]==None:L2.append(0)
        else:
            index=(i,j) #index记录向另一端搜索出的第一个非空闲点的坐标分量
            camp_2=info[index] #向另一端搜索出的第一个非空闲点的阵营
            L2.append(1)
            break
    if camp_2!="not found":
        for (i,j) in zip(range(index[0]+1,x+5,1),range(index[1]-1,y-5,-1)):
            if i>(N//2) or j<-(N//2):break #不能越出棋盘边界
            if info[(i,j)]==camp_2: L2.append(1)
            elif info[(i,j)]==None:  L2.append(0)
            else:   break                   #此线上的搜索完毕。
    #print (L1,L2)
    V135=Line_eval(camp_1,camp_2,L1,L2,camp)
    if V135>=V_five:  return V_five
    #四条线上评估值耦合,因棋形评估值的特殊性,这里简单的取和
    V=V0+V90+V45+V135
    return V

#一下是UI部分(初稿):
class CV(Canvas):
   
    def __init__(self, n, master=Tk(), cnf={}, **kw):
        Canvas.__init__(self,master,cnf,**kw)
        self.size={"N":n,"dx":48,"dy":48,"line_width":2,"padx":24,"pady":24}
        self.master_configure()
        self.configure(bg="#B84",width=(self.size["N"]-1)*self.size["dx"]+2*self.size["padx"],\
                       height=(self.size["N"]-1)*self.size["dy"]+2*self.size["pady"],cursor="hand2")
        self.create_chessboard()
        self.pack()
       
    def create_chessboard(self,tags=("chessboard",)):
        padx,pady,dx,dy,line_width,N=self.size["padx"],self.size["pady"],\
                                      self.size["dx"],self.size["dy"],\
                                      self.size["line_width"],\
                                      self.size["N"]
        for i in range(N):
            self.create_line(padx,pady+dy*i,padx+dx*(N-1),pady+dy*i,\
                        width=line_width,tags=tags)
            self.create_line(padx+dx*i,pady,padx+dx*i,pady+dy*(N-1),\
                        width=line_width,tags=tags)
            d,f,w=5,20,1#about sepcial point
        for xy in [ (padx+N//2*dx,pady+N//2*dy),(padx+3*dx,pady+3*dy),\
                    (padx+(N-4)*dx,pady+3*dy),(padx+3*dx,pady+(N-4)*dy),\
                    (padx+(N-4)*dx,pady+(N-4)*dy) ]:
            self.create_line(xy[0]-f,xy[1]+d,xy[0]-d,xy[1]+d,xy[0]-d,xy[1]+f,width=w)
            self.create_line(xy[0]-f,xy[1]-d,xy[0]-d,xy[1]-d,xy[0]-d,xy[1]-f,width=w)
            self.create_line(xy[0]+f,xy[1]+d,xy[0]+d,xy[1]+d,xy[0]+d,xy[1]+f,width=w)
            self.create_line(xy[0]+f,xy[1]-d,xy[0]+d,xy[1]-d,xy[0]+d,xy[1]-f,width=w)
           
    def plot_chessman(self,x,y,camp): #(x,y):棋子圆心
        r = 23 #radius
        if camp==1:
            o_color="purple"
            i_color="black"
        else:
            o_color="green"
            i_color="white"
           
        self.delete("latest")
        self.create_oval((x-r,y-r,x+r,y+r),tags=("chessman",),outline=\
                         o_color,width=1,fill=i_color) #create chessman
        self.create_oval((x-r,y-r,x+r,y+r),tags="latest",outline="red",width=3)#mark the latest chessman
      
    def master_configure(self):
        self.master["cursor"]="heart"
        self.master.title("联珠")
       
    def restart(self):
        self.delete("chessman")
        self.delete("latest")
       
renju=CV(N)
def add_chessman(event):
    padx,pady,dx,dy,line_width,N=renju.size["padx"],renju.size["pady"],renju.size["dx"],renju.size["dy"],\
                                      renju.size["line_width"],renju.size["N"]
    benchmark=(padx+N//2*dx,pady+N//2*dy)
    Lx=int((event.x-benchmark[0]+0.5*dx)//dx)
    Ly=int((-event.y+benchmark[1]+0.5*dy)//dy)
    if (chessboard.count)%2==0 and chessboard.count< N*N:
        if (Lx,Ly) in [k for k in chessboard.info if (chessboard.info)[k]==None]: #!!!!!!!!!!!!!!!
            renju.plot_chessman(benchmark[0]+Lx*dx,benchmark[1]-Ly*dy,camp=1)
            chessboard.add_chess(Lx,Ly,1)       ##
            renju.unbind("<Button-1>")
            if evaluate(Lx,Ly,camp=1,info=chessboard.info)>=V_five: #human
                if tkMessageBox.showinfo(title="xixi",message="恭喜你赢了!"):
                    chessboard.restart()
                    renju.restart()
                   
            if (chessboard.count)%2==1 and (chessboard.count)< N*N:
                maxv=0
                for (x,y) in [k for k in chessboard.info if (chessboard.info)[k]==None]:
                    V=evaluate(x,y,camp=-1,info=chessboard.info) # computer
                    ###chessboard.info[(x,y)]=camp
                   
                    if V>maxv:
                        #print (x,y), V######################
                        maxv=V
                        choice=(x,y)
                #print################################
                renju.plot_chessman(benchmark[0]+choice[0]*dx,benchmark[1]-choice[1]*dy,camp=-1)
                chessboard.add_chess(choice[0],choice[1],-1) ##
                if maxv>=V_five:
                    if tkMessageBox.showinfo(title="haha",message="小白赢了!"):
                        chessboard.restart()
                        renju.restart()
            renju.bind("<Button-1>",add_chessman)
        #test
if (chessboard.count)==0:
    renju.bind("<Button-1>",add_chessman)
renju.master.mainloop()
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python可视化编程机器学习OpenCV 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档