// Aya_pipe.cpp
// GnuGoとGTPで対戦するため
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>

#include "aya.h"
#include "aya_win.h"
#include "aya_mc.h"

//int fCrazyStone = 0;	// GTP engine にCrazyStoneを使う場合
int nMoGo = 0;
int nFuego = 0;

#define BUF_GTP_SIZE 1024
char BufGetGTP[BUF_GTP_SIZE];


void test_gnugo_loop();

HANDLE hGnuOutReadDup,hGnuInWriteDup,hGnuErrReadDup;	// 親でコピーして使うため。
HANDLE hGnuProcess;

const int FORCE_GNUGO = 1;

void open_gtp_engine_pipe(int fForceGnuGo)
{
	// 匿名パイプを作成
	SECURITY_ATTRIBUTES sa;
	sa.nLength              = sizeof(SECURITY_ATTRIBUTES);
	sa.bInheritHandle       = TRUE;
	sa.lpSecurityDescriptor = NULL;
	HANDLE hGnuOutRead, hGnuOutWrite;	// GnuGoからの出力を読み取るためのパイプ
	HANDLE hGnuInRead,  hGnuInWrite;	// Gnuへ入力を送るためのパイプ
//	HANDLE hGnuErrRead, hGnuErrWrite;	// Gnuのエラーを読み取るため
//	HANDLE hGnuOutReadDup,hGnuInWriteDup;	// 親でコピーして使うため。
	HANDLE hProcess = GetCurrentProcess();

	// GnuGoの入力(STDIN)用のパイプを作る。
	CreatePipe(&hGnuInRead, &hGnuInWrite, &sa, 0);
	DuplicateHandle(hProcess, hGnuInWrite, hProcess, &hGnuInWriteDup, 0, FALSE,	DUPLICATE_SAME_ACCESS);
	CloseHandle(hGnuInWrite);

	// GnuGoの出力(STDOUT)用のパイプを作る。
	CreatePipe(&hGnuOutRead, &hGnuOutWrite, &sa, 0);
	// ハンドルのコピーを作って、親ではこちらを利用する。子に継承されないように。
	DuplicateHandle(hProcess, hGnuOutRead, hProcess, &hGnuOutReadDup, 0, FALSE,	DUPLICATE_SAME_ACCESS);
	CloseHandle(hGnuOutRead);	// 以降はコピーを使うので閉じる。
#if 0 
	// GnuGoのエラー(STDERR)用のパイプを作る。
	CreatePipe(&hGnuErrRead, &hGnuErrWrite, &sa, 0);
	DuplicateHandle(hProcess, hGnuErrRead, hProcess, &hGnuErrReadDup, 0, FALSE, DUPLICATE_SAME_ACCESS);
	CloseHandle(hGnuErrRead);
#endif

	// プロセスに渡す情報の準備
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	ZeroMemory(&si, sizeof(STARTUPINFO));
	si.cb          = sizeof(STARTUPINFO);
	si.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
	si.wShowWindow = SW_HIDE;	// | SW_SHOWDEFAULT;
	if ( nMoGo || nFuego ) si.wShowWindow = SW_HIDE;	// コマンドプロンプトの画面を隠す場合。
	si.hStdInput   = hGnuInRead;	// 入力はこのハンドルに送られる。パイプで繋がってるのでhGnuInWriteDupから命令を送る。
	si.hStdOutput  = hGnuOutWrite;	// 出力はこのハンドルに送られる。パイプで繋がってるのでhGnuOutReadDupで読み取れる。
//	si.hStdError   = hGnuOutWrite;	// hGnuErrWrite;	// エラー出力も送る場合。Goguiではエラーを思考ログで利用しているのでwriteで読み取ると誤判断する。

	// GTP Engine を起動
	char engine_dir[TMP_BUF_LEN];
//	strcpy(engine_dir,"c:\\go\\gnugo\\gnugo.exe --mode gtp --never-resign");
	strcpy(engine_dir,"c:\\go\\gnugo\\gnugo.exe --mode gtp --never-resign --level 0");
//	strcpy(engine_dir,"c:\\go\\kiseki\\kiseki19bot.exe");
//	strcpy(engine_dir,"c:\\go\\gnugo\\gnugo38.exe --mode gtp --never-resign --monte-carlo");
//	strcpy(engine_dir,"c:\\go\\mogo\\mogo.exe --9 --time 3");
//	strcpy(engine_dir,"c:\\go\\mogo\\mogo.exe --19 --time 3");
//	strcpy(engine_dir,"c:\\aya\\vs\\699d\\699d3000po.exe --mode gtp");	// 自己対戦で強くなったかを確認
//	strcpy(engine_dir,"c:\\aya\\vs\\self_nobook\\aya.exe --mode gtp");
//	strcpy(engine_dir,"c:\\prg\\dai\\release\\dai.exe --mode gtp");	// 傀儡師を動かすテスト
//	strcpy(engine_dir,"c:\\go\\gnugo\\gnugo.exe --mode gtp --never-resign --level 16");
	if ( nFuego      ) strcpy(engine_dir,"c:\\go\\fuego\\fuego.exe");
//	if ( fCrazyStone ) strcpy(engine_dir,"c:\\go\\CrazyStone\\CrazyStone-0005.exe");
//	if ( nMoGo ==  9 ) strcpy(engine_dir,"c:\\go\\mogo\\mogo.exe --9  --time 1 --dontDisplay 1");
	if ( nMoGo ==  9 ) strcpy(engine_dir,"c:\\go\\mogo\\mogo.exe --9  --nbTotalSimulations 10000 --dontDisplay 1");
//	if ( nMoGo == 19 ) strcpy(engine_dir,"c:\\go\\mogo\\mogo.exe --19 --totalTime 300 --dontDisplay 1");
	if ( nMoGo == 19 ) strcpy(engine_dir,"c:\\go\\mogo\\mogo.exe --19 --nbTotalSimulations 10000 --dontDisplay 1");
//	"--pondering 1","--useOpeningDatabase 0","--playsAgainstHuman 0","--dontDisplay 1" ... Errorとして思考ログを吐き出す
//  "mogo --13 --nbTotalSimulations 128 --playsAgainstHuman 0"
	if ( fForceGnuGo ) strcpy(engine_dir,"c:\\go\\gnugo\\gnugo.exe --mode gtp --never-resign --chinese-rules");
	CreateProcess(NULL, engine_dir, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
	hGnuProcess = pi.hProcess;

	// GnuGoを起動
//	"c:\\go\\gnugo\\gnugo.exe --mode gtp --clock 2400 --autolevel"
//	"c:\\go\\gnugo\\gnugo.exe --mode gtp --never-resign --clock 2400 --autolevel"
//	"c:\\go\\gnugo\\gnugo.exe --mode gtp --never-resign --clock 2400 --level 12 --autolevel"
//	"c:\\go\\gnugo\\gnugo.exe --mode gtp --never-resign --clock 300  --level 12 --autolevel"
	// 通常
//	"c:\\go\\indigo\\idgconsole2006.exe -gtp"
//	"c:\\aya\\release\\aya.exe --mode gtp"

	// もう使わないハンドルを閉じる。
	CloseHandle(pi.hThread);
	CloseHandle(hGnuInRead);
	CloseHandle(hGnuOutWrite);
//	CloseHandle(hGnuErrWrite);
	Sleep(200);	// 安全のために待つ。
//	WaitForInputIdle(pi.hProcess, INFINITE);	// GnuGoが入力を受け取れるようになるまで待つ ---> AyaをAyaから呼ぶと固まる。
}

void close_gtp_engine_pipe()
{
    // 子プロセスのハンドルを閉じる。
	CloseHandle(hGnuProcess);
	// GnuGoとの通信で使っていたハンドルを閉じる。
	CloseHandle(hGnuInWriteDup);
	CloseHandle(hGnuOutReadDup);
	CloseHandle(hGnuErrReadDup);
}

// パイプを作成
void init_gnugo_pipe(int fTestLoop)
{
	SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS );	// 優先順位を「低」で

	open_gtp_engine_pipe(0);

	if ( fTestLoop == 0 ) return;
	test_gnugo_loop();

	close_gtp_engine_pipe();
}


