前言
中国人讲“琴棋书画”,可见下棋这种需要智慧的游戏,在古人日常生活中是极为重要的。
而说到棋,我们所熟知的有围棋、象棋、军旗、跳棋等等,当然,还有五子棋。
今天的重点也就在这五子棋之上🥂,我们来造一个Python版的五子棋解解馋吧!
小科普:
在古代,五子棋棋具虽然与围棋相类同,但是下法却是完全不同的。
五子棋是“棋类游戏,棋具与围棋相同,两人对局,轮流下子,先将五子连成一行者为胜”,有人将
五子棋称为“连五子”、“连珠”的,可能是源于《汉书》中的“日月如合璧,五星如连珠”。
相传,宋代有一个叫赵师秀的人与叫司马光的人相约下五子棋,司马光有急事未能赴约,使赵师秀
坐立不安;由此,赵师秀留下了一首名诗《有约》:
“黄梅时节家家雨,青草池塘处处蛙。有约不来过夜半,闲敲棋子落灯花。”
由此可见,五子棋在我国古代社会中的影响广泛,人们对它也是十分喜好的。
话有点儿多,那我们开始吧——
Show Time
事情是这样子的:
大家好,小编今天水群的时候,刷到个极其离谱的视频🤳
卧槽,这黑棋子咋还裂变上了😲
五子棋还能这么下的吗?
要是我学会了这招
秒个小朋友可大有希望呀!
————————————
视频这一坨裂变的黑棋怼过去
别说五子,十连珠都有了吧
算了,五子棋就五子棋吧
小编先开一把,爽就完事了✊!
1)背景环境
本文是基于Pygame游戏模块写的界面五子棋小游戏。
安装Pygame:
pip install pygame
C:\Users> pip install pygame
Collecting pygame
Downloading https://files.pythonhosted.org/packages/3e/f5/feabd88a2856ec86166a897b62bfad828bfe7a94a27cbd7ebf07fd
70399/pygame-1.9.4-cp37-cp37m-win_amd64.whl (4.2MB)
100% |██████████████████████████| 4.2MB 6.6MB/s
Installing collected packages: pygam
Successfully installed pygame-1.9.4
Successfully即是已经安装成功。
Pygame常用模块:
模块名 功能 pygame.cdrom 访问光驱 pygame.cursors 加载光标 pygame.display 访问显示设备 pygame.draw 绘制形状、线和点 pygame.event 管理事件 pygame.font 使用字体 pygame.image 加载和存储图片 pygame.joystick 使用游戏手柄或者类似的东西 pygame.key 读取键盘按键 pygame.mixer 声音 pygame.mouse 鼠标 pygame.movie 播放视频 pygame.music 播放音频 pygame.overlay 访问高级视频叠加 pygame.rect 管理矩形区域 pygame.scrap 本地剪贴板访问 pygame.sndarray 操作声音数据 pygame.sprite 操作移动图像 pygame.surface 管理图像和屏幕 pygame.surfarray 管理点阵图像数据 pygame.time 管理时间和帧信息 pygame.transform 缩放和移动图像 |
背景素材:(仅图片)
2)代码演示
主程序:
import pygame
from drawing import Display
from status import State
from setup import *
def main():
""" 主程序 """
state = State()
display = Display(state.screen)
display.drawChessBoard()
display.drawRightSide()
while True:
checkEvents(state, display)
state.clock.tick(FPS)
pygame.display.update()
# noinspection PyPep8Naming
def checkEvents(state, display):
""" 检查事件程序 """
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_F1:
state.reStart()
display.drawChessBoard()
display.drawRightSide()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1 and state.startGame():
human_piece = state.humanPiece()
if human_piece:
display.drawMain(human_piece[0], human_piece[1], human_piece[2], human_piece[3], human_piece[4])
if not human_piece[3]:
return
pygame.display.flip()
ai_piece = state.aiPiece()
display.drawMain(ai_piece[0], ai_piece[1], ai_piece[2], ai_piece[3], ai_piece[4])
pygame.display.flip()
if __name__ == '__main__':
main()
运行界面:棋盘棋子界面等绘制。
import pygame
from setup import *
# noinspection PyPep8Naming
class Display(object):
def __init__(self, screen):
pygame.init()
self.screen = screen
self.black = pygame.image.load(BLACK_IMG)
self.white = pygame.image.load(WHITE_IMG)
self.black_small = pygame.image.load(BLACK_SMALL_IMG)
self.white_small = pygame.image.load(WHITE_SMALL_IMG)
self.restart_surf = pygame.image.load(RESTART_IMG)
def drawMain(self, cur_pos, turn, result_text, starting, step_list):
""" 主显示函数 """
piece_surf = self.white if turn == 0 else self.black
self.drawPiece(cur_pos, piece_surf)
self.drawStep(step_list)
self.drawResult(result_text)
if not starting:
self.drawRestart(self.restart_surf)
else:
if turn == 0:
next_text = 'Next Black'
else:
next_text = 'Next White'
self.drawNext(next_text)
def drawStep(self, step_list):
""" 显示落子记录 """
pygame.draw.rect(self.screen, BG_COLOR,
(WINDOW_W-RIGHT_W+15, RIGHT_SIDE_CENTER_LINE_Y+10,
RIGHT_W-15, WINDOW_H-B_BORDER-RIGHT_SIDE_CENTER_LINE_Y-RIGHT_SIDE_BOTTOM_BLOCK_H), 0)
for n, value in enumerate(step_list[-10:]):
text = '{:03d}: {} X={:02d} Y={:02d}'.format(value[0], value[1], value[2][0], value[2][1])
self.drawText(text, RIGHT_SIDE_STEP_TEXT_FONT, RIGHT_SIDE_STEP_TEXT_SIZE, RIGHT_SIDE_STEP_TEXT_COLOR,
WINDOW_W-RIGHT_W+15, RIGHT_SIDE_CENTER_LINE_Y+10+n*18, 'topleft')
def drawNext(self, next_text):
""" 显示下一步的内容 """
pygame.draw.rect(self.screen, RIGHT_SIDE_COLOR,
(WINDOW_W-RIGHT_W, WINDOW_H-B_BORDER-RIGHT_SIDE_BOTTOM_BLOCK_H,
RIGHT_SIDE_BOTTOM_BLOCK_W, RIGHT_SIDE_BOTTOM_BLOCK_H), 0)
self.drawText(next_text, RIGHT_SIDE_TEXT_FONT, RIGHT_SIDE_BOTTOM_TEXT_SIZE, RIGHT_SIDE_TEXT_COLOR,
WINDOW_W - R_BORDER - RIGHT_SIDE_BOTTOM_BLOCK_W / 2,
WINDOW_H - B_BORDER - RIGHT_SIDE_BOTTOM_BLOCK_H / 2, 'center')
def drawRestart(self, restart_surf):
""" 显示按F1重新游戏 """
restart_rect = restart_surf.get_rect()
restart_rect.center = (L_BORDER+COL*SPACE/2, WINDOW_H/2)
self.screen.blit(restart_surf, restart_rect)
def drawPiece(self, cur_pos, piece_surf):
""" 画棋子 """
piece_rect = piece_surf.get_rect()
piece_rect.center = (cur_pos[0], cur_pos[1])
self.screen.blit(piece_surf, piece_rect)
def drawRightSide(self):
""" 画右边栏的屏幕 """
pygame.draw.line(self.screen, RIGHT_SIDE_COLOR, (WINDOW_W-RIGHT_W+5, T_BORDER),
(WINDOW_W-RIGHT_W+5, WINDOW_H-B_BORDER))
pygame.draw.line(self.screen, RIGHT_SIDE_COLOR, (WINDOW_W-RIGHT_W, T_BORDER+RIGHT_SIDE_BLOCK_H),
(WINDOW_W-R_BORDER, T_BORDER+RIGHT_SIDE_BLOCK_H))
pygame.draw.line(self.screen, RIGHT_SIDE_COLOR, (WINDOW_W-RIGHT_W, RIGHT_SIDE_CENTER_LINE_Y),
(WINDOW_W-R_BORDER, RIGHT_SIDE_CENTER_LINE_Y))
pygame.draw.rect(self.screen, RIGHT_SIDE_COLOR,
(WINDOW_W-RIGHT_W, T_BORDER, RIGHT_SIDE_BLOCK_W, RIGHT_SIDE_BLOCK_H), 0)
pygame.draw.rect(self.screen, RIGHT_SIDE_COLOR,
(WINDOW_W-RIGHT_W, RIGHT_SIDE_CENTER_LINE_Y-RIGHT_SIDE_BLOCK_H,
RIGHT_SIDE_BLOCK_W, RIGHT_SIDE_BLOCK_H), 0)
pygame.draw.rect(self.screen, RIGHT_SIDE_COLOR,
(WINDOW_W-RIGHT_W, WINDOW_H-B_BORDER-RIGHT_SIDE_BOTTOM_BLOCK_H,
RIGHT_SIDE_BOTTOM_BLOCK_W, RIGHT_SIDE_BOTTOM_BLOCK_H), 0)
self.drawText('Players', RIGHT_SIDE_TEXT_FONT, RIGHT_SIDE_TEXT_SIZE, RIGHT_SIDE_TEXT_COLOR,
WINDOW_W-RIGHT_W+5, T_BORDER+RIGHT_SIDE_BLOCK_H, 'bottomleft')
self.drawText('Human', RIGHT_SIDE_TEXT_FONT, RIGHT_SIDE_TEXT_SIZE, RIGHT_SIDE_STEP_TEXT_COLOR,
WINDOW_W-RIGHT_W+35, T_BORDER+RIGHT_SIDE_BLOCK_H+7, 'topleft')
self.drawText('AI', RIGHT_SIDE_TEXT_FONT, RIGHT_SIDE_TEXT_SIZE, RIGHT_SIDE_STEP_TEXT_COLOR,
WINDOW_W-RIGHT_W+35, T_BORDER+RIGHT_SIDE_BLOCK_H+27, 'topleft')
self.drawText('Step List', RIGHT_SIDE_TEXT_FONT, RIGHT_SIDE_TEXT_SIZE, RIGHT_SIDE_TEXT_COLOR,
WINDOW_W-RIGHT_W+5, RIGHT_SIDE_CENTER_LINE_Y, 'bottomleft')
self.drawText('Click Start', RIGHT_SIDE_TEXT_FONT, RIGHT_SIDE_BOTTOM_TEXT_SIZE, RIGHT_SIDE_TEXT_COLOR,
WINDOW_W - R_BORDER - RIGHT_SIDE_BOTTOM_BLOCK_W / 2,
WINDOW_H - B_BORDER - RIGHT_SIDE_BOTTOM_BLOCK_H / 2, 'center')
self.screen.blit(self.black_small, (WINDOW_W-RIGHT_W+15, T_BORDER+RIGHT_SIDE_BLOCK_H+10))
self.screen.blit(self.white_small, (WINDOW_W-RIGHT_W+15, T_BORDER+RIGHT_SIDE_BLOCK_H+30))
def drawChessBoard(self):
""" 画棋盘,分别画出横线,竖线,点,横轴数字,纵轴数字 """
self.screen.fill((128, 128, 128))
for i in range(ROW):
pygame.draw.line(self.screen, LINE_COLOR, (L_BORDER, T_BORDER+i*SPACE),
(L_BORDER+SPACE*(COL-1), T_BORDER+i*SPACE))
for j in range(COL):
pygame.draw.line(self.screen, LINE_COLOR, (L_BORDER+j*SPACE, T_BORDER),
(L_BORDER+j*SPACE, T_BORDER+SPACE*(ROW-1)))
for p in POINT_LIST:
pos = (L_BORDER+p[0]*SPACE, T_BORDER+p[1]*SPACE)
pygame.draw.circle(self.screen, LINE_COLOR, pos, POINT_R)
for m in range(1, ROW):
self.drawText(str(m), AXIS_TEXT_FONT, AXIS_TEXT_SIZE, AXIS_TEXT_COLOR,
L_BORDER-AXIS_TEXT_SIZE/2, T_BORDER+AXIS_TEXT_SIZE/2+m*SPACE, 'bottomright')
for n in range(0, COL):
self.drawText(X_AXIS_LIST[n], AXIS_TEXT_FONT, AXIS_TEXT_SIZE, AXIS_TEXT_COLOR,
L_BORDER+n*SPACE, T_BORDER-int(AXIS_TEXT_SIZE/1.5), 'center')
def drawText(self, text, font, size, color, x, y, site):
""" 在窗口中指定位置显示文字 """
text_font = pygame.font.Font(font, size)
text_surf = text_font.render(text, True, color)
text_rect = text_surf.get_rect()
exec('text_rect.{}=({},{})'.format(site, x, y))
self.screen.blit(text_surf, text_rect)
def drawResult(self, result_text):
""" 显示结果文字 """
pygame.draw.rect(self.screen, RIGHT_SIDE_COLOR,
(WINDOW_W-RIGHT_W, WINDOW_H-B_BORDER-RIGHT_SIDE_BOTTOM_BLOCK_H,
RIGHT_SIDE_BOTTOM_BLOCK_W, RIGHT_SIDE_BOTTOM_BLOCK_H), 0)
self.drawText(result_text, RIGHT_SIDE_TEXT_FONT, RIGHT_SIDE_BOTTOM_TEXT_SIZE, RIGHT_SIDE_TEXT_COLOR,
WINDOW_W-R_BORDER-RIGHT_SIDE_BOTTOM_BLOCK_W/2,
WINDOW_H-B_BORDER-RIGHT_SIDE_BOTTOM_BLOCK_H/2, 'center')
人机对战:我们来设定下机器人的参数。
from setup import *
# noinspection PyPep8Naming
class AI(object):
# noinspection PyUnusedLocal
def __init__(self, maps):
self.maps = maps
self.is_end = False
@staticmethod
def inBoard(x, y):
""" 判断当前位置是否在棋盘内部 """
return True if ROW > x >= 0 and COL > y >= 0 else False
def downOk(self, x, y):
""" 判断当前位置是否可以落子 """
return True if self.inBoard(x, y) and self.maps[x][y] is None else False
def sameColor(self, x, y, i):
""" 判断当前位置是否与给定的棋子(i值)相同 """
return True if self.inBoard(x, y) and self.maps[x][y] == i else False
def numInLine(self, x, y, d):
""" 在给定的方向direct(direct区分正负)上,和该点相同棋子的个数 """
i = x + DX[d]
j = y + DY[d]
same_num = 0
piece = self.maps[x][y]
if piece is None:
return 0
while self.sameColor(i, j, piece):
same_num = same_num + 1
i = i + DX[d]
j = j + DY[d]
return same_num
def numOfSameKey(self, x, y, d, i, key, same_key):
""" 统计在d方向上,和key值相同的点的个数,即和key同色的连子个数 """
if i == 1:
while self.sameColor(x + DX[d] * i, y + DY[d] * i, key):
same_key += 1
i += 1
elif i == -1:
while self.sameColor(x + DX[d] * i, y + DY[d] * i, key):
same_key += 1
i -= 1
return same_key, i
def judgeResult(self, x, y):
""" 从八个方向判断是否有五子相连的情况, 先判断是否有五子相连,再判断平局 """
piece = self.maps[x][y]
for d in range(8):
same_key, i = self.numOfSameKey(x, y, d, 1, piece, 1)
if same_key == 5:
if piece == '1':
return 'B'
elif piece == '0':
return 'W'
none_count = 0
for row in self.maps:
for i in row:
if i is None:
none_count += 1
if none_count == 0:
return 'T'
return 'C'
def liveFour(self, x, y):
""" 该点四个方向里(即v不区分正负),活四局势的个数 """
key = self.maps[x][y]
s = 0
for d in range(4):
same_key = 1
same_key, i = self.numOfSameKey(x, y, d, 1, key, same_key)
if not self.downOk(x + DX[d] * i, y + DY[d] * i):
continue
same_key, i = self.numOfSameKey(x, y, d, -1, key, same_key)
if not self.downOk(x + DX[d] * i, y + DX[d] * i):
continue
if same_key == 4:
s = s + 1
return s
def chongFour(self, x, y):
""" 该点八个方向里(即v区分正负),冲四局势的个数 """
key = self.maps[x][y]
s = 0
for d in range(8):
same_key = 0
flag = True
i = 1
while self.sameColor(x+DX[d]*i, y+DY[d]*i, key) or flag:
if not self.sameColor(x+DX[d]*i, y+DY[d]*i, key):
if flag and self.inBoard(x+DX[d]*i, y+DY[d]*i) and self.maps[x+DX[d]*i][y+DY[d]*i] is not None:
same_key -= 10
flag = False
same_key += 1
i += 1
i -= 1
if not self.inBoard(x+DX[d]*i, y+DY[d]*i):
continue
same_key, i = self.numOfSameKey(x, y, d, -1, key, same_key)
if same_key == 4:
s += 1
return s - self.liveFour(x, y) * 2
def liveThree(self, x, y):
""" 该点四个方向里活三,以及八个方向里断三的个数 """
key = self.maps[x][y]
s = 0
for d in range(4):
same_key = 1
same_key, i = self.numOfSameKey(x, y, d, 1, key, same_key)
if not self.downOk(x+DX[d]*i, y+DY[d]*i):
continue
if not self.downOk(x+DX[d]*(i+1), y+DY[d]*(i+1)):
continue
same_key, i = self.numOfSameKey(x, y, d, -1, key, same_key)
if not self.downOk(x+DX[d]*i, y+DY[d]*i):
continue
if not self.downOk(x+DX[d]*(i-1), y+DX[d]*(i-1)):
continue
if same_key == 3:
s += 1
for d in range(8):
same_key = 0
flag = True
i = 1
while self.sameColor(x+DX[d]*i, y+DY[d]*i, key) or flag:
if not self.sameColor(x+DX[d]*i, y+DY[d]*i, key):
if flag and self.inBoard(x+DX[d]*i, y+DY[d]*i) and self.maps[x+DX[d]*i][y+DY[d]*i] is not None:
same_key -= 10
flag = False
same_key += 1
i += 1
if not self.downOk(x+DX[d]*i, y+DY[d]*i):
continue
if self.inBoard(x+DX[d]*(i-1), y+DX[d]*(i-1)) and self.maps[x+DX[d]*(i-1)][y+DX[d]*(i-1)] is None:
continue
same_key, i = self.numOfSameKey(x, y, d, -1, key, same_key)
if not self.downOk(x+DX[d]*i, y+DY[d]*i):
continue
if same_key == 3:
s += 1
return s
def gameOver(self, x, y):
""" 如果有五子连线,估分最大10000 """
for d in range(4):
if (self.numInLine(x, y, d) + self.numInLine(x, y, d + 4)) >= 4:
return True
return False
def getScore(self, x, y):
""" 主评估函数,返回评估得分 """
if self.gameOver(x, y):
return 10000
score = self.liveFour(x, y) * 1000 + (self.chongFour(x, y) + self.liveThree(x, y)) * 100
for d in range(8):
if self.inBoard(x+DX[d], y+DY[d]) and self.maps[x+DX[d]][y+DY[d]] is not None:
score = score + 1
return score
def layerOne(self):
""" 博弈树第一层,极大值,自己层 """
l1_max = -100000
if self.maps[int((COL-1)/2)][int((ROW-1)/2)] is None:
return int((COL-1)/2), int((ROW-1)/2)
pos_x = -1
pos_y = -1
for y in [8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 0]:
for x in [8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 0]:
if not self.downOk(x, y):
continue
self.maps[x][y] = '0'
score = self.getScore(x, y)
if score == 0:
self.maps[x][y] = None
continue
if score == 10000:
return x, y
score = self.layerTwo(l1_max)
self.maps[x][y] = None
if score > l1_max:
l1_max = score
pos_x = x
pos_y = y
print('{}, score:{}'.format((pos_x, pos_y), l1_max))
return pos_x, pos_y
def layerTwo(self, l1_max):
""" 博弈树第二层,极小值,对手层 """
l2_min = 100000
for y in [8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 0]:
for x in [8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 0]:
if not self.downOk(x, y):
continue
self.maps[x][y] = '1'
score = self.getScore(x, y)
if score == 0:
self.maps[x][y] = None
continue
if score == 10000:
self.maps[x][y] = None
return -10000
score = self.layerThree(score, l2_min)
if score < l1_max:
self.maps[x][y] = None
return -10000
self.maps[x][y] = None
if score < l2_min:
l2_min = score
return l2_min
def layerThree(self, l2_score, l2_min):
""" 博弈树第三层 """
three_max = -100000
for y in [8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 0]:
for x in [8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 0]:
if not self.downOk(x, y):
continue
self.maps[x][y] = '0'
score = self.getScore(x, y)
if score == 0:
self.maps[x][y] = None
continue
if score == 10000:
self.maps[x][y] = None
return -10000
if score - l2_score * 2 > l2_min:
self.maps[x][y] = None
return 10000
self.maps[x][y] = None
if score - l2_score * 2 > three_max:
three_max = score - l2_score * 2
return three_max
@staticmethod
def changePos(x, y):
""" 首先判断坐标是否在棋盘内,然后将鼠标坐标转换为棋盘上的点坐标,否则返回False
:return: [(符合棋盘点的坐标), 轮换值, 胜负结果]
"""
# 判断坐标是否在棋盘内
if x < L_BORDER - PIECE_NEAR or x > L_BORDER + (ROW-1) * SPACE + PIECE_NEAR:
return False
if y < T_BORDER - PIECE_NEAR or y > T_BORDER + (COL-1) * SPACE + PIECE_NEAR:
return False
# 将坐标转换为符合棋盘上点的坐标
x_int = (x - L_BORDER) // SPACE
y_int = (y - T_BORDER) // SPACE
if x - L_BORDER - PIECE_NEAR <= x_int * SPACE:
x_c = L_BORDER + x_int * SPACE
elif x - L_BORDER + PIECE_NEAR >= (x_int + 1) * SPACE:
x_c = L_BORDER + (x_int + 1) * SPACE
else:
return False
if y - T_BORDER - PIECE_NEAR <= y_int * SPACE:
y_c = T_BORDER + y_int * SPACE
elif y - T_BORDER + PIECE_NEAR >= (y_int + 1) * SPACE:
y_c = T_BORDER + (y_int + 1) * SPACE
else:
return False
# 将坐标转换为数组坐标,需要将x, y转置
x_n = (y_c - T_BORDER) // SPACE
y_n = (x_c - L_BORDER) // SPACE
return [(x_c, y_c), (x_n, y_n)]
3)效果如下
3.1 游戏界面
3.2 游戏胜利
(这个机器人不大聪明的亚子,直接胜利毫无压力!哈哈哈)
小结
“棋”思妙想,果断的靠自己的智慧取胜🍖🍖。