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