利用深度优先搜索等算法实现围棋棋盘控制

前言

大家好~今天,是 2022 年的第一天,祝大家元旦快乐!
在 2021 的一年中,我一直作为浏览者,未曾写过一篇博客。在2022年里,我会争取将在开发中遇到并解决的问题分享出来,希望大家能够多多支持🤝

棋盘控制功能

需求简介

由于开发内容所需,要实现一个函数,即将输入的原始棋盘转为合法的盘面并返回。

目前功能

到现在为止,Chessboard Controller 可以实现最为基本的提掉没有气的棋子的功能。不过考虑到禁入点、打劫和类似打二还一的在自己落下棋子暂时没有气的情况下提掉对方棋子的特殊情况还不能支持。该文章中,仅探讨深度优先搜索实现的划分棋块,以及数气的功能。关于上述复杂情况,将在以后陆续实现。

项目代码

该项目代码暂时只在放在 CSDN 上,关于 Github 的共享在完善之后放上去~ 源码地址见:
https://download.csdn.net/download/meituwo/72398295
当然,考虑到语言的阅读方便,其注释及帮助文档都使用了英文。具体的实现过程(中文)自然在本篇中详细解释。

实现过程

创建指令集

为实现操作时的可读性,我们新建Order.py文件,并在之后每次用到时向其添加指令(常量)。

# Order.py

创建存储元素的对象

为处理方便,控制棋盘所需三个类:棋子、棋块、棋盘。

棋子类代码
# Order.py

EMPTY = 0
BLACK = 1
WHITE = 2
# Chessboard.py

class Piece(object):
	  
	def __init__(self, chessboard, y, x, color):
		self.chessboard = chessboard  
		self.y = y  
		self.x = x  
		self.color = color  
		self.opponent = [Order.BLACK, Order.WHITE][[Order.BLACK, Order.WHITE].index(self.color) - 1] \  
		    if color in [Order.BLACK, Order.WHITE] else Order.EMPTY

	def __str__(self):
		color = None  
  
		match self.color:  
		  case Order.BLACK:  
		        color = "Black"  
		  case Order.WHITE:  
		        color = "White"  
		  case _:  
		        color = "Empty"  
		  
		return f"Piece(y: {self.y}, x: {self.x},  color: {color})"
代码解释

Order.py中创建了 3 个常量:
EMPTY,表示该格为空
BLACK,表示该格为黑子
WHITE,表示该格为白子

Chessboard.py中创建了 1 个类:
Piece(
chessboard 该棋子所在的棋盘(二维数组,元素用Order.py的三个指令表示)
y 该棋子所处的y轴
x 该棋子所处的x轴
color 该棋子的颜色(也可为 Order.EMPTY )
)

实例变量中的 opponent :
与该棋子颜色相对的颜色(若该格为空则没有敌方颜色)

格式化 __str__ :
注意 其中所用的 match-case 是 Python 3.10 才有的新语法,若版本过低可以使用 if-elif-else 解决。

# if-elif-else 实现方式

if self.color == Order.BLACK:
	color = "Black"
elif self.color == Order.WHITE:
	color = "White"
else:
	color = "Empty"
棋块类代码
# Order.py

PIECE = 3
# Chessboard.py

class ChessPiece(object):
	
	def __init__(self, chesspiece, pieceColor):
		self.chessPiece = chessPiece  
		self.pieceColor = pieceColor  
		self.detailPiece = [  
		 [Piece(self.chessPiece, y, x, self.chessPiece[y][x]) for x in range(len(self.chessPiece[y]))]  
		  for y in range(len(self.chessPiece))  
		]  
		self.pieces = [x for y in self.detailPiece for x in y if x.color == Order.PIECE]
代码解释

Order.py中新建了 1 个常量:
PIECE,以标记棋块中的棋子

