我用python的pygame写了一部中国象棋的人机小游戏。
界面是这样的:
步骤如下
-
首先准备如下素材:
- 然后以一种小学生都能看懂的写代码方式来写AI和UI逻辑,方便小学生学习。
(莫欺少年穷,勿喷代码low。你看你代码,儿童能读懂?)
import time, random
import pygame
from pygame.locals import *
#============================================AI===============================================
##棋子编号
棋子将 = 0
棋子士 = 1
棋子象 = 2
棋子马 = 3
棋子车 = 4
棋子炮 = 5
棋子兵 = 6
##其他常数
最大的历史走法数 = 256
最大的搜索深度 = 64
将死的分值 = 10000
长将判负的分值 = 将死的分值 - 100
搜索出胜负的分值界限 = 将死的分值 - 200
和棋时返回的分数取负值 = 20
先行权分值 = 3
随机性分值 = 7
空步裁剪的子力边界 = 400
空步裁剪的裁剪深度 = 2
最大置换表大小 = 1 << 20
ALPHA节点的置换表项 = 1
BETA节点的置换表项 = 2
PV节点的置换表项 = 3
判断棋子是否在棋盘中的数组 = (
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
)
判断棋子是否在九宫的数组 = (
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
)
#判断步长是否符合特定走法的数组,1=帅(将),2=仕(士),3=相(象)
判断步长是否符合特定走法的数组 = (
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0
)
根据步长判断马是否蹩腿的数组 = (
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,-16, 0,-16, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0
)
将的步长 = (-16, -1, 1, 16)
士的步长 = (-17, -15, 15, 17)
马的步长以将的步长作为马腿 = ((-33, -31), (-18, 14), (-14, 18), (31, 33))
马被将军的步长以士的步长作为马腿 = ((-33, -18), (-31, -14), (14, 31), (18, 33))
棋盘初始设置 = (
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 20, 19, 18, 17, 16, 17, 18, 19, 20, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0,
0, 0, 0, 22, 0, 22, 0, 22, 0, 22, 0, 22, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 14, 0, 14, 0, 14, 0, 14, 0, 14, 0, 0, 0, 0,
0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 12, 11, 10, 9, 8, 9, 10, 11, 12, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
)
MvvLva每种子力的价值 = (
0, 0, 0, 0, 0, 0, 0, 0,
5, 1, 1, 3, 4, 3, 2, 0,
5, 1, 1, 3, 4, 3, 2, 0
)
子力位置价值表= (
( #帅(将)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 11, 15, 11, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
), ( #仕(士)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 20, 0, 20, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 20, 0, 20, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
), ( #相(象)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 20, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 18, 0, 0, 0, 23, 0, 0, 0, 18, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 20, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
), ( #马
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 90, 90, 90, 96, 90, 96, 90, 90, 90, 0, 0, 0, 0,
0, 0, 0, 90, 96,103, 97, 94, 97,103, 96, 90, 0, 0, 0, 0,
0, 0, 0, 92, 98, 99,103, 99,103, 99, 98, 92, 0, 0, 0, 0,
0, 0, 0, 93,108,100,107,100,107,100,108, 93, 0, 0, 0, 0,
0, 0, 0, 90,100, 99,103,104,103, 99,100, 90, 0, 0, 0, 0,
0, 0, 0, 90, 98,101,102,103,102,101, 98, 90, 0, 0, 0, 0,
0, 0, 0, 92, 94, 98, 95, 98, 95, 98, 94, 92, 0, 0, 0, 0,
0, 0, 0, 93, 92, 94, 95, 92, 95, 94, 92, 93, 0, 0, 0, 0,
0, 0, 0, 85, 90, 92, 93, 78, 93, 92, 90, 85, 0, 0, 0, 0,
0, 0, 0, 88, 85, 90, 88, 90, 88, 90, 85, 88, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
), ( #车
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,206,208,207,213,214,213,207,208,206, 0, 0, 0, 0,
0, 0, 0,206,212,209,216,233,216,209,212,206, 0, 0, 0, 0,
0, 0, 0,206,208,207,214,216,214,207,208,206, 0, 0, 0, 0,
0, 0, 0,206,213,213,216,216,216,213,213,206, 0, 0, 0, 0,
0, 0, 0,208,211,211,214,215,214,211,211,208, 0, 0, 0, 0,
0, 0, 0,208,212,212,214,215,214,212,212,208, 0, 0, 0, 0,
0, 0, 0,204,209,204,212,214,212,204,209,204, 0, 0, 0, 0,
0, 0, 0,198,208,204,212,212,212,204,208,198, 0, 0, 0, 0,
0, 0, 0,200,208,206,212,200,212,206,208,200, 0, 0, 0, 0,
0, 0, 0,194,206,204,212,200,212,204,206,194, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
), ( #炮
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,100,100, 96, 91, 90, 91, 96,100,100, 0, 0, 0, 0,
0, 0, 0, 98, 98, 96, 92, 89, 92, 96, 98, 98, 0, 0, 0, 0,
0, 0, 0, 97, 97, 96, 91, 92, 91, 96, 97, 97, 0, 0, 0, 0,
0, 0, 0, 96, 99, 99, 98,100, 98, 99, 99, 96, 0, 0, 0, 0,
0, 0, 0, 96, 96, 96, 96,100, 96, 96, 96, 96, 0, 0, 0, 0,
0, 0, 0, 95, 96, 99, 96,100, 96, 99, 96, 95, 0, 0, 0, 0,
0, 0, 0, 96, 96, 96, 96, 96, 96, 96, 96, 96, 0, 0, 0, 0,
0, 0, 0, 97, 96,100, 99,101, 99,100, 96, 97, 0, 0, 0, 0,
0, 0, 0, 96, 97, 98, 98, 98, 98, 98, 97, 96, 0, 0, 0, 0,
0, 0, 0, 96, 96, 97, 99, 99, 99, 97, 96, 96, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
), ( #兵(卒)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 9, 9, 9, 11, 13, 11, 9, 9, 9, 0, 0, 0, 0,
0, 0, 0, 19, 24, 34, 42, 44, 42, 34, 24, 19, 0, 0, 0, 0,
0, 0, 0, 19, 24, 32, 37, 37, 37, 32, 24, 19, 0, 0, 0, 0,
0, 0, 0, 19, 23, 27, 29, 30, 29, 27, 23, 19, 0, 0, 0, 0,
0, 0, 0, 14, 18, 20, 27, 29, 27, 20, 18, 14, 0, 0, 0, 0,
0, 0, 0, 7, 0, 13, 0, 16, 0, 13, 0, 7, 0, 0, 0, 0,
0, 0, 0, 7, 0, 7, 0, 15, 0, 7, 0, 7, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
)
)
def 判断棋子是否在棋盘中(棋子位置):
return 判断棋子是否在棋盘中的数组[棋子位置] != 0
def 判断棋子是否在九宫中(棋子位置):
return 判断棋子是否在九宫的数组[棋子位置] != 0
def 获得格子的纵坐标(棋子位置):
return 棋子位置 >> 4
def 获得格子的横坐标(棋子位置):
return 棋子位置 & 15
def 根据坐标获得格子(x, y):
return x + (y << 4)
def 翻转格子(棋子位置):
return 254 - 棋子位置
def 前进一个格子的位置(棋子位置, 谁走):
return 棋子位置 - 16 + (谁走 << 5)
def 走法是否符合帅的步长(起始位置, 目标位置):
return 判断步长是否符合特定走法的数组[目标位置 - 起始位置 + 256] == 1
def 走法是否符合士的步长(起始位置, 目标位置):
return 判断步长是否符合特定走法的数组[目标位置 - 起始位置 + 256] == 2
def 走法是否符合象的步长(起始位置, 目标位置):
return 判断步长是否符合特定走法的数组[目标位置 - 起始位置 + 256] == 3
def 象眼的位置(起始位置, 目标位置):
return (起始位置 + 目标位置) >> 1
def 马腿的位置(起始位置, 目标位置):
return 起始位置 + 根据步长判断马是否蹩腿的数组[目标位置 - 起始位置 + 256]
def 是否未过河(棋子位置, 谁走):
return (棋子位置 & 0x80) != (谁走 << 7)
def 是否已过河(棋子位置, 谁走):
return (棋子位置 & 0x80) == (谁走 << 7)
def 是否在河的同一边(起始位置, 目标位置):
return ((起始位置 ^ 目标位置) & 0x80) == 0
def 是否在同一行(起始位置, 目标位置):
return ((起始位置 ^ 目标位置) & 0xf0) == 0
def 是否在同一列(起始位置, 目标位置):
return ((起始位置 ^ 目标位置) & 0x0f) == 0
def 获得红黑标记(谁走):
return 8 + (谁走 << 3)#(红子是8,黑子是16)
def 获得对方红黑标记(谁走):
return 16 - (谁走 << 3)
def 获得走法的起点(这个走法):
return 这个走法 & 255
def 获得走法的终点(这个走法):
return 这个走法 >> 8
def 根据起点和终点获得走法(起始位置, 目标位置):
return 起始位置 + 目标位置 * 256
#RC4密码流生成器
class RC4密码流生成器:
def __init__(self):
self.s = list(range(256))
self.x, self.y = 0, 0
def 用空密钥初始化密码流生成器(self):
self.x = self.y = j = 0
for i in range(256):
j = (j + self.s[i]) & 255
self.s[i], self.s[j] = self.s[j], self.s[i]
def 生成密码流的下一个字节(self):
self.x = (self.x + 1) & 255
self.y = (self.y + self.s[self.x]) & 255
self.s[self.x],self.s[self.y] = self.s[self.y],self.s[self.x]
return self.s[(self.s[self.x] + self.s[self.y]) & 255]
def 生成密码流的下四个字节(self):
uc0, uc1, uc2, uc3 = 0,0,0,0
uc0 = self.生成密码流的下一个字节()
uc1 = self.生成密码流的下一个字节()
uc2 = self.生成密码流的下一个字节()
uc3 = self.生成密码流的下一个字节()
return uc0 + (uc1 << 8) + (uc2 << 16) + (uc3 << 24)
class Zobrist结构:
def 用零填充Zobrist(self):
self.重复局面校验码 = self.置换表校验码 = 0
def 用密码流填充Zobrist(self, 生成器):
self.重复局面校验码 = 生成器.生成密码流的下四个字节()
self.置换表校验码 = 生成器.生成密码流的下四个字节()
def 执行XOR操作(self, 校验码):
self.重复局面校验码 ^= 校验码.重复局面校验码
self.置换表校验码 ^= 校验码.置换表校验码
class Zobrist表:
Zobrist结构_玩家=Zobrist结构()
桌面=[[Zobrist结构() for i in range(256)] for j in range(14)]
def 初始化Zobrist表():
RC4密码流生成器_生成器=RC4密码流生成器()
RC4密码流生成器_生成器.用空密钥初始化密码流生成器()
Zobrist表.Zobrist结构_玩家.用密码流填充Zobrist(RC4密码流生成器_生成器)
for i in range(14):
for j in range(256):
Zobrist表.桌面[i][j].用密码流填充Zobrist(RC4密码流生成器_生成器)
class 历史走法信息:
def __init__(self):
self.最佳走法 = self.上一步吃子标记 = self.被将军标记 = self.重复局面校验码 = 0
def 设置本历史走法(self, 这个走法, 上一步吃子标记, 被将军标记, dwKey_):
self.最佳走法 = 这个走法
self.上一步吃子标记 = 上一步吃子标记
self.被将军标记 = 被将军标记
self.重复局面校验码 = dwKey_
class 局面结构:
def __init__(self):
self.轮到谁走 = 0#0=红方,1=黑方
self.棋盘上的棋子摆放 = [0 for i in range(256)]
self.红方的子力价值, self.黑方的子力价值 = 0, 0
self.距离根节点的步数, self.历史走法数 = 0, 0
self.历史走法信息列表=[历史走法信息() for i in range(最大的历史走法数)]
self.校验码 = Zobrist结构()
def 清空棋盘(self):
self.轮到谁走 = self.红方的子力价值 = self.黑方的子力价值 = self.距离根节点的步数 = 0
self.棋盘上的棋子摆放=[0 for i in range(256)]
self.校验码.用零填充Zobrist()
def 初始化历史走法信息(self):
self.历史走法信息列表[0].设置本历史走法(0, 0, self.判断是否被将军(), self.校验码.重复局面校验码)
self.历史走法数 = 1
def 初始化棋盘(self):
self.清空棋盘()
for 棋子位置 in range(256):
棋子代号 = 棋盘初始设置[棋子位置]
if 棋子代号 != 0:
self.在棋盘上放一枚棋子(棋子位置, 棋子代号)
self.初始化历史走法信息()
def 交换走子方(self):
self.轮到谁走 = 1 - self.轮到谁走
self.校验码.执行XOR操作(Zobrist表.Zobrist结构_玩家)
def 在棋盘上放一枚棋子(self, 棋子位置, 棋子代号):
self.棋盘上的棋子摆放[棋子位置] = 棋子代号
#红方加分,黑方(注意"子力位置价值表"取值要颠倒)减分
if 棋子代号 < 16:
self.红方的子力价值 += 子力位置价值表[棋子代号 - 8][棋子位置]
self.校验码.执行XOR操作(Zobrist表.桌面[棋子代号 - 8][棋子位置])
else:
self.黑方的子力价值 += 子力位置价值表[棋子代号 - 16][翻转格子(棋子位置)]
self.校验码.执行XOR操作(Zobrist表.桌面[棋子代号 - 9][棋子位置])
def 从棋盘上拿走一枚棋子(self, 棋子位置, 棋子代号):
self.棋盘上的棋子摆放[棋子位置] = 0
#红方减分,黑方(注意"子力位置价值表"取值要颠倒)加分
if 棋子代号 < 16:
self.红方的子力价值 -= 子力位置价值表[棋子代号 - 8][棋子位置]
self.校验码.执行XOR操作(Zobrist表.桌面[棋子代号 - 8][棋子位置])
else:
self.黑方的子力价值 -= 子力位置价值表[棋子代号 - 16][翻转格子(棋子位置)]
self.校验码.执行XOR操作(Zobrist表.桌面[棋子代号 - 9][棋子位置])
def 局面评价函数(self):
return (self.红方的子力价值 - self.黑方的子力价值 if self.轮到谁走 == 0 else self.黑方的子力价值 - self.红方的子力价值) + 先行权分值
def 是否被将军(self):
return self.历史走法信息列表[self.历史走法数 - 1].被将军标记
def 上一步是否吃子(self):#上一步是否吃子
return self.历史走法信息列表[self.历史走法数 - 1].上一步吃子标记 != 0
def 移动一步棋的棋子(self, 这个走法):
起始位置 = 获得走法的起点(这个走法)
目标位置 = 获得走法的终点(这个走法)
上一步吃子标记 = self.棋盘上的棋子摆放[目标位置]
if 上一步吃子标记 != 0:
self.从棋盘上拿走一枚棋子(目标位置, 上一步吃子标记)
棋子代号 = self.棋盘上的棋子摆放[起始位置]
self.从棋盘上拿走一枚棋子(起始位置, 棋子代号)
self.在棋盘上放一枚棋子(目标位置, 棋子代号)
return 上一步吃子标记
def 撤消移动一步棋的棋子(self, 这个走法, 上一步吃子标记):
起始位置 = 获得走法的起点(这个走法)
目标位置 = 获得走法的终点(这个走法)
棋子代号 = self.棋盘上的棋子摆放[目标位置]
self.从棋盘上拿走一枚棋子(目标位置, 棋子代号)
self.在棋盘上放一枚棋子(起始位置, 棋子代号)
if 上一步吃子标记 != 0:
self.在棋盘上放一枚棋子(目标位置, 上一步吃子标记)
def 走一步棋(self,这个走法):#走一步棋
重复局面校验码 = self.校验码.重复局面校验码
吃掉的棋子 = self.移动一步棋的棋子(这个走法)
if self.判断是否被将军():
self.撤消移动一步棋的棋子(这个走法, 吃掉的棋子)
return False
self.交换走子方()
self.历史走法信息列表[self.历史走法数].设置本历史走法(这个走法, 吃掉的棋子, self.判断是否被将军(), 重复局面校验码)
self.历史走法数 +=1
self.距离根节点的步数 +=1
return True
def 撤消走一步棋(self):
self.距离根节点的步数 -= 1
self.历史走法数 -= 1
self.交换走子方()
self.撤消移动一步棋的棋子(self.历史走法信息列表[self.历史走法数].最佳走法, self.历史走法信息列表[self.历史走法数].上一步吃子标记)
def 走一步空步(self):#走一步空步
重复局面校验码 = self.校验码.重复局面校验码
self.交换走子方()
self.历史走法信息列表[self.历史走法数].设置本历史走法(0, 0, False, 重复局面校验码)
self.历史走法数 +=1
self.距离根节点的步数 +=1
def 撤消走一步空步(self):#撤消走一步空步
self.距离根节点的步数 -=1
self.历史走法数 -=1
self.交换走子方()
def 生成所有走法(self, 只生成吃子走法 = False):#如果"只生成吃子走法"为"TRUE"则只生成吃子走法
#生成所有走法,需要经过以下几个步骤:
生成的走法 = []
我方的棋子标记 = 获得红黑标记(self.轮到谁走)
对方的棋子标记 = 获得对方红黑标记(self.轮到谁走)
for 起始位置 in range(256):
#1. 找到一个本方棋子,再做以下判断:
起始位置的棋子 = self.棋盘上的棋子摆放[起始位置]
if (起始位置的棋子 & 我方的棋子标记) == 0:
continue
#2. 根据棋子确定走法
啥棋子 = 起始位置的棋子 - 我方的棋子标记
if 啥棋子==棋子将:
for i in range(4):
目标位置 = 起始位置 + 将的步长[i]
if not 判断棋子是否在九宫中(目标位置):
continue
目标位置的棋子 = self.棋盘上的棋子摆放[目标位置]
if (目标位置的棋子 & 对方的棋子标记) != 0 if 只生成吃子走法 else (目标位置的棋子 & 我方的棋子标记) == 0:
生成的走法.append(根据起点和终点获得走法(起始位置, 目标位置))
elif 啥棋子==棋子士:
for i in range(4):
目标位置 = 起始位置 + 士的步长[i]
if not 判断棋子是否在九宫中(目标位置):
continue
目标位置的棋子 = self.棋盘上的棋子摆放[目标位置]
if (目标位置的棋子 & 对方的棋子标记) != 0 if 只生成吃子走法 else (目标位置的棋子 & 我方的棋子标记) == 0:
生成的走法.append(根据起点和终点获得走法(起始位置, 目标位置))
elif 啥棋子==棋子象:
for i in range(4):
目标位置 = 起始位置 + 士的步长[i]
if not(判断棋子是否在棋盘中(目标位置) and 是否未过河(目标位置, self.轮到谁走) and self.棋盘上的棋子摆放[目标位置] == 0):
continue
目标位置 += 士的步长[i]
目标位置的棋子 = self.棋盘上的棋子摆放[目标位置]
if (目标位置的棋子 & 对方的棋子标记) != 0 if 只生成吃子走法 else (目标位置的棋子 & 我方的棋子标记) == 0:
生成的走法.append(根据起点和终点获得走法(起始位置, 目标位置))
elif 啥棋子==棋子马:
for i in range(4):
目标位置 = 起始位置 + 将的步长[i]
if self.棋盘上的棋子摆放[目标位置] != 0:
continue
for j in range (2):
目标位置 = 起始位置 + 马的步长以将的步长作为马腿[i][j]
if not 判断棋子是否在棋盘中(目标位置):
continue
目标位置的棋子 = self.棋盘上的棋子摆放[目标位置]
if (目标位置的棋子 & 对方的棋子标记) != 0 if 只生成吃子走法 else (目标位置的棋子 & 我方的棋子标记) == 0:
生成的走法.append(根据起点和终点获得走法(起始位置, 目标位置))
elif 啥棋子==棋子车:
for i in range(4):
偏移量 = 将的步长[i]
目标位置 = 起始位置 + 偏移量
while 判断棋子是否在棋盘中(目标位置):
目标位置的棋子 = self.棋盘上的棋子摆放[目标位置]
if 目标位置的棋子 == 0:
if not 只生成吃子走法:
生成的走法.append(根据起点和终点获得走法(起始位置, 目标位置))
else:
if (目标位置的棋子 & 对方的棋子标记) != 0:
生成的走法.append(根据起点和终点获得走法(起始位置, 目标位置))
break
目标位置 += 偏移量
elif 啥棋子==棋子炮:
for i in range(4):
偏移量 = 将的步长[i]
目标位置 = 起始位置 + 偏移量
while 判断棋子是否在棋盘中(目标位置):
目标位置的棋子 = self.棋盘上的棋子摆放[目标位置]
if 目标位置的棋子 == 0:
if not 只生成吃子走法:
生成的走法.append(根据起点和终点获得走法(起始位置, 目标位置))
else:
break
目标位置 += 偏移量
目标位置 += 偏移量
while 判断棋子是否在棋盘中(目标位置):
目标位置的棋子 = self.棋盘上的棋子摆放[目标位置]
if 目标位置的棋子 != 0:
if (目标位置的棋子 & 对方的棋子标记) != 0:
生成的走法.append(根据起点和终点获得走法(起始位置, 目标位置))
break
目标位置 += 偏移量
elif 啥棋子==棋子兵:
目标位置 = 前进一个格子的位置(起始位置, self.轮到谁走)
if 判断棋子是否在棋盘中(目标位置):
目标位置的棋子 = self.棋盘上的棋子摆放[目标位置]
if (目标位置的棋子 & 对方的棋子标记) != 0 if 只生成吃子走法 else (目标位置的棋子 & 我方的棋子标记) == 0:
生成的走法.append(根据起点和终点获得走法(起始位置, 目标位置))
if 是否已过河(起始位置, self.轮到谁走):
for 偏移量 in range(-1,2,2):
目标位置 = 起始位置 + 偏移量
if 判断棋子是否在棋盘中(目标位置):
目标位置的棋子 = self.棋盘上的棋子摆放[目标位置]
if (目标位置的棋子 & 对方的棋子标记) != 0 if 只生成吃子走法 else (目标位置的棋子 & 我方的棋子标记) == 0:
生成的走法.append(根据起点和终点获得走法(起始位置, 目标位置))
return 生成的走法
def 判断走法是否合法(self, 这个走法):
#判断走法是否合法,需要经过以下的判断过程:
#1. 判断起始格是否有自己的棋子
起始位置 = 获得走法的起点(这个走法)
起始位置的棋子 = self.棋盘上的棋子摆放[起始位置]
我方的棋子标记 = 获得红黑标记(self.轮到谁走)
if (起始位置的棋子 & 我方的棋子标记) == 0:
return False
#2. 判断目标格是否有自己的棋子
目标位置 = 获得走法的终点(这个走法)
目标位置的棋子 = self.棋盘上的棋子摆放[目标位置]
if (目标位置的棋子 & 我方的棋子标记) != 0:
return False
#3. 根据棋子的类型检查走法是否合理
啥棋子 = 起始位置的棋子 - 我方的棋子标记
if 啥棋子 == 棋子将:
return 判断棋子是否在九宫中(目标位置) and 走法是否符合帅的步长(起始位置, 目标位置)
elif 啥棋子 == 棋子士:
return 判断棋子是否在九宫中(目标位置) and 走法是否符合士的步长(起始位置, 目标位置)
elif 啥棋子 == 棋子象:
return 是否在河的同一边(起始位置, 目标位置) and 走法是否符合象的步长(起始位置, 目标位置) and self.棋盘上的棋子摆放[象眼的位置(起始位置, 目标位置)] == 0
elif 啥棋子 == 棋子马:
障碍物偏移量 = 马腿的位置(起始位置, 目标位置)
return 障碍物偏移量 != 起始位置 and self.棋盘上的棋子摆放[障碍物偏移量] == 0
elif 啥棋子 == 棋子车 or 啥棋子 == 棋子炮:
if 是否在同一行(起始位置, 目标位置):
偏移量 = -1 if 目标位置 < 起始位置 else 1
elif 是否在同一列(起始位置, 目标位置):
偏移量 = -16 if 目标位置 < 起始位置 else 16
else:
return False
障碍物偏移量 = 起始位置 + 偏移量
while 障碍物偏移量 != 目标位置 and self.棋盘上的棋子摆放[障碍物偏移量] == 0:
障碍物偏移量 += 偏移量
if 障碍物偏移量 == 目标位置:
return 目标位置的棋子 == 0 or 起始位置的棋子 - 我方的棋子标记 == 棋子车
elif 目标位置的棋子 != 0 and 起始位置的棋子 - 我方的棋子标记 == 棋子炮:
障碍物偏移量 += 偏移量
while 障碍物偏移量 != 目标位置 and self.棋盘上的棋子摆放[障碍物偏移量] == 0:
障碍物偏移量 += 偏移量
return 障碍物偏移量 == 目标位置
else:
return False
elif 啥棋子 == 棋子兵:
if 是否已过河(目标位置, self.轮到谁走) and (目标位置 == 起始位置 - 1 or 目标位置 == 起始位置 + 1):
return True
return 目标位置 == 前进一个格子的位置(起始位置, self.轮到谁走)
else:
return False
def 判断是否被将军(self):
我方的棋子标记 = 获得红黑标记(self.轮到谁走)
对方的棋子标记 = 获得对方红黑标记(self.轮到谁走)
#找到棋盘上的帅(将),再做以下判断:
for 起始位置 in range(256):
if self.棋盘上的棋子摆放[起始位置] != 我方的棋子标记 + 棋子将:
continue
#1. 判断是否被对方的兵(卒)将军
if self.棋盘上的棋子摆放[前进一个格子的位置(起始位置, self.轮到谁走)] == 对方的棋子标记 + 棋子兵:
return True
for 偏移量 in range(-1,2,2):
if self.棋盘上的棋子摆放[起始位置 + 偏移量] == 对方的棋子标记 + 棋子兵:
return True
#2. 判断是否被对方的马将军(以仕(士)的步长当作马腿)
for i in range(4):
if self.棋盘上的棋子摆放[起始位置 + 士的步长[i]] != 0:
continue
for j in range(2):
目标位置的棋子 = self.棋盘上的棋子摆放[起始位置 + 马被将军的步长以士的步长作为马腿[i][j]]
if 目标位置的棋子 == 对方的棋子标记 + 棋子马:
return True
#3. 判断是否被对方的车或炮将军(包括将帅对脸)
for i in range(4):
偏移量 = 将的步长[i]
目标位置 = 起始位置 + 偏移量
while 判断棋子是否在棋盘中(目标位置):
目标位置的棋子 = self.棋盘上的棋子摆放[目标位置]
if 目标位置的棋子 != 0:
if 目标位置的棋子 == 对方的棋子标记 + 棋子车 or 目标位置的棋子 == 对方的棋子标记 + 棋子将:
return True
break
目标位置 += 偏移量
目标位置 += 偏移量
while 判断棋子是否在棋盘中(目标位置):
目标位置的棋子 = self.棋盘上的棋子摆放[目标位置]
if 目标位置的棋子 != 0:
if 目标位置的棋子 == 对方的棋子标记 + 棋子炮:
return True
break
目标位置 += 偏移量
return False
return False
def 判断是否被杀(self):
所有的走法 = self.生成所有走法()
for i in 所有的走法:
被吃掉的棋子 = self.移动一步棋的棋子(i)
if not self.判断是否被将军():
self.撤消移动一步棋的棋子(i, 被吃掉的棋子)
return False
else:
self.撤消移动一步棋的棋子(i, 被吃掉的棋子)
return True
def 和棋分值(self):
return -和棋时返回的分数取负值 if (self.距离根节点的步数 & 1) == 0 else 和棋时返回的分数取负值
def 检测重复局面(self, 重复次数 = 1):
是否检查本方 = False
本方长将 = 对方长将 = True
某一步棋索引值 = self.历史走法数 - 1#历史走法数减1,作为最后那一步的索引值
while self.历史走法信息列表[某一步棋索引值].最佳走法 != 0 and self.历史走法信息列表[某一步棋索引值].上一步吃子标记 == 0:
if 是否检查本方:
本方长将 = 本方长将 and self.历史走法信息列表[某一步棋索引值].被将军标记
if self.历史走法信息列表[某一步棋索引值].重复局面校验码 == self.校验码.重复局面校验码:
重复次数 -= 1
if 重复次数 == 0:
return 1 + (2 if 本方长将 else 0) + (4 if 对方长将 else 0)
else:
对方长将 = 对方长将 and self.历史走法信息列表[某一步棋索引值].被将军标记
是否检查本方 = not 是否检查本方
某一步棋索引值 -= 1
return 0
def 重复局面分值(self, 重复状态码):
返回的分值 = (0 if (重复状态码 & 2) == 0 else self.距离根节点的步数 - 长将判负的分值) + (0 if (重复状态码 & 4) == 0 else 长将判负的分值 - self.距离根节点的步数)
return self.和棋分值() if 返回的分值 == 0 else 返回的分值
def 判断是否允许空步裁剪(self):
return (self.红方的子力价值 if self.轮到谁走 == 0 else self.黑方的子力价值) > 空步裁剪的子力边界
当前棋局 = 局面结构()#局面实例
class 置换表项结构:
def __init__(self):
self.深度 = 0
self.标志 = 0
self.分值 = 0
self.最佳走法 = 0
self.置换表校验码 = 0
class 与搜索有关的全局变量:
电脑走的棋 = 0
历史表 = [0 for i in range(65536)]
杀手走法表 = [[0,0] for i in range(最大的搜索深度)]
置换表 = [置换表项结构() for i in range(最大置换表大小)]
#提取置换表项
def 提取置换表项(vlAlpha, vlBeta, 当前深度, 这个走法):
置换表项 = 与搜索有关的全局变量.置换表[当前棋局.校验码.重复局面校验码 & (最大置换表大小 - 1)]
if 置换表项.置换表校验码 != 当前棋局.校验码.置换表校验码:
这个走法[0] = 0
return -将死的分值
这个走法[0] = 置换表项.最佳走法
杀棋标志 = False#杀棋标志:如果是杀棋,那么不需要满足深度条件
if 置换表项.分值 > 搜索出胜负的分值界限:
if 置换表项.分值 < 长将判负的分值:
return -将死的分值#可能导致搜索的不稳定性,立刻退出,但最佳着法可能拿到
置换表项.分值 -= 当前棋局.距离根节点的步数
杀棋标志 = True
elif 置换表项.分值 < -搜索出胜负的分值界限:
if 置换表项.分值 > -长将判负的分值:
return -将死的分值#同上
置换表项.分值 += 当前棋局.距离根节点的步数
杀棋标志 = True
if 置换表项.深度 >= 当前深度 or 杀棋标志:
if 置换表项.标志 == BETA节点的置换表项:
return 置换表项.分值 if 置换表项.分值 >= vlBeta else -将死的分值
elif 置换表项.标志 == ALPHA节点的置换表项:
return 置换表项.分值 if 置换表项.分值 <= vlAlpha else -将死的分值
return 置换表项.分值
return -将死的分值
#保存置换表项
def 保存置换表项(标志, vl, 当前深度, 这个走法):
置换表项 = 与搜索有关的全局变量.置换表[当前棋局.校验码.重复局面校验码 & (最大置换表大小 - 1)]
if 置换表项.深度 > 当前深度:
return
置换表项.标志 = 标志
置换表项.深度 = 当前深度
if vl > 搜索出胜负的分值界限:
if 这个走法 == 0 and vl <= 长将判负的分值:
return#可能导致搜索的不稳定性,并且没有最佳着法,立刻退出
置换表项.分值 = vl + 当前棋局.距离根节点的步数
elif vl < -搜索出胜负的分值界限:
if 这个走法 == 0 and vl >= -长将判负的分值:
return#同上
置换表项.分值 = vl - 当前棋局.距离根节点的步数
else:
置换表项.分值 = vl
置换表项.最佳走法 = 这个走法
置换表项.置换表校验码 = 当前棋局.校验码.置换表校验码
与搜索有关的全局变量.置换表[当前棋局.校验码.重复局面校验码 & (最大置换表大小 - 1)] = 置换表项
def 求MvvLva值(这个走法):
return (MvvLva每种子力的价值[当前棋局.棋盘上的棋子摆放[获得走法的终点(这个走法)]] << 3) - MvvLva每种子力的价值[当前棋局.棋盘上的棋子摆放[获得走法的起点(这个走法)]]
class 走法排序结构:
def 初始化设定置换表走法和两个杀手走法(self, mvHash_):
self.置换表走法 = mvHash_
self.杀手走法一 = 与搜索有关的全局变量.杀手走法表[当前棋局.距离根节点的步数][0]
self.杀手走法二 = 与搜索有关的全局变量.杀手走法表[当前棋局.距离根节点的步数][1]
self.当前阶段 = 0
def 得到下一个走法(self):
if self.当前阶段 == 0:
#"当前阶段"表示着法启发的若干阶段,依次为:
#0. 置换表着法启发,完成后立即进入下一阶段;
self.当前阶段 = 1
if self.置换表走法 != 0:
return self.置换表走法
if self.当前阶段 == 1:
#1. 杀手着法启发(第一个杀手着法),完成后立即进入下一阶段;
self.当前阶段 = 2
if self.杀手走法一 != self.置换表走法 and self.杀手走法一 != 0 and 当前棋局.判断走法是否合法(self.杀手走法一):
return self.杀手走法一
#2. 杀手着法启发(第二个杀手着法),完成后立即进入下一阶段;
if self.当前阶段 == 2:
self.当前阶段 = 3
if self.杀手走法二 != self.置换表走法 and self.杀手走法二 != 0 and 当前棋局.判断走法是否合法(self.杀手走法二):
return self.杀手走法二
#3. 生成所有着法,完成后立即进入下一阶段;
if self.当前阶段 == 3:
self.当前阶段 = 4
self.所有的走法 = 当前棋局.生成所有走法()
self.所有的走法.sort(key = lambda x:与搜索有关的全局变量.历史表[x], reverse=True)
self.当前采用第几个走法 = 0
#4. 对剩余着法做历史表启发;
if self.当前阶段 == 4:
while self.当前采用第几个走法 < len(self.所有的走法):
这个走法 = self.所有的走法[self.当前采用第几个走法]
self.当前采用第几个走法 += 1
if 这个走法 != self.置换表走法 and 这个走法 != self.杀手走法一 and 这个走法 != self.杀手走法二:
return 这个走法
#5. 没有着法了,返回零。
return 0
def 对最佳走法的处理(这个走法, 当前深度):
与搜索有关的全局变量.历史表[这个走法] += 当前深度 * 当前深度
lpmvKillers = 与搜索有关的全局变量.杀手走法表[当前棋局.距离根节点的步数]
if lpmvKillers[0] != 这个走法:
lpmvKillers[1] = lpmvKillers[0]
lpmvKillers[0] = 这个走法
def 静态搜索过程(vlAlpha, vlBeta):
#一个静态搜索分为以下几个阶段
#1. 检查重复局面
vl = 当前棋局.检测重复局面()
if vl != 0:
return 当前棋局.重复局面分值(vl)
#2. 到达极限深度就返回局面评价
if 当前棋局.距离根节点的步数 == 最大的搜索深度:
return 当前棋局.局面评价函数()
#3. 初始化最佳值
最高分 = -将死的分值#这样可以知道,是否一个走法都没走过(杀棋)
if 当前棋局.是否被将军():
#4. 如果被将军,则生成全部走法
所有的走法 = 当前棋局.生成所有走法()
所有的走法.sort(key = lambda x:与搜索有关的全局变量.历史表[x], reverse=True)
else:
#5. 如果不被将军,先做局面评价
vl = 当前棋局.局面评价函数()
if vl > 最高分:
最高分 = vl
if vl >= vlBeta:
return vl
if vl > vlAlpha:
vlAlpha = vl
#6. 如果局面评价没有截断,再生成吃子走法
所有的走法 = 当前棋局.生成所有走法(只生成吃子走法=True)
所有的走法.sort(key = lambda x:求MvvLva值(x),reverse=True)
#7. 逐一走这些走法,并进行递归
for i in 所有的走法:
if 当前棋局.走一步棋(i):
vl = -静态搜索过程(-vlBeta, -vlAlpha)
当前棋局.撤消走一步棋()
#8. 进行Alpha-Beta大小判断和截断
if vl > 最高分:#找到最佳值(但不能确定是Alpha、PV还是Beta走法)
最高分 = vl#"最高分"就是目前要返回的最佳值,可能超出Alpha-Beta边界
if vl >= vlBeta:#找到一个Beta走法
return vl#Beta截断
if vl > vlAlpha:#找到一个PV走法
vlAlpha = vl#缩小Alpha-Beta边界
#9. 所有走法都搜索完了,返回最佳值
return 当前棋局.距离根节点的步数 - 将死的分值 if 最高分 == -将死的分值 else 最高分
def 超出边界的AlphaBeta搜索过程(vlAlpha, vlBeta, 当前深度, 不进行空步裁剪 = False):
置换表走法 = [0]#=====================================
走法排序结构_走法排序 = 走法排序结构()
#一个Alpha-Beta完全搜索分为以下几个阶段
#1. 到达水平线,则调用静态搜索(注意:由于空步裁剪,深度可能小于零)
if 当前深度 <= 0:
return 静态搜索过程(vlAlpha, vlBeta)
#1-1. 检查重复局面(注意:不要在根节点检查,否则就没有走法了)
vl = 当前棋局.检测重复局面()
if vl != 0:
return 当前棋局.重复局面分值(vl)
#1-2. 到达极限深度就返回局面评价
if 当前棋局.距离根节点的步数 == 最大的搜索深度:
return 当前棋局.局面评价函数()
#1-3. 尝试置换表裁剪,并得到置换表走法
vl = 提取置换表项(vlAlpha, vlBeta, 当前深度, 置换表走法)
if vl > -将死的分值:
return vl
#1-4. 尝试空步裁剪(根节点的Beta值是"将死的分值",所以不可能发生空步裁剪)
if not 不进行空步裁剪 and not 当前棋局.是否被将军() and 当前棋局.判断是否允许空步裁剪():
当前棋局.走一步空步()
vl = -超出边界的AlphaBeta搜索过程(-vlBeta, 1 - vlBeta, 当前深度 - 空步裁剪的裁剪深度 - 1, 不进行空步裁剪=True)
当前棋局.撤消走一步空步()
if vl >= vlBeta:
return vl
#2. 初始化最佳值和最佳走法
置换表项标志 = ALPHA节点的置换表项
最高分 = -将死的分值#这样可以知道,是否一个走法都没走过(杀棋)
最棒的走法 = 0#这样可以知道,是否搜索到了Beta走法或PV走法,以便保存到历史表
#3. 初始化走法排序结构
走法排序结构_走法排序.初始化设定置换表走法和两个杀手走法(置换表走法[0])
#4. 逐一走这些走法,并进行递归
这个走法 = 走法排序结构_走法排序.得到下一个走法()
while 这个走法 != 0:
if 当前棋局.走一步棋(这个走法):
#将军延伸
新的搜索深度 = 当前深度 if 当前棋局.是否被将军() else (当前深度 - 1)
#PVS
if 最高分 == -将死的分值:
vl = -超出边界的AlphaBeta搜索过程(-vlBeta, -vlAlpha, 新的搜索深度)
else:
vl = -超出边界的AlphaBeta搜索过程(-vlAlpha - 1, -vlAlpha, 新的搜索深度)
if vl > vlAlpha and vl < vlBeta:
vl = -超出边界的AlphaBeta搜索过程(-vlBeta, -vlAlpha, 新的搜索深度)
当前棋局.撤消走一步棋()
#5. 进行Alpha-Beta大小判断和截断
if vl > 最高分:#找到最佳值(但不能确定是Alpha、PV还是Beta走法)
最高分 = vl#"最高分"就是目前要返回的最佳值,可能超出Alpha-Beta边界
if vl >= vlBeta:#找到一个Beta走法
置换表项标志 = BETA节点的置换表项
最棒的走法 = 这个走法#Beta走法要保存到历史表
break#Beta截断
if vl > vlAlpha:#找到一个PV走法
置换表项标志 = PV节点的置换表项
最棒的走法 = 这个走法#PV走法要保存到历史表
vlAlpha = vl#缩小Alpha-Beta边界
这个走法 = 走法排序结构_走法排序.得到下一个走法()
#5. 所有走法都搜索完了,把最佳走法(不能是Alpha走法)保存到历史表,返回最佳值
if 最高分 == -将死的分值:
#如果是杀棋,就根据杀棋步数给出评价
return 当前棋局.距离根节点的步数 - 将死的分值
#记录到置换表
保存置换表项(置换表项标志, 最高分, 当前深度, 最棒的走法)
if 最棒的走法 != 0:
#如果不是Alpha走法,就将最佳走法保存到历史表
对最佳走法的处理(最棒的走法, 当前深度)
return 最高分
#根节点的Alpha-Beta搜索过程
def 根节点的AlphaBeta搜索过程(当前深度):
走法排序结构_走法排序 = 走法排序结构()
最高分 = -将死的分值
走法排序结构_走法排序.初始化设定置换表走法和两个杀手走法(与搜索有关的全局变量.电脑走的棋)
这个走法 = 走法排序结构_走法排序.得到下一个走法()
while 这个走法 != 0:
if 当前棋局.走一步棋(这个走法):
新的搜索深度 = 当前深度 if 当前棋局.是否被将军() else (当前深度 - 1)
if 最高分 == -将死的分值:
vl = -超出边界的AlphaBeta搜索过程(-将死的分值, 将死的分值, 新的搜索深度, 不进行空步裁剪=True)
else:
vl = -超出边界的AlphaBeta搜索过程(-最高分 - 1, -最高分, 新的搜索深度)
if vl > 最高分:
vl = -超出边界的AlphaBeta搜索过程(-将死的分值, -最高分, 新的搜索深度, 不进行空步裁剪=True)
当前棋局.撤消走一步棋()
if vl > 最高分:
最高分 = vl
与搜索有关的全局变量.电脑走的棋 = 这个走法
if -搜索出胜负的分值界限 < 最高分 < 搜索出胜负的分值界限:
最高分 += (random.randint(0,32767) & 随机性分值) - (random.randint(0,32767) & 随机性分值)
这个走法 = 走法排序结构_走法排序.得到下一个走法()
保存置换表项(PV节点的置换表项, 最高分, 当前深度, 与搜索有关的全局变量.电脑走的棋)
对最佳走法的处理(与搜索有关的全局变量.电脑走的棋, 当前深度)
return 最高分
#迭代加深搜索过程
def 迭代加深搜索过程():
#初始化
与搜索有关的全局变量.历史表=[0 for i in range(65536)]#清空历史表
与搜索有关的全局变量.杀手走法表=[[0,0] for i in range(最大的搜索深度)]#清空杀手走法表
与搜索有关的全局变量.置换表=[置换表项结构() for i in range(最大置换表大小)]#清空置换表
当前棋局.距离根节点的步数 = 0#初始步数
#检查是否只有唯一走法
vl = 0
所有的走法 = 当前棋局.生成所有走法()
for i in 所有的走法:
if 当前棋局.走一步棋(i):
当前棋局.撤消走一步棋()
与搜索有关的全局变量.电脑走的棋 = i
vl += 1
if vl == 1:
return
#迭代加深过程
t = time.process_time()#定时器初始值
for i in range(1,最大的搜索深度):
vl = 根节点的AlphaBeta搜索过程(i)
#搜索到杀棋,就终止搜索
if vl > 搜索出胜负的分值界限 or vl < -搜索出胜负的分值界限:
break
#超过i秒,就终止搜索
if time.process_time() - t > 1:
print('搜索深度',i,'耗时',time.process_time() - t,'秒')
break
def 开始一局游戏():
#初始化全局变量
初始化Zobrist表()
当前棋局.初始化棋盘()
def 电脑回应一步棋():
#电脑走一步棋
迭代加深搜索过程()
当前棋局.走一步棋(与搜索有关的全局变量.电脑走的棋)
#清除上一步棋的选择标记
#把电脑走的棋标记出来
#检查重复局面
vlRep = 当前棋局.检测重复局面(3)
if 当前棋局.判断是否被杀():
#如果分出胜负,那么播放胜负的声音,并且弹出不带声音的提示框
return "请再接再厉!"
elif vlRep > 0:
vlRep = 当前棋局.重复局面分值(vlRep)
#注意:"vlRep"是对玩家来说的分值
return "长打作负,请不要气馁!" if vlRep < -搜索出胜负的分值界限 else "电脑长打作负,祝贺你取得胜利!" if vlRep > 搜索出胜负的分值界限 else "双方不变作和,辛苦了!"
elif 当前棋局.历史走法数 > 100:
return "超过自然限着作和,辛苦了!"
else:
#如果没有分出胜负,那么播放将军、吃子或一般走子的声音
if 当前棋局.上一步是否吃子():
当前棋局.初始化历史走法信息()
#返回空字符串表示游戏没有结束
return ''
def 玩家走一步棋(mv):
#这里应检查一下走法合法,客户端已经检查,主要为防外挂
if 当前棋局.判断走法是否合法(mv):
if 当前棋局.走一步棋(mv):
vlRep = 当前棋局.检测重复局面(3)
if 当前棋局.判断是否被杀():
return "祝贺你取得胜利!"
elif vlRep > 0:
vlRep = 当前棋局.重复局面分值(vlRep)
return "长打作负,请不要气馁!" if vlRep > 搜索出胜负的分值界限 else "电脑长打作负,祝贺你取得胜利!" if vlRep < -搜索出胜负的分值界限 else "双方不变作和,辛苦了!"
elif 当前棋局.历史走法数 > 100:
return "超过自然限着作和,辛苦了!"
else:
#如果没有分出胜负,那么播放将军、吃子或一般走子的声音
if 当前棋局.上一步是否吃子():
当前棋局.初始化历史走法信息()
return 电脑回应一步棋()
else:
return "不能送将!"
return ''
#对玩家对弈接口
def 返回给玩家的电脑走法(re):
start = re % 256
end = re // 256
start_x = start % 16 - 3
start_y = start // 16 - 3
end_x = end % 16 - 3
end_y = end // 16 - 3
return str(start_x)+str(start_y)+str(end_x)+str(end_y)
def 玩家走棋交给电脑(stra):
start_x,start_y,end_x,end_y = int(stra[0]),int(stra[1]),int(stra[2]),int(stra[3])
return ((start_x + 3)+(start_y+3)*16)+((end_x + 3)+(end_y+3)*16)*256
def message(msg):
print("recv:",msg)
if msg == 'start':
开始一局游戏()
elif msg[0] == '9':
t = 电脑回应一步棋()
go = 返回给玩家的电脑走法(与搜索有关的全局变量.电脑走的棋)
print('send:',go+t)
return go+t
else:
t = 玩家走一步棋(玩家走棋交给电脑(msg))
go = 返回给玩家的电脑走法(与搜索有关的全局变量.电脑走的棋)
print('send:',go+t)
return go+t
#======================================GUI==========================================
# 初始化pygame,为使用硬件做准备
pygame.init()
pygame.font.init()
#实例化字体
font = pygame.font.Font("img/fzcyjt.ttf", 30)
# 创建了一个窗口
screen = pygame.display.set_mode((510, 800), 0, 32)#返回一个Surface对象,代表了桌面上出现的窗口。第一个参数代表分辨率;第二个参数是标志位,如果不需要使用热河特性,则指定为0;第三个为色深。
# 设置窗口标题
pygame.display.set_caption("中国象棋")
# 加载并转换图像
#载入背景图
background = pygame.image.load('img/bg.jpg').convert()
button_i = pygame.image.load('img/我先.jpg').convert()
button_u = pygame.image.load('img/电脑先.jpg').convert()
button_b = pygame.image.load('img/底条.jpg').convert()
button_i = pygame.transform.scale(button_i, (200, 80))
button_u = pygame.transform.scale(button_u, (200, 80))
button_b = pygame.transform.scale(button_b, (507, 80))
#载入棋盘图,带透明通道
space = pygame.image.load("img/bg.png").convert_alpha()
#载入棋子,带透明通道
b_j = pygame.image.load("img/b_j.png").convert_alpha()
b_s = pygame.image.load("img/b_s.png").convert_alpha()
b_x = pygame.image.load("img/b_x.png").convert_alpha()
b_m = pygame.image.load("img/b_m.png").convert_alpha()
b_c = pygame.image.load("img/b_c.png").convert_alpha()
b_p = pygame.image.load("img/b_p.png").convert_alpha()
b_z = pygame.image.load("img/b_z.png").convert_alpha()
r_j = pygame.image.load("img/r_j.png").convert_alpha()
r_s = pygame.image.load("img/r_s.png").convert_alpha()
r_x = pygame.image.load("img/r_x.png").convert_alpha()
r_m = pygame.image.load("img/r_m.png").convert_alpha()
r_c = pygame.image.load("img/r_c.png").convert_alpha()
r_p = pygame.image.load("img/r_p.png").convert_alpha()
r_z = pygame.image.load("img/r_z.png").convert_alpha()
select = pygame.image.load("img/r_box.png").convert_alpha()
text = '请选择谁先走'
win = font.render(text,False,(255,200,10))
fpsClock = pygame.time.Clock()
h=(0,57,114,171,228,285,342,399,456,-100)
z=(0,57,114,171,228,285,342,399,456,513,-100)
q=[
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
]
x,y = 9,10
x0,y0 = 9,10
x1,y1 = 9,10
eat = ''
is_select=False
is_go = False
is_red = 1
is_ai_red=2#AI执红为1,执黑为0,玩家互玩设为2,默认为2
is_reverse = False#翻转棋盘
def reverseqp():
global is_reverse
is_reverse = not is_reverse
global q
q.reverse()
global y
global y0
global y1
y,y0,y1 = 9-y,9-y0,9-y1
def restartgame(isred):
global text
global win
text = '游戏开始,请下第一步' if isred == 0 else '电脑思考中'
win = font.render(text,False,(255,200,10))
global q
global is_reverse
q=[
['b_c','b_m','b_x','b_s','b_j','b_s','b_x','b_m','b_c',],
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
['xxx','b_p','xxx','xxx','xxx','xxx','xxx','b_p','xxx',],
['b_z','xxx','b_z','xxx','b_z','xxx','b_z','xxx','b_z',],
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
['r_z','xxx','r_z','xxx','r_z','xxx','r_z','xxx','r_z',],
['xxx','r_p','xxx','xxx','xxx','xxx','xxx','r_p','xxx',],
['xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx',],
['r_c','r_m','r_x','r_s','r_j','r_s','r_x','r_m','r_c',],
]
if isred:
is_reverse = True
q.reverse()
else:
is_reverse = False
global x
global y
x,y = 9,10
global x0
global y0
x0,y0 = 9,10
global x1
global y1
x1,y1 = 9,10
global is_select
is_select=False
global is_go
is_go = False
global is_red
is_red = 1
global is_ai_red
is_ai_red=isred#AI执红为1,执黑为0,玩家互玩设为2
message('start')#重新开始一局游戏
def can(qp,qz,x,y):#着法合规性判断函数
n=[]
x1,y1,x2,y2=x,y,x,y
if qz[2]=='c':
while x1<8:#向右搜索
x1+=1
n.append((x1,y))
if qp[y][x1][1]=='_':
break
while x2>0:#向左搜索
x2-=1
n.append((x2,y))
if qp[y][x2][1]=='_':
break
while y1<9:#向下搜索
y1+=1
n.append((x,y1))
if qp[y1][x][1]=='_':
break
while y2>0:#向上搜索
y2-=1
n.append((x,y2))
if qp[y2][x][1]=='_':
break
elif qz[2]=='m':
x1+=1
x2-=1
y1+=1
y2-=1
if -1<x<7 and qp[y][x1]=='xxx':#向右
if y<9:
n.append((x1+1,y1))#右下
if y>0:
n.append((x1+1,y2))#右上
if 1<x<9 and qp[y][x2]=='xxx':#向左
if y<9:
n.append((x2-1,y1))#左下
if y>0:
n.append((x2-1,y2))#左上
if -1<y<8 and qp[y1][x]=='xxx':#向下
if x<8:
n.append((x1,y1+1))#下右
if x>0:
n.append((x2,y1+1))#下左
if 1<y<10 and qp[y2][x]=='xxx':#向上
if x<8:
n.append((x1,y2-1))#上右
if x>0:
n.append((x2,y2-1))#上左
elif qz[2]=='p':
o=1
while x1<8:#向右搜索
x1+=1
if o and qp[y][x1]=='xxx':
n.append((x1,y))
if qp[y][x1][1]=='_':
if not o:
n.append((x1,y))
break
o=0
o=1
while x2>0:#向左搜索
x2-=1
if o and qp[y][x2]=='xxx':
n.append((x2,y))
if qp[y][x2][1]=='_':
if not o:
n.append((x2,y))
break
o=0
o=1
while y1<9:#向下搜索
y1+=1
if o and qp[y1][x]=='xxx':
n.append((x,y1))
if qp[y1][x][1]=='_':
if not o:
n.append((x,y1))
break
o=0
o=1
while y2>0:#向上搜索
y2-=1
if o and qp[y2][x]=='xxx':
n.append((x,y2))
if qp[y2][x][1]=='_':
if not o:
n.append((x,y2))
break
o=0
elif qz[2]=='x':
x1+=2
x2-=2
y1+=2
y2-=2
if y==5 or y==0 or x==4:
if qp[y+1][x+1]=='xxx':
n.append((x1,y1))
if qp[y+1][x-1]=='xxx':
n.append((x2,y1))
if y==9 or y==4 or x==4:
if qp[y-1][x+1]=='xxx':
n.append((x1,y2))
if qp[y-1][x-1]=='xxx':
n.append((x2,y2))
elif x==0:
if qp[y+1][x+1]=='xxx':
n.append((x1,y1))
if qp[y-1][x+1]=='xxx':
n.append((x1,y2))
elif x==8:
if qp[y+1][x-1]=='xxx':
n.append((x2,y1))
if qp[y-1][x-1]=='xxx':
n.append((x2,y2))
elif qz[2]=='s':
if y==0 or y==7:
n.append((4,y+1))
elif y==2 or y==9:
n.append((4,y-1))
else:
n.append((x+1,y+1))
n.append((x+1,y-1))
n.append((x-1,y+1))
n.append((x-1,y-1))
elif qz[2]=='j':
if x==3 or x==5:
n.append((4,y))
else:
n.append((3,y))
n.append((5,y))
if y==0 or y==2:
n.append((x,1))
elif y==7 or y==9:
n.append((x,8))
else:
n.append((x,y+1))
n.append((x,y-1))
elif qz[2]=='z':
if qz[0]==('b' if is_reverse else 'r'):
if y!=0:
n.append((x,y-1))
if y<5:
if x>0:
n.append((x-1,y))
if x<8:
n.append((x+1,y))
else:
if y!=9:
n.append((x,y+1))
if y>4:
if x>0:
n.append((x-1,y))
if x<8:
n.append((x+1,y))
return n
while True:
#以下是刷新显示程序
screen.blit(background, (0, 0))# 画上背景图
screen.blit(space, (2,0))#画上棋盘
screen.blit(button_b, (2,577))
screen.blit(button_i,(30,680))
screen.blit(button_u,(290,680))
screen.blit(win,(20, 600))
#画上棋子
for i in range(10):
for j in range(9):
if q[i][j]!='xxx':
if q[i][j][0]=='b':
if q[i][j][2]=='j':
screen.blit(b_j, (h[j],z[i]))
elif q[i][j][2]=='s':
screen.blit(b_s, (h[j],z[i]))
elif q[i][j][2]=='x':
screen.blit(b_x, (h[j],z[i]))
elif q[i][j][2]=='m':
screen.blit(b_m, (h[j],z[i]))
elif q[i][j][2]=='c':
screen.blit(b_c, (h[j],z[i]))
elif q[i][j][2]=='p':
screen.blit(b_p, (h[j],z[i]))
else:
screen.blit(b_z, (h[j],z[i]))
else:
if q[i][j][2]=='j':
screen.blit(r_j, (h[j],z[i]))
elif q[i][j][2]=='s':
screen.blit(r_s, (h[j],z[i]))
elif q[i][j][2]=='x':
screen.blit(r_x, (h[j],z[i]))
elif q[i][j][2]=='m':
screen.blit(r_m, (h[j],z[i]))
elif q[i][j][2]=='c':
screen.blit(r_c, (h[j],z[i]))
elif q[i][j][2]=='p':
screen.blit(r_p, (h[j],z[i]))
else:
screen.blit(r_z, (h[j],z[i]))
if is_select==True:
screen.blit(select, (h[x],z[y]))#画上选择框
if is_go==True and is_select==False:
#画上两个选择框
screen.blit(select, (h[x0],z[y0]))
screen.blit(select, (h[x1],z[y1]))
pygame.display.update()# 刷新画面
fpsClock.tick(30)
#以下是下棋控制
if is_ai_red == is_red:#轮到AI下
msg = str(x)+str(9-y if is_reverse else y)+str(x1)+str(9-y1 if is_reverse else y1)
data = message(msg)
if '送将' in data[4:]:
text = data[4:]
win = font.render(text,False,(255,200,10))
q[y][x]=q[y1][x1]#恢复原位
q[y1][x1]=eat#空位还原
is_red=1-is_red
continue
x,y,x1,y1 = int(data[0]), 9-int(data[1]) if is_reverse else int(data[1]),int(data[2]),9-int(data[3]) if is_reverse else int(data[3])
q[y1][x1]=q[y][x]#吃空气或吃子
q[y][x]='xxx'#原位留空
x0,y0=x,y#记录位置赋值
is_red = 1-is_red
is_go = True
is_select = False
if len(data) > 4:
#显示文本并跳出循环,等待玩家重启游戏
text = data[4:]
win = font.render(text,False,(255,200,10))
is_ai_red=2
continue
text = '轮到你下了'
win = font.render(text,False,(255,200,10))
else:#玩家下
#screen.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == QUIT:#接收到退出事件后退出程序
pygame.quit()
exit()
#选取棋子
if event.type == MOUSEBUTTONUP:#鼠标点击后松开
mp = pygame.mouse.get_pos()#获取鼠标点击位置
#先判断是否点击了控制按钮
if 760>mp[1]>680:
if 230>mp[0]>30:#我先
restartgame(0)
elif 490>mp[0]>290:
restartgame(1)
break
if is_go == False and is_select == True:#已选中棋子
#判断鼠标点击的位置是否能落子
if mp[0] < h[0] or mp[0] >= h[8]+57 or mp[1] < z[0] or mp[1] >= z[9]+57:
pass
else:
x1 = mp[0]//57
y1 = mp[1]//57
if q[y1][x1][0] != 'r' if is_red else 'b':#如果选择的落子点上没有红棋/黑棋
#落子
is_can = can(q, q[y][x],x,y)
if (x1,y1) in is_can:
eat = q[y1][x1]#记录吃了啥子,用于送将退回
q[y1][x1]=q[y][x]#吃空气或吃子
q[y][x]='xxx'#原位留空
x0,y0=x,y#记录位置赋值
is_red = 1 - is_red
is_go = True
is_select=False
text = '电脑思考中'
win = font.render(text,False,(255,200,10))
else:#不能落子:
is_select=False
else:#未选中棋子
if mp[0] < h[0] or mp[0] >= h[8]+57 or mp[1] < z[0] or mp[1] >= z[9]+57:
reverseqp()#点在棋盘正下方的其他位置,翻转显示
continue
else:
x = mp[0]//57
y = mp[1]//57
if is_red == 1:
if q[y][x][1] == '_' and q[y][x][0] == 'r':
is_select=True
is_go=False
else:
is_select=False
else:
if q[y][x][1] == '_' and q[y][x][0] == 'b':
is_select=True
is_go=False
else:
is_select=False
- 最后是课后作业:给这个电脑加上开局库、中局库、残局库(这些都自己去找),添加查库逻辑(可以用二分查找算法),让电脑的棋力达到职业九段;使用socket编程,让这个程序能在局域网内实现双人下棋,多人观战(使用协程添加计时器);添加音效和背景音乐。
参考资料:电脑象棋循序渐进的算法和c++代码,不理解AI算法原理的,可以点进去看看。