// パイプからGnuGoの出力を読み出す
int ReadGTP() {
	int n = 0;
	char szOut[2],c;
	DWORD dwRead;
  	char buf[BUF_GTP_SIZE];

	for (;;) {
		if ( ReadFile(hGnuOutReadDup, szOut, 1, &dwRead, NULL) == 0 ) {
			PRT("ReadFile Err\n");
			return 0;
		}
		if ( dwRead == 0 ) continue;
		c = szOut[0];
		if ( c != '\n' && c < 32 ) continue;	// '\r' とかは無視
		if ( c == 9 ) c = 32;	// TABをSPACEに変換
		buf[n]=c;
		n++;
		if ( n >= BUF_GTP_SIZE ) { PRT("Over!\n"); debug(); }
//		PRT("%c(%x)",c,c);
		if ( c == '\n' ) break;	// コマンドが終了した
	}

	buf[n] = 0;
	PRT("ReadGTP %s",buf);
	if ( buf[0] == '#') return 1;	// コメントは無視
	if ( buf[0] == '\n' ) return 1;	// 改行だけの行は区切り。
	strcpy(BufGetGTP,buf);
	return 2;
}

// コマンドを受信する。手の受信も兼ねる。Errorの場合は0を返す。
int ReadCommmand(int *z)
{
	int x=0,y=0,fRet;
	char move[BUF_GTP_SIZE];

	*z = 0;
	
	for (;;) {
		fRet = ReadGTP();	// パイプからGnuGoの出力を読み出す
		if ( fRet == 0 ) return 0;
		if ( fRet == 1 ) break;	// 正常終了
//		if ( BufGetGTP[0] == '\n' ) break;	// 完全終了
		if ( BufGetGTP[0] == '=' && strlen_int(BufGetGTP) > 2 ) {
			memset(move,0,sizeof(move));
			sscanf(BufGetGTP+2,"%s",move);	// id は無視
			PRT("move=%s, ",move);
			if      ( _stricmp(move,"pass")   ==0 ) *z = 0;	// GnuGoは"PASS"と送ってくる。小文字では？
			else if ( _stricmp(move,"resign") ==0 ) *z = -1;
			else {			   // "Q4" なら
				x = move[0];
				if ( x > 'I' ) x--;	// 'I'は存在しないので
				x =	x - 'A' + 1;
				y = atoi(move+1);
			*z = (ban_size+1-y)*256 + x;
			}
			PRT("(x=%d,y=%d,z=%04x)\n",x,y,*z);
		}
	}
	return 1;
}


