象棋棋盘棋子棋谱如何抽象建模?(语言不限)

象棋棋盘棋子棋谱如何抽象建模?(语言不限)
Image
Image

  1. 如何面向对象地对象棋棋盘/棋子/棋谱进行抽象建模?

  2. 附加问题:两个人下棋,P2P或者C-S架构都能实现。但如果是竞技场多人模式(例如四国军棋、跳棋)呢?不需要考虑机器AI,我们只考虑真人对弈。

阅读 5.1k
2 个回答

如果只是想建模出一个从棋谱读取行期记录的中国象棋软件, 可以像这样建模:

clipboard.png

但是经过反思, 发现以下问题:

  1. 没有必要将棋盘和位置各独立出一个类, 因为我们关心的不是棋盘的行为与属性, 而是棋子的
    由于棋谱的位置特性, 位置类需要保留, 棋盘可以由一个二维的位置类数组记录即可.

根据单一职责原则, 保留棋盘与位置类.

  1. 各类棋子行棋规则写在对应具体类型的方法moveByRecord里面, 不方便管理规则

    为何不把所有规则写在一起, 然后根据棋子类型来检查行棋是否符合规则?

    (嗯, 今晚比较晚了, 再加上过年, 过几天有空再更新一下思考结果.)

借用一个现成的3D模型

Python 实现一个中国象棋的棋盘建模
代码:

#-*-coding:utf8;-*-

