C++實現人機對戰圍棋(使用Leela Zero權重)-界面

1。在《leela-zero-next》包中有一個

從人類遊戲訓練的(較弱的)網絡的權重文件(https://sjeng.org/zero/best_v1.txt.zip)(6殘差塊,128通道)

2。並在《easyx吧》百度貼吧下了一個雙人對戰的五子棋+圍棋的遊戲(去掉其中的五子棋部分)。

把這兩個合成一個人機對戰圍棋。

雖然leela-zero也是C++寫的,但是我也看不懂,這裏只使用其權重,還是按前面的方法生成推演網絡。

難點在

(1)圍棋界面的部分的數組數據--->轉換成網絡輸入

(2)網絡輸出--->轉換界面落子位

界面部分

流程:

       載入一張背景圖,在其上畫19x19棋盤。

       然後在鼠標點位下子(黑白輪流下)(鼠標左鍵)(任何時都可以雙方交換子)

       把其中的一方由網絡代替

       提子要手工提(鼠標右鍵)

 

      一些變量:

int player_exchange = 0;//玩家輪流下棋
int arr[19][19] = { 0 };//棋盤數組
char str[5];//outtextxy轉換字符中間容器
int a, b;//全局變量,記錄交叉點位置
int a1, b1, a2, b2;//記錄落子時落在數組的什麼位置
int x, y;//全局變量,記錄交叉點座標

int record_now_player_exchange;//記錄剛讀檔的exchange值,此後exchange就會++
int play_weiqi = 0;//是否選擇圍棋

//-------------------------------------
//人機對戰
int jibw=0;//機器選黑白子 白:0,黑:1
vector<int> 順序黑;//黑方下子序號(在網絡圖中的序號(也是下子順序),連接界面和網絡用)
vector<int> 順序白;//白方下子序號

畫棋盤:

// 畫棋盤
void 畫棋盤()
{
	int i;
	int j;

	BeginBatchDraw();

	IMAGE bk;
	loadimage(&bk, "圍棋背景.jpg", 1100, 1000);

	//五子棋背景圖片
	putimage(0, 0, 20 * 25 + 130, 121 * 25 - 20, &bk, 0, 0);
	setlinecolor(BLACK);
	setlinestyle(PS_SOLID, 1);

	for (j = 1; j <= 19; j++)
	{
		line(1 * 25, j * 25, 19 * 25, j * 25);
	}
	for (i = 1; i <= 19; i++)
	{
		line(i * 25, 1 * 25, i * 25, 19 * 25);
	}
	//寫入下標數字
	for (i = 1; i <= 19; i++)
	{
		for (j = 1; j <= 19; j++)
		{
			setbkmode(0);
			settextstyle(16, 0, _T("宋體"));
			settextcolor(BLACK);
			outtextxy(i * 25 - 6, 19 * 25 + 8, change(i));
			outtextxy(1 * 25 - 24, j * 25 - 9, change(j));
		}
	}
	//畫邊框
	setlinestyle(PS_SOLID, 2);
	line(1 * 25, 1 * 25, 19 * 25, 1 * 25);
	line(1 * 25, 19 * 25, 19 * 25, 19 * 25);
	line(1 * 25, 1 * 25, 1 * 25, 19 * 25);
	line(19 * 25, 1 * 25, 19 * 25, 19 * 25);
	setfillcolor(BLACK);
	//畫四個點
	fillcircle(4 * 25, 4 * 25, 3);
	fillcircle(16 * 25, 4 * 25, 3);
	fillcircle(4 * 25, 16 * 25, 3);
	fillcircle(16 * 25, 16 * 25, 3);
	fillcircle(10 * 25, 10 * 25, 3);
	fillcircle(10 * 25, 4 * 25, 3);
	fillcircle(16 * 25, 10 * 25, 3);
	fillcircle(10 * 25, 16 * 25, 3);
	fillcircle(4 * 25, 10 * 25, 3);
	setbkmode(0);
	settextstyle(16, 0, _T("宋體"));
	settextcolor(BLACK);
	outtextxy(510, 40, "棋子座標爲:");
	//outtextxy(520, 60, "  行  列");
	outtextxy(520, 60, "  列  行");
	outtextxy(510, 100, "該:");
	outtextxy(510, 130, "白棋步數:");
	outtextxy(510, 160, "黑棋步數:");
	outtextxy(510, 190, "圍棋規則:");
	outtextxy(510, 210, "·右鍵摘子");
	outtextxy(510, 230, "·玩家判斷輸贏");
	//outtextxy(510, 260, "五子棋規則:");
	outtextxy(510, 280, "按下a:對換子");
	//outtextxy(510, 350, "按下1:暫停:");
	//outtextxy(510, 380, "按下2:存檔:");
	//outtextxy(510, 410, "按下3:退出:");
	//outtextxy(510, 440, "按下4:悔棋:");

	EndBatchDraw();

}

畫好的棋盤:

 入口:

int main()
{
	initNet();//載入網絡權重
	initgame();//畫棋盤
	system ("title 圍棋");
	player_down();//遊戲循環(人機下子)
	closegraph();
	return 0;
}

 遊戲循環:

//玩家落子 遊戲循環
void player_down()
{
	int i;
	int j;

	MOUSEMSG m;//定義一個鼠標變量m
	hwnd = GetHWnd();
	while (1)
	{
		m = GetMouseMsg();
		//讓棋子下在交叉點
		for (i = 1; i <= 19; i++)
		{
			for (j = 1; j <= 19; j++)
			{
				if ((abs(m.x - i * 25)<12) && (abs(m.y - j * 25)<12))
				{
					//記錄下交叉點的位置
					a = i;
					b = j;
					x = i * 25;
					y = j * 25;
				}
			}
		}
		if(player_exchange % 2 ==jibw)
		{
			if(機器下子())
				落子();
		}
		else
		{
			//玩家下子
			if (m.uMsg == WM_LBUTTONDOWN)
			{
				huiqi_just_one = 1;//賦值爲1,允許悔棋
				if (arr[a - 1][b - 1] != 0)
				{
					//MessageBox(hwnd, TEXT("此地已有子,請另擇他地!"), "提示:", MB_OK);
					continue;
				}
				cout<<"玩家:"<<a<<","<<b<<endl;	
				cout<<"序號:"<<(b-1)*19+a-1<<endl;
				


				落子();

			}
		}

		//按下右鍵,清除棋子——圍棋規則
		if (m.uMsg == WM_RBUTTONDOWN)
		{
			if (arr[a - 1][b - 1]!= 0)
			{
				arr[a - 1][b - 1] = 0;
				delete_stone_youjian();
				continue;
			}
		}
		char ch;//接收鍵盤的變量
		hwnd = GetHWnd();
		while (kbhit())
		{
			ch = getch();
			//玩家和機器對換
			if (ch == 'a')
			{
				if(jibw==0)
					jibw=1;
				else
					jibw=0;
					
			}

		}
	}
}

機器下子:

bool 機器下子()
{

	//黑白子下子順序到後面
	
	//生成最近8步棋數據
	//走子方8步棋
	//非走子方8步棋
	//當前行棋方-----------這些到網絡部分 netgo 生成

	//輸入網絡

	//返回落子位

	bool lu=false;//選點成功嗎,不成功可能要當機了(可以加大,5個不夠)
		
	vector<int> 前5序號;

	netgo(順序黑, 順序白,前5序號);//去網絡部分

	//返回下子序號生成棋盤位置
	for(int i=0;i<前5序號.size();i++)
	{
		int xy=前5序號[i];
		int b0=xy/19;
		int a0=xy%19;
		if(arr[a0][b0]== 0)
		{
			b=b0+1;
			a=a0+1;
			cout<<"機:"<<a<<","<<b<<endl;
			cout<<"序號:"<<xy<<endl;
					x = a * 25;
					y = b * 25;
					lu=true;
					break;
		}
	}
	return lu;
}

落子並生成下棋順序:

//落子並生成下棋順序
void 落子()
{

	if (player_exchange % 2 == 0)
	{
		setfillcolor(WHITE);
		solidcircle(x, y, 8);
		//此時的a,b就是交叉點的位置
		a1 = a - 1; b1 = b - 1;
		arr[a1][b1] = 1;
		number_white++;
		//設置圖片遮蓋字體
		setfillcolor(YELLOW);
		solidrectangle(580, 130, 595, 147);//步數
		solidrectangle(580, 100, 595, 120);//黑,白字
		solidrectangle(519, 60, 534, 77);//棋子座標
		solidrectangle(551, 60, 566, 77);//座標
		//setcolor(BLACK);
		setlinecolor(BLACK);
		outtextxy(580, 130, change(number_white));
		outtextxy(564, 100, ":黑");
		outtextxy(519, 60,change(a1+1));
		outtextxy(551, 60, change(b1+1));
		順序白.push_back(a1+b1*19);
	}
	else
	{
		setfillcolor(BLACK);
		solidcircle(x, y, 8);
		//將黑棋的點的值都設置成爲2
		a2 = a - 1; b2 = b - 1;
		arr[a2][b2] = 2;
		number_black++;
		//設置圖片遮蓋字體
		setfillcolor(YELLOW);
		solidrectangle(580, 160, 595, 177);
		solidrectangle(580, 100, 595, 120);
		solidrectangle(519, 60, 534, 77);//座標
		solidrectangle(551, 60, 566, 77);//座標
		//setcolor(BLACK);
		setlinecolor(BLACK);
		outtextxy(580, 160, change(number_black));
		outtextxy(564, 100, ":白");
		outtextxy(519, 60, change(a2+1));
		outtextxy(551, 60, change(b2+1));
		順序黑.push_back(a2+b2*19);
	}
	player_exchange++;

}

界面部分不都是我寫的(我修改了一部分代碼,,加了一部分,部分原創),有興趣的朋友可以去貼吧下載原始代碼。

下了幾步圖:

界面部分暫時結束。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章