lib/bioshogi/ai/brain.rb in bioshogi-0.0.3 vs lib/bioshogi/ai/brain.rb in bioshogi-0.0.4
- old
+ new
@@ -2,11 +2,11 @@
# frozen-string-literal: true
require "timeout"
module Bioshogi
- module Ai
+ module AI
# 指定の Diver を使って簡単に反復探索するためのクラス
class Brain
def self.human_format(infos)
infos.collect.with_index do |e, i|
{
@@ -48,17 +48,17 @@
# > 最も深い探索でのスコアの推定値がより正確になるという利点がある。また、探索順序を改善することができるため、
# > 探索をより高速に行えるという利点もある(前の反復で最善とされた手を次の反復で最初に調べることでアルファ・ベータ法の効率が良くなる)。
def iterative_deepening(params = {})
# このパラメータはそのまま Diver に渡している
params = {
- depth_max_range: 1..1, # 1..3 なら時間がある限り1→2→3手と読み進めていく。3手詰限定なら 3..3 とする
- time_limit: nil, # nil なら時間制限なし。時間指定なしなら 深度を を 1..3 のようにする意味はなくて最初から 3..3 とすればよい
- mate_mode: false, # 王手になる手と、王手をかわす手だけを生成するか?
+ :depth_max_range => 1..1, # 1..3 なら時間がある限り1→2→3手と読み進めていく。3手詰限定なら 3..3 とする
+ :time_limit => nil, # nil なら時間制限なし。時間指定なしなら 深度を を 1..3 のようにする意味はなくて最初から 3..3 とすればよい
+ :mate_mode => false, # 王手になる手と、王手をかわす手だけを生成するか?
# 常に diver_instance に渡すもの
- base_player: player, # どちらのプレイヤーから開始したか。詰将棋のときなど先後で指し手の方針を明確に分ける必要があるため。
- current_player: player.opponent_player, # Diver#dive の最初の player として使うため
+ :base_player => player, # どちらのプレイヤーから開始したか。詰将棋のときなど先後で指し手の方針を明確に分ける必要があるため。
+ :current_player => player.opponent_player, # Diver#dive の最初の player として使うため
}.merge(params)
if params[:time_limit]
params[:time_limit_exceeded] ||= Time.now + params[:time_limit]
end
@@ -76,10 +76,11 @@
# children = children.to_a # 何度も実行するためあえて配列化しておくの重要
# end
# ordered_children = children # 前の反復で最善とされた順に並んでいる手
+ child_count = 0
hands = []
provisional_hands = []
mate = false
finished = catch :time_limit_exceeded do
params[:depth_max_range].each do |depth_max|
@@ -87,10 +88,11 @@
provisional_hands = []
mate = false
children.each do |hand|
logger.debug "#ROOT #{hand}" if logger
+ child_count += 1
# 即詰があれば探索を速攻打ち切る場合
# 無効にしてもいいけど他の探索で時間がかかったら、この深さの探索全体がTLEでキャンセルされる可能性がある
# if true
# if hand.king_captured?
@@ -102,12 +104,19 @@
# end
hand.sandbox_execute(container) do
start_time = Time.now
v, pv = diver.dive(hand_route: [hand]) # TLEが発生してするとcatchまで飛ぶ
- v = -v # 相手の良い手は自分のマイナス
- provisional_hands << {hand: hand, score: v, black_side_score: v * player.location.value_sign, best_pv: pv, eval_times: diver.eval_counter, sec: Time.now - start_time}
+ v = -v # 相手の良い手は自分のマイナス
+ provisional_hands << {
+ :hand => hand,
+ :score => v,
+ :black_side_score => v * player.location.value_sign,
+ :best_pv => pv,
+ :eval_times => diver.eval_counter,
+ :sec => Time.now - start_time,
+ }
# 1手詰: (v >= SCORE_MAX - 0) 自分勝ち
# 2手詰: (v >= SCORE_MAX - 1) 相手勝ち
# 3手詰: (v >= SCORE_MAX - 2) 自分勝ち
# 4手詰: (v >= SCORE_MAX - 3) 相手勝ち
# 5手詰: (v >= SCORE_MAX - 4) 自分勝ち
@@ -146,11 +155,12 @@
end
end
# 自分の合法手があるのに相手の手を1手も見つけられない状況
# TLEが早過ぎる場合に起きる
- if !children.empty? && hands.empty?
+ # TLEが早過ぎる場合、そもそも childrens.empty? はエラーになる
+ if child_count >= 1 && hands.empty?
raise BrainProcessingHeavy, "合法手を生成したにもかかわらず、指し手の候補を絞れません。制限時間を増やすか読みの深度を浅くしてください : #{params}"
end
hands
end
@@ -179,11 +189,18 @@
diver = diver_instance(current_player: player.opponent_player)
create_all_hands(promoted_only: true).collect { |hand|
hand.sandbox_execute(container) do
start_time = Time.now
v, pv = diver.dive
- {hand: hand, score: -v, socre2: -v * player.location.value_sign, best_pv: pv, eval_times: diver.eval_counter, sec: Time.now - start_time}
+ {
+ :hand => hand,
+ :score => -v,
+ :socre2 => -v * player.location.value_sign,
+ :best_pv => pv,
+ :eval_times => diver.eval_counter,
+ :sec => Time.now - start_time,
+ }
end
}.sort_by { |e| -e[:score] }
end
# すべての手を指してみて評価する (探索しない)
@@ -191,10 +208,17 @@
evaluator = player.evaluator(params.merge(params))
create_all_hands(promoted_only: true).collect { |hand|
hand.sandbox_execute(container) do
start_time = Time.now
v = evaluator.score
- {hand: hand, score: v, socre2: v * player.location.value_sign, best_pv: [], eval_times: 1, sec: Time.now - start_time}
+ {
+ :hand => hand,
+ :score => v,
+ :socre2 => v * player.location.value_sign,
+ :best_pv => [],
+ :eval_times => 1,
+ :sec => Time.now - start_time,
+ }
end
}.sort_by { |e| -e[:score] }
end
end
end