Class: RubyShogi::Engine
- Inherits:
-
Object
- Object
- RubyShogi::Engine
- Defined in:
- lib/ruby_shogi/engine.rb
Constant Summary collapse
- INF =
1000- MATERIAL_SCALE =
0.01
- RESULT_DRAW =
1- RESULT_BLACK_WIN =
2- RESULT_WHITE_WIN =
4
Instance Attribute Summary collapse
-
#board ⇒ Object
Returns the value of attribute board.
-
#debug ⇒ Object
Returns the value of attribute debug.
-
#gameover ⇒ Object
Returns the value of attribute gameover.
-
#move_now ⇒ Object
Returns the value of attribute move_now.
-
#movestogo ⇒ Object
Returns the value of attribute movestogo.
-
#printinfo ⇒ Object
Returns the value of attribute printinfo.
-
#random_mode ⇒ Object
Returns the value of attribute random_mode.
-
#time ⇒ Object
Returns the value of attribute time.
Instance Method Summary collapse
- #bench ⇒ Object
- #draw_moves(moves) ⇒ Object
- #game_status(mgen, moves) ⇒ Object
- #hash_moves(moves) ⇒ Object
- #history_remove ⇒ Object
- #history_reset ⇒ Object
- #history_undo ⇒ Object
- #init_mate_bonus ⇒ Object
-
#initialize(random_mode: false) ⇒ Engine
constructor
A new instance of Engine.
- #is_gameover?(mgen, moves) ⇒ Boolean
- #jishogi? ⇒ Boolean
- #make_move?(move) ⇒ Boolean
- #move_list ⇒ Object
- #print_move_list(moves) ⇒ Object
- #print_score(moves, depth, started) ⇒ Object
- #print_score_stats(results) ⇒ Object
- #search(moves) ⇒ Object
- #search_moves_b(cur, depth, total = 0) ⇒ Object
- #search_moves_w(cur, depth, total = 0) ⇒ Object
- #stats(rounds = 10) ⇒ Object
- #think ⇒ Object
Constructor Details
#initialize(random_mode: false) ⇒ Engine
Returns a new instance of Engine.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# 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 |
Instance Attribute Details
#board ⇒ Object
Returns the value of attribute board.
10 11 12 |
# File 'lib/ruby_shogi/engine.rb', line 10 def board @board end |
#debug ⇒ Object
Returns the value of attribute debug.
10 11 12 |
# File 'lib/ruby_shogi/engine.rb', line 10 def debug @debug end |
#gameover ⇒ Object
Returns the value of attribute gameover.
10 11 12 |
# File 'lib/ruby_shogi/engine.rb', line 10 def gameover @gameover end |
#move_now ⇒ Object
Returns the value of attribute move_now.
10 11 12 |
# File 'lib/ruby_shogi/engine.rb', line 10 def move_now @move_now end |
#movestogo ⇒ Object
Returns the value of attribute movestogo.
10 11 12 |
# File 'lib/ruby_shogi/engine.rb', line 10 def movestogo @movestogo end |
#printinfo ⇒ Object
Returns the value of attribute printinfo.
10 11 12 |
# File 'lib/ruby_shogi/engine.rb', line 10 def printinfo @printinfo end |
#random_mode ⇒ Object
Returns the value of attribute random_mode.
10 11 12 |
# File 'lib/ruby_shogi/engine.rb', line 10 def random_mode @random_mode end |
#time ⇒ Object
Returns the value of attribute time.
10 11 12 |
# File 'lib/ruby_shogi/engine.rb', line 10 def time @time end |
Instance Method Details
#bench ⇒ Object
207 208 209 210 211 212 213 |
# 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) ⇒ Object
138 139 140 141 142 143 144 |
# 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) ⇒ Object
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/ruby_shogi/engine.rb', line 150 def game_status(mgen, moves) if moves.length == 0 if @board.wtm && mgen.checks_b?(@board.find_white_king) return RubyShogi::Engine::RESULT_BLACK_WIN elsif !@board.wtm && mgen.checks_w?(@board.find_black_king) 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) ⇒ Object
146 147 148 |
# File 'lib/ruby_shogi/engine.rb', line 146 def hash_moves(moves) moves.each { |board| board.create_hash } end |
#history_remove ⇒ Object
48 49 50 |
# File 'lib/ruby_shogi/engine.rb', line 48 def history_remove @board = @history.remove end |
#history_reset ⇒ Object
44 45 46 |
# File 'lib/ruby_shogi/engine.rb', line 44 def history_reset @history.reset end |
#history_undo ⇒ Object
52 53 54 |
# File 'lib/ruby_shogi/engine.rb', line 52 def history_undo @board = @history.undo end |
#init_mate_bonus ⇒ Object
35 36 37 38 39 40 41 42 |
# 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) ⇒ Boolean
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
# 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?(@board.find_white_king) puts "0-1 {Black mates}" return true elsif !@board.wtm && mgen.checks_w?(@board.find_black_king) puts "1-0 {White mates}" return true end end false end |
#jishogi? ⇒ Boolean
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# 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) ⇒ Boolean
66 67 68 69 70 71 72 73 74 75 76 77 78 |
# 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 ⇒ Object
60 61 62 63 64 |
# 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) ⇒ Object
56 57 58 |
# 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) ⇒ Object
80 81 82 83 84 85 86 |
# 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) ⇒ Object
239 240 241 242 243 244 245 |
# File 'lib/ruby_shogi/engine.rb', line 239 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) ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# 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) ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 112 |
# 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) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 |
# 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) ⇒ Object
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/ruby_shogi/engine.rb', line 247 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 ⇒ Object
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
# 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 #print_move_list(moves) @board = moves[0] end print_move_list(moves) if @debug @history.add(@board) @board.move_str end |