• 用Python3写一个中国象棋游戏


    一:目的

    就是为了熟悉Python语法

    二:效果

    三:使用方式

    1.用Python3运行里面的main.py即可;

    2.wdsa和上右下左键控制光标移动;空格键选中棋子,再按则是相当于移动棋子,如果在原地再按空格键取消选中;

    3.按q结束游戏,或者吃了主帅后结束游戏

    四:源码

    https://github.com/silentdoer/chinese_chess_python3

    支持Linux和Windows(我这边用ubuntu18.04和Win10 Powershell测试的,如果是其他终端不能保证布局会像GIF中的那样整齐,但是功能肯定是没问题的)

    五:源码介绍

    1.首先需要一个能够实时获取按键的工具方法/工具类,否则每次按完上下左右等功能按键后都要按Enter键太不方便,代码如下:

    class Getch():
        """ 用来实现不需要按enter键读取输入字符 """
    
        def __init__(self):
            import platform
            system_name = platform.system()
            if system_name == 'Windows':
                self.impl = GetchWindows()
            else:  # 默认是Linux(目前就支持Windows和Linux即可)
                self.impl = GetchUnix()
    
        def __call__(self): return self.impl()
    
    
    class GetchUnix:
        def __init__(self):
            pass
    
        def __call__(self):
            # 不要用import sys, tty这种逗号的方式导入
            import sys
            import tty
            import termios
            fd = sys.stdin.fileno()
            old_settings = termios.tcgetattr(fd)
            try:
                tty.setraw(sys.stdin.fileno())
                ch = sys.stdin.read(1)
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
            return ch
    
    
    class GetchWindows:
        def __init__(self):
            pass
    
        def __call__(self):
            import msvcrt
            return msvcrt.getch()

    通过getch()就能实时获取按下的按键

    2.通过一个公共模块来实现清屏和打印颜色文本的操作,代码如下:

    def printc(new_line, content, forecolor='', backcolor=''):
        if forecolor != '':
            forecolor = ';' + forecolor
        if backcolor != '':
            backcolor = ';' + backcolor
    
        flag = ''
        if new_line:
            flag = '
    '
        print('33[0' + forecolor + backcolor + 'm' + content + '33[0m', end=flag)
    
    def clear_console():
        import platform
        import os
        system_name = platform.system()
        if system_name == 'Windows':
            os.system("cls")
        else:  # 默认是Linux(目前就支持Windows和Linux即可)
            os.system("clear")

    3.定义一些常量,主要用于描述阵营,棋子类型(如象,馬,車等),颜色常量,代码如下:

    CAMP_RED = '红方'
    CAMP_WHITE = '白方'
    CAMP_NONE = 'NONE'
    CAMP_CURSOR = 'CURSOR'
    
    FORE_RED = '31'
    FORE_YELLOW = '33'
    FORE_BLUE = '34'
    FORE_WHITE = '37'
    BACK_BLACK = '40'
    
    CHESS_CHE = 1
    CHESS_MA = 2
    CHESS_XIANG = 3
    CHESS_SHI = 4
    CHESS_BOSS = 5
    CHESS_PAO = 6
    CHESS_BING = 7
    # 普通的*
    CHESS_PIECE = 8
    CHESS_CURSOR = 9

    4.然后我们需要定义棋子类,这里因为想熟悉Python语法,所以拆成了Chess和ChineseChess来做用来熟悉继承,代码如下:

    class Piece:
        """ 定义棋子基类 """
    
        def __init__(self, pic):
            self._pic = pic
    
        def show(self, new_line=False):
            if new_line:
                print(self._pic)
            else:
                print(self._pic, end='')
    # coding:utf-8
    import piece
    import constants
    import common
    
    
    class ChessPiece(piece.Piece):
        """
        定义象棋棋子
        """
    
        __camp = ''
    
        __type = ''
    
        def __init__(self, pic, camp):
            piece.Piece.__init__(self, pic)
            self.__camp = camp
    
        def get_camp(self):
            return self.__camp
    
        def get_pic(self):
            return self._pic
    
        def set_camp(self, camp):
            self.__camp = camp
    
        def set_pic(self, pic):
            self._pic = pic
    
        def set_type(self, chess_type):
            self.__type = chess_type
    
        def get_type(self):
            return self.__type
    
        def show(self, new_line=False):
            if self.__camp == constants.CAMP_RED:
                common.printc(new_line, self._pic, constants.FORE_RED)
            elif self.__camp == constants.CAMP_WHITE:
                common.printc(new_line, self._pic, constants.FORE_WHITE)
            elif self.__camp == constants.CAMP_CURSOR:
                common.printc(new_line, self._pic, constants.FORE_YELLOW)
            else:
                common.printc(new_line, self._pic, constants.FORE_BLUE)

    5.接下来就是游戏环境类了,这个类比较大,也是关键,线上代码后面会讲一下思路:

    # coding:utf-8
    import sys
    
    from chess_piece import ChessPiece
    import constants
    from getch import Getch
    import chess_strategy
    import common
    
    
    class GameContext:
        """ 游戏环境类 """
    
        game_over = False
        winner = ''
        # 是否已经选中棋子
        piece_checked = False
        # 红方先行
        firster = constants.CAMP_RED
        _pieces = []
        _width = 9
        _height = 10
        _getch = Getch()
        # 字符串默认值可以是'',数组默认值是[],字典默认值是{},数值默认值是0,而对象默认值是None
        _cursor_original_backup = None
        _cursor_loc = {'x': 4, 'y': 1}
        # 用于记录checked后,被checked的piece的坐标,用于判定是否是取消checked
        _checked_loc = {}
        _checked = False
    
        CURSOR_CHECKED = ''
        CURSOR_UNCHECKED = ''
        PIECE_NONE = ''
    
        strategies = None
    
        def __init__(self, camp=constants.CAMP_RED):
            if camp == constants.CAMP_WHITE:
                self._cursor_loc['y'] = 8
            elif camp != constants.CAMP_RED:
                print('阵营参数错误,游戏退出')
                exit()
            self.firster = camp
            # init pieces
            # init NONE Camp
            for y in range(self._height):
                line = []
                for x in range(self._width):
                    tmp = ChessPiece(self.PIECE_NONE, constants.CAMP_NONE)
                    tmp.set_type(constants.CHESS_PIECE)
                    line.append(tmp)
                self._pieces.append(line)
            del line
            # init RED Camp and Pic
            for idx in range(self._width):
                self._pieces[0][idx].set_camp(constants.CAMP_RED)
            self._pieces[0][0].set_pic('')
            self._pieces[0][0].set_type(constants.CHESS_CHE)
            self._pieces[0][1].set_pic('')
            self._pieces[0][1].set_type(constants.CHESS_MA)
            self._pieces[0][2].set_pic('')
            self._pieces[0][2].set_type(constants.CHESS_XIANG)
            self._pieces[0][3].set_pic('')
            self._pieces[0][3].set_type(constants.CHESS_SHI)
            self._pieces[0][4].set_pic('')
            self._pieces[0][4].set_type(constants.CHESS_BOSS)
            self._pieces[0][5].set_pic('')
            self._pieces[0][5].set_type(constants.CHESS_SHI)
            self._pieces[0][6].set_pic('')
            self._pieces[0][6].set_type(constants.CHESS_XIANG)
            self._pieces[0][7].set_pic('')
            self._pieces[0][7].set_type(constants.CHESS_MA)
            self._pieces[0][8].set_pic('')
            self._pieces[0][8].set_type(constants.CHESS_CHE)
            # 上面的camp已经通过循环统一设置过了
            self._pieces[2][1].set_pic('')
            self._pieces[2][1].set_type(constants.CHESS_PAO)
            self._pieces[2][1].set_camp(constants.CAMP_RED)
            self._pieces[2][7].set_pic('')
            self._pieces[2][7].set_type(constants.CHESS_PAO)
            self._pieces[2][7].set_camp(constants.CAMP_RED)
            self._pieces[3][0].set_pic('')
            self._pieces[3][0].set_type(constants.CHESS_BING)
            self._pieces[3][0].set_camp(constants.CAMP_RED)
            self._pieces[3][2].set_pic('')
            self._pieces[3][2].set_type(constants.CHESS_BING)
            self._pieces[3][2].set_camp(constants.CAMP_RED)
            self._pieces[3][4].set_pic('')
            self._pieces[3][4].set_type(constants.CHESS_BING)
            self._pieces[3][4].set_camp(constants.CAMP_RED)
            self._pieces[3][6].set_pic('')
            self._pieces[3][6].set_type(constants.CHESS_BING)
            self._pieces[3][6].set_camp(constants.CAMP_RED)
            self._pieces[3][-1].set_pic('')
            self._pieces[3][-1].set_type(constants.CHESS_BING)
            self._pieces[3][-1].set_camp(constants.CAMP_RED)
    
            # init WHITE Camp and Pic
            for idx in range(self._width):
                self._pieces[-1][idx].set_camp(constants.CAMP_WHITE)
            self._pieces[-1][0].set_pic('')
            self._pieces[-1][0].set_type(constants.CHESS_CHE)
            self._pieces[-1][1].set_pic('')
            self._pieces[-1][1].set_type(constants.CHESS_MA)
            self._pieces[-1][2].set_pic('')
            self._pieces[-1][2].set_type(constants.CHESS_XIANG)
            self._pieces[-1][3].set_pic('')
            self._pieces[-1][3].set_type(constants.CHESS_SHI)
            self._pieces[-1][4].set_pic('')
            self._pieces[-1][4].set_type(constants.CHESS_BOSS)
            self._pieces[-1][5].set_pic('')
            self._pieces[-1][5].set_type(constants.CHESS_SHI)
            self._pieces[-1][6].set_pic('')
            self._pieces[-1][6].set_type(constants.CHESS_XIANG)
            self._pieces[-1][7].set_pic('')
            self._pieces[-1][7].set_type(constants.CHESS_MA)
            self._pieces[-1][8].set_pic('')
            self._pieces[-1][8].set_type(constants.CHESS_CHE)
            # 上面的camp已经通过循环统一设置过了
            self._pieces[-3][1].set_pic('')
            self._pieces[-3][1].set_type(constants.CHESS_PAO)
            self._pieces[-3][1].set_camp(constants.CAMP_WHITE)
            self._pieces[-3][7].set_pic('')
            self._pieces[-3][7].set_type(constants.CHESS_PAO)
            self._pieces[-3][7].set_camp(constants.CAMP_WHITE)
            self._pieces[-4][0].set_pic('')
            self._pieces[-4][0].set_type(constants.CHESS_BING)
            self._pieces[-4][0].set_camp(constants.CAMP_WHITE)
            self._pieces[-4][2].set_pic('')
            self._pieces[-4][2].set_type(constants.CHESS_BING)
            self._pieces[-4][2].set_camp(constants.CAMP_WHITE)
            self._pieces[-4][4].set_pic('')
            self._pieces[-4][4].set_type(constants.CHESS_BING)
            self._pieces[-4][4].set_camp(constants.CAMP_WHITE)
            self._pieces[-4][6].set_pic('')
            self._pieces[-4][6].set_type(constants.CHESS_BING)
            self._pieces[-4][6].set_camp(constants.CAMP_WHITE)
            self._pieces[-4][8].set_pic('')
            self._pieces[-4][8].set_type(constants.CHESS_BING)
            self._pieces[-4][8].set_camp(constants.CAMP_WHITE)
    
            # init cursor 〄 〠
            self._cursor_original_backup: ChessPiece = self._pieces[self._cursor_loc['y']][self._cursor_loc['x']]
            tmp = ChessPiece(self.cursor_pic(), constants.CAMP_CURSOR)
            tmp.set_type(constants.CHESS_CURSOR)
            self._pieces[self._cursor_loc['y']][self._cursor_loc['x']] = tmp
            del tmp
    
            self.strategies = chess_strategy.ChessStrategy(self._pieces, True)
    
        def show_map(self):
            for y in range(self._height):
                for x in range(self._width):
                    self._pieces[y][x].show()
                if y == 4:
                    print()
                    # 楚汉分割线
                    print('***********************************')
                else:
                    print('
    ')
    
        def clear_map(self):
            return common.clear_console()
    
        def cursor_move(self, p_target):
            self._pieces[self._cursor_loc['y']][self._cursor_loc['x']] = self._cursor_original_backup
            self._cursor_original_backup = self._pieces[p_target['y']][p_target['x']]
            tmp = ChessPiece(self.cursor_pic(), constants.CAMP_CURSOR)
            tmp.set_type(constants.CHESS_CURSOR)
            self._pieces[p_target['y']][p_target['x']] = tmp
            self._cursor_loc = p_target
            # 刷新地图
            self.clear_map()
            self.show_map()
    
        # 参数类型限定和返回值类型限定可以不要【类似ts里可以不要】
        def can_move(self, direct: str) -> bool:
            if direct == 'w':
                if self._cursor_loc['y'] - 1 < 0:
                    return False
            elif direct == 'd':
                if self._cursor_loc['x'] + 1 >= self._
                    return False
            elif direct == 's':
                if self._cursor_loc['y'] + 1 >= self._height:
                    return False
            elif direct == 'a':
                if self._cursor_loc['x'] - 1 < 0:
                    return False
            else:
                return False
            return True
    
        def cursor_pic(self) -> str:
            if self._checked:
                return self.CURSOR_CHECKED
            else:
                return self.CURSOR_UNCHECKED
    
        """ Python3里可以手动指定返回值类型,而且参数类型也是可以指定的 """
        def control(self) -> None:
            while True:
                ch = self._getch()
                # 清空之前的显示状态
                self.clear_map()
                # 重新绘制象棋布局
                self.show_map()
    
                if ch == 'w' or ch == 'A' or ch == b'w' or ch == b'H':
                    if self.can_move('w'):
                        self.cursor_move({'x': self._cursor_loc['x'], 'y': self._cursor_loc['y'] - 1})
                    print('', end='')
                elif ch == 'd' or ch == 'C' or ch == b'd' or ch == b'M':
                    if self.can_move('d'):
                        self.cursor_move({'x': self._cursor_loc['x'] + 1, 'y': self._cursor_loc['y']})
                    print('', end='')
                elif ch == 's' or ch == 'B' or ch == b's' or ch == b'P':
                    if self.can_move('s'):
                        self.cursor_move({'x': self._cursor_loc['x'], 'y': self._cursor_loc['y'] + 1})
                    print('', end='')
                elif ch == 'a' or ch == 'D' or ch == b'a' or ch == b'K':
                    if self.can_move('a'):
                        self.cursor_move({'x': self._cursor_loc['x'] - 1, 'y': self._cursor_loc['y']})
                    print('', end='')
                elif ch.lower() == 'q' or ch == b'q':
                    print('game quit!')
                    sys.stdout.flush()
                    break
                elif ch == ' ' or ch == b' ':
                    if not self._checked:
                        self.do_check()
                        print('选中', end='')
                    else:
                        self.do_release()
                        print('释放', end='')
                    # 判定游戏是否结束
                    if self.game_over:
                        print('
    game over!, 胜利方是:' + self.winner)
                        sys.stdout.flush()
                        break
                else:
                    print('无效按键', end='')
                # 立刻刷新输入流,否则getch()获取的字符不会立刻显示
                sys.stdout.flush()
    
        def can_check(self):
            tmp = self._cursor_original_backup.get_camp()
            if tmp != constants.CAMP_RED and tmp != constants.CAMP_WHITE:
                return False
            if self.firster == constants.CAMP_RED and tmp == constants.CAMP_WHITE:
                return False
            if self.firster == constants.CAMP_WHITE and tmp == constants.CAMP_RED:
                return False
            return True
    
        def do_check(self):
            if self.can_check():
                self._checked = ~self._checked
                self._pieces[self._cursor_loc['y']][self._cursor_loc['x']].set_pic(self.cursor_pic())
                # 复制一份,和Java一样基础类型是值赋值,而对象是引用赋值
                self._checked_loc = self._cursor_loc.copy()
                self.clear_map()
                self.show_map()
    
        def do_release(self):
            # 判定是否是取消,是取消则不翻转当前选手
            if self._cursor_loc['x'] == self._checked_loc['x'] and self._cursor_loc['y'] == self._checked_loc['y']:
                self._checked = ~self._checked
                self._pieces[self._cursor_loc['y']][self._cursor_loc['x']].set_pic(self.cursor_pic())
                self.clear_map()
                self.show_map()
                return
            if self.can_release():
                self._checked = ~self._checked
                # 判断游戏是否结束
                if self._cursor_original_backup.get_camp() != constants.CAMP_NONE and self._cursor_original_backup.get_type() == constants.CHESS_BOSS:
                    self.game_over = True
                    self.winner = self.firster
                # 切换释放棋子后的布局
                self._cursor_original_backup = self._pieces[self._checked_loc['y']][self._checked_loc['x']]
                tmp = ChessPiece(self.PIECE_NONE, constants.CAMP_NONE)
                tmp.set_type(constants.CHESS_PIECE)
                self._pieces[self._checked_loc['y']][self._checked_loc['x']] = tmp
                self._pieces[self._cursor_loc['y']][self._cursor_loc['x']].set_pic(self.cursor_pic())
                self.clear_map()
                self.show_map()
                # 切换选手
                self.reverse_role()
    
        """ 策略层,TODO 待完善 """
        def can_release(self):
            # 初步判定,至少不能吃自己这边的子【Python里函数调用太坑了吧,刚才self._cursor_original_backup.get_camp这种方式居然不报错。。】
            if self._cursor_original_backup.get_camp() == self.firster:
                return False
            # 其他规则
            tmp = self._pieces[self._checked_loc['y']][self._checked_loc['x']]
            if tmp.get_type() == constants.CHESS_BING:
                return self.strategies.bing(tmp, self._checked_loc, self._cursor_loc)
            if tmp.get_type() == constants.CHESS_CHE:
                return self.strategies.che(self._checked_loc, self._cursor_loc)
            if tmp.get_type() == constants.CHESS_PAO:
                return self.strategies.pao(self._cursor_original_backup, self._checked_loc, self._cursor_loc)
            if tmp.get_type() == constants.CHESS_MA:
                return self.strategies.ma(self._checked_loc, self._cursor_loc)
            if tmp.get_type() == constants.CHESS_XIANG:
                return self.strategies.xiang(tmp, self._checked_loc, self._cursor_loc)
            if tmp.get_type() == constants.CHESS_SHI:
                return self.strategies.shi(tmp, self._checked_loc, self._cursor_loc)
            if tmp.get_type() == constants.CHESS_BOSS:
                return self.strategies.boss(tmp, self._cursor_original_backup, self._checked_loc, self._cursor_loc)
            return True
    
        def reverse_role(self):
            if self.firster == constants.CAMP_RED:
                self.firster = constants.CAMP_WHITE
            else:
                self.firster = constants.CAMP_RED
    
        def start(self):
            self.clear_map()
            self.show_map()
            self.control()
    
    
    # 和类定义相关的代码就要求空两行,上面是类结束,所以这里需要空两行(当然不是强制的)
    if __name__ == '__main__':
        game = GameContext()
        game.clear_map()
        game.show_map()
        game.control()

    6.接下来是策略模块,用于对各类棋子的移动和吃子进行限制,这个没啥好讲的,根据象棋规则看代码即可:

    import math
    
    from chess_piece import ChessPiece
    import constants
    
    
    # 好吧,Python规范里要求类和其他代码之间空两行,而且要求整个文件最后一行是空行,然后#和前面的代码空两个空格
    class ChessStrategy:
    
        pieces: []
    
        _height = 0
        _width = 0
    
        _red_up = True
        _red_line = 4
        _white_line = 5
        _boss_x_left = 3
        _boss_x_right = 5
        _boss_y_red = 2
        _boss_y_white = 7
    
        # [ChessPiece]限定列表元素类型为ChessPiece
        def __init__(self, pieces: [ChessPiece], red_up: bool = True):
            self.pieces = pieces
            self._height = len(pieces)
            self._height = len(pieces[0])
            # 红方是否在地图上方,这关乎到兵不能后退等问题
            self._red_up = red_up
            # 红方和白方的楚河汉界线
            if red_up:
                self._red_line = 4
                self._white_line = 5
                self._boss_y_red = 2
                self._boss_y_white = 7
            else:
                self._red_line = 5
                self._white_line = 4
                self._boss_y_red = 7
                self._boss_y_white = 2
    
        """ 是否直着走 """
        def is_straight(self, origin: {}, dest: {}) -> bool:
            if (origin['x'] - dest['x']) * (origin['y'] - dest['y']) == 0:
                return True
    
        """ 兵的阵营,象棋地图,待移动兵的坐标 """
        def bing(self, active: ChessPiece, origin: {}, dest: {}):
            # 存在斜着走判定
            if not self.is_straight(origin, dest):
                return False
            # 存在移动超过多步判定
            if abs(origin['x'] - dest['x']) > 1 or abs(origin['y'] - dest['y']) > 1:
                return False
            # 不能后退判定和过了河才能左右移动判定
            if self._red_up:
                if active.get_camp() == constants.CAMP_RED:
                    if dest['y'] - origin['y'] < 0:
                        return False
                    # 过河才能左右移动
                    if abs(dest['x'] - origin['x']) > 0:
                        if origin['y'] <= self._red_line:
                            return False
                else:
                    if dest['y'] - origin['y'] > 0:
                        return False
                    # 过了河才能左右移动
                    if abs(dest['x'] - origin['x']) > 0:
                        if origin['y'] >= self._white_line:
                            return False
            else:  # 红方在下面
                if active.get_camp() == constants.CAMP_RED:
                    if dest['y'] - origin['y'] > 0:
                        return False
                    # 过了河才能左右移动
                    if abs(dest['x'] - origin['x']) > 0:
                        if origin['y'] >= self._white_line:
                            return False
                else:
                    if dest['y'] - origin['y'] < 0:
                        return False
                    # 过了河才能左右移动
                    if abs(dest['x'] - origin['x']) > 0:
                        if origin['y'] <= self._white_line:
                            return False
            return True
    
        """"""
        def che(self, origin: {}, dest: {}):
            if not self.is_straight(origin, dest):
                return False
            if abs(origin['x'] - dest['x']) == 1 or abs(origin['y'] - dest['y']) == 1:
                return True
            # 横着走
            if origin['x'] - dest['x'] != 0:
                for idx in range(min(origin['x'], dest['x']) + 1, max(origin['x'], dest['x'])):
                    if self.pieces[dest['y']][idx].get_type() != constants.CHESS_PIECE:
                        return False
            else:  # 竖着走
                for idx in range(min(origin['y'], dest['y']) + 1, max(origin['y'], dest['y'])):
                    if self.pieces[idx][dest['x']].get_type() != constants.CHESS_PIECE:
                        return False
            return True
    
        """"""
        def pao(self, dest_piece: ChessPiece, origin: {}, dest: {}):
            if not self.is_straight(origin, dest):
                return False
    
            middle_count = 0
            if origin['x'] - dest['x'] != 0:  # 横着走
                for idx in range(min(origin['x'], dest['x']) + 1, max(origin['x'], dest['x'])):
                    if self.pieces[dest['y']][idx].get_type() != constants.CHESS_PIECE:
                        middle_count += 1
            else:  # 竖着走
                for idx in range(min(origin['y'], dest['y']) + 1, max(origin['y'], dest['y'])):
                    if self.pieces[idx][dest['x']].get_type() != constants.CHESS_PIECE:
                        middle_count += 1
            if middle_count > 1:
                return False
            if middle_count == 1 and dest_piece.get_camp() == constants.CAMP_NONE:
                return False
            if middle_count == 0 and dest_piece.get_camp() != constants.CAMP_NONE:
                return False
            return True
    
        def ma(self, origin: {}, dest: {}):
            # 走日字判断
            if abs((origin['x'] - dest['x']) * (origin['y'] - dest['y'])) != 2:
                return False
            # 拗马脚判断
            tmpy = math.trunc((dest['y'] - origin['y'])/2)
            tmpx = math.trunc((dest['x'] - origin['x'])/2)
            middlex = origin['x'] + tmpx
            middley = origin['y'] + tmpy
            if self.pieces[middley][middlex].get_camp() != constants.CAMP_NONE:
                return False
            return True
    
        def xiang(self, active: ChessPiece, origin: {}, dest: {}):
            # 判断是否是田字走法
            if abs(origin['x'] - dest['x']) != 2 or abs(origin['y'] - dest['y']) != 2:
                return False
            # 判断是否拗象脚
            tmpx = (dest['x'] - origin['x'])//2 + origin['x']
            tmpy = (dest['y'] - origin['y'])//2 + origin['y']
            if self.pieces[tmpy][tmpx].get_camp() != constants.CAMP_NONE:
                return False
            # 象不能过河的判断
            if self._red_up:
                if active.get_camp() == constants.CAMP_RED:
                    if dest['y'] > self._red_line:
                        return False
                else:
                    if dest['y'] < self._white_line:
                        return False
            else:  # 红方在下面
                if active.get_camp() == constants.CAMP_RED:
                    if dest['y'] < self._red_line:
                        return False
                else:
                    if dest['y'] > self._white_line:
                        return False
            return True
    
        def shi(self, active: ChessPiece, origin: {}, dest: {}):
            # 判断是否走的斜线且距离为1
            if abs((dest['x'] - origin['x']) * (dest['y'] - origin['y'])) != 1:
                return False
            # 判断是否移出左右边界
            if dest['x'] < self._boss_x_left or dest['x'] > self._boss_x_right:
                return False
            # 判断是否移出Y轴边界
            if self._red_up:
                if active.get_camp() == constants.CAMP_RED:
                    if dest['y'] > self._boss_y_red:
                        return False
                else:
                    if dest['y'] < self._boss_y_white:
                        return False
            else:  # 红方在下面
                if active.get_camp() == constants.CAMP_RED:
                    if dest['y'] < self._boss_y_red:
                        return False
                else:
                    if dest['y'] > self._boss_y_white:
                        return False
            return True
    
        def boss(self, active: ChessPiece, dest_piece: ChessPiece, origin: {}, dest: {}):
            # 判断是否将帅见面,这种情况下可以移动到对方大本营里吃对方主将
            if active.get_type() == constants.CHESS_BOSS and dest_piece.get_type() == constants.CHESS_BOSS and origin['x'] == dest['x']:
                middle_count = 0
                for idx in range(min(origin['y'], dest['y']) + 1, max(origin['y'], dest['y'])):
                    if self.pieces[idx][dest['x']].get_type() != constants.CHESS_PIECE:
                        middle_count += 1
                        break
                if middle_count == 0:
                    return True
            # 判断是否走的直线且距离为1
            if abs(dest['x'] - origin['x']) + abs(dest['y'] - origin['y']) != 1:
                return False
            # 判断是否移出左右边界
            if dest['x'] < self._boss_x_left or dest['x'] > self._boss_x_right:
                return False
            # 判断是否移出Y轴边界
            if self._red_up:
                if active.get_camp() == constants.CAMP_RED:
                    if dest['y'] > self._boss_y_red:
                        return False
                else:
                    if dest['y'] < self._boss_y_white:
                        return False
            else:  # 红方在下面
                if active.get_camp() == constants.CAMP_RED:
                    if dest['y'] < self._boss_y_red:
                        return False
                else:
                    if dest['y'] > self._boss_y_white:
                        return False
            return True

    7.最后就是main模块,其实就是创建个游戏环境类然后运行游戏的作用:

    # coding:utf-8
    import platform
    import sys
    
    if __name__ == '__main__':
        system = platform.system()
        if system == 'Windows':
            print('请使用Linux系统,最好是Ubuntu18.x版本,否则其他的控制台输出的布局可能会不整齐')
            # exit()
        if sys.version_info < (3, 0) or sys.version_info >= (4, 0):
            print('请使用Python3.x')
            exit()
        print("Game started.")
        import game_context
        import constants
        game = game_context.GameContext(constants.CAMP_WHITE)
        game.start()

    六:源码分析

     这里主要分析游戏环境类,这个是整个游戏的关键;

    1.象棋的地图是通过二维的ChessPiece数组来实现管理的;通过二维数组来实现打印出棋子、光标、空白,这三种都是ChessPiece,不过是它们的阵营,pic等不同

    2.在游戏环境类里通过类属性,记录由谁先行,游戏是否结束,胜利方是谁,是否选中棋子,当前光标位置,选中棋子的位置等数据,用于后续的逻辑判定

    3.在init方法里初始化象棋地图,然后每个棋子都自我实现了printc方法来打印自己(包括颜色,文本/pic),以及初始化光标等参数

    4.这里实现光标移动原理其实就是getch()获得移动按键后,然后修改_pieces,接着清屏和show_map(),这些方法都在类里可以找到;

    5.游戏的控制逻辑在control(self)方法里实现,通过循环调用getch()不断的监听按键输入,然后判断按键类型,如果是方向键则通过cursor_move()方法来实现移动光标,内部通过can_move()来判断是否可以进行方向移动

    ,比如肯定不能越界;然后判断如果是空格键,表示是要选中棋子或者释放棋子,这里则通过判断当前光标是释放状态还是选中状态来分别执行do_check()和do_release(),在do_check()里通过can_check()来判断是否可以

    选中某个piece(因为象棋地图里每个元素都是piece,所以肯定需要判断当前光标所在位置的piece是不是象棋棋子还是空白棋子),选中后光标的图案也会改变;

    在do_release()里也是通过can_release()来判断是否可以释放棋子,比如咱们选中了白方棋子,那么释放棋子的地方肯定不能是另一个白方棋子,因为自己人不能吃自己人。。;然后在can_release()里调用策略模块里的策略

    ,分别对不同类型的棋子进行规则判断,比如炮吃子的话只能隔一个子吃,馬走日且不能拗马脚等等;

    6.总结:重要的方法为control(),cursor_move(),can_move(),do_check(),can_check(),do_release(),can_release(),__init()__这些都是主要的业务逻辑判断;

    七:其他

    这里还有个我用Rust写的俄罗斯方块,不过文章没有写的很全但是代码啥的都在github里了,大家有兴趣可以去看看:https://www.cnblogs.com/silentdoer/p/12160871.html

  • 相关阅读:
    逆向与BOF基础——注入shellcode并执行&Return-to-libc
    20145304 网络对抗技术 逆向与Bof基础
    20145303刘俊谦 《网络对抗》Exp9 Web安全基础实践
    操作系统取证实践
    20145303刘俊谦 Exp8 Web基础
    20145303刘俊谦 Exp7 网络欺诈技术防范
    20145303《网络对抗》信息收集和漏洞扫描技术
    20145303刘俊谦《网络攻防》Exp4 Msf基础
    msf辅助模块的应用
    Adobe漏洞攻击
  • 原文地址:https://www.cnblogs.com/silentdoer/p/12758649.html
Copyright © 2020-2023  润新知