吴昊品游戏核心算法 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 

posted on 2013-02-27 20:45  吴昊系列  阅读(541)  评论(1编辑  收藏  举报

导航