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
print_score(moves, depth, started) click to toggle source
print_score_stats(results) click to toggle source
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