def __init__(self, setting): self.setting = setting self.__logger = LoggerPrint(self.setting) self.log = self.__logger.printLogToSystem() self.times = 0 self.color1, self.color2 = 'red', 'black'
def __init__(self, setting): self.setting = setting self.logger = LoggerPrint(self.setting) self.log = self.logger.printLogToSystem() # 初始化混音器 pygame.mixer.init() self.log.info("初始化混音器成功!")
def __init__(self, all_pieces, player1Color): self.all_pieces = all_pieces self.player1_color = player1Color # self.settings = Settings() self.game_func = GameFunction(self.settings) self.log = LoggerPrint(self.settings).printLogToSystem(False)
def __init__(self, setting): self.setting = setting self.logger = LoggerPrint(self.setting).printLogToSystem()
class GameFunction(): """ 游戏逻辑的所有方法,不涉及到界面显示内容 """ def __init__(self, setting): self.setting = setting self.logger = LoggerPrint(self.setting).printLogToSystem() # 将红黑棋子对应的图片,关联起来,存放在字典中 def _get_all_pieces(self, piece_flag): pieces = [] for piece in self.setting.pieces_list: # value = f"{self.setting.pieces_front}{piece_flag}_{piece}{self.setting.pieces_noun}" if piece in ['shi', 'xiang', 'ma', 'ju', 'pao']: for i in range(1, 3): pieces.append(f"{piece_flag}_{piece}{str(i)}") elif piece == 'zu': for i in range(1, 6): pieces.append(f"{piece_flag}_{piece}{str(i)}") else: pieces.append(f"{piece_flag}_{piece}1") return pieces # 初始化棋子配置 def box_pieces(self): # 获得所有的红黑棋子的dict pieces = self._get_all_pieces('red') pieces_black = self._get_all_pieces('black') pieces += pieces_black all_pieces = {} for i in range(8): for j in range(4): # 随机抽取key放入boxid中,并初始化box的状态为False random_key = random.choice(pieces) all_pieces[f"box_{i}_{j}"] = {'box_key': random_key, 'state': False} pieces.remove(random_key) return all_pieces # 获取鼠标位置所对应的方格id def get_box_xy(self, event_x, event_y): # 确定鼠标的有效矩形位置 min_area_x = self.setting.piece_first_x min_area_y = self.setting.piece_first_y max_area_x = min_area_x + 8 * self.setting.piece_size max_area_y = min_area_y + 4 * self.setting.piece_size # 确认鼠标是在棋盘上有效的矩形范围之内 if (min_area_x < event_x < max_area_x) and (min_area_y < event_y < max_area_y): mouse_x_not = (i * 100 + min_area_x for i in range(1, 9)) mouse_y_not = (i * 100 + min_area_y for i in range(1, 5)) # 鼠标不能在棋盘线上 if (event_x not in mouse_x_not) and (event_y not in mouse_y_not): # 计算当前鼠标坐标所在的方格坐标 box_x = int((event_x - min_area_x) / 100) box_y = int((event_y - min_area_y) / 100) return (box_x, box_y) # 获取棋子所在方格左上角的坐标 # def get_box_local_xy(self, box_x, box_y): # box_local_x = box_x * self.setting.piece_size + self.setting.piece_first_x # box_local_y = box_y * self.setting.piece_size + self.setting.piece_first_y # 加载棋子对应的图片 def get_piece_image(self): pieces_dict = {} for piece_name in self.setting.pieces_list: pieces_dict[f"red_{piece_name}"] = f"{self.setting.pieces_front}red_{piece_name}{self.setting.pieces_noun}" pieces_dict[f"black_{piece_name}"] = f"{self.setting.pieces_front}black_{piece_name}{self.setting.pieces_noun}" return pieces_dict # 两个棋子之间的比较 def piece_VS_piece(self, box1_xy, box2_xy, all_pieces): ''' :param box1_xy: :param box2_xy: :param all_pieces: :return: [True|False|None, '原因', True|False] True|False|None:表示box1和box2的比较结果 '原因':表示原因,便于提示失败的原因,也可指示成功的提示 True|False:表示对于该原因,对后续AI走棋获取该走棋步骤时,是否为需要添加的走棋步骤 ''' value_list = self.setting.pieces_list box1_x = int(box1_xy.split('_')[1]) box1_y = int(box1_xy.split('_')[2]) box2_x = int(box2_xy.split('_')[1]) box2_y = int(box2_xy.split('_')[2]) box1_name = all_pieces[box1_xy]['box_key'] box2_name = all_pieces[box2_xy]['box_key'] box1_color = box1_name.split('_')[0] box1_value = box1_name.split('_')[1] box1_value = box1_value[:-1] box1_value_index = value_list.index(box1_value) # 如果box1的位置比box2的位置大,则交换他们的值 if box1_y > box2_y: box1_y, box2_y = box2_y, box1_y if box1_x > box2_x: box1_x, box2_x = box2_x, box1_x # box2如果为空,则认为第二次选择了棋盘空格,不为空则需要取box2的值与box1比较 if box2_name is not None: box2_color = box2_name.split('_')[0] if box2_color == box1_color: # self.logger.info("false原因:两个棋子在同一个方阵") return [False, 'color', False] box2_value = box2_name.split('_')[1] box2_value = box2_value[:-1] box2_value_index = value_list.index(box2_value) else: return self._other_vs_piece(box1_x, box1_y, box2_x, box2_y, piece_equal=False) # 如果两个棋子相同 if box1_value_index == box2_value_index: if box1_value == 'pao': # '炮'相同时的吃法: # 中间有且只有一个棋子,中间棋子不论打没打开都可以,没有距离限制 return self._pao_vs_piece(box1_x, box1_y, box2_x, box2_y, all_pieces, piece_equal=False) else: # 其他相同棋子的吃法: # 只能相邻位置两个棋子,最后也是两个棋子同时失去 return self._other_vs_piece(box1_x, box1_y, box2_x, box2_y, piece_equal=True) # 如果两个棋子不相同 else: if box1_value == 'pao': # '炮'的吃法: # 1、中间必须有且只有一个棋子,才能吃到对方棋子,没有距离限制; # 2、炮没有大小吃法限制,上到将,下到卒都可以吃; # 3、炮移动只能相邻的格子移动,不能跳着移动; return self._pao_vs_piece(box1_x, box1_y, box2_x, box2_y, all_pieces, piece_equal=False) else: # 其他的棋子的吃法: # 1、大吃小(将(帅)>士>(象)相>马>车>炮和卒(兵)),两两相同则一起吃掉; # 2、炮和卒(兵)、炮和将(帅)相互不能吃,任何棋子都可以吃炮,除了卒(兵)、将(帅)外; # 3、任何棋子都可以吃卒(兵),而卒(兵)只吃对方的帅(将); # 4、棋子只能相邻的吃,而且一次只能走一步,吃一个棋子,炮除外 if box1_value_index < box2_value_index: # 大吃小,除非jiang和zu或pao同时出现 if box1_value == 'jiang' and box2_value == 'zu': # self.logger.info("false原因:jiang在和zu比较") return [False, 'jiang_zu', True] elif box1_value == 'jiang' and box2_value == 'pao': # self.logger.info("false原因:jiang在和pao比较") return [False, 'jiang_pao', False] else: return self._other_vs_piece(box1_x, box1_y, box2_x, box2_y, piece_equal=False) elif box1_value_index > box2_value_index: # 最后一种情况:zu只能吃jiang if box1_value == 'zu' and box2_value == 'jiang': return self._other_vs_piece(box1_x, box1_y, box2_x, box2_y, piece_equal=False) elif box1_value == 'zu' and box2_value == 'pao': # self.logger.info("false原因:zu在和pao比较") return [False, 'zu_pao', False] else: # self.logger.info("false原因:box1比box2还小") return [False, 'box1<box2', True] # pao的比较 def _pao_vs_piece(self, box1_x, box1_y, box2_x, box2_y, all_pieces, piece_equal=True): if box1_x == box2_x and box2_y - box1_y > 1: between_state_have = 0 for i in range(box1_y + 1, box2_y): if all_pieces[f"box_{box1_x}_{i}"]['state'] is not None: between_state_have += 1 if between_state_have > 1: break if between_state_have == 1: if piece_equal is True: # self.logger.info("None:box1和box2相同,都是pao") return [None, 'box1=box2', True] else: return [True, 'success', True] else: # self.logger.info("false原因:box1和box2之间,在y轴上没有棋子,或者有大于2个的棋子") return [False, 'pao_between', False] elif box1_y == box2_y and box2_x - box1_x > 1: between_state_have = 0 for i in range(box1_x + 1, box2_x): if all_pieces[f"box_{i}_{box1_y}"]['state'] is not None: between_state_have += 1 if between_state_have > 1: break if between_state_have == 1: if piece_equal is True: # self.logger.info("None:box1和box2相同,都是pao") return [None, 'box1=box2', True] else: return [True, 'success', True] else: # self.logger.info("false原因:box1和box2之间,在x轴上没有棋子,或者有大于2个的棋子") return [False, 'pao_between', False] else: # self.logger.info("false原因:box1和box2不在同一条x轴或y轴上") return [False, 'box1_noline_box2', False] # 其他棋子的比较 def _other_vs_piece(self, box1_x, box1_y, box2_x, box2_y, piece_equal=True): if (box1_x == box2_x and box2_y - box1_y == 1) or (box1_y == box2_y and box2_x - box1_x == 1): if piece_equal is True: # self.logger.info("None:box1和box2相同") return [None, 'box1=box2', True] else: return [True, 'success', True] else: # self.logger.info("false原因:box1和box2不在同一条x轴或y轴上") return [False, 'box1_noline_box2', False] # 游戏结束判断 def is_game_over(self, all_pieces, nowPlayer, player1Color): # 循环all_pieces中的state得到棋子状态 true_count = 0 none_count = 0 new_pieces = {} for key, value in all_pieces.items(): for key1, value1 in value.items(): if key1 == 'state': if value1 is True: true_count += 1 new_pieces[all_pieces[key]['box_key']] = key elif value1 is False: # 如果查到state的状态为false,立即return return 'none' elif value1 is None: none_count += 1 self.logger.info(f"true_count: {true_count}") self.logger.info(f"none_count: {none_count}") self.logger.info(f"new_pieces: {new_pieces}") # 如果allpieces全部为none,则为平局 if none_count == 32: return 'tie' else: # 只有一个棋子,则取棋子的颜色return if true_count == 1: key_list = list(new_pieces.keys()) box_color = key_list[0].split('_')[0] return box_color # 有多个棋子,则要分开判断 else: # 取红黑棋子分别占有多少 value_list = self.setting.pieces_list red_count, black_count = 0, 0 box_count = len(new_pieces) for key, value in new_pieces.items(): box_color = key.split('_')[0] if box_color == 'red': red_count += 1 else: black_count += 1 self.logger.info(f"red_count: {red_count}") self.logger.info(f"black_count: {black_count}") self.logger.info(f"box_count: {box_count}") # 如果全部为红色或者黑色,则return if red_count == box_count: return 'red' elif black_count == box_count: return 'black' # 总数为2,红黑棋子各占1个 elif box_count == 2 and red_count == 1: red_x = 0 red_y = 0 red_value = '' black_x = 0 black_y = 0 black_value = '' for key, value in new_pieces.items(): box_color = key.split('_')[0] box_value = key.split('_')[1] if box_color == 'red': red_x = int(value.split('_')[1]) red_y = int(value.split('_')[2]) red_value = box_value[:-1] else: black_x = int(value.split('_')[1]) black_y = int(value.split('_')[2]) black_value = box_value[:-1] red_value_index = value_list.index(red_value) black_value_index = value_list.index(black_value) # 如果红黑双方棋子相同或者是不能相互吃掉的棋子,则为平局 if red_value == black_value or \ (red_value in ['jiang', 'pao'] and black_value in ['jiang', 'pao']) or \ (red_value in ['pao', 'zu'] and black_value in ['pao', 'zu']): return 'tie' # 当前玩家和玩家方阵为红方,且红方的棋子比黑方的棋子大,则他们相邻比较的时候就会是平局,红方无法吃掉黑方 if (nowPlayer == self.setting.player1_name and player1Color == 'red') or \ (nowPlayer == self.setting.player2_name and player1Color == 'black'): if red_value_index > black_value_index: if (red_x == black_x and fabs(red_y - black_y) == 1) or ( red_y == black_y and fabs(red_x - black_x) == 1): return 'tie' # 黑方也是一样 elif (nowPlayer == self.setting.player1_name and player1Color == 'black') or \ (nowPlayer == self.setting.player2_name and player1Color == 'red'): if red_value_index < black_value_index: if (red_x == black_x and fabs(red_y - black_y) == 1) or ( red_y == black_y and fabs(red_x - black_x) == 1): return 'tie' # 总数>2,只判断一方只为1个的情况 elif box_count > 2: # 红方只有一个棋子,如果这个棋子在棋盘上黑方有其他棋子能吃掉它,则红方就一定会输 # 相反如果红方的这个棋子在棋盘上黑方没有棋子能吃掉它,那么就只能是平局了 if red_count == 1: return self._one_vs_more(new_pieces, 'red', value_list) elif black_count == 1: return self._one_vs_more(new_pieces, 'black', value_list) else: return 'none' # return 'none' # 判断某一方只有一个棋子时 def _one_vs_more(self, new_pieces, one_color, value_list): index_list = [] for key, value in new_pieces.items(): box_color = key.split('_')[0] box_value = key.split('_')[1] if box_color == one_color: one_value = box_value[:-1] one_index = value_list.index(one_value) else: more_value = box_value[:-1] index_list.append(value_list.index(more_value)) # 红方只有一个pao,黑方也只有pao和zu,则为平局 if one_index == 5 and [0, 1, 2, 3, 4] not in index_list and len(index_list) < 4: return 'tie' count = 0 for index in index_list: if one_index < index: # 要排除jiang和zu同时存在的情况 if one_index == 0 and index == 6: return 'none' count += 1 else: return 'none' # 如果对方所有的棋子都比这一个棋子小,则为平局 if count == len(index_list): return 'tie'
#!/usr/bin/python3 # encoding: utf-8 import timeit from time import sleep from loggerPrint import LoggerPrint from settings import Settings logg = LoggerPrint(Settings()).printLogToSystem(False) def clock(func): def clocked(*args, **kwargs): t0 = timeit.default_timer() result = func(*args, **kwargs) elapsed = timeit.default_timer() - t0 name = func.__name__ logg.info('%s 耗时 [%0.8fs]' % (name, elapsed)) return result return clocked
class SeachMove(): def __init__(self, setting): self.setting = setting self.logg = LoggerPrint(self.setting).printLogToSystem(False) # 寻找AI的下一步走棋 @clock def search_next_move(self, all_pieces, player1Color, depth=1): # 取得当前player2的所有走棋步骤内容 all_moves = Moves(all_pieces, player1Color).generate_all_moves() # best_move = None # allmoves为空,表明要么无棋子可走(输了),要么打开的棋子上没有合适的棋子可走 if len(all_moves) == 0: self.logg.info('当前的allmoves为None!!') # 获得双方剩余棋子的数量 dp = DataPercent(all_pieces) red_count, black_count = dp.pieces_perc() # 根据剩余棋子数量判断是否为0 if red_count == 0 or black_count == 0: self.logg.info(f"一方棋子数量为0,best_move为None!!") else: # 不为0还需判断是否有未打开棋子,如果当前还有棋子未打开,则打开循环到的第一个未打开的棋子 box_to = None for key, value in all_pieces.items(): if value['state'] is False: box_to = key break # box_to不为空,将box_to传递给best_move if box_to is not None: best_move = MoveNodes(None, box_to, '打开', None, box_to, None) self.logg.info(f"打开循环到的第一个未打开的棋子best_move: {box_to}") # box_to为空,表示所有棋子都已打开而且没有合适棋子可走,玩家2自动认输 else: pass # allmoves不为空 else: self.logg.info(f"当player1color为 [{player1Color}] 时, allmove的值:") for move in all_moves: # 基于当前的move计算下一步的走棋,并返回最高得分 self.logg.info( f"{move.box_name_from}===>>>{move.box_from}===>>>{move.box_action}===>>>{move.box_name_to}===>>>{move.box_to}===>>>{move.box_res}===>>>{move.score}") move.score = self._seach_other_moves(all_pieces, player1Color, depth, move) self.logg.info(f"最终的score:{move.score}") # 把得分最高的走棋给best_move if best_move is None or move.score >= best_move.score: best_move = move return best_move # 为下一个玩家寻找行棋内容 def _seach_other_moves(self, all_pieces, player1Color, depth, move): # 设置一个all_pieces的替身,好还原all_pieces的值 all_pieces_1 = deepcopy(all_pieces) # 根据行棋状态修改all_pieces_1的值 if move.box_action == '吃棋': if move.box_res == 'box1=box2': all_pieces_1[move.box_from]['box_key'] = None all_pieces_1[move.box_from]['state'] = None all_pieces_1[move.box_to]['box_key'] = None all_pieces_1[move.box_to]['state'] = None elif move.box_res == 'success': all_pieces_1[move.box_to]['box_key'] = all_pieces_1[move.box_from]['box_key'] all_pieces_1[move.box_to]['state'] = True all_pieces_1[move.box_from]['box_key'] = None all_pieces_1[move.box_from]['state'] = None elif move.box_action == '移动': all_pieces_1[move.box_to]['box_key'] = all_pieces_1[move.box_from]['box_key'] all_pieces_1[move.box_to]['state'] = True all_pieces_1[move.box_from]['box_key'] = None all_pieces_1[move.box_from]['state'] = None elif move.box_action == '打开': # box_action == '打开' 时,不能给出打开这个棋子的信息,只能给出打开当前棋子的价值,同时将depth=0,给出得分 depth = 0 # if depth == 0: return move.score # depth不为0,更换player1Color的颜色方阵,反过来计算如果player1是player2的话,走棋都有哪些内容 player1Color = 'black' if player1Color == 'red' else 'red' # score = 10 moves = Moves(all_pieces_1, player1Color) all_moves = moves.generate_all_moves() self.logg.info(f"player2反置:当player1color为 [{player1Color}] 时, allmove的值:") for move_1 in all_moves: # 回调该函数自己,同时depth-1 self.logg.info( f"player2反置:{move_1.box_name_from}===>>>{move_1.box_from}===>>>{move_1.box_action}===>>>" f"{move_1.box_name_to}===>>>{move_1.box_to}===>>>{move_1.box_res}===>>>{move_1.score}") score = max(score, self._seach_other_moves(all_pieces_1, player1Color, depth-1, move_1)) if score >= 1000: score = 0 move.score += score # 还原all_pieces的值,保证下一个循环用到的值还是原来的all_pieces all_pieces_1 = deepcopy(all_pieces) # return move.score
class PlayMusic(): # 设置音乐播放标志:0表示自动播放,1表示手动介入 _MUSIC_PLAY_FLAG = 0 def __init__(self, setting): self.setting = setting self.logger = LoggerPrint(self.setting) self.log = self.logger.printLogToSystem() # 初始化混音器 pygame.mixer.init() self.log.info("初始化混音器成功!") # 播放背景音乐 def play_bg_music(self, music_file=None): # 如果music_file有值,则播放选中的音乐 if music_file: pygame.mixer.music.stop() self.log.info(f"切换的背景音乐为: {music_file}") # 没有值则随机播放 else: music_list = self.setting.music_list music_file = random.sample(music_list, 1) music_file = music_file[0] self.log.info(f"随机播放的音乐为: {music_file}") pygame.mixer.music.load(f'mids/{music_file}') pygame.mixer.music.play() PlayMusic._MUSIC_PLAY_FLAG = 0 self.is_not_busy() # 播放一首音乐完成之后,自动随机播放下一首 def is_not_busy(self): mixer_state = pygame.mixer.get_init() if mixer_state: # 如果播放器没有工作,则调用播放背景音乐功能方法 if pygame.mixer.music.get_busy( ) == False and PlayMusic._MUSIC_PLAY_FLAG == 0: self.play_bg_music() else: if PlayMusic._MUSIC_PLAY_FLAG == 0: # 定时器,没隔1s检查一次播放器是否有在播放 t = threading.Timer(1, self.is_not_busy) t.start() else: pass # 手动停止背景音乐播放 def stop_bg_music(self): mixer_state = pygame.mixer.get_init() if mixer_state: pygame.mixer.music.stop() self.log.info('背景音乐播放停止!') PlayMusic._MUSIC_PLAY_FLAG = 1 # 加载操作棋子时的音效 def load_play_sound(self, sound): soundobj = pygame.mixer.Sound(sound) soundobj.play() # 退出游戏的时候,需要先退出pygame和mixer播放器 def quit_music(self): pygame.mixer.music.fadeout(1000) pygame.mixer.quit() pygame.quit() self.log.info("混音器销毁成功!pygame退出!")
class Commmon(): def __init__(self, setting): self.setting = setting self.__logger = LoggerPrint(self.setting) self.log = self.__logger.printLogToSystem() self.times = 0 self.color1, self.color2 = 'red', 'black' # 获取当前系统的名称 def get_system_name(self): system_flag = 0 uname = platform.uname().system system_name = platform.platform().split('-')[0] if uname == 'Darwin' or system_name == 'Darwin': # mac系统加载mac配置,设置flag为1 system_flag = 1 elif uname == 'Windows' or system_name == 'Windows': # windows系统加载默认setting设置,设置flag为2 system_flag = 2 else: system_flag = 3 self.log.info(f"当前系统为:{uname}") return system_flag # 压缩图片,改变图片的大小 def change_img(self, img, width=100, height=100): if isinstance(img, str): piece1 = Image.open(img) piece2 = piece1.resize((width, height)) changed_img = ImageTk.PhotoImage(piece2) return changed_img elif isinstance(img, dict): img_dict = {} for key, value in img.items(): piece1 = Image.open(value) piece2 = piece1.resize((width, height)) changed_img = ImageTk.PhotoImage(piece2) img_dict[key] = changed_img return img_dict else: raise ImgNotFound('传入图片格式不正确或者图片不存在!') # 读取文件 def read_file(self, filename, flag=None): try: with open(filename, 'r', encoding='utf-8') as f: if flag is None: return f.read() elif flag == 'info': return f.readlines() except Exception: self.log.exception(f'读取{filename}文件异常!') # 写入info文件 def write_file(self, filename, write_value): try: if isinstance(write_value, str): with open(filename, 'ab+') as f: writestr = (write_value + os.linesep).encode('utf-8') f.write(writestr) elif isinstance(write_value, list): with open(filename, 'w+', encoding='utf-8') as f: f.writelines(write_value) except Exception: self.log.exception(f'写入{filename}文件异常!') # 获取当前系统时间,格式化后添加到文件名称中 def format_now_time(self): ntime = time.strftime('_%Y_%m_%d_%H_%M_%S') return ntime # 获取当前系统时间 def get_now_time(self): ntime = time.strftime('%Y-%m-%d %H:%M:%S') return ntime # 计算两个时间之间的时间差,返回str def how_long_time(self, begintime, endtime): howlongdays = (endtime - begintime).days howlongsecs = (endtime - begintime).seconds hours = int(howlongsecs / 3600) mins = int((howlongsecs % 3600) / 60) secs = (howlongsecs % 3600) % 60 how_long = '' if howlongdays != 0: howlongdays = '%s天' % (str(howlongdays)) how_long += howlongdays if hours != 0: hours = '%s小时' % (str(hours)) how_long += hours if mins != 0: mins = '%s分' % (str(mins)) how_long += mins if secs != 0: secs = '%s秒' % (str(secs)) how_long += secs return how_long # 改变字体颜色 def change_font_color(self): if self.times <= 5: self.color1, self.color2 = self.color2, self.color1 self.times += 1 t = threading.Timer(1, self.change_font_color) t.start() print( f"第 {self.times} 次:color1, color2 = {self.color1}, {self.color2}")