void SendGTP(char *str)
{
	DWORD dwRead;
	WriteFile(hGnuInWriteDup, str, strlen_int(str), &dwRead, NULL);
	PRT("SendGTP %s",str);
}

int SendGTPWaitOK(char *str)
{
	SendGTP(str);
	int z;
	if ( ReadCommmand(&z) == 0 ) return 0;	// パイプから結果を読み出す
	return 1;
}

// パイプからGnuGoのエラー出力を読み出す
int ReadGnuGoErr() {
	char szOut[BUF_GTP_SIZE];
	DWORD dwRead;

	// 同様にエラー出力の処理
	PeekNamedPipe(hGnuErrReadDup, NULL, 0, NULL, &dwRead, NULL);
	if (dwRead > 0) {
		if ( ReadFile(hGnuErrReadDup, szOut, BUF_GTP_SIZE-1, &dwRead, NULL) == 0 ) {
			PRT("ReadFile GnuGoErr Err\n");
			return FALSE;
		}
		szOut[dwRead] = 0;
		PRT("GnuGoStdErr->%s",szOut);
	}
	return TRUE;
}



extern int ContinuousNum;	// 対戦数（２以上の時、連続で対戦する。棋譜も残す）
extern int fAutoSave;		// 棋譜の自動セーブ（通信対戦時）


