def next_move(self, color, board): """next_move """ depth, moves, best_move, scores, = self.depth, None, None, {} pid = Timer.get_pid(self.search) # タイムアウト監視用のプロセスID Timer.set_deadline(pid, self.search._MIN) # 探索クラスのタイムアウトを設定 moves = board.get_legal_moves(color) while True: moves = self.selector.select_moves(color, board, moves, scores, depth) # 次の手の候補を選択 moves = self.orderer.move_ordering(color=color, board=board, moves=moves, best_move=best_move) # 次の手の候補を並び替え best_move, scores = self.search.get_best_move(color, board, moves, depth, pid) # 最善手を取得 if Timer.is_timeout(pid): # タイムアウト発生時、処理を抜ける break if self.limit and depth >= self.limit: # 限界深さに到達時 break depth += 1 # 読みの深さを増やす self.max_depth = depth # 読んだ深さを記録 return best_move
def next_move(self, color, board): """ 次の一手 """ pid = Timer.get_pid(self) # タイムアウト監視用のプロセスID next_color = 'white' if color == 'black' else 'black' moves, max_score = {}, self._MIN # 打てる手の中から評価値の最も高い手を選ぶ legal_moves = board.get_legal_moves(color) for move in legal_moves: board.put_disc(color, *move) # 一手打つ score = -self.get_score(next_color, board, self.depth-1, pid=pid) # 評価値を取得 board.undo() # 打った手を戻す if Timer.is_timeout(pid): # タイムアウト発生時 if max_score not in moves: # 候補がない場合は現在の手を返す return move break else: max_score = max(max_score, score) # 最大値を選択 if score not in moves: # 次の候補を記憶 moves[score] = [] moves[score].append(move) return random.choice(moves[max_score]) # 複数候補がある場合はランダムに選ぶ
def next_move(self, color, board): """ 次の一手 """ pid = Timer.get_pid(self) # タイムアウト監視用のプロセスID if self.table.size != board.size: # テーブルサイズの調整 self.table.set_table(board.size) depth, best_move = self.depth, None while True: moves = list(board.get_legal_moves(color)) # 前回の最善手を優先的に if best_move is not None: moves.remove(best_move) moves.insert(0, best_move) # 4隅はさらに優先的に調べる for corner in [(0, 0), (0, board.size - 1), (board.size - 1, 0), (board.size - 1, board.size - 1)]: if corner in moves: moves.remove(corner) moves.insert(0, corner) best_move = super().get_best_move(color, board, moves, depth, pid) # 最善手 if Timer.is_timeout(pid): # タイムアウト break depth += 1 # 次の読みの深さ return best_move
def get_best_move(self, color, board, moves, depth, pid=None): """ 最善手を選ぶ """ best_move, alpha, beta, scores = None, self._MIN, self._MAX, {} if board.size == 8 and sys.maxsize == MAXSIZE64 and hasattr( board, '_black_bitboard' ) and not NegaScoutMethods.NEGASCOUT_SIZE8_64BIT_ERROR: return NegaScoutMethods.get_best_move(color, board, moves, alpha, beta, depth, self.evaluator, pid, self.timer, self.measure) # 打てる手の中から評価値の最も高い手を選ぶ for move in moves: score = self.get_score(move, color, board, alpha, beta, depth, pid) scores[move] = score if Timer.is_timeout(pid): best_move = move if best_move is None else best_move break else: if score > alpha: # 最善手を更新 alpha = score best_move = move return best_move, scores
def get_score(self, color, board, depth): """ 評価値の取得 """ # ゲーム終了 or 最大深さに到達 legal_moves_b = board.get_legal_moves('black') legal_moves_w = board.get_legal_moves('white') is_game_end = True if not legal_moves_b and not legal_moves_w else False if is_game_end or depth <= 0: return self.evaluate(color, board, legal_moves_b, legal_moves_w) # パスの場合 legal_moves = legal_moves_b if color == 'black' else legal_moves_w next_color = 'white' if color == 'black' else 'black' if not legal_moves: return -self.get_score(next_color, board, depth) # 評価値を算出 max_score = self._MIN for move in legal_moves: board.put_disc(color, *move) score = -self.get_score(next_color, board, depth - 1) board.undo() if Timer.is_timeout(self): break else: max_score = max(max_score, score) # 最大値を選択 return max_score
def _get_score(self, color, board, alpha, beta, depth, pid=None): """ 評価値の取得 """ # ゲーム終了 or 最大深さに到達 legal_moves_b = board.get_legal_moves('black') legal_moves_w = board.get_legal_moves('white') is_game_end = True if not legal_moves_b and not legal_moves_w else False if is_game_end or depth <= 0: return self.evaluate(color, board, legal_moves_b, legal_moves_w) # パスの場合 legal_moves = legal_moves_b if color == 'black' else legal_moves_w next_color = 'white' if color == 'black' else 'black' if not legal_moves: return -self._get_score(next_color, board, -beta, -alpha, depth) # 評価値を算出 for move in legal_moves: board.put_disc(color, *move) score = -self._get_score(next_color, board, -beta, -alpha, depth - 1) board.undo() if Timer.is_timeout(pid): break else: alpha = max(alpha, score) # 最大値を選択 if alpha >= beta: # 枝刈り break return alpha
def next_move(self, color, board): """ 次の一手 """ pid = Timer.get_pid(self) # タイムアウト監視用のプロセスID if board.size == 8 and sys.maxsize == MAXSIZE64 and hasattr(board, '_black_bitboard') and not EndGameMethods.ENDGAME_SIZE8_64BIT_ERROR: return EndGameMethods.next_move(color, board, self.depth, pid, self.timer, self.measure) return self.alphabeta_n.next_move(color, board)
def next_move(self, color, board): """ 次の一手 """ pid = Timer.get_pid(self) # タイムアウト監視用のプロセスID moves = board.get_legal_moves(color) # 手の候補 best_move = self.get_best_move(color, board, moves, self.depth, pid) return best_move
def next_move(self, color, board): """ 次の一手 """ pid = Timer.get_pid(self) # タイムアウト監視用のプロセスID moves = board.get_legal_moves(color) # 手の候補を取得 scores = [0 for _ in range(len(moves))] # スコアの初期化 for _ in range(self.count): for i, move in enumerate(moves): scores[i] += self._playout(color, board, move, pid=pid) # この手を選んだ時の勝敗を取得 if Timer.is_timeout(pid): break if Timer.is_timeout(pid): break best_score = max(scores) # ベストスコアを取得 best_moves = [ move for i, move in enumerate(moves) if scores[i] == best_score ] # ベストスコアの手を選ぶ return random.choice(best_moves) # 複数ある場合はランダムに選ぶ
def next_move(self, color, board): """ 次の一手 """ pid = Timer.get_pid(self) # タイムアウト監視用のプロセスID if board.size == 8 and sys.maxsize == MAXSIZE64 and hasattr( board, '_black_bitboard' ) and not NegaScoutMethods.NEGASCOUT_SIZE8_64BIT_ERROR: return NegaScoutMethods.next_move(color, board, self._MIN, self._MAX, self.depth, self.evaluator, pid, self.timer, self.measure) moves = board.get_legal_moves(color) # 手の候補 best_move, _ = self.get_best_move(color, board, moves, self.depth, pid) return best_move
def get_score(self, color, board, depth, pid=None): """ 評価値の取得 """ # ゲーム終了 or 最大深さに到達 legal_moves_b_bits = board.get_legal_moves_bits('black') legal_moves_w_bits = board.get_legal_moves_bits('white') is_game_end = True if not legal_moves_b_bits and not legal_moves_w_bits else False if is_game_end or depth <= 0: sign = 1 if color == 'black' else -1 return self.evaluator.evaluate(color=color, board=board, possibility_b=board.get_bit_count(legal_moves_b_bits), possibility_w=board.get_bit_count(legal_moves_w_bits)) * sign # noqa: E501 # パスの場合 legal_moves_bits = legal_moves_b_bits if color == 'black' else legal_moves_w_bits next_color = 'white' if color == 'black' else 'black' if not legal_moves_bits: return -self.get_score(next_color, board, depth, pid=pid) # 評価値を算出 max_score = self._MIN size = board.size mask = 1 << ((size**2)-1) for y in range(size): skip = False for x in range(size): if legal_moves_bits & mask: board.put_disc(color, x, y) score = -self.get_score(next_color, board, depth-1, pid=pid) board.undo() if Timer.is_timeout(pid): skip = True break else: max_score = max(max_score, score) # 最大値を選択 mask >>= 1 if skip: break return max_score
def get_best_move(self, color, board, moves, depth, pid=None): """ 最善手を選ぶ """ best_move, alpha, beta = None, self._MIN, self._MAX # 打てる手の中から評価値の最も高い手を選ぶ for move in moves: score = self.get_score(move, color, board, alpha, beta, depth, pid) if Timer.is_timeout(pid): best_move = move if best_move is None else best_move break else: if score > alpha: # 最善手を更新 alpha = score best_move = move return best_move
def _get_score(func, alphabeta, color, board, alpha, beta, depth, pid): """_get_score """ # ゲーム終了 or 最大深さに到達 legal_moves_b_bits = board.get_legal_moves_bits('black') legal_moves_w_bits = board.get_legal_moves_bits('white') is_game_end = True if not legal_moves_b_bits and not legal_moves_w_bits else False if is_game_end or depth <= 0: sign = 1 if color == 'black' else -1 return alphabeta.evaluator.evaluate(color=color, board=board, possibility_b=board.get_bit_count(legal_moves_b_bits), possibility_w=board.get_bit_count(legal_moves_w_bits)) * sign # noqa: E501 # パスの場合 legal_moves_bits = legal_moves_b_bits if color == 'black' else legal_moves_w_bits next_color = 'white' if color == 'black' else 'black' if not legal_moves_bits: return -func(func, alphabeta, next_color, board, -beta, -alpha, depth, pid) # 評価値を算出 size = board.size mask = 1 << ((size**2)-1) for y in range(size): skip = False for x in range(size): if legal_moves_bits & mask: board.put_disc(color, x, y) score = -func(func, alphabeta, next_color, board, -beta, -alpha, depth-1, pid) board.undo() if Timer.is_timeout(pid): return alpha alpha = max(alpha, score) # 最大値を選択 if alpha >= beta: # 枝刈り skip = True break mask >>= 1 if skip: break return alpha
def next_move(self, color, board): """next_move """ pid = Timer.get_pid(self) # タイムアウト監視用のプロセスID # select best move next_color = 'white' if color == 'black' else 'black' next_moves = {} best_score = self._MIN if color == 'black' else self._MAX legal_moves = board.get_legal_moves(color) for move in legal_moves: board.put_disc(color, *move) score = self.get_score(next_color, board, self.depth-1, pid=pid) board.undo() best_score = max(best_score, score) if color == 'black' else min(best_score, score) # memorize next moves if score not in next_moves: next_moves[score] = [] next_moves[score].append(move) return random.choice(next_moves[best_score]) # random choice if many best scores
def _get_score(func, negascout, color, board, alpha, beta, depth, pid): """_get_score """ # ゲーム終了 or 最大深さに到達 legal_moves_b_bits = board.get_legal_moves_bits('black') legal_moves_w_bits = board.get_legal_moves_bits('white') is_game_end = True if not legal_moves_b_bits and not legal_moves_w_bits else False sign = 1 if color == 'black' else -1 if is_game_end or depth <= 0: return negascout.evaluator.evaluate( color=color, board=board, possibility_b=board.get_bit_count(legal_moves_b_bits), possibility_w=board.get_bit_count( legal_moves_w_bits)) * sign # noqa: E501 # パスの場合 legal_moves_bits = legal_moves_b_bits if color == 'black' else legal_moves_w_bits next_color = 'white' if color == 'black' else 'black' if not legal_moves_bits: return -func( func, negascout, next_color, board, -beta, -alpha, depth, pid=pid) # 着手可能数に応じて手を並び替え tmp = [] size = board.size mask = 1 << ((size**2) - 1) for y in range(size): for x in range(size): if legal_moves_bits & mask: board.put_disc(color, x, y) possibility_b = board.get_bit_count( board.get_legal_moves_bits('black')) possibility_w = board.get_bit_count( board.get_legal_moves_bits('white')) tmp += [((x, y), (possibility_b - possibility_w) * sign)] board.undo() mask >>= 1 next_moves = [i[0] for i in sorted(tmp, reverse=True, key=lambda x: x[1])] # NegaScout法 tmp, null_window, index = None, beta, 0 for move in next_moves: if alpha < beta: board.put_disc(color, *move) tmp = -func(func, negascout, next_color, board, -null_window, -alpha, depth - 1, pid=pid) board.undo() if alpha < tmp: if tmp <= null_window and index: board.put_disc(color, *move) alpha = -func(func, negascout, next_color, board, -beta, -tmp, depth - 1, pid=pid) board.undo() if Timer.is_timeout(pid): return alpha else: alpha = tmp null_window = alpha + 1 else: break index += 1 return alpha