1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CGFGoban109を改造しようぜ☆(^~^)

Last updated at Posted at 2018-11-23

そういえば容量上限で 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 ☆(^~^) 十分☆(^~^)

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?