// 座標を'K15'のような文字列に変換
void change_z_str(char *str, int z)
{
	int x,y;

	if ( z == 0 ) sprintf(str,"pass");
	else if ( z < 0 ) sprintf(str,"resign");
	else {
		x =  z & 0x00ff;
		y = (z & 0xff00) >> 8;
		x = 'A' + x - 1;
		if ( x >= 'I' ) x++;
		y = ban_size - y + 1;
		sprintf(str,"%c%d",x,y);
	}
}

// GnuGoの初期化
int init_gnugo_gtp()
{
	char str[TMP_BUF_LEN];
	char sName[TMP_BUF_LEN];
	const int NAME_STR_MAX = 10;

	sprintf(str,"boardsize %d\n",ban_size);
	if ( SendGTPWaitOK(str) == 0 ) return 0;	// 19路盤にしろ、というコマンド
	if ( SendGTPWaitOK("clear_board\n") == 0 ) return 0;	// 盤面を初期化しろ、というコマンド
	if ( SendGTPWaitOK("name\n") == 0 ) return 0;
	PRT("Name=%s\n",BufGetGTP+2);
	if ( strlen_int(BufGetGTP+2) > NAME_STR_MAX ) *(BufGetGTP+2 + NAME_STR_MAX) = 0;
	strcpy(sName,BufGetGTP+2);
	int n = strlen_int(sName);
	if ( n > 0 && sName[n-1] < ' ' ) sName[n-1] = 0;
	if ( SendGTPWaitOK("version\n") == 0 ) return 0;
	PRT("Version=%s\n",BufGetGTP+2);
	if ( strlen_int(BufGetGTP+2) > NAME_STR_MAX ) *(BufGetGTP+2 + NAME_STR_MAX) = 0;
	strcat(sName," ");
	strcat(sName,BufGetGTP+2);
	n = strlen_int(sName);
	if ( n > 0 && sName[n-1] < ' ' ) sName[n-1] = 0;
	strcpy(sPlayerName[UN_COL(aya)-1],sName);

	sprintf(str,"komi %.1f\n",komi);
	if ( SendGTPWaitOK(str) == 0 ) return 0;
/*
	if ( fCrazyStone ) {
		SendGTP("time_settings 300\n");	// crazy stone 用、時間設定。でないと1手60秒考えるので。
		if ( ReadCommmand(&z) == 0 ) return 0;
		SendGTP("crazy-analyst MCSearch param Processors 2\n");	// 2CPU
		if ( ReadCommmand(&z) == 0 ) return 0;
	}
*/
	if ( nFuego      ) {
//		SendGTP("uct_param_player ponder 1\n");
//		SendGTP("uct_param_player reuse_subtree 1\n");
		SendGTPWaitOK("uct_param_player max_games 10000\n");	// 正しく機能
//		SendGTP("uct_param_search max_nodes 10000\n");			// どうもメモリを制限している感じ。不適切。
//		SendGTP("uct_param_search number_playouts 10000\n");	// これは機能しないようだ
//		SendGTP("go_param timelimit 3\n");						// 正しく機能
//		SendGTPWaitOK("uct_max_memory 134217728\n");			// 128MBまでしか確保させない場合。9路で10000playoutで730000nodeを使う。1 node=144byteなので105120012(=100MB)は必要。
		SendGTPWaitOK("uct_param_search number_threads 1\n");
	}

	return 1;
}


