Connect 4

屏風式四子棋(Connect Four),簡稱四子棋,是Howard Wexler在1974年推出的連棋類遊戲。

img

0. 本章重點

  1. 怎樣用演算法去製這一個會捉棋的AI。

1. 建立spot class, gameBoard class和遊戲畫面

connectFour.pyde:

gameBoard.py:

spot.py:

image-20230314134723582

跟之前的俄羅斯方塊一樣,我們用一個叫GameBoard的class來裝起整個遊戲。之後要寫人工智能去跟玩家遊玩的話會容易後多。

Spot這個class中,ij是用來紀錄畫面的col和row的,而xy則是其座標,方便直接顯示;而value則是用來紀錄其內容,沒有填入任任何顏色時就是'',填入黃色和紅色的話則分別是YR,用字符去紀錄。

2. 加入mouse操控

connect.pyde:

gameBoard.py:

spot.py沒有改變。

image-20230315102028741

首先在gameBoard中,

加入3個變數,currnetCol為用來紀錄之後選擇要入棋的欄,而currentRow是用來紀錄該疊高到哪一個格,colHeight則用來紀錄每一欄的高度,每填一粒棋的話該行的高度就加一。

 

在主程式中,

加入一個滑鼠函數,如果滑鼠在相對應的位置按下按鍵,則將currentCol設為i,即你按下的欄數,currentRow暫時設定為0,而colHeight則將相對應的欄加1。之後print colHeight出來debug。

3. 測試落子

connectFour.pyde:

gameBoard.py:

spot.py沒有改變。

image-20230316083339739

在主程式中,

將原本直接測試colHeight是否能夠相加堆疊的功能,轉成一個gameBoard class的函數,方便之後跟電腦對玩時電腦操作。

 

gameBoard.py中,先看看最下面的trigger()函數,

承上面主程式的gameBoard.trigger(),在這個class中,開一個叫trigger()的函數,就是落棋落在哪一欄。如果該欄未滿(if self.colHeight[i] < 6:),就將該欄的高度self.colHeight[i]加一,currentCol指定在這一行,currentRow直接指定為6-colHeight[i],即該欄堆疊的高度,之後就將該個格填成currentPlayer,這個變數只有RY兩個值,用來紀錄現在的回合是哪個顏色,最後就用swapPlayer()交換顏色。

 

而上面的swapPlayer()函數,則是用來交換RY兩個值。

 

最後記得在最上面的__init()__中,加入self.currentPlayer = 'R'紅子先下棋。原本我的設計中,是有動畫做落子動作,模擬現實情況棋子會一步一步落下的,但為簡潔起見,這次的遊戲重點並不在此,就不做這個功能了,如果你有與趣做到一步一步落棋的效果,可以參考上一章tetris遊戲

4. 檢查是否勝出

connectFour.pyde:

gameBoard.py:

spot.py沒有改變。

image-20230316105116299

 

先看看gameBoard.py的class,主要是在最下方加入了checkWin()的函數,這部分我直接用圖片去說明:

IMG_0031_1

如圖所示,我們首先檢查打橫是否有四個連續一樣,但又不是''空內容的格,由於打橫只有7欄,我們又連續檢查到j+3(即當格後的第4格),所以j只需要for j in range(4),例如上圖藍色的檢查條,只由j=3出發,去到j=6

IMG_0031_2

同理,最後藍色的檢查條只有i=2i=5,所以i只需要for i in range(3)就夠了。

IMG_0031_3

IMG_0031_4

其他我就不再冗述了。當然,我做我方法是分開4次for loop,分別檢查打橫、打直和兩個打斜。你也可以只用一個for loop,同時檢查4個分向。又或者,栱子是由最底部開始堆疊的,所以由最下方檢查到最上方,會比較快找出贏家,減少運算時間。

5. 加入game over和you win

connectFour.pyde:

gameBoard.py:

spot.py沒有改變。

image-20230316122423646

主要改變都是在gameBoard的class,但先說明一下主程式:

將原本winner = gameBoard.checkWin()print(winner)都刪除,一會兒gameBoard的class中,因我們要紀錄誰是贏家做出對應的反應。

 

gameBoard.py中,

在初始化的最下,加入gameOverwinner兩個變數。

 

在顯示函數中,加入,如果gameOver的話,就看看winner是誰,是R的話即我方贏,如Y的話即電腦贏。

 

下棋的函數中,首先要加入一個限制,只有在self.gameOver == False的情況下才會下棋。其次,將原本在主程式的self.winner = self.checkWin()搬過來,同時保留winner變數,如果winner有內容的話,就將gameOver變成True

 

到這裡,今次的遊戲,手動版就暫時作結,因遊戲規則和畫面都十分簡單,而且為聚焦於遊戲本身,我也刻意地減少了動畫效果和美化。下一節會首先介紹一下遊戲AI的演算法,之後就會教大家用minmax演算法製造一個AI跟玩家對奕。