華容道自動求解 java版

以前自學程序設計時, 研究過華容道的自動求解,已經是幾年前的事了。

當時找到一個高人寫的程序,效率非常高,但是,是C語言的代碼,代碼可讀性不好,以前弄明白過這個程序,現在又忘記了,故而這次把C語言的代碼改成java版的,有重新理解一遍,並記下來,以後不怕在忘了。

C 和 javascript版的代碼

http://www.fjptsz.com/xxjs/xjw/rj/110.htm

對以上代碼,我着重理解了盤面是如何編碼的,還有原作者如何用數組實現廣度優先搜索,如何找到最終答案的。

package com.global;

public class LocalConst {
	//用1-15表示各棋子,空位用0表示,兵1-4,豎將5-9,橫將10-14,大王15
	//大王只能1個,將必須5個(橫豎合計),兵必須爲4個
//	static final public String U = "ABBBBCCCCCHHHHHM";
	static final public int[] U = {'A','B','B','B','B','C','C','C','C','C','H','H','H','H','H','M'};
	static final public int[] COL = {0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3};   //列號表
	static final public int[] ROW = {0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4};   //行號表
	static final public int[] WQ2 = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096}; //二進制位權表(12個)
	
	//使用128k(17位)哈希表,如果改用更大的表,相應的哈希計算位數也要改
	static final public int hsize = 128 * 1024;
}

盤面是如何編碼?

盤面的狀態(節點)數量是十分有限的,狀態總數不會超過50萬種。(橫刀立馬爲例)

曹操的走法只有12種,任你如何排放,只有12種,不是20種。

橫將(關羽)的排法最多隻有11種

接下來對4個豎將排列(組合),排列第一個豎將的排法最多10種,第二個8種,第三個6種,第四個4種。組合數是10*8*6*4/4!=80,後來爲來編程方便,做了更多冗於,組合數用C10取4,即C(10,4)=10*9*8*7/4!=210,這樣,4個豎將的某一排列組合必對應0—209中的一個數,這個數就是我們所要的豎將組合編碼值。

同理小兵的組合爲C(6,4)=15,編碼範圍在0—14

因此對這4種(10個)棋子全排列,種數最多爲12*11*210*15=415800,即4百多K。

最後易得盤面編碼:各種棋子的編碼值乘以碼權,然後取和。

碼權只需遵照排列規律,隨你定,是比較簡單的。可設兵的碼權爲1,豎將則是15,橫將則爲15*210,曹操爲15*210*11。

要如何對各種棋子高速有效的編碼呢?如“橫刀立馬”開局,如何編碼?

這又變成一個組合問題。

我們一個一個的排放“橫刀立馬”棋子並演示編碼過程。

曹操有12個可排放位置,這12個位置編號爲0-11,曹操位置在1,注意,首個是0。

關羽有11個可排放位置,這11個位置編號爲0-10,關羽位置在1個。

豎將有10個可排放的位置,編號爲0-9,一將是0,二將是1,三將是4,四將是5。

小兵有6個可排放的位置,編號爲0-5,一兵是0,二兵是1,三兵是2,四兵是5。


組合序號表是什麼意思?

比如小兵只有4個, 可以佔用6個位置,前面的14個位置已經被大王,橫將,豎將佔了

小兵的組合序號表Bz的內容爲 001111 (序號爲0), 011101 (序號爲1), 011011 (序號爲2),010111 (序號爲3), ...

在Bz[i]=Bx++; 處設置斷點,有利於理解程序。

組合序號表就是對各種組合進行一個編號。

比如C4 取2的組合,其組合序號表爲

0:  0011

1:  0101

2:  0110

3:  1001

4:  1010

5:  1100

共有六個元素,實際上,把下面代碼的變量 Hz, Sz, Bz用字典來存儲,代碼可讀性更強。

PmBm構造函數的那個二重循環,容易讓人看得不知所云,實際上就是用來初始化組合序號表的,

特別的,當橫將或在豎將只有一個時,完全無必要用組合序號表這種字典,組合序號表的作用就是把多於一個棋子位置擺放僅用一個數字來描述,

如果不用組合序號表,描述四個兵的位置,就需要更多的比特,而現在只需要六個比特,比一個字符佔的空間(8比特)還要小。


PmBm構造函數出現很多變量,多次讓我懵逼,爲什麼會多次呢?因爲編碼過程本身有些複雜,加上代碼命名方式不友好。

