本文主要内容:python Pygame alpha-beta剪枝算法 玩中国象棋 相当于入门水平,我还是能下赢它
完整简洁并有详细注释的代码:
python Pygame alpha-beta剪枝算法 玩中国象棋 相当于入门水平
运行入口为:chinachess.py
算法和代码解释请查看参考文献里的文章
1、界面演示
2、关键代码
可视化中国象棋运行入口
import time
import pygame
import ChinaChess.constants
from ChinaChess import constants, pieces, computer
import ChinaChess.computer
import ChinaChess.my_game as mg
'''
此文件为可视化中国象棋运行入口
'''
class MainGame():
window = None
Start_X = ChinaChess.constants.Start_X
Start_Y = ChinaChess.constants.Start_Y
Line_Span = ChinaChess.constants.Line_Span
Max_X = Start_X + 8 * Line_Span
Max_Y = Start_Y + 9 * Line_Span
from_x = 0
from_y = 0
to_x = 0
to_y = 0
clickx = -1
clicky = -1
mgInit = mg.my_game()
player1Color = constants.player1Color
player2Color = constants.player2Color
Putdownflag = player1Color
piecesSelected = None
piecesList = []
def start_game(self):
MainGame.window = pygame.display.set_mode([constants.SCREEN_WIDTH, constants.SCREEN_HEIGHT])
pygame.display.set_caption("中国象棋")
# 把所有棋子摆好
self.piecesInit()
while True:
time.sleep(0.1)
# 获取事件
MainGame.window.fill(constants.BG_COLOR)
self.drawChessboard()
# 遍历所有棋子,显示所有棋子
self.piecesDisplay()
# 判断游戏胜利
self.VictoryOrDefeat()
# 轮到电脑了
self.Computerplay()
# 获取所有的事件
self.getEvent()
pygame.display.update()
pygame.display.flip()
def drawChessboard(self):
mid_end_y = MainGame.Start_Y + 4 * MainGame.Line_Span
min_start_y = MainGame.Start_Y + 5 * MainGame.Line_Span
for i in range(0, 9):
x = MainGame.Start_X + i * MainGame.Line_Span
if i == 0 or i == 8:
pygame.draw.line(MainGame.window, constants.BLACK, [x, MainGame.Start_Y], [x, MainGame.Max_Y], 1)
else:
pygame.draw.line(MainGame.window, constants.BLACK, [x, MainGame.Start_Y], [x, mid_end_y], 1)
pygame.draw.line(MainGame.window, constants.BLACK, [x, min_start_y], [x, MainGame.Max_Y], 1)
for i in range(0, 10):
y = MainGame.Start_Y + i * MainGame.Line_Span
pygame.draw.line(MainGame.window, constants.BLACK, [MainGame.Start_X, y], [MainGame.Max_X, y], 1)
speed_dial_start_x = MainGame.Start_X + 3 * MainGame.Line_Span
speed_dial_end_x = MainGame.Start_X + 5 * MainGame.Line_Span
speed_dial_y1 = MainGame.Start_Y + 0 * MainGame.Line_Span
speed_dial_y2 = MainGame.Start_Y + 2 * MainGame.Line_Span
speed_dial_y3 = MainGame.Start_Y + 7 * MainGame.Line_Span
speed_dial_y4 = MainGame.Start_Y + 9 * MainGame.Line_Span
pygame.draw.line(MainGame.window, constants.BLACK, [speed_dial_start_x, speed_dial_y1],
[speed_dial_end_x, speed_dial_y2], 1)
pygame.draw.line(MainGame.window, constants.BLACK, [speed_dial_start_x, speed_dial_y2],
[speed_dial_end_x, speed_dial_y1], 1)
pygame.draw.line(MainGame.window, constants.BLACK, [speed_dial_start_x, speed_dial_y3],
[speed_dial_end_x, speed_dial_y4], 1)
pygame.draw.line(MainGame.window, constants.BLACK, [speed_dial_start_x, speed_dial_y4],
[speed_dial_end_x, speed_dial_y3], 1)
def piecesInit(self):
MainGame.piecesList.append(pieces.Rooks(MainGame.player2Color, 0, 0))
MainGame.piecesList.append(pieces.Rooks(MainGame.player2Color, 8, 0))
MainGame.piecesList.append(pieces.Elephants(MainGame.player2Color, 2, 0))
MainGame.piecesList.append(pieces.Elephants(MainGame.player2Color, 6, 0))
MainGame.piecesList.append(pieces.King(MainGame.player2Color, 4, 0))
MainGame.piecesList.append(pieces.Knighs(MainGame.player2Color, 1, 0))
MainGame.piecesList.append(pieces.Knighs(MainGame.player2Color, 7, 0))
MainGame.piecesList.append(pieces.Cannons(MainGame.player2Color, 1, 2))
MainGame.piecesList.append(pieces.Cannons(MainGame.player2Color, 7, 2))
MainGame.piecesList.append(pieces.Mandarins(MainGame.player2Color, 3, 0))
MainGame.piecesList.append(pieces.Mandarins(MainGame.player2Color, 5, 0))
MainGame.piecesList.append(pieces.Pawns(MainGame.player2Color, 0, 3))
MainGame.piecesList.append(pieces.Pawns(MainGame.player2Color, 2, 3))
MainGame.piecesList.append(pieces.Pawns(MainGame.player2Color, 4, 3))
MainGame.piecesList.append(pieces.Pawns(MainGame.player2Color, 6, 3))
MainGame.piecesList.append(pieces.Pawns(MainGame.player2Color, 8, 3))
MainGame.piecesList.append(pieces.Rooks(MainGame.player1Color, 0, 9))
MainGame.piecesList.append(pieces.Rooks(MainGame.player1Color, 8, 9))
MainGame.piecesList.append(pieces.Elephants(MainGame.player1Color, 2, 9))
MainGame.piecesList.append(pieces.Elephants(MainGame.player1Color, 6, 9))
MainGame.piecesList.append(pieces.King(MainGame.player1Color, 4, 9))
MainGame.piecesList.append(pieces.Knighs(MainGame.player1Color, 1, 9))
MainGame.piecesList.append(pieces.Knighs(MainGame.player1Color, 7, 9))
MainGame.piecesList.append(pieces.Cannons(MainGame.player1Color, 1, 7))
MainGame.piecesList.append(pieces.Cannons(MainGame.player1Color, 7, 7))
MainGame.piecesList.append(pieces.Mandarins(MainGame.player1Color, 3, 9))
MainGame.piecesList.append(pieces.Mandarins(MainGame.player1Color, 5, 9))
MainGame.piecesList.append(pieces.Pawns(MainGame.player1Color, 0, 6))
MainGame.piecesList.append(pieces.Pawns(MainGame.player1Color, 2, 6))
MainGame.piecesList.append(pieces.Pawns(MainGame.player1Color, 4, 6))
MainGame.piecesList.append(pieces.Pawns(MainGame.player1Color, 6, 6))
MainGame.piecesList.append(pieces.Pawns(MainGame.player1Color, 8, 6))
def piecesDisplay(self):
# 遍历所有棋子,显示所有棋子
for item in MainGame.piecesList:
item.displaypieces(MainGame.window)
# MainGame.window.blit(item.image, item.rect)
def getEvent(self):
# 获取所有的事件
eventList = pygame.event.get()
for event in eventList:
if event.type == pygame.QUIT:
self.endGame()
elif event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
mouse_x = pos[0]
mouse_y = pos[1]
if (mouse_x > MainGame.Start_X - MainGame.Line_Span / 2 and mouse_x < MainGame.Max_X + MainGame.Line_Span / 2) and (
mouse_y > MainGame.Start_Y - MainGame.Line_Span / 2 and mouse_y < MainGame.Max_Y + MainGame.Line_Span / 2):
print(str(mouse_x) + "" + str(mouse_y))
print(str(MainGame.Putdownflag))
if MainGame.Putdownflag != MainGame.player1Color:
return
click_x = round((mouse_x - MainGame.Start_X) / MainGame.Line_Span)
click_y = round((mouse_y - MainGame.Start_Y) / MainGame.Line_Span)
click_mod_x = (mouse_x - MainGame.Start_X) % MainGame.Line_Span
click_mod_y = (mouse_y - MainGame.Start_Y) % MainGame.Line_Span
if abs(click_mod_x - MainGame.Line_Span / 2) >= 5 and abs(
click_mod_y - MainGame.Line_Span / 2) >= 5:
print("有效点:x=" + str(click_x) + " y=" + str(click_y))
# 有效点击点
self.from_x = MainGame.clickx
self.from_y = MainGame.clicky
self.to_x = click_x
self.to_y = click_y
print(self.from_x)
print(self.from_y)
MainGame.clickx = click_x
MainGame.clicky = click_y
self.PutdownPieces(MainGame.player1Color, click_x, click_y)
else:
print("out")
def PutdownPieces(self, t, x, y):
selectfilter = list(
filter(lambda cm: cm.x == x and cm.y == y and cm.player == MainGame.player1Color, MainGame.piecesList))
if len(selectfilter):
MainGame.piecesSelected = selectfilter[0]
return
if MainGame.piecesSelected:
print("MainGame.piecesSelected")
arr = pieces.listPiecestoArr(MainGame.piecesList)
if MainGame.piecesSelected.canmove(arr, x, y):
self.PiecesMove(MainGame.piecesSelected, x, y)
MainGame.Putdownflag = MainGame.player2Color
else:
fi = filter(lambda p: p.x == x and p.y == y, MainGame.piecesList)
listfi = list(fi)
if len(listfi) != 0:
MainGame.piecesSelected = listfi[0]
def PiecesMove(self, pieces, x, y):
for item in MainGame.piecesList:
if item.x == x and item.y == y:
MainGame.piecesList.remove(item)
pieces.x = x
pieces.y = y
print("move to " + str(x) + " " + str(y))
return True
def Computerplay(self):
if MainGame.Putdownflag == MainGame.player2Color:
print("轮到电脑了")
computermove = computer.getPlayInfo(MainGame.piecesList, self.from_x, self.from_y, self.to_x, self.to_y,
self.mgInit)
if computer == None:
return
piecemove = None
for item in MainGame.piecesList:
if item.x == computermove[0] and item.y == computermove[1]:
piecemove = item
self.PiecesMove(piecemove, computermove[2], computermove[3])
MainGame.Putdownflag = MainGame.player1Color
# 判断游戏胜利
def VictoryOrDefeat(self):
result = [MainGame.player1Color, MainGame.player2Color]
for item in MainGame.piecesList:
if type(item) == pieces.King:
if item.player == MainGame.player1Color:
result.remove(MainGame.player1Color)
if item.player == MainGame.player2Color:
result.remove(MainGame.player2Color)
if len(result) == 0:
return
if result[0] == MainGame.player1Color:
txt = "你失败了哦!"
else:
txt = "你胜利了哦!"
MainGame.window.blit(self.getTextSuface("%s" % txt), (constants.SCREEN_WIDTH - 100, 200))
MainGame.Putdownflag = constants.overColor
def getTextSuface(self, text):
pygame.font.init()
print(pygame.font.get_fonts())
font = pygame.font.SysFont('kaiti', 18)
txt = font.render(text, True, constants.TEXT_COLOR)
return txt
def endGame(self):
print("退出")
exit()
if __name__ == '__main__':
MainGame().start_game()
非可视化中国象棋运行入口
import ChinaChess.my_chess as mc
import ChinaChess.chess_constants as cc
import numpy as np
'''
此文件为非可视化中国象棋运行入口
'''
class HistoryTable: # 历史启发算法
def __init__(self):
self.table = np.zeros((2, 90, 90))
def get_history_score(self, who, step):
return self.table[who, step.from_x * 9 + step.from_y, step.to_x * 9 + step.to_y]
def add_history_score(self, who, step, depth):
self.table[who, step.from_x * 9 + step.from_y, step.to_x * 9 + step.to_y] += 2 << depth
class Relation:
def __init__(self):
self.chess_type = 0
self.num_attack = 0
self.num_guard = 0
self.num_attacked = 0
self.num_guarded = 0
self.attack = [0, 0, 0, 0, 0, 0]
self.attacked = [0, 0, 0, 0, 0, 0]
self.guard = [0, 0, 0, 0, 0, 0]
self.guarded = [0, 0, 0, 0, 0, 0]
class my_game:
def __init__(self):
self.board = mc.chess_board()
self.max_depth = cc.max_depth
self.history_table = HistoryTable()
self.best_move = mc.step()
self.cnt = 0
def alpha_beta(self, depth, alpha, beta): # alpha-beta剪枝,alpha是大可能下界,beta是最小可能上界
who = (self.max_depth - depth) % 2 # 那个玩家
if self.is_game_over(who): # 判断是否游戏结束,如果结束了就不用搜了
return cc.min_val
if depth == 1: # 搜到指定深度了,也不用搜了
# print(self.evaluate(who))
return self.evaluate(who)
move_list = self.board.generate_move(who) # 返回所有能走的方法
# 利用历史表0
for i in range(len(move_list)):
move_list[i].score = self.history_table.get_history_score(who, move_list[i])
move_list.sort() # 为了让更容易剪枝利用历史表得分进行排序
# for item in move_list:
# print(item.score)
best_step = move_list[0]
score_list = []
for step in move_list:
temp = self.move_to(step)
score = -self.alpha_beta(depth - 1, -beta, -alpha) # 因为是一层选最大一层选最小,所以利用取负号来实现
score_list.append(score)
self.undo_move(step, temp)
if score > alpha:
alpha = score
if depth == self.max_depth:
self.best_move = step
best_step = step
if alpha >= beta:
best_step = step
break
# print(score_list)
# 更新历史表
if best_step.from_x != -1:
self.history_table.add_history_score(who, best_step, depth)
return alpha
def evaluate(self, who): # who表示该谁走,返回评分值
self.cnt += 1
# print('====================================================================================')
relation_list = self.init_relation_list()
base_val = [0, 0]
pos_val = [0, 0]
mobile_val = [0, 0]
relation_val = [0, 0]
for x in range(9):
for y in range(10):
now_chess = self.board.board[x][y]
type = now_chess.chess_type
if type == 0:
continue
# now = 0 if who else 1
now = now_chess.belong
pos = x * 9 + y
temp_move_list = self.board.get_chess_move(x, y, now, True)
# 计算基础价值
base_val[now] += cc.base_val[type]
# 计算位置价值
if now == 0: # 如果是要求最大值的玩家
pos_val[now] += cc.pos_val[type][pos]
else:
pos_val[now] += cc.pos_val[type][89 - pos]
# 计算机动性价值,记录关系信息
for item in temp_move_list:
# print('----------------')
# print(item)
temp_chess = self.board.board[item.to_x][item.to_y] # 目的位置的棋子
if temp_chess.chess_type == cc.kong: # 如果是空,那么加上机动性值
# print('ok')
mobile_val[now] += cc.mobile_val[type]
# print(mobile_val[now])
continue
elif temp_chess.belong != now: # 如果不是自己一方的棋子
# print('ok1')
if temp_chess.chess_type == cc.jiang: # 如果能吃了对方的将,那么就赢了
if temp_chess.belong != who:
# print(self.board.board[item.from_x][item.from_y])
# print(temp_chess)
# print(item)
# print('bug here')
return cc.max_val
else:
relation_val[1 - now] -= 20 # 如果不能,那么就相当于被将军,对方要减分
continue
# 记录攻击了谁
relation_list[x][y].attack[relation_list[x][y].num_attack] = temp_chess.chess_type
relation_list[x][y].num_attack += 1
relation_list[item.to_x][item.to_y].chess_type = temp_chess.chess_type
# print(item)
# 记录被谁攻击
# if item.to_x == 4 and item.to_y == 1:
# print('--------------')
# print(now_chess.chess_type)
# print(item.from_x, item.from_y)
# print('*************')
# print(temp_chess.chess_type)
# print(item.to_x, item.to_y)
# print(relation_list[item.to_x][item.to_y].num_attacked)
# print([relation_list[item.to_x][item.to_y].attacked[j] for j in range(relation_list[item.to_x][item.to_y].num_attacked)])
# if relation_list[item.to_x][item.to_y].num_attacked == 5:
# print('###################')
# self.board.print_board()
# print('###################')
relation_list[item.to_x][item.to_y].attacked[relation_list[item.to_x][item.to_y].num_attacked] = type
relation_list[item.to_x][item.to_y].num_attacked += 1
elif temp_chess.belong == now:
# print('ok2')
if temp_chess.chess_type == cc.jiang: # 保护自己的将没有意义,直接跳过
continue
# 记录关系信息-guard
# print(item)
# if item.to_x == 4 and item.to_y == 1:
# print('--------------')
# print(now_chess.chess_type)
# print(item)
# print('*************')
# print(temp_chess.chess_type)
# print(relation_list[item.to_x][item.to_y].num_guarded)
# print([relation_list[item.to_x][item.to_y].guarded[j] for j in range(relation_list[item.to_x][item.to_y].num_guarded)])
# if relation_list[item.to_x][item.to_y].num_guarded == 5:
# print('###################')
# print(x, y, who)
# self.board.print_board(True)
# print('###################')
relation_list[x][y].guard[relation_list[x][y].num_guard] = temp_chess
relation_list[x][y].num_guard += 1
relation_list[item.to_x][item.to_y].chess_type = temp_chess.chess_type
relation_list[item.to_x][item.to_y].guarded[
relation_list[item.to_x][item.to_y].num_guarded] = type
relation_list[item.to_x][item.to_y].num_guarded += 1
# relation_list[x][y].chess_type = type
for x in range(9):
for y in range(10):
num_attacked = relation_list[x][y].num_attacked
num_guarded = relation_list[x][y].num_guarded
now_chess = self.board.board[x][y]
type = now_chess.chess_type
now = now_chess.belong
unit_val = cc.base_val[now_chess.chess_type] >> 3
sum_attack = 0 # 被攻击总子力
sum_guard = 0
min_attack = 999 # 最小的攻击者
max_attack = 0 # 最大的攻击者
max_guard = 0
flag = 999 # 有没有比这个子的子力小的
if type == cc.kong:
continue
# 统计攻击方的子力
for i in range(num_attacked):
temp = cc.base_val[relation_list[x][y].attacked[i]]
flag = min(flag, min(temp, cc.base_val[type]))
min_attack = min(min_attack, temp)
max_attack = max(max_attack, temp)
sum_attack += temp
# 统计防守方的子力
for i in range(num_guarded):
temp = cc.base_val[relation_list[x][y].guarded[i]]
max_guard = max(max_guard, temp)
sum_guard += temp
if num_attacked == 0:
relation_val[now] += 5 * relation_list[x][y].num_guarded
else:
muti_val = 5 if who != now else 1
if num_guarded == 0: # 如果没有保护
relation_val[now] -= muti_val * unit_val
else: # 如果有保护
if flag != 999: # 存在攻击者子力小于被攻击者子力,对方将愿意换子
relation_val[now] -= muti_val * unit_val
relation_val[1 - now] -= muti_val * (flag >> 3)
# 如果是二换一, 并且最小子力小于被攻击者子力与保护者子力之和, 则对方可能以一子换两子
elif num_guarded == 1 and num_attacked > 1 and min_attack < cc.base_val[type] + sum_guard:
relation_val[now] -= muti_val * unit_val
relation_val[now] -= muti_val * (sum_guard >> 3)
relation_val[1 - now] -= muti_val * (flag >> 3)
# 如果是三换二并且攻击者子力较小的二者之和小于被攻击者子力与保护者子力之和,则对方可能以两子换三子
elif num_guarded == 2 and num_attacked == 3 and sum_attack - max_attack < cc.base_val[
type] + sum_guard:
relation_val[now] -= muti_val * unit_val
relation_val[now] -= muti_val * (sum_guard >> 3)
relation_val[1 - now] -= muti_val * ((sum_attack - max_attack) >> 3)
# 如果是n换n,攻击方与保护方数量相同并且攻击者子力小于被攻击者子力与保护者子力之和再减去保护者中最大子力,则对方可能以n子换n子
elif num_guarded == num_attacked and sum_attack < cc.base_val[
now_chess.chess_type] + sum_guard - max_guard:
relation_val[now] -= muti_val * unit_val
relation_val[now] -= muti_val * ((sum_guard - max_guard) >> 3)
relation_val[1 - now] -= sum_attack >> 3
# print('-------------------------')
# print(base_val[0], pos_val[0], mobile_val[0], relation_val[0])
# print(base_val[1], pos_val[1], mobile_val[1], relation_val[1])
my_max_val = base_val[0] + pos_val[0] + mobile_val[0] + relation_val[0]
my_min_val = base_val[1] + pos_val[1] + mobile_val[1] + relation_val[1]
if who == 0:
return my_max_val - my_min_val
else:
return my_min_val - my_max_val
def init_relation_list(self):
res_list = []
for i in range(9):
res_list.append([])
for j in range(10):
res_list[i].append(Relation())
return res_list
def is_game_over(self, who): # 判断游戏是否结束
for i in range(9):
for j in range(10):
if self.board.board[i][j].chess_type == cc.jiang:
if self.board.board[i][j].belong == who:
return False
return True
def move_to(self, step, flag=False): # 移动棋子
belong = self.board.board[step.to_x][step.to_y].belong
chess_type = self.board.board[step.to_x][step.to_y].chess_type
temp = mc.chess(belong, chess_type)
# if flag:
# self.board.print_board()
# print(self.board.board[step.to_x][step.to_y].chess_type)
self.board.board[step.to_x][step.to_y].chess_type = self.board.board[step.from_x][step.from_y].chess_type
# if flag:
# print(self.board.board[step.from_x][step.from_y].chess_type)
# print(self.board.board[step.to_x][step.to_y].chess_type)
# print(step.to_x, step.to_y)
self.board.board[step.to_x][step.to_y].belong = self.board.board[step.from_x][step.from_y].belong
self.board.board[step.from_x][step.from_y].chess_type = cc.kong
self.board.board[step.from_x][step.from_y].belong = -1
return temp
def undo_move(self, step, chess): # 恢复棋子
self.board.board[step.from_x][step.from_y].belong = self.board.board[step.to_x][step.to_y].belong
self.board.board[step.from_x][step.from_y].chess_type = self.board.board[step.to_x][step.to_y].chess_type
self.board.board[step.to_x][step.to_y].belong = chess.belong
self.board.board[step.to_x][step.to_y].chess_type = chess.chess_type
if __name__ == "__main__":
game = my_game()
game.board.print_board()
while (True):
from_x = int(input())
from_y = int(input())
to_x = int(input())
to_y = int(input())
s = mc.step(from_x, from_y, to_x, to_y)
game.board.print_board()
game.alpha_beta(game.max_depth, cc.min_val, cc.max_val)
print(game.best_move)
game.move_to(game.best_move)
game.move_to(s)
game.board.print_board()
3、参考文献
以下的代码冗长且复杂,文件多,我对他们的代码进行了注释并简化了不少,所以下载我的就行
中国象棋python实现(拥有完整源代码) Alpha-beta剪枝+GUI+历史启发式+有普通人棋力
转载:https://blog.csdn.net/qq_30803353/article/details/114787872
查看评论