三种版本的中国象棋

一.图片资源:
在这里插入图片描述

二.图片存放位置:
在这里插入图片描述
在这里插入图片描述
三.三种不同版本的中国象棋源代码

三种源代码运行之前都需要
点击项目-属性
在这里插入图片描述
找到这个地方,把字符集改成“使用多字节字符集”:
在这里插入图片描述

一.版本1:中国象棋简洁版(部分特效+无棋子规则限制移动)
源文件:ChineseChess.cpp

#include<stdio.h>
#include<easyx.h>	//easyx图形库函数,需要安装,easyx.h是C++独有的头文件

#define ROW 10			//棋盘行数
#define COL 9			//棋盘列数
#define INTERVAL 50		//间隔
#define GRID_SIZE 80	//格子宽度

int preRunx = -1, preRuny = -1;//给点击的棋子增加特效

//特效坐标的初始化
void initPreRun()
{
	preRunx = -1;
	preRuny = -1;
}

//游戏数据
enum Pieces 
{
	NONE = -1,//NONE代表没有棋子 -1,,,,,,,//红方棋子 0-6 ,,,,,,,//黑方棋子 7-13
	BEGIN, END,//14-15
};

//枚举数组,给id赋值
enum Pieces redChess[] = {,,,,,,};
enum Pieces blackChess[] = {,,,,,,};

//定义字符串(中文属于字符串)
const char* ChessName[] = { "車","馬","象","士","将","砲","卒","俥", "马", "相", "仕", "帥", "炮", "兵" };

//定义每一个棋子的属性
struct Chess
{
	enum Pieces id;		//棋子名称
	DWORD type;			//棋子类型,表明是红方的棋还是黑方的棋
	int x;				//记录棋子的横坐标
	int y;				//记录棋子的纵坐标
	bool  isRiver;		//是否过了河
};

//游戏地图
struct Chess map[ROW][COL];

//定义鼠标状态
struct State
{
	int begr;//鼠标起点点击位置的横坐标
	int begc;//鼠标起点点击位置的纵坐标
	int endr;//鼠标终点点击位置的横坐标
	int endc;//鼠标终点点击位置的纵坐标
	int state;//鼠标状态state的初值是BEGIN
}state = { -1,-1,-1,-1,BEGIN };

//打印数组
void show()
{
	for (int i = 0; i < ROW; i++)
	{
		for (int k = 0; k < COL; k++)
		{
			printf("%2d ", map[i][k].id);
		}
		printf("\n");
	}
}

//清空棋子状态
void initState()
{
	state = { -1,-1,-1,-1,BEGIN };
}

//初始化数据
void init()
{
	//遍历地图
	for (int i = 0; i < ROW; i++)
	{
		int temp = 0;
		for (int k = 0; k < COL; k++)
		{
			map[i][k].id = NONE;	//先把棋子置为没有
			if (i <= 4)	//黑棋
			{
				if (i == 0)	//放置第一行的棋子
				{
					//0 1 2 3 4
					if (k <= 4)
					{
						temp = k;
					}
					// 3 2 1 0
					else
					{
						// k == 5
						temp = 4 - (k - 4);
						/*
						4 - (5-4)	//3
						4 - (6-4)	//2
						4 - (7-4)	//1
						4 - (8-4)	//0
						*/
					}
					map[i][k].id = blackChess[temp];
				}
				//设置炮
				if (i == 2 && (k == 1 || k == 7))
				{
					map[i][k].id = blackChess[5];
				}
				//设置兵
				if (i == 3 && k % 2 == 0)
				{
					map[i][k].id = blackChess[6];
				}
				if (map[i][k].id != NONE)
					map[i][k].type = BLACK;
			}
			else       //红棋
			{
				map[i][k].type = RED;
				if (i == 9)	
				{
					//0 1 2 3 4
					if (k <= 4)
					{
						temp = k;
					}
					// 3 2 1 0
					else
					{
						// k == 5
						temp = 4 - (k - 4);
						/*
						4 - (5-4)	//3
						4 - (6-4)	//2
						4 - (7-4)	//1
						4 - (8-4)	//0
						*/
					}
					map[i][k].id = redChess[temp];
				}
				//设置炮
				if (i == 7 && (k == 1 || k == 7))
				{
					map[i][k].id = redChess[5];
				}
				//设置兵
				if (i == 6 && k % 2 == 0)
				{
					map[i][k].id = redChess[6];
				}
				/*			if (map[i][k].id != NONE)
								map[i][k].type = RED;*/
			}
			map[i][k].isRiver = false;
			map[i][k].x = k * GRID_SIZE + INTERVAL;
			map[i][k].y = i * GRID_SIZE + INTERVAL;
		}
	}
}

//定义绘制棋子函数
void draw()
{
	setfillcolor(RGB(252, 215, 162));//设置棋子文字的填充颜色
	setlinestyle(PS_SOLID, 2);//设置棋子边框圆线条
	settextstyle(30, 0, "楷体"); //设置棋子的字体高度为30,宽度为0,字体为楷体
	for (int i = 0; i < ROW; i++)
	{
		for (int k = 0; k < COL; k++)
		{
			if (map[i][k].id == NONE)
				continue;
			settextcolor(map[i][k].type);//设置棋子文字的颜色
			setlinecolor(map[i][k].type);//设置棋子边框圆线条的颜色
			//绘制棋子
			fillcircle(map[i][k].x, map[i][k].y, 30);//以x和y为圆心,30为半径的填充圆
			fillcircle(map[i][k].x, map[i][k].y, 25);//以x和y为圆心,25为半径的填充圆,目的是为了让棋子看起来有立体感
			outtextxy(map[i][k].x - 15, map[i][k].y - 15, ChessName[map[i][k].id]);
			//显示横坐标为map[i][k].x-15,纵坐标为map[i][k].y-15的字符串ChessName[map[i][k].id]
		}
	}
}