Chessboard.py中新建了 1 个类:
Chesspiece(
chesspiece 整个二维数组表示的棋盘,其中该棋块的部分用PIECE标示出
pieceColor 该棋块的颜色,Order.BLACK 或 Order.WHITE
)
该类中的几个实例变量:
detailPiece 由 Piece( … ) 组成的详细的棋盘
pieces 存储self.detailPiece 中属于该棋块(即 color 为 PIECE )的棋子( Piece( … ) )列表

棋盘类代码
# Chessboard.py

class Chessboard(object):
	def __init__(self, chessboardList):
		self.rawChessboard = chessboardList  
		self.detailChessboard = [  
		    [Piece(self.rawChessboard, y, x, self.rawChessboard[y][x]) for x in  
		    range(len(self.rawChessboard[y]))]  
		    for y in range(len(self.rawChessboard))  
		]
代码解释

Chessboard.py中新建了 1 个类:
Chessboard(
chessboardList 整个棋盘的二维数组,由 BLACK, WHITE 或 EMPTY 填充
)

加入功能

功能叙述

为更便利地查找某个棋子周围的棋子信息,我们首先要在棋子类中添加查找旁边棋子功能。

代码实现
# Chessboard.py

class Piece(object);
	  
	def getUp(self):  
	  if self.y == 0:  
	        return Piece(self.chessboard, -1, -1, self.opponent)  
	  
	  return Piece(self.chessboard, self.y - 1, self.x, self.chessboard[self.y - 1][self.x])  
	  
	def getDown(self):  
	  if self.y == len(self.chessboard[self.x]) - 1:  
	        return Piece(self.chessboard, -1, -1, self.opponent)  
	  
	  return Piece(self.chessboard, self.y + 1, self.x, self.chessboard[self.y + 1][self.x])  
	  
	def getLeft(self):  
	  if self.x == 0:  
	        return Piece(self.chessboard, -1, -1, self.opponent)  
	  
	  return Piece(self.chessboard, self.y, self.x - 1, self.chessboard[self.y][self.x - 1])  
	  
	def getRight(self):  
	  if self.x == len(self.chessboard) - 1:  
	        return Piece(self.chessboard, -1, -1, self.opponent)  
	  
	  return Piece(self.chessboard, self.y, self.x + 1, self.chessboard[self.y][self.x + 1])
代码解释

Chessboard.Piece 类中新建了 4 个方法:
getUp 获取该棋子上方的棋子
getDown 获取该棋子上方的棋子
getLeft 获取该棋子上方的棋子
getRight 获取该棋子上方的棋子
注意:
每个方法前的 if 检测的是该棋子是否处在边缘,若是,则返回敌方棋子(即该处没有气)。

功能叙述

除棋子外,棋块类应当有对该棋块的数气功能。数气可以在初始化(__init__)时数出并保存在实例变量中,需要时取出。

代码实现
# Chessboard.py

class ChessPiece(object):
	
	def __init__(self):
		self.liberty = 0  
		for y in self.detailPiece:  
		    for x in y:  
		        if x.color == Order.EMPTY:  
		            if self.pieceColor in [  
						x.getUp().color,  
						x.getDown().color,  
						x.getLeft().color,  
					    x.getRight().color  
		            ]:  
		                self.liberty += 1
代码解释

在 Chessboard.Piece 的初始化方法中新建了 liberty (棋块气数) 的实例变量,在循环中遍历棋盘每个格,若该格四周有棋块棋子,则将气数加一。

功能叙述

在给 Piece 、 ChessPiece 两个类添加了基础功能之后,最重要的棋盘分块功能就要开始着手实现了。它运用了关于深度优先搜索的知识,在 Chessboard 类的初始化时将棋盘分为几个棋块,在获取合法棋盘时实现提子并返回。
在使用深度优先技术时,需要用到栈。这里,我自己实现了栈类:

# Chessboardpy

class Stack(object):
	def __init__(self, initial: list = None):  
	    self.stack = [] if not initial else initial  
	  
	def __str__(self):  
	    return f"({', '.join(self.stack)})"  
	  
	def push(self, item: any):  
	    self.stack.append(item)  
	  
	def pop(self):  
	    self.stack.pop(-1)  
	      
	def peek(self):  
	    return self.stack[-1]
	    
	def isNull(self):  
	    return self.stack

