上几篇中,我们详细介绍了,棋盘类的定义和关键属性,简要介绍了棋盘绘制算法的骨架。
本篇,我们将详细解读棋盘绘制算法的每一个细节。
强烈建议,大家结合文章末尾的“棋盘截图”来思考绘制算法细节,不然,很可能会遇到问题。
有些绘制细节,很难懂,不好描述,不再详细叙述。
1.绘制算法骨架
/**
* 绘制棋盘
* <P>
* 绘制棋盘背景
* </P>
* <P>
* 10条横线
* <P>
* 9条纵线
* </P>
* <P>
* 炮兵卒14个标记
* </P>
* <P>
* 九宫格
* </P>
* <P>
* 楚河漢界
* </P>
* <P>
* 如果有棋子移动,画出2个提示框,每个提示框由8条线组成
* </P>
* <P>
* 绘制可选走法的提示框
* </P>
* <P>
* 绘制竖线标记
* </P>
*
*
* 根据需要还绘制棋子移动的标记
*/
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// 绘制棋盘背景
drawBackgroundImage(g);
Graphics2D g2 = (Graphics2D) g;
// 兵、卒、炮标记笔画
BasicStroke bsFlag = new BasicStroke(2);
// 楚河汉界、棋盘边框笔画
BasicStroke bsLine = new BasicStroke(2);
// 棋盘线笔画
BasicStroke bs1 = new BasicStroke(1);
// 绘制直线
drawLines(g2, bsLine, bs1);
// 绘制九宫格
drawJiuGongLines(g2, bs1);
// 绘制楚河漢界
drawChuheHanjieString(g2);
// 绘制炮和兵标记
drawPaoBingFlag(g2, bsFlag);
// 如果有棋子移动,画出2个提示框,每个提示框由8条线组成
drawMoveFlag(g2);
// 绘制可选走法的提示框
drawWillMoveFlag(g2);
// 设置字体和线宽,为画坐标做准备
BasicStroke bsOld = new BasicStroke(1);
g2.setStroke(bsOld);
g2.setFont(new Font("宋体", Font.PLAIN, 14));
g2.setColor(new Color(0, 0, 0));
// 绘制竖线标记
drawShuXianFlag(g2);
}
2.绘制算法细节
2.1绘制棋盘背景
/**
* 绘制棋盘背景
*/
private void drawBackgroundImage(Graphics g) {
//获得棋盘背景
Image image = getBackgroundImage();
if (image != null) {
Dimension size = new Dimension(super.getWidth(), super.getHeight());
//在指定的矩形区域绘制image图像
g.drawImage(image, 0, 0, size.width, size.height, null);
}
}
// 默认不绘制背景图片
protected Image getBackgroundImage() {
return null;
}
需要特别说明的是,这里用到了“模版方法模式”。
具体的子类可以重载getBackgroundImage方法,从而可以自定义背景图像。
2.2绘制直线(10条横线和9条竖线)
private void drawLines(Graphics2D g2, BasicStroke bsLine, BasicStroke bs1) {
// 10条横线
for (int j = 1; j <= Y; j++) {
// 4条需要加粗的横线
if (j == 1 || j == 5 || j == 6 || j == 10) {
g2.setStroke(bsLine);
// TODO 重复代码可以提取出来
g2.drawLine(chessPoints[1][j].getX(), chessPoints[1][j].getY(),
chessPoints[X][j].getX(), chessPoints[X][j].getY());
}
// 6条不需要加粗的横线
else {
g2.setStroke(bs1);
g2.drawLine(chessPoints[1][j].getX(), chessPoints[1][j].getY(),
chessPoints[X][j].getX(), chessPoints[X][j].getY());
}
}
// 9条纵线
for (int i = 1; i <= X; i++) {
// 中间的纵线
if (i != 1 && i != X) {
g2.setStroke(bs1);
//上半区的纵线
g2.drawLine(chessPoints[i][1].getX(), chessPoints[i][1].getY(),
chessPoints[i][Y - 5].getX(),
chessPoints[i][Y - 5].getY());
//下半区的纵线
g2.drawLine(chessPoints[i][Y - 4].getX(),
chessPoints[i][Y - 4].getY(), chessPoints[i][Y].getX(),
chessPoints[i][Y].getY());
}
// 两边的加粗的纵线
else {
g2.setStroke(bsLine);
g2.drawLine(chessPoints[i][1].getX(), chessPoints[i][1].getY(),
chessPoints[i][Y].getX(), chessPoints[i][Y].getY());
}
}
}
2.3绘制九宫格
private void drawJiuGongLines(Graphics2D g2, BasicStroke bs1) {
// 红黑双方将帅的九宫格,4条斜线
g2.setStroke(bs1);
g2.drawLine(chessPoints[4][1].getX(), chessPoints[4][1].getY(),
chessPoints[6][3].getX(), chessPoints[6][3].getY());
g2.drawLine(chessPoints[6][1].getX(), chessPoints[6][1].getY(),
chessPoints[4][3].getX(), chessPoints[4][3].getY());
g2.drawLine(chessPoints[4][8].getX(), chessPoints[4][8].getY(),
chessPoints[6][Y].getX(), chessPoints[6][Y].getY());
g2.drawLine(chessPoints[4][Y].getX(), chessPoints[4][Y].getY(),
chessPoints[6][8].getX(), chessPoints[6][8].getY());
}
2.4绘制楚河漢界4个汉字
private void drawChuheHanjieString(Graphics2D g2) {
// 楚河、汉界
g2.setFont(new Font("宋体", Font.PLAIN, 32));
g2.drawString("漢 界", chessPoints[2][5].getX(), chessPoints[2][5].getY()
+ 2 * UNIT_HEIGHT / 3 + 2);
g2.drawString("楚 河", chessPoints[6][5].getX(), chessPoints[2][5].getY()
+ 2 * UNIT_HEIGHT / 3 + 2);
}
2.5绘制炮和兵的位置标记
private void drawPaoBingFlag(Graphics2D g2, BasicStroke bsFlag) {
// 画炮和兵的位置的标记
int size = sidePoints.size();
// 棋子中心点到标记直角边交点的水平距离
double x = PIECE_WIDTH / 9;
// 标记的长度
double side = PIECE_WIDTH / 6;
for (int i = 0; i < size; i++) {
double a = sidePoints.get(i).getX();
double b = sidePoints.get(i).getY();
g2.setStroke(bsFlag);
if (i >= 0 && i <= 9) {
//绘制中间的炮兵10个棋子
drawPBMiddle(g2, x, side, a, b);
} else if (i == 10 || i == 11) {
//右边的1个卒和1个兵 TODO 方法名称不够合理
drawPBRight(g2, x, side, a, b);
} else if (i == 12 || i == 13) {
//左边的1个卒和1个兵 TODO 方法名称不够合理
drawPBLeft(g2, x, side, a, b);
}
}
}
//绘制中间的炮兵10个棋子,1个完整的标记由8条线构成
private void drawPBMiddle(Graphics2D g2, double x, double side, double a,
double b) {
// 左上角
g2.drawLine((int) (a - x), (int) (b - x), (int) (a - x),
(int) (b - x - side));
g2.drawLine((int) (a - x), (int) (b - x), (int) (a - x - side),
(int) (b - x));
// 左下角
g2.drawLine((int) (a - x), (int) (b + x), (int) (a - x),
(int) (b + x + side));
g2.drawLine((int) (a - x), (int) (b + x), (int) (a - x - side),
(int) (b + x));
// 右上角
g2.drawLine((int) (a + x), (int) (b - x), (int) (a + x),
(int) (b - x - side));
g2.drawLine((int) (a + x), (int) (b - x), (int) (a + x + side),
(int) (b - x));
// 右下角
g2.drawLine((int) (a + x), (int) (b + x), (int) (a + x),
(int) (b + x + side));
g2.drawLine((int) (a + x), (int) (b + x), (int) (a + x + side),
(int) (b + x));
}
2.6如果有棋子移动,画出2个提示框,每个提示框由8条线组成
先计算出,棋子起始位置的坐标,然后绘制提示框。
类似于“绘制炮和兵的位置标记”。
2.7绘制可选走法的提示框
先计算出,所有可选走法位置的坐标,然后绘制提示框。
类似于“绘制炮和兵的位置标记”。
2.8绘制竖线标记
// 默认,竖线标记1到9,一到九,是按照红方在下,黑方在上绘制的。如果子类不应该这样话,应该重载此方法,重新绘制。
//方便棋手走棋,“馬八进七”。
protected void drawShuXianFlag(Graphics2D g2) {
// 绘制上方的1到9
for (int i = 1; i <= X; i++) {
g2.drawString("" + i, i * UNIT_WIDTH - 4, UNIT_HEIGHT / 2 - 4);
}
// 绘制下方的一到九
for (int i = 1; i <= X; i++) {
g2.drawString("" + ChessUtils.numToZi(10 - i), i * UNIT_WIDTH - 4,
10 * UNIT_HEIGHT + 34);
}
}
3.棋盘效果
4.总结
棋盘绘制算法的核心思路就是,定制算法骨架,分别实现每一个子算法。
主要用到的是Java图形界面和绘图类库,包括Swing GUI, Graphics、Graphics2D、BasicStroke。
如果有疑问,建议,读者多参考Java API文档。
今后,如有可能,我们再详细介绍这些类库的用法。
5.痛点
绘制棋盘是中国象棋程序非常有难度的一个问题。
限于时间、耐心、表达能力有限,本文仅仅是较为详细地介绍了一部分算法细节。
如果想要更好的介绍,1种方式是“当面交流”,或者是“图文并茂”。
可惜,后2种方式,不够现实,太麻烦。
今后如有可能,我会尝试后面2种方式的。
相关阅读
原文参见:http://FansUnion.cn/articles/2919
分享到:
相关推荐
使用alpha-beta剪枝算法实现中国象棋人机对战,AI具有中级的智能,可以应对一般的象棋爱好者。
算法分析课程设计:棋盘游戏设计-象棋 目录 一.选题背景和研究意义……………………………… 二.需求分析……………………………………………… (1)系统运行环境………………………………………… (2)问题描述...
我们的中国象棋使用python实现,总共2000+行代码,分为走法计算、评估函数与搜索和UI三部分,并采用历史启发算法进行优化,有着不错的效果。可以实现正常的人机对战,有着普通人的棋力。详细信息(有惊喜)可以查看...
用pygame简单实现的一个中国象棋代码
基于QT_C++中国象棋算法设计与实现源码论文答辩ppt.rar
java程序-中国象棋java程序-中国象棋java程序-中国象棋java程序-中国象棋java程序-中国象棋java程序-中国象棋java程序-中国象棋
这是一个uniapp项目源码例子【中国象棋-单机游戏】项目完整,通过HBuilder X开发工具选uniapp方式打开,uniapp项目可编译发行跨端应用(包括了各种小程序以及APP),可编译正常运行,供学习请参考文章...
Android自定义中国象棋,原创-------转载注明出处; Android自定义中国象棋,原创-------转载注明出处; Android自定义中国象棋,原创-------转载注明出处; Android自定义中国象棋,原创-------转载注明出处; ...
C#中国象棋算法设 首先对棋盘进行初始化以,然后将每一个棋子给予初值;并将每个棋子放在棋盘对应的位置上,全部初始化完毕后就可以得到一个简易的摆好的象棋棋盘,其中红子的数值小于20,绿子的数值大于20。而后...
画中国象棋棋盘。在图形模式下画出中国象棋的棋盘,不需要画出棋子。
中国象棋源码,基于王小春《PC游戏编成--人机对弈》 版权归原作者所有 。
毕业设计,中国象棋游戏的设计与实现。visual c++6.0写的
问题描述:设计一个国际象棋的马踏棋盘的演示程序 基本要求: 1.将马随机放在国际象棋的8×8棋盘的某个方格中,马按照走棋的规则进行移动。要求每个方格只进入一次,走遍棋盘的全部64个方格。编制非递归程序,求出马...
中国象棋发展至今已有数千年的历史了,它是中华民族智慧的结晶。...辑封装于类,实现基于对局树算法的中国象棋游戏系统。系统使用QT 开发工具,实现 了一个具有一定棋力的中国象棋人机对弈和双人对战程序。
2010年,我从CSDN下载中心下载了1个中国象棋程序,功能比较简单。 我在此基础上,开发了当前版本(1.0)的程序。相对于下载的版本, 增加了联网对战,悔棋等功能,增加必要的注释,重命名类、函数、变量的名字,提高...
这是一个微信小程序源码例子【中国象棋-单机游戏】项目完整,通过微信开发工具小程序方式打开,可编译正常运行,供学习请参考文章https://blog.csdn.net/zs1028/article/details/121152445#comments_18994821
1.QT基础介绍 2.象棋整体框架介绍 3.使用QT绘制棋盘代码演示
中国象棋的AI算法,一步一步教你学习AI设计,六个版本,难度逐次增加。非常好的学习资料。
基于深度学习的天天象棋盘面自动识别算法
中国象棋算法独家收藏,人机对战,通过各种渠道很难总结出来的。。。希望对大家有所帮助。