比如
Hz = new short[4096 * 3];
Sz = new short[4096];
Bz = new short[128];
//C12取5=792
Hw = new int[792*2];
Sw = new int[792];

爲什麼,五個數組初始化這麼大,其實不用糾結,Bz的大小其實 2 的 6 次方, 64 就夠了,
Hz 的大小應該是 2 的 11 次方,關羽這種橫將 在 大王之後擺放,只有11種位置選擇,
Sz 的大小應該是 2 的 10 次方,  每個豎將最多有10個位置選擇

以“橫刀立馬”爲例子
Bx = 15 就是6 中 取 4 的組合數

Sw = [0, 15, 30, ...] length = 210, 就是10 中 取 4 的組合數

Hmax = 2048, Smax = 1024,

Bx = 15 ,  Sx = 210, 10箇中取4個的組合數, Hq = 15 * 210, Mq = 15 * 210 * 11
Bx四個兵的組合數目, Sx豎將的組合數目,Hx橫將的組合數目

Mw, Hw, Sw這三個數組,用查表來替代乘法

package com.gamedata;

import com.global.LocalConst;

//盤面編碼類
public class PmBm {
	
	//組合序號表
	short[] Hz;  	//橫將
	short[] Sz;		//豎將
	short[] Bz;		//小兵
	
	//權值表
	int[] Hw;
	int[] Sw;
	int[] Mw;
	
	public int bmTotal;
	
	public PmBm(int[] qiPan) {
		Hz = new short[4096 * 3];
		Sz = new short[4096];
		Bz = new short[128];
		//C12取5=792
		Hw = new int[792*2];
		Sw = new int[792];
		int i,j,k;
		int Hn=0, Bn=0, Sn=0; //各類子數目,大王默認爲1不用計數
		for(i=0;i<20;i++){   //計算各種棋子的個數
			if(LocalConst.U[qiPan[i]]=='B') Bn++;
			if(LocalConst.U[qiPan[i]]=='H') Hn++;
			if(LocalConst.U[qiPan[i]]=='C') Sn++;
		}
		Hn /= 2;
		Sn /= 2;
		int[] WQ2 = LocalConst.WQ2;
		int Hmax=WQ2[11],Smax=WQ2[12-Hn*2],Bmax=WQ2[16-(Hn+Sn)*2]; //各種子的最大二進位數
		short Hx=0,Sx=0,Bx=0; //各種棋子組合的最大序號
		//初始化組合序號表
		for(i=0; i<4096; i++){
			for(j=0,k=0;j<12;j++) {
				if((i & WQ2[j]) > 0) {
					k++; //計算1的個數
				}
			}
			if(k==Hn && i<Hmax) {
				Hz[i] = Hx++;
			}
			if(k==Sn && i<Smax) {
				Sz[i]=Sx++;
			}
			if(k==Bn && i<Bmax) {
				Bz[i]=Bx++;
			}
		}
		int Sq=Bx,Hq=Bx*Sx,Mq=Bx*Sx*Hx; //豎將位權,橫將位權,王位權
		Mw = new int[12];
		Hw = new int[Hx];
		Sw = new int[Sx];
		for(i=0;i<12;i++) Mw[i]=i*Mq; //初始化大王權值表
		for(i=0;i<Hx;i++) Hw[i]=i*Hq; //初始化橫將權值表
		for(i=0;i<Sx;i++) Sw[i]=i*Sq; //初始化豎將權值表
		bmTotal = Mq*12;
	}
	
