そういえば容量上限で Qiita に画像を貼れないのだった。画像を使わず 文字だけで書く。
GitHub より古い記事なんで ソースを見ながら読み替えろだぜ☆(^~^)
https://github.com/muzudho/cgfgoban109_for_kifuwarabe2018
コンピューター囲碁プログラムの作り方が欲しければ きふわらべのソース でも読んでろだぜ、ルール通り指せてないけどな☆(^~^)
https://github.com/muzudho/rust_kifuwarabe_igo2018
で。
ここから cgfgoban109.zip をダウンロードして解答する。
cgfgoban/
|
+-- cgf.h 他いっぱい
cgfthink
|
+-- cgfthink.c
+-- cgfthink.dsp 他いくつか
cgfgoban.exe
cgfgoban.txt
cgfthink.dll
cgfthink.txt
stone.wav
といったファイル構成をしている。見ての通り 生C だぜ☆
ソースを改造する前に。
コンピューター囲碁を実行するために必要なのは以下の ファイル構成だぜ。
cgfgoban.exe
cgfthink.dll
stone.wav
cgfgoban.exe
をダブルクリックすれば コンピューター囲碁ソフトが起動する☆(^~^)
cgfthink.dll
が コンピューターの思考部分なので、選手はこれを改造すればいいわけだぜ☆(^~^)
stone.wav
は石打った音☆(^~^)
ソースを改造する。
Visual Studio 2017 Installer を使って Visual Studio 2017 に
Windows デスクトップ C++
の構成をインストールしておく。
そして ファイルエクスプローラーから cgfthink/cgfthink.dsp
ファイルをダブルクリックする。
バージョンの自動アップデートを行ってくれるので、任せる。
すると 警告1つ、エラー1つが出る。
警告 D9035 オプション 'Gm' の使用は現在推奨されていません。今後のバージョンからは削除されます。
エラー D8016 コマンド ライン オプション '/ZI' と '/Gy-' は同時に指定できません
じゃあ 右側ぐらいに表示されている [ソリューション エクスプローラー] を見てみよう。
ソリューション エクスプローラー
ソリューション 'cgfthink' (1 プロジェクト)
|
+-- cgfthink
|
+-- 参照
|
+-- 外部依存関係
|
+-- Header Files
|
+-- Resource Files
|
+-- Source Files
|
+-- cgfthink.c
[Header Files] を右クリックして [追加] - [既存の項目] から cgfthink.h
を選択して 追加しておいてくれだぜ。
ツリー構造のルートが ソリューション
、 2層目が プロジェクト
と呼ばれる。
cgfthink
プロジェクトを右クリックしてくれだぜ。 [プロパティ]をクリック。
cgfthink プロパティー ページ
というダイアログボックスが開いたかと思う。
警告とエラーの2つがあるが、まず最初に重要度の高い エラー の方から解決する。
'/ZI' と '/Gy-'
というのは コマンドライン オプションだ。
コマンドライン オプションを変えようぜ。
cgfthink プロパティー ページ の左側のツリー構造を見ろだぜ☆(^~^)
構成プロパティ
|
+-- C/C++
|
+-- コマンドライン
と、たどってクリックしろだぜ☆(^q^) 「すべてのオプション」という欄が見える。
/GS /analyze- /W3 /Gy- /Zc:wchar_t /ZI /Gm /Od /Fd".\Debug\" /Zc:inline /fp:precise /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "CGFTHINK_EXPORTS" /D "_VC80_UPGRADE=0x0600" /D "_WINDLL" /D "_MBCS" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /Oy- /MTd /FC /Fa".\Debug\" /EHsc /nologo /Fo".\Debug\" /Fp".\Debug\cgfthink.pch" /diagnostics:classic
確かに '/ZI' と '/Gy-'
の両方を含んでいる☆
こいつらが何者かを調べたかったら、同じく cgfthink プロパティー ページ の左側のツリー構造を見ろだぜ☆(^~^)
構成プロパティ
|
+-- C/C++
|
+-- すべてのオプション
と、たどってクリックしろだぜ☆(^q^) リストが見える。 '/ZI' と '/Gy-'
を探すと、
デバッグ情報の形式 エディット コンティニューのプログラム データベース(/ZI)
コンパイラにより生成されるデバッグ情報の種類を指定します。これにより互換性のある
リンカー設定が必要になります。(/Z7, /Zd, /Zi, /ZI)
関数レベルでリンクする いいえ(/Gy-)
コンパイラで、パッケージ化された関数の形式(COMDATs)により個別の関数を
パッケージ化することを許可します。エディット コンティニュで必要です。(/Gy)
の2つと分かる☆(^~^) 説明を読むと /Gy
を いいえの /Gy-
にしてるのが悪いんじゃないのか☆(^q^)?
なんでこんな初期設定なのか、うざ……☆(^~^) /Gy
に変更してダイアログボックスの[OK]ボタンをクリック。
Visual Studio 2017 のトップメニューの [ビルド] - [ソリューションのビルド] をクリック。
はい、エラーは消えた☆(^~^)
で、改造したいのは cgfthink.c ファイルの中の cgfgui_thinking 関数だぜ☆(^~^)
これが何かは 前に書いたので、わたしのブログ記事をググって読めばいいだろう☆(^~^)
ここで ググるキーワードが思い浮かばない人用に 検索キーワードの例を教えておこう。次の単語でググれだぜ☆(^~^)
cgfgui_thinking ☆(^~^)
というかググって気づいたんだが 前にも同じ記事を書いている。
もうブログ書くの飽きた。
なんにしろ ビルドして cgfthink.dll を書きだすことはできるんだろ、あれ……、なんで x86 なのか……☆
64bit 版対応したいぜ☆(^~^)
Debugモードではなく、Releaseモードでビルドすることが重要だぜ☆(^~^)
x86 を x64 に変更しようぜ☆(^~^)?
cgfgoban109
|
+-- cgfgoban
|
+-- cgfgoban.dsp
cgfgoban.dsp
をダブルクリックしろだぜ☆(^~^)
Visual Studio 2017 にバージョンアップしろだぜ☆(^~^)
Release x64
でビルドしたいが、初期設定で x64 がないので、
その[ソリューション プラットフォーム]欄の右側の▼ボタンをクリックして [構成マネージャー...] をクリック。
構成マネージャー
ダイアログボックスが出てくるので
[アクティブ ソリューション プラットフォーム]欄の x86 を[<新規作成...>]に変更。
[新しいプラットフォームを入力または選択してください]のリストから [x64] を選んで [OK]、[閉じる]。
とりあえず [ビルド] - [ソリューションのビルド] でビルドは通るので良しとする。
cgfgoban109
|
+-- cgfgoban
|
+-- Release
|
+-- cgfgoban.exe
この cgfgoban.exe
が 64ビット版になってるので、これを使っていく。
cgfthink の方も同様に x64 にしろだぜ☆(^~^) バージョンを合わせないと .dll ファイルを開けられない☆(^~^)
改造するポイントを挙げると、
// 思考ルーチン。次の1手を返す。
// 本体から初期盤面、棋譜、手数、手番、盤のサイズ、コミ、が入った状態で呼ばれる。
DLL_EXPORT int cgfgui_thinking(
int dll_init_board[], // 初期盤面
int dll_kifu[][3], // 棋譜 [][0]...座標、[][1]...石の色、[][2]...消費時間(秒)
int dll_tesuu, // 手数
int dll_black_turn, // 手番(黒番...1、白番...0)
int dll_board_size, // 盤面のサイズ
double dll_komi, // コミ
int dll_endgame_type, // 0...通常の思考、1...終局処理、2...図形を表示、3...数値を表示。
int dll_endgame_board[] // 終局処理の結果を代入する。
)
{
int z, col, t, i, xxyy, ret_z;
// 現在局面を棋譜と初期盤面から作る
for (i = 0; i < BOARD_MAX; i++) board[i] = dll_init_board[i]; // 初期盤面をコピー
board_size = dll_board_size;
hama[0] = hama[1] = 0;
sg_time[0] = sg_time[1] = 0; // 累計思考時間を初期化
kou_z = 0;
for (i = 0; i < dll_tesuu; i++) {
z = dll_kifu[i][0]; // 座標、y*256 + x の形で入っている
col = dll_kifu[i][1]; // 石の色
t = dll_kifu[i][2]; // 消費時間
sg_time[i & 1] += t;
if (move_one(z, col) != MOVE_SUCCESS) break;
}
# if 0 // 中断処理を入れる場合のサンプル。0を1にすればコンパイルされます。
for (i = 0; i < 300; i++) { // 300*10ms = 3000ms = 3秒待ちます。
PassWindowsSystem(); // 一時的にWindowsに制御を渡します。
if (*pThinkStop != 0) break; // 中断ボタンが押された場合。
Sleep(10); // 10ms(0.01秒)停止。
}
# endif
// 終局処理、図形、数値を表示する場合
if (dll_endgame_type == GAME_END_STATUS) return endgame_status(dll_endgame_board);
if (dll_endgame_type == GAME_DRAW_FIGURE) return endgame_draw_figure(dll_endgame_board);
if (dll_endgame_type == GAME_DRAW_NUMBER) return endgame_draw_number(dll_endgame_board);
// サンプルの思考ルーチンを呼ぶ
if (dll_black_turn) col = BLACK;
else col = WHITE;
// (2017-03-17 Add begin)
// ファイル書き出し
{
FILE* fp;
char* fname = "out.txt";
//fp = fopen(fname, "w");
//if (fp == NULL) {
if (0 != fopen_s(&fp, fname, "w")) {
printf("%s file can not open.\n", fname);
return -1;
}
// 現局面を出力
for (int y = 0; y < 21; y++)
{
for (int x = 0; x < 21; x++)
{
int i = y * 256 + x;
char buffer[20];
itoa(board[i], buffer, 10);
fprintf(fp, buffer);
fprintf(fp, ",");
}
fprintf(fp, "\n");
}
{
char buffer[20];
// アゲハマを出力
itoa(hama[0], buffer, 10);
fprintf(fp, buffer);
fprintf(fp, ",");
itoa(hama[1], buffer, 10);
fprintf(fp, buffer);
fprintf(fp, ",");
// 累計思考時間を出力
itoa(sg_time[0], buffer, 10);
fprintf(fp, buffer);
fprintf(fp, ",");
itoa(sg_time[1], buffer, 10);
fprintf(fp, buffer);
fprintf(fp, ",");
// コウを出力
xxyy = (kou_z % 256) * 100 + (kou_z / 256);
itoa(xxyy, buffer, 10);
fprintf(fp, buffer);
fprintf(fp, ",");
// 手番(石の色)を出力
itoa(col, buffer, 10);
fprintf(fp, buffer);
fprintf(fp, ",");
fprintf(fp, "\n");
}
fclose(fp);
}
// 着手
ret_z = -1;
// ループ
while (-1 == ret_z) {
// ファイル読込
FILE* pInFile;
char* pInFileName = "in.txt";
char buffer[100];
//pInFile = fopen(pInFileName, "r");
//if (pInFile == NULL) {
if (0 != fopen_s(&pInFile, pInFileName, "r")) {
// ファイルを読込めなかった
// 0.1 秒スリープ
Sleep(100);
}
else
{
PRT("in.txt ファイルを読込みました\n");
if (fgets(buffer, 100, pInFile) != NULL) {
xxyy = atoi(buffer);
ret_z = (xxyy % 100) * 256 + xxyy / 100;
}
fclose(pInFile);
PRT("着手の要求 %d\n", ret_z);
// ローテーション・デリート
{
// ファイル読込
FILE* pInOldFile;
char* pInOldFileName = "in_old.txt";
//pInOldFile = fopen(pInOldFileName, "r");
//if (pInOldFile != NULL) {
if (0 == fopen_s(&pInOldFile, pInOldFileName, "r")) {
PRT("in_old.txt ファイルが在る\n");
// 1つ前のファイルは削除
fclose(pInOldFile);
if (remove(pInOldFileName) == 0) {
// ファイルの削除成功
PRT("in_old.txt ファイルを削除\n");
}
else {
// ファイルの削除失敗
PRT("【▲!】(>_<)in_old.txt ファイルの削除失敗\n");
}
}
// ファイルのリネーム
if (rename("in.txt", "in_old.txt") == 0) {
// ファイルの削除成功
PRT("in.txt を in_old.txt にリネームした\n");
}
else {
// ファイルの削除失敗
PRT("【▲!】(>_<)in.txt を in_old.txt にリネーム失敗\n");
}
}
// ローテーション・デリート
{
// ファイル読込
FILE* pInOldFile;
char* pInOldFileName = "in_help_old.txt";
//pInOldFile = fopen(pInOldFileName, "r");
// if (pInOldFile != NULL) {
if (0 == fopen_s(&pInOldFile, pInOldFileName, "r")) {
PRT("in_help_old.txt ファイルが在る\n");
// 1つ前のファイルは削除
fclose(pInOldFile);
if (remove(pInOldFileName) == 0) {
// ファイルの削除成功
PRT("in_help_old.txt ファイルを削除\n");
}
else {
// ファイルの削除失敗
PRT("【▲!】(>_<)in_help_old.txt ファイルの削除失敗\n");
}
}
// ファイルのリネーム
if (rename("in_help.txt", "in_help_old.txt") == 0) {
// ファイルの削除成功
PRT("in_help.txt を in_help_old.txt にリネームした\n");
}
else {
// ファイルの削除失敗
PRT("【▲!】(>_<)in_help.txt を in_help_old.txt にリネーム失敗\n");
}
}
}
}
// (2017-03-17 Add end)
// (2017-03-17 Modify begin)
if (0 == ret_z)
{
PRT("(^◇^)パスするぐらいならランダム打ちだ☆!\n");
// PASSとダイアログが出てきて対局が止まるのを防止☆
ret_z = think_sample(col);
}
// (2017-03-17 Modify end)
PRT("思考時間:先手=%d秒、後手=%d秒\n", sg_time[0], sg_time[1]);
PRT("着手=(%2d,%2d)(%04x), 手数=%d,手番=%d,盤size=%d,komi=%.1f\n", (ret_z & 0xff), (ret_z >> 8), ret_z, dll_tesuu, dll_black_turn, dll_board_size, dll_komi);
// print_board();
return ret_z;
}
手を決める部分を、テキストファイルに書き出して、別プログラムに丸投げするということだぜ☆(^~^)
JSON にしようかと思ったが、CSV で十分な気がする☆(^~^)
上述の改造で、自分の手番のとき以下のテキストファイルが作成される。
out.txt
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
0,0,0,0,0,1,
これで十分な気がする☆(^~^)
0: 空き。
1: 黒石。
2: 白石。
3: 枠。
最終行の6つの数字は 左から
- 黒のアゲハマ。
- 白のアゲハマ。
- 黒の累計思考時間。
- 白の累計思考時間。
- コウの番地 'XXYY'書式の数。なければ 0。
- 手番の石の色。
だぜ☆(^~^)
in.txt
1918
指し手の入力もこれで x=19, y=18 ☆(^~^) 十分☆(^~^)