Python五子棋实现

一、Python GTK环境搭建

          pygtk环境搭建我第一次用了all-in-one包不行出

Traceback (most recent call last): 

  File "<stdin>", line 1, in <module> 
  File "C:\Python26\lib\site-packages\gtk-2.0\gtk\__init__.py", line 
48, in <mod 
ule> 
    from gtk import _gtk 
ImportError: DLL load failed: The specified module could not be found

          这样的错误,最后发现是因为几个包版本的问题导致的,所以推荐大家用一下几个版本:


-- http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.16/gtk+-bundle_2.16.6-20090911_win32.zip


-- http://ftp.gnome.org/pub/GNOME/binaries/win32/pycairo/1.4/pycairo-1.4.12-2.win32-py2.6.exe


-- http://ftp.gnome.org/pub/GNOME/binaries/win32/pygobject/2.14/pygobject-2.14.2-2.win32-py2.6.exe


-- http://ftp.gnome.org/pub/GNOME/binaries/win32/pygtk/2.12/pygtk-2.12.1-3.win32-py2.6.exe


二、五子棋游戏的实现


1.逻辑变量与常量

逻辑变量与常量的分析如下:

Five=3000 #五子连线得分

FFDL=2900 #双活四得分

FFSL=2800 #活四成四得分

FTDL=2700 #活四活三得分

FL =2600 #单活四得分

FFNL=2500 #双成四得分

FTSL=2400 #成四活三得分

TTDL=2300 #双活三得分

 

self.x = 0

self.y = 0 #临时横纵坐标

self.lblock = [False,False,False,False]

self.rblock = [False,False,False,False]  #左堵右堵记录变量数组

self.countnum = 0 #棋步总数记录变量

self.emptynum = [0, 0, 0, 0] #定义空个数记录变量数组

self.piecenum = [0, 0, 0, 0] #定义棋子个数记录数组

self.scoreE = [0, 0, 0, 0] #定义得分数组

self.scoreS = 0 #定义得分记录空间变量

self.scoretar = 0

self.state = True #棋局状态

self.firstmove = True #游戏次序

self.piece = [[0 for x in range(15)] for yin range(15)]Public X%, Y% '水平与垂直坐标

注:以上声明均在模块中进行。

 

2.算法概述

    为了实现电脑行棋的人工智能化,需要实现对棋局面分析与估值的人工智能化。即实现对一定棋局的数值化反馈。首先应有一个函数来判断某一条直线上的棋形。根据单线棋形的不同,可将不同的单线棋形分为五字连线、活四、成四、活三等情况,再将反馈值交由一个局面评估函数进行量化处理,即得相应局面某点的评估分数的数值化表示。但进行量化处理的过程中需注意:每种棋形的得分值是唯一的,即程序头所确定的特殊棋形得分与所计算的不同棋形得分间不应存在交集。最后有一个取最优走法函数根据得分最高值取得相应的最佳走法。在算法实现的过程中有一定的近似计算,即只需判断两个方向上的单线棋形即可。这是科学的,因为多元(受棋子数、端堵情况、空子数三个变量制约)估值函数接近实际值的必要而不充分条件为 (自变量为相应棋形的棋子数),且由于三线共线棋形成立的条件为单线或双线棋形,其得分上也存在相应的继承性;故其与特殊棋形与计算分值间存在分值的量级差相一致。事实也证明,这样做明显提高了程序的执行效率。棋步处理流程如图1所示:

 

图1 棋步处理流程图

 

3.程序

首先建立基本界面。分别建立一个窗体和模块,并将窗体调整为合适长宽。VB中提供了强大的面向对象的设计功能,但为了提高程序模块化程度,所有关键变量均使用形参的形式传递。参数为实现画棋盘操作的窗体类。在模块中输入画棋盘函数:

def draw_pad(self):

       self.gc.set_foreground(self.color[3])

       for i in range(15, 450, 30):

           self.draw_line(15, i, 30*14, True)

           self.draw_line(i, 15, 30*14, False)

以下是画棋子函数。首参为欲实现画棋子操作的窗体类,第二个参数为欲画棋子在棋盘上的水平坐标,第三参数为欲画棋子在棋盘上竖直方向的纵坐标,第四参数为代表玩家信息的身份变量。

def draw_piece(self, x, y, flag):

       if flag == 1:

           self.gc.set_foreground(self.color[1])

           self.area.window.draw_arc(self.gc, True, y*30+5, x*30+5, 20, 20, 0,360*64)

       else:

           self.gc.set_foreground(self.color[2])

           self.area.window.draw_arc(self.gc, True, y*30+5, x*30+5, 20, 20, 0,360*64)

       Return

 

除了棋盘界面的画棋子函数,需要实现棋局数组在棋盘上的动态显示;可利用两个函数访问保存棋局信息的数组,并调用画棋盘与画棋子函数来动态更新棋盘之显示。以下即为实现动态变更棋子数组的落子函数:

def lay_piece(self, x, y, flag):

       if self.piece[x][y] == 0:

           self.piece[x][y] = flag

       else:

           Msgbox("此处不能落子!")

           return False

       self.countnum = self.countnum + 1

       if self.judge_winner(x, y ,flag)==True :

            Msgbox(["白棋获胜!","黑棋获胜!"][flag-1])

           self.state = False

       if self.countnum == 220:

           Msgbox("和棋!")

           self.state = False

       return True

 

数据能够实时更新后,又有了画棋子与画棋盘函数的支持;以下即为在前面几个函数基础之上的棋盘数据实时更新函数:

def motion_notify_cb(self, widget, event):

       self.area.modify_bg(gtk.STATE_NORMAL, self.color[0])

       self.draw_pad()

       for i in range(0, 15, 1):

           for j in range(0, 15, 1):

                if self.piece[i][j] != 0:

                    self.draw_piece(i, j, self.piece[i][j])

       return True

 

在单线棋形判断函数基础之上的玩家胜负判断函数:

def judge_winner(self, xint, yint, flag):

       self.x = xint

       self.y = yint

       if self.judge_num(1, 0, flag) >= 5 or self.judge_num(1, 1, flag)>= 5 or \

           self.judge_num(0, 1, flag) >= 5 or self.judge_num(-1, 1, flag) >=5:

           return True

       return False    

 