const int gnugo_random_game_num = 1000;
static char (*ban_random)[BANMAX] = NULL;	//static char ban_random[gnugo_random_game_num+1][BANMAX];


// *p がオリジナルの盤面、*q に90度回転させた位置に石を置く
void right_turn_onestone(int x,int y,char *p,char *q)
{
	int xx,yy;
	right_turn_xy(x, y, &xx,&yy,1);
	char k = *(p + y*256+ x);
	*(q + yy*256 + xx) = k;
}

// 鏡像
void mirror_turn_onestone(int x,int y,char *p, char *q)
{
	char k;
	k = *(p + y*256+ x);
	x = (ban_size+1) - x;
	*(q + y*256 + x) = k;
}


int is_same_position_rotate_mirror(int n)
{
	int i,j,k,kk,x,y,z,flag;
	if ( n==0 ) return 0;
	static int count = 0;
	for (i=0;i<8;i++) {
		for (j=0;j<n;j++) {
			flag = 0;
			for (y=0;y<ban_size;y++) for (x=0;x<ban_size;x++) {
				z = ((y+1)<<8) + x+1;
				k  = ban_random[n][z];	// 打つ順番が入っている
				kk = ban_random[j][z];
				if ( k!=0 ) {
					if ( k&1 ) k = 1;
					else k = 2;
				}
				if ( kk!=0 ) {
					if ( kk&1 ) kk = 1;
					else kk = 2;
				}
				if ( k != kk ) flag = 1;
				if ( flag ) break;
			}
			if ( flag == 0 ) {
				PRT("同型 n=%3d, 同じ=%3d,回転=%d,count=%d\n",n,j,i,count++);
				return 1;	// 同じ初形
			}
		}
/*		
		PRT("n=%3d,i=%d\n",n,i);
		for (y=0;y<ban_size;y++) for (x=0;x<ban_size;x++) {
			z = ((y+1)<<8) + x+1;	PRT("%d ",ban_random[n][z]); if ( x==ban_size-1 ) PRT("\n");
		}
*/
		for (y=0;y<ban_size;y++) for (x=0;x<ban_size;x++) {
			right_turn_onestone(x+1,y+1, ban_random[n],ban_random[n+1]);
		}
		for (j=0;j<BANMAX;j++) ban_random[n][j] = ban_random[n+1][j];
		if ( i==3 ) {
			for (y=0;y<ban_size;y++) for (x=0;x<ban_size;x++) {
				mirror_turn_onestone(x+1,y+1, ban_random[n],ban_random[n+1]);
			}
			for (j=0;j<BANMAX;j++) ban_random[n][j] = ban_random[n+1][j];
		}
	}
	return 0;	// 同型なし
}
	
// 9路盤ではあまりにも同型が頻出するので、序盤の数手を完全に独立にする。
// 鏡像、対称を含めて、初手は6通り。初手天元に対しては、5通り。
// 4手まで中央にランダムに打つ。そして先後を入れ替えて打つ。平等。
// 石が並ぶ形は打たない。
void init_9ro_random_opening(void)
{
	int i,j,n,x,y,z;
	char *p;
	
	ban_random = (char(*)[BANMAX])malloc((gnugo_random_game_num+1)*BANMAX);
	if ( ban_random == NULL ) debug();

	for (i=0;i<gnugo_random_game_num;i++) {
		p = ban_random[i];
		for (j=0;j<BANMAX;j++) *(p+j) = 0;
		n = 0;
		for (;;) {
			if ( ban_size == BAN_19 ) {
				x = rand_yss() % 3;
				y = rand_yss() % 3;
				if ( n==0 ) { x += 3; y += 3; }
				if ( n==1 ) { x +=15; y += 3; }
				if ( n==2 ) { x +=15; y +=15; }
				if ( n==3 ) { x += 3; y +=15; }
			}
			if ( ban_size == BAN_9 ) {
				x = (rand_yss() % (ban_size-4))+3;	// 9路で %5 0..4 -> 3..7
				y = (rand_yss() % (ban_size-4))+3;
			}
			char *pp = p + y*256 + x;
			if ( *pp != 0 ) continue;
			for (j=0;j<4;j++) {
				if ( *(pp+dir4[j]) ) break; 
			}
			if ( j!=4 ) continue;
			n++;
			*pp = (char)n;
			if ( n==4 ) break; 
		}

		// 対象形、鏡像を調べる
		// 右に一回、回転
		if ( is_same_position_rotate_mirror(i) ) {
			i--;
			continue;
		}
	}
	for (i=0;i<2;i++) {
		PRT("i=%d\n",i);
		for (y=0;y<ban_size;y++) for (x=0;x<ban_size;x++) {
			z = ((y+1)<<8) + x+1;
			PRT("%d ",ban_random[i][z]);
			if ( x==ban_size-1 ) PRT("\n");
		}
	}
}

