読者です 読者をやめる 読者になる 読者になる

ai65536's blog

将棋とかプログラムとか

ShogiGUIで詰めろ判定はありまぁす

ShogiGUIのver 0.0.3.15以降で詰めろ判定できます。

 

ShogiGUIでの詰めろ判定は最初は通常探索でNull Move Pruningをしているのなら、「そこで詰めろ判定できんじゃね?それなら検討モードもそんなにいじらなくていいし」と思っていましたが、いろいろあって断念しました。

そもそも通常探索の評価値のみでよくね?と思っていた(る)ので、ごとく放置・・・

 

次善の策として、「じゃあ手動でやろう」というわけで、パス(Null Move)を指し手として入力して、詰み探索できるようにしました。

 

操作の仕方としては、

・メニューー>編集ー>パスで1手パスを入力

・その後詰み探索 これで詰みなら詰めろ

 

f:id:ai65536:20150420223614p:plain

 

 

ただ、それだけだと面倒くさいという人向けに、アシスタントという機能を入れました。アシスタントというかアナライザーなんだけど、棋譜解析と名称が被りそうだったのでアシスタントにしました。

f:id:ai65536:20150420224129p:plain

メニューー>表示ー>アシスタン 

 

アシスタント機能は、入力、対局、検討の各モードで動作し、通常探索と、詰み探索ができます。

f:id:ai65536:20150420224431p:plain

詰みにチェックを入れ開始ボタンを押すと、現在の局面を詰み探索後、詰みが無ければ1手パスして詰み探索(詰めろ探索)をします。

自動にチェックが入っていれば、局面が変わったときに自動的に詰み探索を実行します。

このパスという指し手を導入することによって、継ぎ盤や棋譜へ分岐の追加など他の機能とそご無く詰めろ機能を実装できました。

 

f:id:ai65536:20150420232158p:plain

▲2四角からの詰めろ! 

 

が、しかし、問題点がいくつかあります。

(1)棋譜出力はどうするの?

(2)gpsfishなどの将棋エンジンの対応は?

 

(1)については、棋譜にはパスという指し手が出力されます。パスという指し手はKifu for windows等では読み込めませんので、その棋譜はShogiGUIでしか読み込めなくなります。

(2)については検討モードや詰み探索では、現在は指し手をエンジン側に渡していないので問題ありません。

問題になるのはパス降を継続対局する場合ですが、positionコマンドの指し手には"pass"という文字列を送ります。これはgpsfishが"pass"という指し手を受け付けるようになっていたので、それに便乗しました。

uci(チェス)の方は"0000"という指し手をパスと見なしているようですが、gpsfishは"0000"には未対応なので"pass"の方を採用しました。

 

現在パスに対応しているものは、gpsfishと、apery - コンピュータ将棋

ここで配布しているapery 20150327盤だけです。たぶん。

ただこれもパス以降を継続対局するなどという、奇特 な人はあまりいないでしょうから、特に問題ないでしょう。

 

 

実験 AperyをAndroidで動かしてみる

やねうらおさんのブログでスマホで動かしたら云々の記事があったので、
AperyをAndroid端末で動かして動作速度を計測してみました。

動作環境

android
Android 4.1 Jelly Bean
RK3066 1.6GHz Cortex-A9 dual
memory 1GB

PC
core i5-3230M 2.6GHz 2core 4 thread
memory 8GB

※ターボ・ブースト時 3.2GHz


アンドロイドのスマホは持っていないので、スティック型のアンドロイドで代用しました。アンドロイドもPCも2年くらい前の機種です。

動作条件

・スレッド数 2

・定跡OFF
・初期局面を思考5秒で思考しNPSを計測

結果

 

機種 NPS
android 138K70K
pc 32bit nosse 914K
pc 64bit sse4.1 1466K

 

 64bitとの比較だと、だいだい速度は1/101/20くらいですね。

 32bit nosseだと1/61/12。

 

 追記

sshAndroid端末に接続して、ShogiGUIで対局してみた。

f:id:ai65536:20150417155904p:plain

1局だけ、 先手apery 32bit nosseの勝利!

 

 

32bit版AperyについてApery作者の平岡さんが興味深いツイートをしていました。

ehashというのは評価値のハッシュのことですかね。

では、該当部分のコードを見てみましょう。