	//盤面編碼
	public int Bm(int[] qiPan) {
		int Bb=0,Bd=-1; //空位序號記錄器
		int Sb=0,Sd=-1; //豎條序號記錄器
		int Hb=0,Hd=-1; //橫條序號記錄器
		int Mb = 0;         //大王序號記錄器
		int c,lx;
		int[] f = new int[16];
		
		for(int i = 0; i < 20; i++){
			c=qiPan[i];
			lx = LocalConst.U[c]; //當前的值
			if(lx=='M') { //大王定序
		       if(f[c] == 0) {
		    	   Mb = i - LocalConst.ROW[i];
		    	   f[c] = 1;
		       }
		       continue;
		     }
		     if (LocalConst.COL[i]<3 && LocalConst.U[qiPan[i+1]] <= 'H') {
		    	 Hd++; //橫條位置序號(編號)
		     }
		     if (lx == 'H') {//橫將定序,轉爲二進制進行詢址得Hb
		       if(f[c] == 0) {
		    	   Hb += LocalConst.WQ2[Hd];
		    	   f[c]=1;
		       }
		       continue;
		     }
		     if (LocalConst.ROW[i]<4 && LocalConst.U[qiPan[i+4]]<='C') {
		    	 Sd++; //豎將位置序號(編號)
		     }
		     if (lx=='C') { //豎條定序,轉爲二進制進行詢址得Sb
		       if(f[c] == 0) {
		    	   Sb += LocalConst.WQ2[Sd];
		    	   f[c]=1;
		       }
		       continue;
		     }
		     if(lx<='B') Bd++;  //小兵位置序號(編號)
		     if(lx=='B') Bb += LocalConst.WQ2[Bd]; //小兵定序,轉爲二進制進行詢址得Bb
		   }
	   //Hb,Sb,Bb爲組合序號,"橫刀立馬"最大值爲小兵C(6,4)-1=15-1,豎條C(10,4)-1=210-1
	   Bb=Bz[Bb];
	   Sb=Sz[Sb];
	   Hb=Hz[Hb];//詢址後得得Bb,Sb,Hb組合序號
	   return Bb+Sw[Sb]+Hw[Hb]+Mw[Mb]; //用位權編碼,其中Bb的位權爲1
	}
	
	//按左右對稱規則考查棋盤,對其編碼
	public int dcBM(int[] q){ 
		char i;
		int[] q2 = new int[20];
		for(i=0; i<20; i+=4) {
			q2[i]=q[i+3];
			q2[i+1]=q[i+2];
			q2[i+2]=q[i+1];
			q2[i+3]=q[i];
		}
		return Bm(q2);
	}
}


PMZB 這個類是負責盤面走步的,非常簡單,就是一些判斷,容易理解

package com.gamelogic;

import com.global.LocalConst;

public class PMZB {
	//原位置,目標位置,最多隻會有10步
	int[] src = new int[10];
	int[] dst = new int[10];
	//總步數
	int n;
	
	private void zbIn(PMZB zb, int s, int d) {
		zb.src[n] = s;
		zb.dst[n] = d;
		zb.n++;
	}

	public void analyze(int[] qiPan, PMZB zb) {
		int i,col,k1=0,k2=0,h=0; //i,列,空格1位置,空格2位置,h爲兩空格的聯合類型
		int c,lx;
		//f[]記錄已判斷過的棋字
		int[] f = new int[16];
		zb.n=0; //計步復位
		for(i=0; i<20; i++){
			if(qiPan[i] == 0) {
				k1=k2;
				k2=i; //查空格的位置
			}
		}
		if (k1 + 4 == k2) {
			h = 1;	//空格豎聯合
		}
		if (k1 + 1 == k2 && LocalConst.COL[k1] < 3) {
			h = 2;	//空格橫聯合
		}
		for (i = 0; i < 20; i++) {
			c = qiPan[i];
			lx = LocalConst.U[c];
			col = LocalConst.COL[i];
			if (f[c] == 1) {
				continue;
			}
			switch (lx) {
			case 'M'://曹操可能的走步
				if (i + 8 == k1 && h == 2) {
					//向下
					zbIn(zb, i, i + 4);
				}
				if (i - 4 == k1 && h == 2) {
					//向上
					zbIn(zb, i, i - 4);
				}
				if (col < 2 && i + 2 == k1 && h == 1) {
					//向右
					zbIn(zb, i, i + 1);
				}
				if (col > 0 && i - 1 == k1 && h == 1) {
					//向左
					zbIn(zb, i, i - 1);
				}
				f[c] = 1;
				break;
			case 'H':
				//關羽可能的走步
				if (i + 4 == k1 && h == 2) {
					zbIn(zb, i, i+4);
				}
				if (i - 4 == k1 && h == 2) {
					zbIn(zb, i, i - 4);
				}
				if (col < 2 && (i + 2 == k1 || i + 2 == k2)) {
					zbIn(zb, i, i + 1);
					if (h == 2) {
						zbIn(zb, i, k1);
					}
				}
				if (col > 0 && (i - 1 == k1 || i - 1 == k2)) {
					zbIn(zb, i, i - 1);
					if (h == 2) {
						zbIn(zb, i, k1);
					}
				}
				f[c] = 1;
				break;
			case 'C':
				//張飛,馬超,趙雲,黃忠可能的走步
				if (i + 8 == k1 || i + 8 == k2) {
					zbIn(zb, i, i + 4);
					if (h == 1) {
						zbIn(zb, i, k1);
					}
				}
				if (i - 4 == k1 || i - 4 == k2) {
					zbIn(zb, i, i - 4);
					if (h == 1) {
						zbIn(zb, i, k1);
					}
				}
				if (col < 3 && i + 1 == k1 && h == 1) {
					zbIn(zb, i, i+1);
				}
				if (col > 0 && i - 1 == k1 && h == 1) {
					zbIn(zb, i, i-1);
				}
				f[c] = 1;
				break;
			case 'B':
				if (i + 4 == k1 || i + 4 == k2) {
					if (h > 0) {
						zbIn(zb, i, k1);
						zbIn(zb, i, k2);
					} else {
						zbIn(zb, i, i + 4);
					}
				}
				if (i - 4 == k1 || i - 4 == k2) {
					if (h > 0) {
						zbIn(zb, i, k1);
						zbIn(zb, i, k2);
					} else {
						zbIn(zb, i, i - 4);
					}
				}
				if (col < 3 && (i + 1 == k1 || i + 1 == k2)) {
					if (h > 0) {
						zbIn(zb, i, k1);
						zbIn(zb, i, k2);
					} else {
						zbIn(zb, i, i + 1);
					}
				}
				if (col > 0 && (i - 1 == k1 || i - 1 == k2)) {
					if (h > 0) {
						zbIn(zb, i, k1);
						zbIn(zb, i, k2);
					} else {
						zbIn(zb, i, i - 1);
					}
				}
				break;
			}
		}
	}
	
