本文为大家分享了C++五子棋的设计思路和设计实现,供大家参考,具体内容如下
算法思路:
在结束了对C++的学习之后,准备自己编制一些简单的练习程序。目前初步设想是编制一个人机对战的简易五子棋软件。 以下为个人设计思考的过程。
首先,进行问题分析与设计。计划实现的功能为,开局选择人机或双人对战,确定之后比赛开始。比赛结束后初始化棋盘,询问是否继续比赛或退出。后续可加入复盘、悔棋等功能。整个过程中,涉及到了棋子和棋盘两种对象,同时要加上人机对弈时的AI对象,即涉及到三个对象。
棋盘类的设计。
数据存储:五子棋棋盘为15*15的网格结构,即一共有225个节点,每个节点有横竖坐标各一,同时每个节点状态有3种,黑,白,或者为空。可考虑采用二维数组来存储。每个节点三种状态,适合采用枚举(enum)类型。
-* 实现功能*:1. 棋盘对象应负责实时更新棋盘状态并对外显示,因此需要接收棋子对象的输入,同时需要对外输出。而棋盘对象不需要对棋子对象发送信息,所以可以设计棋盘类中更新棋盘状态的函数接收棋子对象作为形参。2. 同时,在每次走棋之后,棋盘对象都要及时更新棋盘信息,即输出棋盘状态。3. 而且,每次走棋之后更新输出之前,应该对棋子走棋是否符合规则以及输赢进行判定。若将规则判定单独封装为一个对象,则不方便调用棋盘信息,故将规则判定设计为棋盘类的成员函数,接收两个形参,一个是棋盘当前的状态,一个是即将走的下一步棋,即一个棋子对象。
设计棋子对象。棋子对象应包含两种信息。一是棋子颜色,二是当前要走的棋子的位置坐标,并保留对外输出的接口。
接下来细化规则判定函数。
- 首先进行走棋规则判定。接收当前棋子位置信息后,判定该位置是否越界或为空,若非空或者越界,则判定违规,抛掷异常,交付上级调用处理。
- 然后进行输赢判定。按照一定顺序对当前落子位置的相邻元素进行同色判定并计数。当发现某条直线上同色棋子超过四枚,则判定当前走棋方获胜 。判定过程中同样需要注意是否越界。若均未构成五星连珠,则进入平局判定。
- 平局判定,遍历棋盘,统计空位,若空位为0,即棋盘已满,判定为平局。
接下来设计下棋AI。设计为一个棋子类型的函数,即接收当前棋盘状态和对方最后一次落棋,返回棋子对象类型。
对弈算法设计。
- 首先进行先后手判定:若棋盘为空则直接落子(8,8),正中开局。
- 然后进行防守判定:针对对方上次落棋进行活棋检测,在横、竖、左斜、右斜四条直线上依次进行检测。在任意方向检测到四或活三,即可进行 封堵操作,给出所有可行的封堵位置。若未检测到四或活三,则统计活二并给出所有可能的封堵位置。然后针对所有可能的封堵位置进行评分,选取分数最高的位置进行落子。若上述检测均未找到防守点,则转入进攻算法。
- 进攻算法:采用枚举,即暴力破解的方法。遍历整个棋盘的所有空位,并给出每个空位的评分,选取最高分进行落子。
- 活棋检测算法:给定参照棋子,在四个方向上分别检测。以横向检测为例,设参照棋子坐标为(x,y),设定同色计数器count=1(计算同色棋子数目),设定封锁端统计量lock=0,设定已判断的方向统计judge=0。对x-1,判断节点状态,若同色计数器加1,继续判断x-2;若异色,则lock+1,judge+1,若judge=2,终止判断,若judge<2,反向判断x+1;若空白,judge+1,若judge=2,终止判断,若judge<2,反向判断。最后得到被封堵的端口数lock和同色数count。若lock=0,count=3或2,判定为活3或活2。若lock=1,count=4,判定为4,若lock=1,count=3,判定为半3。但是在这种算法中,关于空白的判定存在着一些问题。用0代表空白,用+代表同色,-代表异色,则当出现下列情况时:-0++0-,-+++0-,事实上是死棋,而+0++0,+0+++-,实际上相当于活3或半4。为此,需要对活2和半3的情况进行进一步筛选,即空白端应保证连续两个空白。在活棋检测过程中,如果遇到活3或者半4,则立即终止检测,返回落子的可能位置。若没有则记录活2半3的防守位置,换方向检测。最后返回一个棋子类的数组,包含所有建议的落子位置。若均无,则遍历棋盘,统计所有空白位置,返回。
- 落子位置评分算法:对活棋检测返回数组中的每个位置进行评分,即以该点为己方参照点,进行活棋检测,若有count=5,直接返回该落子位置。有活三或者半4,分数加20,有活2,分数加5,对角线有相邻同色,分数+2,有异色,分数-2,横竖有同色,分数+1,有异色,分数-1。最后排序,取最高分对应的落子,返回该落子。
接下来则是数据结构和对象设计及主函数调用实现:
类及类的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
|
#define RENJU_H #include <iostream> #include <windows.h> #include <string> #define hor 7 #define ver 4 using namespace std; //用于记录坐标 struct position { int x; int y; position() { x = 0; y = 0; } position( int a, int b) { x = a; y = b; } }; //用于记录棋子颜色和节点状态 enum state { blank=0,black=1,white=2 }; //用于存储棋局分析信息:未完赛,犯规,平局,黑方胜,白方胜 enum result { go_on,error,draw,black_win,white_win }; // 设置光标 void setpos(COORD a) { HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleCursorPosition(out, a); } // 设置光标 void setpos( int i, int j) { COORD pos = { i, j }; setpos(pos); } //绘图函数,用于在指定坐标输出指定字符 inline void gps( int x, int y, char c) { setpos(y,x); cout<<c; } //绘图函数,用于在指定坐标输出整数 inline void gps( int x, int y, int i) { setpos(y,x); if (i>=10) cout<<i; else cout<<0<<i; } //绘图函数,用于在指定坐标输出字符串 inline void gps( int x, int y,string s) { setpos(y,x); cout<<s; } //绘图函数,用于在给定坐标打印棋盘中的一格 void tab( int x, int y,state str) { string s; switch (str) { case blank: s= " " ; break ; case black: s= "黑" ; break ; case white: s= "白" ; break ; default : break ; } setpos(y,x); cout<< " ------ " ; setpos(y,x+1); cout<< "| |" ; setpos(y,x+2); cout<< "| " <<s<< " |" ; setpos(y,x+3); cout<< "| |" ; setpos(y,x+4); cout<< " ------ " ; } //查找最大值 int MAX( const int *a, int n) { int max = a[0]; for ( int i =1; i < n ;i++) { if (a[i] > max) max = a[i]; } return max; } //检测是否符合胜利条件 //棋子类 class chess { public : inline chess( int x=0, int y=0,state c=blank) { point.x=x,point.y=y; color=c; }; inline chess(chess &ch) { point=ch.drop_point(); color=ch.get_color(); } ~chess(){}; struct position drop_point() const ; //用于向外部提供棋子位置 inline state get_color() const { return color;} //提供棋子颜色信息 void set_point(); //用于从外部获取棋子位置 void set_point( int x, int y){ point.x=x,point.y=y;} void set_point(position p){ point.x=p.x,point.y=p.y;} void set_color(state c){ color=c;} //设置棋子颜色 private : position point; enum state color; }; position chess::drop_point() const { return point; } void chess::set_point() { if (color==black) { setpos(110,1); cout<< "请黑方输入落子列数和行数,空格隔开:" ; cin>>point.x>>point.y; while (point.x<1||point.x>15) { setpos(110,1); cout<< "列数输入超出范围,请重新输入1~15之间数字 " ; cin>>point.x; } while (point.y<1||point.y>15) { setpos(110,2); cout<< "行数输入超出范围,请重新输入1~15之间数字 " ; cin>>point.y; } } else if (color==white) { setpos(110,1); cout<< "请白方输入落子列数和行数,空格隔开:" ; cin>>point.x>>point.y; while (point.x<1||point.x>15) { setpos(110,1); cout<< "列数输入超出范围,请重新输入1~15之间数字 " ; cin>>point.x; } while (point.y<1||point.y>15) { setpos(110,2); cout<< "行数输入超出范围,请重新输入1~15之间数字 " ; cin>>point.y; } } point.x--; point.y--; } //棋盘类 class chessboard { public : chessboard() { for ( int i=0;i<15;i++) for ( int j=0;j<15;j++) { square[i][j]=blank; } } chessboard(chessboard *cb) { for ( int i=0;i<15;i++) for ( int j=0;j<15;j++) { square[i][j]=cb->viewboard(i,j); } } inline state viewboard(position p_c) const ; //接收坐标,返回该位置的状态 inline state viewboard( int x, int y) const ; //接收整数坐标,返回该位置的状态 void update(chess ch); //接收新棋子,更新棋盘状态 void display() const ; //向外输出棋盘状态 result judge(chess ch) const ; //规则函数,判断走棋是否犯规和输赢 void clear() //清空棋盘 { for ( int i=0;i<15;i++) for ( int j=0;j<15;j++) { square[i][j]=blank; } } private : state square[15][15]; }; int check_five(chessboard bd,chess ch) { position ori=ch.drop_point(); int count=1; //计数器,统计同色个数 int sum[4]={0}; bool locked=0; //逻辑标记量,用来标记是否遇到了非同色节点 //水平方向检测 for ( int i=0,locked=0;i<5&&((ori.x-i)>=0)&&(!locked);i++) //终止循环条件:同色超过4个或触碰到棋盘边界或遇到非同色节点 if (ch.get_color()==bd.viewboard(ori.x-i,ori.y)) count++; else if (i!=0) locked=1; //sum[0]=count-1; for ( int i=0,locked=0;i<5&&((ori.x+i)<=14)&&(!locked);i++) //终止循环条件:同色超过4个或触碰到棋盘边界或遇到非同色节点 if (ch.get_color()==bd.viewboard(ori.x+i,ori.y)) count++; else if (i!=0) locked=1; //sum[1]=count-sum[0]-2; sum[0]=count; if (count>=5) return count; //竖直方向检测 count=1; for ( int i=0,locked=0;i<5&&((ori.y-i)>=0)&&(!locked);i++) //终止循环条件:同色超过4个或触碰到棋盘边界或遇到非同色节点 if (ch.get_color()==bd.viewboard(ori.x,ori.y-i)) count++; else if (i!=0) locked=1; //sum[2]=count-1; for ( int i=0,locked=0;i<5&&((ori.y+i)<=14)&&(!locked);i++) //终止循环条件:同色超过4个或触碰到棋盘边界或遇到非同色节点 if (ch.get_color()==bd.viewboard(ori.x,ori.y+i)) count++; else if (i!=0) locked=1; //sum[3]=count-sum[2]-2; sum[1]=count; if (count>=5) return count; //左上到右下斜向检测 count=1; for ( int i=0,locked=0;i<5&&((ori.y-i)>=0)&&((ori.x-i)>=0)&&(!locked);i++) //终止循环条件:同色超过4个或触碰到棋盘边界或遇到非同色节点 if (ch.get_color()==bd.viewboard(ori.x-i,ori.y-i)) count++; else if (i!=0) locked=1; //sum[4]=count-1; for ( int i=0,locked=0;i<5&&((ori.x+i)<=14)&&((ori.y+i)<=14)&&(!locked);i++) //终止循环条件:同色超过4个或触碰到棋盘边界或遇到非同色节点 if (ch.get_color()==bd.viewboard(ori.x+i,ori.y+i)) count++; else if (i!=0) locked=1; //sum[5]=count-sum[4]-2; sum[2]=count; if (count>=5) return count; //左下到右上斜向检测 count=1; for ( int i=0,locked=0;i<5&&((ori.y+i)<=14)&&((ori.x-i)>=0)&&(!locked);i++) //终止循环条件:同色超过4个或触碰到棋盘边界或遇到非同色节点 if (ch.get_color()==bd.viewboard(ori.x-i,ori.y+i)) count++; else if (i!=0) locked=1; //sum[6]=count-1; for ( int i=0,locked=0;i<5&&((ori.x+i)<=14)&&((ori.y-i)>=0)&&(!locked);i++) //终止循环条件:同色超过4个或触碰到棋盘边界或遇到非同色节点 if (ch.get_color()==bd.viewboard(ori.x+i,ori.y-i)) count++; else if (i!=0) locked=1; //sum[7]=count-sum[6]-2; sum[3]=count; if (count>=5) return count; return MAX(sum,4); } state chessboard::viewboard(position p_c) const { return square[p_c.x][p_c.y]; } state chessboard::viewboard( int x, int y) const { return square[x][y]; } void chessboard::update(chess ch) { position pos=ch.drop_point(); square[pos.x][pos.y]=ch.get_color(); } void chessboard::display() const { system ( "cls" ); for ( int i=0;i<15;i++) //打印列坐标说明 { gps(0,6+i*hor,i+1); } for ( int i=0;i<15;i++) //打印列坐标说明 { gps(16*ver,6+i*hor,i+1); } for ( int i=0;i<15;i++) //打印行坐标说明 { gps(3+i*ver,1,i+1); } for ( int i=0;i<15;i++) //打印行坐标说明 { gps(3+i*ver,1+16*hor,i+1); } for ( int i=0,j=0;i<15;i++) { for (j=0;j<15;j++) tab(1+i*ver,3+hor*j,square[j][i]); } cout<<endl; } result chessboard::judge(chess set) const { bool full=1; if (viewboard(set.drop_point())!=blank) { return error; } if (check_five(* this ,set)>=5&&(set.get_color()==black)) return black_win; if (check_five(* this ,set)>=5&&(set.get_color()==white)) return white_win; for ( int i=0;i<15;i++) for ( int j=0;j<15;j++) { if (square[i][j]==blank) full=0; } if (full==1) return draw; else return go_on; } #endif |
主函数,应该再定义game类进行优化,不过设计的时候没有考虑周全,还是按照C的思想写了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
|
#include "ai.h" #include <stdlib.h> int main() { system ( "mode con cols=150 lines=150 " ); system ( "color 27" ); chessboard bd; chess now; result final; int mode; gps(5,40, "==============欢迎使用简易五子棋!==============" ); gps(10,50, ">>>>输入1或2进行模式选择<<<<" ); gps(11,50, " <1> 双人对战 " ); gps(12,50, " <2> 人机对战 " ); do { cout<<endl<< " input mode:" ; cin>>mode; } while (mode != 1 && mode != 2); //双人对战 if (mode==1) { loop1: now.set_color(black); //执黑先行 bd.clear(); bd.display(); //初始化棋盘 setpos(110,0); cout<< "对局开始,黑旗先行" ; //循环判断上一次落子结果,收集下一次输入,直到棋局出结果 do { now.set_point(); //输入 final=bd.judge(now); //判断是否违规 while (final==error) //犯规输入则重新输入 { system ( "cls" ); bd.display(); setpos(110,10); cout<< "犯规(输入坐标已有棋子)!" ; now.set_point(); final=bd.judge(now); } //正确落子后更新棋盘 bd.update(now); bd.display(); //反转下一步走棋的颜色 if (now.get_color()==black) now.set_color(white); else now.set_color(black); } while (final==go_on); switch (final) { case go_on: break ; case error: break ; case draw: setpos(110,10); cout<< "平局:游戏结束" ; break ; case black_win: setpos(110,10); cout<< "黑旗获胜:游戏结束" ; break ; case white_win: setpos(110,10); cout<< "白旗获胜:游戏结束" ; break ; default : break ; } setpos(110,11); cout<< "是否继续下一局?Y/N" ; char flag; cin>>flag; if (flag == 'Y' ) goto loop1; } if (mode == 2) { chess ai_ch; system ( "cls" ); gps(5,40, "==============欢迎使用简易五子棋!==============" ); gps(10,50, ">>>>输入1或2进行模式选择<<<<" ); gps(11,50, " <1> 执黑先行 " ); gps(12,50, " <2> 执白后行 " ); do { cout<<endl<< " input mode:" ; cin>>mode; } while (mode != 1 && mode != 2); if (mode == 1) { loop2: now.set_color(black); //执黑先行 bd.clear(); bd.display(); //初始化棋盘 Ai afago(bd,white); setpos(110,0); cout<< "对局开始,请您落子" ; now.set_point(); bd.update(now); ai_ch.set_color(white); ai_ch.set_point(left(now.drop_point(), false )); bd.update(ai_ch); bd.display(); //循环判断上一次落子结果,收集下一次输入,直到棋局出结果 do { now.set_point(); //输入 final=bd.judge(now); //判断是否违规 while (final==error) //犯规输入则重新输入 { system ( "cls" ); bd.display(); setpos(110,10); cout<< "犯规(输入坐标已有棋子)!" ; now.set_point(); final=bd.judge(now); } //正确落子后更新棋盘 bd.update(now); if (final != black_win) { ai_ch=afago.set_chess(); final=bd.judge(ai_ch); bd.update(ai_ch); bd.display(); } else {bd.display();} } while (final==go_on); switch (final) { case go_on: break ; case error: break ; case draw: setpos(110,10); cout<< "平局:游戏结束" ; break ; case black_win: setpos(110,10); cout<< "恭喜您棋艺高超,战胜了AI:游戏结束" ; break ; case white_win: setpos(110,10); cout<< "电脑获胜,请继续努力提高自己:游戏结束" ; break ; default : break ; } setpos(110,11); cout<< "是否继续下一局?Y/N" ; char flag; cin>>flag; if (flag== 'Y' ) goto loop2; } if (mode == 2) { loop3: ai_ch.set_color(black); //AI执黑先行 now.set_color(white); bd.clear(); //初始化棋盘 Ai afago(bd,black); ai_ch.set_point(7,7); bd.update(ai_ch); bd.display(); setpos(110,0); cout<< "对局开始,请您落子" ; //循环判断上一次落子结果,收集下一次输入,直到棋局出结果 do { now.set_point(); //输入 final=bd.judge(now); //判断是否违规 while (final==error) //犯规输入则重新输入 { system ( "cls" ); bd.display(); setpos(110,10); cout<< "犯规(输入坐标已有棋子)!" ; now.set_point(); final=bd.judge(now); } //正确落子后更新棋盘 bd.update(now); if (final != white_win) { ai_ch=afago.set_chess(); final=bd.judge(ai_ch); bd.update(ai_ch); bd.display(); } else {bd.display();} } while (final==go_on); switch (final) { case go_on: break ; case error: break ; case draw: setpos(110,10); cout<< "平局:游戏结束" ; break ; case white_win: setpos(110,10); cout<< "恭喜您棋艺高超,战胜了AI:游戏结束" ; break ; case black_win: setpos(110,10); cout<< "电脑获胜,请继续努力提高自己:游戏结束" ; break ; default : break ; } setpos(110,11); cout<< "是否继续下一局?Y/N" ; char flag; cin>>flag; if (flag== 'Y' ) goto loop3; } } return 0; } |
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/black_kyatu/article/details/79327680