1. 程式人生 > >黑白棋遊戲原始碼分析

黑白棋遊戲原始碼分析

 

遊戲規則:

在一個8x8的棋盤有兩個玩家,他們輪流放入分別放入白色和黑色的棋子。

當一個玩家在空棋子的位置放入一個棋子,我們檢查放入棋子的8個方向 上 下 左 右 左上 左下 右上 右下,如果這8個方向上有自己這方的棋子夾的全是對方的棋子,則對方的棋子將被吃掉,變成我方的棋子。

當旗盤中不能放入棋子的時候遊戲結束。如下圖,黑方先走,然後是白方走

有使用者輸入8x8旗盤的格局和哪一方先走,請編寫一個程式輸出哪一方最後能夠贏得遊戲,或是平局。

輸入:

有一個8x8的旗盤(‘b’代表黑色的棋子,‘w’代表白色的棋子,’e’代表空棋子),之後輸入誰先走。當輸入”endofinpur”(沒有了格局)時結束遊戲;

在每一種格局裡面,最多隻有10個空棋子。

輸出:

每一種格局都用一行輸出贏的一方(黑方或是白方)或是平局。確定每一行都沒有空棋子。

例如輸入:

wwwwwwww

wwwwwwww

wwwwwwww

wwwwwwww

wwwwwwww

wwwwwwww

wwwwwwww

wwwwwwww

black

wwwwwwww

wwwwwwww

wwwwwwww

wwwwwwww

bbbbbbbb

bbbbbbbb

bbbbbbbb

bbbbbbbb

white

endofinput

輸出:

White

Draw

原始碼:

#include"stdio.h"
#include"string.h"
int  kong[10][2], kong_num = 0,    //kong[][]儲存旗盤中所有空棋子的位置,kong_num儲存空棋子的數目
     yidong[8][2]={{-1, -1},{-1, 0},{-1, 1},{0, 1},{1, 1},{1, 0},{1, -1},{0, -1}}; //yidong[][]儲存移動的8個方向

void panduan(char qi[8][8]) //通過檢查白棋個數判斷黑白棋哪一方勝利
{
	/*white_num儲存旗盤中的白棋個數*/
	int white_num = 0;      
	for(int i = 0; i < 8; i++)
		for(int j = 0; j < 8; j++)
			if(qi[i][j] == 'w')
				white_num++;
	if(white_num < 32)
		printf("\nBlack\n");
	else if(white_num == 32)
		printf("\nDraw\n");
	else
		printf("\nWhite\n");

}

void boyi(char qi[8][8], char first,char next )  //根據最優策略,求出當下旗的走法,first儲存要走的旗幟,next儲存被吃的棋子
{
	/*quan[]儲存當前空棋子放入first旗幟後可以吃掉next旗幟的個數;max為可以被吃掉最多棋子個數的quan[]的位置;
	next_row,next_col分別為下一個棋子的行列號;times與yidong[j][]相乘就是某一方向上下一個要改變的旗幟偏移量;
	temp[]儲存某個方向上將要改變的的旗幟*/
	int quan[10], max = 0, next_row, next_col, times;
	char temp[8];
	for(int i = 0; i < kong_num; i++)  //根據最優策略,求出能被吃掉最多棋子的空棋子的位置
	{
		quan[i] = 0;
		for(int j = 0; j < 8; j++)
		{
			next_row = kong[i][0] + yidong[j][0];  //下一個要吃掉的旗幟的行號
		    next_col = kong[i][1] + yidong[j][1];  //下一個要吃掉的棋子的列號
			times = 1;
			/*如果下一個要吃掉的棋子的位置沒有溢位,並且下一個棋子要是被吃掉的棋子,將能被吃掉的棋子個數記錄下來*/
			while(next_row < 8 && next_row >=0 && next_row < 8 && next_row >=0 && qi[next_row][next_col] == next)
			{
				    quan[i]++;
					times++;
				    next_row = kong[i][0] + times * yidong[j][0];
		            next_col = kong[i][1] + times * yidong[j][1];
			}
			/*如果這個方向上的棋子不能被吃掉,恢復這一方向上的棋子數去掉*/
			if(next_row >= 8 || next_row <0 || next_col >= 8 || next_col <0 || qi[next_row][next_col] != first)
				quan[i] = quan[i] - times + 1;
		}
		if(quan[i] > quan[max])  //求出能被吃掉最多棋子個數的,空棋子的位置
			max = i;
	}   
	qi[kong[max][0]][kong[max][1]] = first;
	for(int j = 0; j < 8; j++)//根據最優策略,將first棋子放入並更改棋盤上的格局
	{
		next_row = kong[max][0] + yidong[j][0];
		next_col = kong[max][1] + yidong[j][1];
	    times = 1;
		/*如果下一個要吃掉的棋子的位置沒有溢位,並且下一個棋子要是被吃掉的棋子,將下一個棋子吃掉*/
		while(next_row < 8 && next_row >=0 && next_col < 8 && next_col >=0 && qi[next_row][next_col] == next)
		{
			temp[times] = qi[next_row][next_col];
			qi[next_row][next_col] = first;
			times++;
		    next_row = kong[max][0] + times * yidong[j][0];
		    next_col = kong[max][1] + times * yidong[j][1];
		}
		/*如果這個方向上的棋子不能被吃掉,恢復這一方向上的棋子原來的格局*/
		if(next_row >= 8 || next_row <0 || next_col >= 8 || next_col <0 || qi[next_row][next_col] != first)
		{
			for(i = 1;i < times; i++)
			{
				next_row = kong[max][0] + i * yidong[j][0];
		        next_col = kong[max][1] + i * yidong[j][1];
				qi[next_row][next_col] = temp[i];
			}
		}

	}
	for(int q = max; q < kong_num - 1; q++) //去掉儲存空棋子中的 放入棋子的位子
	{
		kong[q][0] = kong[q + 1][0];
		kong[q][1] = kong[q + 1][1];
	}
	kong_num--;    
	/*
	printf("\n========\n");
	for(i = 0; i < 8; i++)
	{
		for(int j = 0; j < 8; j++)
			printf("%c",qi[i][j]);
		printf("\n");
	}
	*/
	if(kong_num == 0)//如果空棋子沒有了,判斷結果
		panduan(qi);
	else           //如果空棋子還,繼續博弈
		boyi(qi, next, first);
}

