参考自Originum学长的博客,个人进行了一些小修改,
原博客地址:https://blog.csdn.net/Originum/article/details/80356452
联系邮箱:Originum@126.com
本人博客地址:Megalomania
一个简单的中国象棋游戏,主要实现:
1、两人对弈;
2、简单的棋子移动以及规则判断,不符合规则便重走;
3、有一方获胜便结束游戏。
一、类的设计
- “Chess”类:
(1) 私有成员有int型的Id,用于记录棋子的归属以及判断轮到的玩家是否可以移动;
(2) 公共成员函数有Get函数来获取Id,以及判断走法是否正确的Judgement纯虚函数;
(3) 作为基类来派生出其他棋子的类。
具体代码如下:
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() {}
};
- “Chessboard”类:
(1) 私有成员有一个10*11的指向Chess类的指针(为了方便操作将0的位置腾了出来),用于存放棋子的地址;
(2) 还有一个Char型的Chessword数组用于存放棋子的名字(一个汉字占4个Char型空间);
(3) 公共成员部分有static int型的 Player用于记录轮到哪个玩家以及哪个玩家获胜。
具体实现代码如下:
class Chessboard
{
private:Chess *c[10][11]; //棋盘:X为横(9),Y为纵(10),从1开始记char Chessword[15][4] = { "兵","炮","车","马","相","仕","帅"," ","将","士","象","馬","車","砲","卒" };
public:static int Player; //上半区为1,下半区为-1static 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)控制走棋是否继续)。
~General(){Chessboard::End = false;}
- 当有一位玩家的某一步棋使双方的将(帅)面对面时,直接判负。具体实现是检测将(帅)所在的4、5、6 三列中将和帅是否在同一类且两者间的指针为全为空指针;
int 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()之前加函数如下即可
system("cls");
- 棋盘及棋子颜色由windows.h头文件中的函数实现,具体如下
#include<windows.h>HANDLE handle;handle = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleTextAttribute(handle, 0xFC);
其中0xFC中F为字体的背景色,C为字体色;只有如0xF这种情况只改字体颜色!!!切记!!!!
附上颜色表:
0 = 黑色 | 8 = 灰色 |
---|---|
1 = 蓝色 | 9 = 淡蓝色 |
2 = 绿色 | A = 淡绿色 |
3 = 浅绿色 | B = 淡浅绿色 |
4 = 红色 | C = 淡红色 |
5 = 紫色 | D = 淡紫色 |
6 = 黄色 | E = 淡黄色 |
7 = 白色 | F = 亮白色 |
PS:附上完整代码
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<windows.h>
#include<cmath>
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,下半区为-1static 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");
}
文章转载请注明本人博客地址:
转载自:Megalomania - 【C++】中国象棋