'''
三维四子棋是在三维空间率先四子连珠的游戏,在传统五子棋基本被研究透的情况下,三维四子棋增加了规则和难度,更加考验计算力、空间感、观察力和想象力。
本模块实现三维四子棋的GUI。
仅供学习和参考。
By SoulCheung On May 28th
Email:a1594983583@163.com
QQ:1594983583
'''
import sys,os,math,random,psutil
from itertools import product
from PyQt5 import QtWidgets,QtCore,QtGui
from PyQt5.QtCore import Qt,pyqtSignal,QThread,QObject
from PyQt5.QtGui import QPalette,QPixmap,QIcon
from PyQt5.QtMultimedia import QSound
if os.name == "nt":
dir = os.path.split(__file__)[0]
board_image=dir+'/assets/board.png'
black_image=dir+'/assets/black.png'
white_image=dir+'/assets/white.png'
br_image=dir+'/assets/blacks-removebg-preview.png'
wr_image=dir+'/assets/whites-removebg-preview.png'
move_sound=dir+'/assets/move.wav'
win_sound=dir+'/assets/win.wav'
defeated_sound=dir+'/assets/defeated.wav'
def overload():
if psutil.Process(os.getpid()).memory_info().rss/1024/1024/1024>19 or psutil.virtual_memory().percent>92:
return True
else:
return False
def judge(t,tl):
for i in tl:
if abs(t[0]-i[0])<2 and abs(t[1]-i[1])<2 and abs(t[2]-i[2])<2:
x,y,z=t[0]-i[0],t[1]-i[1],t[2]-i[2]
if (i[0]-x,i[1]-y,i[2]-z) in tl:
if (t[0]+x,t[1]+y,t[2]+z) in tl or (i[0]-x*2,i[1]-y*2,i[2]-z*2) in tl:
return True
elif (t[0]+x,t[1]+y,t[2]+z) in tl:
if (i[0]-x,i[1]-y,i[2]-z) in tl or (t[0]+x*2,t[1]+y*2,t[2]+z*2) in tl:
return True
else:
return False
def transform(x,y):
if y in range(50,270):
y=(y-50)//44
if x in range(50,270) or x in range(320,540) or x in range(590,810) or x in range(860,1080) or x in range(1130,1350):
z=x//270
x=(x-270*z-50)//44
return (x,y,z)
else:
return False
else:
return False
def printpos(t):
x=['A','B','C','D','E']
y=['1','2','3','4','5']
z=['一','二','三','四','五']
return x[t[0]]+y[t[1]]+z[t[2]]
class Player():
def __init__(self,n,st,et):
if n == 0:
self.name='黑'
self.n=n
else:
self.name='白'
self.n=1
self.chesslist=[]
self.ai=False
self.st=st
self.et=et
class Node():
def __init__(self,pc,pd,b):
super().__init__()
self.parent = None
self.children = []
self.visit_times = 1
self.win_times = 0
self.situation = None
self.n=0
self.depth=0
self.leaf=False
self.pc=pc
self.pd=pd
self.board=b
def set_patnet(self,node):
self.parent=node
if self.parent.n==0:
self.n=1
else:
self.n=0
self.depth=self.parent.depth+1
def set_children(self):
for i in self.board:
new_board=self.board[:]
if i[2]<4:
new_board[self.board.index(i)]=(i[0],i[1],i[2]+1)
newpc=self.pc[:]
newpc.append(i)
child=Node(self.pd[:],newpc,new_board)
child.set_patnet(self)
child.situation=i
self.children.append(child)
def set_leaf(self):
self.leaf=True
def ucb(self):
return self.win_times/self.visit_times+math.sqrt(2*math.log(self.parent.visit_times,math.e)/self.visit_times)
def chose(self):
ucb=-1
l=[]
for i in self.children:
if i.leaf==True:
l=[i]
break
n=i.ucb()
if n>ucb:
l=[i]
ucb=n
if n==ucb:
l.append(i)
return random.choice(l)
def maxchild(self):
rate=0.5
l=[]
for i in self.children:
n=i.win_times/i.visit_times
if n > rate:
l=[i]
rate=n
if n==rate:
l.append(i)
return random.choice(l)
class LaBel(QtWidgets.QLabel):
def __init__(self, parent):
super().__init__(parent)
self.setMouseTracking(True)
def enterEvent(self, e):
e.ignore()
class Timer(QtWidgets.QLCDNumber):
TimeOut=pyqtSignal()
OneSecond=pyqtSignal()
def __init__(self, parent,n:int):
super().__init__(parent)
self.time=n
self.of=False
def resetn(self,n):
self.time=n
def start(self):
self.of=True
self.ddl=self.startTimer(1000,Qt.PreciseTimer)
def timerEvent(self, e):
self.time-=1
m,s=divmod(self.time,60)
self.display("%02d:%02d" % (m, s))
self.OneSecond.emit()
if self.time<=0:
self.close()
self.TimeOut.emit()
def close(self):
try:
self.killTimer(self.ddl)
except AttributeError:
pass
self.of=False
class WorkerThread(QThread):
run_status=True
def __init__(self,node):
super().__init__()
self.node=node
def run(self):
def postback(node,n):
lvnode=node
while True:
lvnode.visit_times+=1
if lvnode.n==n:
lvnode.win_times+=1
if lvnode.parent!=None:
lvnode=lvnode.parent
else:
break
while self.run_status:
if not overload():
node=self.node
while not node.leaf:
if len(node.children)==0:
node.set_children()
node=node.chose()
if judge(node.situation,node.parent.pc):
node.set_leaf()
postback(node,node.n)
break
else:
break
def stop(self):
self.run_status=False
class Gobang(QtWidgets.QWidget):
def __init__(self):
super(Gobang,self).__init__()
self.resize(1400, 800)
self.setMinimumSize(QtCore.QSize(1400,800))
self.setMaximumSize(QtCore.QSize(1400,800))
self.setWindowTitle("三维四子棋")
self.setWindowIcon(QIcon(black_image))
palette1 = QPalette()
palette1.setBrush(self.backgroundRole(), QtGui.QBrush(QtGui.QPixmap(board_image)))
self.setPalette(palette1)
self.black=QPixmap(black_image)
self.white=QPixmap(white_image)
self.ms=QSound(move_sound)
self.ws=QSound(win_sound)
self.ds=QSound(defeated_sound)
self.br=QtWidgets.QLabel(self)
self.br.setPixmap(QPixmap(br_image))
self.br.setScaledContents(True)
self.br.setGeometry(560,400,80,80)
self.wr=QtWidgets.QLabel(self)
self.wr.setPixmap(QPixmap(wr_image))
self.wr.setScaledContents(True)
self.wr.setGeometry(560,600,80,80)
self.startbt=QtWidgets.QPushButton('开始',self)
self.startbt.setGeometry(1150,400,100,35)
self.startbt.clicked.connect(self.gamestart)
self.resetbt=QtWidgets.QPushButton('重置',self)
self.resetbt.setGeometry(1150,500,100,35)
self.resetbt.clicked.connect(self.initui)
self.pausebt=QtWidgets.QPushButton('暂停',self)
self.pausebt.setCheckable(True)
self.pausebt.toggle()
self.pausebt.setGeometry(1150,600,100,35)
self.pausebt.clicked.connect(self.gamepause)
self.rollbackbt=QtWidgets.QPushButton('悔棋',self)
self.rollbackbt.setGeometry(1150,700,100,35)
self.rollbackbt.clicked.connect(self.gamerollback)
self.bai=QtWidgets.QCheckBox('笨笨的机器人代打',self)
self.bai.stateChanged.connect(self.aistart)
self.wai=QtWidgets.QCheckBox('笨笨的机器人代打',self)
self.wai.stateChanged.connect(self.aistart)
self.bai.setGeometry(545,490,125,25)
self.wai.setGeometry(545,690,125,25)
self.pieces=[LaBel(self) for i in range(125)]
for piece in self.pieces:
piece.setVisible(True)
piece.setScaledContents(True)
self.bst=Timer(self,90)
self.bst.setGeometry(700,375,150,60)
self.bet=Timer(self,900)
self.bet.setGeometry(700,465,150,60)
self.bst.TimeOut.connect(self.aiplace)
self.wst=Timer(self,90)
self.wst.setGeometry(700,575,150,60)
self.wet=Timer(self,900)
self.wet.setGeometry(700,665,150,60)
self.wst.TimeOut.connect(self.aiplace)
self.initui()
def initui(self):
self.wet.display('15:00')
self.wst.display('01:30')
self.bet.display('15:00')
self.bst.display('01:30')
self.startbt.setEnabled(True)
self.pausebt.setEnabled(False)
self.bai.setEnabled(True)
self.wai.setEnabled(True)
self.bai.setChecked(False)
self.wai.setChecked(False)
self.bst.close()
self.bst.resetn(90)
self.bet.close()
self.bet.resetn(900)
self.wst.close()
self.wst.resetn(90)
self.wet.close()
self.wet.resetn(900)
for i in self.pieces:
i.setPixmap(QPixmap(''))
self.rates=[]
self.poss=list(tuple(list(i)+[0]) for i in [a for a in product(range(5),repeat=2)])
for i in self.poss:
l=QtWidgets.QLabel(self)
l.setGeometry(50+80*i[0],345+80*i[1],80,80)
l.setText(printpos(i))
self.rates.append(l)
self.gaming=False
self.step=0
self.pl=[Player(0,self.bst,self.bet),Player(1,self.wst,self.wet)]
self.pc=self.pl[0]
self.pc.st.OneSecond.connect(self.showrates)
self.node=Node(self.pc.chesslist[:],self.pl[(self.pl.index(self.pc)+1)%2].chesslist[:],self.poss[:])
self.ai=WorkerThread(self.node)
self.ai.start()
def showrates(self):
for i in self.node.children:
self.rates[i.situation[0]*5+i.situation[1]].setText(printpos(i.situation)+'\n'+str(i.win_times)+'/'+str(i.visit_times-1))
def aistart(self):
if self.bai.isChecked():
self.pl[0].ai=True
else:
self.pl[0].ai=False
if self.wai.isChecked():
self.pl[1].ai=True
else:
self.pl[1].ai=False
def aiplace(self):
if self.pc.ai:
self.place(self.node.maxchild().situation,self.step%2)
else:
self.pc.et.start()
def gamestart(self):
self.gaming=True
self.startbt.setEnabled(False)
self.pausebt.setEnabled(True)
self.bai.setEnabled(False)
self.wai.setEnabled(False)
self.bst.start()
def gamepause(self):
if self.gaming:
self.gaming=False
self.pausebt.setText('恢复')
if self.pc.st.of:
self.pc.st.close()
else:
self.pc.et.close()
else:
self.pausebt.setText('暂停')
self.gaming=True
self.pc.st.start()
def gamerollback(self):
if len(self.pl[1].chesslist)>0:
t1=self.pl[0].chesslist[-1][:]
t2=self.pl[1].chesslist[-1][:]
self.poss[self.poss.index((t1[0],t1[1],t1[2]+1))]=t1
self.poss[self.poss.index((t2[0],t2[1],t2[2]+1))]=t2
for i in self.pl:
i.chesslist.pop(-1)
self.pieces[self.step-1].setPixmap(QPixmap(''))
self.pieces[self.step-2].setPixmap(QPixmap(''))
self.step-=2
def place(self,t,flag):
if flag==0:
pie=self.black
else:
pie=self.white
self.pieces[self.step].setPixmap(pie)
self.pieces[self.step].setGeometry(t[0]*44+54+t[2]*270, t[1]*44+54, 36, 36)
self.ms.play()
self.step+=1
if judge(t,self.pc.chesslist):
self.ws.play()
self.gaming=False
self.rollbackbt.setEnabled(False)
self.pausebt.setEnabled(False)
if self.pc.st.of:
self.pc.st.close()
else:
self.pc.et.close()
else:
self.pc.chesslist.append(t[:])
if t[2] < 4:
self.poss[self.poss.index(t)]=(t[0],t[1],t[2]+1)
else:
self.poss[self.poss.index(t)]=None
if self.pc.st.of==False:
self.pc.et.close()
else:
self.pc.st.close()
self.pc.st.resetn(90)
self.pc.st.display('01:30')
self.pc=self.pl[self.step%2]
self.pc.st.start()
self.ai.stop()
self.node=Node(self.pc.chesslist[:],self.pl[(self.pl.index(self.pc)+1)%2].chesslist[:],self.poss[:])
self.ai=WorkerThread(self.node)
self.ai.start()
self.pc.st.OneSecond.connect(self.showrates)
def mousePressEvent(self, e):
if self.pc.ai==False:
if e.button() == Qt.LeftButton and self.gaming==True:
t=transform(e.x(), e.y())
if t in self.poss:
self.place(t,self.step%2)
if __name__=="__main__":
app = QtWidgets.QApplication(sys.argv)
gb = Gobang()
gb.show()
exit(app.exec_())
资源链接:基于MCTS的三维四子棋AI模拟和基于PyQt5的应用交互界面: 基于MCTS的三维四子棋AI模拟和基于PyQt5的应用交互界面 - Gitee.com
标签:__,基于,四子,AI,self,pc,dir,poss,def From: https://blog.csdn.net/zhangshuo88/article/details/139326869