为了判断玩家的获胜情况,需要一个执行效率较高的函数来判断单线棋形最大棋子数。以下函数实现了上述功能。第二和第三参数两两决定了四种棋盘方向。

  defjudge_num(self, xdiff, ydiff, flag):

       i = self.x

       j = self.y

       value = 0

       while i>=0 and i<15 and j>=0 and j<15:  

           if self.piece[i][j] != self.piece[self.x][self.y]:

                break

           i = i + xdiff

           j = j + ydiff

           value = value + 1

       i = self.x

       j = self.y

       while i>=0 and i<15 and j>=0 and j<15:

           if self.piece[i][j] != self.piece[self.x][self.y]:

                break

           i = i - xdiff

           j = j - ydiff

           value = value + 1

        return value – 1

 

为了在窗体棋盘上某一点恰当地估值,需要分析在该点处的落子前后棋形之差异。而棋形判断函数又分为单线和多线两类,多线以单线为基础,再利用数组排序选出最高的两值作为多线棋形判断依据。单线棋形制约因素主要有三个:棋子数、端堵情况和空子数。实现的核心代码如下:

#单线棋形判断函数,用来判断棋形并计算相应分值;参数ArrNum代表的是储存方向情况与得分情况的数组成员序号,定义为1为(1,0),2为(1,1),3为(0,1),4为(-1,1);实现算法的关键是基础的单线棋形的判断与反馈

   def judge_line_num(self, xint, yint, xdiff, ydiff, arrnum, flag):

                   #log.write("judge_line--%d(xint),%d(yint),%d(xdiff),%d(ydiff)--\n"%\

                   #(xint,yint,xdiff,ydiff))

                   xtmp= xint; lb = False; rb = False

                   ytmp= yint; count = 0; piecenum = 0; emptynum = 0

                   fori in range(1, 5, 1):

                            exitfor= True

                            count= count + 1

                            xtmp= xint - i*xdiff

                            ytmp= yint - i*ydiff

                            #log.write("judge_line(lb):%d,%d:(%d,%d)\n"%(xint,yint,xtmp,ytmp))

                            ifxtmp < 0 or xtmp > 14 or ytmp < 0 or ytmp > 14:

                                     lb= True

                                     break

                            ifself.piece[xtmp][ytmp]==flag:

                                     piecenum= piecenum + 1

                            ifself.piece[xtmp][ytmp]==[1,2][2-flag]:

                                     lb= True

                                     break

                            ifself.piece[xtmp][ytmp]==0:

                                     xtmp= xtmp - xdiff

                                     ytmp= ytmp - ydiff

                                     ifxtmp < 0 or xtmp > 14 or ytmp < 0 or ytmp > 14:

                                               lb= False

                                               break

                                     ifself.piece[xtmp][ytmp] == 0:

                                               lb= False

                                               break

                                     ifself.piece[xtmp][ytmp] == flag:

                                               emptynum= emptynum + 1

                                     ifself.piece[xtmp][ytmp] == [1,2][2-flag]:

                                               lb= False

                            exitfor= False

                   ifexitfor == False:

                            xtmp= xtmp - xdiff

                            ytmp= ytmp - ydiff

                            ifxtmp < 0 or xtmp > 14 or ytmp < 0 or ytmp > 14:

                                     lb= True

                            elifself.piece[xtmp][ytmp] == 0:

                                     lb= False

                            elifself.piece[xtmp][ytmp] == [1,2][2-flag]:

                                      lb = True

                   else:

                            count= count - 1

                   #log.write("judge_line(lb):%d(exitfor)%d(lb)\n"%(exitfor,lb))

                  

                   fori in range(1, 5, 1):

                            exitfor= True

                            count= count + 1

                            xtmp= xint + i*xdiff

                            ytmp= yint + i*ydiff

                            #log.write("judge_line(rb):%d,%d:(%d,%d)\n"%(xint,yint,xtmp,ytmp))

                            ifxtmp < 0 or xtmp > 14 or ytmp < 0 or ytmp > 14:

                                     rb= True

                                     break

                            ifself.piece[xtmp][ytmp]==flag:

                                     piecenum= piecenum + 1

                            ifself.piece[xtmp][ytmp]==[1,2][2-flag]:

                                     rb= True

                                     break

                            ifself.piece[xtmp][ytmp]==0:

                                     xtmp= xtmp + xdiff

                                     ytmp= ytmp + ydiff

                                     ifxtmp < 0 or xtmp > 14 or ytmp < 0 or ytmp > 14:

                                               rb= False

                                               break

                                     ifself.piece[xtmp][ytmp] == 0:

                                               rb= False

                                               break

                                     ifself.piece[xtmp][ytmp] == flag:

                                               emptynum= emptynum + 1

                                     ifself.piece[xtmp][ytmp] == [1,2][2-flag]:

                                               rb= False

                            exitfor= False

                   ifexitfor == False:

                            xtmp= xtmp + xdiff

                            ytmp= ytmp + ydiff

                            ifxtmp < 0 or xtmp > 14 or ytmp < 0 or ytmp > 14:

                                     rb= True

                            elif self.piece[xtmp][ytmp]== 0:

                                     rb= False

                            elifself.piece[xtmp][ytmp] == [1,2][2-flag]:

                                     rb= True

                   #log.write("judge_line(rb):%d(exitfor)%d(rb)\n"%(exitfor,rb))

                   #log.write("judge_line:%d,%d:%d(lb),%d(rb),%d(piecenum),%d(emptynum)\n"%\

                   #(xint,yint,lb==True,rb==False,piecenum,emptynum))

                   self.lblock[arrnum]= lb

                   self.rblock[arrnum]= rb

                   value= [5**piecenum, 5**piecenum-5**emptynum][emptynum!=0]

                   self.emptynum[arrnum]= emptynum

                   ifrb:

                            iflb:

                                     self.scoreE[arrnum]= 1

                            else:

                                     self.scoreE[arrnum]= value / 2

                   else:

                            iflb:

                                     self.scoreE[arrnum]= value / 2

                            else:

                                     self.scoreE[arrnum]= value

                   #log.write("judge_line:%d,%d(%d,%d):%d(score)%d(value)%d(piecenum)%d(emptynum)\n"%(xint,yint,flag,arrnum,self.scoreE[arrnum],value,piecenum,emptynum))

                   returnpiecenum

 

在单线棋形判断函数的基础上,可评估某点综合棋局情况时多重方向上的棋形叠加。如前所述,用冒泡排序法选出两种最优单线棋形得分;这样做在不改变程序核心的权重估值算法的前提下很大程度上提高了程序执行效率。以下为多线棋形评估函数:

