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

Posted 快他妈帮帮胡桃吧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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[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】了解,或者有问题也可以在评论区或者进群提问希望大家可以在这里得到自己想要的知识,也希望如果对你有所帮助的话可以多多关注点赞评论,有建议也可以在评论区提出,谢谢大家的支持!

点击下方链接进群:

源码素材图形库还有各种学习资料都在这里等你哦https://jq.qq.com/?_wv=1027&k=3n6aJrfO

以上是关于C/C++游戏项目:中国程序员一定要会的中国象棋教程的主要内容,如果未能解决你的问题,请参考以下文章

C/C++项目实战:华容道游戏开发丨570 行源码分享来啦~

一定要会的synchronized关键字的用法

一定要会的synchronized关键字的用法

前端开发注意啦!一定要会的DOM基础操作(上)

测试人一定要会的技能:selenium的三种等待方式解读,清晰明了

minecraft我的世界gradle构建,总是失败,求告诉详细教程。不要发网址,要会的人。不会的