class GameBoard:
# 中国象棋棋盘 90 格的坐标约定从 0,0 到 8,9
# 国际象棋棋盘 64 格的坐标约定从 0,0 到 7,7
# 如果没有棋子则在格子上标记 0, 如果有棋子, 则在该格子上放置棋子 ID
# ID>0 表示有棋子, 注意 ID=0 或 None 都代表格子上没有棋子, ID<0 会导致错误
# 棋子从一个格子走到另一个格子后,擦除脚印,只保留棋子当前位置
# GameBoard.hasPieceAtCoordinate([x,y]) 检查棋盘坐标 x,y 位置, 如果发现有棋子则返回棋子的 ID, 否则默认返回 0(或 None)表示 false
# GameBoard.dump(sys.stdout) 可以通过终端命令提示符窗口(或指定其他日志文件)查看当前棋盘

    """GameBoard for 围棋、象棋等棋类游戏的棋盘建模"""

    def width(self): # 棋盘宽度
        return self.__width
    def height(self): # 棋盘高度
        return self.__height
    def __init__(self, width, height):
        self.__width = width
        self.__height = height
        self.__pieceNameList = [] # 存储所有棋子的名字字符串, 初始状态为空列表
        # __battlefield[y][x] 对应坐标点 x,y 处的棋子ID, 初始值 0 表示格子上没有棋子
        self.__battlefield = [[0 for x in range(width)] for y in range(height)]
        self.__survivors = {} # 字典映射记录棋盘上每个棋子的位置, 以棋子 pieceId 为键, 以绝对坐标为值
    def __del__(self):
        pass
    def dump(self, file):
        borad=[
            '┌┬┬┲┳┱┬┬┐',
            '├┼┼╊╳╉┼┼┤',
            '├╬┼╄╇╃┼╬┤',
            '╠┼╬┼╬┼╬┼╣',
            '├┴┴┴┴┴┴┴┤',
            '├┬┬┬┬┬┬┬┤',
            '╠┼╬┼╬┼╬┼╣',
            '├╬┼╆╈╅┼╬┤',
            '├┼┼╊╳╉┼┼┤',
            '└┴┴┺┻┹┴┴┘']
        borad.reverse()
        debugdumpfile = file
        if not file:
            import os
            # 默认将调试日志文件输出到 os.devnull 不输出调试信息
            debugdumpfile = open(os.devnull, 'wa') # 调试信息的设置
        for y in reversed(range(self.__height)):
            for x in range(self.__width):
                id = self.__battlefield[y][x]
                if not id or id<=0: # 0 表示当前格子无棋子, id=None 时也满足该条件, id<0 为异常状态
                    name = borad[y][x]
                else:
                    name = self.__pieceNameList[id-1] # 备忘: ID 最小值是从 1 开始的, 但数组下标是从 0 开始的
                print(name, end='', file=debugdumpfile)
            print(file=debugdumpfile)
        print('棋盘上有 %d 个棋子' % len(self.__survivors), file=debugdumpfile)
        if not file:
            debugdumpfile.close()
    def makeIdForNewChessPiece(self, pieceName="", coordinate=None):
        self.__pieceNameList.append(pieceName)
        pieceId = len(self.__pieceNameList)
        try:
            x,y = coordinate
        except ValueError: # coordinate 必须是 x,y 坐标形式, 否则触发 ValueError 异常
            pass           # 注: 这里允许调用者定义一开始不放在棋盘上的棋子
        else:
            if (x<0 or x>=self.__width):
                raise ValueError('Error: Invalid coordinate=%s' % (str(coordinate)))
            if (y<0 or y>=self.__height):
                raise ValueError('Error: Invalid coordinate=%s' % (str(coordinate)))
            self.__battlefield[y][x] = pieceId
            # 注: 考虑以后需要独立更改 x 和 y 的坐标值, 下面存储坐标用的是 [x,y] 而不是 (x,y)
            self.__survivors[pieceId] = [x, y] # 词典以 pieceId 为键, 以棋子坐标为值
        return (pieceId) # 返回值最小从 1 开始表示有棋子, pieceId==0 的棋盘格子无棋子
    def hasPieceAtCoordinate(self, coordinate):
        x,y = coordinate # coordinate 必须是 x,y 坐标形式 ----FIXME: 检查参数
        if (x<0 or x>=self.__width):
            return False
        if (y<0 or y>=self.__height):
            return False
        pieceId = self.__battlefield[y][x]
        return pieceId # 注: pieceId 等于 0 时表示当前格子无棋子
    def findPiece(self, pieceId):
        # 备注: 棋盘上找不到棋子时直接抛出 ValueError 异常, 如果找到则返回坐标
        if not pieceId or pieceId<=0 or pieceId>len(self.__pieceNameList): # None 、负数或 0 均为无效棋子 ID
            raise ValueError('Error: Invalid ID=%s' % (str(pieceId)))
        try:
            x,y = self.__survivors[pieceId]
        except KeyError:
            raise ValueError('Notice: 棋盘上找不到棋子 %s' % (self.__pieceNameList[pieceId]))
        return x,y
    def movePieceToCoordinate(self, pieceId, coordinate):
        # 备注: 棋盘上找不到棋子时直接抛出 ValueError 异常, 如果找到则移动棋子到新坐标
        if not pieceId or pieceId<=0 or pieceId>len(self.__pieceNameList): # None 、负数或 0 均为无效棋子 ID
            raise ValueError('Error: Invalid ID=%s' % (str(pieceId)))
        x,y = coordinate # coordinate 必须是 x,y 坐标形式 ----FIXME: 检查参数
        if (x<0 or x>=self.__width):
            raise ValueError('Error: Invalid coordinate=%s' % (str(coordinate)))
        if (y<0 or y>=self.__height):
            raise ValueError('Error: Invalid coordinate=%s' % (str(coordinate)))
        try:
            x0,y0 = self.findPiece(pieceId)
        except ValueError as e:
            raise
        else:
            self.__battlefield[y0][x0] = 0
        try: # 从棋盘上移除被吃掉的棋子
            del self.__survivors[self.__battlefield[y][x]]
        except KeyError:
            pass
        self.__battlefield[y][x] = pieceId
        # survivors 字典中的坐标值可以原地修改:
        self.__survivors[pieceId][0],self.__survivors[pieceId][3] = x,y
    def position(self): # 棋盘上剩余的棋子
        return self.__survivors.copy()
    def pieceIsAlive(self, pieceId):
        return ( pieceId in self.__survivors )
