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++游戏项目:中国程序员一定要会的中国象棋教程的更多相关文章
- [转]ThoughtWorks(中国)程序员读书雷达
http://agiledon.github.io/blog/2013/04/17/thoughtworks-developer-reading-radar/#rd?sukey=f64bfa68330 ...
- 第一章-第七题( 有人认为,“中文编程”, 是解决中国程序员编程效率一个秘密武器,请问它是一个 “银弹” 么? )--By 侯伟婷
首先,“银弹”在百度百科中的解释是银色的子弹,我们更熟知的“银弹”一词,应该是在<人月神话>中提到的.银弹原本应该是指某种策略.技术或者技巧可以极大地提高程序员的生产力[1].此题目中关于 ...
- 【转载】张逸--ThoughtWorks(中国)程序员读书雷达
原文地址:ThoughtWorks(中国)程序员读书雷达 软件业的特点是变化.若要提高软件开发的技能,就必须跟上技术发展的步伐.埋首醉心于项目开发与实战,固然能够锤炼自己的开发技巧,却难免受限于经验与 ...
- 远程办公《Remote》读书笔记:中国程序员在家上班月入过六万不是梦
这不是一本新书,这是一本很值得中国程序员看的老书,所以我不是来做卖新书广告的:) 但它的确是一本好书,这本书在Amazon上3个business categories排第一.作者Jason Fried ...
- ThoughtWorks(中国)程序员读书雷达 —— 书籍下载整理
ThoughtWorks(中国)程序员读书雷达 http://agiledon.github.io/blog/2013/04/17/thoughtworks-developer-reading-rad ...
- ThoughtWorks(中国) 程序员读书雷达
ThoughtWorks(中国)程序员读书雷达 软件业的特点是变化.若要提高软件开发的技能,就必须跟上技术发展的步伐.埋首醉心于项目开发与实战,固然能够锤炼自己的开发技巧,却难免受限于经验与学识.世界 ...
- 中国程序员容易发错音的单词「GitHub 热点速览 v.22.23」
中国程序员容易发错音的单词,像极了学生时代的纠错本,收录着偶尔会忘记的单词.不过,它似乎更新频率跟不上我们的进步速度,至少一半以上的单词读起来是没有压力的.同样没有压力的还有让应用程序动起来的 aut ...
- 对程序员的不尊重是中国it产业的悲哀。
电脑刚进入中国时,“程序员”三个字是一份令人尊敬的岗位,那个时候中国互联网人才奇缺.程序员的价格也就水涨船高.小的时候电视里到处播放着电脑培训学院的招生广告.一说到程序员,给我们的印象都是白领,高薪的 ...
- 推荐:ThoughtWorks(中国)程序员读书雷达
部分转自张逸的博客:http://agiledon.github.io/blog/2013/04/17/thoughtworks-developer-reading-radar/ 长久以来一直对程序员 ...
随机推荐
- Vue指令02——v-on指令和v-show的使用
Vue指令02--v-on指令和v-show的使用 v-on指令 格式1:v-on:事件="方法" 格式2:@事件="方法" 格式3:@事件="方法( ...
- Java如何跳出当前的多重嵌套循环?
在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环.例如, outer: for(int i=0;i<1 ...
- 四种类型的数据节点 Znode ?
1.PERSISTENT-持久节点 除非手动删除,否则节点一直存在于 Zookeeper 上 2.EPHEMERAL-临时节点 临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与 zoo ...
- vue单文件组件data选项的函数体获取vue实例对象
因配置的关系,导致 vue的data选项中存在事件.而事件无法获取 vue 的实例对象:项目是单文件形式的,以下代码只是例子 new Vue({ el:..., data:{ a: { onevent ...
- Javascript 占位符替换
String.prototype.format=function(){ if(arguments.length===0){ return String(this); } let reg=/(\{\d\ ...
- 在java web工程中实现登入和安全验证
登入页面的话我们之前做过直接可以拿来用翻一翻之前的博客就可以找到 在这个基础上添加验证功能 代码如下: 1 package security; 2 /** 3 * @author 鐜嬭儨鍗? 4 */ ...
- 不同版本的 Spring Framework 有哪些主要功能?
Version Feature Spring 2.5 发布于 2007 年.这是第一个支持注解的版本. Spring 3.0 发布于 2009 年.它完全利用了 Java5 中的改进,并为 JEE6 ...
- 学习heartbeat-02安装及配置
2.部署Heartbeat高可用需求 2.1 操作系统 CentOS-6.8-x86_64 2.2 Heartbeat服务主机资源准备 主服务器A: 主机名:heartbeat-1-130 eth0网 ...
- DateFormat类,利用SimpleDateFormat解决系统时间初始(格式化/解析)问题
目标: java.text.DateFormat 是日期/时间格式化子类的抽象类,我们通过这个类可以帮我们完成日期和文本之间的转换,也就是可以在Date对象与String对象之间进行来回转换. 格式化 ...
- asp.net 可视化操作(二)——Sql数据库连接及简单查询功能的实现
目录 连接数据库 利用repeater控件实现数据显示 查询功能 页面CSS美化 数据插入.更新-- 连接数据库 添加test.aspx 添加控件SqlDataSource,选择配置数据源 选择新建连 ...