Python3利用Qt5實現簡易的五子棋遊戲
要寫出一個五子棋遊戲,我們最先要解決的,就是如何下子,如何判斷已經五子連珠,而不是如何繪製畫面,因此我們先確定棋盤
五子棋採用15*15的棋盤,因此,我們可以使用二維列表來建立一個棋盤,不妨認為0表示未放置棋子,1表示放置白子,2表示放置黑子。
顯而易見可以建立列表,注意不能使用*來複制列表
self.chess_board = [[0 for i in range(15)] for i in range(15)]
下棋的步驟十分好做,只需要找到對應的索引進行賦值即可,下一步應該解決如何判斷五子連珠的問題。
每當我們落子結束後,應該判斷是否已經完成五子連珠。對於剛放置的一顆棋子而言,可能的情況大致分為四種:
- 水平
- 斜向右下
- 豎直
- 斜向右上
要判斷是否已經連珠成功,我們以剛放置的棋子為起點,先向前遍歷4個棋子,並計算相同棋子的個數,一旦遇到不同的棋子,就停止,然後從起點向後遍歷4個棋子,直到全部遍歷完成或者棋子總數已經達到5個,就可以返回。我們只需要注意如何獲得棋子的前後棋子以及棋盤的邊界問題,棋子不可能超出棋盤,因此被遍歷的棋子也不能超出棋盤。
以水平為例,可以得到程式碼
def judge_1(self,x:int,y:int) -> bool: count = 1 if self.chess_board[x][y] != 0: for i in range(1,5): if y - i >= 0: if self.chess_board[x][y] == self.chess_board[x][y-i]: print(x,y-i) count += 1 else: break else: break for i in range(1,5): if y + i <=14: if self.chess_board[x][y] == self.chess_board[x][y+i]: print(x,y+i) count += 1 else: break else: break if count == 5: return True return False
以相似的步驟完成其餘三種判斷,就已經完成了五子棋遊戲的核心要素了,剩下的就需要交給PyQt5來完成遊戲的繪製來完善遊戲了。
我們建立一個類來繼承QWidget類,建立一個視窗,之後我們需要建立幾個屬性來完成儲存我們的資料資訊
#棋子的座標
self.x = -1
self.y = -1
#區分玩家
#開始標籤
self.flag = False
#儲存已經下好的白子
self.white_chess = []
#儲存已經下好的黑子
self.black_chess = []
我們已經可以開始繪製棋盤,在Qt5中,如果我們需要進行繪製,我們應該重寫paintEvent方法,這個方法會由程式自動呼叫執行。建立一個QPainter物件,將需要繪製的內容用begin與end方法包裹起來,就可以完成繪製。
我們用drawLine方法來繪製線條,用drawEllipse方法來繪製棋子,使用setPen來更改線條樣式,setBrush來更改棋子樣式。
得到程式碼(本段程式碼有參考他人程式碼,這是我第一次接觸Qt的繪製)
--------------------GUI中的x軸豎直向下,y軸水平向右,因此繪製棋子時的x與y需要顛倒---------------
#繪製棋盤與棋子
def paintEvent(self, e) -> None:
qp = QPainter()
qp.begin(self)
qp.fillRect(self.rect(), QColor("light blue"))
qp.drawRect(self.rect())
qp.setBackground(QColor("yellow"))
qp.setPen(QPen(QColor(0, 0, 0), 2, Qt.SolidLine))
for i in range(15):
qp.drawLine(QPoint(30, 30 + 30 * i), QPoint(450, 30 + 30 * i))
for i in range(15):
qp.drawLine(QPoint(30 + 30 * i, 30), QPoint(30 + 30 * i, 450))
qp.setBrush(QColor(0, 0, 0))
key_points = [(3, 3), (11, 3), (3, 11), (11, 11), (7, 7)]
if len(self.black_chess) != 0:
for t in self.black_chess:
#畫黑子
qp.drawEllipse(QPoint(30 + 30 * t[1], 30 + 30 * t[0]), 6, 6)
for t in key_points:
#棋盤的5個定點
qp.drawEllipse(QPoint(30 + 30 * t[0], 30 + 30 * t[1]), 3, 3)
qp.setBrush(QColor(255,255,255))
if len(self.white_chess) != 0:
for t in self.white_chess:
#畫白子
qp.drawEllipse(QPoint(30 + 30 * t[1], 30 + 30 * t[0]), 6, 6)
qp.end()
另一個需要在GUI中解決的問題就是,如何獲取要下的棋子的座標?我們可以通過重寫滑鼠事件來解決,重寫單機事件mousePressEvent,並修改棋子的x座標與y座標即可,另外,使用者不可能每次都恰巧點到我們規定的座標點上,因此需要給出一個大致範圍判斷,這裡我的方式是先獲取座標,然後根據座標找到距離最近的點
def mousePressEvent(self, e) -> None:
if e.buttons() == QtCore.Qt.LeftButton:
if e.x() > 15 and e.x() < 465 and e.y() > 15 and e.y() < 465:
x = e.x()/30 - e.x()//30
y = e.y()/30 - e.y()//30
self.y = (e.x()-30)//30 if x < 0.5 else (e.x()-30)//30 + 1
self.x = (e.y()-30)//30 if y < 0.5 else (e.y()-30)//30 + 1
if self.flag:
print(self.x,self.y)
if self.player % 2 == 1:
if goBang.put_white_chess(self.x,self.y):
self.player += 1
print('黑子行動')
else:
print('白子行動')
if goBang.judge(self.x,self.y):
msg_box = QMessageBox(QMessageBox.Information, '提示', '白子獲勝!')
msg_box.exec_()
else:
if goBang.put_black_chess(self.x,self.y):
self.player += 1
print('白子行動')
else:
print('黑子行動')
if goBang.judge(self.x,self.y):
msg_box = QMessageBox(QMessageBox.Information, '提示', '黑子獲勝!')
msg_box.exec_()
每當遊戲完成,我們應該可以清空棋盤,也就是將所有儲存資料的變數都重新初始化再重繪棋盤
#清除棋盤,重開遊戲
def clear(self) -> None:
self.x = -1
self.y = -1
self.player = 0
self.flag = False
self.white_chess = []
self.black_chess = []
self.chess_board = [[0 for i in range(15)] for i in range(15)]
self.update()
這樣就大致結束了!!||ヽ(* ̄▽ ̄*)ノミ|Ю
下面是全部程式碼:
from PyQt5 import *
from PyQt5 import QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class GoBang(QWidget):
#初始化棋盤
def __init__(self):
super().__init__()
self.setWindowTitle('五子棋Hi~ o(* ̄▽ ̄*)ブ')
self.x = -1
self.y = -1
#區分玩家
self.player = 0
#開始標籤
self.flag = False
#儲存已經下好的白子
self.white_chess = []
#儲存已經下好的黑子
self.black_chess = []
self.setFixedSize(800,600)
self.chess_board = [[0 for i in range(15)] for i in range(15)]
btn1 = QPushButton('開始',self)
btn1.setGeometry(500,100,50,30)
btn1.clicked.connect(self.setFlag)
btn2 = QPushButton('重開',self)
btn2.setGeometry(550,100,50,30)
btn2.clicked.connect(self.clear)
self.show()
#繪製棋盤與棋子
def paintEvent(self, e) -> None:
qp = QPainter()
qp.begin(self)
qp.fillRect(self.rect(), QColor("light blue"))
qp.drawRect(self.rect())
qp.setBackground(QColor("yellow"))
qp.setPen(QPen(QColor(0, 0, 0), 2, Qt.SolidLine))
for i in range(15):
qp.drawLine(QPoint(30, 30 + 30 * i), QPoint(450, 30 + 30 * i))
for i in range(15):
qp.drawLine(QPoint(30 + 30 * i, 30), QPoint(30 + 30 * i, 450))
qp.setBrush(QColor(0, 0, 0))
key_points = [(3, 3), (11, 3), (3, 11), (11, 11), (7, 7)]
if len(self.black_chess) != 0:
for t in self.black_chess:
#畫黑子
qp.drawEllipse(QPoint(30 + 30 * t[1], 30 + 30 * t[0]), 6, 6)
for t in key_points:
#棋盤的5個定點
qp.drawEllipse(QPoint(30 + 30 * t[0], 30 + 30 * t[1]), 3, 3)
qp.setBrush(QColor(255,255,255))
if len(self.white_chess) != 0:
for t in self.white_chess:
#畫白子
qp.drawEllipse(QPoint(30 + 30 * t[1], 30 + 30 * t[0]), 6, 6)
qp.end()
#更改標籤,開始遊戲
def setFlag(self) -> None:
self.flag = True
def mousePressEvent(self, e) -> None:
if e.buttons() == QtCore.Qt.LeftButton:
if e.x() > 15 and e.x() < 465 and e.y() > 15 and e.y() < 465:
x = e.x()/30 - e.x()//30
y = e.y()/30 - e.y()//30
self.y = (e.x()-30)//30 if x < 0.5 else (e.x()-30)//30 + 1
self.x = (e.y()-30)//30 if y < 0.5 else (e.y()-30)//30 + 1
if self.flag:
print(self.x,self.y)
if self.player % 2 == 1:
if goBang.put_white_chess(self.x,self.y):
self.player += 1
print('黑子行動')
else:
print('白子行動')
if goBang.judge(self.x,self.y):
msg_box = QMessageBox(QMessageBox.Information, '提示', '白子獲勝!')
msg_box.exec_()
else:
if goBang.put_black_chess(self.x,self.y):
self.player += 1
print('白子行動')
else:
print('黑子行動')
if goBang.judge(self.x,self.y):
msg_box = QMessageBox(QMessageBox.Information, '提示', '黑子獲勝!')
msg_box.exec_()
#下白子
def put_white_chess(self,x:int,y:int) -> bool:
if self.chess_board[x][y] != 0:
msg_box = QMessageBox(QMessageBox.Information, '提示', '這個位置已經有棋子了!')
msg_box.exec_()
return False
else:
self.chess_board[x][y] = 1
self.white_chess.append((x,y))
self.update()
return True
#下黑子
def put_black_chess(self,x:int,y:int) -> bool:
if self.chess_board[x][y] != 0:
msg_box = QMessageBox(QMessageBox.Information, '提示', '這個位置已經有棋子了!')
msg_box.exec_()
return False
else:
self.chess_board[x][y] = 2
self.black_chess.append((x,y))
self.update()
return True
#清除棋盤,重開遊戲
def clear(self) -> None:
self.x = -1
self.y = -1
self.player = 0
self.flag = False
self.white_chess = []
self.black_chess = []
self.chess_board = [[0 for i in range(15)] for i in range(15)]
self.update()
#判斷是否已經五子連珠
def judge(self,x:int,y:int) -> bool:
if self.judge_1(x,y) or self.judge_2(x,y) or self.judge_3(x,y) or self.judge_4(x,y):
return True
return False
#判斷橫線
def judge_1(self,x:int,y:int) -> bool:
count = 1
if self.chess_board[x][y] != 0:
for i in range(1,5):
if y - i >= 0:
if self.chess_board[x][y] == self.chess_board[x][y-i]:
print(x,y-i)
count += 1
else:
break
else:
break
for i in range(1,5):
if y + i <=14:
if self.chess_board[x][y] == self.chess_board[x][y+i]:
print(x,y+i)
count += 1
else:
break
else:
break
if count == 5:
return True
return False
#判斷右下線
def judge_2(self,x:int,y:int) -> bool:
count = 1
if self.chess_board[x][y] != 0:
for i in range(1,5):
if x-i >= 0 and y - i >= 0:
if self.chess_board[x][y] == self.chess_board[x-i][y-i]:
print(x-i,y-i)
count += 1
else:
break
else:
break
for i in range(1,5):
if x + i <= 14 and y + i <= 14:
if self.chess_board[x][y] == self.chess_board[x+i][y+i]:
print(x+i,y+i)
count += 1
else:
break
else:
break
if count == 5:
return True
return False
#判斷豎線
def judge_3(self,x:int,y:int) -> bool:
count = 1
if self.chess_board[x][y] != 0:
for i in range(1,5):
if x - i >= 0:
if self.chess_board[x][y] == self.chess_board[x-i][y]:
print(x-i,y)
count += 1
else:
break
else:
break
for i in range(1,5):
if x + i <= 14:
if self.chess_board[x][y] == self.chess_board[x+i][y]:
print(x+i,y)
count += 1
else:
break
else:
break
if count == 5:
return True
return False
#判斷右上線
def judge_4(self,x:int,y:int) -> bool:
count = 1
if self.chess_board[x][y] != 0:
for i in range(1,5):
if x - i >= 0 and y + i <= 14:
if self.chess_board[x][y] == self.chess_board[x-i][y+i]:
print(x-i,y+i)
count += 1
else:
break
else:
break
for i in range(1,5):
if x + i <= 14 and y - i >= 0:
if self.chess_board[x][y] == self.chess_board[x+i][y-i]:
print(x+i,y-i)
count += 1
else:
break
else:
break
if count == 5:
return True
return False
#程式入口
if __name__ == '__main__':
app = QApplication(sys.argv)
goBang = GoBang()
sys.exit(app.exec_())