#在单线棋形判断函数的基础上,可评估某点综合棋局情况时多重方向上的棋形叠加

   def dogen(self, xint, yint, flag):

                   self.piecenum[0]= self.judge_line_num(xint, yint, 1, 0, 0, flag)

                   self.piecenum[1]= self.judge_line_num(xint, yint, 1, 1, 1, flag)

                   self.piecenum[2]= self.judge_line_num(xint, yint, 0, 1, 2, flag)

                   self.piecenum[3]= self.judge_line_num(xint, yint, -1, 1, 3, flag)

                   self.arraysort()

                   #log.write("dogen:%d,%d(%d):(%d,%d,%d,%d)(%d,%d,%d,%d)\n"%\

                   #(xint,yint, flag, self.scoreE[0],self.scoreE[1],self.scoreE[2],self.scoreE[3]\

                   #,self.piecenum[0],self.piecenum[1],self.piecenum[2],self.piecenum[3]))

                   #双活四

                   if(self.lblock[0]==False and self.rblock[0]==False) and ( \

                            self.lblock[1]==Falseand self.lblock[1]==False) and ( \

                            self.piecenum[0]==4and self.piecenum[1]==4):

                            returnFFDL

                   #成四活四

                   if(self.lblock[0]==False and self.rblock[0]==False) and ( \

                            (self.lblock[1]==Falseand self.rblock[1]==True) or \

                            (self.lblock[1]==Trueand self.rblock[1]==False)) and \

                            self.piecenum[0]==4and self.piecenum[1]==4:

                            returnFFSL

                   #活四活三

                   if(self.lblock[0]==False and self.rblock[0]==False) and ( \

                            self.lblock[1]==Falseand self.lblock[1]==False) and ( \

                            self.piecenum[0]==4and self.piecenum[1]==3):

                            returnFTDL

                   #双成四

                   if((self.lblock[0]==False and self.rblock[0]==True) or \

                            (self.lblock[0]==Trueand self.rblock[0]==False)) and ( \

                            (self.lblock[1]==Falseand self.rblock[1]==True) or \

                            (self.lblock[1]==Trueand self.rblock[1]==False)) and ( \

                            self.piecenum[0]==4and self.piecenum[1]==4):

                            returnFFNL

                   #单活四或跳成四

                   if(self.lblock[0]==False and self.rblock[0]==False) and \

                            self.piece[0]==4:

                            ifself.emptynum[0]==0:

                                     returnFL

                            else:

                                     returnFL - 5^emptynum[0]

                   #成四活三

                   if((self.lblock[0]==True and self.rblock[0]==False) or \

                            (self.lblock[0]==Falseand self.rblock[0]==True)) and \

           self.piecenum[0]==4 and (self.lblock[1]==False andself.rblock[1]==False) and \

           self.piecenum[1]==3:

                            returnFTSL

                   #双活三

                   if(self.lblock[0]==False and self.rblock[0]==False) and ( \

                            self.lblock[1]==Falseand self.rblock[1]==False) and \

                            self.piecenum[0]==3and \

                            self.piecenum[1]==3:

                            returnTTDL

                   returnself.scoreE[0] + self.scoreE[1]

 

在局面估值函数的基础之上,利用一个二重循环来扫描整个棋盘空闲位置,并在各位置分别用己方和对方子力估值求出两个权重指数。分别乘以两个权重系数,我们称之为攻防系数。这就为算法执行效果的拓展提供了理论依据。比如,简单地以参数的形式修改权重系数而不修改算法,就可以使电脑具备冒进、稳健、保守等多种截然不同的下棋风格。实践也证明,在估值函数与局面评估函数完全相同的条件下,攻防系数的不同可以产生完全不同的走法。以下是返回当前最重要棋步的函数:

#在局面估值函数的基础之上,利用一个二重循环来扫描整个棋盘空闲位置,并在各位置分别用己方和对方子力估值求出两个权重指数。

   def get_vital_step(self, flag):

                   #self.dump_pieces()

                   #看有无己方胜利位置

                   fori in range(0, 15, 1):

                            forj in range(0, 15, 1):

                                     ifself.piece[i][j]==0:

                                               self.piece[i][j]=flag

                                               ifself.judge_winner(i, j, flag)==True:

                                                        self.piece[i][j]=0

                                                        self.scoretar=Five

                                                        returni+15*j

                                               self.piece[i][j]=0

                   #看有无对方胜利位置

                   fori in range(0, 15, 1):

                            forj in range(0, 15, 1):

                                     ifself.piece[i][j]==0:

                                               self.piece[i][j]=[1,2][2-flag]

                                               ifself.judge_winner(i, j, [1,2][2-flag])==True:

                                                        self.piece[i][j]=0

                                                        self.scoretar=Five

                                                        returni+15*j

                                               self.piece[i][j]=0

                   #扫描已方成活四伴随棋形

                   fori in range(0, 15, 1):

                            forj in range(0, 15, 1):

                                     ifself.piece[i][j]==0:

                                               t= self.dogen(i, j, flag)

                                               ift==FFDL:

                                                        self.scoretar= FFDL

                                                        returni+j*15

                                               ift==FFSL:

                                                        self.scoretar= FFSL

                                                        returni+j*15

                                               ift==FTDL:

                                                        self.scoretar= FTDL

                                                        returni+j*15

                                               ift==FL:

                                                        self.scoretar= FL

                                                        returni+j*15

                   #扫描己方成四伴随棋形

                   fori in range(0, 15, 1):

                            forj in range(0, 15, 1):

                                     ifself.piece[i][j]==0:

                                               t= self.dogen(i, j, flag)

                                               ift==FTSL:

                                                        self.scoretar= FTSL

                                                        returni+j*15

                                               ift==FFNL:

                                                        self.scoretar= FFNL

                                                        returni+j*15

                   #扫描对方活四伴随棋形

                   fori in range(0, 15, 1):

                            forj in range(0, 15, 1):

                                     ifself.piece[i][j]==0:

                                               t= self.dogen(i, j, [1,2][2-flag])

                                               ift==FFDL:

                                                        returni+j*15

                                               ift==FTDL:

                                                        self.scoretar= FTDL

                                                        returni+j*15

                                               ift==FFSL:

                                                        self.scoretar= FFSL

                                                        returni+j*15

                                               ift==FL:

                                                        self.scoretar= FL

                                                        returni+j*15

                   #扫描对方成四伴随棋形

                   fori in range(0, 15, 1):

                            forj in range(0, 15, 1):

                                     ifself.piece[i][j]==0:

                                               t= self.dogen(i, j, [1,2][2-flag])

                                               ift==FTSL:

                                                        self.scoretar= FTSL

                                                        returni+j*15

                                               ift==FFNL:

                                                        self.scoretar= FFNL

                                                        returni+j*15

                   #扫描己方双活三棋形

                   fori in range(0, 15, 1):

                            forj in range(0, 15, 1):

                                     ifself.piece[i][j]==0:

                                               t= self.dogen(i, j, flag)

                                               ift==TTDL:

                                                        self.scoretar= TTDL

                                                        returni+j*15

                   #扫描对方双活三棋形

                   fori in range(0, 15, 1):

                            forj in range(0, 15, 1):

                                     ifself.piece[i][j]==0:

                                               t= self.dogen(i, j, [1,2][2-flag])

                                               ift==TTDL:

                                                        self.scoretar= TTDL

                                                        returni+j*15   

       #若前面的棋形均无则自动取得分函数计算最大值点作为落子点

                  max = 0

                   fori in range(0, 15, 1):

                            forj in range(0, 15, 1):

                                     ifself.piece[i][j]==0:

                                               t= self.dogen(i, j, flag)

                                               r= self.dogen(i, j, [1,2][2-flag])

                                               ift + r > max:

                                                        max= t + r

                                                        imax= i

                                                        jmax= j

                   self.scoretar= max

                   returnimax+jmax*15

 

