ホーム>エンジン作成時の注意点
以下に、エンジン作成時に注意する点をいくつか記述します。(なお、エンジンはC++で作ると想定しています。JavaやC#で作った場合のことは調べていないのでわかりません。)
思考エンジンを作成する時は、USIプロトコルに基づいたコンソールアプリケーションとして作成することになりますが、文字の出力で注意することがあります。
エンジンがGUIに対して何か出力をする時は、普通にprintf()などで標準出力に文字を書くことになります。しかし、標準出力はデフォルトだとバッファリングされているため、その状態のままだと、プロセス間通信の時はバッファがあふれるまでGUI側に何も送られないことになってしまいます。
そのため、エンジンは標準出力がバッファリングされないようにしておかなければいけません。エンジンの起動直後に次の行を呼んでおくのが簡単です。
setvbuf(stdout, NULL, _IONBF, 0);
なお、以前の説明では「念のため、標準入力もバッファリングしないようにしておきます」と書いていましたが、Visual Studio 2015ではそれをやると正常に動作しなくなるようなので、標準入力に関しては何も変更しないで下さい。
将棋所2.8.0より前のバージョンでは、エンジン設定ダイアログを出す時にエンジンがoptionをコマンドを返したあと、setoptionコマンドでエンジンに送る設定内容はエンジン側で保存しておく必要がありましたが、将棋所2.8.0からは将棋所の側で設定内容を保存しておいて、対局開始時にその内容をsetoptionコマンドで送るようになりました。そのため、エンジン側で設定内容を初期設定ファイルなどに保存しておく必要はなくなりました。
エンジンの開発時、USIプロトコルとは別に、デバッグウィンドウに任意の文字列を表示したいということがあるかもしれません。この時、エンジンが日本語を送ると将棋所が異常終了することがあります。例えば、
printf("日本語\n");
のように、単純に日本語を送ると高い確率で将棋所が落ちます。これは、将棋所で文字列を受信する前に落ちてしまうので、将棋所の側で回避する方法はありません。(厳密に言うと、落ちることを回避する方法はありますが、その方法を使うと肝心の日本語が文字化けするので使えません。)この問題を回避するためには、
printf("debug 日本語\n");
のように、半角英数字に続けて日本語を書くか、あるいは
printf("\n");
printf("日本語\n");
のように、日本語の前に一度改行を入れるといいようです。
ただし、USIプロトコルというのは本来、半角英数字を使ってコマンドをやり取りするものなので、プロトコルと関係ない文字列についても、日本語を使うことを推奨するわけではありません。
Visual C++でコンパイルしたコンソールアプリケーションを他のPCにコピーして動かそうとすると、「このアプリケーションの構成が正しくないため、アプリケーションを開始できませんでした。アプリケーションを再度インストールすることにより問題が解決する場合があります。」という警告が出て起動しないことがあります。
これは、Visual C++の開発環境がインストールされていないPCには、そのアプリケーションが必要とするDLLファイルが含まれていないことが原因です。この現象を防ぐためには、Visual C++の「プロジェクト」メニューからプロジェクトのプロパティを開き、「構成」プロパティの「C/C++」→「コード生成」で、「ランタイムライブラリ」の欄を変更して下さい。デバッグ用の構成なら「マルチスレッド デバッグ DLL (/MDd)」を「マルチスレッド デバッグ (/MTd)」に、リリース用の構成なら「マルチスレッド DLL (/MD)」を「マルチスレッド (/MT)」に変更します。こうすることによって、必要なDLLファイルがコンパイル時にリンクされるので、上記の現象を防ぐことができます。
将棋所3.0.0から、時間切れで負けになる機能を追加しました。消費時間の計測は、原則として端数は切り捨てるので、例えば9.9秒で手を指した場合は9秒と記録されます。(ただし、1手につき最低でも1秒は消費することになっているので、0.1秒で指しても1秒と記録されますが、その場合は除外します。)
そのため、秒読みを10秒にして切れ負けの設定にした場合、秒読みが10秒をカウントした瞬間に負けになるので、持ち時間を使い切ったあとは1手の消費時間が10秒以上になることはあり得ないはずですが、gpsfishなどのエンジンで対局を行うと、その設定でも1手の消費時間が10秒になることがあります。
これは、タイマーの動作時間が正確ではないことが原因です。秒読みのタイマーは1秒間隔に設定していますが、実際に計測すると、それよりわずかに長く1.01秒で動作したりすることがあります。この場合、タイマーが10回動作するためには10.1秒が必要になりますが、エンジンが消費時間10.03秒で手を指すと、タイマーはまだ10回動作していないので切れ負けにはならず、その指し手の消費時間は10.03秒の端数を切り捨てて10秒と記録されることになってしまいます。
エンジンが手を指した時の消費時間を調べて、それが秒読みの制限時間に達していたら負けというようにすればこの問題は回避できますが、時間に関する非常に微妙な問題であることや、通信対局の場合など相手側の時間計測と異なる可能性もあるので、そこまで厳格に対応しても意味がないと思い、そのような処理はしていません。エンジンの思考中は、設定の時間ぎりぎりまで使わず、少し時間に余裕を持たせて指し手を返すように心掛けて下さい。
ホーム>エンジン作成時の注意点