// 64bit 変数1つなのは理由があって、
// データを取得している最中に他のスレッドから書き換えられることが無くなるから。
// lockless hash と呼ばれる。
// 128bit とかだったら、64bitずつ2回データを取得しないといけないので、
// key と score が対応しない可能性がある。
// transposition table は正にその問題を抱えているが、
// 静的評価値のように差分評価をする訳ではないので、問題になることは少ない。
// 64bitに収まらない場合や、transposition table なども安全に扱いたいなら、
// lockする、SSEやAVX命令を使う、チェックサムを持たせる、key を複数の変数に分けて保持するなどの方法がある。
// 32bit OS 場合、64bit 変数を 32bitずつ2回データ取得するので、下位32bitを上位32bitでxorして
// データ取得時に壊れていないか確認する。
// 31- 0 keyhigh32
// 63-32 score
struct EvaluateHashEntry {
	u32 key() const     { return static_cast<u32>(word); }
	Score score() const { return static_cast<Score>(static_cast<s64>(word) >> 32); }
	void save(const Key k, const Score s) {
		word = static_cast<u64>(k >> 32) | static_cast<u64>(static_cast<s64>(s) << 32);
	}
#if defined __x86_64__
	void encode() {}
	void decode() {}
#else
	void encode() { word ^= word >> 32; }
	void decode() { word ^= word >> 32; }
#endif
	u64 word;
};
Score evaluate(Position& pos, SearchStack* ss) {
	if (ss->staticEvalRaw != INT_MAX) {
		// null move の次の手の時のみ、ここに入る。
		assert((pos.turn() == Black ? ss->staticEvalRaw : -ss->staticEvalRaw) == evaluateUnUseDiff(pos));
		return (pos.turn() == Black ? ss->staticEvalRaw : -ss->staticEvalRaw) / FVScale;
	}

	const u32 keyHigh32 = static_cast<u32>(pos.getKey() >> 32);
	const Key keyExcludeTurn = pos.getKeyExcludeTurn();
	// ポインタで取得してはいけない。lockless hash なので key と score を同時に取得する。
	EvaluateHashEntry entry = *g_evalTable[keyExcludeTurn];
	entry.decode();
	if (entry.key() == keyHigh32) {
		ss->staticEvalRaw = entry.score();
		assert((pos.turn() == Black ? ss->staticEvalRaw : -ss->staticEvalRaw) == evaluateUnUseDiff(pos));
		return (pos.turn() == Black ? entry.score() : -entry.score()) / FVScale;
	}

	const Score score = static_cast<Score>(evaluateBody(pos, ss));
	entry.save(pos.getKey(), score);
	entry.encode();
	*g_evalTable[keyExcludeTurn] = entry;
	return (pos.turn() == Black ? score : -score) / FVScale;
}

ハッシュから評価値をとってきて、壊れていたら再計算するというようなコードでしょうか。
lock free hash とかlock less hashとか言われている処理ですかね。

詳細はよく分かりませんが、bonanzaにも同じような処理が入っていたと思います。

実験

コードを見ても妥当性がよくわからないので、
おかしいなら複数スレッド側が弱いだろうとの予測から、
1スレッドと複数スレッドの設定で自己対戦してテストしてみました。

設定は
・対局数は10局で先後を入れ替えながら対局
・スレッド数とPonderをOFF以外はデフォルトのままです
・時間は1手2秒

テストに使った32bit版はここに置いています。
https://sites.google.com/site/shogixyz/home/apery


Apery 32bit nosse

スレッド数1 スレッド数2
2勝 8勝

スレッド数1 スレッド数4
2勝 7勝 引き分け1


Apery 64bit sse4.1

スレッド数1 スレッド数2
3勝 7勝

スレッド数1 スレッド数4
1勝 9勝


Aper 32bit nosse (encode, decode部分を空行にした場合)

スレッド数1 スレッド数2
5勝 5勝
スレッド数1 スレッド数4
2勝 8勝

結論

スレッド数を増やすと強くなるの問題ないでしょう。
単純に負けなくなると思っていたのですが、64bit版も勝率100%とはいかないので、
こんなものなんでしょう。

しかし、xorしているコード除去してもそんなに弱くなってない?
という疑問がのこりました。もうちょっと弱くなると思ったんですが。


