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

news/2024/11/25 4:44:59/

本文章通过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,谢谢!
动画播放功能不太流畅,而且可以进一步更改透明度以实现更好的展示效果。
如需要文中出现的素材,可联系作者。


http://www.ppmy.cn/news/323160.html

相关文章

【论文研读】-用于约束多目标优化的新型双阶段双种群进化算法

论文研读-用于约束多目标优化的新型双阶段双种群进化算法 A Novel Dual-Stage Dual-Population Evolutionary Algorithm for Constrained Multi-Objective Optimization 觉得有用的话,欢迎一起讨论相互学习~ 最近我在学习约束多目标问题的论文&#xff0c;其中由明博士和张教…

【计算机组成原理·笔记】有符号数和无符号数

有符号数和无符号数 有符号数对比无符号数要留出一位来表示符号。 机器数和真值 机器数&#xff1a;符号数字化带 -号的数字 原码 0正1负&#xff0c;符号位和数值位用,号相隔 注意&#xff1a; [ 0 ] 原 ≠ [ − 0 ] 原 [0]_原 \ne [-0]_原 [0]原​[−0]原​ 补码 …

黑鲨2 游戏手机 打开相机不能拍照

我的是黑鲨2 &#xff0c;打开相机后发现拍照不能用&#xff0c;但是专业模式&#xff0c;录像&#xff0c;人像等都能用&#xff0c;后来自己发现了问题 手机自带的 “扫一扫” 功能会占用相机权限&#xff0c;卸载这个应用即可 我的 “扫一扫” 在 “系统工具” 文件组里 …

黑鲨会升级鸿蒙吗,黑鲨4首批用户评价已出炉,不吹不黑,优缺点都很明显!...

近期新品手机市场可谓是十分热闹&#xff0c;各大厂商新机是一款接着一款发布&#xff0c;一时间许多小伙伴也是有些眼花缭乱&#xff0c;而谈起其中热度最高的机型&#xff0c;前些天发布的黑鲨4应该算是名列前茅&#xff0c;作为小米系上半年主打的游戏旗舰&#xff0c;黑鲨4…

黑鲨4Pro和黑鲨3Pro的区别 哪个更值得入手

黑鲨4Pro&#xff1a;搭载6.76英寸的AMOLED屏幕&#xff0c;为用户带来和小米11相同的2k三星E4屏幕&#xff0c;带来120Hz的屏幕刷新。黑鲨3Pro&#xff1a;搭载6.71英寸的AMOLED屏幕&#xff0c;带来2k三星屏幕&#xff0c;带来90Hz的屏幕刷新 黑鲨手机爆降 800这活动太给力了…

黑鲨3怎么安装鸿蒙系统,黑鲨3 Pro机械按键详解,横握时可进行高频点击操作

黑鲨早前正式官宣新款黑鲨3 Pro将采用机械按键设计&#xff0c;今日黑鲨手机产品中心部长也详细介绍了一下黑鲨3 Pro机械按键及屏幕压感综合使用下的表现。 黑鲨3 Pro的机械按键在用户横握时可以保证玩家在不影响屏幕操作的情况下进行高频点击&#xff0c;适合游戏中经常需要触…

黑鲨手机能不能用鸿蒙系统,黑鲨游戏手机3 Pro的操控方式曝光

集微网2月29日消息(文/数码控)&#xff0c;下个月黑鲨游戏手机3 Pro就要发布了&#xff0c;在此之前黑鲨产品中心部长 黑鲨关二爷 继续曝光该机的信息&#xff0c;今天他放出的是黑鲨游戏手机3 Pro的操控方式。 黑鲨关二爷 称黑鲨游戏手机3 Pro拥有机械按键和屏幕压感两种按键操…

黑鲨能换鸿蒙系统吗,黑鲨告诉你重装系统对电脑的好坏处

重装系统电脑好坏处 再好用的系统用久了难免也会出现问题&#xff0c;这时候如果难以修复&#xff0c;我们就会选择在线重装系统。那么电脑重装系统好么&#xff1f;对于这个问题要具体分析才能下达结论。下面就让小编就这个问题给大家具体分析&#xff0c;告诉大家重装系统对电…