本文章通过C++中的900行代码实现中国象棋游戏以及相关功能,主要的内容如下:
1.设置未进入游戏前的主页面;
2.绘制棋盘(如果有刚好尺寸的图片也可直接加载),包括棋盘网格,炮与兵的特殊标记绘制;
3.绘制和创建棋子,并令其初始化在棋盘的相应位置;
4.游戏开始,开始计时,鼠标点击棋子,控制其在棋盘的移动;
5.各类棋子的行棋规则判定(主要思路是先让其能够按照自身规则移动,后面再判断是否吃子)
6.其它功能,主要包括:
不同场合和时刻的音效处理;
回合计数;
是否认输判断:当游戏进行到10回合以后方可发起认输;
倒计时设置,任一颜色方先耗尽时间则判定为输;
吃子时的简单动图Gif读取展示;
7.视频效果展示

首先给出所有的头文件 和 全局变量 以及函数声明

#define _CRT_SECURE_NO_DEPRECATE  //opencv
#include <iostream>
#include "Pieces.h"
#include <graphics.h>
#include <conio.h>
#include <math.h>
#include <ctime>
#include <thread>
#include <Windows.h>
#include <string>
#include <mmsystem.h>
#pragma  comment(lib,"winmm.lib")
#include<opencv2/opencv.hpp>     //opencv
using namespace cv;              //opencv
using namespace std;
//标注"opencv"的项是为了实现吃子时动图GIF的展示效果,需要提前下载和设置好OPENCV的环境。#define ROW 10         //棋盘行
#define COL 9          //棋盘列
#define UDGAP 70       //上下棋盘距边界的间隙
#define LGAP 308       //左棋盘距边界的间隙
#define RGAP 50        //右棋盘距边界的间隙
#define SIZE 90        //棋盘 网格尺寸
#define Ches_Ri 32     //棋子内圆径
#define Ches_Ro 40     //棋子外圆径
//本文内容是在分辨率为1920 * 1080的显示屏上完成的,因此上面设置的各数值需要在同样的分辨率下才能体现出布局和排版效果。Pieces Mesh[ROW][COL];
DWORD ord[2] = { RED,BLACK };     //颜色方
int Time[4] = { 14,59,14,59 };    //倒计时  双方各15分钟
int R_C[4] = { -1, -1, -1, -1 };  //棋子选取移动前后的坐标
int Msgxyz[3] = { -1, -1, -1 };   //前2个值判断是否点击认输,第3个值用于判断是否认输退出循环
enum Pece                         //棋子枚举
{SPACE = -1,                   //从0开始,即:車=0, 马=1, 象=2...兵=13.車, 马, 象, 士, 将, 炮, 卒,    //黑棋车, 馬, 相, 仕, 帅, 砲, 兵,    //红棋
};
Pece   redpiece[] = { 车, 馬, 相, 仕, 帅, 砲, 兵 };
Pece blackpiece[] = { 車, 马, 象, 士, 将, 炮, 卒 };
const char* pecename[] = { "车","馬","相","仕","将","砲","卒",   //棋子名称"車","马","象","士","帅","炮","兵" };
void MainPage();            //主页界面
void GamePage();            //游戏界面
void Flicker();             //闪烁效果
void DrawBoard();          //绘制棋盘
void Drawp1(int x, int y);  //兵和炮的位置标记
void Drawp2(int x, int y);  //“士”的路线
void CreatPieces();         //加载棋子
void Initgame();            //初始化游戏开始界面
void GameSTART();           //游戏开始
void MouseClick(int t, int& count, int& num);           //鼠标控制棋子
void PieceMove(int& num);   //棋子移动
int  PlayRules();           //行棋规则
void PlayGIF(int& num);     //吃棋动画
bool Judgexy(int x, int y, int l, int u, int r, int b); //判断某点在不在某个区域内
void Round(int& count, int& num);                       //回合数 及 操作方判断
int  Timer(int& num);       //倒计时
void DrawTime(int& num);    //时间绘制及更新
void IfGivein(int& count, int& num);                    //是否认输以及认输是否有效

1 开始游戏前的主页面

void MainPage()
{IMAGE img;loadimage(&img, "游戏封面.jpg");    //宽:1065 高:655    initgraph(img.getwidth(), img.getheight());putimage(0, 0, &img);              //放置图片位置并显示mciSendString("play BGM1.mp3 repeat", NULL, 0, NULL); //主界面循环播放BGM1settextstyle(30, 0, "楷体");       // 字体  可随意在网页上下载一个 改名放在文件夹目录使用setbkmode(TRANSPARENT);            //透明打印settextcolor(BLACK);outtextxy(400, 250, "欢迎进入中国象棋!"); Flicker();                         //闪烁效果closegraph();
}
void Flicker()
{while (!_kbhit())                 // 判断是否键入 如果键入 则退出{settextcolor(RED);                        outtextxy(830, 620, "按任意键继续...");Sleep(500);settextcolor(RGB(128, 128, 255));outtextxy(830, 620, "按任意键继续...");Sleep(500);settextcolor(BLACK);outtextxy(830, 620, "按任意键继续...");Sleep(500);}mciSendString("close BGM1.mp3", NULL, 0, NULL);
}

2 游戏界面(包含:棋盘绘制+棋子位置初始化+棋子创建)

2.1 游戏界面

void GamePage()
{mciSendString("play BGM2.mp3 repeat", NULL, 0, NULL);IMAGE img, img_p1, img_p2;loadimage(&img, "游戏界面.jpg");loadimage(&img_p1, "杨玉环.jpg");loadimage(&img_p2, "陈圆圆.jpg");HWND hWnd = initgraph(img.getwidth(), img.getheight(), 1); //窗口句柄定义,为了居中显示,需结合电脑屏幕分辨率设置SetWindowPos(hWnd, NULL, 197, 32, 1536, 985, SWP_SHOWWINDOW);putimage(0, 0, &img);putimage(1083, 100, &img_p1);putimage(1083, 640, &img_p2);setfillcolor(RGB(253, 216, 161));fillrectangle(258, 20, 1078, 930);              //绘制有边框的矩形填充system("color f0");DrawBoard();Initgame();CreatPieces();
}

2.2 棋盘绘制

void DrawBoard()
{//可使用生成的图片,但需要理想的图片背景  如果有且知道各网格点坐标,则不需要绘制//IMAGE BKGD;//loadimage(&BKGD,"棋盘.jpg");//putimage(0, 0, &BKGD);//自定义棋盘布局  根据需要布置棋局     结合屏幕分辨率调节,因此没有注释setfillcolor(RGB(253, 216, 161)); fillrectangle(258, 20, 1078, 930);    //棋盘区fillrectangle(1078, 430, 1285, 520);  //回合区setfillcolor(WHITE);fillrectangle(1078, 70, 1285, 100);   //区域1fillrectangle(1078, 310, 1285, 430);  //区域2fillrectangle(1078, 520, 1285, 640);  //区域3fillrectangle(1078, 850, 1285, 880);  //区域4setcolor(BLACK);setlinestyle(0, 3);           setcolor(RED);rectangle(LGAP - 5, UDGAP - 5, 1078 - RGAP + 5, 950 - UDGAP + 5); setlinestyle(0, 2);for (int x = LGAP; x < 1078; x += SIZE){//绘制网格竖线if (x == LGAP || x == 1078 - RGAP)line(x, UDGAP, x, 950 - UDGAP);else{line(x, UDGAP, x, 4 * SIZE + UDGAP);line(x, 520, x, 950 - UDGAP);}}for (int y = UDGAP; y < 950; y += SIZE){//绘制网格横线line(LGAP, y, 1078 - RGAP, y);}for (int xb = LGAP, xp = LGAP + SIZE; xb < 1078 || xp < 1078; xb += 2 * SIZE, xp += 6 * SIZE){//绘制兵和炮的位置Drawp1(xb, UDGAP + 3 * SIZE);       //楚河界上兵Drawp1(xb, UDGAP + 6 * SIZE);       //楚河界下兵if (xb <= 230){Drawp1(xp, UDGAP + 2 * SIZE);   //楚河界上炮   炮也可重新使用循环绘制Drawp1(xp, UDGAP + 7 * SIZE);   //楚河界下炮}}Drawp2(LGAP + 3 * SIZE, UDGAP);Drawp2(LGAP + 3 * SIZE, UDGAP + 7 * SIZE);setbkmode(TRANSPARENT);settextstyle(76, 0, "楷体");settextcolor(BLACK);outtextxy(400, 437, "楚 河    汉 界");//汉字  楚河汉界int init = -1;DrawTime(init); //时间初始化
}void Drawp1(int x, int y)   //兵标记
{setlinecolor(BLACK);setlinestyle(0, 3);int d1 = 5, d2 = 12;if (x == LGAP)              //左侧边界 4段  棋盘线外无标志{line(x + d1, y - d1, x + d2, y - d1);line(x + d1, y + d1, x + d2, y + d1);line(x + d1, y - d2, x + d1, y - d1);line(x + d1, y + d1, x + d1, y + d2);}else if (x == 1078 - RGAP)  //右侧边界 4段  棋盘线外无标志{line(x - d2, y - d1, x - d1, y - d1);line(x - d2, y + d1, x - d1, y + d1);line(x - d1, y - d2, x - d1, y - d1);line(x - d1, y + d1, x - d1, y + d2);}else                        //中间区域 8段{//4短横line(x - d2, y - d1, x - d1, y - d1);line(x - d2, y + d1, x - d1, y + d1);line(x + d1, y - d1, x + d2, y - d1);line(x + d1, y + d1, x + d2, y + d1);//4短竖line(x - d1, y - d2, x - d1, y - d1);line(x + d1, y - d2, x + d1, y - d1);line(x - d1, y + d1, x - d1, y + d2);line(x + d1, y + d1, x + d1, y + d2);}}void Drawp2(int x, int y)   //炮标记
{setlinecolor(RED);setlinestyle(0, 2);line(x, y, x + 2 * SIZE, y + 180);line(x + 180, y, x, y + 2 * SIZE);
}

2.3 时间打印输出

void DrawTime(int& num)    //在 “认输”  右侧打印时间  实现数字小于 10 时添 0
{char m[5], s[5];setfillcolor(WHITE);settextcolor(BLACK);settextstyle(30, 0, "楷体");if (num == 0 || num == -1){_itoa_s(Time[0], m, 10);_itoa_s(Time[1], s, 10);fillrectangle(1190, 30, 1275, 60);if (Time[0] < 10){outtextxy(1195, 30, "0");outtextxy(1210, 30, m);}elseouttextxy(1195, 30, m);outtextxy(1223, 25, ":");if (Time[1] < 10){outtextxy(1235, 30, "0");outtextxy(1250, 30, s);}elseouttextxy(1235, 30, s);}if (num == 1 || num == -1){_itoa_s(Time[2], m, 10);_itoa_s(Time[3], s, 10);fillrectangle(1190, 890, 1275, 920);if (Time[2] < 10){outtextxy(1195, 890, "0");outtextxy(1210, 890, m);}elseouttextxy(1195, 890, m);outtextxy(1223, 885, ":");if (Time[3] < 10){outtextxy(1235, 890, "0");outtextxy(1250, 890, s);}elseouttextxy(1235, 890, s);}
}

3 棋子位置初始化 与 创建

3.1 棋子位置初始化

void Initgame()
{//为ID赋值 step_2 for (int i = 0; i < ROW; i++){for (int j = 0; j < COL; j++){Pece ID = SPACE;DWORD type = 0;if (i <= 4)       //红棋{type = RED;if (i == 0)   //红棋第一排棋子赋值{if (j <= 4)ID = redpiece[j];elseID = redpiece[8 - j];}if (i == 2 && (j == 1 || j == 7))   //红棋 炮 赋值ID = redpiece[5];if (i == 3 && j % 2 == 0)           //红棋 兵 赋值ID = redpiece[6];}else               //黑棋{type = BLACK;if (i == 9)    //黑棋第一排棋子赋值{if (j <= 4)ID = blackpiece[j];elseID = blackpiece[8 - j];}if (i == 7 && (j == 1 || j == 7))   //黑棋 砲 赋值ID = blackpiece[5];if (i == 6 && j % 2 == 0)           //黑棋 卒 赋值ID = blackpiece[6];}Mesh[i][j]._id = ID;Mesh[i][j]._type = type;Mesh[i][j].surv = 1;        //初始均为存活   判断将领存活状态和游戏结束Mesh[i][j].river = 0;       //初始均未过河Mesh[i][j].Cor_x = j * SIZE + LGAP;   //具体横坐标Mesh[i][j].Cor_y = i * SIZE + UDGAP;  //具体纵坐标}}
}

3.2 棋子创建

void CreatPieces()
{setlinestyle(0, 2);settextstyle(50, 0, "楷体");setbkmode(0);  //透明显示  等同于  TRANSPARENTsetfillcolor(GREEN);fillrectangle(1078, 20, 1178, UDGAP);fillrectangle(1078, 950 - UDGAP, 1178, 930);outtextxy(1078, 20, "认输");settextcolor(BLACK);outtextxy(1078, 950 - UDGAP, "认输");for (int i = 0; i < ROW; i++){for (int j = 0; j < COL; j++){if (Mesh[i][j]._id != SPACE){//绘制棋子外观setfillcolor(RGB(253, 216, 161));setlinecolor(Mesh[i][j]._type);fillcircle(Mesh[i][j].Cor_x, Mesh[i][j].Cor_y, Ches_Ro);   fillcircle(Mesh[i][j].Cor_x, Mesh[i][j].Cor_y, Ches_Ri);   //为棋子赋名settextcolor(Mesh[i][j]._type);outtextxy(Mesh[i][j].Cor_x - 25, Mesh[i][j].Cor_y - 25, pecename[Mesh[i][j]._id]);}}}
}

4 游戏开始

4.1 游戏开始

void GameSTART()
{//R_C[0] R_C[1]表示第1次点击的行列坐标,R_C[2] R_C[3]表示第2次点击的行列坐标 int num = 0, count = 0;std::thread func1(Timer, ref(num));  //由于计时和点击鼠标同时进行,开启多线程while (R_C[0] == -1 && R_C[2] == -1) //需要一直点击显示窗口坐标   由于每次鼠标点击都需要循环,采点2次则需要2次循环{Round(count, num);             //显示回合数 及 操作方MouseClick(0, count, num);     //采第1个点if (R_C[0] != -1 && Mesh[R_C[0]][R_C[1]]._id != SPACE && ord[num] == Mesh[R_C[0]][R_C[1]]._type)  //第一次点击的位置在棋盘上 且 为正确的颜色{  setlinecolor(RGB(0, 128, 255));         //蓝色方框表示选中int tx = Mesh[R_C[0]][R_C[1]].Cor_x;int ty = Mesh[R_C[0]][R_C[1]].Cor_y;rectangle(tx - Ches_Ro, ty - Ches_Ro, tx + Ches_Ro, ty + Ches_Ro);while (R_C[2] == -1){MouseClick(2, count, num);        //采第2个点if (R_C[2] != -1){          if (!(R_C[0] == R_C[2] && R_C[1] == R_C[3]) && PlayRules()){//如果两次点击的不为同一个棋子 且 满足行棋规则if ((Mesh[R_C[2]][R_C[3]]._id != SPACE && Mesh[R_C[0]][R_C[1]]._type != Mesh[R_C[2]][R_C[3]]._type)|| Mesh[R_C[2]][R_C[3]]._id == SPACE){//如果第二次点击的位置有棋子且颜色与第一次不一样  或  第二次点击的位置无棋子  则 移动棋子PieceMove(num);if (++num == 2)    //结束一轮,即2次后   重新由红方开始{num = 0;count++;}}}DrawBoard();CreatPieces();R_C[2] = -1;R_C[3] = -1;break;}}}R_C[0] = -1;                     //如果第一次选择的位置没有棋子,则无法移动R_C[1] = -1;if (num > 1 || Msgxyz[2] != -1)  //退出条件:将领被吃,认输 或 时间结束break;}func1.join();                        //多线程函数一直执行  直至满足条件退出循环
}

4.2 计时开始

int  Timer(int& num)
{int top, bot;for (; Time[2 * num] >= 0; Time[2 * num]--){for (; Time[2 * num + 1] >= 0; Time[2 * num + 1]--){if (num == 1)  //黑方计时{top = 890;bot = 920;}else{top = 30; //红方计时bot = 60;}DrawTime(num); //打印时间if (Time[2 * num] == 0 && Time[2 * num + 1] == 0) //时间结束判断{if (num == 0)Mesh[0][4].surv = false;elseMesh[9][4].surv = false;num = 10;return 1;}if (Time[2 * num + 1] == 0){Time[2 * num + 1] = 59;break;}if (Mesh[0][4].surv == false || Mesh[9][4].surv == false)return 1;Sleep(1000);}}return 0;
}

4.3 鼠标点击控制棋子

void MouseClick(int t, int& count, int& num)
{int flag = 0;if (MouseHit()){MOUSEMSG msg = GetMouseMsg();if (msg.uMsg == WM_LBUTTONDOWN)           //如果点击左键{for (int i = 0; i < 10; i++){for (int j = 0; j < 9; j++){//int d1 = abs((msg.y - GAP) - i * SIZE);  //方形区域//int d2 = abs((msg.x - GAP) - j * SIZE);//if (d1 + d2 <= 2 * Ches_Ro)//{//cout << "(" << i << " , " << j << ")" << endl;//    break;//}Msgxyz[0] = msg.x;Msgxyz[1] = msg.y;IfGivein(count, num);             //判断是否点击了认输double R2 = pow((msg.y - UDGAP) - i * SIZE, 2) + pow((msg.x - LGAP) - j * SIZE, 2);if (R2 <= pow(Ches_Ro, 2))        //圆形区域{flag = 1;R_C[t] = i;R_C[t + 1] = j;Mesh[R_C[t]][R_C[t + 1]].Doriver();break;}}if (i == 9 && flag == 0)cout << "未选中任何棋子位置!" << endl;if (flag == 1)break;}}}
}

4.4 棋子移动 及 播放gif

void PieceMove(int& num)
{string color[2] = { "红","黑" };if (Mesh[R_C[2]][R_C[3]]._id != SPACE) {cout << color[num] << "方." << pecename[Mesh[R_C[0]][R_C[1]]._id] << "(" << R_C[0] << " , " << R_C[1] << ")"<< "  吃 (" << R_C[2] << " , " << R_C[3] << ")" << color[1 - num] << "方." << pecename[Mesh[R_C[2]][R_C[3]]._id] << endl;std::thread func2(PlayGIF, ref(num));  //播放吃子动画的同时移动棋子Mesh[R_C[2]][R_C[3]].surv = false;if (Mesh[0][4].surv == false || Mesh[9][4].surv == false){cout << endl << color[1 - num] << "方将领被吃,游戏结束!" << endl << endl;mciSendString("play 游戏结束.mp3", NULL, 0, NULL);num = 10;}func2.join();}else{cout << color[num] << "方." << pecename[Mesh[R_C[0]][R_C[1]]._id] << "(" << R_C[0] << " , " << R_C[1] << ")"<< "  → (" << R_C[2] << " , " << R_C[3] << ")" << endl;}mciSendString("play 走棋.mp3", NULL, 0, NULL);Mesh[R_C[2]][R_C[3]]._id = Mesh[R_C[0]][R_C[1]]._id;    //没有必要对其他棋子的存活状态进行更新,如果需要将被吃的棋子展示,则可以更新Mesh[R_C[2]][R_C[3]]._type = Mesh[R_C[0]][R_C[1]]._type;Mesh[R_C[0]][R_C[1]]._id = SPACE;
}void PlayGIF(int& num)
{cv::VideoCapture capture;cv::namedWindow("Display window", WINDOW_AUTOSIZE);cv::moveWindow("Display window", 735, 435);                    //移动窗口到屏幕中央 窗口左上角在坐标(735,435)cv::setWindowProperty("Display window", WND_PROP_TOPMOST, 1);  //图片顶端显示if (num == 0){capture.open("杨玉环.gif");mciSendString("play 吃棋yyh.mp3", NULL, 0, NULL);}else{capture.open("陈圆圆.gif");mciSendString("play 吃棋cyy.mp3", NULL, 0, NULL);}cv::Mat frame;HWND win_handle = FindWindow(0, "Display window");                       unsigned int flags = (SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER);  flags &= ~SWP_NOSIZE;                                                            while (capture.read(frame)){if (frame.empty())break;cv::imshow("Display window", frame);SetWindowPos(win_handle, HWND_NOTOPMOST, 735, 435, 280, 292, flags);     SetWindowLong(win_handle, GWL_STYLE, GetWindowLong(win_handle, GWL_EXSTYLE) | WS_EX_TOPMOST);    ShowWindow(win_handle, SW_SHOW); waitKey(150);}capture.release();cv::destroyAllWindows();
}

5 各类棋子移动规则判定

int  PlayRules()
{//R_C[0] R_C[1]表示第1次点击的行列坐标,R_C[2] R_C[3]表示第2次点击的行列坐标 POINT CenterZone[2] = { {0,3},{7,3} };POINT To_Bo_Zone[2] = { {0,0},{5,0} };switch (Mesh[R_C[0]][R_C[1]]._id){case Pece::帅:case Pece::将:{for (int s = 0; s < 2; s++){for (int i = CenterZone[s].x; i <= CenterZone[s].x + 2; i++){for (int j = CenterZone[s].y; j <= CenterZone[s].y + 2; j++){if ((R_C[2] == i && R_C[3] == j)                                    //表示在九宫格内&& (R_C[2] == R_C[0] || R_C[3] == R_C[1])                       //表示只能走直线&& (abs(R_C[2] - R_C[0]) == 1 || abs(R_C[3] - R_C[1]) == 1))    //一次只能走一格{//cout << "KING moved!" << endl;return 1;}}}}return 0;}case Pece::士:case Pece::仕:{for (int s = 0; s < 2; s++){for (int i = CenterZone[s].x; i <= CenterZone[s].x + 2; i++){for (int j = CenterZone[s].y; j <= CenterZone[s].y + 2; j++){if ((R_C[2] == i && R_C[3] == j) && abs(R_C[2] - R_C[0]) == 1 && abs(R_C[3] - R_C[1]) == 1)  //表示在九宫格内且只能走单步斜线{//cout << "SHI moved!" << endl;return 1;}}}}return 0;}case Pece::象:case Pece::相:{for (int s = 0; s < 2; s++){for (int i = To_Bo_Zone[s].x; i <= To_Bo_Zone[s].x + 4; i++){for (int j = To_Bo_Zone[s].y; j <= To_Bo_Zone[s].y + 8; j++){int dx = (R_C[2] - R_C[0]) / 2;int dy = (R_C[3] - R_C[1]) / 2;if ((R_C[2] == i && R_C[3] == j) && abs(dx) == 1 && abs(dy) == 1                //表示在对应区域内且只能走田字&& ((R_C[2] <= 4 && R_C[2] % 2 == 0) || (R_C[2] >= 5 && R_C[2] % 2 == 1))   //表示象无法过河&& Mesh[R_C[0] + dx][R_C[1] + dy]._id == SPACE)                              //表示不存在象脚{//cout << "XIANG moved!" << endl;return 1;}}}}return 0;}case Pece::马:case Pece::馬:{for (int i = 0; i <= 9; i++){for (int j = 0; j <= 8; j++){int dx = (R_C[2] - R_C[0]);int dy = (R_C[3] - R_C[1]);if ((R_C[2] == i && R_C[3] == j)                                           //表示在对应区域内&& ((abs(dx) == 1 && abs(dy) == 2) || (abs(dx) == 2 && abs(dy) == 1))  //表示只能走日字&& Mesh[R_C[0] + dx / 2][R_C[1] + dy / 2]._id == SPACE)                 //表示不存在象脚{//cout << "MA moved!" << endl;return 1;}}}return 0;}case Pece::車:case Pece::车:{//横向移动if ((R_C[2] == R_C[0]))             //两点的行数相等{if (R_C[3] < R_C[1])            //左移{for (int i = R_C[1] - 1; i >= R_C[3]; i--)   //遍历 列R_C[1] 与 列R_C[3]之间是否有棋存在{if (Mesh[R_C[0]][i]._id != SPACE)         //如果存在棋{if (i == R_C[3])                     //如果该棋子即为落棋位置   则  吃子return 1;else                                 //如果第一次遍历到中间存在棋子,但不是要落点的位置,则无法操作return 0;}if (i == R_C[3])                         //如果遍历结束中间不存在棋  则 直接落子return 1;}}if (R_C[3] > R_C[1])            //右移{for (int i = R_C[1] + 1; i <= R_C[3]; i++){if (Mesh[R_C[0]][i]._id != SPACE){if (i == R_C[3])return 1;elsereturn 0;}if (i == R_C[3])return 1;}}}//纵向移动if ((R_C[3] == R_C[1])){if (R_C[2] < R_C[0])            //上移{for (int i = R_C[0] - 1; i >= R_C[2]; i--){if (Mesh[i][R_C[1]]._id != SPACE){if (i == R_C[2])return 1;elsereturn 0;}if (i == R_C[2])return 1;}}if (R_C[2] > R_C[0])            //下移{for (int i = R_C[0] + 1; i <= R_C[2]; i++){if (Mesh[i][R_C[1]]._id != SPACE){if (i == R_C[2])return 1;elsereturn 0;}if (i == R_C[2])return 1;}}}return 0;}case Pece::兵:case Pece::卒:{if (((Mesh[R_C[0]][R_C[1]].river == false) && R_C[3] == R_C[1])            //表示过河前 只能纵向移动&& ((Mesh[R_C[0]][R_C[1]]._type == RED && R_C[2] - R_C[0] == 1)        //且红兵只能下移|| (Mesh[R_C[0]][R_C[1]]._type == BLACK && R_C[2] - R_C[0] == -1)))    //且黑卒只能上移{//cout << "SOLDIER moved!" << endl;return 1;}if ((Mesh[R_C[0]][R_C[1]].river == true) && ((abs(R_C[3] - R_C[1]) == 1)   //表示过河后 不能后退|| (Mesh[R_C[0]][R_C[1]]._type == RED && R_C[2] - R_C[0] == 1)         //红兵不能上移|| (Mesh[R_C[0]][R_C[1]]._type == BLACK && R_C[2] - R_C[0] == -1)))    //黑卒不能下移{//cout << "SOLDIER moved!" << endl;return 1;}return 0;}case Pece::炮:case Pece::砲:{//横向移动if ((R_C[2] == R_C[0]))             //两点的行数相等{if (R_C[3] < R_C[1])            //左移{int ct = 0;for (int i = R_C[1] - 1; i >= R_C[3]; i--)   //遍历 列R_C[1] 与 列R_C[3]之间是否有棋存在{if (Mesh[R_C[0]][i]._id != SPACE)         //如果存在棋{++ct;if (R_C[3] == i && ct == 2)        //如果该棋子的位置即为落棋点 且是第二个遍历到的棋子 则 吃子  第一个存在的点为炮台return 1;if (ct == 2)                          //如果遍历到第二个点,但仍不是落棋点,则无法操作return 0;}if (ct == 0 && R_C[3] == i && Mesh[R_C[2]][R_C[3]]._id == SPACE)     //如果中间不存在棋且落棋点无子  则 直接落子return 1;}}if (R_C[3] > R_C[1])            //右移{int ct = 0;for (int i = R_C[1] + 1; i <= R_C[3]; i++){if (Mesh[R_C[0]][i]._id != SPACE){++ct;if (R_C[3] == i && ct == 2)return 1;if (ct == 2)return 0;}if (ct == 0 && R_C[3] == i && Mesh[R_C[2]][R_C[3]]._id == SPACE)return 1;}}}//纵向移动if ((R_C[3] == R_C[1]))             //两点的列数相等{if (R_C[2] < R_C[0])            //上移{int ct = 0;for (int i = R_C[0] - 1; i >= R_C[2]; i--){if (Mesh[i][R_C[1]]._id != SPACE){++ct;if (R_C[2] == i && ct == 2)return 1;if (ct == 2)return 0;}if (ct == 0 && R_C[2] == i && Mesh[R_C[2]][R_C[3]]._id == SPACE)return 1;}}if (R_C[2] > R_C[0])            //下移{int ct = 0;for (int i = R_C[0] + 1; i <= R_C[2]; i++){if (Mesh[i][R_C[1]]._id != SPACE){++ct;if (R_C[2] == i && ct == 2)return 1;if (ct == 2)return 0;}if (ct == 0 && R_C[2] == i && Mesh[R_C[2]][R_C[3]]._id == SPACE)return 1;}}}return 0;}default: return 0;}
}

6 回合 与 认输

void Round(int& count, int& num)
{settextcolor(BLACK);settextstyle(30, 0, "楷体");char s[5];_itoa_s(count + 1, s, 10);outtextxy(1120, 440, "当前回合");outtextxy(1170, 480, s);settextcolor(ord[num]);if (num == 0)outtextxy(1100, 350, "→ 红方行棋");elseouttextxy(1100, 570, "→ 黑方行棋");
}void IfGivein(int& count, int& num)
{if (count > 9)                   //设置 10回合 后方可投降认输{settextstyle(30, 0, "楷体");if ((Judgexy(Msgxyz[0], Msgxyz[1], 1078, 20, 1178, UDGAP)) && num == 0){outtextxy(1110, UDGAP, "红方认输!");Msgxyz[2] = 1;Mesh[0][4].surv = false;}if ((Judgexy(Msgxyz[0], Msgxyz[1], 1078, 950 - UDGAP, 1178, 930)) && num == 1){outtextxy(1110, 950 - UDGAP - 30, "黑方认输!");Msgxyz[2] = 1;Mesh[9][4].surv = false;}}else{settextstyle(20, 0, "楷体");if ((Judgexy(Msgxyz[0], Msgxyz[1], 1078, 20, 1178, UDGAP)) && num == 0)outtextxy(1088, 75, "未满10回合,不可认输");if ((Judgexy(Msgxyz[0], Msgxyz[1], 1078, 950 - UDGAP, 1178, 930)) && num == 1)outtextxy(1088, 855, "未满10回合,不可认输");}
}bool Judgexy(int x, int y, int l, int u, int r, int b)    //判断是否点击了认输按钮
{if (x >= l && x <= r && y >= u && y <= b)return true;elsereturn false;
}

main 函数以及视频展示

最后,再放上main()函数即可运行了!
注:代码中出现的图片均以给出像素尺寸,涉及到MP3的播放均可注释不影响运行,gif的播放展示需要下载OpenCV,如果不需要进行动画展示,也可关闭 “PlayGIF()”与相应的头文件,不影响运行。

main 函数

int  main()
{MainPage();GamePage();GameSTART();settextstyle(50, 0, "楷体");if (Mesh[0][4].surv == false){settextcolor(BLACK);outtextxy(1080, 525, "游戏结束 ");outtextxy(1080, 585, "黑方获胜");}if (Mesh[9][4].surv == false){settextcolor(RED);outtextxy(1080, 315, "游戏结束");outtextxy(1080, 370, "红方获胜");}mciSendString("close BGM2.mp3", NULL, 0, NULL);system("pause");return 0;
}

视频效果展示

视频效果已在个人主页-视频模块中上传,为了不影响阅读代码,请转至该模块中观看。

最后

本文章内容可能存在诸多不足,欢迎指点和交流学习!QQ:1127793157,谢谢!
动画播放功能不太流畅,而且可以进一步更改透明度以实现更好的展示效果。
如需要文中出现的素材,可联系作者。

C++900行代码实现中国象棋游戏规则以及相关功能相关推荐

  1. 朋友写的一个中国象棋游戏,JAVA代码

    朋友写的一个中国象棋游戏,JAVA代码.有兴趣的可以这里下载:中国象棋下载 (1)地址,不知现在还能下否....中国象棋历史悠久,吸引了无数的人研究,现对中国象棋的对战和实现棋谱的制作做如下的设计和说 ...

  2. 中国象棋游戏Chess(3) - 实现走棋规则

    棋盘的绘制和走棋参看博文:中国象棋游戏Chess(1) - 棋盘绘制以及棋子的绘制,中国象棋游戏Chess(2) - 走棋 现在重新整理之前写的代码,并且对于每个棋子的走棋规则都进行了限制,不像之前那 ...

  3. Qt终极教程——用Qt编程实现中国象棋游戏(提供源代码和程序编译运行教程)

    Qt终极教程--用Qt编程实现中国象棋游戏 目录 Qt终极教程--用Qt编程实现中国象棋游戏 简介 运行可执行程序体验象棋游戏 Qt 安装 源代码的编译.运行与调试 生成预编译的可执行程序 简介 本文 ...

  4. 基于UDP协议的中国象棋游戏实现!

    基于UDP协议的中国象棋游戏 1.效果图 2.项目阐述 3.项目知识点 4.部分界面实现 4.1.背景界面面板 4.2.输入客户端信息界面面板 4.3.主界面 5.功能实现 5.1.界面切换 5.2. ...

  5. c语言编程一个象棋游戏,急求:C语言编写的中国象棋游戏一个

    急求:C语言编写的中国象棋游戏一个 來源:互聯網  2009-09-08 12:30:35  評論 分類: 電腦/網絡 >> 程序設計 >> 其他編程語言 問題描述: 由于学习 ...

  6. 基于python的游戏设计与实现-基于Python的网络中国象棋游戏设计与实现

    基于Python的网络中国象棋游戏设计与实现 摘要中国象棋是一种家喻户晓的棋类游戏,随着互联网时代的到来,人们的娱乐方式也逐渐向PC端和移动端上发展.本文将传统的中国象棋游戏和当下的互联网技术结合作为 ...

  7. C/C++编程笔记:C语言打造中国象棋游戏,项目源代码分享!

    中国象棋是起源于中国的一种棋,属于二人对抗性游戏的一种,在中国有着悠久的历史.由于用具简单,趣味性强,成为流行极为广泛的棋艺活动. 它是中国棋文化,也是中华民族的文化瑰宝,它源远流长,趣味浓厚,基本规 ...

  8. Java+Swing实现中国象棋游戏

    目录 一.系统介绍 1.开发环境 2.技术选型 3.系统功能 二.系统展示 1.首页 2.黑棋走 3.红旗走 三.部分代码 ChineseCheseRule.java 四.其他 1.更多系统 Java ...

  9. java象棋人机代码_中国象棋人机对弈Java版源码

    [实例简介] 中国象棋人机对弈Java版源码,包含人工智能实现(含多个难度级别,采用α-β迭代搜索算法) [实例截图] [核心代码] 中国象棋人机对弈Java版源码 ├── boards │   ├─ ...

最新文章

  1. 网络安全系列之七 网站提权
  2. generator自动生成mybatis配置和类信息
  3. Doctrine官方手册 - 缓存
  4. word文档下划线无法显示的解决方法
  5. 配送交付时间轻量级预估实践
  6. mac 无法启动linux系统安装,苹果官方技术文档显示新款Mac Mini不能安装Linux系统...
  7. JAVA编程中的类和对象
  8. WebApi 基于token的多平台身份认证架构设计
  9. java linq select_Java 8是否还需要LINQ?还是已经比LINQ 更好?
  10. Linux下yum命令及软件的安装
  11. μc/os-II原理简介(笔记)
  12. 微信小程序tabbar消失_微信小程序tabBar 返回tabBar不刷新页面
  13. MIS--信息管理系统
  14. 【学点心理学】八本值得反复阅读的心理类书籍推荐
  15. 新疆几十公里花海开始盛放,一株580年最老野树,开出最艳鲜花
  16. 如何查美国公司的年报
  17. 手机ZTE中兴U802 U807手机解锁图案忘了 如何处理
  18. win7右下角网路图标不见了,将这个操作删除掉就行了
  19. 牛客网-推理判断练习
  20. mysql set类型的用户变量,mysql用户变量的圈套

热门文章

  1. Python中的程序控制结构 顺序结构和选择结构
  2. 干货 | 超全整理|Python 操作 Excel 库 xlwings 常用操作详解!
  3. 在xp中不能查看或更改文件夹的“只读”属性或“系统”属性解决方法
  4. 不改HOST,另类打开谷歌搜索的方法
  5. 前后端鉴权方案,一文打尽!
  6. linux ntpdate同步错误,差一个小时的问题
  7. java热敏POS打印机编程
  8. 什么是根证书和中间证书(中级证书)?
  9. 凝心聚力共赢未来!“数智新基建,低碳新发展”港股开户活动圆满落
  10. angular如何生成条形码下载条形码