まあ今回の実験の有効性は少し疑問が疑問残りましたが、32bit版を使っておかしいなとおもったらスレッド数を1にすればいいわけですし、それで性能に不安があるのであればPCを買い替えましょう。

ShogiGUIでリモートPC上の将棋エンジンを動かす

ShogiGUIでリモートPC上の将棋エンジンを動かします。

SSHを使うだけです。

 

・この記事はバッチファイルやSSHが何か分かる人を対象に書いています。

・リモートPCがSSHで接続できることを前提に話を進めます。

Unix系のOSなら普通にSSHで接続できると思われますが、Windowsの場合はフリーのSSHサーバーがあるので、それらをインストールしてください。

 

1.SSHクライアントをインストール

PuTTY Download Page ここからplink.exeをダウンロードしてくる。

コマンドラインで使えるsshクライアントならなんでもいいです。

 

2.バッチファイルを書く

以下のようなバッチファイルを作成します。

plink -pw password user@host cd path;./engine 

 

user,passwordはホストへ接続するためのユーザーIDとパスワードです。

hostはホストのアドレスです。

pathはエンジンのあるパス

engineはエンジンの実行ファイル名です。

 

鍵の交換等すればパスワード等は不要に出来ると思いますが、詳細はググってください。

 

3.バッチファイルを動かしてみる

バッチファイルをコマンドラインか、ダブルクリックするかして実行してみてください。

上手く動作しましたか?動作しない場合は設定を見なおしてください。

 

4.作成したバッチファイルをShogiGUIにエンジン登録する

エンジンの追加で全てのファイルを選択すれば、バッチファイルも登録できます。

 

以上でリモートPC上の将棋エンジンが動作します。

これでPCを2台用意したCOM同士の対戦や、リモートPCを使った棋譜解析や検討もできますね。

 

AperyをVisual Studioでビルドして動作させる。

AperyをVisual Studio2013でビルド、動作させた時のメモ

 

ソースはaperyからforkして修正したものが以下のところにあります。

github.com


 

エラーになるところを変更

文字コードUTF-8 (!= UTF-8N)に統一

constexprを使用しないように変更、constexprを使う一部インライン関数はマクロに変更

 

この変更でビルドは通りますが、起動すると正常に動作しません。

 

動作の不具合箇所修正

デバッガで追ってみるとスレッド関連で不具合が起こってそう。

ぱっと見のコードは正しい(gccで動作しているのであたりまえだけど)ので、

さらにデバッガで追うとThreadを継承しているMainThreadでThreadのidleLoopが呼び出されている。

Aperyはスレッドの作成をTheradクラスのコンストラクタ内で行っているけれど、

これがどうも悪さをしてそうな感じ。

stockfishの該当部分のコードを参考にnewした後にスレッドを作成するように変更、あわせて破棄する部分のコードも変更をいれる。

 

これで直ったかとおもったら、quit後にAperyが正常終了しない。

デバッガで止めてみるとスレッドをjoin()したところでふんづまってる。

Aperyはスレッドの破棄をスレッドプールのデストラクタで行っているけれど、このスレッドプールはグローバル変数、スレッドの破棄はmain関数を抜けたあとに行われていることになる。

考えるのが面倒だったので、この部分もstockfishを参考にmain関数の最後に行うように変更すると、正常に終了するようになった。

追記

Hashのサイズを小さくすると、メモリ確保に失敗するという変な現象がでていて、調べるとfirstOneFromMSB()で使われている_BitScanReverse64()関数はgcc系の__builtin_clzll()と戻り値が逆なので、MSB側からカウントした値に変換する必要があります。

さいごに

AperyをVisual Studio2013でビルド、動作させることができた。

素晴らしい将棋エンジンのソースコードを公開してくれた平岡さんに感謝。

 

ShogiGUIでの対局の様子

f:id:ai65536:20150321120525p:plain

 

 

 

 

 

 

 

 

電王戦FINAL 斎藤慎太郎 五段 vs Apery ログ解析

Aperyのログを解析してみたいと思います。

ログをkif形式にして読みやすくしたもの。ここにあります。

ShogiGUIで見ると、見やすいかも。

 

評価値の遷移

 

f:id:ai65536:20150320100324j:plain

 

記者会見で平岡さんが仰っていたように、一度もAperyが有利になっていません。

 

