一、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()