吴昊品游戏核心算法 Round 5 —— 五子棋(无禁手)后台的判胜负AI(bfs+queue容器)(HDOJ 2699)
五子棋的历史
连子棋类游戏起源很早,在四千年前的两河文明就有古西亚连棋。
五子棋则咸信是流传于古中国的传统棋种之一,至今仍在民间广泛流传,规则相当简单。或许因没有形成一套独立完整的棋种理论及文化内涵,更无制定公平完善的规则来解决黑白平衡问题,一直没有得到发展,所以没有像围棋、象棋等传统棋类流传广泛,导致缺少可考古的棋具,也没像直棋、方棋等乡土棋类记载在地方县志、古人笔记等文献。
五子棋在传入日本后,被日本人发扬光大,自1899年日本棋士黒岩涙香证明了原始规则的五子棋先下必胜后,五子棋迈入一条不断改良的道路,经过数十年的修改、验证、再修改,最终发展出加入禁手的五子棋,并经过公开征名,称为连珠(RENJU),因此规则在日本成型,又称为日式规则或连珠规则。原始规则在中国依然有人在玩,也被称为无禁规则、自由规则,有软件可验证黑手必胜(这个稍后再证明)。
连珠规则禁止先下的黑棋下出双活三、双四、长连(超过五子以上的连线)下出则判败,此举限制黑棋的取胜方式,白棋则增加逼迫黑子下出禁手来取胜的手 段,使黑棋的优势稍稍减少。不过禁手也许对初学者来说是一种障碍,但下久后会发现,禁手使得双方棋手必需更加精准的掌握棋子的落点,增加了连珠的技术性、 复杂性及趣味性。
又过了几十年,人们发现单单加入禁手,尚无法完全平衡黑棋一子之先的优势,因此在国际比赛使用的“RIF规则”,在连珠规则的基础上,又加入了三手交换及五手两打,算第一个可以真正合乎公平竞技的职业规则。
Problem——该问题,是用一串二维数组装载的字符串,用.表示空,用W表示白棋,用B表示黑棋,来判断目前的局面中的当前走棋的一方是否赢了整盘棋。
Solve:
/*
该程序可以判定当前的一方是否获胜,如果暂时没有获胜或者已经告负,本
程序会有相应的输出。
AI的策略基本上和连连看的相似,也用的是queue容器+BFS
(1)判断当前局势,利用目前棋盘上黑子和白子的数量进行比较
(2)五子连环必须是恰好有五个子,也就是说,沿端点方向上的点应该是空格的
(3)表面上是八个方向,实际上考虑到对称性,四个方向即可
(4)在for循环的条件判断上加上flag,找到一例,即可退出循环
*/
2 #include<queue>//STL里面的queue容器
3 using namespace std;
4
5 struct Node //每个点有一个坐标,最大连通数以及方向
6 {
7 char x,y,time,dirc;
8 bool no_stone;
9 };
10
11 char map[15][15],c;//定义一个二维数组,表示棋盘,c表示现在轮到谁
12
13 int dir[4][2]={{0,1},{1,0},{1,-1},{1,1}};//四个方向
14
15 //BFS搜索,和之前的连连看类似
16 bool bfs(int x,int y)
17 {
18 queue<Node> Q;
19 Node first,next;
20 int i;
21 first.x=x;
22 first.y=y;
23 first.no_stone=false;
24 first.time=1;
25 first.dirc=-1;
26 Q.push(first);
27
28 while(!Q.empty())
29 {
30 first=Q.front();
31 Q.pop();
32 //从第一个点开始,保证有且仅有五个是连在一起的,用no_stone标识没有多连
33 if(first.time==5&&first.no_stone)
34 {
35 return true;
36 }
37 for(i=0;i<4;i++)
38 {
39 //初始方向不确定
40 if(first.dirc==-1||first.dirc==i)
41 {
42 next.x=first.x+dir[i][0];
43 next.y=first.y+dir[i][1];
44 if(next.x>=0&&next.x<15&&next.y>=0&&next.y<15)
45 {
46 next.dirc=i;
47 next.time=first.time+1;
48 //唯独不标识对手的棋子,自己的棋子和空棋子用no_stone区分
49 if(map[next.x][next.y]==c)
50 {
51 next.no_stone=first.no_stone;
52 Q.push(next);
53 }
54 else if(map[next.x][next.y]=='.'&&!first.no_stone)
55 {
56 next.no_stone=true;
57 Q.push(next);
58 }
59 }
60 }
61 }
62 }
63 return false;
64 }
65
66 int main()
67 {
68 int T;
69 scanf("%d",&T);
70 while(T--)
71 {
72 int i,j;
73 int white_num=0,black_num=0;
74 getchar(); //读入一个回车键
75 for(i=0;i<15;i++)
76 {
77 gets(map[i]);
78 }
79 for(i=0;i<15;i++)
80 {
81 for(j=0;j<15;j++)
82 {
83 if(map[i][j]=='W')
84 {
85 white_num++;
86 }
87 else if(map[i][j]=='B')
88 {
89 black_num++;
90 }
91 }
92 }
93 //记录黑白棋子的数量,主要是判断当前下棋方是黑还是白
94 if(black_num>white_num)
95 {
96 c='W';
97 }
98 else
99 {
100 c='B';
101 }
102 bool flag=true;
103 //只要找到一个“五子连”就可以了
104 for(i=0;i<15&&flag;i++)
105 {
106 for(j=0;j<15&&flag;j++)
107 {
108 if(map[i][j]==c)
109 {
110 flag=!bfs(i,j);
111 }
112 }
113 }
114 if(flag) printf("NO\n");
115 else printf("YES\n");
116 }
117 return 0;
118 }