具体代码解释没什么可说的, __str__ 魔法方法定义了较友好的输出。下面是棋盘分块代码。

代码实现
# Order.py

UP = 4  # 深度优先搜索路径-上
DOWN = 5  # 深度优先搜索路径-下  
LEFT = 6  # 深度优先搜索路径-左  
RIGHT = 7  # 深度优先搜索路径-右
  
class Chessboard(object):  

  def __init__(self, chessboardList):
	  self.chunkedChessboard = []

       for yIndex, yItem in enumerate(self.detailChessboard):
           for xIndex, xItem in enumerate(yItem):
               if xItem.color != Order.PIECE and xItem.color != Order.EMPTY:
                   chessboard = deepcopy(self.detailChessboard)  # 内置库copy.deepcopy深拷贝,以防误更改实例变量chessboard
                   rawChessboard = deepcopy(self.rawChessboard)  # 同上
                   piece = xItem
                   pieceColor = piece.color
                   chessboard[piece.y][piece.x].color = Order.PIECE
                   rawChessboard[piece.y][piece.x] = Order.PIECE
                   surrounding = [  # 该棋子周围颜色
                       piece.getUp().color,
                       piece.getDown().color,
                       piece.getLeft().color,
                       piece.getRight().color
                   ]
                   record = Stack()

                   while pieceColor in surrounding \  # 若周围有同颜色棋子
                           or Order.PIECE in surrounding:  # 回溯

                       if pieceColor in surrounding:
                           chessboard[piece.y][piece.x] = Piece(rawChessboard, piece.y, piece.x, Order.PIECE)  # 在临时的变量 chessboard 中标记  注意与实例变量 chessboard 区分

                           if piece.getUp().color == pieceColor:
                               piece = piece.getUp()  # 移动当前棋子
                               record.push(Order.UP)  # 路径添加
                           elif piece.getDown().color == pieceColor:
                               piece = piece.getDown()  # 移动当前棋子
                               record.push(Order.DOWN)  # 路径添加
                           elif piece.getLeft().color == pieceColor:
                               piece = piece.getLeft()  # 移动当前棋子
                               record.push(Order.LEFT)  # 路径添加
                           elif piece.getRight().color == pieceColor:
                               piece = piece.getRight()  # 移动当前棋子
                               record.push(Order.RIGHT)  # 路径添加
                       else:
                           if record.isNull():  # 若已回到原点(注意:先判断的是周围是否有同颜色棋子,即回原点且周围无其余可探索路径),跳出while循环
                               break

                           elif piece.getUp().color == Order.PIECE and record.peek()() == Order.DOWN:
                               piece = piece.getUp()
                               record.pop()  # 弹出最后一项
                           elif piece.getDown().color == Order.PIECE and record.peek()() == Order.UP:
                               piece = piece.getDown()
                               record.pop()
                           elif piece.getLeft().color == Order.PIECE and record.peek()() == Order.RIGHT:
                               piece = piece.getLeft()
                               record.pop()
                           elif piece.getRight().color == Order.PIECE and record.peek()() == Order.LEFT:
                               piece = piece.getRight()
                               record.pop()

                   self.chunkedChessboard.append(ChessPiece(rawChessboard, pieceColor))  # 向实例变量 chunkedChessboard 中添加棋块
    def legalChessboard(self):
    chessboard = deepcopy(self.rawChessboard)  
  
	for chessPiece in self.chunkedChessboard:  
	    if chessPiece.liberty == 0:  
	        for piece in chessPiece.pieces:  
	            chessboard[piece.y][piece.x] = Order.EMPTY  # 提子
	  
	return chessboard  # 返回合法盘面
代码解释

代码较长,解释较多,故解释已作为注释放在代码中。

总结

这篇关于利用深度优先实现的围棋盘面转换工具的博客就到此结束了。希望得到大家的支持~
再见!

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__程序喵__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值