//移动棋子
void chessMove()
{
	//什么情况下能够移动棋子
	if (!(state.begr == state.endr && state.begc == state.endc) &&	//点击的不是同一个棋子
		state.endr != -1 && state.begr != -1 &&		//下标必须合法
		map[state.begr][state.begc].id != NONE//没有棋子不能移动
		&& ((map[state.endr][state.endc].id == NONE)//当棋子移动到的第二个位置为空的时候 
			|| (map[state.begr][state.begc].type != map[state.endr][state.endc].type)))	//不能自己吃自己
	{
		if (map[state.begr][state.begc].type == RED)
		{
			printf("红棋");
		}
		else
		{
			printf("黑棋");
		}
		printf("从(%d,%d)走到(%d,%d)\n\n", state.begr, state.begc, state.endr, state.endc);

		map[state.endr][state.endc].id = map[state.begr][state.begc].id;
		map[state.begr][state.begc].id = NONE;

		map[state.endr][state.endc].isRiver = map[state.begr][state.begc].isRiver;
		map[state.endr][state.endc].type = map[state.begr][state.begc].type;

	}
	initPreRun();
	initState();//到这一步无论有没有走棋,都重置
}

//鼠标操作
void mouseEvent()
{
	ExMessage msg;	//定义消息结构体变量
	if (peekmessage(&msg, EM_MOUSE))
	{
		if (msg.message == WM_LBUTTONDOWN)	//鼠标左键按下
		{
			//通过鼠标坐标得出点击的数组的下标
			//k * GRID_SIZE + INTERVAL = x;
			int col = (msg.x - INTERVAL) / GRID_SIZE;
			int row = (msg.y - INTERVAL) / GRID_SIZE;

			//下标校准
			if (msg.x > map[row][col].x + 30 && msg.y < map[row][col].y + 30)
			{
				col++;
			}
			if (msg.x < map[row][col].x + 30 && msg.y > map[row][col].y + 30)
			{
				row++;
			}
			if (msg.x > map[row][col].x + 30 && msg.y > map[row][col].y + 30)
			{
				row++;
				col++;
			}

			if (state.state == BEGIN)
			{
				state.begr = row;
				state.begc = col;
				printf("鼠标第一次点击的");
				if (map[state.begr][state.begc].type == RED && map[state.begr][state.begc].id != NONE)
				{
					printf("红棋为");
					printf("%s,", ChessName[map[state.begr][state.begc].id]);
				}
				else if (map[state.begr][state.begc].type == BLACK && map[state.begr][state.begc].id != NONE)
				{
					printf("黑棋为");
					printf("%s", ChessName[map[state.begr][state.begc].id]);
				}
				else
				{
					printf("空棋");
				}
				printf("其坐标是:(%d,%d)\n", state.begr, state.begc);
				state.state = END;
				preRunx = row;
				preRuny = col;
			}
			else if (state.state == END)
			{

				state.endr = row;
				state.endc = col;
				printf("鼠标第二次点击的");
				if (map[state.endr][state.endc].type == RED && map[state.endr][state.endc].id != NONE)
				{
					printf("红棋为");
					printf("%s,", ChessName[map[state.endr][state.endc].id]);
				}
				else if (map[state.endr][state.endc].type == BLACK && map[state.endr][state.endc].id != NONE)
				{
					printf("黑棋为");
					printf("%s", ChessName[map[state.endr][state.endc].id]);
				}
				else
				{
					printf("空棋,");
				}
				printf("其坐标是:(%d,%d)\n", state.endr, state.endc);
				state.state = BEGIN;
				chessMove();
			}
		}
	}
}

//特效
void special_effects()
{
	setlinecolor(BLUE);
	if (preRunx != -1 && preRuny != -1 && map[preRunx][preRuny].id != NONE)
	{
		rectangle(map[preRunx][preRuny].x - 30, map[preRunx][preRuny].y - 30, map[preRunx][preRuny].x + 30, map[preRunx][preRuny].y + 30);
	}
}

int main()
{
	//创建图形窗口
	initgraph(740, 820, EW_SHOWCONSOLE);//窗口的宽度为740像素,高度为820像素,EW_SHOWCONSOLE是用来显示黑窗口的
	//设置背景模式
	setbkmode(TRANSPARENT);//设置棋子文字的背景颜色为透明
	IMAGE img_board;
	loadimage(&img_board, "./res/ChessBoard.png");//图片路径

	init();
	BeginBatchDraw();//双缓冲绘图,防止闪屏
	while (true)
	{
		cleardevice();//清屏
		putimage(0, 0, &img_board);//输出图片
		draw();
		mouseEvent();
		special_effects();
		FlushBatchDraw();//双缓冲绘图,防止闪屏
	}
	EndBatchDraw();//双缓冲绘图,防止闪屏
	char t=getchar();//防止闪退
	return 0;
}

版本2:中国象棋加强版(完整特效+无棋子规则限制移动)
源文件:ChineseChess.cpp

#include<stdio.h>
#include<easyx.h>	//easyx图形库函数,需要安装,easyx.h是C++独有的头文件

#define ROW 10			//棋盘行数
#define COL 9			//棋盘列数
#define INTERVAL 50		//间隔
#define GRID_SIZE 80	//格子宽度

int curRun = 0;//记录当前该谁走棋,默认为0代表该红棋走,-1代表黑棋走
int victory = 0; //记录赢棋状态
int preRunx = -1, preRuny = -1;//给点击的棋子增加特效

//特效坐标的初始化
void initPreRun()
{
	preRunx = -1;
	preRuny = -1;
}

//游戏数据
enum Pieces 
{
	NONE = -1,//NONE代表没有棋子 -1,,,,,,,//红方棋子 0-6 ,,,,,,,//黑方棋子 7-13
	BEGIN, END,//14-15
};

//枚举数组,给id赋值
enum Pieces redChess[] = {,,,,,,};
enum Pieces blackChess[] = {,,,,,,};

//定义字符串(中文属于字符串)
const char* ChessName[] = { "車","馬","象","士","将","砲","卒","俥", "马", "相", "仕", "帥", "炮", "兵" };

//定义每一个棋子的属性
struct Chess
{
	enum Pieces id;		//棋子名称
	DWORD type;			//棋子类型,表明是红方的棋还是黑方的棋
	int x;				//记录棋子的横坐标
	int y;				//记录棋子的纵坐标
	bool  isRiver;		//是否过了河
};