// 序盤の4手を乱数で
void move_9ro_random_opening(int n)
{
	int i,z;
	char *p = ban_random[n];
	char str[BUF_GTP_SIZE],str2[BUF_GTP_SIZE];
	for (i=0;i<4;i++) {
		for (z=0;z<BANMAX;z++) if ( *(p+z) == i+1 ) break;
		if ( z==BANMAX ) { PRT("move_9ro n=%d\n",n); debug(); }
		PRT("i=%d,z=%04x\n",i,z);
		change_z_str(str, z);
		if ( tesuu & 1 ) sprintf(str2,"play white %s\n",str);
		else             sprintf(str2,"play black %s\n",str);

		if ( SendGTPWaitOK(str2) == 0 ) break;	// OKの'='を受信
		set_kifu_time(z,(tesuu&1)+1,0);
	}
}

int test_random()
{
	int z=0,i;
	for (i=0;i<300;i++) {
		int x = (rand_yss() % 17) + 2;	// 2-18。盤の端には打たない。
		int y = (rand_yss() % 17) + 2;	
		z = y * 256 + x;
		if ( ban[z] != 0 ) continue;
		if ( ban[z+1] && ban[z-1] && ban[z+0x100] && ban[z-0x100] ) continue;
		break;
	}
	if ( i==300 ) z = 0;
	return z;
}

double dGnuGoFinalScore;

// GnuGoを利用して結果を返す。900poだと300局で9局ほどErrになるようだ。--> GnuGoも間違いまくる・・・。300局で6局。
double getGnuGoFinalScoreGTP()
{
	close_gtp_engine_pipe();
	open_gtp_engine_pipe(FORCE_GNUGO);
	init_gnugo_gtp();

	char str[BUF_GTP_SIZE],str2[BUF_GTP_SIZE];
	int i;
	for (i=0;i<tesuu;i++) {
		int z = kifu[i+1][0];
		change_z_str(str, z);
		if ( i & 1 ) sprintf(str2,"play white %s\n",str);
		else         sprintf(str2,"play black %s\n",str);
		if ( SendGTPWaitOK(str2) == 0 ) { PRT("FinalScore play Err\n"); debug(); }
	}

	if ( SendGTPWaitOK("final_score\n") == 0 ) { PRT("FinalScore Err\n"); return 0; }

	double result = 0;
	char c = *(BufGetGTP+2);	// "W+1.5", "B+24.5", "0" (持碁の場合)
	if ( c=='W' || c=='B' ) result = atof(BufGetGTP+3);
	if ( c=='W' ) result = -result;
	PRT("result=%.1f\n",result);
	close_gtp_engine_pipe();
	open_gtp_engine_pipe(0);
	return result;
}