	//走一步函數
	void zb(int[] qiPan, int src, int dst) {
		int c = qiPan[src];
		int lx = LocalConst.U[c];
		switch(lx) {
		case 'B':
			qiPan[src] = 0;
			qiPan[dst] = c;
			break;
		case 'C':
			qiPan[src] = qiPan[src+4] = 0;
			qiPan[dst] = qiPan[dst+4] = c;
			break;
		case 'H':
			qiPan[src] = qiPan[src+1]=0;
			qiPan[dst] = qiPan[dst+1]=c;
			break;
		case 'M':
			qiPan[src] = qiPan[src+1]= qiPan[src+4]=qiPan[src+5]=0;
			qiPan[dst] = qiPan[dst+1]= qiPan[dst+4]=qiPan[dst+5]=c;
			break;
		}
	}
}


ZBD,走步隊,這個類不太容易理解

變量n 表示當前隊的長度

變量m 表示當前入隊的佈局,它的父親佈局在隊列z 中的索引

比如,我們有一個初始佈局 0, 佈局0 通過PMZB類的分析, 有 3個兒子節點,分別命名爲a1, a2, a3

首先把初始佈局 0 放入隊列z 中,這時m=0, 把m放入數組 hs 中, 接着把兒子 a1, a2, a3 都放入 隊列z中,這時

z = [0, a1, a2, a3], hs = [0, 0, 0, 0], 然後m = m + 1;  假設 a1 有兒子節點b1, b2,  a2 有兒子 c1, a3 有兒子d1, d2, 

b1有兒子 e1, e2, b2 有兒子 f1, f2, f3,  c1有兒子 g1, g2, 

如下:


如果搞清了隊列z, 數組hs, n, m的變化過程,就理解了代碼

最終

0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
z= 0 a1 a2 a3 b1 b2 c1 d1 d2 e1 e2 f1 f2 f3 g1 g2
hs= 0 0 0 0 1 1 2 3 3 4 4 5 5 5 6 6


如何知道g1節點是怎麼來的,通過hs 知道g2節點是 隊列z的第6個節點的兒子,也就是c1, 又c1的父親是第2個節點 a2, a2的父親是初始佈局 0


ZBD.bfs() 中的變量 i 爲何能代表層數,以上圖爲例,

假設,z.z隊列中爲root, a1, a2, a3, b1, b2, c1,d1
 k=4, z.m=3, 執行到代碼第42行後,當前佈局爲d2, 
m值爲4,等於k,跳出while循環,執行代碼第96行,z.zbrd(),將d2加入z.z隊列,這時n從7變爲8,
k = 8,這裏,隊列的第8個節點就是該層的最後一個節點,然後執行z.zbcd(),
執行到代碼第50行後,當前佈局從d2變爲b1.

下列代碼的第50行有兩種作用,
1. 還原當前佈局爲其父佈局
2. 剛剛換層時,i剛剛變化時,把當前層的第一個佈局賦值給當前佈局