//游戏地图
struct Chess map[ROW][COL];

//定义鼠标状态
struct State
{
	int begr;//鼠标起点点击位置的横坐标
	int begc;//鼠标起点点击位置的纵坐标
	int endr;//鼠标终点点击位置的横坐标
	int endc;//鼠标终点点击位置的纵坐标
	int state;//鼠标状态state的初值是BEGIN
}state = { -1,-1,-1,-1,BEGIN };

//打印数组
void show()
{
	for (int i = 0; i < ROW; i++)
	{
		for (int k = 0; k < COL; k++)
		{
			printf("%2d ", map[i][k].id);
		}
		printf("\n");
	}
}
//清空棋子状态
void initState()
{
	state = { -1,-1,-1,-1,BEGIN };
}

//初始化数据
void init()
{
	//遍历地图
	for (int i = 0; i < ROW; i++)
	{
		int temp = 0;
		for (int k = 0; k < COL; k++)
		{
			map[i][k].id = NONE;	//先把棋子置为没有
			if (i <= 4)	//黑棋
			{
				if (i == 0)	//放置第一行的棋子
				{
					//0 1 2 3 4
					if (k <= 4)
					{
						temp = k;
					}
					// 3 2 1 0
					else
					{
						// k == 5
						temp = 4 - (k - 4);
						/*
						4 - (5-4)	//3
						4 - (6-4)	//2
						4 - (7-4)	//1
						4 - (8-4)	//0
						*/
					}
					map[i][k].id = blackChess[temp];
				}
				//设置炮
				if (i == 2 && (k == 1 || k == 7))
				{
					map[i][k].id = blackChess[5];
				}
				//设置兵
				if (i == 3 && k % 2 == 0)
				{
					map[i][k].id = blackChess[6];
				}
				if (map[i][k].id != NONE)
					map[i][k].type = BLACK;
			}
			else       //红棋
			{
				map[i][k].type = RED;
				if (i == 9)	//放置第一行的棋子
				{
					//0 1 2 3 4
					if (k <= 4)
					{
						temp = k;
					}
					// 3 2 1 0
					else
					{
						temp = 4 - (k - 4);
						/*
						4 - (5-4)	//3
						4 - (6-4)	//2
						4 - (7-4)	//1
						4 - (8-4)	//0
						*/
					}
					map[i][k].id = redChess[temp];
				}
				//设置炮
				if (i == 7 && (k == 1 || k == 7))
				{
					map[i][k].id = redChess[5];
				}
				//设置兵
				if (i == 6 && k % 2 == 0)
				{
					map[i][k].id = redChess[6];
				}
			}
			map[i][k].isRiver = false;
			map[i][k].x = k * GRID_SIZE + INTERVAL;
			map[i][k].y = i * GRID_SIZE + INTERVAL;
		}
	}
}

//定义绘制棋子函数
void draw()
{
	setfillcolor(RGB(252, 215, 162));//设置棋子文字的填充颜色
	setlinestyle(PS_SOLID, 2);//设置棋子边框圆线条
	settextstyle(30, 0, "楷体"); //设置棋子的字体高度为30,宽度为0,字体为楷体
	for (int i = 0; i < ROW; i++)
	{
		for (int k = 0; k < COL; k++)
		{
			if (map[i][k].id == NONE)
				continue;
			settextcolor(map[i][k].type);//设置棋子文字的颜色
			setlinecolor(map[i][k].type);//设置棋子边框圆线条的颜色
			//绘制棋子
			fillcircle(map[i][k].x, map[i][k].y, 30);//以x和y为圆心,30为半径的填充圆
			fillcircle(map[i][k].x, map[i][k].y, 25);//以x和y为圆心,25为半径的填充圆,目的是为了让棋子看起来有立体感
			outtextxy(map[i][k].x - 15, map[i][k].y - 15, ChessName[map[i][k].id]);
			//显示横坐标为map[i][k].x-15,纵坐标为map[i][k].y-15的字符串ChessName[map[i][k].id]
		}
	}
}

//检查当前走棋是否正常
bool check()
{
	if (state.begr == state.endr && state.begc == state.endc) 
		return false;//点击的不是同一个棋子
	if (state.endr == -1 || state.begr == -1) 
		return false;//下标必须合法
	if (map[state.begr][state.begc].id == NONE) 
		return false;//没有棋子不能移动
	if (!(map[state.endr][state.endc].id == NONE //当棋子移动到的第二个位置不为空的时候 
		|| map[state.begr][state.begc].type != map[state.endr][state.endc].type)) 
		return false;//不能自己吃自己

	//作用:第一次只能移动红棋、第三次只能移动红棋...
	if (curRun == 0 && map[state.begr][state.begc].type != RED) 
		return false;
	//作用:第二次只能移动黑棋...
	if (curRun == -1 && map[state.begr][state.begc].type != BLACK) 
		return false;
	return true;
}

