八數碼 A*算法 和 數據結構

        沒有辦法,迫於興趣,只好對以前寫過的八數碼問題進行重寫,這次重寫(A*依舊使用原來的估價方式估價)着重考慮 數據結構 的, 來提高執行效率。 

               通過測試,現在的八數碼,執行效率還不錯。  在此,把代碼提供出來,一起學習~~ 不過,我把其寫在同一個.cpp中(雖然這麼做,不合理,但是我還是這麼做了~) 該程序中:1:採用棋盤壓縮   2:hash表判重  3: 路徑記錄通過hashtable中回溯   4:優先隊列 用堆實現

       源代碼如下(代碼調試運行平臺爲:Microsoft Office 提供的 VC++6.0 編譯器):

#include <stdio.h>
#include 
<memory.h>
#include 
<windows.h>
#define HashTableSize 260441 // 562147 460387 400249 360323 260441  
#define HeapSize      5000
#define MaxSteps      100
  
 
/**//*** 經過從  1 2 3 4 5 6 7 8 0 到  0 8 7 6 5 4 3 2 1 枚舉測試,
      各個素數(HashTableSize) 的 hash 查詢次數和表使用率如下: 

            素數      平均查詢次數      使用率
           562147        1.22            32.3%
           460387        1.28            39.4%
           400249        1.36            45.3%
           360323        1.41            50.4%
           260441        1.75            70.0%
*/


/**//***
   這個棋盤採用4個字節表示。 
   其中 空格, 以及對應的 1~ 8 的數字, 用三個位表示, 然後 用四個位表示 空格的位置
   (這共花了 3 * 9 + 4  = 31 個位 )。
   注意棋盤是按 從上到下,從左到右, 依次標上編號:

                --------------
               | 00 | 01 | 02 |
                --------------
               | 03 | 04 | 05 |
                --------------
               | 06 | 07 | 08 |
                --------------
   
   棋盤表示法:
   000 : 1  , 001 : 2 , 010 : 3  
   011 : 4  , 100 : 5 , 101 : 6
   110 : 7  , 111 : 8 , 000 : 空格.
   注意: 有的這裏 空格的位置 和 數碼 1 的編碼是一樣,這並不矛盾, 因爲實際上 空格編碼可以刪除,
   因爲 後面要用 4 個位記錄空格位置。 那麼爲什麼還給 空格編碼呢? 這主要是對壓縮棋盤的上下左右
   移動更加方便。
   例如: 在 對下 棋盤:
       6 8 3 
       0 5 2
       4 7 1
   那麼對應的Reduce_Map 編碼是: 101  111  010  000  100  001  011  110  000   0011    0 
                                 ---  ---  ---  ---  ---  ---  ---  ---  ---   ----   ---
                                  6    8    3   空格  5    2    4    7    1      3   標誌位
       
*/

/**//*** 棋盤數據結構 ****/

typedef 
struct reduce...{
public:
    unsigned  a00: 
3  ,  a01: 3 ,  a02: 3 ;
    unsigned  a03: 
3  ,  a04: 3 ,  a05: 3 ;
    unsigned  a06: 
3  ,  a07: 3 ,  a08: 3 ;
    unsigned  empty: 
4 ;   //  記錄 空格的位置.
    unsigned  used:  1 ;  // 在 HashTable 元素中該位用來表示,是否已經佔了。
    reduce():used(0)...{}
    
bool operator == (const reduce&  a);
}
Reduce_Map ;  

/**//**棋盤的表示**/
typedef 
struct ...{
public:
    unsigned __int8 Board[
9];
    unsigned __int8 empty ; 
    
void print()
    
...{
        
for(int i =0 ; i < 9 ; i ++ )
        
...{
            
if(i%3 == 0 ) printf(" "); 
            printf(
"%5d",Board[i]);
        }

    }

}
Map ; 

/**//*****搜索的節點的表示******/
struct Nodes...{
public:
    Reduce_Map         RMap ;
    unsigned __int8       gx ;
    unsigned __int8       fx ;
//    unsigned short    hx ;   //  hx = fx - gx ;  
    unsigned int       parent ;   // 父節點, 在hash表中的位置
    unsigned int       index ;    // 該節點在hashtable中的位置
    Nodes():gx(0),fx(0)...{}
    
void Evaluate()...{}
}
;