z.n表示隊列中有 n 個佈局(節點)
其中z.m之前節點都是已經處理過的,z.m之後節點爲待處理的,
索引爲z.m的節點,表示正在處理,怎麼處理? 首先對其進行走法分析,然後對其產生的所有子佈局
進行編碼或者求其hash值,如果已經在全局字典中則丟棄,否則放入z.z隊列,並放入全局字典


package com.gamelogic;

import com.gamedata.PmBm;
import com.gamedata.PmHx;

public class ZBD {

	public int[][] z;	//隊列,變量命應該爲queue
	PMZB zbj;
	int n;       //隊長度
	int[] hs;//回溯用的指針及棋子
	int[] hss;
	int m,cur;   //隊頭及隊頭內步集遊標,用於廣度搜索
	int max;     //最大隊長
	int[] res;//結果
	int ren;
	 
	private void reset() {
		n=0;
		m=0;
		cur=-1;
		hss[0]=-1;
		ren=0;
	}
	
	public ZBD(int k) {
		zbj = new PMZB();
		z = new int[k][20];
		hs = new int[k*2 + 500];
		hss = new int[k];
		res = new int[k];
		max = k;
		reset();
	}
	//走步出隊
	int zbcd(int[] qiPan) {
		if (cur == -1) {
			zbj.analyze(z[m], zbj);
		}
		cur++;
		if (cur >= zbj.n) {
			m++;
			cur = -1;
			return 1;
		}
		if (hss[m] == zbj.src[cur]) {
			//和上次移動同一個棋子時不搜索,可提速20%左右
			return 1;
		}
		qpcpy(qiPan, z[m]);  //還原當前佈局爲其父佈局 或者 剛剛換層時,i剛剛變化時,把當前層的第一個佈局賦值給當前佈局
		zbj.zb(qiPan, zbj.src[cur], zbj.dst[cur]); //改變當前佈局
		return 0;
	}
	
	//走步入隊
	void zbrd(int[] qiPan) {
		 if (n >= max) {
			 System.out.println("對溢出");
			 return;
		 }
		 qpcpy(z[n], qiPan);	//入隊
		 if (cur >= 0) {
			 hss[n] = zbj.dst[cur];//記錄移動的子(用於回溯)
		 }
		 hs[n++] = m;//記錄回溯點
	}
	
	//參數:層數
	void hui(int cs) {
		int k = cs - 2;
		ren = cs;
		res[cs - 1] = m;
		for (; k >=0; k--) {
			res[k] = hs[res[k+1]]; //回溯
		}
	}
	
	//取第n步盤面
	int[] getre(int n) {
		return z[res[n]];
	}
	private static void qpcpy(int[] q1, int[] q2) {
		for (int i = 0; i < q1.length; i++) {
			q1[i] = q2[i];
		}
	}
	