//移动棋子
void chessMove()
{
	//什么情况下能够移动棋子
	if (check() && victory == 0)
	{
		if (map[state.begr][state.begc].type == RED)
		{
			printf("红棋");
		}
		else {
			printf("黑棋");
		}
		printf("从(%d,%d)走到(%d,%d)\n\n", state.begr, state.begc, state.endr, state.endc);

		int t = map[state.endr][state.endc].id;//记录第二个棋子的id,若为空,则id为-1

		map[state.endr][state.endc].id = map[state.begr][state.begc].id;
		map[state.begr][state.begc].id = NONE;//此时map[preRunx][preRuny].id或map[state.begr][state.begc].id为-1

		//map[state.endr][state.endc].isRiver = map[state.begr][state.begc].isRiver;//这段代码没用
		map[state.endr][state.endc].type = map[state.begr][state.begc].type;
		curRun = ~curRun; //~是异或符号,如果原来curRun是0,~curRun就是-1,如果原来是-1,~就是0

		//每走完一步棋就判断一次对面笑杀
		int sx = 0, sy = 4;//定义帅的坐标位置
		int jx = 9, jy = 4;//定义将的坐标位置

		for (int i = 0; i <= 2; i++) //遍历黑棋的九宫格找帥的位置
		{
			for (int j = 3; j <= 5; j++)
			{
				if (map[i][j].id ==)
				{
					sx = i;
					sy = j;
					break;
				}
			}
		}

		for (int i = 7; i <= 9; i++) //遍历红棋的九宫格找将的位置
		{
			for (int j = 3; j <= 5; j++)
			{
				if (map[i][j].id ==)
				{
					jx = i;
					jy = j;
					break;
				}
			}
		}

		if (sy == jy) //如果将和帥在一条直线上
		{
			int num = 0;
			for (int i = sx + 1; i < jx; i++) //判断之间是否有其他棋子
			{
				if (map[i][sy].id != NONE)//之间有棋子
				{
					num++;//则num数量增加
				}
			}
			if (num == 0) //之间没有棋子,触发对面笑杀
			{ //先判断一下该谁走棋,如果该红棋,则红棋胜
				if (curRun == 0) //黑棋走完了,curRun经过异或变成0,此时应该红棋走,但由于已经出发了对面笑杀,则红棋胜
				{
					t = map[sx][sy].id;
					map[sx][sy].id = map[jx][jy].id;//map[sx][sy].id是黑方的帅,map[jx][jy].id是红方的将,实现了将吃帅
					map[jx][jy].id = NONE;//把之前将的id置为0
					map[sx][sy].type = map[jx][jy].type;//把被吃棋子的颜色换成将的颜色
				}
				else if (curRun == -1) //红棋走完了,curRun经过异或变成-1,此时应该黑棋走,但由于已经出发了对面笑杀,则黑棋胜
				{
					t = map[jx][jy].id;
					map[jx][jy].id = map[sx][sy].id;//map[jx][jy].id是红方的将,map[sx][sy].id是黑方的帅,实现了帅吃将
					map[sx][sy].id = NONE;//把之前帅的id置为0
					map[jx][jy].type = map[sx][sy].type;//把被吃棋子的颜色换成帅的颜色
				}
			}
		}

		if (t ==)
		{
			victory = -1; //红棋赢了!
		}
		else if (t ==)
		{
			victory = 1; //黑棋赢了!
		}
	}
	initPreRun();
	initState();//到这一步无论有没有走棋,都重置
}

//鼠标操作
void mouseEvent()
{
	ExMessage msg;	//定义消息结构体变量
	if (peekmessage(&msg, EM_MOUSE))
	{
		if (msg.message == WM_LBUTTONDOWN)	//鼠标左键按下
		{
			//通过鼠标坐标得出点击的数组的下标
			//k * GRID_SIZE + INTERVAL = x;
			int col = (msg.x - INTERVAL) / GRID_SIZE;
			int row = (msg.y - INTERVAL) / GRID_SIZE;

			//下标校准
			if (msg.x > map[row][col].x + 30 && msg.y < map[row][col].y + 30)
			{
				col++;
			}
			if (msg.x < map[row][col].x + 30 && msg.y > map[row][col].y + 30)
			{
				row++;
			}
			if (msg.x > map[row][col].x + 30 && msg.y > map[row][col].y + 30)
			{
				row++;
				col++;
			}

			if (state.state == BEGIN)
			{
				state.begr = row;
				state.begc = col;
				printf("鼠标第一次点击的");
				if (map[state.begr][state.begc].type == RED && map[state.begr][state.begc].id != NONE)
				{
					printf("红棋为");
					printf("%s,", ChessName[map[state.begr][state.begc].id]);
				}
				else if (map[state.begr][state.begc].type == BLACK && map[state.begr][state.begc].id != NONE)
				{
					printf("黑棋为");
					printf("%s", ChessName[map[state.begr][state.begc].id]);
				}
				else
				{
					printf("空棋");
				}
				printf("其坐标是:(%d,%d)\n", state.begr, state.begc);
				state.state = END;
				preRunx = row;
				preRuny = col;
			}
			else if (state.state == END)
			{
				state.endr = row;
				state.endc = col;
				printf("鼠标第二次点击的");
				if (map[state.endr][state.endc].type == RED && map[state.endr][state.endc].id != NONE)
				{
					printf("红棋为");
					printf("%s,", ChessName[map[state.endr][state.endc].id]);
				}
				else if (map[state.endr][state.endc].type == BLACK && map[state.endr][state.endc].id != NONE)
				{
					printf("黑棋为");
					printf("%s", ChessName[map[state.endr][state.endc].id]);
				}
				else
				{
					printf("空棋,");
				}
				printf("其坐标是:(%d,%d)\n", state.endr, state.endc);
				state.state = BEGIN;
				if (map[state.endr][state.endc].type == map[state.begr][state.begc].type && map[state.endr][state.endc].id != NONE) 
				{
					state.state = END;
					state.begr = row;
					state.begc = col;
					preRunx = row;
					preRuny = col;
				}
				else
				{
					chessMove();//只有当成功点击两次才进行走棋判断
				}
			}
		}
	}
}

//特效
void special_effects()
{
	setlinecolor(BLUE);
	if (preRunx != -1 && preRuny != -1 && map[preRunx][preRuny].id != NONE)
	{
		rectangle(map[preRunx][preRuny].x - 30, map[preRunx][preRuny].y - 30, map[preRunx][preRuny].x + 30, map[preRunx][preRuny].y + 30);
	}
	settextstyle(100, 0, _T("宋体"));
	settextcolor(RGB(0, 122, 204));
	if (victory == -1)
	{
		outtextxy(150, 360, "红棋赢了!");
	}
	else if (victory == 1)
	{
		outtextxy(150, 360, "黑棋赢了!");
	}
}

int main()
{
	//创建图形窗口
	initgraph(740, 820, EW_SHOWCONSOLE);
	//设置背景模式
	setbkmode(TRANSPARENT);
	//贴棋盘
	IMAGE img_board;
	loadimage(&img_board, "./res/ChessBoard.png");

	init();
	//双缓冲绘图,防止闪屏
	BeginBatchDraw();
	while (true)
	{
		cleardevice();
		putimage(0, 0, &img_board);
		draw();
		mouseEvent();
		special_effects();//特效
		FlushBatchDraw();
	}
	EndBatchDraw();

	char t=getchar();
	return 0;
}