/**//***棧:用來記錄路徑**/
struct stacks...{
public:
     __int8  length ; 
     Reduce_Map  path[MaxSteps];

     stacks():length(
-1)...{}
     
void       push(Reduce_Map& temp)...{  path[++length] = temp; }
     Reduce_Map pop()
...return path[length--] ; }
}
Stack;

/**//****hash表****/
struct hashtable...{
    Reduce_Map status ; 
    
int PreIndex ;                  // 用來記錄 該節點的父節點在HashTable中對應的位置。
}
HashTable[HashTableSize];

/**//****優先隊列******/
struct PriorityQueue
...{
public:
     Nodes            Heap[HeapSize];    
// 堆數組
     int              bear ;             // 堆長度

     PriorityQueue():bear(
-1)...{}
     Nodes pop();
     
void  push(Nodes);
     
void  print();
     
bool  empty()...return (bear== -1);}
}
Queue;

void inline swap(Nodes&  , Nodes&);
bool inline compare(const Nodes& , const Nodes&);

/**//*** 重載 == 運算符*****/
bool reduce::operator ==(const reduce& a)
...{
     
static Reduce_Map temp ; 
     temp 
= a ; 
     temp.used 
= this->used ; 
     
if( memcmp( this , &temp , 4 ) == 0  ) return true ;
     
return false ; 
}


void PriorityQueue::push(Nodes temp)
...{
      Heap[
++bear] = temp ; 
      
int k = bear, p ; 
      
while(k)
      
...{
          p 
= (k - 1 )>>1;
          
if( compare( Heap[k],Heap[p]) ) return ; 
          swap(Heap[k],Heap[p]);
          k 
= p ;
      }

      
return ;
}


Nodes PriorityQueue::pop()
...{
      
int flage , k = 0 , p ; 
      swap(Heap[
0],Heap[bear--]);
      
while( ( p = ( k << 1 ) ) < bear )
      
...{
          flage 
= 1
          
if( ( (p + 1< bear ) && compare( Heap[ p + 1 ] , Heap[ p + 2 ] ) ) flage ++ ;
          
if(compare( Heap[ p + flage ] , Heap[k] ) ) break ;
          swap(Heap[ p 
+ flage] , Heap[k]);
          k 
= p + flage ;
      }

      
return Heap[bear+1];
}


void PriorityQueue::print()
...{
    
for(int i =0 ; i <= bear; i ++ )
        printf(
" fx:%d hx:%d ",Heap[i].fx , Heap[i].fx-Heap[i].gx);
    printf(
" ");
    getchar();
}



/**//**方格中不同位置之間的距離**/
static unsigned __int8 Steps[9][9]=...{
    
...0 , 1 , 2 , 1 , 2 , 3 , 2 , 3 , 4 } ,
    
...1 , 0 , 1 , 2 , 1 , 2 , 3 , 2 , 3 } ,
    
...2 , 1 , 0 , 3 , 2 , 1 , 4 , 3 , 2 } , 
    
...1 , 2 , 3 , 0 , 1 , 2 , 1 , 2 , 3 } ,
    
...2 , 1 , 2 , 1 , 0 , 1 , 2 , 1 , 2 } , 
    
...3 , 2 , 1 , 2 , 1 , 0 , 3 , 2 , 1 } ,
    
...2 , 3 , 4 , 1 , 2 , 3 , 0 , 1 , 2 } , 
    
...3 , 2 , 3 , 2 , 1 , 2 , 1 , 0 , 1 } ,
    
...4 , 3 , 2 , 3 , 2 , 1 , 2 , 1 , 0 }
}
;

/**//**兩中目標狀態***/
static unsigned __int8 DlTarget[2][8]=...{
    
...0 , 1 , 2 , 3 , 4 , 5 , 6 , 7  },
    
...0 , 1 , 2 , 5 , 8 , 7 , 6 , 3  }  
}
;

