中国象棋程序的设计与实现(十二)--棋盘绘制算法(尽管注释非常详细,完全理解仍有难度)
2013-10-26 07:27
661 查看
上几篇中,我们详细介绍了,棋盘类的定义和关键属性,简要介绍了棋盘绘制算法的骨架。
本篇,我们将详细解读棋盘绘制算法的每一个细节。
强烈建议,大家结合文章末尾的“棋盘截图”来思考绘制算法细节,不然,很可能会遇到问题。
有些绘制细节,很难懂,不好描述,不再详细叙述。
1.绘制算法骨架
2.绘制算法细节
2.1绘制棋盘背景
需要特别说明的是,这里用到了“模版方法模式”。
具体的子类可以重载getBackgroundImage方法,从而可以自定义背景图像。
2.2绘制直线(10条横线和9条竖线)
2.3绘制九宫格
2.4绘制楚河漢界4个汉字
2.5绘制炮和兵的位置标记
2.6如果有棋子移动,画出2个提示框,每个提示框由8条线组成
先计算出,棋子起始位置的坐标,然后绘制提示框。
类似于“绘制炮和兵的位置标记”。
2.7绘制可选走法的提示框
先计算出,所有可选走法位置的坐标,然后绘制提示框。
类似于“绘制炮和兵的位置标记”。
2.8绘制竖线标记
3.棋盘效果
4.总结
棋盘绘制算法的核心思路就是,定制算法骨架,分别实现每一个子算法。
主要用到的是Java图形界面和绘图类库,包括Swing GUI, Graphics、Graphics2D、BasicStroke。
如果有疑问,建议,读者多参考Java API文档。
今后,如有可能,我们再详细介绍这些类库的用法。
5.痛点
绘制棋盘是中国象棋程序非常有难度的一个问题。
限于时间、耐心、表达能力有限,本文仅仅是较为详细地介绍了一部分算法细节。
如果想要更好的介绍,1种方式是“当面交流”,或者是“图文并茂”。
可惜,后2种方式,不够现实,太麻烦。
今后如有可能,我会尝试后面2种方式的。
相关阅读
原文参见:http://FansUnion.cn/articles/2919
本篇,我们将详细解读棋盘绘制算法的每一个细节。
强烈建议,大家结合文章末尾的“棋盘截图”来思考绘制算法细节,不然,很可能会遇到问题。
有些绘制细节,很难懂,不好描述,不再详细叙述。
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种方式的。
相关阅读
中国象棋程序的设计与实现(零)--原始版源码
中国象棋程序的设计与实现(一)--项目截图
中国象棋程序的设计与实现(二)--源码
中国象棋程序的设计与实现(三)--2012本科毕业论文等重要文档资料
中国象棋程序的设计与实现(四)-- 一次“流产”的写书计划
中国象棋程序的设计与实现(五)--回答CSDN读者的一些问题
中国象棋程序的设计与实现(六)--N皇后问题的算法设计与实现(源码+注释+截图)
中国象棋程序的设计与实现(七)--心得体会和开发日志
中国象棋程序的设计与实现(八)-如何构造一个棋子(車馬炮等)
中国象棋程序的设计与实现(九)–棋子点,棋子的小窝
中国象棋程序的设计与实现(十)--棋盘的定义和绘制原文参见:http://FansUnion.cn/articles/2919
相关文章推荐
- 中国象棋程序的设计与实现(十二)--棋盘绘制算法(尽管注释非常详细,完全理解仍有难度)
- 中国象棋程序的设计与实现(十二)--棋盘绘制算法(尽管注释非常详细,完全理解仍有难度)
- 中国象棋程序的设计与实现(十二)--棋盘绘制算法(尽管注释非常详细,完全理解仍有难度)
- 中国象棋程序的设计与实现(十)--棋盘的定义和绘制
- 中国象棋程序的设计与实现(十)--棋盘的定义和绘制
- 中国象棋程序的设计与实现(十)--棋盘的定义和绘制
- 中国象棋程序的设计与实现(十)--棋盘的定义和绘制
- 中国象棋程序的设计与实现(六)--N皇后问题的算法设计与实现(源码+注释+截图)
- 中国象棋程序的设计与实现(六)--N皇后问题的算法设计与实现(源码+注释+截图)
- 中国象棋程序的设计与实现(六)--N皇后问题的算法设计与实现(源码+注释+截图)
- 中国象棋程序的设计与实现(六)--N皇后问题的算法设计与实现(源码+注释+截图)
- 中国象棋程序的设计与实现(十)--棋盘的定义和绘制
- Windows 下利用MFC实现的中国象棋棋盘绘制程序
- JAVA中用程序绘制国际象棋与中国象棋棋盘
- 中国象棋程序的设计与实现(七)--心得体会和开发日志
- 中国象棋程序的设计与实现(二)--源码
- 中国象棋程序的设计与实现(三)--2012本科毕业论文等重要文档资料
- 中国象棋程序的设计与实现(九)–棋子点,棋子的小窝
- 中国象棋程序的设计与实现(二)--源码
- 中国象棋程序的设计与实现(二)--源码