版本3:中国象棋完整版(完整特效+有棋子规则限制移动)
源文件:ChineseChess.cpp

#include<stdio.h>
#include<easyx.h>	//easyx图形库函数,需要安装,easyx.h是C++独有的头文件

#define ROW 10			//棋盘行数
#define COL 9			//棋盘列数
#define INTERVAL 50		//间隔
#define GRID_SIZE 80	//格子宽度

int curRun = 0;//记录当前该谁走棋,默认为0代表该红棋走,-1代表黑棋走
int victory = 0; //记录赢棋状态
int preRunx = -1, preRuny = -1;//给点击的棋子增加特效

//特效坐标的初始化
void initPreRun()
{
	preRunx = -1;
	preRuny = -1;
}

//游戏数据
enum Pieces //棋子
{
	NONE = -1,//NONE代表没有棋子 -1,,,,,,,//红方棋子 0-6 ,,,,,,,//黑方棋子 7-13
	BEGIN, END,//14-15
};

//枚举数组,给id赋值
enum Pieces redChess[] = {,,,,,,};
enum Pieces blackChess[] = {,,,,,,};

//定义字符串(中文属于字符串)
const char* ChessName[] = { "車","馬","象","士","将","砲","卒","俥", "马", "相", "仕", "帥", "炮", "兵" };

//定义每一个棋子的属性
struct Chess
{
	enum Pieces id;		//棋子名称
	DWORD type;			//棋子类型,表明是红方的棋还是黑方的棋
	int x;				//记录棋子的横坐标
	int y;				//记录棋子的纵坐标
	bool  isRiver;		//是否过了河
};

//游戏地图
struct Chess map[ROW][COL];

//定义鼠标状态
struct State
{
	int begr;//鼠标起点点击位置的横坐标
	int begc;//鼠标起点点击位置的纵坐标
	int endr;//鼠标终点点击位置的横坐标
	int endc;//鼠标终点点击位置的纵坐标
	int state;//鼠标状态state的初值是BEGIN
}state = { -1,-1,-1,-1,BEGIN };

//打印数组
void show()
{
	for (int i = 0; i < ROW; i++)
	{
		for (int k = 0; k < COL; k++)
		{
			printf("%2d ", map[i][k].id);
		}
		printf("\n");
	}
}

//清空棋子状态
void initState()
{
	state = { -1,-1,-1,-1,BEGIN };
}

//定义初始化棋子数据函数
void init()
{
	for (int i = 0; i < ROW; i++)
	{
		int temp = 0;
		for (int k = 0; k < COL; k++)
		{
			map[i][k].id = NONE;	//开始先把棋子置为没有
			//对黑棋进行设置
			if (i <= 4)//黑棋子
			{
				map[i][k].type = BLACK;	//先把棋子设置为黑色,BLACK是easyx图形库自己定义的
				/*设置第0行的棋子*/
				if (i == 0)//如果是第0行棋子
				{
					//0 1 2 3 4
					if (k <= 4)//如果是棋子的列数为0-4
					{
						temp = k;//temp的值就是0、1、2、3、4
					}
					//3 2 1 0
					else//如果是棋子的列数为5-8
					{
						temp = 4 - (k - 4);//temp的值就是3、2、1、0
					}
					map[i][k].id = blackChess[temp];//设置第0行棋子的名称为:車, 馬, 象, 士, 将, 士, 象,馬,車
				}
				/*设置第2行的棋子*/
				if (i == 2 && (k == 1 || k == 7))
				{
					map[i][k].id = blackChess[5];//设置第2行棋子的名称为:砲,砲
				}
				/*设置第3行的棋子*/
				if (i == 3 && k % 2 == 0)
				{
					map[i][k].id = blackChess[6];//设置第3行棋子的名称为:卒,卒,卒,卒,卒
				}
			}
			else//红棋子
			{
				map[i][k].type = RED;	//先把棋子设置为红色,RED是easyx图形库自己定义的
				/*设置第9行的棋子*/
				if (i == 9)//如果是第9行棋子
				{
					//0 1 2 3 4
					if (k <= 4)//如果是棋子的列数为0-4
					{
						temp = k;//temp的值就是0、1、2、3、4
					}
					//3 2 1 0
					else//如果是棋子的列数为5-8
					{
						temp = 4 - (k - 4);//temp的值就是3、2、1、0
					}
					map[i][k].id = redChess[temp];//设置第9行棋子的名称为:俥, 马, 相, 仕, 帥, 仕,相,马,俥
				}
				/*设置第7行的棋子*/
				if (i == 7 && (k == 1 || k == 7))
				{
					map[i][k].id = redChess[5];//设置第7行棋子的名称为:炮,炮
				}
				/*设置第3行的棋子*/
				if (i == 6 && k % 2 == 0)
				{
					map[i][k].id = redChess[6];//设置第3行棋子的名称为:卒,卒,卒,卒,卒
				}
			}
			map[i][k].isRiver = false;
			map[i][k].x = k * GRID_SIZE + INTERVAL;//这个是鼠标的纵坐标x
			map[i][k].y = i * GRID_SIZE + INTERVAL;//这个是鼠标的横坐标y
		}
	}
}

//定义绘制棋子函数
void draw()
{
	setfillcolor(RGB(252, 215, 162));//设置棋子文字的填充颜色
	setlinestyle(PS_SOLID, 2);//设置棋子边框圆线条
	settextstyle(30, 0, "楷体"); //设置棋子的字体高度为30,宽度为0,字体为楷体
	for (int i = 0; i < ROW; i++)
	{
		for (int k = 0; k < COL; k++)
		{
			if (map[i][k].id == NONE)
				continue;
			settextcolor(map[i][k].type);//设置棋子文字的颜色
			setlinecolor(map[i][k].type);//设置棋子边框圆线条的颜色
			//绘制棋子
			fillcircle(map[i][k].x, map[i][k].y, 30);//以x和y为圆心,30为半径的填充圆
			fillcircle(map[i][k].x, map[i][k].y, 25);//以x和y为圆心,25为半径的填充圆,目的是为了让棋子看起来有立体感
			outtextxy(map[i][k].x - 15, map[i][k].y - 15, ChessName[map[i][k].id]);
			//显示横坐标为map[i][k].x-15,纵坐标为map[i][k].y-15的字符串ChessName[map[i][k].id]
		}
	}
}