到此为止,电脑AI的设计就告一段落。程序还应具备基本的写出棋谱的能力,并应尽可能的模块化。写棋谱函数的返回值为布尔型,写出为真;反之为假。文件结构由于其有序性而采用顺序文件结构。棋谱文件中每一项记录的组成:[横坐标][纵坐标][玩家标记]。棋盘的长宽是大于十的,因而我们采用十六进制数来表示棋盘坐标。在悔棋记录数组的基础之上,可利用其方便地实现棋谱的文件化输出。实现代码如下:

def saveas(self, w, data):

                   self.filewa= gtk.FileSelection("Save File selection")

                   self.filewa.ok_button.connect("clicked",self.filea_ok_sel)

                   self.filewa.cancel_button.connect("clicked",lambda w: self.filewa.destroy())

                   self.filewa.set_filename("pad.data")

                   self.filewa.show()   

                  

   def filea_ok_sel(self, widget):

       padfile = open(self.filewa.get_filename(), "w")

       pickle.dump(self.piece, padfile)

       padfile.close()

       self.filewa.destroy()

 

与写棋谱函数对应,参数与写棋谱函数相同。

deffile_ok_sel(self, widget):

        try:

            padfile = open(self.filew.get_filename(),"r")

        except:

            Msgbox("File Open Error!")

        self.piece = pickle.load(padfile)

        padfile.close()

        self.filew.destroy()

在调用数组复制命令时需要用到的数组复制函数:

def arraysort(self):

                   forj in range(1, 4, 1):

                            fori in range(j, 0, -1):

                                     ifself.scoreE[i] > self.scoreE[i-1]:

                                               self.scoreE[i]= self.scoreE[i] + self.scoreE[i-1]

                                               self.scoreE[i-1]= self.scoreE[i] - self.scoreE[i-1]

                                               self.scoreE[i]= self.scoreE[i] - self.scoreE[i-1]

                                               self.piecenum[i]= self.piecenum[i] + self.piecenum[i-1]

                                               self.piecenum[i-1]= self.piecenum[i] - self.piecenum[i-1]

                                               self.piecenum[i]= self.piecenum[i] - self.piecenum[i-1]

                                               self.emptynum[i]= self.emptynum[i] + self.emptynum[i-1]

                                               self.emptynum[i-1]= self.emptynum[i] - self.emptynum[i-1]

                                               self.emptynum[i]= self.emptynum[i] - self.emptynum[i-1]

                                               self.lblock[i]= self.lblock[i] + self.lblock[i-1]

                                               self.lblock[i-1]= self.lblock[i] - self.lblock[i-1]

                                               self.lblock[i]= self.lblock[i] - self.lblock[i-1]

                                               self.rblock[i]= self.rblock[i] + self.rblock[i-1]

                                               self.rblock[i-1]= self.rblock[i] - self.rblock[i-1]

                                               self.rblock[i]= self.rblock[i] - self.rblock[i-1]


啊哈,运行展示



三、代码
#!/usr/bin/env python

import pygtk
pygtk.require('2.0')
import gtk
import string
import cPickle as pickle

Five=3000 #五子连线得分
FFDL=2900 #双活四得分
FFSL=2800 #活四成四得分
FTDL=2700 #活四活三得分
FL  =2600 #单活四得分
FFNL=2500 #双成四得分
FTSL=2400 #成四活三得分
TTDL=2300 #双活三得分

#log=file('log.txt','w') # open for 'w'riting

class Msgbox:
	def __init__(self, text):
		self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
		self.vbox = gtk.VBox()
		
		self.hbox1 = gtk.HBox()
		self.label = gtk.Label(text)
		self.hbox1.pack_start(self.label, True, False, 10)			
		self.vbox.pack_start(self.hbox1, True, False, 10)
		
		self.hbox2 = gtk.HBox()		
		self.button_cancel = gtk.Button("取消")
		self.button_cancel.connect("clicked", lambda w: self.window.destroy())
		self.hbox2.pack_start(self.button_cancel, True, False, 5)		
		
		self.button = gtk.Button("确定")
		self.button.connect("clicked", lambda w: self.window.destroy())
		self.hbox2.pack_start(self.button, True, False, 5)
		
		self.vbox.pack_start(self.hbox2, True, False, 10)
		self.window.add(self.vbox)
		
		self.label.show()
		self.hbox1.show()
		self.button.show()
		self.button_cancel.show()
		self.hbox2.show()
		self.vbox.show()
		self.window.show()
		
