class RubyShogi::Engine
Constants
- INF
- MATERIAL_SCALE
- RESULT_BLACK_WIN
- RESULT_DRAW
- RESULT_WHITE_WIN
Attributes
board[RW]
debug[RW]
gameover[RW]
move_now[RW]
movestogo[RW]
printinfo[RW]
random_mode[RW]
time[RW]
Public Class Methods
new(random_mode: false)
click to toggle source
# File lib/ruby_shogi/engine.rb, line 18 def initialize(random_mode: false) init_mate_bonus @board = RubyShogi::Board.new @random_mode = random_mode @history = RubyShogi::History.new @board.startpos @printinfo = true @time = 10 # seconds @movestogo = 40 @stop_time = 0 @stop_search = false @nodes = 0 @move_now = false @debug = false @gameover = false end
Public Instance Methods
bench()
click to toggle source
# File lib/ruby_shogi/engine.rb, line 207 def bench t = Time.now @time = 500 think diff = Time.now - t puts "= #{@nodes} nodes | #{diff.round(3)} s | #{(@nodes/diff).to_i} nps" end
draw_moves(moves)
click to toggle source
# File lib/ruby_shogi/engine.rb, line 138 def draw_moves(moves) moves.each do | board | if @history.is_draw?(board) board.nodetype, board.score = 2, 0 end end end
game_status(mgen, moves)
click to toggle source
# File lib/ruby_shogi/engine.rb, line 150 def game_status(mgen, moves) if moves.length == 0 if @board.wtm && mgen.checks_b? return RubyShogi::Engine::RESULT_BLACK_WIN elsif !@board.wtm && mgen.checks_w? return RubyShogi::Engine::RESULT_WHITE_WIN end return RubyShogi::Engine::RESULT_DRAW end @board.create_hash if @history.is_draw?(@board, 3) || @board.material_draw? return RubyShogi::Engine::RESULT_DRAW end 0 end
hash_moves(moves)
click to toggle source
# File lib/ruby_shogi/engine.rb, line 146 def hash_moves(moves) moves.each { |board| board.create_hash } end
history_remove()
click to toggle source
# File lib/ruby_shogi/engine.rb, line 48 def history_remove @board = @history.remove end
history_reset()
click to toggle source
# File lib/ruby_shogi/engine.rb, line 44 def history_reset @history.reset end
history_undo()
click to toggle source
# File lib/ruby_shogi/engine.rb, line 52 def history_undo @board = @history.undo end
init_mate_bonus()
click to toggle source
# File lib/ruby_shogi/engine.rb, line 35 def init_mate_bonus @mate_bonus = [1] * 100 (0..20).each { |i| @mate_bonus[i] += 20 - i } @mate_bonus[0] = 50 @mate_bonus[1] = 40 @mate_bonus[2] = 30 @mate_bonus[3] = 25 end
is_gameover?(mgen, moves)
click to toggle source
# File lib/ruby_shogi/engine.rb, line 184 def is_gameover?(mgen, moves) @board.create_hash return true if jishogi? if @board.fullmoves > 900 puts "1/2-1/2 {Draw by Max Moves}" return true end if @history.is_draw?(@board, 3) puts "1/2-1/2 {Draw by Sennichite}" return true end if moves.length == 0 if @board.wtm && mgen.checks_b? puts "0-1 {Black mates}" return true elsif !@board.wtm && mgen.checks_w? puts "1-0 {White mates}" return true end end false end
jishogi?()
click to toggle source
# File lib/ruby_shogi/engine.rb, line 166 def jishogi? if @board.jishogi? w = @board.count_jishogi_w b = @board.count_jishogi_b if w >= 24 && b < 24 puts "1-0 {White wins by Jishogi}" return true elsif w < 24 && b >= 24 puts "0-1 {Black wins by Jishogi}" return true else puts "1/2-1/2 {Draw by Impasse}" return true end end false end
make_move?(move)
click to toggle source
# File lib/ruby_shogi/engine.rb, line 66 def make_move?(move) mgen = @board.mgen_generator moves = mgen.generate_moves moves.each do |board| if board.move_str == move @history.add(board) @board = board return true end end puts "illegal move: #{move}" false end
move_list()
click to toggle source
# File lib/ruby_shogi/engine.rb, line 60 def move_list mgen = @board.mgen_generator moves = mgen.generate_moves moves.each_with_index { |board, i| puts "#{i} / #{board.move_str} / #{board.score}" } end
print_move_list(moves)
click to toggle source
# File lib/ruby_shogi/engine.rb, line 56 def print_move_list(moves) moves.each_with_index { |board, i| puts "#{i} / #{board.move_str} / #{board.score}" } end
print_score(moves, depth, started)
click to toggle source
# File lib/ruby_shogi/engine.rb, line 80 def print_score(moves, depth, started) return unless @printinfo moves = moves.sort_by(&:score).reverse best = moves[0] n = (100 * (Time.now - started)).to_i puts " #{depth} #{(best.score).to_i} #{n} #{@nodes} #{best.move_str}" end
print_score_stats(results)
click to toggle source
# File lib/ruby_shogi/engine.rb, line 238 def print_score_stats(results) wscore = results[RubyShogi::Engine::RESULT_WHITE_WIN] bscore = results[RubyShogi::Engine::RESULT_BLACK_WIN] draws = results[RubyShogi::Engine::RESULT_DRAW] total = wscore + bscore + draws printf("[ Score: %i - %i - %i [%.2f] %i ]\n", wscore, bscore, draws, (wscore + 0.5 * draws) / total, total) end
search(moves)
click to toggle source
# File lib/ruby_shogi/engine.rb, line 114 def search(moves) now = Time.now time4print = 0.5 divv = @movestogo < 10 ? 20 : 30 @stop_time = now + (@time / divv) depth = 2 while true moves.each do |board| puts "> #{@nodes} / #{board.move_str}" if @debug next if board.nodetype == 2 depth = 3 + rand(20) board.score += board.wtm ? search_moves_w(board, depth, 0) : search_moves_b(board, depth, 0) if Time.now > @stop_time || @move_now print_score(moves, depth, now) return end end if Time.now - now > time4print now = Time.now print_score(moves, depth, now) end end end
search_moves_b(cur, depth, total = 0)
click to toggle source
# File lib/ruby_shogi/engine.rb, line 101 def search_moves_b(cur, depth, total = 0) @nodes += 1 @stop_search = Time.now > @stop_time || total > 90 return 0 if @stop_search return MATERIAL_SCALE * cur.material if depth < 1 mgen = RubyShogi::MgenBlack.new(cur) moves = mgen.generate_moves if moves.length == 0 # assume mate return mgen.checks_w? ? 0.1 * @mate_bonus[total] * INF + rand : 1 end search_moves_w(moves.sample, depth - 1, total + 1) end
search_moves_w(cur, depth, total = 0)
click to toggle source
# File lib/ruby_shogi/engine.rb, line 88 def search_moves_w(cur, depth, total = 0) @nodes += 1 @stop_search = Time.now > @stop_time || total > 90 return 0 if @stop_search return MATERIAL_SCALE * cur.material if depth < 1 mgen = RubyShogi::MgenWhite.new(cur) moves = mgen.generate_moves if moves.length == 0 # assume mate return mgen.checks_b? ? 0.1 * @mate_bonus[total] * -INF + rand : 1 end search_moves_b(moves.sample, depth - 1, total + 1) end
stats(rounds = 10)
click to toggle source
# File lib/ruby_shogi/engine.rb, line 246 def stats(rounds = 10) @nodes = 0 @move_now = false board = @board results = [0] * 5 puts "Running stats ..." rounds.times do |n| @board = board @history.reset while true mgen = @board.mgen_generator moves = mgen.generate_moves hash_moves(moves) draw_moves(moves) status = game_status(mgen, moves) @board = moves.sample @history.add(@board) if status != 0 results[status] += 1 break end end print_score_stats(results) if (n + 1) % 2 == 0 && n + 1 < rounds end puts "=" print_score_stats(results) end
think()
click to toggle source
# File lib/ruby_shogi/engine.rb, line 215 def think @nodes = 0 @move_now = false board = @board mgen = @board.mgen_generator moves = mgen.generate_moves hash_moves(moves) draw_moves(moves) func = -> { board.wtm ? moves.sort_by(&:score).reverse : moves.sort_by(&:score) } @gameover = is_gameover?(mgen, moves) return if @gameover if @random_mode @board = moves.sample else search(moves) moves = func.call @board = moves[0] end print_move_list(moves) if @debug @history.add(@board) @board.move_str end