0%

【C/C++】C++下的简易中国象棋

参考自Originum学长的博客,个人进行了一些小修改,
原博客地址:https://blog.csdn.net/Originum/article/details/80356452
联系邮箱:Originum@126.com

一个简单的中国象棋游戏,主要实现:
1、两人对弈;
2、简单的棋子移动以及规则判断,不符合规则便重走;
3、有一方获胜便结束游戏。

一、类的设计

  1. “Chess”类:
    (1) 私有成员有int型的Id,用于记录棋子的归属以及判断轮到的玩家是否可以移动;
    (2) 公共成员函数有Get函数来获取Id,以及判断走法是否正确的Judgement纯虚函数;
    (3) 作为基类来派生出其他棋子的类。

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
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() {}
};
  1. “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()函数时,会判断:

  1. 传入的两个位置是否越界(超出了10*11数组的范围),越界则返回false。
  2. 传入的开始位置的点的id是否为零(当点上无棋子,也就是空指针的时候为零),若为零则返回false。
  3. 当前选的棋子是否是这一回对应那一方的棋子,若不是则返回false。
  4. 对当前棋子的具体规则进行判断(通过指针调用虚函数,判断该棋子的具体规则)。若错误则返回false。

若以上判断都准确无误,则把棋子走到对应位置:

  1. 不吃子:目标位置为空指针时,把目标位置的指针指向该棋子对象,把棋子的起始位置指针赋值NULL,设为空指针。
  2. 吃子:delete目标位置的棋子对象,该棋子被吃。把目标位置的指针指向该棋子对象,把棋子的起始位置指针赋值NULL,设为空指针。

四、判断结束

判断结束:

  1. 棋盘类有一个静态变量bool End, 初始化为true。当将(帅)对象被析构时,把end赋值为false,表示棋局结束,退出走棋的循环(while(chessboard :: end)控制走棋是否继续)。

    1
    2
    3
    4
    ~General()
    {
    Chessboard::End = false;
    }
  2. 当有一位玩家的某一步棋使双方的将(帅)面对面时,直接判负。具体实现是检测将(帅)所在的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
    26
    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之间交替表示棋手的轮流下棋。以此来鉴别胜利的是哪一方

    五、某些棋子的规则算法

  3. 判断目标点是否是己方的棋:两方的子的Id互为相反数,所以只有当S_Id* E_Id>0时表示目标位置是己方的子,不能走。因而只有满足S_Id*E_Id<=0才是合法条件(空位置为0)。

  4. 判断是否满足只能走1格,或马走日,象走田的条件时,只需要区偏移的距离进行判断。例如TempX*TempX+TempY+TempY表示偏移量的平方,当该值为1时,表示只走了任意方向的一格;当值为2时,表示士的斜走;值为5时,表示马走日;为8时,表示象走田。

  5. 判断马走日的蹩脚马时,用数学计算方法,可以归纳出只需判断(X+TempX/2,Y+TempY/2)位置是否有子便可。而判断塞象时,只用判段(X+TempX/2,Y+TempY/2)的位置是否有子即可。

  6. 判断将(帅)和士是否在3*3的营里只需看其坐标是否在(endx >= 4 && endx <= 6) && (endy >= 1 && endy <= 3 || endy >= 8 && endy <= 10)中。

六、棋盘的背景颜色以及棋子的颜色更改和每步的刷新棋盘

  1. 刷新棋盘很简单,只需在每次的Show()之前加函数如下即可
    1
    system("cls");
  2. 棋盘及棋子颜色由windows.h头文件中的函数实现,具体如下
    1
    2
    3
    4
    #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:附上完整代码

    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
    #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,下半区为-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");
    }
-------- 本文结束 感谢阅读 --------