作為經(jīng)典的棋類游戲,五子棋深受大眾喜愛,但如果僅實(shí)現(xiàn)人與人的博弈,那程序很簡(jiǎn)單,如果要實(shí)現(xiàn)人機(jī)對(duì)戰(zhàn),教會(huì)計(jì)算機(jī)如何戰(zhàn)勝人類,那就不是十分容易的事了。本文我們先從簡(jiǎn)單入手,完成五子棋游戲的基本操作,然后再嘗試引入一些實(shí)現(xiàn)人工智能的編程方法和技巧,從而實(shí)現(xiàn)人機(jī)博弈。
1.創(chuàng)建棋盤和棋子
對(duì)于棋類游戲來說,游戲的場(chǎng)景就是下棋的棋盤,而游戲的主要角色就是棋子。接下來我們開始創(chuàng)建棋盤和棋子。
1.1繪制棋盤
五子棋的棋盤可以看成是一個(gè)1818的網(wǎng)格,他由19條豎線和19條橫線(包含邊界)構(gòu)成,直線的交叉處就下棋子的位置。這里,我們用1818個(gè)棕色格子來填滿棋盤,每個(gè)格子的間距就是棋盤的縱橫交錯(cuò)的線條,python可以實(shí)現(xiàn)該功能的庫有pgzero、tkinter。這里我們用pygame
# 創(chuàng)建窗口,背景為棕色
screen = pygame.display.set_mode((615, 615))
pygame.display.set_caption('五子棋')
screen.fill("#DD954F")
#創(chuàng)建外邊框
a = pygame.Surface((603, 603), flags=pygame.HWSURFACE)
a.fill(color='#121010')
b = pygame.Surface((585, 585), flags=pygame.HWSURFACE)
b.fill(color="#DD954F")
c = pygame.Surface((579, 579), flags=pygame.HWSURFACE)
c.fill(color='#121010')
#
# d = pygame.Surface((576, 576), flags=pygame.HWSURFACE)
# d.fill(color="#DD954F")
#棋盤格子
e = pygame.Surface((31, 31), flags=pygame.HWSURFACE)
e.fill(color="#DD954F")
screen.blit(a, (6.5, 6.5))
screen.blit(b, (15, 15))
screen.blit(c, (18, 18))
for j in range(18):
for i in range(18):
#起點(diǎn)是20,間隔是32,每個(gè)格子大小31,所以格子間距1
screen.blit(e, (20 + 32 * i, 20 + 32 * j))
#存儲(chǔ)棋盤狀態(tài)
alist = np.zeros((19, 19))
#星位
pygame.draw.circle(screen, '#121010', [307.5, 307.5], 5)
pygame.draw.circle(screen, '#121010', [115.5, 307.5], 5)
pygame.draw.circle(screen, '#121010', [499.5, 307.5], 5)
pygame.draw.circle(screen, '#121010', [115.5, 499.5], 5)
pygame.draw.circle(screen, '#121010', [499.5, 499.5], 5)
pygame.draw.circle(screen, '#121010', [115.5, 115.5], 5)
pygame.draw.circle(screen, '#121010', [499.5, 115.5], 5)
pygame.draw.circle(screen, '#121010', [307.5, 499.5], 5)
pygame.draw.circle(screen, '#121010', [307.5, 115.5], 5)
#刷新窗口
pygame.display.flip()
1.2創(chuàng)建棋子
本來打算直接上圖片,后來參考了別的博主的做法,其實(shí)可以通過多個(gè)半徑顏色不同的同心圓疊加來繪制圓滑的棋子
def black(x, y):
a = 20
b =20
c =20
d = 0.01
#循環(huán)50次,每次繪制50個(gè)半徑顏色不同的同心圓
for i in range(50):
pygame.draw.circle(screen, (a, b, c), [19.5 + 32 * x, 19.5 + 32 * y], (16/(d-5)+16))
a += 1
b += 1
c += 1
d += 0.08
pygame.display.update()
def white(x, y):
a = 170
b = 170
c = 170
d = 0.02
for i in range(50):
pygame.draw.circle(screen, (a, b, c), [19.5 + 32 * x, 19.5 + 32 * y],(16/(d-5)+16))
a += 1
b += 1
c += 1
d += 0.08
pygame.display.update()
2.完成走棋操作
棋盤和棋子都準(zhǔn)備好了,下面來實(shí)現(xiàn)下棋的功能,看看如何把棋子擺放在棋盤上。
五子棋的規(guī)則如下
:
1.對(duì)局雙方各執(zhí)一色棋子。 空棋盤開局。 黑先、白后,交替下子,每次只能下一子。
2.棋子下在棋盤的空白點(diǎn)上,棋子下定后,不得向其它點(diǎn)移動(dòng),不得從棋盤上拿掉或拿起另落別處。
3.五顆連續(xù)的同色棋子先連城一條線的那方贏。
人可以通過眼睛來判斷那個(gè)位置能下棋和何時(shí)輸贏,那計(jì)算機(jī)怎么判斷這些呢。計(jì)算不像人那樣有聰明的大腦,想要讓計(jì)算機(jī)能做出這些判斷,就必須把這些操作轉(zhuǎn)換成計(jì)算機(jī)能理解的數(shù)據(jù)。
怎么讓計(jì)算機(jī)知道棋盤的情況呢?我們用一個(gè)二維數(shù)組來模擬棋盤,棋盤上每個(gè)位置都有三種狀態(tài)(無子、黑子、白子)那我們就用(0、1、2)來表示這三種情況。用wb來表示當(dāng)前棋子,如果該位置為0則,更新該位置的值和wb的值,然后調(diào)用對(duì)用繪畫函數(shù)來更新窗口
#存儲(chǔ)棋盤狀態(tài)
alist = np.zeros((19, 19))
if event.type == pygame.MOUSEBUTTONDOWN:
x, y = pygame.mouse.get_pos()
# 棋盤邊界線的中點(diǎn)是19.5, 通過計(jì)算得到當(dāng)前坐標(biāo)在棋盤的行號(hào)和列號(hào)(x,y)
x = round((x - 19.5) / 32)
y = round((y - 19.5) / 32)
if x < 0:
x = 0
if x > 18:
x = 18
if y < 0:
y = 0
if y > 18:
y = 18
z = False
if alist[x][y] == 0:
#這步相當(dāng)于black(x,y)或者是white(x,y)取決于wb
eval(wb + "({},{})".format(x, y))
3.判斷輸贏
怎么判斷贏了沒?我們通過鼠標(biāo)左機(jī)屏幕來下棋子,pygame也給我們提供了獲取鼠標(biāo)坐標(biāo)的函數(shù),每次鼠標(biāo)點(diǎn)擊后就進(jìn)行check()操做
分別檢查四個(gè)方向上是否有連子的情況,先找到該方向的第一個(gè)棋子然后向后統(tǒng)計(jì)同色數(shù)量,判斷輸贏。
def check(x,y):
xx = x
yy = y
while True:
#從最上邊的棋子開始檢查,記錄顏色相同得棋子數(shù)量
#先找到最同一條線上最上邊的同色棋子
if xx == 0:
break
elif alist[xx][yy] != alist[x][y]:
xx += 1
break
else:
xx -= 1
num = 0
while True:
if xx == 18:
break
elif alist[xx][yy] != alist[x][y]:
break
else:
xx += 1
num += 1
if num >= 5:
win(wb1)
# 從最邊的棋子開始檢查,記錄顏色相同得棋子數(shù)量
# 先找到最同一條線上最左邊的同色棋子
xx = x
yy = y
while True:
if yy == 0:
break
elif alist[xx][yy] != alist[x][y]:
yy += 1
break
else:
yy -= 1
num = 0
while True:
if yy == 18:
break
elif alist[xx][yy] != alist[x][y]:
break
else:
yy += 1
num += 1
if num >= 5:
win(wb1)
# 從左上方的棋子開始檢查,記錄顏色相同得棋子數(shù)量
# 先找到最同一條線上左上方的同色棋子
xx = x
yy = y
while True:
if xx == 0:
break
elif yy == 0:
break
elif alist[xx][yy] != alist[x][y]:
xx += 1
yy += 1
break
else:
xx -= 1
yy -= 1
num = 0
while True:
if xx == 18:
break
elif yy == 18:
break
elif alist[xx][yy] != alist[x][y]:
break
else:
xx += 1
yy += 1
num += 1
if num >= 5:
win(wb1)
# 從右上方的棋子開始檢查,記錄顏色相同得棋子數(shù)量
# 先找到最同一條線上右上方的同色棋子
xx = x
yy = y
while True:
if xx == 0:
break
elif yy == 18:
break
elif alist[xx][yy] != alist[x][y]:
xx += 1
yy -= 1
break
else:
xx -= 1
yy += 1
num = 0
while True:
if xx == 18:
break
elif yy == 0:
break
elif alist[xx][yy] != alist[x][y]:
break
else:
xx += 1
yy -= 1
num += 1
if num >= 5:
pygame.font.init()
win(wb1)
def win(wb1):
font = pygame.font.Font(None, 36)
text = font.render("{}贏了".format(wb1), True, (255, 255, 255))
screen.blit(text, ((665 - text.get_width()) / 2, (665 - text.get_height()) / 2))
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
do()
完成了這些步驟后,五子棋的基本規(guī)則已經(jīng)實(shí)現(xiàn),敲了幾十行代碼,終于能得到點(diǎn)反饋了。
下面是初級(jí)版的完整代碼
import random
import sys
import pygame
import numpy as np
def init():
pygame.init()
# 創(chuàng)建窗口,背景為棕色
global screen
screen = pygame.display.set_mode((615, 615))
pygame.display.set_caption('五子棋')
screen.fill("#DD954F")
# 創(chuàng)建外邊框
a = pygame.Surface((603, 603), flags=pygame.HWSURFACE)
a.fill(color='#121010')
b = pygame.Surface((585, 585), flags=pygame.HWSURFACE)
b.fill(color="#DD954F")
c = pygame.Surface((579, 579), flags=pygame.HWSURFACE)
c.fill(color='#121010')
e = pygame.Surface((31, 31), flags=pygame.HWSURFACE)
e.fill(color="#DD954F")
screen.blit(a, (6.5, 6.5))
screen.blit(b, (15, 15))
screen.blit(c, (18, 18))
# 棋盤格子
for j in range(18):
for i in range(18):
# 起點(diǎn)是20,間隔是32,每個(gè)格子大小31,所以格子間距1
screen.blit(e, (20 + 32 * i, 20 + 32 * j))
# 存儲(chǔ)棋盤狀態(tài)
global alist
alist = np.zeros((19, 19))
# 星位
pygame.draw.circle(screen, '#121010', [307.5, 307.5], 5)
pygame.draw.circle(screen, '#121010', [115.5, 307.5], 5)
pygame.draw.circle(screen, '#121010', [499.5, 307.5], 5)
pygame.draw.circle(screen, '#121010', [115.5, 499.5], 5)
pygame.draw.circle(screen, '#121010', [499.5, 499.5], 5)
pygame.draw.circle(screen, '#121010', [115.5, 115.5], 5)
pygame.draw.circle(screen, '#121010', [499.5, 115.5], 5)
pygame.draw.circle(screen, '#121010', [307.5, 499.5], 5)
pygame.draw.circle(screen, '#121010', [307.5, 115.5], 5)
# 刷新窗口
pygame.display.flip()
# 繪制棋子
def black(x, y):
a = 20
b = 20
c = 20
d = 0.01
# 循環(huán)50次,每次繪制50個(gè)半徑顏色不同的同心圓
for i in range(50):
pygame.draw.circle(screen, (a, b, c), [19.5 + 32 * x, 19.5 + 32 * y], (16 / (d - 5) + 16))
a += 1
b += 1
c += 1
d += 0.08
pygame.display.update()
def white(x, y):
a = 170
b = 170
c = 170
d = 0.02
for i in range(50):
pygame.draw.circle(screen, (a, b, c), [19.5 + 32 * x, 19.5 + 32 * y], (16 / (d - 5) + 16))
a += 1
b += 1
c += 1
d += 0.08
pygame.display.update()
pygame.font.init()
font1 = pygame.font.Font(None, 250)
# 主要操作
def do(wb):
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
x, y = pygame.mouse.get_pos()
# 棋盤邊界線的中點(diǎn)是19.5, 通過計(jì)算得到當(dāng)前坐標(biāo)在棋盤的行號(hào)和列號(hào)(x,y)
x = round((x - 19.5) / 32)
y = round((y - 19.5) / 32)
if x < 0:
x = 0
if x > 18:
x = 18
if y < 0:
y = 0
if y > 18:
y = 18
if alist[x][y] == 0:
eval(wb + "({},{})".format(x, y))
if wb == "black":
alist[x][y] = 1
wb1 = "black"
wb = "white"
elif wb == "white":
alist[x][y] = 2
wb1 = "white"
wb = "black"
check(x, y, wb1)
def check(x, y, wb1):
xx = x
yy = y
while True:
# 從最上邊的棋子開始檢查,記錄顏色相同得棋子數(shù)量
# 先找到最同一條線上最上邊的同色棋子
if xx == 0:
break
elif alist[xx][yy] != alist[x][y]:
xx += 1
break
else:
xx -= 1
num = 0
while True:
if xx == 18:
break
elif alist[xx][yy] != alist[x][y]:
break
else:
xx += 1
num += 1
if num >= 5:
win(wb1)
# 從最邊的棋子開始檢查,記錄顏色相同得棋子數(shù)量
# 先找到最同一條線上最左邊的同色棋子
xx = x
yy = y
while True:
if yy == 0:
break
elif alist[xx][yy] != alist[x][y]:
yy += 1
break
else:
yy -= 1
num = 0
while True:
if yy == 18:
break
elif alist[xx][yy] != alist[x][y]:
break
else:
yy += 1
num += 1
if num >= 5:
win(wb1)
# 從左上方的棋子開始檢查,記錄顏色相同得棋子數(shù)量
# 先找到最同一條線上左上方的同色棋子
xx = x
yy = y
while True:
if xx == 0:
break
elif yy == 0:
break
elif alist[xx][yy] != alist[x][y]:
xx += 1
yy += 1
break
else:
xx -= 1
yy -= 1
num = 0
while True:
if xx == 18:
break
elif yy == 18:
break
elif alist[xx][yy] != alist[x][y]:
break
else:
xx += 1
yy += 1
num += 1
if num >= 5:
win(wb1)
# 從右上方的棋子開始檢查,記錄顏色相同得棋子數(shù)量
# 先找到最同一條線上右上方的同色棋子
xx = x
yy = y
while True:
if xx == 0:
break
elif yy == 18:
break
elif alist[xx][yy] != alist[x][y]:
xx += 1
yy -= 1
break
else:
xx -= 1
yy += 1
num = 0
while True:
if xx == 18:
break
elif yy == 0:
break
elif alist[xx][yy] != alist[x][y]:
break
else:
xx += 1
yy -= 1
num += 1
if num >= 5:
pygame.font.init()
win(wb1)
def win(wb1):
pygame.font.init()
font = pygame.font.Font(None, 70)
text = font.render(f"{wb1} won", True, (255, 255, 255))
screen.blit(text, ((655 - text.get_width()) / 2, (665 - text.get_height()) / 2))
pygame.display.update()
pygame.display.flip()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
init()
do("black")
if __name__ == "__main__":
init()
do('black')
4.實(shí)現(xiàn)人機(jī)對(duì)弈
現(xiàn)在五子棋的基本公能已經(jīng)實(shí)現(xiàn)了,但這種充滿策略與博弈的棋類游戲,猜測(cè)并應(yīng)對(duì)對(duì)手可能的布局,相互之間進(jìn)行激烈的智力對(duì)決才是游戲最好玩的部分,一個(gè)人玩也太無聊了,要是電腦能像人一樣可以思考該多好呀,就像近幾年的特別火的“阿爾法狗”一樣,我很期待被自己的程序打敗。那么怎樣實(shí)現(xiàn)人機(jī)對(duì)弈呢?
隨著人工智能的快速發(fā)展,其在許多領(lǐng)域都得到了廣泛應(yīng)用,其中就包括游戲人工智能。游戲人工智能分為兩大類,
一類叫做確定性人工智能,另一類叫做非確定性人工智能。
確定性人工智能,是指電腦按照確定的規(guī)則進(jìn)行判斷和抉擇。非確定性人工智能是指電腦根據(jù)已有的規(guī)則來學(xué)習(xí)新的規(guī)則,例如神經(jīng)網(wǎng)絡(luò)、遺傳算法、決策樹和概率方法等。
因?yàn)槲遄悠宓囊?guī)則是確定的,所以我們只用讓電腦記住規(guī)則,并按照規(guī)則來決策和判斷。在棋類游戲中,如果把棋子的每一種布局都當(dāng)作是一種狀態(tài)
,那對(duì)局中所有的可能局面的集合被稱為狀態(tài)空間
,狀態(tài)空間搜素就是根據(jù)某種規(guī)則,在所有的局面中找到最有利的局面,并以此來決定下一步走法。由于五子棋的規(guī)則簡(jiǎn)單,不同狀態(tài)的最優(yōu)解比較少,不必采用耗費(fèi)空間的狀態(tài)空間搜素法,只用讓電腦記住所有的最優(yōu)解即可。
4.1列舉最優(yōu)解
回想一下自己下棋的思考過程,決策和判斷的依據(jù)無非就那幾種,進(jìn)過權(quán)衡后選擇最有利的走法。模仿人的思考過程,我們可以告訴電腦所有的走法,然后將這些走法按等級(jí)排序,電腦每次決策的時(shí)候就在這些走法集合中按照等級(jí)由高到低的順序依次搜素,沒有找到最優(yōu)解則隨機(jī)下子。
為了讓電腦能識(shí)別走法,首先要對(duì)棋局的各種走法進(jìn)行描述和儲(chǔ)存,這里我們用一個(gè)cdata列表實(shí)現(xiàn),0表示空棋、1表示黑棋、 2表示白棋、3表示將要下棋的位置。
# 0表示空棋
# 1表示黑棋
# 2表示白棋
# 3表示下棋的位置
cdata = [
# 一顆棋子的情況
[1, 3, 0, 0, 0], [0, 1, 3, 0, 0], [0, 0, 1, 3, 0], [0, 0, 0, 1, 3], [0, 0, 0, 3, 1],
[2, 3, 0, 0, 0], [0, 2, 3, 0, 0], [0, 0, 2, 3, 0], [0, 0, 0, 2, 3], [0, 0, 0, 3, 2],
# 二顆棋子的情況
[0, 1, 3, 1, 0], [1, 1, 3, 0, 0], [0, 0, 3, 1, 1],
[2, 2, 3, 0, 0], [0, 0, 3, 2, 2], [0, 2, 3, 2, 0],
# 三顆棋子的情況
[1, 1, 1, 3, 0], [0, 3, 1, 1, 1], [1, 1, 3, 1, 0], [1, 3, 1, 1, 0],
[2, 2, 0, 3, 2], [2, 3, 0, 2, 2], [0, 3, 2, 2, 2], [2, 2, 3, 2, 0],
[2, 3, 2, 2, 0], [0, 2, 3, 2, 2], [0, 2, 2, 3, 2], [2, 2, 2, 3, 0], [3, 2, 2, 2, 0],
# 四顆棋子情況
[1, 1, 1, 1, 3], [3, 1, 1, 1, 1], [1, 1, 1, 3, 1], [1, 3, 1, 1, 1], [1, 1, 3, 1, 1],
[2, 2, 2, 2, 3], [3, 2, 2, 2, 2], [2, 2, 3, 2, 2], [2, 3, 2, 2, 2], [2, 2, 2, 3, 2]
]
4.2匹配最優(yōu)解
現(xiàn)在電腦已經(jīng)掌握各種走法了,決勝秘籍在手,電腦改怎么用呢?接下來我們定義三個(gè)全局變量,用來輔助匹配算法的執(zhí)行。 用ai_col 下棋位置列號(hào), ai_row下棋位置行號(hào),max_level走法等級(jí)
global ai_col, ai_row, max_level
ai_col = -1
ai_row = -1
max_level = -1
接著我們來定義auto_match()函數(shù)
def auto_mach(row, col, level, dx, dy):
global ai_col, ai_row, max_level
col_sel = -1 # 暫存棋子列號(hào)
row_sel = -1 # 暫存棋子行號(hào)
isfind = True # 匹配成功的標(biāo)記
for j in range(5):
cs = alist[row + j * dx][col + j * dy]
if cs == 0:
if cdata[level][j] == 3:
row_sel = row + j * dx
col_sel = col + j * dy
elif cdata[level][j] == 1:
isfind = False
break
elif cdata[level][j] == 2:
isfind = False
break
elif cs != cdata[level][j]:
isfind = False
break
if isfind:
ai_row = row_sel
ai_col = col_sel
max_level = level
return True
return False
函數(shù)需要4個(gè)參數(shù)row和col是棋子的位置,level是走法等級(jí),dx,dy是下一步方向,用row_sel,col_sel來存儲(chǔ)匹配過程中棋子的位值,棋子按照dx,dy的方向一次往后匹配,
如果棋盤布局和走法列表匹配成功則發(fā)揮True,反正返回False。
4.3自主操作
完成了匹配操作后,電腦就有了判斷的能力了,接下來我們來實(shí)現(xiàn)電腦的自主操作。下面定義ai_play()
def ai_play():
global ai_col, ai_row, max_level
ai_col = -1
ai_row = -1
max_level = -1
# 搜素棋盤每個(gè)位置
for i in range(19):
for j in range(19):
# 從高到低搜索
for level in range(len(cdata) - 1, -1, -1):
if level > max_level:
if i + 4 < 19:
if auto_mach(i, j, level, 1, 0):
break
if j + 4 < 19:
if auto_mach(i, j, level, 0, 1):
break
if i + 4 < 19 and j + 4 < 19:
if auto_mach(i, j, level, 1, 1):
break
if i + 4 < 19 and j - 4 > 0:
if auto_mach(i, j, level, 1, -1):
break
if ai_row!=-1 and ai_row!=-1:
alist[ai_row][ai_col]=2
return True
while True:
col = random.randint(0,18)
row = random.randint(0, 18)
if alist[row][col]==0:
alist[row][col]=2
ai_row=row
ai_col=col
return True
return False
該函數(shù)首先將全局變量max_level,ai_row,ai_col重置為-1,清除上次結(jié)果的影響,然后從棋盤第一個(gè)位置開始匹配,max_level記錄最高等級(jí),凡是等級(jí)比其低的走法直接不考慮,這樣能減少不必要的操作,加快程序運(yùn)行速率,匹配結(jié)束后,若找到最優(yōu)解了就更新ai_row,ai_col,沒找到就隨機(jī)選一個(gè)符合要求的位置。
完整代碼
至此程序全部編寫完成,現(xiàn)在運(yùn)行游戲玩一下,看看你和計(jì)算機(jī)誰更厲害,如果被打敗了這就是你肝了半天的代碼最好的反饋。
import random
import sys
import pygame
import numpy as np
# 0表示空棋
# 1表示黑棋
# 2表示白棋
# 3表示下棋的位置
cdata = [
# 一顆棋子的情況
[1, 3, 0, 0, 0], [0, 1, 3, 0, 0], [0, 0, 1, 3, 0], [0, 0, 0, 1, 3], [0, 0, 0, 3, 1],
[2, 3, 0, 0, 0], [0, 2, 3, 0, 0], [0, 0, 2, 3, 0], [0, 0, 0, 2, 3], [0, 0, 0, 3, 2],
# 二顆棋子的情況
[0, 1, 3, 1, 0], [1, 1, 3, 0, 0], [0, 0, 3, 1, 1],
[2, 2, 3, 0, 0], [0, 0, 3, 2, 2], [0, 2, 3, 2, 0],
# 三顆棋子的情況
[1, 1, 1, 3, 0], [0, 3, 1, 1, 1], [1, 1, 3, 1, 0], [1, 3, 1, 1, 0],
[2, 2, 0, 3, 2], [2, 3, 0, 2, 2], [0, 3, 2, 2, 2], [2, 2, 3, 2, 0],
[2, 3, 2, 2, 0], [0, 2, 3, 2, 2], [0, 2, 2, 3, 2], [2, 2, 2, 3, 0], [3, 2, 2, 2, 0],
# 四顆棋子情況
[1, 1, 1, 1, 3], [3, 1, 1, 1, 1], [1, 1, 1, 3, 1], [1, 3, 1, 1, 1], [1, 1, 3, 1, 1],
[2, 2, 2, 2, 3], [3, 2, 2, 2, 2], [2, 2, 3, 2, 2], [2, 3, 2, 2, 2], [2, 2, 2, 3, 2]
]
def auto_mach(row, col, level, dx, dy):
global ai_col, ai_row, max_level
col_sel = -1 # 暫存棋子列號(hào)
row_sel = -1 # 暫存棋子行號(hào)
isfind = True # 匹配成功的標(biāo)記
for j in range(5):
cs = alist[row + j * dx][col + j * dy]
if cs == 0:
if cdata[level][j] == 3:
row_sel = row + j * dx
col_sel = col + j * dy
elif cdata[level][j] == 1:
isfind = False
break
elif cdata[level][j] == 2:
isfind = False
break
elif cs != cdata[level][j]:
isfind = False
break
if isfind:
ai_row = row_sel
ai_col = col_sel
max_level = level
return True
return False
def ai_play():
global ai_col, ai_row, max_level
ai_col = -1
ai_row = -1
max_level = -1
# 搜素棋盤每個(gè)位置
for i in range(19):
for j in range(19):
# 從高到低搜索
for level in range(len(cdata) - 1, -1, -1):
if level > max_level:
if i + 4 < 19:
if auto_mach(i, j, level, 1, 0):
break
if j + 4 < 19:
if auto_mach(i, j, level, 0, 1):
break
if i + 4 < 19 and j + 4 < 19:
if auto_mach(i, j, level, 1, 1):
break
if j + 4 < 19 and i - 4 > 0:
if auto_mach(i, j, level, -1, 1):
break
if ai_row!=-1 and ai_row!=-1:
alist[ai_row][ai_col]=2
return True
while True:
col = random.randint(0,18)
row = random.randint(0, 18)
if alist[row][col]==0:
alist[row][col]=2
ai_row=row
ai_col=col
return True
return False
def init():
pygame.init()
# 創(chuàng)建窗口,背景為棕色
global screen
screen = pygame.display.set_mode((615, 615))
pygame.display.set_caption('五子棋')
screen.fill("#DD954F")
# 創(chuàng)建外邊框
a = pygame.Surface((603, 603), flags=pygame.HWSURFACE)
a.fill(color='#121010')
b = pygame.Surface((585, 585), flags=pygame.HWSURFACE)
b.fill(color="#DD954F")
c = pygame.Surface((579, 579), flags=pygame.HWSURFACE)
c.fill(color='#121010')
e = pygame.Surface((31, 31), flags=pygame.HWSURFACE)
e.fill(color="#DD954F")
screen.blit(a, (6.5, 6.5))
screen.blit(b, (15, 15))
screen.blit(c, (18, 18))
# 棋盤格子
for j in range(18):
for i in range(18):
# 起點(diǎn)是20,間隔是32,每個(gè)格子大小31,所以格子間距1
screen.blit(e, (20 + 32 * i, 20 + 32 * j))
# 存儲(chǔ)棋盤狀態(tài)
global alist
alist = np.zeros((19, 19))
# 星位
pygame.draw.circle(screen, '#121010', [307.5, 307.5], 5)
pygame.draw.circle(screen, '#121010', [115.5, 307.5], 5)
pygame.draw.circle(screen, '#121010', [499.5, 307.5], 5)
pygame.draw.circle(screen, '#121010', [115.5, 499.5], 5)
pygame.draw.circle(screen, '#121010', [499.5, 499.5], 5)
pygame.draw.circle(screen, '#121010', [115.5, 115.5], 5)
pygame.draw.circle(screen, '#121010', [499.5, 115.5], 5)
pygame.draw.circle(screen, '#121010', [307.5, 499.5], 5)
pygame.draw.circle(screen, '#121010', [307.5, 115.5], 5)
# 刷新窗口
pygame.display.flip()
# 繪制棋子
def black(x, y):
a = 20
b = 20
c = 20
d = 0.01
# 循環(huán)50次,每次繪制50個(gè)半徑顏色不同的同心圓
for i in range(50):
pygame.draw.circle(screen, (a, b, c), [19.5 + 32 * x, 19.5 + 32 * y], (16 / (d - 5) + 16))
a += 1
b += 1
c += 1
d += 0.08
pygame.display.update()
def white(x, y):
a = 170
b = 170
c = 170
d = 0.02
for i in range(50):
pygame.draw.circle(screen, (a, b, c), [19.5 + 32 * x, 19.5 + 32 * y], (16 / (d - 5) + 16))
a += 1
b += 1
c += 1
d += 0.08
pygame.display.update()
pygame.font.init()
font1 = pygame.font.Font(None, 250)
# 主要操作
def do(wb):
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
x, y = pygame.mouse.get_pos()
# 棋盤邊界線的中點(diǎn)是19.5, 通過計(jì)算得到當(dāng)前坐標(biāo)在棋盤的行號(hào)和列號(hào)(x,y)
x = round((x - 19.5) / 32)
y = round((y - 19.5) / 32)
if x < 0:
x = 0
if x > 18:
x = 18
if y < 0:
y = 0
if y > 18:
y = 18
if alist[x][y] == 0:
black(x, y)
alist[x][y] = 1
wb1 = "You"
wb = "white"
check(x, y, wb1)
pygame.time.wait(100)
if ai_play():
white(ai_row, ai_col)
wb1 = "AI"
wb = "black"
check(ai_row, ai_col, wb1)
def check(x, y, wb1):
xx = x
yy = y
while True:
# 從最上邊的棋子開始檢查,記錄顏色相同得棋子數(shù)量
# 先找到最同一條線上最上邊的同色棋子
if xx == 0:
if alist[xx][yy] != alist[x][y]:
xx += 1
break
elif alist[xx][yy] != alist[x][y]:
xx += 1
break
else:
xx -= 1
num = 0
while True:
if xx == 18:
if alist[xx][yy] == alist[x][y]:
num += 1
break
elif alist[xx][yy] != alist[x][y]:
break
else:
xx += 1
num += 1
if num >= 5:
win(wb1)
# 從最邊的棋子開始檢查,記錄顏色相同得棋子數(shù)量
# 先找到最同一條線上最左邊的同色棋子
xx = x
yy = y
while True:
if yy == 0:
if alist[xx][yy] != alist[x][y]:
yy += 1
break
elif alist[xx][yy] != alist[x][y]:
yy += 1
break
else:
yy -= 1
num = 0
while True:
if yy == 18:
if alist[xx][yy] == alist[x][y]:
num += 1
break
elif alist[xx][yy] != alist[x][y]:
break
else:
yy += 1
num += 1
if num >= 5:
win(wb1)
# 從左上方的棋子開始檢查,記錄顏色相同得棋子數(shù)量
# 先找到最同一條線上左上方的同色棋子
xx = x
yy = y
while True:
if xx == 0:
if alist[xx][yy] != alist[x][y]:
xx += 1
yy += 1
break
elif yy == 0:
if alist[xx][yy] != alist[x][y]:
xx += 1
yy += 1
break
elif alist[xx][yy] != alist[x][y]:
xx += 1
yy += 1
break
else:
xx -= 1
yy -= 1
num = 0
while True:
if xx == 18:
if alist[xx][yy] == alist[x][y]:
num += 1
break
elif yy == 18:
if alist[xx][yy] == alist[x][y]:
num += 1
break
elif alist[xx][yy] != alist[x][y]:
break
else:
xx += 1
yy += 1
num += 1
if num >= 5:
win(wb1)
# 從右上方的棋子開始檢查,記錄顏色相同得棋子數(shù)量
# 先找到最同一條線上右上方的同色棋子
xx = x
yy = y
while True:
if xx == 0:
if alist[xx][yy] != alist[x][y]:
xx += 1
yy -= 1
break
elif yy == 18:
if alist[xx][yy] != alist[x][y]:
xx += 1
yy -= 1
break
elif alist[xx][yy] != alist[x][y]:
xx += 1
yy -= 1
break
else:
xx -= 1
yy += 1
num = 0
while True:
if xx == 18:
if alist[xx][yy] == alist[x][y]:
num += 1
break
elif yy == 0:
if alist[xx][yy] == alist[x][y]:
num += 1
break
elif alist[xx][yy] != alist[x][y]:
break
else:
xx += 1
yy -= 1
num += 1
if num >= 5:
win(wb1)
def win(wb1):
pygame.font.init()
font = pygame.font.Font(None, 70)
text = font.render(f"{wb1} won", True, (255, 255, 255))
screen.blit(text, ((655 - text.get_width()) / 2, (665 - text.get_height()) / 2))
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
init()
text = font.render("Start the game", True, (255, 255, 255))
screen.blit(text, ((655 - text.get_width()) / 2, (665 - text.get_height()) / 2))
pygame.display.update()
pygame.time.wait(500)
init()
do("black")
if __name__ == "__main__":
init()
do('black')
文章來源:http://www.zghlxwxcb.cn/news/detail-751363.html
參考資料《趣學(xué)python游戲編程》文章來源地址http://www.zghlxwxcb.cn/news/detail-751363.html
到了這里,關(guān)于讓電腦變得更聰明——用python實(shí)現(xiàn)五子棋游戲的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!