Aperyは序盤は定跡手か思考手をランダムで選択、思考手は40手目までは評価値の差が少ない最大3つの候補からランダムで選択しています。

定跡手として指していいるのは、2,4,6,14手目、それ以外は思考手になっています。

思考手で最善手を選ばなかったのは、以下の3手だけでした。

8手目の42銀に代えて42飛車

10手目32銀に代えて62玉

20手目54銀に代えて82玉

 

話題の28手目の65銀、36手目の44角はどちらもAperyの最善手になっています。

 

28手目 65銀の局面

f:id:ai65536:20150320101524j:plain

この時の読み筋は▲5八金(49) △7二銀(71) ▲9九玉(88) △7六銀(65) ▲3三角成(77) △同 桂(21) ▲6八金(58)△2二飛(42)

 

Aperyの読み筋、その後の局面

f:id:ai65536:20150320102120j:plain

穴熊に入りかけの形が悪いとAperyは判断しているんでしょうか。

 

36手目 44と打った局面

f:id:ai65536:20150320102729j:plain

1 評価値 268 読み筋 △4四角打 ▲5五角打 △同 角(44) ▲同 歩(56) △4七歩成(46)
2 評価値 350 読み筋 △4七歩成(46) ▲6一龍(21) △7二角打 ▲4三歩打 △同 金(52)
3 評価値 588 読み筋 △7二銀(71) ▲4六歩(47) △4四角打 ▲5五角打 △同 角(44)

 

3つの候補から最善手である44角打を選択、先手の55打は読み筋どおり。

プロ推奨の47歩成は44角打より悪いと判断して採用されず。

 

LinuxでShogiGUIを使ってみる

2chのコンピュータ将棋スレにLinuxでの動作報告があったので、 とりあえず対局や検討をひと通り動作確認してみる。

 

動作確認環境

 

ShogiGUI ver 0.0.3.11

VMWare Player Ubuntu 12.0.4

Mono ver 3.2.1

 

動作環境は仮想マシンです。

エンジンは上記環境でビルドしたgpsfish r2837を使いました。

 

NG項目

棋譜のコピー/貼り付け

   Linux -> Linux NG
   Windows -> Linux OK
   Linux -> Windows ?

・将棋盤の画像を保存

   画像が壊れてる

・駒音が鳴らない

・コア数(プロセッサ数)表示がでない
・Docking Windowのレイアウト変更ができない
 単なるタブとして動作している。
 レイアウトは一応覚えているっぽい(Windows側で操作すると一応再現できる)


・終了時に盛大にエラーを吐く
・盤面の段の数字の向きが変
・盤面の罫線がはみ出している?


☆対局でCOMの思考2回目くらいで止まる(Linuxごと止まる)

 

対局が致命的ですね。検討や棋譜解析は動作していました。

エンジン設定のプロパティエディタの部分は2chの動作報告時に気になった箇所を変更していて、その対応のおかげか正常に動作していました。

 

さすがに致命的な☆の部分を調べてみる

Ver 0.0.3.8を試しに動かすと、正常に対局できる。
0.0.3.9で入れ込んだ不具合のみたいですね。

  

コンソールにログを出してみると、セマホ待ちで死んでるっぽい。

で、よくよくソースコードを見ると、一箇所WaitAsyncというのを間違って使っているのを発見。

これをWaitに直すと正しく動作する模様。

これは次のリリースで修正したほうが良さそうですね。

 

まとめ

ShogiGUIはとりあえずLinuxで対局、検討、棋譜解析が動作するようになった。

 Docking Window周りはライブラリを使っていて手がだせませんが、

それ以外はWindowsとMonoの微妙な実装の違いっぽい不具合ですね。

対応を入れたほうが互換性のとれた、良い実装になりそうですが、

まあ、致命的でもないんでそのままですかね。

 

あと動作確認ではLinux上でビルドしたエンジンを使いましたが、Windowsのバイナリ(.EXE)は動作可能なんだろうか?

CILイメージじゃないとか言われて動作しないんだけど。

追記:exeは以下の様なシェススクリプトを書くと回避できた。

 #!/bin/sh

./gpsfish.exe

 

 

わざわざLinuxでShogiGUIを使いたいという人たちは、エンジンはどうするんだろうか?自前でgpsfishをビルドするの?それともWindowsのバイナリが動作するるんだろうか?

ちょっと謎ですね。