//車和俥的移动规则
bool carRule() //能走棋返回true,不能则返回false
{
	//以下四个循环是判断该棋子所在行或列上有无其他棋子,不判断起点(起点上它自己在那)和
	//终点(如果终点是不同色的棋子,车可以直接吃子,如果终点是同色的则会直接切换棋子特效,上面写过)
	//判断的原理是遍历两个坐标之间,看一下有没有其他的id不为NONE的坐标,有的话就返回false,这样就不会走棋
	//但是由于不知道车是往上走还是往下走,所以需要判断两次,也可以用其他方法,只不过更麻烦

	//两列之间
	for (int i = state.begc + 1; i < state.endc; i++) //判断两点之间行上有没有其他棋子
	{
		if (map[state.begr][i].id != NONE)
			return false;
	}

	//也是两列之间
	for (int i = state.endc + 1; i < state.begc; i++)
	{
		if (map[state.begr][i].id != NONE)
			return false;
	}

	//两行之间
	for (int i = state.begr + 1; i < state.endr; i++)//同理判断列上有没有棋子
	{
		if (map[i][state.begc].id != NONE)
			return false;
	}

	//也是两行之间
	for (int i = state.endr + 1; i < state.begr; i++)//同理也是不知道车往左右还是上下
	{
		if (map[i][state.begc].id != NONE)
			return false;
	}
	return true;
}

//砲和炮的移动规则
bool cannoRule()
{
	int num = 0; //定义炮路径上的障碍物个数
	//同样不判断起点和终点,判断之间有几个棋子,如果有一个棋子,就吃子。
	//如果之间有超过1个棋子,则返回false
	//如果之间没有棋子,就走子

	//两列之间
	for (int i = state.begc + 1; i < state.endc; i++) 
	{
		if (map[state.begr][i].id != NONE) 
			num++;
	}
	//也是两列之间
	for (int i = state.endc + 1; i < state.begc; i++) 
	{
		if (map[state.begr][i].id != NONE) 
			num++;
	}
	//两行之间
	for (int i = state.begr + 1; i < state.endr; i++) 
	{
		if (map[i][state.begc].id != NONE) 
			num++;
	}
	//也是两行之间
	for (int i = state.endr + 1; i < state.begr; i++) 
	{
		if (map[i][state.begc].id != NONE) 
			num++;
	}

	if (num == 0 && map[state.endr][state.endc].id == NONE) //路径之间没有棋子,判断终点如果没有棋子则能走棋
	{
		return true;//能走棋则返回true
	}
	else if (num == 1 && map[state.endr][state.endc].id != NONE) //路径之间有一个棋子,判断终点如果有棋子则能跳跃吃棋
	{
		return true;//能跳跃吃棋则返回true
	}
	else //其他情况则都是false
	{ 
		return false;
	}
}

//馬和马的移动规则
bool horseRule()//判断马走棋
{
	int bx = state.begr, by = state.begc; //简化起始坐标写法
	int ex = state.endr, ey = state.endc; //简化终点坐标写法
	int nx[10] = { -1,-2,-2,-1,1,2,2,1 }, ny[10] = { -2,-1,1,2,2,1,-1,-2 }; //相对于马的所有偏移量
	int zx[10] = { 0,-1,-1,0,0,1,1,0 }, zy[10] = { -1,0,0,1,1,0,0,-1 }; //计算得与上对应的障碍的位置
	//上面的nx,ny结合就是马可以走棋(马走日)的相对位置,同理zx,zx就是绊马脚的相对位置
	//这个可以看着棋盘自己计算
	bool flag = false;
	for (int i = 0; i < 8; i++) //如果下一步合法
	{ 
		if (ex == bx + nx[i] && ey == by + ny[i]) //判断终点位置是否为以上合法坐标中的其中一个
		{ 
			if (map[bx + zx[i]][by + zy[i]].id == NONE) //且不被绊马脚
				flag = true;
			break;
		}
	}
	return flag;
}

//象和相的移动规则
bool elephantRule()//判断象走棋
{
	int bx = state.begr, by = state.begc; //简化起始坐标写法
	int ex = state.endr, ey = state.endc; //简化终点坐标写法
	if (map[bx][by].type == RED) //判断是红棋还是黑棋,以便于分析是否过河
	{ 
		if (ex < 5) 
			return false; //如果红棋过河就返回false
	}
	else 
	{
		if (ex > 4) 
			return false;//如果黑棋过河就返回false
	}
	//和马的走棋规则同理
	int nx[5] = { -2,-2,2,2 }, ny[5] = { -2,2,2,-2 }; //通过计算得到下一步所有的合法位置
	int zx[5] = { -1,-1,1,1 }, zy[5] = { -1,1,1,-1 }; //计算得与上对应的障碍的位置
	bool flag = false;
	for (int i = 0; i < 4; i++)//如果下一步合法
	{ 
		if (ex == bx + nx[i] && ey == by + ny[i]) 
		{
			if (map[bx + zx[i]][by + zy[i]].id == NONE) //且不被绊象脚
				flag = true;
			break;
		}
	}
	return flag;
}

//士和仕的移动规则
bool chapRule()
{
	int bx = state.begr, by = state.begc; //简化起始坐标写法
	int ex = state.endr, ey = state.endc; //简化终点坐标写法
	if (map[bx][by].type == RED) //判断是红棋还是黑棋,以便于分析是否过八方格
	{ 
		if (ex < 7 || ey < 3 || ey > 5) 
			return false; //如果红棋过界就返回false
	}
	else 
	{
		if (ex > 2 || ey < 3 || ey > 5) 
			return false; //如果黑棋过界就返回false
	}
	int nx[5] = { -1,-1,1,1 }, ny[5] = { -1,1,1,-1 }; //通过计算得到下一步所有的合法位置
	bool flag = false;
	for (int i = 0; i < 4; i++) //如果下一步合法
	{ 
		if (ex == bx + nx[i] && ey == by + ny[i]) 
		{
			flag = true;
			break;
		}
	}
	return flag;
}