	//--廣度優先--
	public static int bfs(int[] qiPan, int dep) {
		int i,j,k,bm,v;
		int js = 0;
		int js2 = 0;
		PmBm coder = new PmBm(qiPan);
		int[] JD = new int[coder.bmTotal];//建立節點數組
		ZBD z = new ZBD(coder.bmTotal / 10);
		for (z.zbrd(qiPan), i=1; i <= dep; i++) {
			k = z.n;
//多次執行z.zbcd() 後會產生當前佈局的若干個兒子佈局,對於每個兒子佈局,如果子佈局之前從來沒有出現過,則其編碼放入全局字典,
//並且入隊(獲得了繁衍後代的資格),否則丟棄子佈局
			while (z.m < k) {
				if (z.zbcd(qiPan) == 1) {
					continue;//返回1說明是步集出隊,不是步出隊
				}
				js++;
				if (qiPan[17] == 15 && qiPan[18] == 15) {
					//大王出來了
					z.hui(i);
					for (int h=0; h<z.ren; h++) {
						prt(z.getre(h));	//輸出結果
					}
					prt(qiPan);
					return 1;
				}
				if (i == dep) {
					//到了最後一層可以不再入隊了
					continue;
				}
				bm = coder.Bm(qiPan);
				if (JD[bm] == 0) {
					js2++;	//js搜索總次數計數和js2遍歷的實結點個數
					JD[bm] = 1;
					JD[coder.dcBM(qiPan)] = 1;
					z.zbrd(qiPan);
				}
			}
		}
		return 0;
	}
	//打印棋盤
	static private void prt(int[] qipan) {
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 4; j++) {
				System.out.print(qipan[i*4+j] + "\t");
			}
			System.out.println();
		}
		System.out.println();
	}
	
	public static void test() {
		int[] qp = {
			6, 15, 15, 7,
			6, 15, 15, 7,
			8, 11, 11, 5,
			8, 3, 4, 5,
			2, 0, 0, 1
		};
		int ret = bfs(qp, 200);
		if (ret == 1) {
			
		}
	}
	
	public static int bfs_hx(int[] qiPan, int dep) {
		int all = 0;
		if (dep > 500 || dep <= 0) {
			dep = 200;
		}
		int[] q = new int[20];
		qpcpy(q, qiPan);
		int i,k;
		int js = 0;
		int js2 = 0;
		PmHx hx = new PmHx();
		ZBD worker = new ZBD(50000);
		for (worker.zbrd(q), i=1; i<=dep; i++) {
			//一層一層的搜索
			k = worker.n;
			if (worker.m == k) {
				return -1;
			}
			while (worker.m < k) {
				//廣度優先
				if (worker.zbcd(q) == 1) {
					continue;     //返回1說明是步集出隊,不是步出隊
				}
				js ++; //遍歷總次數計數
				if (q[17] == 5 && q[18] == 5) {
					//大王出來了
					worker.hui(i);
					for (int h = 0; h < worker.ren; h++) {
						prt(worker.getre(h));	//輸出結果
					}
					prt(q);
					return 1;
				}
				
				if (i < dep && hx.check(q) == 1) {
					//js2遍歷的實結點個數,js2不包括起始點,出隊時阻止了回頭步,始節點不可能再遍歷
					js2++;  
					//對稱節點做哈希處理
					hx.check2(q);
					worker.zbrd(q);
				}
			}
		}
		return 0;
	}
	
}


盤面編碼,解碼,以及測試代碼

package game.hrd;

//盤面編碼類
public class PmBm {

    //組合序號表 根據組合數找序號
    short[] Hz;    //橫將
    short[] Sz;       //豎將
    short[] Bz;       //小兵

    //根據序號 找 組合數, 用於解碼
    int[] rHz = new int[792];
    int[] rSz = new int[792]; //C12取5=792
    int[] rBz = new int[16];

    //權值表
    int[] Hw;
    int[] Sw;
    int[] Mw;

    int Sq; //豎將位權,
    int Hq; //橫將位權,
    int Mq; //王位權
    int Hn=0, Bn=0, Sn=0; //各類子數目,大王默認爲1不用計數

    public int bmTotal;

    public PmBm(int[] qiPan) {
        Hz = new short[4096 * 3];
        Sz = new short[4096];
        Bz = new short[128];
        //C12取5=792
        Hw = new int[792*2];
        Sw = new int[792];
        int i,j,k;

        for(i=0;i<20;i++){   //計算各種棋子的個數
            if(LocalConst.U[qiPan[i]]=='B') Bn++;
            if(LocalConst.U[qiPan[i]]=='H') Hn++;
            if(LocalConst.U[qiPan[i]]=='C') Sn++;
        }
        Hn /= 2;
        Sn /= 2;
        int[] WQ2 = LocalConst.WQ2;
        int Hmax=WQ2[11],Smax=WQ2[12-Hn*2],Bmax=WQ2[16-(Hn+Sn)*2]; //各種子的最大二進位數
        short Hx=0,Sx=0,Bx=0; //各種棋子組合的最大序號
        //初始化組合序號表
        for(i=0; i<4096; i++){
            for(j=0,k=0;j<12;j++) {
                if((i & WQ2[j]) > 0) {
                    k++; //計算1的個數
                }
            }
            if(k==Hn && i<Hmax) {
                Hz[i] = Hx;
                rHz[Hx] = i;
                Hx++;
            }
            if(k==Sn && i<Smax) {
                Sz[i]=Sx;
                rSz[Sx] = i;
                Sx++;
            }
            if(k==Bn && i<Bmax) {
                Bz[i]=Bx;
                rBz[Bx] = i;
                Bx++;
            }
        }
        Sq=Bx;
        Hq=Bx*Sx;
        Mq=Bx*Sx*Hx;
        Mw = new int[12];
        Hw = new int[Hx];
        Sw = new int[Sx];
        for(i=0;i<12;i++) Mw[i]=i*Mq; //初始化大王權值表
        for(i=0;i<Hx;i++) Hw[i]=i*Hq; //初始化橫將權值表
        for(i=0;i<Sx;i++) Sw[i]=i*Sq; //初始化豎將權值表
        bmTotal = Mq*12;
    }

