算法作业喜加一。这alpha-Beta剪枝也太绕了
看了Tech With Tim的教程……真的好厉害噢
这个跳棋和中国的三角式还不太一样,棋盘是8*8的
大概就是两边各执一方,然后正常跳,跳过去的时候会把对手棋子吃掉,跳到对面最后一列就会变成KING,然后可以往回反跳。
最后就是看谁剩下的棋子多谁赢。
所以
他的总和就是比如图上。
白色棋子数减去红色棋子数。
白方希望这个数越大,而红方希望这个数越小。
对于每个棋子,
所有的可能性行为就是一棵搜索树。
所以就成了经典的的最大最小博弈算法。
#伪代码:
function minimax(node, depth, maximizingPlayer)
if depth ==0 or node is a terminal node then
return static evaluation of node
if MaximizingPlayer then // for Maximizer Player
maxEva= -infinity
for each child of node do
eva= minimax(child, depth-1, false)
maxEva= max(maxEva,eva) //gives Maximum of the values
return maxEva
else // for Minimizer player
minEva= +infinity
for each child of node do
eva= minimax(child, depth-1, true)
minEva= min(minEva, eva) //gives minimum of the values
return minEva
minimax 算法的主要缺点是对于复杂的游戏,比如国际象棋、围棋等,它变得非常慢。这种类型的游戏具有数量庞大的分支node,但是可以通过alpha-beta 剪枝算法来改进。
Alpha-Beta剪枝用于裁剪所有不会真正影响最终决策但使算法变慢的节点,以提高运算速度,可以在不检查搜索树的每个节点的情况下计算正确的极小极大决策,涉及到两个阈值参数 Alpha 和 bet。
Alpha:到目前为止,在 Maximizer 路径上的任何一点找到的最佳(最高值)选择。alpha 的初始值为-∞。
Beta:在 Minimizer 路径上的任何一点找到的最佳(最低值)选择。beta 的初始值为+∞。
当一个 Min 节点的 β值≤任何一个父节点的α值时 ,剪掉该节点的所有子节点(下一个节点对应Min就更新β值)
当一个 Max 节点的 α值≥任何一个父节点的β值时 ,剪掉该节点的所有子节点(下一个节点对应Max就更新 α值)
每一层都需要判断是否α>=β ,如果是,就终止遍历进行pruning
总结一下关键点就是:
max对应α
min对应β
α>=β就剪枝
#伪代码
function minimax(node, depth, alpha, beta, maximizingPlayer) is
if depth ==0 or node is a terminal node then
return static evaluation of node
if MaximizingPlayer then
for Maximizer Player
maxEva= -infinity
for each child of node do
eva= minimax(child, depth-1, alpha, beta, False)
maxEva= max(maxEva, eva)
alpha= max(alpha, maxEva)
if beta<=alpha
break
return maxEva
else
for Minimizer player
minEva= +infinity
for each child of node do
eva= minimax(child, depth-1, alpha, beta, true)
minEva= min(minEva, eva)
beta= min(beta, eva)
if beta<=alpha
break
return minEva
虽然很绕,但是youtube随便搜了个教学视频就懂了
但是蛮怪的,这个alpha beta
到底是哪个神仙发明的算法
好绕噢
构建while loop循环游戏
棋子类
棋盘类,实例化每个棋子
如果棋子剩余数量为0结束游戏
属性有行,列。(x,y)颜色,是否为王
[downside_red,downside_blue,upside_red,upside_blue] 0 is false
计算位置:是对应的行列+格子中心
x = SQUARE_SIZE * self.col + SQUARE_SIZE // 2
y = SQUARE_SIZE * self.row + SQUARE_SIZE // 2
然后定义获取是否是KING,是的话在中心位置把王冠图叠上去。
接着移动棋子的方法也放在这里,把棋子行列更新一下后计算新的位置
最后用 __repr__方法,返回棋子的颜色。
属性有双方棋子数,困难度
参数ROW和COL都是8
每个格子大小就是设置的页面大小整除格子长宽
#一个隔一个涂色
for col in range(row % 2, COLS, 2)
pygame.draw.rect
pygame.draw.rect(Surface, color, Rect, width=0): return Rect
在Surface上绘制矩形,第二个参数是线条(或填充)的颜色,第三个参数Rect的形式是((x, y), (width, height)),表示的是所绘制矩形的区域,其中第一个元组(x, y)表示的是该矩形左上角的坐标,第二个元组 (width, height)表示的是矩形的宽度和高度。width表示线条的粗细,单位为像素;默认值为0,表示填充矩形内部。
(row*SQUARE_SIZE, col *SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE)
元组区域就是(对应的行,对应的列,格子的宽,格子的高)
在移动方法里
把新位置和棋子的位置进行交换,如果是国王行,对应国王+1且调用棋子里的国王方法。
self.board[piece.row][piece.col], self.board[row][col] = self.board[row][col], self.board[piece.row][piece.col]
捕获方法就是把他对应的位置变0,棋子数量-1,捕获的对象如果是国王,则直接获胜触发弑君
所有绘画都在这里,棋盘,棋子,有效的移动
有效移动方法,启发函数,获取所有棋子,获取界面棋子状态(有多少红蓝在上半,多少在下半)
创建一个move的dictionary且返回所有可能性
{(目标位置x,y),[是否有能吃的颜色,返回颜色]}
如果斜对角有棋子且颜色相同,break
有棋子且颜色不同,吃掉,并把吃掉的位置保存,
没有棋子,遍历移动
如果吃完后没有下一步的位置了,break(比如边界)
吃完后有棋子,重复颜色判断
最终返回所有的可能性
这段其实是抄的实在有点绕不出来了
但是抄完感觉懂了(。)
在game类里实例board
属性有pygame界面,有效移动dic{},轮次
select piece funcation选择棋子
update function 更新游戏状态
select function对应回合选择棋子,用到board里validmove
其他杂七杂八的还有改变轮次,和基于board的继承实现
deepcocy复制棋盘,ai改一次复制一次,
棋子,ai的移动,ai吃的棋子,游戏的状态