//将和帥的移动规则
bool masterRule()
{
	int bx = state.begr, by = state.begc; //简化起始坐标写法
	int ex = state.endr, ey = state.endc; //简化终点坐标写法
	if (map[bx][by].type == RED) //判断是红棋还是黑棋,以便于分析是否过八方格
	{ 
		if (ex < 7 || ey < 3 || ey > 5) 
			return false; //如果红棋过界就返回false
	}
	else 
	{
		if (ex > 2 || ey < 3 || ey > 5) 
			return false; //如果黑棋过界就返回false
	}
	int nx[5] = { 0,-1,0,1 }, ny[5] = { -1,0,1,0 }; //通过计算得到下一步所有的合法位置
	bool flag = false;
	for (int i = 0; i < 4; i++) //如果下一步合法
	{ 
		if (ex == bx + nx[i] && ey == by + ny[i]) //判断终点位置是否合法
		{
			flag = true;
			break;
		}
	}
	return flag;
}

//卒和兵的移动规则
bool soliderRule()
{
	int bx = state.begr, by = state.begc; //简化起始坐标写法
	int ex = state.endr, ey = state.endc; //简化终点坐标写法

	if (map[bx][by].type == RED) //判断是红棋还是黑棋,以便于分析是否过河
	{ 
		if (bx < 5) //如果红棋过河
		{  
			if (abs(bx - ex) + abs(by - ey) > 1) //兵过了河之后只可以移动一格
				return false;//如果超过一步就返回否
			if (ex > bx) //但兵不能后退
				return false;//如果想后退就返回false,兵不能后退
		}
		else //如果红棋没过河
		{
			if (ey != by || ex != bx - 1) //兵只能前进
				return false; //如果没过河并且走的不是直棋,则返回false
			else 
				return true;
		}
	}
	else 
	{
		if (bx > 4) //如果黑棋过河
		{  
			if (abs(bx - ex) + abs(by - ey) > 1) //卒过了河之后只可以移动一格
				return false;//如果超过一步就返回否
			if (ex < bx) //但卒不能后退
				return false;//如果想后退就返回false,卒不能后退
		}
		else 
		{
			if (ey != by || ex != bx + 1) //卒只能前进
				return false;//如果没过河并且走的不是直棋,则返回false
			else 
				return true;
		}
	}
	return true;
}

//检查当前走棋是否正常
bool check()
{
	if (state.begr == state.endr && state.begc == state.endc) 
		return false;//点击的不是同一个棋子
	if (state.endr == -1 || state.begr == -1) 
		return false;//下标必须合法
	if (map[state.begr][state.begc].id == NONE) 
		return false;//没有棋子不能移动
	if (!(map[state.endr][state.endc].id == NONE //当棋子移动到的第二个位置不为空的时候 
		|| map[state.begr][state.begc].type != map[state.endr][state.endc].type)) 
		return false;//不能自己吃自己

	//作用:第一次只能移动红棋、第三次只能移动红棋...
	if (curRun == 0 && map[state.begr][state.begc].type != RED) 
		return false;
	//作用:第二次只能移动黑棋...
	if (curRun == -1 && map[state.begr][state.begc].type != BLACK) 
		return false;

	bool canMove = false;
	switch (map[state.begr][state.begc].id)
	{
	case:
	case:
		if (state.begr == state.endr || state.begc == state.endc)
		{
			//起始点和结束点之间是否有阻碍
			if (carRule())
			{
				canMove = true;
			}
		}
		break;
	case:
	case:
		//结束点是否合法,并且不被绊马脚
		if (horseRule())
		{
			canMove = true;
		}
		break;
	case:
	case:
		if (elephantRule())
		{
			canMove = true;
		}
		break;
	case:
	case:
		if (chapRule())
		{
			canMove = true;
		}
		break;
	case:
	case:
		if (masterRule())
		{
			canMove = true;
		}
		break;
	case:
	case:
		if (state.begr == state.endr || state.begc == state.endc)
		{
			//判断炮的走棋和吃棋
			if (cannoRule())
			{
				canMove = true;
			}
		}
		break;
	case:
	case:
		if (soliderRule())
		{
			canMove = true;
		}
		break;
	default:
		break;
	}
	return canMove;
}

