用pygame制作象棋人机对弈程序

我用python的pygame写了一部中国象棋的人机小游戏。
界面是这样的:


开始界面

我下一步

电脑下一步

步骤如下

  • 首先准备如下素材:


    目录结构
img文件夹中的素材图片、中文字体
  • 然后以一种小学生都能看懂的写代码方式来写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算法原理的,可以点进去看看。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352