C/C++游戏项目:中国程序员一定要会的中国象棋教程
中国象棋是中国一种流传十分广泛的游戏。 下棋双方根据自己对棋局形式的理解和对棋艺规律的掌握,调动车马,组织兵力,协调作战在棋盘这块特定的战场上进行着象征性的军事战斗。 象棋,亦作“象碁”,为了区别“国际象棋”也作“中国象棋”,中国象棋在中国有着悠久的历史,属于二人对抗性游戏的一种,由于用具简单,趣味性强,成为流行极为广泛的棋艺活动。游戏规则
行棋规则:
棋子行棋规则帅/将移动范围:只能在九宫内移动移动规则:每一步只可以水平或垂直移动一点特殊规则:帅和将不准在同一直线上直接对面(中间无棋子),如一方已先占据位置,则另一方必须回避,否则就算输仕/士移动范围:只能在九宫内移动移动规则:每一步只可以沿对角线方向移动一点相/象移动范围:河界的一侧移动规则:每一步只可以沿对角线方向移动两点,可使用汉字中的田字形象地表述:田字格的对角线,俗称相(象)走田字。当相(象)行走路线中,即田字中心有棋子时(无论己方或是对方棋子),则不允许走过去,俗称:塞相(象)眼。馬移动范围:任何位置移动规则:每一步只可以水平或垂直移动一点,再按对角线方面向左或者右移动。可使用汉字中的日字来形容马的行走方式,俗称:马走日字(斜对角线)。当馬行走时,第一步直行或横行处有别的棋子(无论己方或是对方棋子)挡住,则不许走过去,俗称:蹩马腿。車移动范围:任何位置移动规则:可以水平或垂直方向移动任意个无阻碍的点炮/砲移动范围:任何位置移动规则:移动起来和车很相似,但它必须跳过一个棋子来吃掉对方棋子。兵/卒移动范围:任何位置移动规则:过河界前,每步只能向前移动一点。过河界后,增加了向左右移动的能力,兵(卒)不允许向后移动。
吃子规则:
无论什么棋子,通常只要根据行棋规则能走到的部位有对方的棋子就能吃掉对方的棋子。
唯一例外的是炮的吃棋方法,比较特殊,需要中间隔有棋子(无论是己方或对方棋子)才能吃掉对方的棋子。
胜负判定:
帅(将)被对方“将死”或“困毙”一方算输。
宣布认输的一方算输。
今天我就用C语言带大家一步步去完成好玩有趣学会就能和朋友对弈的中国象棋小游戏。
PS:要安装easyx图形库哦 #include<easyx.h>
开发工具为VS2013
在此之前呢,和大家说明一下,因为这是一个比较大的项目了,所以展示所有代码会有些困难,所以我裁剪了主要的大部分代码,主要目的是让大家明白实现这个项目的逻辑思路,希望大家可以理解
第一步:创建一个项目,并将准备好的素材资源(文末获取)放到同级目录下如图:
第二步:接下来就是我们的主要函数main.Cpp了,创建一个窗口再贴上棋盘图,加上双缓冲绘图防止闪屏:
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(); FlushBatchDraw();}EndBatchDraw(); getchar();return 0;}
第三步:利用绘图找到各个点的坐标并绘制棋子,以及黑红棋子及棋子过河等:
enum Pieces //棋子{NONE = -1,車, 馬, 象, 士, 将, 砲, 卒,俥, 马, 相, 仕, 帥, 炮, 兵,BEGIN, END,};//给id赋值enum Pieces redChess[] = { 車, 馬, 象, 士, 将, 砲, 卒 };enum Pieces blackChess[] = { 俥, 马, 相, 仕, 帥, 炮, 兵 };//绘制时转化成字符串const char* ChessName[] = { "車","馬","象","士","将","砲","卒","俥", "马", "相", "仕", "帥", "炮", "兵" }; //每一个棋子的属性struct Chess{enum Pieces id;//棋子名称DWORD type;//棋子类型,红?黑?short x;short y;bool isRiver;//是否过了河};
第四步:宏定义#define ROW 10 #define COL 9 绘制十列九行的地图,并初始化数据,设置棋子的特殊移动规则:
//游戏地图struct Chess map; struct State{int begr;int begc;int endr;int endc;int state;}state = {-1,-1,-1,-1,BEGIN}; void chessMove();//打印数组void show(){for (size_t i = 0; i < ROW; i++){for (size_t k = 0; k < COL; k++){printf("%2d ", map.id);}printf("\n");}}//初始化数据void init(){//遍历地图for (size_t i = 0; i < ROW; i++){size_t temp = 0;for (size_t k = 0; k < COL; k++){map.id = NONE;//先把棋子置为没有if (i <= 4)//黑棋子{map.type = BLACK;if (i == 0)//放置第一行的棋子{//0 1 2 3 4if (k <= 4){temp = k;}// 3 2 1 0else{// k == 5temp = 4 - (k - 4);/*4 - (5-4)//34 - (6-4)//24 - (7-4)//14 - (8-4)//0*/}map.id = blackChess;}//设置炮if (i == 2 && (k == 1 || k == 7)){map.id = blackChess;}//设置兵if (i == 3 && k % 2 == 0){map.id = blackChess;}}else //红棋{map.type = RED;if (i == 9)//放置第一行的棋子{//0 1 2 3 4if (k <= 4){temp = k;}// 3 2 1 0else{// k == 5temp = 4 - (k - 4);/*4 - (5-4)//34 - (6-4)//24 - (7-4)//14 - (8-4)//0*/}map.id = redChess;}//设置炮if (i == 7 && (k == 1 || k == 7)){map.id = redChess;}//设置兵if (i == 6 && k % 2 == 0){map.id = redChess;}}map.isRiver = false;map.x = k * GRID_SIZE + INTERVAL;map.y = i * GRID_SIZE + INTERVAL;}}}//绘制void draw(){setfillcolor(RGB(252, 215, 162));setlinestyle(PS_SOLID, 2);//设置文字的样式settextstyle(30, 0, "楷体");for (size_t i = 0; i < ROW; i++){for (size_t k = 0; k < COL; k++){if (map.id == NONE)continue;settextcolor(map.type);setlinecolor(map.type);//绘制棋子fillcircle(map.x, map.y, 30);fillcircle(map.x, map.y, 25);outtextxy(map.x - 15, map.y - 15, ChessName.id]);}}}
第五步:设置获取鼠标操作:
//鼠标操作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.x + 30 && msg.y < map.y + 30){col++;}if (msg.x < map.x + 30 && msg.y > map.y + 30){row++;}if (msg.x > map.x + 30 && msg.y > map.y + 30){row++;col++;}//printf("(%d %d)\n", row, col); if (state.state == BEGIN){state.begr = row;state.begc = col;state.state = END;}else if (state.state == END){state.endr = row;state.endc = col;state.state = BEGIN;}chessMove();}}}int hasBlock(struct State* state){int cnt = 0;state->begr;state->begc;state->endr;state->endc; */ return cnt;}
第六步:设置棋子的移动:
//移动棋子void chessMove(){printf("beg(%d %d) end(%d %d)\n", state.begr, state.begc, state.endr, state.endc);bool canMove = false;//什么情况下能够移动棋子if (!(state.begr == state.endr && state.begc == state.endc) &&//点击的不是同一个棋子state.endr!=-1 && state.begr!=-1&&//下标必须合法map.id != NONE//没有棋子不能移动/*&&map.type != map.type*/)//不能自己吃自己{ switch (map.id){case 車:case 俥:if (state.begr == state.endr || state.begc == state.endc){//起始点和结束点之间是否有阻碍if (hasBlock(&state)){canMove = true;}}break;case 馬:case 马:break;case 象:case 相:break;case 士:case 仕:break;case 将:case 帥:break;case 砲:case 炮:break;case 卒:case 兵:break;default:break;}if (canMove){printf("canMove\n");map.id = map.id;map.id = NONE; map.isRiver = map.isRiver;map.type = map.type;}}}
中国象棋的教程就到此结束啦,有兴趣的同学可以尝试写出来,后续我会发布更多的项目教程,希望大家可以持续关注,希望大家可以在这里得到自己想要的知识,也希望如果对你有所帮助的话可以多多关注点赞评论,有建议也可以在评论区提出,谢谢大家的支持,大家也可以多逛逛我的主页!
点击链接加入群聊【C语言C++编程交流群】:https://jq.qq.com/?_wv=1027&k=XUKoNMPFjq.qq.com/?_wv=1027&k=XUKoNMPF
页:
[1]