//移动棋子
void chessMove()
{
	bool canMove = false;
	//什么情况下能够移动棋子
	if (check() && victory == 0)
	{
		if (map[state.begr][state.begc].type == RED)
		{
			printf("红棋");
		}
		else {
			printf("黑棋");
		}
		printf("从(%d,%d)走到(%d,%d)\n\n", state.begr, state.begc, state.endr, state.endc);

		int t = map[state.endr][state.endc].id;//记录第二个棋子的id,若为空,则id为-1

		map[state.endr][state.endc].id = map[state.begr][state.begc].id;
		map[state.begr][state.begc].id = NONE;//此时map[preRunx][preRuny].id或map[state.begr][state.begc].id为-1

		//map[state.endr][state.endc].isRiver = map[state.begr][state.begc].isRiver;//这段代码没用
		map[state.endr][state.endc].type = map[state.begr][state.begc].type;
		curRun = ~curRun; //~是异或符号,如果原来curRun是0,~curRun就是-1,如果原来是-1,~就是0

		//每走完一步棋就判断一次对面笑杀
		int sx = 0, sy = 4;//定义帅的坐标位置
		int jx = 9, jy = 4;//定义将的坐标位置

		for (int i = 0; i <= 2; i++) //遍历黑棋的九宫格找帥的位置
		{  
			for (int j = 3; j <= 5; j++) 
			{
				if (map[i][j].id ==) 
				{
					sx = i;
					sy = j;
					break;
				}
			}
		}

		for (int i = 7; i <= 9; i++) //遍历红棋的九宫格找将的位置
		{  
			for (int j = 3; j <= 5; j++) 
			{
				if (map[i][j].id ==) 
				{
					jx = i;
					jy = j;
					break;
				}
			}
		}

		if ( sy == jy) //如果将和帥在一条直线上
		{ 
			int num = 0;
			for (int i = sx + 1; i < jx; i++) //判断之间是否有其他棋子
			{ 
				if (map[i][sy].id != NONE)//之间有棋子
				{
					num++;//则num数量增加
				}
			}
			if (num == 0) //之间没有棋子,触发对面笑杀
			{ //先判断一下该谁走棋,如果该红棋,则红棋胜
				if (curRun == 0) //黑棋走完了,curRun经过异或变成0,此时应该红棋走,但由于已经出发了对面笑杀,则红棋胜
				{ 
					t = map[sx][sy].id;
					map[sx][sy].id = map[jx][jy].id;//map[sx][sy].id是黑方的帅,map[jx][jy].id是红方的将,实现了将吃帅
					map[jx][jy].id = NONE;//把之前将的id置为0
					map[sx][sy].type = map[jx][jy].type;//把被吃棋子的颜色换成将的颜色
				}
				else if (curRun == -1) //红棋走完了,curRun经过异或变成-1,此时应该黑棋走,但由于已经出发了对面笑杀,则黑棋胜
				{ 
					t = map[jx][jy].id;
					map[jx][jy].id = map[sx][sy].id;//map[jx][jy].id是红方的将,map[sx][sy].id是黑方的帅,实现了帅吃将
					map[sx][sy].id = NONE;//把之前帅的id置为0
					map[jx][jy].type = map[sx][sy].type;//把被吃棋子的颜色换成帅的颜色
				}
			}
		}

		if (t ==)
		{
			victory = -1; //红棋赢了!
		}
		else if (t ==)
		{
			victory = 1; //黑棋赢了!
		}
	}
	initPreRun();
	initState();//到这一步无论有没有走棋,都重置
}

//鼠标操作
void mouseEvent()
{
	ExMessage msg;	//定义消息结构体变量
	if (peekmessage(&msg, EM_MOUSE))
	{
		if (msg.message == WM_LBUTTONDOWN)	//鼠标左键按下
		{
			//通过鼠标坐标得出点击的数组的下标
			//k * GRID_SIZE + INTERVAL = x;
			int col = (msg.x - INTERVAL) / GRID_SIZE;
			int row = (msg.y - INTERVAL) / GRID_SIZE;

			//下标校准
			if (msg.x > map[row][col].x + 30 && msg.y < map[row][col].y + 30)
			{
				col++;
			}
			if (msg.x < map[row][col].x + 30 && msg.y > map[row][col].y + 30)
			{
				row++;
			}
			if (msg.x > map[row][col].x + 30 && msg.y > map[row][col].y + 30)
			{
				row++;
				col++;
			}

			if (state.state == BEGIN && ((curRun == 0 && map[row][col].type == RED) || (curRun == -1 && map[row][col].type == BLACK)))
			{
				state.begr = row;
				state.begc = col;
				printf("鼠标第一次点击的");
				if (map[state.begr][state.begc].type == RED && map[state.begr][state.begc].id != NONE)
				{
					printf("红棋为");
					printf("%s,", ChessName[map[state.begr][state.begc].id]);
				}
				else if (map[state.begr][state.begc].type == BLACK && map[state.begr][state.begc].id != NONE)
				{
					printf("黑棋为");
					printf("%s", ChessName[map[state.begr][state.begc].id]);
				}
				else
				{
					printf("空棋");
				}
				printf("其坐标是:(%d,%d)\n", state.begr, state.begc);
				state.state = END;
				preRunx = row;
				preRuny = col;
			}
			else if (state.state == END)
			{
				state.endr = row;
				state.endc = col;
				printf("鼠标第二次点击的");
				if (map[state.endr][state.endc].type == RED && map[state.endr][state.endc].id != NONE)
				{
					printf("红棋为");
					printf("%s,", ChessName[map[state.endr][state.endc].id]);
				}
				else if (map[state.endr][state.endc].type == BLACK && map[state.endr][state.endc].id != NONE)
				{
					printf("黑棋为");
					printf("%s", ChessName[map[state.endr][state.endc].id]);
				}
				else
				{
					printf("空棋,");
				}
				printf("其坐标是:(%d,%d)\n", state.endr, state.endc);

				state.state = BEGIN;

				if (map[state.endr][state.endc].type == map[state.begr][state.begc].type && map[state.endr][state.endc].id != NONE) 
				{
					state.state = END;
					state.begr = row;
					state.begc = col;
					preRunx = row;
					preRuny = col;
				}
				else
				{
					chessMove();//只有当成功点击两次才进行走棋判断
				}
			}
		}
	}
}

//特效
void special_effects()
{
	setlinecolor(BLUE);
	if (preRunx != -1 && preRuny != -1 && map[preRunx][preRuny].id != NONE)
	{
		rectangle(map[preRunx][preRuny].x - 30, map[preRunx][preRuny].y - 30, map[preRunx][preRuny].x + 30, map[preRunx][preRuny].y + 30);
	}
	settextstyle(100, 0, _T("宋体"));
	settextcolor(RGB(0, 122, 204));
	if (victory == -1)
	{
		outtextxy(150, 360, "红棋赢了!");
	}
	else if (victory == 1)
	{
		outtextxy(150, 360, "黑棋赢了!");
	}
}

int main()
{
	//创建图形窗口
	initgraph(740, 820, EW_SHOWCONSOLE);
	//设置背景模式
	setbkmode(TRANSPARENT);
	//贴棋盘
	IMAGE img_board;
	loadimage(&img_board, "./res/ChessBoard.png");

	init();
	//双缓冲绘图,防止闪屏
	BeginBatchDraw();
	while (true)
	{
		cleardevice();
		putimage(0, 0, &img_board);
		draw();
		mouseEvent();
		special_effects();
		FlushBatchDraw();
	}
	EndBatchDraw();

	char t = getchar();
	return 0;
}

四.运行结果:
在这里插入图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值