一.图片资源:
二.图片存放位置:
三.三种不同版本的中国象棋源代码
三种源代码运行之前都需要
点击项目-属性
找到这个地方,把字符集改成“使用多字节字符集”:
一.版本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;
}
四.运行结果: