参考自Originum学长的博客,个人进行了一些小修改,
原博客地址:https://blog.csdn.net/Originum/article/details/80356452
联系邮箱:Originum@126.com
一个简单的中国象棋游戏,主要实现:
1、两人对弈;
2、简单的棋子移动以及规则判断,不符合规则便重走;
3、有一方获胜便结束游戏。
一、类的设计
- “Chess”类:
(1) 私有成员有int型的Id,用于记录棋子的归属以及判断轮到的玩家是否可以移动;
(2) 公共成员函数有Get函数来获取Id,以及判断走法是否正确的Judgement纯虚函数;
(3) 作为基类来派生出其他棋子的类。
具体代码如下:
1 | class Chess |
“Chessboard”类:
(1) 私有成员有一个10*11的指向Chess类的指针(为了方便操作将0的位置腾了出来),用于存放棋子的地址;
(2) 还有一个Char型的Chessword数组用于存放棋子的名字(一个汉字占4个Char型空间);
(3) 公共成员部分有static int型的 Player用于记录轮到哪个玩家以及哪个玩家获胜。
具体实现代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 class Chessboard
{
private:
Chess *c[10][11]; //棋盘:X为横(9),Y为纵(10),从1开始记
char Chessword[15][4] = { "兵","炮","车","马","相","仕","帅"," ","将","士","象","馬","車","砲","卒" };
public:
static int Player; //上半区为1,下半区为-1
static bool End; //判断是否结束
Chessboard();
Chess *Get(int x, int y);//返回指定点的指针
int Getid(int x, int y);//返回指定点处棋子ID的指针
bool Move(int startx, int starty, int endx, int endy); //移动
void Init(); //初始化棋子
void Show(); //打印
void Play(); //开始游戏
~Chessboard();
};
二、具体变量的意义
红方棋子:”兵”,”炮”,”车”,”马”,”相”,”仕”,”帅”
Id为:7 6 5 4 3 2 1
绿方棋子:”将”,”士”,”象”,”馬”,”車”,”砲”,”卒”
Id为:-1 -2 - 3 -4 -5 -6 -7
用于实现移动的变量:
startx,starty: 开始的位置;
endx,endy:目标的位置;
S_Id: 存储开始位置的棋子的编号,若该点没有棋子(空指针),则值为0;
E_Id: 存储目标位置的棋子的编号,若该点没有棋子(空指针),则值为0;
TempX: 开始位置到目标位置的x坐标的偏移量(startx-endx);
TempY: 开始位置到目标位置的y坐标的偏移量(starty-endy)。
三、实现走棋
每次把位置信息传到Chessboard类的Move()函数时,会判断:
- 传入的两个位置是否越界(超出了10*11数组的范围),越界则返回false。
- 传入的开始位置的点的id是否为零(当点上无棋子,也就是空指针的时候为零),若为零则返回false。
- 当前选的棋子是否是这一回对应那一方的棋子,若不是则返回false。
- 对当前棋子的具体规则进行判断(通过指针调用虚函数,判断该棋子的具体规则)。若错误则返回false。
若以上判断都准确无误,则把棋子走到对应位置:
- 不吃子:目标位置为空指针时,把目标位置的指针指向该棋子对象,把棋子的起始位置指针赋值NULL,设为空指针。
- 吃子:delete目标位置的棋子对象,该棋子被吃。把目标位置的指针指向该棋子对象,把棋子的起始位置指针赋值NULL,设为空指针。
四、判断结束
判断结束:
棋盘类有一个静态变量bool End, 初始化为true。当将(帅)对象被析构时,把end赋值为false,表示棋局结束,退出走棋的循环(while(chessboard :: end)控制走棋是否继续)。
1
2
3
4~General()
{
Chessboard::End = false;
}当有一位玩家的某一步棋使双方的将(帅)面对面时,直接判负。具体实现是检测将(帅)所在的4、5、6 三列中将和帅是否在同一类且两者间的指针为全为空指针;
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
26int ifalg = 0;
for (int i = 4; i < 7; i++)
{
for (int j = 1; j < 11; j++)
{
if (c[i][j] != NULL)
{
if ((int)fabs(c[i][j]->Get()) == 1)
{
iflag++;
}
else if (iflag != 0 && iflag != 2)
{
if ((int)fabs(c[i][j]->Get()) != 1)
{
iflag--;
}
}
}
}
}
if (iflag == 2)
{
Player *= -1;
Chessboard::End = false;
}输出胜利的一方的信息:
棋盘类有一个静态变量int player,初始化为-1,每走棋一次乘以-1,在1与-1之间交替表示棋手的轮流下棋。以此来鉴别胜利的是哪一方五、某些棋子的规则算法
判断目标点是否是己方的棋:两方的子的Id互为相反数,所以只有当S_Id* E_Id>0时表示目标位置是己方的子,不能走。因而只有满足S_Id*E_Id<=0才是合法条件(空位置为0)。
判断是否满足只能走1格,或马走日,象走田的条件时,只需要区偏移的距离进行判断。例如TempX*TempX+TempY+TempY表示偏移量的平方,当该值为1时,表示只走了任意方向的一格;当值为2时,表示士的斜走;值为5时,表示马走日;为8时,表示象走田。
判断马走日的蹩脚马时,用数学计算方法,可以归纳出只需判断(X+TempX/2,Y+TempY/2)位置是否有子便可。而判断塞象时,只用判段(X+TempX/2,Y+TempY/2)的位置是否有子即可。
判断将(帅)和士是否在3*3的营里只需看其坐标是否在(endx >= 4 && endx <= 6) && (endy >= 1 && endy <= 3 || endy >= 8 && endy <= 10)中。
六、棋盘的背景颜色以及棋子的颜色更改和每步的刷新棋盘
- 刷新棋盘很简单,只需在每次的Show()之前加函数如下即可
1
system("cls");
- 棋盘及棋子颜色由windows.h头文件中的函数实现,具体如下其中0xFC中F为字体的背景色,C为字体色;只有如0xF这种情况只改字体颜色!!!切记!!!!
1
2
3
4
HANDLE handle;
handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle, 0xFC);
附上颜色表:0 = 黑色 8 = 灰色 1 = 蓝色 9 = 淡蓝色 2 = 绿色 A = 淡绿色 3 = 浅绿色 B = 淡浅绿色 4 = 红色 C = 淡红色 5 = 紫色 D = 淡紫色 6 = 黄色 E = 淡黄色 7 = 白色 F = 亮白色 PS:附上完整代码
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
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
using namespace std;
class Chessboard;
class Chess
{
private:
int Id;
public:
Chess(int x) :Id(x) {}
int Get() //取ID
{
return Id;
}
virtual bool Judgement(Chessboard& ch, int startx, int starty, int endx, int endy) = 0;//判断走步合理性
virtual ~Chess() {}
};
class Chessboard
{
private:
Chess *c[10][11]; //棋盘:X为横(9),Y为纵(10),从1开始记
char Chessword[15][4] = { "兵","炮","车","马","相","仕","帅"," ","将","士","象","馬","車","砲","卒" };
public:
static int Player; //上半区为1,下半区为-1
static bool End; //判断是否结束
Chessboard();
Chess *Get(int x, int y);//返回指定点的指针
int Getid(int x, int y);//返回指定点处棋子ID的指针
bool Move(int startx, int starty, int endx, int endy); //移动
void Init(); //初始化棋子
void Show(); //打印
void Play(); //开始游戏
~Chessboard();
};
class General :public Chess//将、帅类,ID为-1和1
{
public:
General(int i) :Chess((i == 0 ? -1 : 1)) {}
bool Judgement(Chessboard& ch, int startx, int starty, int endx, int endy)
{
int TempX = startx - endx;
int TempY = starty - endy;
int S_Id = ch.Getid(startx, starty);
int E_Id = ch.Getid(endx, endy);
if ((S_Id*E_Id <= 0) && (TempX*TempX + TempY * TempY == 1) && (endx >= 4 && endx <= 6) && (endy >= 1 && endy <= 3 || endy >= 8 && endy <= 10))
{
return true;
}
return false;
}
~General()
{
Chessboard::End = false;
}
};
class BodyGuard :public Chess//士、仕类,ID为-2和2
{
public:
BodyGuard(int i) :Chess((i == 0 ? -2 : 2)) {}
bool Judgement(Chessboard& ch, int startx, int starty, int endx, int endy)
{
int TempX = startx - endx;
int TempY = starty - endy;
int S_Id = ch.Getid(startx, starty);
int E_Id = ch.Getid(endx, endy);
if ((S_Id*E_Id <= 0) && (TempX*TempX + TempY * TempY == 2) && (endx >= 4 && endx <= 6) && (endy >= 1 && endy <= 3 || endy >= 8 && endy <= 10))
{
return true;
}
return false;
}
};
class Chancellor :public Chess//象、相类,ID为-3和3
{
public:
Chancellor(int i) :Chess((i == 0 ? -3 : 3)) {}
bool Judgement(Chessboard& ch, int startx, int starty, int endx, int endy)
{
int TempX = startx - endx;
int TempY = starty - endy;
int S_Id = ch.Getid(startx, starty);
int E_Id = ch.Getid(endx, endy);
if ((S_Id*E_Id <= 0) && (TempX*TempX + TempY * TempY == 8) && (endx % 2 != 0 && endx >= 1 && endy <= 9) && ((starty - 1) / 5 == (endy - 1) / 5) && !ch.Get(startx + (TempX / 2), starty + (TempY / 2)))
{
return true;
}
return false;
}
};
class Horse :public Chess//馬、马类,ID为-4和4
{
public:
Horse(int i) :Chess((i == 0 ? -4 : 4)) {}
bool Judgement(Chessboard& ch, int startx, int starty, int endx, int endy)
{
int TempX = startx - endx;
int TempY = starty - endy;
int S_Id = ch.Getid(startx, starty);
int E_Id = ch.Getid(endx, endy);
if ((S_Id*E_Id <= 0) && (TempX*TempX + TempY * TempY == 5) && !ch.Get(startx + (TempX / 2), starty + (TempY / 2)))
{
return true;
}
return false;
}
};
class Chariot :public Chess//車、车类,ID为-5和5
{
public:
Chariot(int i) :Chess((i == 0 ? -5 : 5)) {}
bool Judgement(Chessboard& ch, int startx, int starty, int endx, int endy)
{
int TempX = startx - endx;
int TempY = starty - endy;
int S_Id = ch.Getid(startx, starty);
int E_Id = ch.Getid(endx, endy);
if ((S_Id*E_Id <= 0) && (!(TempX&&TempY)) && (TempX + TempY))
{
if (TempX)
{
int Sign = (TempX > 0 ? -1 : 1);
for (int i = 1; i < fabs(TempX); i++)
{
if (ch.Get(startx + Sign * i, starty))
{
return false;
}
}
}
else
{
int Sign = (TempY > 0 ? -1 : 1);
for (int i = 1; i < fabs(TempY); i++)
{
if (ch.Get(startx, starty + Sign * i))
{
return false;
}
}
}
return true;
}
return false;
}
};
class Cannon :public Chess//砲、炮类,ID为-6和6
{
public:
Cannon(int i) :Chess((i == 0 ? -6 : 6)) {}
bool Judgement(Chessboard& ch, int startx, int starty, int endx, int endy)
{
int TempX = startx - endx;
int TempY = starty - endy;
int S_Id = ch.Getid(startx, starty);
int E_Id = ch.Getid(endx, endy);
if ((S_Id*E_Id <= 0) && (!(TempX&&TempY)) && (TempX + TempY))
{
int Tmp = 0;
if (TempX)
{
int Sign = (TempX > 0 ? -1 : 1);
for (int i = 1; i < fabs(TempX); i++)
{
if (ch.Get(startx + Sign * i, starty))
{
Tmp++;
}
}
}
else
{
int Sign = (TempY > 0 ? -1 : 1);
for (int i = 1; i < fabs(TempY); i++)
{
if (ch.Get(startx, starty + Sign * i))
{
Tmp++;
}
}
}
if (E_Id)
{
if (Tmp == 1)
{
return true;
}
}
else
{
if (!Tmp)
{
return true;
}
}
}
return false;
}
};
class Soldier :public Chess//卒、兵类,ID为-7和7
{
public:
Soldier(int i) :Chess((i == 0 ? -7 : 7)) {}
bool Judgement(Chessboard& ch, int startx, int starty, int endx, int endy)
{
int TempX = startx - endx;
int TempY = starty - endy;
int S_Id = ch.Getid(startx, starty);
int E_Id = ch.Getid(endx, endy);
if ((S_Id*E_Id <= 0) && (S_Id*TempY <= 0))
{
if (fabs(TempY) == 1 && TempX == 0)
{
return true;
}
if (fabs(TempX) == 1 && TempY == 0)
{
if (((starty - 1) / 5 == 0 && S_Id < 0) || ((starty - 1) / 5 == 1 && S_Id > 0))
{
return true;
}
}
}
return false;
}
};
int Chessboard::Player = -1;
bool Chessboard::End = true;
inline Chessboard::Chessboard()
{
memset(c, NULL, sizeof(c));
}
inline Chess * Chessboard::Get(int x, int y)
{
return c[x][y];
}
int Chessboard::Getid(int x, int y)
{
if (c[x][y] != NULL)
{
return c[x][y]->Get();
}
return NULL;
}
bool Chessboard::Move(int startx, int starty, int endx, int endy)
{
if (startx >= 1 && startx <= 9 && starty >= 1 && starty <= 10 && endx >= 1 && endx <= 9 && endy >= 1 && endy <= 10 && Getid(startx, starty) && Getid(startx, starty)*Player > 0 && c[startx][starty]->Judgement(*this, startx, starty, endx, endy))
{
if (c[endx][endy] != NULL)
{
delete c[endx][endy]; //吃子
}
c[endx][endy] = c[startx][starty];
c[startx][starty] = NULL;
Player *= -1; //更换玩家操作
return true;
}
else
{
cout << "走法错误,请重新输入:" << endl;
return false;
}
}
void Chessboard::Init()
{
c[1][1] = new Chariot(1);
c[9][1] = new Chariot(1);
c[2][1] = new Horse(1);
c[8][1] = new Horse(1);
c[3][1] = new Chancellor(1);
c[7][1] = new Chancellor(1);
c[4][1] = new BodyGuard(1);
c[6][1] = new BodyGuard(1);
c[5][1] = new General(1);
c[2][3] = new Cannon(1);
c[8][3] = new Cannon(1);
c[1][4] = new Soldier(1);
c[3][4] = new Soldier(1);
c[5][4] = new Soldier(1);
c[7][4] = new Soldier(1);
c[9][4] = new Soldier(1);
c[1][10] = new Chariot(0);
c[9][10] = new Chariot(0);
c[2][10] = new Horse(0);
c[8][10] = new Horse(0);
c[3][10] = new Chancellor(0);
c[7][10] = new Chancellor(0);
c[4][10] = new BodyGuard(0);
c[6][10] = new BodyGuard(0);
c[5][10] = new General(0);
c[2][8] = new Cannon(0);
c[8][8] = new Cannon(0);
c[1][7] = new Soldier(0);
c[3][7] = new Soldier(0);
c[5][7] = new Soldier(0);
c[7][7] = new Soldier(0);
c[9][7] = new Soldier(0);
}
void Chessboard::Show()
{
cout << endl;
HANDLE handle;
handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle, 0xF0);
cout << " P2 一 二 三 四 五 六 七 八 九" << endl << endl;
char num[11][4] = { "零","一","二","三","四","五","六","七","八","九" ,"十" };
for (int i = 1; i < 11; i++)
{
if (i == 6)
{
SetConsoleTextAttribute(handle, 0xF0);
cout << " ————楚河 汉界————" << endl << endl;
}
SetConsoleTextAttribute(handle, 0xF0);
cout << " " << num[i] << " ";
for (int j = 1; j < 10; j++)
{
if (c[j][i] != NULL)
{
if (c[j][i]->Get() > 0)
{
SetConsoleTextAttribute(handle, 0xF2);
cout << Chessword[c[j][i]->Get() + 7] << " ";
}
else
{
SetConsoleTextAttribute(handle, 0xFC);
cout << Chessword[c[j][i]->Get() + 7] << " ";
}
}
else if ((i == 2 && j == 5) || (i == 9 && j == 5))
{
SetConsoleTextAttribute(handle, 0xF0);
cout << "米" << " ";
}
else
{
SetConsoleTextAttribute(handle, 0xF0);
cout << "十" << " ";
}
}
cout << endl << endl;
}
SetConsoleTextAttribute(handle, 0xF0);
cout << " P1 一 二 三 四 五 六 七 八 九" << endl << endl;
}
void Chessboard::Play()
{
HANDLE handle;
handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle, 0xFC);
system("cls");
this->Init();
this->Show();
do
{
int startx, starty, aimx, aimy, iflag;
int sid, aid;
iflag = 0;
do
{
sid = aid = 0;
if ((Chessboard::Player == 1 ? 2 : 1) == 1)
{
SetConsoleTextAttribute(handle, 0xFC);
}
else
{
SetConsoleTextAttribute(handle, 0xF2);
}
cout << "请P" << (Chessboard::Player == 1 ? 2 : 1) << "输入起始棋子位置与目标位置的坐标:" << endl;
cin >> startx >> starty >> aimx >> aimy;
} while (!this->Move(startx, starty, aimx, aimy));
system("cls");
this->Show();
for (int i = 4; i < 7; i++)
{
for (int j = 1; j < 11; j++)
{
if (c[i][j] != NULL)
{
if ((int)fabs(c[i][j]->Get()) == 1)
{
iflag++;
}
else if (iflag != 0 && iflag != 2)
{
if ((int)fabs(c[i][j]->Get()) != 1)
{
iflag--;
}
}
}
}
}
if (iflag == 2)
{
Player *= -1;
Chessboard::End = false;
}
} while (Chessboard::End);
if ((Chessboard::Player == 1 ? 1 : 2) == 1)
{
SetConsoleTextAttribute(handle, 0xFC);
}
else
{
SetConsoleTextAttribute(handle, 0xF2);
}
cout << "结束,赢家是Player" << (Chessboard::Player == 1 ? 1 : 2) << endl;
}
Chessboard::~Chessboard()
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 11; j++)
{
if (c[i][j] != NULL)
{
delete c[i][j];
c[i][j] = NULL;
}
}
}
}
using namespace std;
int main()
{
Chessboard C;
C.Play();
system("pause");
}