    //盤面編碼
    public int Bm(int[] qiPan) {
        int Bb=0,Bd=-1; //空位序號記錄器
        int Sb=0,Sd=-1; //豎條序號記錄器
        int Hb=0,Hd=-1; //橫條序號記錄器
        int Mb = 0;         //大王序號記錄器
        int c,lx;
        int[] f = new int[16];

        for(int i = 0; i < 20; i++){
            c=qiPan[i];
            lx = LocalConst.U[c]; //當前的值
            if(lx=='M') { //大王定序
                if(f[c] == 0) {
                    Mb = i - LocalConst.ROW[i];
                    f[c] = 1;
                }
                continue;
            }
            if (LocalConst.COL[i]<3 && LocalConst.U[qiPan[i+1]] <= 'H') {
                Hd++; //橫條位置序號(編號)
            }
            if (lx == 'H') {//橫將定序,轉爲二進制進行詢址得Hb
                if(f[c] == 0) {
                    Hb += LocalConst.WQ2[Hd];
                    f[c]=1;
                }
                continue;
            }
            if (LocalConst.ROW[i]<4 && LocalConst.U[qiPan[i+4]]<='C') {
                Sd++; //豎將位置序號(編號)
            }
            if (lx=='C') { //豎條定序,轉爲二進制進行詢址得Sb
                if(f[c] == 0) {
                    Sb += LocalConst.WQ2[Sd];
                    f[c]=1;
                }
                continue;
            }
            if(lx<='B') Bd++;  //小兵位置序號(編號)
            if(lx=='B') Bb += LocalConst.WQ2[Bd]; //小兵定序,轉爲二進制進行詢址得Bb
        }
        //Hb,Sb,Bb爲組合序號,"橫刀立馬"最大值爲小兵C(6,4)-1=15-1,豎條C(10,4)-1=210-1
        Bb=Bz[Bb];
        Sb=Sz[Sb];
        Hb=Hz[Hb];//詢址後得得Bb,Sb,Hb組合序號
        return Bb+Sw[Sb]+Hw[Hb]+Mw[Mb]; //用位權編碼,其中Bb的位權爲1
    }

    //按左右對稱規則考查棋盤,對其編碼
    public int dcBM(int[] q){
        char i;
        int[] q2 = new int[20];
        for(i=0; i<20; i+=4) {
            q2[i]=q[i+3];
            q2[i+1]=q[i+2];
            q2[i+2]=q[i+1];
            q2[i+3]=q[i];
        }
        return Bm(q2);
    }

    public int[] decode(int code) {
        int mb = code / Mq;
        int left = code % Mq;
        int hbCombo = rHz[left / Hq];
        left = left % Hq;
        int sbCombo = rSz[left / Sq];
        left = left % Sq;
        int bCombo = rBz[left];
        int[] ret = new int[20];
        ret[mb] = ret[mb + 1] = 5;
        ret[mb + 4] = ret[mb + 5] = 1;
        int[] WQ = LocalConst.WQ2;

        int[] hArr = new int[Hn];
        int a = 0;
        for (int i = 0; i < WQ.length; i++) {
            if ( (hbCombo & WQ[i]) > 0) {
                int count = 0;
                for (int j = 0; j < ret.length; j++) {
                    if (LocalConst.COL[j] < 3 &&
                            ret[j] == 0 && ret[j+1] == 0) {
                        if (count == i) {
                            hArr[a] = j;
                            a++;
                            break;
                        }
                        count++;
                    }
                }
            }
        }
        for (int i = 0; i < hArr.length; i++) {
            int loc = hArr[i];
            ret[loc] = ret[loc+1] = 4;
        }

        int[] sArr = new int[Sn];
        a = 0;
        for (int i = 0; i < WQ.length; i++) {
            if ( (sbCombo & WQ[i]) > 0) {
                int count = 0;
                for (int j = 0; j < ret.length; j++) {
                    if (LocalConst.ROW[j] < 4 &&
                            ret[j] == 0 && ret[j+4] == 0) {
                        if (count == i) {
                            sArr[a] = j;
                            a++;
                            break;
                        }
                        count++;
                    }
                }
            }
        }
        for (int i = 0; i < sArr.length; i++) {
            int loc = sArr[i];
            ret[loc] = 3;
            ret[loc+4] = 1;
        }

        int[] bArr = new int[Bn];
        a=0;
        for (int i = 0; i < WQ.length; i++) {
            if ( (bCombo & WQ[i]) > 0) {
                int count = 0;
                for (int j = 0; j < ret.length; j++) {
                    if (ret[j] == 0) {
                        if (count == i) {
                            bArr[a]=j;
                            a++;
                            break;
                        }
                        count++;
                    }
                }
            }
        }
        for (int i = 0; i < bArr.length; i++) {
            int loc = bArr[i];
            ret[loc] = 2;
        }

        return ret;
    }
}

