吴昊品游戏核心算法 Round 4 —— 连连看AI(用BFS+STL实现)(HDOJ 1175)

连连看游戏的历史:

游戏“连连看”顾名思义就是找出相关联的东西,这个连连看在网上基本是用在小游戏中,就是找出相同的两样东西,在一定的规则之内可以做为相关联处理。“连连看”的发展经历了从桌面游戏、在线游戏、社交游戏三个过程。

游 戏“连连看”是源自台湾的桌面小游戏,自从流入大陆以来风靡一时,也吸引众多程序员开发出多种版本的“连连看”。这其中,顾方编写的“阿达连连看”以其精 良的制作广受好评,这也成为顾方“阿达系列软件”的核心产品。并于2004年,取得了国家版权局的计算机软件著作权登记证书。

随着Flash应用的流行,网上出现了多种在线Flash版本“连连看”。如“水晶连连看”、“果蔬连连看”等,流行的“水晶连连看”以华丽界面吸引了一大批的女性玩家。

2008年,随着社交网络的普及和开放平台的兴起,“连连看”被引入了社交网络。“连连看”与个人空间相结合,被快速的传播,成为一款热门的社交游戏,其中以开发者Jonevey在Manyou开放平台上推出的“宠物连连看”最为流行。

  规则如下:第 一次使用鼠标点击棋盘中的棋子,该棋子此时为“被选中”,以特殊方式显示;再次以鼠标点击其他棋子,若该棋子与被选中的棋子图案相同,且把第一个棋子到 第二个棋子连起来,中间的直线不超过3根,则消掉这一对棋子,否则第一颗棋子恢复成未被选中状态,而第二颗棋子变成被选中状态。

  下面我来品析的这个AI实际上实现的是一个查询功能,属于连连看的一个小“作弊工具”,使用它之后,可以判断当前局面是否还有可以消去的“图形对”(实际上,该小工具还可以搜索到任意的一种这样的“图形对”并提示可以删去,当然,这些都是大同小异了)

  我们利用BFS(宽度优先搜索)进行,并且,利用STL里面的队列queue结构来存储每一种结果。(这种或者类似的方法,在推箱子游戏的AI中也是比较常见的)

下面,我来给出HDOJ 1175关于此问题的介绍和一些input和output。

  Problem Description——“连连看”相信很多人都玩过。没玩过也没关系,下面我给大家介绍一下游戏规则:在一个棋盘中,放了很多的棋子。如果某两个相同的棋子,可以通过一条线连起来 (这条线不能经过其它棋子),而且线的转折次数不超过两次,那么这两个棋子就可以在棋盘上消去。不好意思,由于我以前没有玩过连连看,咨询了同学的意见, 连线不能从外面绕过去的,但事实上这是错的。现在已经酿成大祸,就只能将错就错了,连线不能从外围绕过(吴昊评注:这个也算是连连看的一个变种吧!)。
玩家鼠标先后点击两块棋子,试图将他们消去,然后游戏的后台判断这两个方格能不能消去。现在你的任务就是写这个后台程序(吴昊评注:前台的绚丽靠UI和美工,后台的绚丽靠数据结构和算法)

Input—— 输入数据有多组。每组数据的第一行有两个正整数n,m(0<n<=1000,0<m<1000),分别表示棋盘的行数与列数。在 接下来的n行中,每行有m个非负整数描述棋盘的方格分布。0表示这个位置没有棋子,正整数表示棋子的类型。接下来的一行是一个正整数 q(0<q<50),表示下面有q次询问。在接下来的q行里,每行有四个正整数x1,y1,x2,y2,表示询问第x1行y1列的棋子与第 x2行y2列的棋子能不能消去。n=0,m=0时,输入结束。注意:询问之间无先后关系,都是针对当前状态的!

Output——每一组输入数据对应一行输出。如果能消去则输出"YES",不能则输出"NO"。

亮点如下:

(1)   上下左右四个方向的标识,采用一个二维数组,这也是一种常用技巧dir[4][2].

(2)   采用STL队列容器,装载每一个可能的点。

