吴昊品游戏核心算法 Round 9 —— 黑白棋AI系列(第一弹)(BFS+位压缩存储)(POJ 1753)

 

在历史上,关于黑白棋(官方名称为奥赛罗),有许多个变种,比如这一弹中,我准备给出其中一种(和我在Round 7中介绍的熄灯问题)比较相似的(甚至可以说是几乎一摸一样)的游戏,其名字叫做翻转棋(Flip Game)。这款游戏的规则非常简单,在一个4*4的界面中,随意翻转一个子,其自身以及相邻的棋子都会跟着翻转并变换其颜色。了 解了规则之后,我们可以给出如下的AI,该AI可以翻转最短的次数,来找到目标位置(可以是全黑(All Black)或者是全白(All White))。在“熄灯问题”中,我们说过一种方法叫做BFS+Enum(也就是广度优先搜索配合枚举),这里注意的一点是,我们不能使用STL里面的 容器(包括bitset和queue),因为会产生溢出。这里,我们将数组改变成队列或者栈,在65535个长度以内都不会发生溢出的问题了。

 

Problem——源自POJ 1753,给出四行四列的16个字符,b代表黑子(black),w代表白子(white),我们的输出需要给出一个整形的数字,来表示最少几次就可以达到我们所需要的目标。如果完全无法达到的话,就输出Impossible。

队列实现:


  1  /*
  2 
  3      Highlights:
  4 
  5     (1)为了获得更多的装填量,利用数组模拟队列,而不使用STL中的各种容器
  6 
  7     (2)1<<x表示置x位为黑位,16个格子,正好用short int的短整型数表示16个点
  8 
  9     (3)巧妙运用XOR运算,将每一次变化后的状态存储
 10 
 11    */
 12 
 13  
 14 
 15  #include<iostream>
 16 
 17  #include<queue>
 18 
 19   using  namespace std;
 20 
 21  
 22 
 23   int step[ 65535];  // 利用数组,可以得到比容器更多的装载量(这里表示所有可能的装载量)
 24 
 25   bool flag[ 65535];  // 防止重复搜索
 26 
 27  unsigned  short qState[ 65535];  // 搜索的状态,由于65536=2^16,所以可以用每一位的'0'和'1'状态标记是否被搜索
 28 
 29  
 30 
 31   int rear= 0// 队列尾指针
 32 
 33   int top= 0// 队列头指针
 34 
 35   
 36 
 37    // 初始化,读入棋盘的初始状态并将其转化为16位数存储在头队列中
 38 
 39    // 黑为'1'而白为'0'
 40 
 41  
 42 
 43   void init()
 44 
 45  {
 46 
 47    unsigned  short temp= 0; // 无字符短整型变量,省空间
 48 
 49     char c; // 每次读入一个字符
 50 
 51     for( int i= 0;i< 4;i++)
 52 
 53    {
 54 
 55       for( int j= 0;j< 4;j++)
 56 
 57      {
 58 
 59        cin>>c;
 60 
 61         if( ' b '==c) // 注意这样写的妙处!如果写成c=='b'很容易和c='b'混淆
 62 
 63          temp=temp|( 1<<(i* 4+j)); // 将黑位置为高位       
 64 
 65      }       
 66 
 67    }    
 68 
 69    qState[rear++]=temp; // 载入状态队列
 70 
 71    flag[temp]= true; // 标记该位
 72 
 73  }
 74 
 75  
 76 
 77   // 翻转一个棋子,并按照规则对周围棋子施加影响
 78 
 79  unsigned  short move(unsigned  short state, int i)
 80 
 81  {
 82 
 83    unsigned  short temp= 0;
 84 
 85    temp|=( 1<<i);
 86 
 87     if((i+ 1)% 4!= 0// 不在最右的右
 88 
 89      temp|=( 1<<(i+ 1));
 90 
 91     if(i% 4!= 0// 不在最左的左
 92 
 93      temp|=( 1<<(i- 1));
 94 
 95     if(i+ 4< 16//
 96 
 97      temp|=( 1<<(i+ 4));
 98 
 99     if(i- 4>= 0//
100 
101      temp|=( 1<<(i- 4));
102 
103     return (state^temp); // XOR异或运算        
104 
105  }
106 
107  
108 
109   // 采用BFS将每一次
110 
111   bool BFS()
112 
113  {
114 
115     while(rear>top) // 直到队列中无元素
116 
117    {
118 
119       // 这个指令是在模拟qState.pop();
120 
121      unsigned  short state=qState[top++];
122 
123       for( int i= 0;i< 16;i++)
124 
125      {
126 
127        unsigned  short temp;
128 
129        temp=move(state,i);       
130 
131         if( 0==state|| 65535==state) // 上述说过的技巧
132 
133        {
134 
135          cout<<step[state]; 
136 
137           return  true;                        
138 
139        }
140 
141         else  if(!flag[temp]) // 防止重复搜索
142 
143        {
144 
145           // 模拟进队列操作qState.push(temp);
146 
147          qState[rear++]=temp;
148 
149          flag[temp]= true;
150 
151          step[temp]=step[state]+ 1;    
152 
153        }
154 
155      }              
156 
157    }    
158 
159     return  0;
160 
161  }
162 
163  
164 
165   int main()
166 
167  {
168 
169    init();
170 
171     if(!BFS()) cout<< " Impossible ";
172 
173     char c;
174 
175    cin>>c;
176 
177     return  0;   
178 
179  }

你可能感兴趣的:(round)