# 以下为模块自测试代码
def main():
    import sys
    global __name__
    print('模块名:', __name__)
    print('创建中国象棋初始棋盘')
    brd = GameBoard(9, 10)
    class Player:
         def __init__(self, name):
            self.__name = name
            self.rooks = [None]*2
            self.knights = [None]*2
            self.bishops = [None]*2
            self.guards = [None]*2
            self.general = None
            self.cannons = [None]*2
            self.pawns = [None]*5
    black = Player('黑方')
    black.rooks[0] = brd.makeIdForNewChessPiece("車", (0, 9))
    black.rooks[1] = brd.makeIdForNewChessPiece("車", (8, 9))
    black.knights[0] = brd.makeIdForNewChessPiece("马", (1, 9))
    black.knights[1] = brd.makeIdForNewChessPiece("马", (7, 9))
    black.bishops[0] = brd.makeIdForNewChessPiece('象', (2, 9))
    black.bishops[1] = brd.makeIdForNewChessPiece('象', (6, 9))
    black.guards[0] = brd.makeIdForNewChessPiece('士', (3, 9))
    black.guards[1] = brd.makeIdForNewChessPiece('士', (5, 9))
    black.general = brd.makeIdForNewChessPiece('將', (4, 9))
    black.cannons[0] = brd.makeIdForNewChessPiece('砲', (1, 7))
    black.cannons[1] = brd.makeIdForNewChessPiece('砲', (7, 7))
    black.pawns = [brd.makeIdForNewChessPiece('卒', (0, 6)),
                   brd.makeIdForNewChessPiece('卒', (2, 6)),
                   brd.makeIdForNewChessPiece('卒', (4, 6)),
                   brd.makeIdForNewChessPiece('卒', (6, 6)),
                   brd.makeIdForNewChessPiece('卒', (8, 6))]
    red = Player('红方')
    red.rooks[0] = brd.makeIdForNewChessPiece("俥", (0, 0))
    red.rooks[1] = brd.makeIdForNewChessPiece("俥", (8, 0))
    red.knights[0] = brd.makeIdForNewChessPiece("馬", (1, 0))
    red.knights[1] = brd.makeIdForNewChessPiece("馬", (7, 0))
    red.bishops[0] = brd.makeIdForNewChessPiece('相', (2, 0))
    red.bishops[1] = brd.makeIdForNewChessPiece('相', (6, 0))
    red.guards[0] = brd.makeIdForNewChessPiece('仕', (3, 0))
    red.guards[1] = brd.makeIdForNewChessPiece('仕', (5, 0))
    red.general = brd.makeIdForNewChessPiece('帥', (4, 0))
    red.cannons[0] = brd.makeIdForNewChessPiece('炮', (1, 2))
    red.cannons[1] = brd.makeIdForNewChessPiece('炮', (7, 2))
    red.pawns = [brd.makeIdForNewChessPiece('兵', (0, 3)),
                 brd.makeIdForNewChessPiece('兵', (2, 3)),
                 brd.makeIdForNewChessPiece('兵', (4, 3)),
                 brd.makeIdForNewChessPiece('兵', (6, 3)),
                 brd.makeIdForNewChessPiece('兵', (8, 3))]
    brd.dump(sys.stdout)
    print()

    brd.movePieceToCoordinate(red.cannons[0], (4, 2))
    print('红棋炮二平五:')
    brd.dump(sys.stdout)
    print()

    brd.movePieceToCoordinate(black.knights[0], (2, 7))
    print('黑棋马8进7:')
    brd.dump(sys.stdout)
    print()

    brd.movePieceToCoordinate(red.cannons[0], (4, 6))
    print('红棋炮五进四(吃卒):')
    brd.dump(sys.stdout)
    print()
    game = """
        車┬象士將士象马車
        ├┼┼╊╳╉┼┼┤
        ├砲马╄╇╃┼砲┤
        卒┼卒┼炮┼卒┼卒
        ├┴┴┴┴┴┴┴┤
        ├┬┬┬┬┬┬┬┤
        兵┼兵┼兵┼兵┼兵
        ├╬┼╆╈╅┼炮┤
        ├┼┼╊╳╉┼┼┤
        俥馬相仕帥仕相馬俥
        """

    brd.movePieceToCoordinate(black.knights[0], (4, 6))
    print('黑棋马7进5(吃炮):')
    brd.dump(sys.stdout)
    game = """
        車┬象士將士象马車
        ├┼┼╊╳╉┼┼┤
        ├砲┼╄╇╃┼砲┤
        卒┼卒┼马┼卒┼卒
        ├┴┴┴┴┴┴┴┤
        ├┬┬┬┬┬┬┬┤
        兵┼兵┼兵┼兵┼兵
        ├╬┼╆╈╅┼炮┤
        ├┼┼╊╳╉┼┼┤
        俥馬相仕帥仕相馬俥
        """
if '__main__' == __name__ :
    main()
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进