上期褒链,我們做好了游戲的棋盤,本期我們處理如何落子窘俺,及設(shè)計我們的AI
廢話少說是掰,先看看如何落子
落子
落子的過程大概為
- 獲取鼠標(biāo)的位置
- 計算位置所對應(yīng)的落子點
- 畫出棋子
看一下主函數(shù)中的流程
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
# 1.有鼠標(biāo)點擊事件發(fā)生了,我們獲取鼠標(biāo)的位置
# 2.計算鼠標(biāo)點擊所在網(wǎng)格點的位置
# 3.添加棋子
- 獲取鼠標(biāo)位置
獲取鼠標(biāo)的位置很簡單耍休,pygame為我們做好了各種事件的檢測及記錄押蚤,我們只需要看有沒有鼠標(biāo)落下事件的發(fā)生,然后獲取位置就好了
好了羹应,我們看如何獲取鼠標(biāo)的位置揽碘,只有一個語句就好了
pos = event.pos
- 計算網(wǎng)格點的位置
計算網(wǎng)格點的位置也很簡單,只要用坐標(biāo)值除以網(wǎng)格的寬度就好了园匹,這里有一點需要注意雳刺,就是我們不可能每次都點擊到網(wǎng)格點上,因此需要有一個四舍五入的過程裸违,就是我們點擊的位置距離哪個點近掖桦,我們就默認(rèn)用戶點擊了哪一個點。
grid = (int(round(event.pos[0] / (GRID_WIDTH + .0))),
int(round(event.pos[1] / (GRID_WIDTH + .0))))
- 添加棋子
要畫出棋子供汛,我們必須記錄下我們走的每一步棋枪汪,并且在刷新屏幕的時候?qū)⑦@些棋子全部畫出來涌穆。我們定義一個全局變量movements用于我們的每一步棋。然后雀久,每次落子之后宿稀,我們就將落子信息存儲在里面。
movements = []
def add_coin(screen, pos, color):
movements.append(((pos[0] * GRID_WIDTH, pos[1] * GRID_WIDTH), color))
pygame.draw.circle(screen, color,
(pos[0] * GRID_WIDTH, pos[1] * GRID_WIDTH), 16)
再定義一個畫出每一步棋的函數(shù)
def draw_movements(screen):
for m ini movements:
# m[0] 存的是位置赖捌,m[1]存的是顏色
pygame.draw.circle(screen, m[1], m[0], 16)
好了祝沸,我們在添加棋子的注釋下面添加一句
add_coin(screen, pos, BLACK)
在刷新屏幕之前調(diào)用畫出棋子的函數(shù)
draw_movements(screen)
好了,我們運行一下越庇,在屏幕中畫一個五字~~
OK罩锐,落子順利完成,
下一步就是我們游戲的核心了卤唉,我們要寫我們自己的AI~~
五子棋 AI
在我們開發(fā)我們的AI之前我們還有一個事情要做
判斷游戲結(jié)束
如何判斷游戲結(jié)束涩惑?只要有五個子連成線就好了!對的桑驱,每次落子的時候我們只要判斷所落的子周圍有沒有統(tǒng)一顏色的棋子可以連成五子的境氢,因此我們需要一個矩陣記錄每個位置子的顏色~
color_metrix = [[None] * 20 for i in range(20)]
此時,我們就可以定義我們的判斷游戲結(jié)束的函數(shù)了碰纬,函數(shù)的邏輯很簡單,只要判斷所落子的周圍是否有五子連成線一共有四個方向
def game_is_over(pos, color):
hori = 1
verti = 1
slash = 1
backslash = 1
left = pos[0] - 1
while left > 0 and color_metrix[left][pos[1]] == color:
left -= 1
hori += 1
right = pos[0] + 1
while right < 20 and color_metrix[right][pos[1]] == color:
right += 1
hori += 1
up = pos[1] - 1
while up > 0 and color_metrix[pos[0]][up] == color:
up -= 1
verti += 1
down = pos[1] + 1
while down < 20 and color_metrix[pos[0]][down] == color:
down += 1
verti += 1
left = pos[0] - 1
up = pos[1] - 1
while left > 0 and up > 0 and color_metrix[left][up] == color:
left -= 1
up -= 1
backslash += 1
right = pos[0] + 1
down = pos[1] + 1
while right < 20 and down < 20 and color_metrix[right][down] == color:
right += 1
down += 1
backslash += 1
right = pos[0] + 1
up = pos[1] - 1
while right < 20 and up > 0 and color_metrix[right][up] == color:
right += 1
up -= 1
slash += 1
left = pos[0] - 1
down = pos[1] + 1
while left > 0 and down < 20 and color_metrix[left][down] == color:
left -= 1
down += 1
slash += 1
if max([hori, verti, backslash, slash]) == 5:
return True
好的在我們畫出棋子之后加入游戲結(jié)束的判斷
if game_is_over(grid, BLACK):
running = False
對了記得在添加棋子的函數(shù)里將改變我們color_metrix的語句加進去问芬。這樣只要我們有五個同色子連成線游戲就結(jié)束了悦析!
先前我們是直接在主循環(huán)中處理事件的這樣不好,我們提取出一個函數(shù)來用于處理用戶走子此衅,及AI的響應(yīng)函數(shù)接口像這樣:
def move(surf, pos):
'''
Args:
surf: 我們的屏幕
pos: 用戶落子的位置
Returns a tuple or None:
None: if move is invalid else return a
tuple (bool, player):
bool: True is game is not over else False
player: winner (USER or AI)
'''
這個函數(shù)首先判斷落子的位置是否已近有子强戴,有的話返回None, 否則落子為合法的挡鞍,我們調(diào)用add_coin骑歹, 最后我們調(diào)用respond函數(shù)
這個函數(shù)是我們用來運行我們的AI,并決定下一步動作的墨微,返回值和我們的move一樣道媚,這樣,我們的move就變成了下面這樣
def move(surf, pos):
'''
Args:
surf: 我們的屏幕
pos: 用戶落子的位置
Returns a tuple or None:
None: if move is invalid else return a
tuple (bool, player):
bool: True is game is not over else False
player: winner (USER or AI)
'''
grid = (int(round(pos[0] / (GRID_WIDTH + .0))),
int(round(pos[1] / (GRID_WIDTH + .0))))
if grid[0] <= 0 or grid[0] > 19:
return
if grid[1] <= 0 or grid[1] > 19:
return
pos = (grid[0] * GRID_WIDTH, grid[1] * GRID_WIDTH)
# num_pos = gridpos_2_num(grid)
# if num_pos not in remain:
# return None
if color_metrix[grid[0]][grid[1]] is not None:
return None
curr_move = (pos, BLACK)
add_coin(surf, BLACK, grid, USER)
if game_is_over(grid, BLACK):
return (False, USER)
return respond(surf, movements, curr_move)
這里我們給add_coin 添加了一個參數(shù)翘县,就是當(dāng)前落子的角色最域,其中
USER, AI = 1, 0
我們用隨機落子代替我們的AI
def respond(surf, movements, curr_move):
# 測試用,隨機落子
grid_pos = (random.randint(1, 19), random.randint(1, 19))
# print(grid_pos)
add_coin(surf, WHITE, grid_pos, 16)
if game_is_over(grid_pos, WHITE):
return (False, AI)
return None
看一下效果~
好了~ 我們的五子棋完成了锈麸!
什么镀脂?? 不滿意忘伞?
好吧薄翅,AI確實比較麻煩沙兰,這里我給大家介紹一下思路,完整的代碼大家可以到我的github上去下載翘魄,下載鏈接在文章的最后~
AI思路
五子棋的規(guī)則大家都懂鼎天,沒下一個棋子都對周圍的區(qū)域會產(chǎn)生影響,我們的AI可以記錄每個位置的價值熟丸,每次下棋的時候可以選擇得分最高的那個位置训措,具體操作我們可以這樣,連成子的數(shù)目越多光羞,價值越高绩鸣,如果可以在多個方向上連成子,我們可以將價值相應(yīng)的增加倍數(shù)纱兑,同時在對方有三個子連成線(獨立的呀闻,即兩邊沒有其他子)的時候必須堵住對方。大概就是這樣的潜慎。
具體實現(xiàn)我們就不講解了捡多,看一下最終效果
我試了一下,和AI下我基本上有輸?shù)念盱牛灿汹A的垒手。可能是我的水平比較差倒信,大家可以寫自己的AI或者在我的AI基礎(chǔ)上再改進科贬!
完整的AI代碼可以去我的 github 看,點擊這里進入GitHub鳖悠。
如果這篇文章對您有幫助榜掌,贊賞一下吧~
您的支持是我繼續(xù)創(chuàng)作的動力~~~