1. 程式人生 > >【遊戲之樂】中國象棋 讓你學會位運算

【遊戲之樂】中國象棋 讓你學會位運算

任務難度:3

任務說明:

在中國象棋裡,當只剩下將帥(A和B代指將和帥)兩個棋子時  將帥不能處於同一列  要求輸出將帥的所有合法位置(將帥所在的九宮格從上到下從左至右編號為1~9)
例如:
    合法位置:
    A=1,B=2
    不合法位置:
    A=1,B=1
要求:只能使用一個變數

乍一看問題並不難,但是最後的要求是隻能使用一個變數,這也是好玩之處~~

先讓我們思考一下A和B的狀態共有多少種,九宮格的9個位置,即各有9種狀態,9需要用4bit來儲存,儲存AB兩種狀態則至少需要8bit,即一個BYTE

那麼主體思想就是我們通過某種迴圈方式,分別列舉AB狀態的所有值(1~9)當兩個狀態的%3即為兩個狀態的列數相同時,則不能輸出,否則輸出

接下來介紹三種方法,讓你學會位運算:

方法一:

假設我們開一個BYTE型的變數b,用左邊的4bit儲存B的狀態,用右邊的4bit儲存A的狀態,我們需要實現4種操作,即取出該變數的左邊四位或右邊四位,將該變數的左邊四位或右邊四位設定為n值(n取1~9)

取出右邊的值只需要將變數b與00001111(RMASK)相與即可,取出左邊四位的值只需要先將b與11110000(LMASK)相與,再右移四位即可

而將右邊的值改為n 就需要將左邊的值先保留下來 即將變數b與11110000相與,然後再和n異或即可

而將左邊的值改為n 就需要將右邊的值先保留下來,即將變數b與00001111相與,然後再和右移四位後的n異或即可

我們用四個巨集定義的函式實現上述功能,迴圈列舉的時候只需要先將b的左右置為1,然後每次多置1即可,程式碼如下:

#include <cstdio>
#include <windows.h>
/*解法一*/

#define HALF_BIT_LENGTH 4						//因為要頻繁使用左右移4位的操作,這裡把4巨集定義出來
#define FULLMASK 255							//即11111111 用於得出LMASK和RMASK
#define LMASK (FULLMASK<<HALF_BIT_LENGTH)		//將11111111左移4位 即得到11110000
#define RMASK (FULLMASK>>HALF_BIT_LENGTH)		//即00001111
#define RGET(b) (b & RMASK)						//得到右4位
#define LGET(b) ((b& LMASK)>>HALF_BIT_LENGTH)	//得到左4位
#define RSET(b,n) (b= (b & LMASK)^n)			//將右四位置為n
#define LSET(b,n) (b= (b & RMASK)^(n<<HALF_BIT_LENGTH))	//將左四位置為n
#define GRIDW 3			//由於求列數時需要對3取餘,這裡也巨集定義出來

int main()
{
	unsigned char b;
	for (RSET(b,1);RGET(b)<=GRIDW*GRIDW;RSET(b,(RGET(b)+1)))
	{//列舉右四位從1到9的情況,即列舉A的9種狀態
		for (LSET(b,1);LGET(b)<=GRIDW*GRIDW;LSET(b,(LGET(b)+1)))
		{//列舉左四位1到9的情況,即列舉B的9種狀態
			if (RGET(b)%GRIDW!=LGET(b)%GRIDW)//列數不同則表示AB位置合法
			{
				printf("A=%d, B=%d\n",RGET(b),LGET(b));
			}
		}
	}
	return 0;
}

是不是感覺很奇妙呢?這就是位運算的妙用,接下來介紹兩種更為簡便的方法

方法二:我們將AB的所有狀態排列組合,其實只有81種組合方式,我們直接定義一個BYTE變數i表示AB位置的排列方式,i=1表示A為1 B為1

用i/9+1表示A i%9+1表示B i的取值範圍為1~81  想一想這樣是不是涵蓋了A與B分別取1~9的81種情況

程式碼如下:

#include <cstdio>
#include <windows.h>
BYTE i=0;

int main()
{
	for (;i<=81;i++)
	{

		if ( i/9%3 != i%9%3)
		{
			printf("A=%d, B=%d\n",i/9+1,i%9+1);
		}
	}

	return 0;

接下來介紹最後一種方法 最簡單也最容易理解 直接建立一個大小為BYTE的struct變數 將BYTE的前4位和後4位分開直接設為兩個成員變數

程式碼如下:

#include <cstdio>
#include <windows.h>
struct  
{
	unsigned char a:4;
	unsigned char b:4;
}i;

int main()
{
	for (i.a=1;i.a<=9;i.a++)
	{
		for (i.b=1;i.b<=9;i.b++)
		{
			if (i.a%3!=i.b%3)
			{
				printf("A=%d, B=%d\n",i.a,i.b);
			}
		}
	}

	return 0;
怎麼樣 是不是很有意思呢?