void test_self_loop()
{
	ContinuousNum = gnugo_random_game_num;
	fAutoSave = 1;		// 棋譜の自動セーブ（通信対戦時）

	for (;;) {
		int i,fRet = 0;
		double gct;
		view_value = 0.500;
		for (i=0;i<10;i++) {
			int z,z0[4] = {0x0404,0x0505,0x0403,0x0504};	// 初手は4通りから選ぶ(天元に打ちたがるので)
#if 1
			if ( i==0 ) { z = z0[ContinuousNum%4]; gct = 0; }
			else z = ThinkNegaMaxWithoutCol(&gct);
//			z = ThinkNegaMaxWithoutCol(&gct);
#else
			z = 0;	// 一方に定石を使うテスト
			if ( ((ContinuousNum +i) & 1)==0 ) { z = local[0].search_selfplay_book_9x9(); gct = 0; }	// 1局目は黒が定石利用
			if ( z == 0 ) z = ThinkNegaMaxWithoutCol(&gct);
#endif
			sprintf(sComment[i],"%5.3f",view_value);
			if ( thinking_utikiri_flag == TUF_USER_INTERRUPT ) return;
			fRet = set_kifu_time(z,1+(i&1),(int)gct);
			if ( fRet ) break;
//			if ( view_value	> 0.530 || view_value < 0.470 ) break;	// 差が付きすぎた
		}
//		if ( fRet < 2 ) break;
		ContinuousPlay(1);	// 連続通信対戦の終局処理
		if ( ContinuousNum <= 0 ) break;
	}
}

// council system, counsel、合議制
int MajorityThinking(double *pct)
{
	int col = TebanColor();	// color only depends on moves and hadicaps.
	const int nCouncil = 2;		// 通常は3だが2でもOK?
	double result[nCouncil][3];
	int i,j;
	double ct1 = get_clock();
	for (i=0;i<nCouncil;i++) {
		double dct;
		int z = ThinkNegaMaxWithoutCol(&dct);
		double v = view_value;
		if ( col == 2 ) v = -v;
		result[i][0] = z;
		result[i][1] = v;
		result[i][2] = 0;

		for (j=0;j<i;j++) {
			if ( result[j][0] == z ) { result[j][2] += 1; break; }
		}
		if ( i < nCouncil-1 ) set_latest_komi_before();	// コミ調整はなかったことにする(最後の1回以外)
	}
	*pct = get_spend_time(ct1);

	double max_v = -100;
	int max_n = -1;
	int best_n = -1;
	int best_count = 0;
	for (i=0;i<nCouncil;i++) {
		PRT("%d: z=%04x, v=%.5f, count=%.0f\n",i,(int)result[i][0],result[i][1],result[i][2]);
		double v = result[i][1];
		if ( v > max_v ) { max_v = v; max_n = i; }
		int count  = (int)result[i][2];
		if ( count > best_count ) { best_count = count; best_n = i; }
	}
	int z = (int)result[max_n][0];
	if ( best_n >= 0 ) z = (int)result[best_n][0];
	PRT("final z=%04x, ",z);
	static int	sum_council[nCouncil];
	sum_council[best_count]++;
	int sum = 0;
	for (i=0;i<nCouncil;i++) {
		sum += sum_council[i];
		PRT("[%d]=%d,",i,sum_council[i]);
	}
	PRT("all=%d\n",sum);
	return z;
}

int nGnuGoHandi = 4;