(3)   在BFS中,每相邻两个点放在一起判断,定期更新每个点处拐弯次数的最小值,这一点很不错。


  1 #include<iostream>
  2 
  3  #include<queue>  // 利用STL中的queue容器
  4 
  5   using  namespace std;
  6 
  7  
  8 
  9   const  int N= 1001; // 棋盘的尺度上限
 10 
 11   bool flag; // 判断最终是成功了还是失败了
 12 
 13   int n,m,sx,sy,ex,ey; // 棋盘的行列以及始点和终点
 14 
 15   int hash[N][N],map[N][N]; //  描述整个棋盘
 16 
 17    // 标示上,下,左,右四个方向
 18 
 19   int dir[ 4][ 2]={{ 1, 0},{ 0, 1},{- 1, 0},{ 0,- 1}};
 20 
 21  
 22 
 23   struct node
 24 
 25  {
 26 
 27     int x,y,turn,d; // 每个点的(x,y)坐标,转了几次,以及当前的方向      
 28 
 29  }start;
 30 
 31  
 32 
 33  queue<node> q; // queue容器的实例q装载结构体node
 34 
 35   
 36 
 37    // 下述函数可以判断是否越界
 38 
 39  inline  bool  in( const node &p)  // 内联函数,在需要调用的地方展开
 40 
 41  {
 42 
 43     if(p.x< 0||p.y< 0||p.x>=n||p.y>=m)
 44 
 45    {
 46 
 47       return  false;                               
 48 
 49    }      
 50 
 51     return  true;
 52 
 53  }
 54 
 55  
 56 
 57   void bfs()
 58 
 59  {
 60 
 61    node now,t;
 62 
 63     while(!q.empty())
 64 
 65    {
 66 
 67      now=q.front(); // 队首元素
 68 
 69      q.pop(); // 出队列
 70 
 71       if(now.x==ex&&now.y==ey&&now.turn<= 2) // 成功条件
 72 
 73      {
 74 
 75        flag= true;
 76 
 77         return;                                              
 78 
 79      }       
 80 
 81       for( int i= 0;i< 4;i++) // 可以让结点分别朝着不同方向前进一步
 82 
 83      {
 84 
 85        t.x=now.x+dir[i][ 0];
 86 
 87        t.y=now.y+dir[i][ 1];
 88 
 89         if(now.d==i)
 90 
 91          t.turn=now.turn,t.d=now.d;
 92 
 93         else
 94 
 95          t.turn=now.turn+ 1,t.d=i;
 96 
 97         // 点既在区域内又没有点占据或者说到达了重点,并且其拐点次数由于当前那个点上的拐点次数
 98 
 99          // 最后一个判断乃是一个优先的原则
100 
101         if( in(t)&&(map[t.x][t.y]== 0||t.x==ex&&t.y==ey)&&hash[t.x][t.y]>=t.turn)
102 
103        {
104 
105          hash[t.x][t.y]=t.turn;
106 
107          q.push(t);                                                                      
108 
109        }       
110 
111      }        
112 
113    }    
114 
115  }
116 
117  
118 
119   int main()
120 
121  {
122 
123     int i,j,t;
124 
125     while(scanf( " %d%d ",&n,&m),(n||m)) // 如果n和m都是0方可退出
126 
127    {
128 
129       for(i= 0;i<n;i++)
130 
131         for(j= 0;j<m;j++)
132 
133          scanf( " %d ",&map[i][j]);
134 
135      scanf( " %d ",&t);
136 
137       while(t--)
138 
139      {
140 
141        scanf( " %d%d%d%d ",&sx,&sy,&ex,&ey); // 每一个要查询的首尾点
142 
143          // 这里有一个处理,将实际的游戏界面和程序中的二维数组匹配
144 
145        sx--,sy--,ex--,ey--;
146 
147         // 以下四种情况,判断是不符合条件的(实际上还有转折超过2的情况)
148 
149         if((map[sx][sy]!=map[ex][ey])||map[sx][sy]== 0||map[ex][ey]== 0||(sx==ex&&sy==ey))
150 
151        {
152 
153          puts( " NO ");
154 
155           continue;                                                                               
156 
157        }         
158 
159         for(i= 0;i<n;i++)
160 
161           for(j= 0;j<m;j++)
162 
163            hash[i][j]= 11;
164 
165         for(i= 0;i< 4;i++)
166 
167        {
168 
169          start.x=sx,start.y=sy,start.turn= 0,start.d=i;
170 
171          q.push(start);               
172 
173        }
174 
175        flag= false,hash[sx][sy]= 0;
176 
177        bfs(); // 搜索
178 
179        puts(flag ?  " YES ": " NO ");
180 
181      }                                  
182 
183    }
184 
185     return  0;   
186 
187  }
188 
189 

你可能感兴趣的:(round)