资讯专栏INFORMATION COLUMN

C/C++游戏项目:中国程序员一定要会的中国象棋教程

kamushin233 / 1914人阅读

摘要:中国象棋是中国一种流传十分广泛的游戏。宣布认输的一方算输。今天我就用语言带大家一步步去完成好玩有趣学会就能和朋友对弈的中国象棋小游戏。

       中国象棋是中国一种流传十分广泛的游戏。 下棋双方根据自己对棋局形式的理解和对棋艺规律的掌握,调动车马,组织兵力,协调作战在棋盘这块特定的战场上进行着象征性的军事战斗。 象棋,亦作“象碁”,为了区别“国际象棋”也作“中国象棋”,中国象棋在中国有着悠久的历史,属于二人对抗性游戏的一种,由于用具简单,趣味性强,成为流行极为广泛的棋艺活动。

游戏规则

行棋规则:

棋子行棋规则帅/将移动范围:只能在九宫内移动移动规则:每一步只可以水平或垂直移动一点特殊规则:帅和将不准在同一直线上直接对面(中间无棋子),如一方已先占据位置,则另一方必须回避,否则就算输仕/士移动范围:只能在九宫内移动移动规则:每一步只可以沿对角线方向移动一点相/象移动范围:河界的一侧移动规则:每一步只可以沿对角线方向移动两点,可使用汉字中的田字形象地表述:田字格的对角线,俗称相(象)走田字。当相(象)行走路线中,即田字中心有棋子时(无论己方或是对方棋子),则不允许走过去,俗称:塞相(象)眼。馬移动范围:任何位置移动规则:每一步只可以水平或垂直移动一点,再按对角线方面向左或者右移动。可使用汉字中的日字来形容马的行走方式,俗称:马走日字(斜对角线)。当馬行走时,第一步直行或横行处有别的棋子(无论己方或是对方棋子)挡住,则不许走过去,俗称:蹩马腿。車移动范围:任何位置移动规则:可以水平或垂直方向移动任意个无阻碍的点炮/砲移动范围:任何位置移动规则:移动起来和车很相似,但它必须跳过一个棋子来吃掉对方棋子。兵/卒移动范围:任何位置移动规则:过河界前,每步只能向前移动一点。过河界后,增加了向左右移动的能力,兵(卒)不允许向后移动。

吃子规则:

无论什么棋子,通常只要根据行棋规则能走到的部位有对方的棋子就能吃掉对方的棋子。

唯一例外的是炮的吃棋方法,比较特殊,需要中间隔有棋子(无论是己方或对方棋子)才能吃掉对方的棋子。

胜负判定:

帅(将)被对方“将死”或“困毙”一方算输。

宣布认输的一方算输。

今天我就用C语言带大家一步步去完成好玩有趣学会就能和朋友对弈的中国象棋小游戏。

PS:要安装easyx图形库哦 #include    

开发工具为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[ROW][COL];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[i][k].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[i][k].id = NONE;	//先把棋子置为没有			if (i <= 4)	//黑棋子			{				map[i][k].type = BLACK;				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];				}			}			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];			}			}			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, "楷体");	for (size_t i = 0; i < ROW; i++)	{		for (size_t 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);			fillcircle(map[i][k].x, map[i][k].y, 25);			outtextxy(map[i][k].x - 15, map[i][k].y - 15, ChessName[map[i][k].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[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++;			}			//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[state.begr][state.begc].id != NONE//没有棋子不能移动		/*&&map[state.begr][state.begc].type != map[state.endr][state.endc].type*/)	//不能自己吃自己	{		switch (map[state.begr][state.begc].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[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;		}	}}

中国象棋的教程就到此结束啦,有兴趣的同学可以尝试写出来,后续我会发布更多的项目源码以及学习资料,希望大家可以持续关注,想要C/C++学习资料以及完整源码素材图形库开发工具等的可以加群【639681529】了解,或者有问题也可以在评论区或者进群提问希望大家可以在这里得到自己想要的知识,也希望如果对你有所帮助的话可以多多关注点赞评论,有建议也可以在评论区提出,谢谢大家的支持!

点击下方链接进群:

进群领取项目源码素材及图形库哦~还有各种学习资料等你!http://点击链接加入群聊【C语言/C++学习答疑交流】:https://jq.qq.com/?_wv=1027&k=9UocdboWhttp://xn--c-14ts39jerazz506etjubj1ayhbm86c60et16a/C++%E5%AD%A6%E4%B9%A0%E7%AD%94%E7%96%91%E4%BA%A4%E6%B5%81%E3%80%91%EF%BC%9Ahttps://jq.qq.com/?_wv=1027&k=9UocdboW

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/123682.html

相关文章

  • 开源集锦

    摘要:密码服务层分片分片新思路目前国内流行的开源数据库分片实现都基于的改写分发与结果归并。虽然独立开发人员仍是开源社区的重要组成部分,但如今开源项目的大部分工作由企业开发人员完成。 Android2017 这些技术 —— 你都了解过吗 插件化 热更新 对Android平台,我们通常所说的在线热更新就是,例如对已经发布在应用市场上的宿主APK,当我们从crash统计平台上发现某个函数调用有bu...

    Simon 评论0 收藏0
  • “互联网+”带给云计算的机遇

    摘要:今年月,互联网行动计划出炉。互联网带给云计算的机会互联网下的传统行业转型将百花齐放,但云计算的使命却相当明确首先要为传统行业提供一个互联网化的基础平台,其次要针对具体行业打造上层应用。  今年3月,互联网+行动计划出炉。政府明确提出,要借助互联网提升全社会的创新力和生产力,形成更广泛的以互联网为基础设施和实现工具的经济发展新形态。显然,互联网+新业态需要互联网与传统行业的融合与创新,而作为互...

    fxp 评论0 收藏0
  • 2018前端值得关注的技术

    摘要:年前端有哪些领域,技术值得关注,哪些技术会兴起,哪些技术会没落。自从谷歌提出后,就持续的获得了业界的关注,热度可见一斑。就在今年,谷歌也宣布将获得与安卓原生应用同等的待遇与权限。但是无论都值得关注。 1.前言 2017悄然过去,2018已经来到。人在进步,技术在发展。2018年前端有哪些领域,技术值得关注,哪些技术会兴起,哪些技术会没落。下面就我个人的判断进行一个预测判断,希望能对大家...

    xiao7cn 评论0 收藏0
  • 2018前端值得关注的技术

    摘要:年前端有哪些领域,技术值得关注,哪些技术会兴起,哪些技术会没落。自从谷歌提出后,就持续的获得了业界的关注,热度可见一斑。就在今年,谷歌也宣布将获得与安卓原生应用同等的待遇与权限。但是无论都值得关注。 1.前言 2017悄然过去,2018已经来到。人在进步,技术在发展。2018年前端有哪些领域,技术值得关注,哪些技术会兴起,哪些技术会没落。下面就我个人的判断进行一个预测判断,希望能对大家...

    用户84 评论0 收藏0
  • 专访Zynga CTO Cadir Lee,谈谈Zynga的云计算与核心价值观

    摘要:我们使用了很多的公共云资源,自己也建立了私有的云计算中心。那你们会给腾讯提供一些这方面的建议吗会的,我们跟他们合作密切,我们之间的交流很频繁。 Cadir Lee,现任Zynga CTO,统管公司的技术平台和海量基础架构的研发和创新。他管理数据分析、网络运维、安全等方面的团队。在加入Zynga之前,他担任Support.com的CTO11年之久,而Support.com也是他和Zynga创始...

    ThinkSNS 评论0 收藏0

发表评论

0条评论

kamushin233

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<