python做一個人機對戰五子棋,AI版本

川川菜鳥發表於2020-12-25

川川以前夢寐以求的想找個五子棋對手,由於學習原因,實在沒小夥伴陪我玩了,於是我就想著做一個AI五子棋來陪我玩吧。網上的遊戲搜可以看到一大堆,我們不能說白嫖,白嫖過來總要改一改成自己想要的吧,雖然我也喜歡白嫖,但是我還是喜歡改成我自己想要的。

需要完整檔案加我**QQ:2835809579**

於是我就改編做了一個五子棋,別說,這五子棋真的強,有時候我下不過他!
看一小段視訊演示一下:

AI.mp4

截圖看下:
在這裡插入圖片描述
程式碼實在太多了,我就只上傳一部分了,有需要再加我 QQ.

from PyQt5.QtWidgets import QMainWindow, QMessageBox
from PyQt5.QtGui import QPainter, QPen, QColor, QPalette, QBrush, QPixmap, QRadialGradient
from PyQt5.QtCore import Qt, QPoint, QTimer
import traceback
from game import Gomoku
from corner_widget import CornerWidget


def run_with_exc(f):
    """遊戲執行出現錯誤時,用messagebox把錯誤資訊顯示出來"""

    def call(window, *args, **kwargs):
        try:
            return f(window, *args, **kwargs)
        except Exception:
            exc_info = traceback.format_exc()
            QMessageBox.about(window, '錯誤資訊', exc_info)
    return call


class GomokuWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.init_ui()  # 初始化遊戲介面
        self.g = Gomoku()  # 初始化遊戲內容

        self.last_pos = (-1, -1)
        self.res = 0  # 記錄那邊獲得了勝利
        self.operate_status = 0  # 遊戲操作狀態。0為遊戲中(可操作),1為遊戲結束閃爍過程中(不可操作)

    def init_ui(self):
        """初始化遊戲介面"""
        # 1. 確定遊戲介面的標題,大小和背景顏色
        self.setObjectName('MainWindow')
        self.setWindowTitle('五子棋')
        self.setFixedSize(650, 650)
        # self.setStyleSheet('#MainWindow{background-color: green}')
        palette = QPalette()
        palette.setBrush(QPalette.Window, QBrush(QPixmap('imgs/muzm.jpg')))
        self.setPalette(palette)
        # 2. 開啟滑鼠位置的追蹤。並在滑鼠位置移動時,使用特殊符號標記當前的位置
        self.setMouseTracking(True)
        # 3. 滑鼠位置移動時,對滑鼠位置的特殊標記
        self.corner_widget = CornerWidget(self)
        self.corner_widget.repaint()
        self.corner_widget.hide()
        # 4. 遊戲結束時閃爍的定時器
        self.end_timer = QTimer(self)
        self.end_timer.timeout.connect(self.end_flash)
        self.flash_cnt = 0  # 遊戲結束之前閃爍了多少次
        self.flash_pieces = ((-1, -1), )  # 哪些棋子需要閃爍
        # 5. 顯示初始化的遊戲介面
        self.show()

    @run_with_exc
    def paintEvent(self, e):
        """繪製遊戲內容"""

        def draw_map():
            """繪製棋盤"""
            qp.setPen(QPen(QColor(0, 0, 0), 2, Qt.SolidLine))  # 棋盤的顏色為黑色
            # 繪製橫線
            for x in range(15):
                qp.drawLine(40 * (x + 1), 40, 40 * (x + 1), 600)
            # 繪製豎線
            for y in range(15):
                qp.drawLine(40, 40 * (y + 1), 600, 40 * (y + 1))
            # 繪製棋盤中的黑點
            qp.setBrush(QColor(0, 0, 0))
            key_points = [(4, 4), (12, 4), (4, 12), (12, 12), (8, 8)]
            for t in key_points:
                qp.drawEllipse(QPoint(40 * t[0], 40 * t[1]), 5, 5)

        def draw_pieces():
            """繪製棋子"""
            # 繪製黑棋子
            qp.setPen(QPen(QColor(0, 0, 0), 1, Qt.SolidLine))
            # qp.setBrush(QColor(0, 0, 0))
            for x in range(15):
                for y in range(15):
                    if self.g.g_map[x][y] == 1:
                        if self.flash_cnt % 2 == 1 and (x, y) in self.flash_pieces:
                            continue
                        radial = QRadialGradient(40 * (x + 1), 40 * (y + 1), 15, 40 * x + 35, 40 * y + 35)  # 棋子的漸變效果
                        radial.setColorAt(0, QColor(96, 96, 96))
                        radial.setColorAt(1, QColor(0, 0, 0))
                        qp.setBrush(QBrush(radial))
                        qp.drawEllipse(QPoint(40 * (x + 1), 40 * (y + 1)), 15, 15)
            # 繪製白棋子
            qp.setPen(QPen(QColor(160, 160, 160), 1, Qt.SolidLine))
            # qp.setBrush(QColor(255, 255, 255))
            for x in range(15):
                for y in range(15):
                    if self.g.g_map[x][y] == 2:
                        if self.flash_cnt % 2 == 1 and (x, y) in self.flash_pieces:
                            continue
                        radial = QRadialGradient(40 * (x + 1), 40 * (y + 1), 15, 40 * x + 35, 40 * y + 35)  # 棋子的漸變效果
                        radial.setColorAt(0, QColor(255, 255, 255))
                        radial.setColorAt(1, QColor(160, 160, 160))
                        qp.setBrush(QBrush(radial))
                        qp.drawEllipse(QPoint(40 * (x + 1), 40 * (y + 1)), 15, 15)

        if hasattr(self, 'g'):  # 遊戲還沒開始的話,就不用畫了
            qp = QPainter()
            qp.begin(self)
            draw_map()  # 繪製棋盤
            draw_pieces()  # 繪製棋子
            qp.end()

    @run_with_exc
    def mouseMoveEvent(self, e):
        # 1. 首先判斷滑鼠位置對應棋盤中的哪一個格子
        mouse_x = e.windowPos().x()
        mouse_y = e.windowPos().y()
        if 25 <= mouse_x <= 615 and 25 <= mouse_y <= 615 and (mouse_x % 40 <= 15 or mouse_x % 40 >= 25) and (mouse_y % 40 <= 15 or mouse_y % 40 >= 25):
            game_x = int((mouse_x + 15) // 40) - 1
            game_y = int((mouse_y + 15) // 40) - 1
        else:  # 滑鼠當前的位置不對應任何一個遊戲格子,將其標記為(01, 01
            game_x = -1
            game_y = -1

        # 2. 然後判斷滑鼠位置較前一時刻是否發生了變化
        pos_change = False  # 標記滑鼠位置是否發生了變化
        if game_x != self.last_pos[0] or game_y != self.last_pos[1]:
            pos_change = True
        self.last_pos = (game_x, game_y)
        # 3. 最後根據滑鼠位置的變化,繪製特殊標記
        if pos_change and game_x != -1:
            self.setCursor(Qt.PointingHandCursor)
        if pos_change and game_x == -1:
            self.setCursor(Qt.ArrowCursor)
        if pos_change and game_x != -1:
            self.corner_widget.move(25 + game_x * 40, 25 + game_y * 40)
            self.corner_widget.show()
        if pos_change and game_x == -1:
            self.corner_widget.hide()

    @run_with_exc
    def mousePressEvent(self, e):
        """根據滑鼠的動作,確定落子位置"""
        if not (hasattr(self, 'operate_status') and self.operate_status == 0):
            return
        if e.button() == Qt.LeftButton:
            # 1. 首先判斷按下了哪個格子
            mouse_x = e.windowPos().x()
            mouse_y = e.windowPos().y()
            if (mouse_x % 40 <= 15 or mouse_x % 40 >= 25) and (mouse_y % 40 <= 15 or mouse_y % 40 >= 25):
                game_x = int((mouse_x + 15) // 40) - 1
                game_y = int((mouse_y + 15) // 40) - 1
            else:  # 滑鼠點選的位置不正確
                return
            self.g.move_1step(True, game_x, game_y)

            # 2. 根據操作結果進行一輪遊戲迴圈
            res, self.flash_pieces = self.g.game_result(show=True)  # 判斷遊戲結果
            if res != 0:  # 如果遊戲結果為“已經結束”,則顯示遊戲內容,並退出主迴圈
                self.repaint(0, 0, 650, 650)
                self.game_restart(res)
                return
            # self.g.ai_move_1step()  # 電腦下一步
            self.g.ai_play_1step()  # 電腦下一步
            res, self.flash_pieces = self.g.game_result(show=True)
            if res != 0:
                self.repaint(0, 0, 650, 650)
                self.game_restart(res)
                return
            self.repaint(0, 0, 650, 650)  # 在遊戲還沒有結束的情況下,顯示遊戲內容,並繼續下一輪迴圈

    @run_with_exc
    def end_flash(self):
        # 遊戲結束時的閃爍操作
        if self.flash_cnt <= 5:
            # 執行閃爍
            self.flash_cnt += 1
            self.repaint()
        else:
            # 閃爍完畢,執行重新開始的操作
            self.end_timer.stop()
            # 1. 顯示遊戲結束的資訊
            if self.res == 1:
                QMessageBox.about(self, '遊戲結束', '玩家獲勝!')
            elif self.res == 2:
                QMessageBox.about(self, '遊戲結束', '電腦獲勝!')
            elif self.res == 3:
                QMessageBox.about(self, '遊戲結束', '平局!')
            else:
                raise ValueError('當前遊戲結束的標誌位為' + self.res + '. 而遊戲結束的標誌位必須為1, 2 或 3')
            # 2. 遊戲重新開始的操作
            self.res = 0
            self.operate_status = 0
            self.flash_cnt = 0
            self.g = Gomoku()  # 重新初始化遊戲內容
            self.repaint(0, 0, 650, 650)  # 重新繪製遊戲介面

    def game_restart(self, res):
        """遊戲出現開始"""
        self.res = res  # 標記誰獲勝了
        self.operate_status = 1  # 遊戲結束時的閃爍過程中,不可操作
        self.end_timer.start(300)  # 開始結束時閃爍的計時器

from PyQt5.QtGui import QPainter, QPen
from PyQt5.QtWidgets import QWidget
from PyQt5.QtCore import Qt


class CornerWidget(QWidget):

    def __init__(self, parent):
        super().__init__(parent=parent)
        self.setFixedSize(30, 30)

    def paintEvent(self, e):
        qp = QPainter()
        qp.begin(self)
        pen = QPen(Qt.red, 3, Qt.SolidLine)
        qp.setPen(pen)
        qp.drawLine(0, 8, 0, 0)
        qp.drawLine(0, 0, 8, 0)
        qp.drawLine(22, 0, 28, 0)
        qp.drawLine(28, 0, 28, 8)
        qp.drawLine(28, 22, 28, 28)
        qp.drawLine(28, 28, 20, 28)
        qp.drawLine(8, 28, 0, 28)
        qp.drawLine(0, 28, 0, 22)

現在我準備把這個封裝成exe軟體來用,嘿嘿!

相關文章