前言
大家好~今天,是 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 # 返回合法盘面
代码解释
代码较长,解释较多,故解释已作为注释放在代码中。
总结
这篇关于利用深度优先实现的围棋盘面转换工具的博客就到此结束了。希望得到大家的支持~
再见!