class Gomoku:
    def print_hello(self, w, data):
        print "Hello, World!"

    # This is the ItemFactoryEntry structure used to generate new menus.
    # Item 1: The menu path. The letter after the underscore indicates an
    #         accelerator key once the menu is open.
    # Item 2: The accelerator key for the entry
    # Item 3: The callback.
    # Item 4: The callback action.  This changes the parameters with
    #         which the callback is called.  The default is 0.
    # Item 5: The item type, used to define what kind of an item it is.
    #       Here are the possible values:

    #       NULL               -> "<Item>"
    #       ""                 -> "<Item>"
    #       "<Title>"          -> create a title item
    #       "<Item>"           -> create a simple item
    #       "<CheckItem>"      -> create a check item
    #       "<ToggleItem>"     -> create a toggle item
    #       "<RadioItem>"      -> create a radio item
    #       <path>             -> path of a radio item to link against
    #       "<Separator>"      -> create a separator
    #       "<Branch>"         -> create an item to hold sub items (optional)
    #       "<LastBranch>"     -> create a right justified branch 

    def get_main_menu(self, window):
        accel_group = gtk.AccelGroup()
        item_factory = gtk.ItemFactory(gtk.MenuBar, "<main>", accel_group)
        item_factory.create_items(self.menu_items)
        window.add_accel_group(accel_group)
        self.item_factory = item_factory
        return item_factory.get_widget("<main>")
		
    def __init__(self):
        self.x = 0
        self.y = 0
        self.lblock = [False,False,False,False]
        self.rblock = [False,False,False,False]
        self.countnum = 0
        self.emptynum = [0, 0, 0, 0]
        self.piecenum = [0, 0, 0, 0]
        self.scoreE = [0, 0, 0, 0]
        self.scoreS = 0
        self.scoretar = 0
        self.state = True
        self.firstmove = True
        self.piece = [[0 for x in range(15)] for y in range(15)]
        self.menu_items = (
            ( "/_File",         None,         None, 0, "<Branch>" ),
            ( "/File/_New",     "<control>N", self.start, 0, None ),
            ( "/File/_Open",    "<control>O", self.loadpad, 0, None ),
            ( "/File/_Save",    "<control>S", self.savepad, 0, None ),
            ( "/File/Save _As", None,         self.saveas, 0, None ),
            ( "/File/sep1",     None,         None, 0, "<Separator>" ),
            ( "/File/Quit",     "<control>Q", gtk.main_quit, 0, None ),
            ( "/_Options",      None,         None, 0, "<Branch>" ),
			( "/Options/_BackGroundColor",  "<control>B",     self.set_ba_color, 0, None ),
			( "/Options/_UserColor",  "<control>U",     self.set_pe_color, 0, None ),
            ( "/Options/_PCColor",  "<control>P",     self.set_pc_color, 0, None ),
            ( "/_Help",         None,         None, 0, "<LastBranch>" ),
            ( "/_Help/About",   None,         None, 0, None ),
            )
		
        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        window.set_title("Gomoku")
        window.connect("destroy", lambda w: gtk.main_quit())
        self.table = gtk.Table(2,1)
		
        self.area = gtk.DrawingArea()
        self.area.set_size_request(450, 450)
        self.pangolayout = self.area.create_pango_layout("")
        self.table.attach(self.area, 0, 1, 1, 2, yoptions=0)
		
        self.menubar = self.get_main_menu(window)
        self.table.attach(self.menubar, 0, 1, 0, 1, yoptions=0)
		
        window.add(self.table)
        self.area.set_events(gtk.gdk.EXPOSURE_MASK 
							 | gtk.gdk.LEAVE_NOTIFY_MASK
							 | gtk.gdk.BUTTON_PRESS_MASK
							 | gtk.gdk.POINTER_MOTION_MASK
							 | gtk.gdk.POINTER_MOTION_HINT_MASK )
        self.area.connect("expose_event", self.area_expose_cb)
        self.area.connect("button_press_event", self.button_press_cb)
        self.area.connect("motion_notify_event", self.motion_notify_cb)
        self.menubar.show()
        self.area.show()
        self.table.show()
        window.show()
	
        self.color = {}
        self.drawable = self.area.window
        self.gc = self.drawable.new_gc()
        self.colormap = self.gc.get_colormap()
        self.color[0] = self.colormap.alloc_color('#FFFFFF', True, True)
        self.color[1] = self.colormap.alloc_color('#FF0000', True, True)
        self.color[2] = self.colormap.alloc_color('#000000', True, True)
        self.color[3] = self.colormap.alloc_color('#000000', True, True)
        #self.gc.set_background(self.color[0])
        self.gc.set_foreground(self.color[2])
        self.area.modify_bg(gtk.STATE_NORMAL, self.color[0])
        #self.color[1] = gtk.gdk.color_parse("#FFFFFF")
        #self.style = self.area.get_style()
        #self.gc = self.drawable.new_gc(foreground=self.color[0],background=self.color[1])
		#self.style.fg_gc[gtk.STATE_NORMAL]

    def area_expose_cb(self, area, event):
        self.draw_pad()
        for i in range(0, 15, 1):
            for j in range(0, 15, 1):
                if self.piece[i][j] != 0:
                    self.draw_piece(i, j, self.piece[i][j])
        return True

    def button_press_cb(self, widget, event):
        if self.state==False:
            Msgbox("已结束,请重新开始!")
            return False
        if self.firstmove==False:
            Msgbox("还没轮到你!")
            return False
        if event.button == 1:
            x = int(event.y//30)
            y = int(event.x//30)
            if self.lay_piece(x, y, 1):
                self.firstmove=False
                vital_step = self.get_vital_step(2); self.lay_piece(vital_step%15, vital_step/15, 2);  self.firstmove=True
        return True
    
    def motion_notify_cb(self, widget, event):
        self.area.modify_bg(gtk.STATE_NORMAL, self.color[0])
        self.draw_pad()
        for i in range(0, 15, 1):
            for j in range(0, 15, 1):
                if self.piece[i][j] != 0:
                    self.draw_piece(i, j, self.piece[i][j])
        return True
		
    def draw_line(self, x, y, len, flag):
        if flag == True:
            self.area.window.draw_line(self.gc, x, y, x + len, y)
        else:
            self.area.window.draw_line(self.gc, x, y, x, y + len)

    def draw_pad(self):
        self.gc.set_foreground(self.color[3])
        for i in range(15, 450, 30):
            self.draw_line(15, i, 30*14, True)
            self.draw_line(i, 15, 30*14, False)

    def draw_piece(self, x, y, flag):
        if flag == 1:
            self.gc.set_foreground(self.color[1])
            #self.drawable.draw_rectangle(self.gc, True, 430, 0, 20, 20)
            self.area.window.draw_arc(self.gc, True, y*30+5, x*30+5, 20, 20, 0, 360*64)
        else:
            self.gc.set_foreground(self.color[2])
            #self.drawable.draw_rectangle(self.gc, True, 430, 430, 20, 20)
            self.area.window.draw_arc(self.gc, True, y*30+5, x*30+5, 20, 20, 0, 360*64)						  
        #self.pangolayout.set_text("Arcs")
        #self.area.window.draw_layout(self.gc, x+5, y+80, self.pangolayout)
        return
		
    def lay_piece(self, x, y, flag):
        if self.piece[x][y] == 0:
            self.piece[x][y] = flag
        else:
            Msgbox("此处不能落子!")
            return False
        self.countnum = self.countnum + 1
        if self.judge_winner(x, y ,flag)==True :
            Msgbox(["白棋获胜!","黑棋获胜!"][flag-1])
            self.state = False
        if self.countnum == 220:
            Msgbox("和棋!")
            self.state = False
        return True
		
    def start(self, w, data):
		self.state = True
		for i in range(0, 15, 1):
			for j in range(0, 15, 1):
				self.piece[i][j] = 0
		self.gc.set_foreground(self.color[0])
		self.drawable.draw_rectangle(self.gc, True, 0, 0, 450, 450)
		#self.dump_pieces()

    def savepad(self, w, data):
        padfile = open("pad.data", "w")
        pickle.dump(self.piece, padfile)
        padfile.close()
		
    def loadpad(self, w, data):
		self.filew = gtk.FileSelection("Load File Selection")
		self.filew.ok_button.connect("clicked", self.file_ok_sel)
		self.filew.cancel_button.connect("clicked", lambda w: self.filew.destroy())
		self.filew.set_filename("pad.data")
		self.filew.show()
		return

    def file_ok_sel(self, widget):
        try:
            padfile = open(self.filew.get_filename(), "r")
        except:
            Msgbox("File Open Error!")
        self.piece = pickle.load(padfile)
        padfile.close()
        self.filew.destroy()
		
    def saveas(self, w, data):
		self.filewa = gtk.FileSelection("Save File selection")
		self.filewa.ok_button.connect("clicked", self.filea_ok_sel)
		self.filewa.cancel_button.connect("clicked", lambda w: self.filewa.destroy())
		self.filewa.set_filename("pad.data")
		self.filewa.show()	
		
    def filea_ok_sel(self, widget):
        padfile = open(self.filewa.get_filename(), "w")
        pickle.dump(self.piece, padfile)
        padfile.close()
        self.filewa.destroy()
	
    def set_ba_color(self, widget, data):
		self.bacolseldlg = gtk.ColorSelectionDialog("Select background color")
		self.bacolsel = self.bacolseldlg.colorsel
		self.bacolsel.set_current_color(self.color[0])
		self.bacolsel.set_has_palette(True)
		self.bacolsel.connect("color_changed", self.pc_color_changed, self.bacolsel)
		response = self.bacolseldlg.run()
		if response -- gtk.RESPONSE_OK:
			self.color[0] = self.bacolsel.get_current_color()
		else:
			print "ok"
			self.area.modify_bg(gtk.STATE_NORMAL, self.color[0])
		self.bacolseldlg.hide()
		
    def set_pe_color(self, w, data):
		self.pecolseldlg = gtk.ColorSelectionDialog("Select Pe background color")
		self.pecolsel = self.pecolseldlg.colorsel
		self.pecolsel.set_current_color(self.color[1])
		self.pecolsel.set_has_palette(True)
		self.pecolsel.connect("color_changed", self.pc_color_changed, self.pecolsel)
		response = self.pecolseldlg.run()
		if response -- gtk.RESPONSE_OK:
			self.color[1] = self.pecolsel.get_current_color()
		else:
			self.area.modify_bg(gtk.STATE_NORMAL, self.color[0])
		self.pecolseldlg.hide()
		
    def set_pc_color(self, w, data):
		self.pccolseldlg = gtk.ColorSelectionDialog("Select PC background color")
		self.pccolsel = self.pccolseldlg.colorsel
		self.pccolsel.set_current_color(self.color[2])
		self.pccolsel.set_has_palette(True)
		self.pccolsel.connect("color_changed", self.pc_color_changed, self.pccolsel)
		response = self.pccolseldlg.run()
		if response -- gtk.RESPONSE_OK:
			self.color[2] = self.pccolsel.get_current_color()
		else:
			self.area.modify_bg(gtk.STATE_NORMAL, self.color[0])
		self.pccolseldlg.hide()
	
    def pc_color_changed(self, widget, data):
        dcolor = data.get_current_color()
        #self.area.modify_bg(gtk.STATE_NORMAL, color)
		
    def dump_pieces(self):
        for i in range(0, 15, 1):
            print self.piece[i][0],self.piece[i][1],self.piece[i][2],self.piece[i][3],self.piece[i][4],\
					self.piece[i][5],self.piece[i][6],self.piece[i][7],self.piece[i][8],self.piece[i][9],\
					self.piece[i][10],self.piece[i][11],self.piece[i][12],self.piece[i][13],self.piece[i][14]
					
    def arraysort(self):
		for j in range(1, 4, 1):
			for i in range(j, 0, -1):
				if self.scoreE[i] > self.scoreE[i-1]:
					self.scoreE[i] = self.scoreE[i] + self.scoreE[i-1]
					self.scoreE[i-1] = self.scoreE[i] - self.scoreE[i-1]
					self.scoreE[i] = self.scoreE[i] - self.scoreE[i-1]
					self.piecenum[i] = self.piecenum[i] + self.piecenum[i-1]
					self.piecenum[i-1] = self.piecenum[i] - self.piecenum[i-1]
					self.piecenum[i] = self.piecenum[i] - self.piecenum[i-1]
					self.emptynum[i] = self.emptynum[i] + self.emptynum[i-1]
					self.emptynum[i-1] = self.emptynum[i] - self.emptynum[i-1]
					self.emptynum[i] = self.emptynum[i] - self.emptynum[i-1]
					self.lblock[i] = self.lblock[i] + self.lblock[i-1]
					self.lblock[i-1] = self.lblock[i] - self.lblock[i-1]
					self.lblock[i] = self.lblock[i] - self.lblock[i-1]
					self.rblock[i] = self.rblock[i] + self.rblock[i-1]
					self.rblock[i-1] = self.rblock[i] - self.rblock[i-1]
					self.rblock[i] = self.rblock[i] - self.rblock[i-1]

    def judge_winner(self, xint, yint, flag):
        self.x = xint
        self.y = yint
        if self.judge_num(1, 0, flag) >= 5 or self.judge_num(1, 1, flag) >= 5 or \
            self.judge_num(0, 1, flag) >= 5 or self.judge_num(-1, 1, flag) >= 5:
            return True
        return False
	
    def judge_num(self, xdiff, ydiff, flag):
        i = self.x
        j = self.y
        value = 0
        while i>=0 and i<15 and j>=0 and j<15:	
            if self.piece[i][j] != self.piece[self.x][self.y]:
                break
            i = i + xdiff
            j = j + ydiff
            value = value + 1
        i = self.x
        j = self.y
        while i>=0 and i<15 and j>=0 and j<15:
            if self.piece[i][j] != self.piece[self.x][self.y]:
                break
            i = i - xdiff
            j = j - ydiff
            value = value + 1
        return value - 1
	
	#单线棋形判断函数,用来判断棋形并计算相应分值;参数ArrNum代表的是储存方向情况与得分情况的数组成员序号,定义为1为(1,0),2为(1,1),3为(0,1),4为(-1,1);实现算法的关键是基础的单线棋形的判断与反馈
    def judge_line_num(self, xint, yint, xdiff, ydiff, arrnum, flag):
		#log.write("judge_line--%d(xint),%d(yint),%d(xdiff),%d(ydiff)--\n"%\
		#(xint,yint,xdiff,ydiff))
		xtmp = xint; lb = False; rb = False
		ytmp = yint; count = 0; piecenum = 0; emptynum = 0
		for i in range(1, 5, 1):
			exitfor = True
			count = count + 1
			xtmp = xint - i*xdiff
			ytmp = yint - i*ydiff
			#log.write("judge_line(lb):%d,%d:(%d,%d)\n"%(xint,yint,xtmp,ytmp))
			if xtmp < 0 or xtmp > 14 or ytmp < 0 or ytmp > 14:
				lb = True
				break
			if self.piece[xtmp][ytmp]==flag:
				piecenum = piecenum + 1
			if self.piece[xtmp][ytmp]==[1,2][2-flag]:
				lb = True
				break
			if self.piece[xtmp][ytmp]==0:
				xtmp = xtmp - xdiff
				ytmp = ytmp - ydiff
				if xtmp < 0 or xtmp > 14 or ytmp < 0 or ytmp > 14:
					lb = False
					break
				if self.piece[xtmp][ytmp] == 0:
					lb = False
					break
				if self.piece[xtmp][ytmp] == flag:
					emptynum = emptynum + 1
				if self.piece[xtmp][ytmp] == [1,2][2-flag]:
					lb = False
			exitfor = False
		if exitfor == False:
			xtmp = xtmp - xdiff
			ytmp = ytmp - ydiff
			if xtmp < 0 or xtmp > 14 or ytmp < 0 or ytmp > 14:
				lb = True
			elif self.piece[xtmp][ytmp] == 0:
				lb = False
			elif self.piece[xtmp][ytmp] == [1,2][2-flag]:
				lb = True
		else:
			count = count - 1
		#log.write("judge_line(lb):%d(exitfor)%d(lb)\n"%(exitfor,lb))
		
		for i in range(1, 5, 1):
			exitfor = True
			count = count + 1
			xtmp = xint + i*xdiff
			ytmp = yint + i*ydiff
			#log.write("judge_line(rb):%d,%d:(%d,%d)\n"%(xint,yint,xtmp,ytmp))
			if xtmp < 0 or xtmp > 14 or ytmp < 0 or ytmp > 14:
				rb = True
				break
			if self.piece[xtmp][ytmp]==flag:
				piecenum = piecenum + 1
			if self.piece[xtmp][ytmp]==[1,2][2-flag]:
				rb = True
				break
			if self.piece[xtmp][ytmp]==0:
				xtmp = xtmp + xdiff
				ytmp = ytmp + ydiff
				if xtmp < 0 or xtmp > 14 or ytmp < 0 or ytmp > 14:
					rb = False
					break
				if self.piece[xtmp][ytmp] == 0:
					rb = False
					break
				if self.piece[xtmp][ytmp] == flag:
					emptynum = emptynum + 1
				if self.piece[xtmp][ytmp] == [1,2][2-flag]:
					rb = False
			exitfor = False
		if exitfor == False:
			xtmp = xtmp + xdiff
			ytmp = ytmp + ydiff
			if xtmp < 0 or xtmp > 14 or ytmp < 0 or ytmp > 14:
				rb = True
			elif self.piece[xtmp][ytmp] == 0:
				rb = False
			elif self.piece[xtmp][ytmp] == [1,2][2-flag]:
				rb = True
		#log.write("judge_line(rb):%d(exitfor)%d(rb)\n"%(exitfor,rb))
		#log.write("judge_line:%d,%d:%d(lb),%d(rb),%d(piecenum),%d(emptynum)\n"%\
		#(xint,yint,lb==True,rb==False,piecenum,emptynum))
		self.lblock[arrnum] = lb
		self.rblock[arrnum] = rb
		value = [5**piecenum, 5**piecenum-5**emptynum][emptynum!=0]
		self.emptynum[arrnum] = emptynum
		if rb:
			if lb:
				self.scoreE[arrnum] = 1
			else:
				self.scoreE[arrnum] = value / 2
		else:
			if lb:
				self.scoreE[arrnum] = value / 2
			else:
				self.scoreE[arrnum] = value
		#log.write("judge_line:%d,%d(%d,%d):%d(score)%d(value)%d(piecenum)%d(emptynum)\n"%(xint,yint,flag,arrnum,self.scoreE[arrnum],value,piecenum,emptynum))
		return piecenum
		
	#在单线棋形判断函数的基础上,可评估某点综合棋局情况时多重方向上的棋形叠加
    def dogen(self, xint, yint, flag):
		self.piecenum[0] = self.judge_line_num(xint, yint, 1, 0, 0, flag)
		self.piecenum[1] = self.judge_line_num(xint, yint, 1, 1, 1, flag)
		self.piecenum[2] = self.judge_line_num(xint, yint, 0, 1, 2, flag)
		self.piecenum[3] = self.judge_line_num(xint, yint, -1, 1, 3, flag)
		self.arraysort()
		#log.write("dogen:%d,%d(%d):(%d,%d,%d,%d)(%d,%d,%d,%d)\n"%\
		#(xint, yint, flag, self.scoreE[0],self.scoreE[1],self.scoreE[2],self.scoreE[3]\
		#,self.piecenum[0],self.piecenum[1],self.piecenum[2],self.piecenum[3]))
		#双活四
		if (self.lblock[0]==False and self.rblock[0]==False) and ( \
			self.lblock[1]==False and self.lblock[1]==False) and ( \
			self.piecenum[0]==4 and self.piecenum[1]==4):
			return FFDL
		#成四活四
		if (self.lblock[0]==False and self.rblock[0]==False) and ( \
			(self.lblock[1]==False and self.rblock[1]==True) or \
			(self.lblock[1]==True and self.rblock[1]==False)) and \
			self.piecenum[0]==4 and self.piecenum[1]==4:
			return FFSL
		#活四活三
		if (self.lblock[0]==False and self.rblock[0]==False) and ( \
			self.lblock[1]==False and self.lblock[1]==False) and ( \
			self.piecenum[0]==4 and self.piecenum[1]==3):
			return FTDL
		#双成四
		if ((self.lblock[0]==False and self.rblock[0]==True) or \
			(self.lblock[0]==True and self.rblock[0]==False)) and ( \
			(self.lblock[1]==False and self.rblock[1]==True) or \
			(self.lblock[1]==True and self.rblock[1]==False)) and ( \
			self.piecenum[0]==4 and self.piecenum[1]==4):
			return FFNL
		#单活四或跳成四
		if (self.lblock[0]==False and self.rblock[0]==False) and \
			self.piece[0]==4:
			if self.emptynum[0]==0:
				return FL
			else:
				return FL - 5^emptynum[0]
		#成四活三
		if ((self.lblock[0]==True and self.rblock[0]==False) or \
			(self.lblock[0]==False and self.rblock[0]==True)) and \
            self.piecenum[0]==4 and (self.lblock[1]==False and self.rblock[1]==False) and \
            self.piecenum[1]==3:
			return FTSL
		#双活三
		if (self.lblock[0]==False and self.rblock[0]==False) and ( \
			self.lblock[1]==False and self.rblock[1]==False) and \
			self.piecenum[0]==3 and \
			self.piecenum[1]==3:
			return TTDL
		return self.scoreE[0] + self.scoreE[1]
		
	#在局面估值函数的基础之上,利用一个二重循环来扫描整个棋盘空闲位置,并在各位置分别用己方和对方子力估值求出两个权重指数。
    def get_vital_step(self, flag):
		#self.dump_pieces()
		#看有无己方胜利位置
		for i in range(0, 15, 1):
			for j in range(0, 15, 1):
				if self.piece[i][j]==0:
					self.piece[i][j]=flag
					if self.judge_winner(i, j, flag)==True:
						self.piece[i][j]=0
						self.scoretar=Five
						return i+15*j
					self.piece[i][j]=0
		#看有无对方胜利位置
		for i in range(0, 15, 1):
			for j in range(0, 15, 1):
				if self.piece[i][j]==0:
					self.piece[i][j]=[1,2][2-flag]
					if self.judge_winner(i, j, [1,2][2-flag])==True:
						self.piece[i][j]=0
						self.scoretar=Five
						return i+15*j
					self.piece[i][j]=0
		#扫描已方成活四伴随棋形
		for i in range(0, 15, 1):
			for j in range(0, 15, 1):
				if self.piece[i][j]==0:
					t = self.dogen(i, j, flag)
					if t==FFDL:
						self.scoretar = FFDL
						return i+j*15
					if t==FFSL:
						self.scoretar = FFSL
						return i+j*15
					if t==FTDL:
						self.scoretar = FTDL
						return i+j*15
					if t==FL:
						self.scoretar = FL
						return i+j*15
		#扫描己方成四伴随棋形
		for i in range(0, 15, 1):
			for j in range(0, 15, 1):
				if self.piece[i][j]==0:
					t = self.dogen(i, j, flag)
					if t==FTSL:
						self.scoretar = FTSL
						return i+j*15
					if t==FFNL:
						self.scoretar = FFNL
						return i+j*15
		#扫描对方活四伴随棋形
		for i in range(0, 15, 1):
			for j in range(0, 15, 1):
				if self.piece[i][j]==0:
					t = self.dogen(i, j, [1,2][2-flag])
					if t==FFDL:
						return i+j*15
					if t==FTDL:
						self.scoretar = FTDL
						return i+j*15
					if t==FFSL:
						self.scoretar = FFSL
						return i+j*15
					if t==FL:
						self.scoretar = FL
						return i+j*15
		#扫描对方成四伴随棋形
		for i in range(0, 15, 1):
			for j in range(0, 15, 1):
				if self.piece[i][j]==0:
					t = self.dogen(i, j, [1,2][2-flag])
					if t==FTSL:
						self.scoretar = FTSL
						return i+j*15
					if t==FFNL:
						self.scoretar = FFNL
						return i+j*15
		#扫描己方双活三棋形
		for i in range(0, 15, 1):
			for j in range(0, 15, 1):
				if self.piece[i][j]==0:
					t = self.dogen(i, j, flag)
					if t==TTDL:
						self.scoretar = TTDL
						return i+j*15
		#扫描对方双活三棋形
		for i in range(0, 15, 1):
			for j in range(0, 15, 1):
				if self.piece[i][j]==0:
					t = self.dogen(i, j, [1,2][2-flag])
					if t==TTDL:
						self.scoretar = TTDL
						return i+j*15	
        #若前面的棋形均无则自动取得分函数计算最大值点作为落子点
		max = 0
		for i in range(0, 15, 1):
			for j in range(0, 15, 1):
				if self.piece[i][j]==0:
					t = self.dogen(i, j, flag)
					r = self.dogen(i, j, [1,2][2-flag])
					if t + r > max:
						max = t + r
						imax = i
						jmax = j
		self.scoretar = max
		return imax+jmax*15
		
def main():
    gtk.main()
    return 0

if __name__ == "__main__":
    Gomoku()
    main()


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值