用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 = '\n' print('\033[0' + forecolor + backcolor + 'm' + content + '\033[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('\n') 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._width: 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('\ngame 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
posted on 2020-04-23 09:56 Silentdoer 阅读(4051) 评论(0) 编辑 收藏 举报