static __int8    Derection[4= ...1 , -1 , 3 , -3 } ;
//static char   DeToChar[4] =  {'r', 'l','d', 'u'} ;

/**//******目標狀態********/
static unsigned __int8 Target[8] ;
 

bool inline compare(const Nodes& Nodes1, const Nodes &Nodes2)
...{
    
if(Nodes1.fx  > Nodes2.fx) return true ; 
    
if(Nodes1.fx  < Nodes2.fx) return false
    
return(Nodes1.gx  < Nodes2.gx); 
}


void inline swap(Nodes& a , Nodes&b)
...{
    Nodes c 
= b ; 
    b 
= a ; 
    a 
= c ;  
}


 
/**//*** 棋盤的壓縮**/

inline 
void ReduceTheMap(Map& IMap , Reduce_Map& RMap )
...{
     IMap.Board[IMap.empty] 
++ ; 
                      RMap.a00 
= IMap.Board[0- 1 , RMap.a01 = IMap.Board[1- 1 , RMap.a02 = IMap.Board[2- 1
     RMap.a03 
= IMap.Board[3- 1 , RMap.a04 = IMap.Board[4- 1 , RMap.a05 = IMap.Board[5- 1;
     RMap.a06 
= IMap.Board[6- 1 , RMap.a07 = IMap.Board[7- 1 , RMap.a08 = IMap.Board[8- 1;
     RMap.empty 
= IMap.empty ;
     IMap.Board[IMap.empty] 
-- ;
     
return ;
}


/**//*** 棋盤的解壓**/
inline 
void UnreduceMap(Map& IMap ,  Reduce_Map& RMap)
...{
          IMap.Board[
0= RMap.a00 + 1 , IMap.Board[1= RMap.a01 + 1 , IMap.Board[2= RMap.a02 + 1 ;
          IMap.Board[
3= RMap.a03 + 1 , IMap.Board[4= RMap.a04 + 1 , IMap.Board[5= RMap.a05 + 1 ;
          IMap.Board[
6= RMap.a06 + 1 , IMap.Board[7= RMap.a07 + 1 , IMap.Board[8= RMap.a08 + 1 ;
          IMap.empty 
= RMap.empty ;
          IMap.Board[IMap.empty] 
-- ; 
          
return ; 
}


/**//*** 判斷是否查找過了(使用雙散列函數探測法),
     函數返回該節點在hashtable中的位置,
     如果返回值爲:-1 表示已經存在
*/

int InsetHashTable(  Reduce_Map RMap , int PreIndex = -1 ) 
...{
    unsigned 
int Code;
    __asm                     
// 彙編實現同字節不同類型值的賦值:  Code = RMap 
    ...{
        mov eax,dword ptr[RMap]
        mov dword ptr[Code] ,eax
    }

    Code 
%= HashTableSize;

    
if(HashTable[Code].status.used == 0 ) // 如果used 爲 0 ,表示 該處未被佔用 
    ...
         HashTable[Code].status 
= RMap ; 
         HashTable[Code].status.used 
= 1 ;
         HashTable[Code].PreIndex 
= PreIndex ; // 記錄父節點在hashtable中的位置,便於回溯找路徑
         return Code ; 
    }

    
if( HashTable[Code].status == RMap )  return -1 ; 
    
    unsigned 
int temp = Code | RMap.empty;   // 經過測試,這樣簡單操作後,使得二次探測碰撞減少了些~~

    
while(HashTable[Code].status.used == 1 )...{
         Code 
+= temp ;
         
if(Code >= HashTableSize) Code -= HashTableSize ;
         
if( HashTable[Code].status == RMap ) return -1 ; 
    }

    HashTable[Code].status 
= RMap ; 
                     HashTable[Code].status.used 
= 1 ; 
    HashTable[Code].PreIndex 
= PreIndex ; 
    
return Code ; 
}


/**//***對 hx 的估價(使用的是: 每個數碼到目標位置的距離 之和 ) ***/
inline 
void Evaluate(Nodes& one)
...{
           one.fx 
= - Steps[ one.RMap.empty ][ Target[ 0 ] ] ; 
           one.fx 
+= Steps[ 0 ][ Target[ one.RMap.a00 ] ] + Steps[ 1 ][ Target[ one.RMap.a01 ] ] +
           Steps[ 
2 ][ Target[ one.RMap.a02 ] ] + Steps[ 3 ][ Target[ one.RMap.a03 ] ] +
           Steps[ 
4 ][ Target[ one.RMap.a04 ] ] + Steps[ 5 ][ Target[ one.RMap.a05 ] ] +
           Steps[ 
6 ][ Target[ one.RMap.a06 ] ] + Steps[ 7 ][ Target[ one.RMap.a07 ] ] +
           Steps[ 
8 ][ Target[ one.RMap.a08 ] ] ;
           one.fx 
+= one.gx ; 
}


/**//****回溯找出路徑***/
void FindFinalPath(unsigned int& ObjIndex )
...{
           
int temp = ObjIndex ; 
           
while(temp != -1 )
    
...{
                Stack.push(HashTable[temp].status);
                temp 
= HashTable[temp].PreIndex;
            }

    
return ;
}


void search(Nodes begin )...
    Nodes one , now ; 
    
int temp , flage ; 
    Queue.push(begin);
    
while!Queue.empty() )...{
        now 
= Queue.pop();
        
if(now.gx == now.fx ) 
        
...{
            FindFinalPath(now.index); 
            
return ;
        }

        
for(int k =0 ; k < 4; k ++ )
        
...{
            temp 
= now.RMap.empty + Derection[k] ; 
            
if(temp >=0 && temp< 9 && Steps[temp][now.RMap.empty] == 1 ) 
            
...
                one.RMap 
= now.RMap ;
                one.RMap.empty 
= temp;
                __asm                        
// 由於這裏RMap 被定義爲struct 類型, 因此移動很不方便,所以只好採用彙編了
                ...{
                    ; 把one.RMap.empty 空格的位置取出(即移動後 空格所在位置)
                    mov    ecx , dword ptr[one.RMap]
                    shr    ecx , 
27       ; 0000001bH
                    ;and    ecx , 
15       ; 0000000fH

                    ; 把one.RMap中的要移動的數碼取出,並把該位清零
----“空格”~
                    imul   ecx , 
3
                    mov    ebx , 
7 
                    shl    ebx , cl
                    mov    eax , ebx
                    and    ebx , dword ptr[one.RMap]   ; 把數碼取出放在 ebx 中
                    shr    ebx , cl 

                    not    eax
                    and    dword ptr[one.RMap],  eax   ; 把該位清零
----“空格”操作

                    ; 把now.RMap.empty 空格的位置取出(即移動前,空格所在位置)
                    mov    ecx , dword ptr[now.RMap]
                    shr    ecx , 
27       ; 0000001bH
                    ;and    ecx , 
15       ; 0000000fH

                    ; 把取出的數碼移到空格位置上去
                    imul   ecx , 
3
                    shl    ebx , cl
                    or     dword ptr[one.RMap] , ebx 
                }

                flage 
= InsetHashTable( one.RMap , now.index );
                                    
if( flage >= 0 ) 
                
...{
                    one.gx 
= now.gx +1 ;
                    Evaluate(one);
                    one.parent 
= now.index ; 
                    one.index 
= flage ; 
                                                       Queue.push(one);
                }

            }

        }

    }

}


void input(Nodes& begin)
...{
    
int i ,j ; 
    
int sum =0 , k ; 
    Map temp ; 
    printf(
"請輸入相應八數碼的位置: ");
                     
for(i =0 ;i  < 9 ; i ++ )...{
        scanf(
"%I8d",&temp.Board[i]);
        
if(temp.Board[i] == 0 ) 
        
...{
            k 
= i ; 
            
continue;
        }

        
for(j =0 ; j< i ; j ++ )
            
if(temp.Board[j] > temp.Board[i]) sum ++ ; 
    }

    temp.empty 
= k ; 

    ReduceTheMap(temp,begin.RMap);

    
if(sum%2) memcpy(Target,DlTarget[1],8);
    
else memcpy(Target,DlTarget[0],8);

    Evaluate(begin);
    begin.index 
= InsetHashTable(begin.RMap);
    
return ;
}


void output()...{
   
if(Stack.length <= 0 ) printf("已經是目標狀態了,你這不是自找麻煩嘛! ");
   
else 
   
...{
        Map temp ;
        printf(
"搞定!  供需 %d 步 , 步驟如下: ",Stack.length );
        
for(int i =Stack.length ; i >= 0 ; i --  )
        
...{
            UnreduceMap( temp , Stack.pop());
            temp.print();
            getchar();
            
if(i) printf(" ----> ");
        }

   }

   printf(
" ");
   
return ;
}


int main()
...{
     Nodes begin ; 
     input(begin);
     
long time = GetTickCount();
     search(begin);
     printf(
"計算耗時:%dMS ",GetTickCount()-time);
     output();
     
return 0 ; 
}

 

運行效果爲:

  

 爲了便於複製代碼(因此便有下面的了):

#include <stdio.h>
#include <memory.h>
#include <windows.h>
#define HashTableSize 260441 // 562147 460387 400249 360323 260441 
#define HeapSize      5000
#define MaxSteps      100
 
 /*** 經過從  1 2 3 4 5 6 7 8 0 到  0 8 7 6 5 4 3 2 1 枚舉測試,
      各個素數(HashTableSize) 的 hash 查詢次數和表使用率如下:

         素數      平均查詢次數      使用率
        562147        1.22            32.3%
     460387        1.28            39.4%
     400249        1.36            45.3%
     360323        1.41            50.4%
     260441        1.75            70.0%
*/

/***
   這個棋盤採用4個字節表示。
   其中 空格, 以及對應的 1~ 8 的數字, 用三個位表示, 然後 用四個位表示 空格的位置
   (這共花了 3 * 9 + 4  = 31 個位 )。
   注意棋盤是按 從上到下,從左到右, 依次標上編號:

                --------------
               | 00 | 01 | 02 |
                --------------
               | 03 | 04 | 05 |
                --------------
               | 06 | 07 | 08 |
                --------------
  
   棋盤表示法:
   000 : 1  , 001 : 2 , 010 : 3 
   011 : 4  , 100 : 5 , 101 : 6
   110 : 7  , 111 : 8 , 000 : 空格.
   注意: 有的這裏 空格的位置 和 數碼 1 的編碼是一樣,這並不矛盾, 因爲實際上 空格編碼可以刪除,
   因爲 後面要用 4 個位記錄空格位置。 那麼爲什麼還給 空格編碼呢? 這主要是對壓縮棋盤的上下左右
   移動更加方便。
   例如: 在 對下 棋盤:
       6 8 3
    0 5 2
    4 7 1
   那麼對應的Reduce_Map 編碼是: 101  111  010  000  100  001  011  110  000   0011    0
                                 ---  ---  ---  ---  ---  ---  ---  ---  ---   ----   ---
          6    8    3   空格  5    2    4    7    1      3   標誌位
      
*/
/*** 棋盤數據結構 ****/

typedef struct reduce{
public:
    unsigned  a00: 3  ,  a01: 3 ,  a02: 3 ;
 unsigned  a03: 3  ,  a04: 3 ,  a05: 3 ;
 unsigned  a06: 3  ,  a07: 3 ,  a08: 3 ;
 unsigned  empty: 4 ;   //  記錄 空格的位置.
 unsigned  used:  1 ;  // 在 HashTable 元素中該位用來表示,是否已經佔了。
 reduce():used(0){}
 bool operator == (const reduce&  a);
}Reduce_Map ; 

/**棋盤的表示**/
typedef struct {
public:
    unsigned __int8 Board[9];
 unsigned __int8 empty ;
 void print()
 {
  for(int i =0 ; i < 9 ; i ++ )
  {
   if(i%3 == 0 ) printf("/n");
   printf("%5d",Board[i]);
  }
 }
}Map ;

/*****搜索的節點的表示******/
struct Nodes{
public:
    Reduce_Map         RMap ;
 unsigned __int8       gx ;
 unsigned __int8       fx ;
// unsigned short    hx ;   //  hx = fx - gx ; 
 unsigned int       parent ;   // 父節點, 在hash表中的位置
 unsigned int       index ;    // 該節點在hashtable中的位置
 Nodes():gx(0),fx(0){}
 void Evaluate(){}
};

/***棧:用來記錄路徑**/
struct stacks{
public:
     __int8  length ;
  Reduce_Map  path[MaxSteps];

  stacks():length(-1){}
  void       push(Reduce_Map& temp){  path[++length] = temp; }
  Reduce_Map pop(){ return path[length--] ; }
}Stack;

/****hash表****/
struct hashtable{
 Reduce_Map status ;
 int PreIndex ;                  // 用來記錄 該節點的父節點在HashTable中對應的位置。
}HashTable[HashTableSize];

/****優先隊列******/
struct PriorityQueue
{
public:
     Nodes            Heap[HeapSize];    // 堆數組
  int              bear ;             // 堆長度

     PriorityQueue():bear(-1){}
  Nodes pop();
  void  push(Nodes);
  void  print();
  bool  empty(){ return (bear== -1);}
}Queue;

void inline swap(Nodes&  , Nodes&);
bool inline compare(const Nodes& , const Nodes&);

/*** 重載 == 運算符*****/
bool reduce::operator ==(const reduce& a)
{
  static Reduce_Map temp ;
  temp = a ;
  temp.used = this->used ;
     if( memcmp( this , &temp , 4 ) == 0  ) return true ;
  return false ;
}

void PriorityQueue::push(Nodes temp)
{
      Heap[++bear] = temp ;
      int k = bear, p ;
   while(k)
   {
    p = (k - 1 )>>1;
    if( compare( Heap[k],Heap[p]) ) return ;
    swap(Heap[k],Heap[p]);
    k = p ;
   }
   return ;
}

Nodes PriorityQueue::pop()
{
   int flage , k = 0 , p ;
      swap(Heap[0],Heap[bear--]);
      while( ( p = ( k << 1 ) ) < bear )
   {
    flage = 1;
    if( ( (p + 1) < bear ) && compare( Heap[ p + 1 ] , Heap[ p + 2 ] ) ) flage ++ ;
    if(compare( Heap[ p + flage ] , Heap[k] ) ) break ;
    swap(Heap[ p + flage] , Heap[k]);
    k = p + flage ;
   }
   return Heap[bear+1];
}

void PriorityQueue::print()
{
    for(int i =0 ; i <= bear; i ++ )
  printf("/tfx:%d hx:%d/t/n",Heap[i].fx , Heap[i].fx-Heap[i].gx);
 printf("/n");
 getchar();
}


/**方格中不同位置之間的距離**/
static unsigned __int8 Steps[9][9]={
    { 0 , 1 , 2 , 1 , 2 , 3 , 2 , 3 , 4 } ,
 { 1 , 0 , 1 , 2 , 1 , 2 , 3 , 2 , 3 } ,
 { 2 , 1 , 0 , 3 , 2 , 1 , 4 , 3 , 2 } ,
 { 1 , 2 , 3 , 0 , 1 , 2 , 1 , 2 , 3 } ,
 { 2 , 1 , 2 , 1 , 0 , 1 , 2 , 1 , 2 } ,
 { 3 , 2 , 1 , 2 , 1 , 0 , 3 , 2 , 1 } ,
 { 2 , 3 , 4 , 1 , 2 , 3 , 0 , 1 , 2 } ,
 { 3 , 2 , 3 , 2 , 1 , 2 , 1 , 0 , 1 } ,
 { 4 , 3 , 2 , 3 , 2 , 1 , 2 , 1 , 0 }
};

/**兩中目標狀態***/
static unsigned __int8 DlTarget[2][8]={
    { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7  },
 { 0 , 1 , 2 , 5 , 8 , 7 , 6 , 3  } 
};

static __int8    Derection[4] = { 1 , -1 , 3 , -3 } ;
//static char   DeToChar[4] =  {'r', 'l','d', 'u'} ;

/******目標狀態********/
static unsigned __int8 Target[8] ;
 

bool inline compare(const Nodes& Nodes1, const Nodes &Nodes2)
{
    if(Nodes1.fx  > Nodes2.fx) return true ;
    if(Nodes1.fx  < Nodes2.fx) return false;
 return(Nodes1.gx  < Nodes2.gx);
}

void inline swap(Nodes& a , Nodes&b)
{
 Nodes c = b ;
 b = a ;
 a = c ; 
}

 /*** 棋盤的壓縮**/

inline void ReduceTheMap(Map& IMap , Reduce_Map& RMap )
{
  IMap.Board[IMap.empty] ++ ;
     RMap.a00 = IMap.Board[0] - 1 , RMap.a01 = IMap.Board[1] - 1 , RMap.a02 = IMap.Board[2] - 1;
  RMap.a03 = IMap.Board[3] - 1 , RMap.a04 = IMap.Board[4] - 1 , RMap.a05 = IMap.Board[5] - 1;
  RMap.a06 = IMap.Board[6] - 1 , RMap.a07 = IMap.Board[7] - 1 , RMap.a08 = IMap.Board[8] - 1;
  RMap.empty = IMap.empty ;
  IMap.Board[IMap.empty] -- ;
  return ;
}

/*** 棋盤的解壓**/
inline void UnreduceMap(Map& IMap ,  Reduce_Map& RMap)
{
     IMap.Board[0] = RMap.a00 + 1 , IMap.Board[1] = RMap.a01 + 1 , IMap.Board[2] = RMap.a02 + 1 ;
     IMap.Board[3] = RMap.a03 + 1 , IMap.Board[4] = RMap.a04 + 1 , IMap.Board[5] = RMap.a05 + 1 ;
  IMap.Board[6] = RMap.a06 + 1 , IMap.Board[7] = RMap.a07 + 1 , IMap.Board[8] = RMap.a08 + 1 ;
  IMap.empty = RMap.empty ;
  IMap.Board[IMap.empty] -- ;
  return ;
}

/*** 判斷是否查找過了(使用雙散列函數探測法),
     函數返回該節點在hashtable中的位置,
     如果返回值爲:-1 表示已經存在
*/
int InsetHashTable(  Reduce_Map RMap , int PreIndex = -1 )
{
    unsigned int Code;
 __asm                     // 彙編實現同字節不同類型值的賦值:  Code = RMap
 {
  mov eax,dword ptr[RMap]
  mov dword ptr[Code] ,eax
 }
 Code %= HashTableSize;

 if(HashTable[Code].status.used == 0 ) // 如果used 爲 0 ,表示 該處未被佔用
 {
   HashTable[Code].status = RMap ;
      HashTable[Code].status.used = 1 ;
   HashTable[Code].PreIndex = PreIndex ; // 記錄父節點在hashtable中的位置,便於回溯找路徑
   return Code ;
 }
 if( HashTable[Code].status == RMap )  return -1 ;
 
    unsigned int temp = Code | RMap.empty;   // 經過測試,這樣簡單操作後,使得二次探測碰撞減少了些~~

 while(HashTable[Code].status.used == 1 ){
   Code += temp ;
   if(Code >= HashTableSize) Code -= HashTableSize ;
   if( HashTable[Code].status == RMap ) return -1 ;
 }
 HashTable[Code].status = RMap ;
    HashTable[Code].status.used = 1 ;
 HashTable[Code].PreIndex = PreIndex ;
 return Code ;
}

/***對 hx 的估價(使用的是: 每個數碼到目標位置的距離 之和 ) ***/
inline void Evaluate(Nodes& one)
{
    one.fx = - Steps[ one.RMap.empty ][ Target[ 0 ] ] ;
 one.fx += Steps[ 0 ][ Target[ one.RMap.a00 ] ] + Steps[ 1 ][ Target[ one.RMap.a01 ] ] +
        Steps[ 2 ][ Target[ one.RMap.a02 ] ] + Steps[ 3 ][ Target[ one.RMap.a03 ] ] +
        Steps[ 4 ][ Target[ one.RMap.a04 ] ] + Steps[ 5 ][ Target[ one.RMap.a05 ] ] +
        Steps[ 6 ][ Target[ one.RMap.a06 ] ] + Steps[ 7 ][ Target[ one.RMap.a07 ] ] +
        Steps[ 8 ][ Target[ one.RMap.a08 ] ] ;
 one.fx += one.gx ;
}

/****回溯找出路徑***/
void FindFinalPath(unsigned int& ObjIndex )
{
 int temp = ObjIndex ;
 while(temp != -1 )
 {
  Stack.push(HashTable[temp].status);
  temp = HashTable[temp].PreIndex;
 }
 return ;
}

void search(Nodes begin ){
    Nodes one , now ;
 int temp , flage ;
 Queue.push(begin);
 while( !Queue.empty() ){
  now = Queue.pop();
  if(now.gx == now.fx )
  {
   FindFinalPath(now.index);
   return ;
  }
  for(int k =0 ; k < 4; k ++ )
  {
      temp = now.RMap.empty + Derection[k] ;
   if(temp >=0 && temp< 9 && Steps[temp][now.RMap.empty] == 1 )
   {
       one.RMap = now.RMap ;
       one.RMap.empty = temp;
       __asm                        // 由於這裏RMap 被定義爲struct 類型, 因此移動很不方便,所以只好採用彙編了
    {
        ; 把one.RMap.empty 空格的位置取出(即移動後 空格所在位置)
        mov    ecx , dword ptr[one.RMap]
        shr    ecx , 27       ; 0000001bH
        ;and    ecx , 15       ; 0000000fH

        ; 把one.RMap中的要移動的數碼取出,並把該位清零----“空格”~
                    imul   ecx , 3
        mov    ebx , 7
        shl    ebx , cl
                    mov    eax , ebx
        and    ebx , dword ptr[one.RMap]   ; 把數碼取出放在 ebx 中
        shr    ebx , cl

        not    eax
        and    dword ptr[one.RMap],  eax   ; 把該位清零----“空格”操作

        ; 把now.RMap.empty 空格的位置取出(即移動前,空格所在位置)
        mov    ecx , dword ptr[now.RMap]
        shr    ecx , 27       ; 0000001bH
        ;and    ecx , 15       ; 0000000fH

        ; 把取出的數碼移到空格位置上去
        imul   ecx , 3
        shl    ebx , cl
        or     dword ptr[one.RMap] , ebx
    }
    flage = InsetHashTable( one.RMap , now.index );
       if( flage >= 0 )
    {
     one.gx = now.gx +1 ;
     Evaluate(one);
     one.parent = now.index ;
     one.index = flage ;
           Queue.push(one);
    }
   }
  }
 }
}

void input(Nodes& begin)
{
 int i ,j ;
 int sum =0 , k ;
 Map temp ;
 printf("請輸入相應八數碼的位置:/n");
    for(i =0 ;i  < 9 ; i ++ ){
  scanf("%I8d",&temp.Board[i]);
  if(temp.Board[i] == 0 )
  {
   k = i ;
   continue;
  }
  for(j =0 ; j< i ; j ++ )
   if(temp.Board[j] > temp.Board[i]) sum ++ ;
 }
 temp.empty = k ;

 ReduceTheMap(temp,begin.RMap);

 if(sum%2) memcpy(Target,DlTarget[1],8);
 else memcpy(Target,DlTarget[0],8);

 Evaluate(begin);
 begin.index = InsetHashTable(begin.RMap);
 return ;
}

void output(){
   if(Stack.length <= 0 ) printf("已經是目標狀態了,你這不是自找麻煩嘛!/n");
   else
   {
     Map temp ;
        printf("搞定!/t 供需 %d 步 , 步驟如下:/n",Stack.length );
  for(int i =Stack.length ; i >= 0 ; i --  )
  {
   UnreduceMap( temp , Stack.pop());
   temp.print();
   getchar();
   if(i) printf("/n/n---->/n");
  }
   }
   printf("/n");
   return ;
}

int main()
{
     Nodes begin ;
  input(begin);
  long time = GetTickCount();
  search(begin);
  printf("計算耗時:%dMS/n",GetTickCount()-time);
  output();
  return 0 ;
}

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