void test_gnugo_loop()
{
	int fRet = 0;
	int z;
	double gct,ct1;
	char str[BUF_GTP_SIZE],str2[BUF_GTP_SIZE];

	if ( ban_size==BAN_9 ) init_9ro_random_opening();

	ContinuousNum = gnugo_random_game_num;
	fAutoSave = 1;		// 棋譜の自動セーブ（通信対戦時）

//	aya = 1;	// 彩が黒ではじめる(Italy)

	for (;;) {
		if ( nGnuGoHandi ) { komi = nGnuGoHandi + 0.5; aya = 2; strcpy(sPlayerName[1],sVersion); }

		// GnuGoの初期化
		if ( init_gnugo_gtp() == 0 ) return;

		// 序盤の4手を乱数で
		if ( ban_size==BAN_9 ) move_9ro_random_opening((gnugo_random_game_num - ContinuousNum)/2);	// 同じ序盤を交互に

		int i = 0;
		int fMaybeWin = 0;
		int col = 1;

		if ( nGnuGoHandi ) {
			if ( 1 ) for (i=0; i<nGnuGoHandi; i++) {
				int x = rand_yss() % 3;
				int y = rand_yss() % 3;
				if ( i==0 ) { x += 3; y += 3; }
				if ( i==1 ) { x +=15; y += 3; }
				if ( i==2 ) { x +=15; y +=15; }
				if ( i==3 ) { x += 3; y +=15; }
				int z = y*256+x;
				change_z_str(str, z);
				sprintf(str2,"play black %s\n",str);
				if ( SendGTPWaitOK(str2) == 0 ) break;	// OKの'='を受信
				set_kifu_time(z,1,0);
			} else for (i=0; i<nGnuGoHandi; i++) {	// GnuGoに自由置碁させると隅に連打して手堅すぎる。
				SendGTP("genmove black\n");
				if ( ReadCommmand(&z) == 0 ) break;	// OKの'='を受信
				set_kifu_time(z,1,0);
			}
			extern int fGtpClient; fGtpClient = 1;
			col = 2;
			goto aya_first;
		}

		if ( aya == 1 ) goto aya_first;

		for (i=0;i<1000;i++) {
			// パイプからGnuGoのエラー出力を読み出す
//			if ( ReadGnuGoErr() == FALSE ) break;	// うまく読み取れない？

			ct1 = get_clock();	// 探索開始時間。1000分の１秒単位で測定。
			if ( col == 2 ) SendGTP("genmove white\n");
			else            SendGTP("genmove black\n");
			if ( ReadCommmand(&z) == 0 ) break;
			if ( z < 0 ) { fRet = 2; break; }

			gct = get_spend_time(ct1);
			fRet = set_kifu_time(z,col,(int)gct);
			if ( fRet ) break;
			col = UN_COL(col);
aya_first:
//			z = test_random();
			z = ThinkNegaMax(&gct,col);
//			z = ThinkNegaMaxWithoutCol(&gct);	// 残り時間判定付きの思考ルーチン呼び出し
//			z = MajorityThinking(&gct);

			if ( i > 10 && ((col==1 && view_value > 0.90) || (col==2 && view_value < 0.10)) ) fMaybeWin = 1;
			if ( fMaybeWin && z < 0 ) fUpsetLoss = 1;
			if ( thinking_utikiri_flag == TUF_USER_INTERRUPT ) return;

			change_z_str(str, z);
			if ( col==2 ) sprintf(str2,"play white %s\n",str);
			else          sprintf(str2,"play black %s\n",str);

			if ( SendGTPWaitOK(str2) == 0 ) break;	// OKの'='を受信

			fRet = set_kifu_time(z,col,(int)gct);
			if ( fRet ) break;
			col = UN_COL(col);
		}
		
		dGnuGoFinalScore = 0;
		if ( nGnuGoHandi==0 && tesuu > 1 && kifu[tesuu][0] == 0 && kifu[tesuu-1][0] == 0 ) dGnuGoFinalScore = getGnuGoFinalScoreGTP();	// 置き碁はGnuGoの計算がおかしい

		if ( nMoGo || nFuego ) {
			SendGTP("quit\n");	// 明示的に終了させる
//			ReadCommmand(&z);
			Sleep(300);	// 安全のために待つ。
			close_gtp_engine_pipe();
			Sleep(100);	// 安全のために待つ。
			open_gtp_engine_pipe(0);	// MoGoのWindows版は連続対戦させると不安定なので再起動
		}

		if ( fRet < 2 ) break;
		ContinuousPlay(1);	// 連続通信対戦の終局処理
		if ( ContinuousNum <= 0 ) break;
	}
}