void shuru()  //接收使用者輸入的黑白棋的的格局
{
	/*first儲存判斷黑白方先走的一個方的程式碼;qi[][]存放當前棋子的格局;
	row_1[]存放第一行輸入的字串;end[]存放結束字串,judge[]存放先走的一方*/
	int first;
	char qi[8][8],row_1[11],end[11] = "endofinput", judge[6];
	while(1)
	{
		printf("\n請輸入8行8列的黑白棋的旗盤局勢:\nw 代表白棋\tb 代表黑棋\te 代表空\tendofinput 退出\n");
		gets(row_1);                //接收第一行的字串
		if(strcmp(row_1,end) == 0)  //判斷是否為結束提示語,如果是的則跳出迴圈
			break;
		else      
		{
			for(int r = 0; r < 8; r++)  //將第一行的棋子格局儲存起來,並記錄其中空棋子的位置
			{
				if(row_1[r] == 'e')
					{
						kong[kong_num][0] = 0;
				    	kong[kong_num][1] = r;
				    	kong_num++;
					}
				qi[0][r] = row_1[r];
			}
			for(int i = 1; i < 8; i++) //將 2-8 行的棋子格局儲存起來,並記錄其中空棋子的位置
			{
				for(int j = 0; j <8; j++)
				{
					scanf("%c", &qi[i][j]);
			    	if(qi[i][j] == 'e')
					{
						kong[kong_num][0] = i;
				    	kong[kong_num][1] = j;
				    	kong_num++;
					}
				}
			    getchar();
			}
			printf("\n請輸入哪一方先走:white 白方先走,black 黑方先走\n");
		    gets(judge);  //判斷哪一方先走
	     	if(kong_num != 0)
			{
				if(strcmp(judge,"white") == 0)
					boyi(qi, 'w', 'b');
			    else
				    boyi(qi, 'b', 'w');
			}
		    else
			     panduan(qi);
		}
	}
}
	

int main()
{
	shuru(); //輸入棋盤的格局
	getchar();
	return 0;
}

解題思路:

這個遊戲便是我們經常玩的一種黑白旗的遊戲,由於它裡面空的棋子最多為10個,那麼我們可以用最優策略來下棋。即當將我方棋子放入空棋子的位置的時候能吃掉最多對方棋子數目的時候,此位置為最優解位置。

那麼此遊戲的程式可以分為3個階段:

第一階段:將旗盤的格局和先走的一方接收並將其儲存起來。

第二階段:根據輸入的旗盤格局和先走的一方,用遞迴的方法對其進行博弈(當前走棋的

一方用最優策略進行走棋),直到旗盤裡面沒有空棋子為止。

第三階段:通過檢查白色棋子的個數(是否大於32或是等於32)判斷哪一方獲勝並將其

輸出。

此遊戲主要難在第二階段的吃棋子的問題上面。首先要在第一階段輸入棋子格局的時候將空棋子所在的位置儲存起來,然後第二個階段的時候,在每一個空棋子的位置判斷可吃棋子的個數。可吃棋子個數最多的位置就是當前最優位置,則在這個位置放入棋子,並吃掉對方的棋子。

而在此處,怎樣吃棋子又是一個難點。可以把棋子的8個方向分開來判斷,在某一個方向上,如果下一個棋子沒有溢位,並且下一個棋子是對方的棋子,則將其吃掉。如果下一個棋子不是對方的棋子是自己的棋子,則這個方向上吃棋子結束,跳入到下一個方向。如果下一個棋子不是對方的棋子,是棋子溢位了或是空棋子,則將這一個方向上面吃掉的棋子還原。進入下一個方向吃棋子。如果8個方向的棋子的判斷過了,那麼吃棋子結束。 如果還有空棋子,那麼遞迴輪到對方吃棋子。