昨日はミニマックス法を実装したけど、処理が遅いという問題が。
そこで、今日はパフォーマンスの改善を行っていく。
プロファイル
パフォーマンスの改善を考えるときに、まず最初にやらないといけないことが、プロファイル。
ボトルネックを勘違いして修正処理を行っても、全然効果が出ないから。
Rubyの場合、標準添付ライブラリの1つであるprofileを使うことで、プロファイルを簡単に行うことが出来る。
使い方は簡単。
$ ruby -r profile (プロファイルしたいRubyスクリプト)
こうすると、標準エラー出力に各メソッドの実行時間に関する統計結果が出力される。
例えば、貪欲AIの実行をプロファイルしてみると、次のような感じ。
$ ruby -r profile greedy_com.rb 1> /dev/null % cumulative self self total time seconds seconds calls ms/call ms/call name 26.47 51.95 51.95 2191201 0.02 0.03 YWF::Board#color 20.97 93.12 41.17 895402 0.05 0.08 YWF::Board#traverse_to 7.41 107.66 14.54 2979875 0.00 0.01 BasicObject#!= 6.52 120.45 12.79 1328418 0.01 0.12 YWF::Board#count 6.00 132.22 11.77 9759514 0.00 0.00 Array#[] 4.90 141.83 9.61 8764806 0.00 0.00 Fixnum#< 4.75 151.15 9.32 270050 0.03 1.37 Range#each 3.86 158.73 7.58 1392098 0.01 0.06 YWF::Board#has_color_from_to? 2.83 164.28 5.55 651540 0.01 0.16 YWF::Board#playable? 2.67 169.53 5.25 4868360 0.00 0.00 Fixnum#== 2.60 174.64 5.11 997637 0.01 0.15 YWF::Board#has_color_from? 2.11 178.79 4.15 347256 0.01 0.12 YWF::Board#copy 2.03 182.77 3.98 3198591 0.00 0.00 Fixnum#+ 2.00 186.69 3.92 730639 0.01 0.44 YWF::Board#playable_places 1.80 190.23 3.54 144914 0.02 1.80 Array#each 1.42 193.02 2.79 527489 0.01 0.01 Proc#call 0.26 193.54 0.52 57039 0.01 0.23 YWF::Board#put_piece 0.25 194.04 0.50 31846 0.02 0.21 YWF::Board#changeable? 0.19 194.42 0.38 327849 0.00 0.00 Array#[]= 0.15 194.71 0.29 16684 0.02 0.02 YWF::BoardViewer#mark 0.13 194.97 0.26 32919 0.01 0.48 YWF::Board#changeable_places 0.11 195.18 0.21 18291 0.01 0.21 YWF::BoardViewer#view 0.07 195.32 0.14 7396 0.02 17.82 YWF::Board#game_end? 0.06 195.44 0.12 18090 0.01 0.01 Kernel#print 0.05 195.54 0.10 7195 0.01 17.79 YWF::Board#win? 0.05 195.64 0.10 3796 0.03 95.93 YWF::GreedyCom#select 0.04 195.71 0.07 2605 0.03 6.58 YWF::Board#change 0.04 195.78 0.07 26938 0.00 0.00 IO#write 0.04 195.85 0.07 7830 0.01 13.94 YWF::Board#must_pass? 0.03 195.91 0.06 3817 0.02 3.92 YWF::Board#initialize 0.03 195.96 0.05 4424 0.01 0.01 IO#puts 0.03 196.01 0.05 2605 0.02 0.02 YWF::Board#change_token 0.03 196.06 0.05 3816 0.01 0.02 YWF::Board#change_turn 0.02 196.10 0.04 4424 0.01 0.02 Kernel#puts 0.02 196.14 0.04 3597 0.01 43.18 YWF::GreedyCom#calculate_board_value 0.02 196.17 0.03 4245 0.01 0.01 YWF::Board#opponent 0.02 196.20 0.03 3796 0.01 2.29 YWF::Board#legal_actions 0.02 196.23 0.03 42422 0.00 0.00 Fixnum#=== 0.01 196.25 0.02 54032 0.00 0.00 Array#push 0.01 196.27 0.02 8060 0.00 0.00 Array#size 0.01 196.29 0.02 1191 0.02 5.57 YWF::Board#play 0.00 196.29 0.00 1 0.00 0.00 TracePoint#enable 0.00 196.29 0.00 201 0.00 0.00 Kernel#sprintf 0.00 196.29 0.00 1 0.00 0.00 YWF::Game#initialize 0.00 196.29 0.00 4019 0.00 3.72 Class#new 0.00 196.29 0.00 2 0.00 0.00 YWF::GreedyCom#initialize 0.00 196.29 0.00 1811 0.00 0.00 Fixnum#to_s 0.00 196.29 0.00 1 0.00 0.00 #<Object:0x007fc6f90de478>.include 0.00 196.29 0.00 1 0.00 0.00 Module#included 0.00 196.29 0.00 1 0.00 0.00 Module#append_features 0.00 196.29 0.00 3 0.00 0.00 Kernel#require_relative 0.00 196.29 0.00 2 0.00 0.00 String#== 0.00 196.29 0.00 199 0.00 0.00 Array#initialize 0.00 196.29 0.00 1 0.00 0.00 Module#module_function 0.00 196.29 0.00 4245 0.00 0.00 Fixnum#% 0.00 196.29 0.00 2 0.00 0.00 BasicObject#singleton_method_added 0.00 196.29 0.00 2 0.00 0.00 Module#private 0.00 196.29 0.00 1 0.00 0.00 Module#protected 0.00 196.29 0.00 3816 0.00 0.00 Kernel#class 0.00 196.29 0.00 1 0.00 0.00 Module#attr_reader 0.00 196.29 0.00 33 0.00 0.00 Module#method_added 0.00 196.29 0.00 1 0.00 0.00 TracePoint#disable 0.00 196.29 0.00 6 0.00 0.00 IO#set_encoding 0.00 196.29 0.00 3816 0.00 0.00 YWF::Board#add_move 0.00 196.29 0.00 1 0.00 0.00 MonitorMixin#mon_exit 0.00 196.29 0.00 1 0.00 0.00 Mutex#unlock 0.00 196.29 0.00 3596 0.00 0.00 Fixnum#- 0.00 196.29 0.00 1 0.00 0.00 MonitorMixin#mon_check_owner 0.00 196.29 0.00 3598 0.00 0.00 Fixnum#> 0.00 196.29 0.00 1 0.00 0.00 MonitorMixin#mon_enter 0.00 196.29 0.00 1 0.00 0.00 Mutex#lock 0.00 196.29 0.00 3 0.00 0.00 Thread.current 0.00 196.29 0.00 199 0.00 0.00 Symbol#to_s 0.00 196.29 0.00 398 0.00 0.00 Fixnum#inspect 0.00 196.29 0.00 199 0.00 0.00 Array#to_s 0.00 196.29 0.00 199 0.00 0.00 Array#flatten 0.00 196.29 0.00 202 0.00 1943.27 YWF::Game#start 0.00 196.29 0.00 20 0.00 10.00 YWF::Board#pass 0.00 196.29 0.00 3 0.00 0.00 Class#inherited -0.00 196.29 -0.00 1 -0.00 196250.00 Kernel#loop 0.00 196.29 0.00 1 0.00 196290.00 #toplevel
メソッドが呼び出されてから返るまでの時間を「全体時間」、全体時間のうち、そのメソッド自身が処理を行ってた時間を「正味時間」と呼ぶことにしたとき、各列は左から順に
- 正味時間の合計のプログラム実行全体に対する割合(%)
- 全体時間の合計(秒)
- 正味時間の合計(秒)
- 呼び出された回数
- 呼び出し1回あたりの正味時間の平均(ミリ秒)
- 呼び出し1回あたりの全体時間の平均(ミリ秒)
- メソッド名
を表している。
例えば、YWF::Board#traverse_toメソッド(上から2つ目)は、1回の呼び出しで正味時間として平均0.05ミリ秒、全体時間として平均0.08ミリ秒かかっていて、それが895,402回呼び出されていて、結果として、正味時間の合計は41.17秒、全体時間の合計は93.12秒となっていて、プログラム全体の20.97%の時間を使っている、となっている。
時間がかかっている処理で、自分が書いた部分の上位を見ていくと、
- YWF::Board#color
- YWF::Board#traverse_to
- YWF::Board#count
- YWF::Board#has_color_from_to?
- YWF::Board#playable?
という感じ。
なので、このあたりをいかに改善していくかがポイントとなる。
ここで、時間がかかっている原因を分けて考える必要がある。
- 1回の処理は軽いけれど、たくさん呼ばれているので、合計として時間がかかっている
- たくさん呼ばれているわけじゃないけど、1回の処理が重いので、合計として時間がかかっている
- その両方(処理も重いし、呼ばれる回数も多い)
たくさん呼ばれるので時間がかかるというのであれば、それを呼び出してる側で出来るだけ呼び出す回数を少なくする必要があるし、1回の処理が重いというのであれば、その処理自体を軽くする必要がある。
上位に挙がっているメソッドをみてみると、それ自体の処理時間はそれほど多くかかってない。
しかし、とにかく呼び出される回数が多いので、そのせいで全体として時間がかかってしまっている感じだ。
なので、いかにこれらのメソッドを呼び出さなくて済むようにするのかが、パフォーマンス改善の肝となる。
キャッシュ
こういったときに有効なのが、キャッシュだ。
メソッドの実行結果をメモリに保存しておくことで、2回目以降はそのメソッドを呼び出さずに、キャッシュに残っている結果だけを見ればよくなる。
キャッシュを使う場合、一つ気をつけなければいけないのは、キャッシュが無効になる場合があるということ。
メソッドの呼び出しがオブジェクトの状態に依存している場合、オブジェクトの状態が変わってしまうと、そのキャッシュはもはや有効とは限らなくなる。
しかし、今回の場合、ボードの状態は不変なので、そういったことを気にする必要はない。
これは、石を置くときに、オブジェクトの状態を変えてしまうのではなく、新たにオブジェクトを作るようにしたことのメリットでもある。
さて、あとはどのデータをキャッシュしておくのかの問題で、playable?→has_color_from_to?→traverse_toと呼び出しがされていくわけだから、playable?やchangeable?の結果をキャッシュしておくのがまずよさそうだ。
さらに、playable?やchangeable?を呼び出すplayable_placesやchangeable_placesの結果もキャッシュしてしまってもいいかもしれない。
それと、countからは各マスごとにcolorが呼び出されるので、countの結果もキャッシュしておくとよさそうだ。
そこで、次のような修正を加える。
def initialize(other=nil) @board = [ # 省略 ] @move = 1 @turn = BLACK @token = GRAY if other copy(other) end + + # cache (lazy) + @count = Array.new(4) + @legal_check_cache = Array.new(ROW_MAX*COL_MAX) + @playable_places_cache = nil + @changeable_places_cache = nil end # 省略 def count(color) if (color != EMPTY) && (color != BLACK) && (color != GRAY) && (color != WHITE) raise "invalid color. [color: #{color}]" end - count = 0 - (ROW_MIN..ROW_MAX).each do |row| - (COL_MIN..COL_MAX).each do |col| - if self.color(row, col) == color - count += 1 + if @count[color].nil? + count = 0 + (ROW_MIN..ROW_MAX).each do |row| + (COL_MIN..COL_MAX).each do |col| + if self.color(row, col) == color + count += 1 + end end end + @count[color] = count end - count + @count[color] end def playable?(row, col) if self.color(row, col) != EMPTY return false end - has_color_from?(row, col) + index = (row - 1) * COL_MAX + (col - 1) + if @legal_check_cache[index].nil? + @legal_check_cache[index] = has_color_from?(row, col) + end + @legal_check_cache[index] end def changeable?(row, col) if self.color(row, col) != GRAY return false end - if (@token != GRAY) && (@token != @turn) - return false + index = (row - 1) * COL_MAX + (col - 1) + if @legal_check_cache[index].nil? + if (@token != GRAY) && (@token != @turn) + @legal_check_cache[index] = false + else + @legal_check_cache[index] = has_color_from?(row, col) + end end - - has_color_from?(row, col) + @legal_check_cache[index] end # 省略 def playable_places - places = [] - (ROW_MIN..ROW_MAX).each do |row| - (COL_MIN..COL_MAX).each do |col| - if self.playable?(row, col) - places.push [row, col] + if @playable_places_cache.nil? + places = [] + (ROW_MIN..ROW_MAX).each do |row| + (COL_MIN..COL_MAX).each do |col| + if self.playable?(row, col) + places.push [row, col] + end end end + @playable_places_cache = places end - places + @playable_places_cache.clone end def changeable_places - places = [] - if @token != opponent - (ROW_MIN..ROW_MAX).each do |row| - (COL_MIN..COL_MAX).each do |col| - if self.changeable?(row, col) - places.push [row, col] + if @changeable_places_cache.nil? + places = [] + if @token != opponent + (ROW_MIN..ROW_MAX).each do |row| + (COL_MIN..COL_MAX).each do |col| + if self.changeable?(row, col) + places.push [row, col] + end end end end + @changeable_places_cache = places end - places + @changeable_places_cache.clone end
キャッシュを保存するためのインスタンス変数を新たに追加し、キャッシュが空なら実際の呼び出しを行って、そうでなければキャッシュに保存されている内容を使うようにしてある。
この修正を加えて先程と同じようにプロファイルをしてみると、次のような結果になった。
$ ruby -r profile greedy_com.rb 1> /dev/null % cumulative self self total time seconds seconds calls ms/call ms/call name 28.60 35.97 35.97 1549276 0.02 0.03 YWF::Board#color 16.61 56.87 20.90 441897 0.05 0.08 YWF::Board#traverse_to 7.66 66.51 9.64 1004058 0.01 0.12 YWF::Board#count 6.26 74.38 7.87 1594437 0.00 0.01 BasicObject#!= 6.03 81.96 7.58 6102807 0.00 0.00 Array#[] 5.77 89.22 7.26 6197106 0.00 0.00 Fixnum#< 5.33 95.93 6.71 190800 0.04 1.22 Range#each 3.94 100.89 4.96 310206 0.02 0.16 YWF::Board#playable? 3.16 104.87 3.98 347256 0.01 0.13 YWF::Board#copy 3.08 108.75 3.88 698284 0.01 0.06 YWF::Board#has_color_from_to? 2.71 112.16 3.41 2806018 0.00 0.00 Fixnum#== 1.92 114.57 2.41 1806569 0.00 0.00 Fixnum#+ 1.80 116.83 2.26 351379 0.01 0.46 YWF::Board#playable_places 1.73 119.00 2.17 469574 0.00 0.15 YWF::Board#has_color_from? 1.49 120.88 1.88 70356 0.03 2.26 Array#each 1.05 122.20 1.32 287180 0.00 0.01 Proc#call 0.37 122.67 0.47 404806 0.00 0.00 Array#[]= 0.37 123.14 0.47 57039 0.01 0.24 YWF::Board#put_piece 0.33 123.56 0.42 23179 0.02 0.18 YWF::Board#changeable? 0.21 123.82 0.26 18291 0.01 0.23 YWF::BoardViewer#view 0.18 124.05 0.23 143114 0.00 0.00 Fixnum#- 0.14 124.23 0.18 16684 0.01 0.01 YWF::BoardViewer#mark 0.11 124.37 0.14 18090 0.01 0.01 Kernel#print 0.11 124.51 0.14 69759 0.00 0.00 Fixnum#* 0.09 124.62 0.11 7396 0.01 9.56 YWF::Board#game_end? 0.09 124.73 0.11 23289 0.00 0.52 YWF::Board#changeable_places 0.06 124.81 0.08 26938 0.00 0.00 IO#write 0.06 124.88 0.07 7830 0.01 7.18 YWF::Board#must_pass? 0.06 124.95 0.07 3597 0.02 26.17 YWF::GreedyCom#calculate_board_value 0.06 125.02 0.07 8458 0.01 0.01 Kernel#clone 0.06 125.09 0.07 69759 0.00 0.00 BasicObject#== 0.06 125.16 0.07 4424 0.02 0.02 IO#puts 0.05 125.22 0.06 2605 0.02 6.25 YWF::Board#change 0.05 125.28 0.06 3796 0.02 61.90 YWF::GreedyCom#select 0.04 125.33 0.05 11653 0.00 1.37 Class#new 0.04 125.38 0.05 4108 0.01 0.01 YWF::Board#opponent 0.04 125.43 0.05 28772 0.00 0.00 Array#push 0.03 125.47 0.04 42422 0.00 0.00 Fixnum#=== 0.03 125.51 0.04 7195 0.01 9.28 YWF::Board#win? 0.02 125.54 0.03 8458 0.00 0.01 Kernel#initialize_clone 0.02 125.57 0.03 1191 0.03 5.80 YWF::Board#play 0.02 125.60 0.03 3796 0.01 0.73 YWF::Board#legal_actions 0.02 125.63 0.03 3817 0.01 4.17 YWF::Board#initialize 0.02 125.66 0.03 7833 0.00 0.00 Array#initialize 0.02 125.68 0.02 2605 0.01 0.01 YWF::Board#change_token 0.02 125.70 0.02 4424 0.00 0.03 Kernel#puts 0.02 125.72 0.02 8458 0.00 0.00 Array#initialize_copy 0.02 125.74 0.02 3816 0.01 0.02 YWF::Board#change_turn 0.01 125.75 0.01 199 0.05 0.05 Array#to_s 0.01 125.76 0.01 15101 0.00 0.00 NilClass#nil? 0.01 125.77 0.01 3598 0.00 0.00 Fixnum#> 0.01 125.78 0.01 8060 0.00 0.00 Array#size 0.01 125.79 0.01 7955 0.00 0.00 Kernel#nil? 0.00 125.79 0.00 1 0.00 0.00 TracePoint#enable 0.00 125.79 0.00 201 0.00 0.00 Kernel#sprintf 0.00 125.79 0.00 1 0.00 0.00 YWF::Game#initialize 0.00 125.79 0.00 2 0.00 0.00 YWF::GreedyCom#initialize 0.00 125.79 0.00 1 0.00 0.00 #<Object:0x007fce938de460>.include 0.00 125.79 0.00 1 0.00 0.00 Module#included 0.00 125.79 0.00 1 0.00 0.00 Module#append_features 0.00 125.79 0.00 3 0.00 0.00 Kernel#require_relative 0.00 125.79 0.00 2 0.00 0.00 String#== 0.00 125.79 0.00 1 0.00 0.00 Module#module_function 0.00 125.79 0.00 4108 0.00 0.00 Fixnum#% 0.00 125.79 0.00 1811 0.00 0.00 Fixnum#to_s 0.00 125.79 0.00 2 0.00 0.00 BasicObject#singleton_method_added 0.00 125.79 0.00 2 0.00 0.00 Module#private 0.00 125.79 0.00 3816 0.00 0.00 Kernel#class 0.00 125.79 0.00 1 0.00 0.00 Module#protected 0.00 125.79 0.00 1 0.00 0.00 Module#attr_reader 0.00 125.79 0.00 1 0.00 125780.00 Kernel#loop 0.00 125.79 0.00 3816 0.00 0.01 YWF::Board#add_move 0.00 125.79 0.00 3 0.00 0.00 Class#inherited 0.00 125.79 0.00 6 0.00 0.00 IO#set_encoding 0.00 125.79 0.00 1 0.00 0.00 MonitorMixin#mon_exit 0.00 125.79 0.00 1 0.00 0.00 Mutex#unlock 0.00 125.79 0.00 1 0.00 0.00 MonitorMixin#mon_check_owner 0.00 125.79 0.00 1 0.00 0.00 MonitorMixin#mon_enter 0.00 125.79 0.00 1 0.00 0.00 Mutex#lock 0.00 125.79 0.00 199 0.00 0.00 Symbol#to_s 0.00 125.79 0.00 398 0.00 0.00 Fixnum#inspect 0.00 125.79 0.00 3 0.00 0.00 Thread.current 0.00 125.79 0.00 199 0.00 0.00 Array#flatten 0.00 125.79 0.00 1 0.00 0.00 TracePoint#disable 0.00 125.79 0.00 20 0.00 2.00 YWF::Board#pass 0.00 125.79 0.00 33 0.00 0.00 Module#method_added -0.00 125.79 -0.00 202 -0.00 1245.40 YWF::Game#start 0.00 125.79 0.00 1 0.00 125790.00 #toplevel
見ての通り、かなり改善されていることが分かると思う。
2,191,201回あったYWF::Board#colorの呼び出しは1,549,276回に減り、YWF::Board#traverse_toの呼び出しも895,402回から441,987回に減っている。
全体の実行時間としても、元は196秒近くかかっていたのが、126秒に減っている。
これは約1.5倍のスピードだ。
実行例
さて、スピードも改善されたことだし、ミニマックスAIの実行例を。
[001] turn: O, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | - | O | X | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | X | - | O | | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | X | - | | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ play [3, 7]. [002] turn: X, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | | | | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | - | O | - | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | X | O | O | | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | X | - | | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ play [3, 6]. [003] turn: O, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | | | X | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | - | - | - | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | X | O | O | | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | X | - | | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ play [3, 4]. [004] turn: X, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | O | | X | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | O | O | - | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | - | O | O | | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | X | - | | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ play [7, 6]. [005] turn: O, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | O | | X | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | O | O | X | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | - | O | - | | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | X | X | | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | X | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ play [2, 7]. [006] turn: X, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | O | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | O | | - | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | O | O | X | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | - | O | - | | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | X | X | | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | X | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ play [3, 3]. [007] turn: O, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | O | | | +---+---+---+---+---+---+---+---+---+ 3 | | | X | O | | - | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | - | O | X | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | - | - | - | | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | X | X | | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | X | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ play [4, 3]. [008] turn: X, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | O | | | +---+---+---+---+---+---+---+---+---+ 3 | | | X | O | | - | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | O | O | O | X | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | - | - | - | | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | X | X | | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | X | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ play [4, 2]. [009] turn: O, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | O | | | +---+---+---+---+---+---+---+---+---+ 3 | | | X | O | | - | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | X | - | - | - | X | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | - | - | - | | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | X | X | | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | X | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ play [6, 3]. [010] turn: X, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | O | | | +---+---+---+---+---+---+---+---+---+ 3 | | | X | O | | O | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | X | - | - | O | X | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | O | - | - | | | | +---+---+---+---+---+---+---+---+---+ 6 | | | O | O | X | X | | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | X | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ play [3, 2]. 〜長いので省略〜 [081] turn: O, token: O 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | O | O | O | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 2 | O | O | O | - | X | - | - | - | - | +---+---+---+---+---+---+---+---+---+ 3 | O | O | - | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 4 | O | - | - | - | - | - | - | O | O | +---+---+---+---+---+---+---+---+---+ 5 | O | O | X | O | O | - | O | O | - | +---+---+---+---+---+---+---+---+---+ 6 | O | O | X | - | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 7 | - | O | X | O | - | O | O | O | | +---+---+---+---+---+---+---+---+---+ 8 | O | O | X | O | O | - | - | O | | +---+---+---+---+---+---+---+---+---+ 9 | X | - | X | - | - | X | X | O | | +---+---+---+---+---+---+---+---+---+ change [2, 9]. [082] turn: X, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | O | O | O | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 2 | O | O | O | O | - | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 3 | O | O | - | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 4 | O | - | - | - | - | - | - | O | O | +---+---+---+---+---+---+---+---+---+ 5 | O | O | X | O | O | - | O | O | - | +---+---+---+---+---+---+---+---+---+ 6 | O | O | X | - | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 7 | - | O | X | O | - | O | O | O | | +---+---+---+---+---+---+---+---+---+ 8 | O | O | X | O | O | - | - | O | | +---+---+---+---+---+---+---+---+---+ 9 | X | - | X | - | - | X | X | O | | +---+---+---+---+---+---+---+---+---+ change [4, 7]. [083] turn: O, token: O 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | O | O | O | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 2 | O | O | O | O | - | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 3 | O | O | - | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 4 | O | - | - | - | - | - | X | O | O | +---+---+---+---+---+---+---+---+---+ 5 | O | O | X | O | O | X | - | O | - | +---+---+---+---+---+---+---+---+---+ 6 | O | O | X | - | - | O | - | O | O | +---+---+---+---+---+---+---+---+---+ 7 | - | O | X | - | - | O | - | O | | +---+---+---+---+---+---+---+---+---+ 8 | O | O | X | O | O | - | X | O | | +---+---+---+---+---+---+---+---+---+ 9 | X | - | X | - | - | X | X | O | | +---+---+---+---+---+---+---+---+---+ change [4, 4]. [084] turn: X, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | O | O | O | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 2 | O | O | O | O | - | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 3 | O | O | O | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 4 | O | O | O | O | O | O | - | O | O | +---+---+---+---+---+---+---+---+---+ 5 | O | O | - | O | O | X | - | O | - | +---+---+---+---+---+---+---+---+---+ 6 | O | O | X | - | - | O | - | O | O | +---+---+---+---+---+---+---+---+---+ 7 | - | O | X | - | - | O | - | O | | +---+---+---+---+---+---+---+---+---+ 8 | O | O | X | O | O | - | X | O | | +---+---+---+---+---+---+---+---+---+ 9 | X | - | X | - | - | X | X | O | | +---+---+---+---+---+---+---+---+---+ play [7, 9]. [085] turn: O, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | O | O | O | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 2 | O | O | O | O | - | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 3 | O | O | O | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 4 | O | O | O | O | O | O | - | O | O | +---+---+---+---+---+---+---+---+---+ 5 | O | O | - | O | O | X | - | O | - | +---+---+---+---+---+---+---+---+---+ 6 | O | O | X | - | - | O | - | O | O | +---+---+---+---+---+---+---+---+---+ 7 | - | O | X | X | X | - | X | - | X | +---+---+---+---+---+---+---+---+---+ 8 | O | O | X | O | O | - | X | - | | +---+---+---+---+---+---+---+---+---+ 9 | X | - | X | - | - | X | X | O | | +---+---+---+---+---+---+---+---+---+ play [9, 9]. [086] turn: X, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | O | O | O | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 2 | O | O | O | O | - | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 3 | O | O | O | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 4 | O | O | O | O | O | O | - | O | O | +---+---+---+---+---+---+---+---+---+ 5 | O | O | - | O | O | X | - | O | - | +---+---+---+---+---+---+---+---+---+ 6 | O | O | X | - | - | O | - | O | O | +---+---+---+---+---+---+---+---+---+ 7 | - | O | X | X | X | - | - | - | X | +---+---+---+---+---+---+---+---+---+ 8 | O | O | X | O | O | - | X | O | | +---+---+---+---+---+---+---+---+---+ 9 | X | - | X | - | - | X | X | O | O | +---+---+---+---+---+---+---+---+---+ play [8, 9]. [087] turn: O, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | O | O | O | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 2 | O | O | O | O | - | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 3 | O | O | O | O | O | O | O | O | O | +---+---+---+---+---+---+---+---+---+ 4 | O | O | O | O | O | O | - | O | O | +---+---+---+---+---+---+---+---+---+ 5 | O | O | - | O | O | X | - | O | - | +---+---+---+---+---+---+---+---+---+ 6 | O | O | X | - | - | O | X | O | O | +---+---+---+---+---+---+---+---+---+ 7 | - | O | X | X | X | - | - | X | X | +---+---+---+---+---+---+---+---+---+ 8 | O | O | X | O | O | - | X | - | X | +---+---+---+---+---+---+---+---+---+ 9 | X | - | X | - | - | X | X | O | O | +---+---+---+---+---+---+---+---+---+ ---------- black: 51, white: 15 O win.
ランダムAIとの対戦成績も調べたいところだけど、スピードが速くなったとはいえ、まだまだそれなりに時間がかかる・・・
なので、これについてはちょっと持ち越し。
今日はここまで!