測試代碼

import game.hrd.LocalConst;
import game.hrd.PmBm;
import game.hrd.ZBD;
import game.hrd.game.hrd.refactor.HrdUtil;
import game.hrd.game.hrd.refactor.LayoutEnumerator;
import game.hrd.game.hrd.refactor.LayoutToCode;
import game.hrd.game.hrd.refactor.Solver;

/**
 * Created by huangcongjie on 2017/12/17.
 */
public class HuaRongDao {
    public static void main(String args[]) {
        int[] qp = {
                6, 15, 15, 4,
                6, 15, 15, 3,
                8, 11, 11, 5,
                8, 12, 12, 5,
                2, 0,   0, 1
        };
        PmBm coder = new PmBm(qp);
        int[] qp2 = {
                6, 15, 15, 5,
                6, 15, 15, 5,
                8, 11, 11, 0,
                8, 3,   4, 2,
                12, 12, 1, 0
        };
        int code = coder.Bm(qp2);
        int[] decoded = coder.decode(code);
        printLayout(decoded);
    }

    static public void printLayout(int[] layout) {
        String layoutStr = "";
        for (int i = 0; i < 20; i++) {
            layoutStr += layout[i] + "\t";
            if (i % 4 == 3) {
                layoutStr += "\n";
            }
        }
//        layoutStr = layoutStr.substring(0, layoutStr.length() - 1);
        System.out.print(layoutStr);
    }
}



                      ┌────┐
                      │root│
                      └────┘
         ┌──────────────┼──────────┐
         a1             a2         a3
    ┌────┴────┐         │       ┌──┴──┐
   b1         b2        c1      d1    d2
┌──┴──┐   ┌───┼───┐  ┌──┴──┐ 
e1    e2 f1  f2  f3  g1    g2


                                      ┌────┐
                                      │root│
                                      └────┘
               ┌────────────────────────┼────────────┐
              ┌──┐                     ┌──┐         ┌──┐
              │a1│                     │a2│         │a3│
              └──┘                     └──┘         └──┘
      ┌────────┴───────┐                │         ┌──┴──┐
    ┌──┐              ┌──┐             ┌──┐      ┌──┐  ┌──┐ 
    │b1│              │b2│             │c1│      │d1│  │d2│
    └──┘              └──┘             └──┘      └──┘  └──┘
  ┌──┴───┐     ┌───────┼──────┐      ┌──┴──┐ 
┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐
│e1│   │e2│   │f1│   │f2│   │f3│   │g1│   │g2│
└──┘   └──┘   └──┘   └──┘   └──┘   └──┘   └──┘


<!DOCTYPE html>
<html>
<head>
    <title>jQuery UI</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    
    <!--    jQuery UI font sizes are relative to document's,
            so set a base size here. -->
    <style type="text/css">
        body {
            font-size:12px;
            font-family:sans-serif
        }
    </style>
    
    <!-- Load the jQuery UI style sheet. -->
    <!-- href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/themes/start/jquery-ui.css" -->
    <link rel="stylesheet"
        href="https://code.jquery.com/ui/1.12.1/themes/start/jquery-ui.css"
        type="text/css" media="all" />
    
    <!-- Load jQuery. -->
    <script src="https://code.jquery.com/jquery-1.12.4.js"
        type="text/javascript"></script> 
    
    <!-- Load jQuery UI. -->
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"
        type="text/javascript"></script> 
    
   
    <script>
        // On DOM loaded, initialize a date picker widget on the input element
        // with id of 'datepicker'.
        $(function() {
            $( "#datepicker" ).datepicker();
        });
	</script>
        
</head>
<body>
    <!-- The following input element will be turned into a date picker -->
    <p>Enter Date: <input type="text" id="datepicker"></p>
</body>
</html>



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