Java-无界面版中国象棋

  • 说明
  • 版权声明
  • 环境
  • 一、效果
  • 二、测试Demo
  • 三、静态内容
  • 四、主要规则
  • 五、棋子移动规则
  • 后记

说明

  1. 博客日期:2022年2月5日13点14分星期六
  2. 无界面版中国象棋,是基于JDK 17的版本,虽然说是无界面,但其实还是能看到命令行界面的;
  3. 本工程尽可能采用模块化编程,并没有一个完全体,如有需要可以自行组装,默认组装了一个简单功能;
  4. 已有的功能包括棋子移动规则、将军规则、移动棋子等,计时、悔棋、死棋等功能按需自行完善;
  5. 本工程主要供参考棋子移动规则实现思路,后期可能移植到开发板上打发时间(摸鱼)。
  6. 项目仓库:ChineseChess ,如果仓库失效,请参考本文代码。

版权声明

该工程的代码均为本文作者撰写,无其他参考,允许使用在任何场景,遵循GPLv3.0开源协议,但转载本文请标注出处。

环境

IntelliJ IDEA 2021.3.1 (Ultimate Edition)
For educational use only.
Runtime version: 11.0.13+7-b1751.21 amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
Windows 10 10.0
GC: G1 Young Generation, G1 Old Generation
Memory: 2048M
Cores: 4Kotlin: 213-1.5.10-release-949-IJ6461.79
Java Development Kit 17

一、效果

  1. 先手是红方,当红方执棋完毕后,交换显示为黑方,只需要输入起始坐标和目标坐标即可,注意,行坐标优先

  2. 棋子移动不符合移动规范触发规则限制。

  3. 将军!当棋子监测到可以吃掉对方将帅即认为被将军。

  4. 如果被将军的一方还不肯阻挡被将,那么游戏可能结束,如果按下yY则可以重开游戏,因为测试是在一个循环内。

二、测试Demo

这样就是主函数,没啥好说的,起始相当于一个测试Demo,功能随意拼装,比如符合棋子移动规则后是否需要移动棋子、是否检查将军条件等;

package chess;import java.util.Scanner;/*** @author THDMI* @version 0.0.1* @date 2022/01/28*/
public class StartMain {public static void main(String[] args) {while (true) {PieceRule pieceRule = new PieceRule();Scanner sc = new Scanner(System.in);while (true) {Const.printPiece(pieceRule.chessboard, pieceRule.getFlagPlayer());if (null == pieceRule.getPosGeneral(pieceRule.getFlagPlayer())) {System.err.println("游戏结束!是否重开?");String choice = sc.next();if ("Y".equals(choice) || "y".equals(choice) || "Yes".equals(choice) || "yes".equals(choice)) {break;}System.exit(0);}System.out.format("当前阵营:%s方\n", Const.PIECE_FORMAT.getProperty(String.valueOf(pieceRule.getFlagPlayer())));System.out.print("请选择棋子行坐标X、列坐标Y、落子行坐标X、列坐标Y(空格间隔):");byte posSrcX = sc.nextByte();byte posSrcY = sc.nextByte();byte posDstX = sc.nextByte();byte posDstY = sc.nextByte();if (posSrcX < 0 || posSrcY < 0 || posDstX < 0 || posDstY < 0) {System.err.println("输入不合法,请重新输入!");continue;}if (pieceRule.isPermitRuleMove(posSrcX, posSrcY, posDstX, posDstY, pieceRule.getFlagPlayer())) {pieceRule.moveToPos(posSrcX, posSrcY, posDstX, posDstY);System.out.println("移动成功!");if (pieceRule.isCheckmate(posDstX, posDstY, pieceRule.getFlagPlayer())) {System.err.println("将军!");}pieceRule.exchangeFlagPlayer();} else {System.err.println("无法移动!");}}}}
}

三、静态内容

package chess;import java.util.Properties;/*** 在该项目中,规定从左上角开始为(0,0),水平向右方向为Y轴正方向,用列或Y表示,垂直向下方向为X轴正方向,用行或X表示** @author THDMI* @version 0.0.1* @date 2022/01/28*/
public class Const {/*** 单一阵营棋子类别总数*/public final static short AMOUNT_PIECE_TYPE = 7;/*** 红方*/public final static short FLAG_R = 0xFF;/*** 黑方*/public final static short FLAG_B = 0xFE;/*** 水平移动*/public final static boolean MOVE_HORIZONTAL = true;/*** 垂直移动*/public final static boolean MOVE_VERTICAL = false;/*** 空位*/public final static short BLOCK = 0x00;/*** 红方棋子标识:帅:0x01 仕:0x02 相:0x03 車: 0x04 馬:0x05 砲:0x06 兵:0x07*/public final static short[] PIECE_R = new short[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};/*** 黑方棋子标识:将:0x11 士:0x12 象:0x13 车: 0x14 马:0x15 炮:0x16 卒:0x17*/public final static short[] PIECE_B = new short[]{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};/*** 棋子文字:帅 仕 相 車 馬 砲 兵 将 士 象 车 马 炮 卒*/public final static String[] PIECE_NAME = new String[]{"帅", "仕", "相", "車", "馬", "砲", "兵", "将", "士", "象", "车", "马", "炮", "卒"};/*** 棋子标识和棋名之间的对应关系属性*/public final static Properties PIECE_FORMAT = initPieceCorresponding();/*** 垂直于楚河汉界的黑方阵营和红方阵营纵向起止坐标范围X*/public final static byte[][] RANGE_CAMP = new byte[][]{{0, 4}, {5, 9}};/*** 平行于楚河汉界的棋盘宽度(横向)起止范围Y、垂直于楚河汉界的棋盘长度(纵向)起止坐标范围X*/public final static byte[][] RANGE_CHESSBOARD = new byte[][]{{0, 8}, {0, 9}};/*** 红黑双方九宫的列坐标范围Y、黑方九宫的行坐标范围X、红方九宫的行坐标范围X*/public final static byte[][] RANGE_SUDOKU = new byte[][]{{3, 5}, {0, 2}, {7, 9}};/*** 棋子马的位移距离之和限制*/public final static byte LIMIT_STEP_CAVALRY = 3;/*** 棋子象的位移距离之和限制*/public final static byte LIMIT_STEP_PRIME_MINISTER = 4;/*** 初始化棋子标识和棋子名字之间的对应关系属性** @return properties 对应属性的键值对*/protected static Properties initPieceCorresponding() {Properties properties = new Properties();for (int i = 0; i < AMOUNT_PIECE_TYPE; ++i) {properties.setProperty(String.valueOf(PIECE_R[i]), PIECE_NAME[i]);}for (int i = AMOUNT_PIECE_TYPE; i < PIECE_NAME.length; ++i) {properties.setProperty(String.valueOf(PIECE_B[i - AMOUNT_PIECE_TYPE]), PIECE_NAME[i]);}properties.setProperty(String.valueOf(BLOCK), "〇");properties.setProperty(String.valueOf(FLAG_R), "红");properties.setProperty(String.valueOf(FLAG_B), "黑");return properties;}/*** 控制台打印输出棋盘各棋子** @param posPiece 二维数组形式的棋盘棋子位置*/public static void printPiece(short[][] posPiece, short flagPlayer) {String[] numberId = new String[]{"00", "01", "02", "03", "04", "05", "06", "07", "08", "09"};if (FLAG_R == flagPlayer) {System.out.println("-- 零 一 二 三 四 五 六 七 八");for (int i = RANGE_CHESSBOARD[1][0]; i < RANGE_CHESSBOARD[1][1] + 1; ++i) {System.out.print(numberId[i] + " ");for (int j = RANGE_CHESSBOARD[0][0]; j < RANGE_CHESSBOARD[0][1] + 1; ++j) {System.out.print(PIECE_FORMAT.getProperty(String.valueOf(posPiece[i][j])) + " ");}System.out.println();}} else if (FLAG_B == flagPlayer) {System.out.println("-- 八 七 六 五 四 三 二 一 零");for (int i = RANGE_CHESSBOARD[1][1]; i >= RANGE_CHESSBOARD[1][0]; --i) {System.out.print(numberId[i] + " ");for (int j = RANGE_CHESSBOARD[0][1]; j >= RANGE_CHESSBOARD[0][0]; --j) {System.out.print(PIECE_FORMAT.getProperty(String.valueOf(posPiece[i][j])) + " ");}System.out.println();}}}
}

四、主要规则

package chess;import java.util.Arrays;/*** 在该项目中,规定从左上角开始为(0,0),水平向右方向为Y轴正方向,用列或Y表示,垂直向下方向为X轴正方向,用行或X表示** @author THDMI* @version 0.0.1* @date 2022/01/30*/
public class RuleDefined {/*** 当前棋手阵营(红方起手)*/private short flagPlayerCurrent = Const.FLAG_R;/*** 棋盘棋子位置和标识*/public short[][] chessboard = new short[][]{{0x14, 0x15, 0x13, 0x12, 0x11, 0x12, 0x13, 0x15, 0x14},{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},{0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00},{0x17, 0x00, 0x17, 0x00, 0x17, 0x00, 0x17, 0x00, 0x17},{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},{0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07},{0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00},{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},{0x04, 0x05, 0x03, 0x02, 0x01, 0x02, 0x03, 0x05, 0x04}};/*** 返回当前棋手阵营标识** @return 当前阵营标识*/public short getFlagPlayer() {return this.flagPlayerCurrent;}/*** 交换并返回当前阵营标识**/public void exchangeFlagPlayer() {if (Const.FLAG_R == this.flagPlayerCurrent) {this.flagPlayerCurrent = Const.FLAG_B;} else if (Const.FLAG_B == this.flagPlayerCurrent) {this.flagPlayerCurrent = Const.FLAG_R;}}/*** 获取某个阵营的将帅位置,如果提供阵营不存在,则有可能引发数组下标越界异常** @param flagPlayer 阵营标识* @return 该阵营将帅位置的行和列(X、Y)*/public byte[] getPosGeneral(short flagPlayer) {if (Const.FLAG_R == flagPlayer) {for (byte i = Const.RANGE_SUDOKU[1 + 1][0]; i < Const.RANGE_SUDOKU[1 + 1][1] + 1; ++i) {for (byte j = Const.RANGE_SUDOKU[0][0]; j < Const.RANGE_SUDOKU[0][1] + 1; ++j) {if (Const.PIECE_R[0] == chessboard[i][j]) {return new byte[]{i, j};}}}} else if (Const.FLAG_B == flagPlayer) {for (byte i = Const.RANGE_SUDOKU[1][0]; i < Const.RANGE_SUDOKU[1][1] + 1; ++i) {for (byte j = Const.RANGE_SUDOKU[0][0]; j < Const.RANGE_SUDOKU[0][1] + 1; ++j) {if (Const.PIECE_B[0] == chessboard[i][j]) {return new byte[]{i, j};}}}}return null;}/*** 判断当前棋子移动是否可能对对方进行将军** @param posX       当前棋子行坐标* @param posY       当前棋子列坐标* @param flagPlayer 当前棋子所属阵营* @return 是否能够将对方的军(是true、否false)*/public boolean isCheckmate(byte posX, byte posY, short flagPlayer) {// 优先判断两方将帅是否在同一直线且在两者之间没有棋子阻隔byte[] posGeneralR = this.getPosGeneral(Const.FLAG_R);byte[] posGeneralB = this.getPosGeneral(Const.FLAG_B);if (null == posGeneralR || null == posGeneralB) {return true;}if (posGeneralR[1] == posGeneralB[1] && 0 == this.getHinderCount(posGeneralB[0], posGeneralR[0], posGeneralB[1], Const.MOVE_VERTICAL)) {return true;}if (Const.FLAG_R == flagPlayer) {byte[] posGeneralOpposite = this.getPosGeneral(Const.FLAG_B);if (null == posGeneralOpposite) {return true;}return this.isPermitRuleMove(posX, posY, posGeneralOpposite[0], posGeneralOpposite[1], Const.FLAG_R);} else if (Const.FLAG_B == flagPlayer) {byte[] posGeneralOpposite = this.getPosGeneral(Const.FLAG_R);if (null == posGeneralOpposite) {return true;}return this.isPermitRuleMove(posX, posY, posGeneralOpposite[0], posGeneralOpposite[1], Const.FLAG_B);}throw new IndexOutOfBoundsException("该阵营标识不存在,请提供正确的阵营标识。");}/*** 根据选取的棋子和目标位置进行对应的位移操作判断是否符合移动规则** @param posSrcX    己方棋子所在行坐标* @param posSrcY    己方棋子所在列坐标* @param posDstX    目标位置行坐标吧* @param posDstY    目标位置列坐标* @param flagPlayer 当前回合玩家阵营标识* @return 移动棋子是否符合规则(符合true、不符合false)*/public boolean isPermitRuleMove(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY, short flagPlayer) {if (!this.isOverRange(posSrcX, posSrcY, Const.RANGE_CHESSBOARD[1], Const.RANGE_CHESSBOARD[0])) {// 如果初始位置选取超出了棋盘区域则不可移动return false;} else if (!this.isOverRange(posDstX, posDstY, Const.RANGE_CHESSBOARD[1], Const.RANGE_CHESSBOARD[0])) {// 如果目标位置选取超出了棋盘区域则不可移动return false;}if (!this.isSelfCamp(flagPlayer, posSrcX, posSrcY) || this.isSelfCamp(flagPlayer, posDstX, posDstY)) {// 如果源起始棋子不是己方,或者目标位置棋子还是己方的,均不可移动return false;}switch (chessboard[posSrcX][posSrcY]) {case 0x01:case 0x11:return this.ruleGeneral(posSrcX, posSrcY, posDstX, posDstY, flagPlayer);case 0x02:case 0x12:return this.ruleLifeguard(posSrcX, posSrcY, posDstX, posDstY, flagPlayer);case 0x03:case 0x13:return this.rulePrimeMinister(posSrcX, posSrcY, posDstX, posDstY, flagPlayer);case 0x04:case 0x14:return this.ruleChariot(posSrcX, posSrcY, posDstX, posDstY);case 0x05:case 0x15:return this.ruleCavalry(posSrcX, posSrcY, posDstX, posDstY);case 0x06:case 0x16:return this.ruleBattery(posSrcX, posSrcY, posDstX, posDstY);case 0x07:case 0x17:return this.ruleInfantry(posSrcX, posSrcY, posDstX, posDstY, flagPlayer);default:}return false;}/*** 移动棋子至目标位置** @param posSrcX 当前位置行坐标X* @param posSrcY 当前位置列坐标Y* @param posDstX 目标位置行坐标X* @param posDstY 目标位置列坐标Y*/public void moveToPos(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY) {chessboard[posDstX][posDstY] = chessboard[posSrcX][posSrcY];chessboard[posSrcX][posSrcY] = Const.BLOCK;}/*** 计算移动的行坐标和列坐标距离并返回两者位移距离的绝对值** @param posSrcX 源位置行坐标(第X行)* @param posSrcY 源位置列坐标(第Y列)* @param posDstX 目标位置行坐标(第X行)* @param posDstY 目标位置列坐标(第Y列)* @return int[] 存放移动距离的行坐标和列坐标的绝对值的二维数组 {x, y}*/public byte[] calStepAbs(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY) {return new byte[]{(byte) Math.abs(posDstX - posSrcX), (byte) Math.abs(posDstY - posSrcY)};}/*** 计算移动的行坐标和列坐标距离并返回两者位移距离** @param posSrcX 源位置行坐标(第X行)* @param posSrcY 源位置列坐标(第Y列)* @param posDstX 目标位置行坐标(第X行)* @param posDstY 目标位置列坐标(第Y列)* @return int[] 存放移动距离的行坐标和列坐标的二维数组 {x, y}*/public byte[] calStep(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY) {return new byte[]{(byte) (posDstX - posSrcX), (byte) (posDstY - posSrcY)};}/*** 检测传入目标位置是否在一定范围内(含边缘)** @param posX      传入位置X* @param posY      传入位置Y* @param posLimitX 位置限制范围检测(最小X,最大X)* @param posLimitY 位置限制范围检测(最小Y,最大Y)* @return 目标是否在范围内(是true、否false)*/public boolean isOverRange(byte posX, byte posY, byte[] posLimitX, byte[] posLimitY) {return (posLimitX[0] <= posX && posX <= posLimitX[1] && posLimitY[0] <= posY && posY <= posLimitY[1]);}/*** 判断当前所指位置的棋子是否属于当前操作方阵营** @param flagPlayer 当前操作方阵营* @param posPieceX  当前位置棋子的X坐标* @param posPieceY  当前位置棋子的Y坐标* @return 棋子是否属于当前操作方(是:true、否:false)*/public boolean isSelfCamp(short flagPlayer, byte posPieceX, byte posPieceY) {if (Const.FLAG_R == flagPlayer) {return Arrays.binarySearch(Const.PIECE_R, this.chessboard[posPieceX][posPieceY]) >= 0;} else if (Const.FLAG_B == flagPlayer) {return Arrays.binarySearch(Const.PIECE_B, this.chessboard[posPieceX][posPieceY]) >= 0;}return false;}/*** 统计所设定直线路径上棋子数目必须保证输入的参数`posMin`和`posMax`关系是一小一大,否则抛出异常* 假设想计算 (4, 3) -> (0, 3) 路径上存在棋子数目:* 传入 posMin=0, posMax=4, posFix=3, dir=Const.MOVE_VERTICAL (false) 即可计算** @param posMin 可变坐标较小的值* @param posMax 可变坐标较大的值* @param posFix 固定坐标的值* @param dir    方向,参考Const.java里的相关参数,true是水平方向,false是垂直方向* @return 路径上存在的棋子的数目*/public byte getHinderCount(byte posMin, byte posMax, byte posFix, boolean dir) {byte countHinder = 0;for (int i = posMin + 1; i < posMax; ++i) {if (dir && Const.BLOCK != chessboard[posFix][i]) {// 水平移动++countHinder;} else if (!dir && Const.BLOCK != chessboard[i][posFix]) {// 垂直移动++countHinder;}}return countHinder;}/*** 将帅移动规则** @param posSrcX    源位置行坐标(第X行)* @param posSrcY    源位置列坐标(第Y列)* @param posDstX    目标位置行坐标(第X行)* @param posDstY    目标位置列坐标(第Y列)* @param flagPlayer 当前源位置棋子玩家阵营标识* @return 是否符合该棋子移动规则(是true、否false)*/public boolean ruleGeneral(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY, short flagPlayer) {return false;}/*** 士移动规则** @param posSrcX    源位置行坐标(第X行)* @param posSrcY    源位置列坐标(第Y列)* @param posDstX    目标位置行坐标(第X行)* @param posDstY    目标位置列坐标(第Y列)* @param flagPlayer 当前源位置棋子玩家阵营标识* @return 是否符合该棋子移动规则(是true、否false)*/public boolean ruleLifeguard(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY, short flagPlayer) {return false;}/*** 相移动规则** @param posSrcX    源位置行坐标(第X行)* @param posSrcY    源位置列坐标(第Y列)* @param posDstX    目标位置行坐标(第X行)* @param posDstY    目标位置列坐标(第Y列)* @param flagPlayer 当前源位置棋子玩家阵营标识* @return 是否符合该棋子移动规则(是true、否false)*/public boolean rulePrimeMinister(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY, short flagPlayer) {return false;}/*** 车移动规则** @param posSrcX 源位置行坐标(第X行)* @param posSrcY 源位置列坐标(第Y列)* @param posDstX 目标位置行坐标(第X行)* @param posDstY 目标位置列坐标(第Y列)* @return 是否符合该棋子移动规则(是true、否false)*/public boolean ruleChariot(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY) {return false;}/*** 马移动规则** @param posSrcX 源位置行坐标(第X行)* @param posSrcY 源位置列坐标(第Y列)* @param posDstX 目标位置行坐标(第X行)* @param posDstY 目标位置列坐标(第Y列)* @return 是否符合该棋子移动规则(是true、否false)*/public boolean ruleCavalry(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY) {return false;}/*** 炮移动规则** @param posSrcX 源位置行坐标(第X行)* @param posSrcY 源位置列坐标(第Y列)* @param posDstX 目标位置行坐标(第X行)* @param posDstY 目标位置列坐标(第Y列)* @return 是否符合该棋子移动规则(是true、否false)*/public boolean ruleBattery(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY) {return false;}/*** 兵移动规则** @param posSrcX    源位置行坐标(第X行)* @param posSrcY    源位置列坐标(第Y列)* @param posDstX    目标位置行坐标(第X行)* @param posDstY    目标位置列坐标(第Y列)* @param flagPlayer 当前源位置棋子玩家阵营标识* @return 是否符合该棋子移动规则(是true、否false)*/public boolean ruleInfantry(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY, short flagPlayer) {return false;}
}

五、棋子移动规则

package chess;/*** 在该项目中,规定从左上角开始为(0,0),水平向右方向为Y轴正方向,用列或Y表示,垂直向下方向为X轴正方向,用行或X表示** @author THDMI* @version 0.0.1* @date 2022/01/28*/
public class PieceRule extends RuleDefined {@Overridepublic boolean ruleGeneral(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY, short flagPlayer) {byte[] tmpStepAbs = this.calStepAbs(posSrcX, posSrcY, posDstX, posDstY);if (1 != tmpStepAbs[0] + tmpStepAbs[1]) {return false;}return Const.FLAG_R == flagPlayer && this.isOverRange(posDstX, posDstY, Const.RANGE_SUDOKU[2], Const.RANGE_SUDOKU[0]) ||Const.FLAG_B == flagPlayer && this.isOverRange(posDstX, posDstY, Const.RANGE_SUDOKU[1], Const.RANGE_SUDOKU[0]);}@Overridepublic boolean ruleLifeguard(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY, short flagPlayer) {byte[] tmpStepAbs = this.calStepAbs(posSrcX, posSrcY, posDstX, posDstY);if (tmpStepAbs[0] != tmpStepAbs[1]) {return false;}return Const.FLAG_R == flagPlayer && this.isOverRange(posDstX, posDstY, Const.RANGE_SUDOKU[2], Const.RANGE_SUDOKU[0]) ||Const.FLAG_B == flagPlayer && this.isOverRange(posDstX, posDstY, Const.RANGE_SUDOKU[1], Const.RANGE_SUDOKU[0]);}@Overridepublic boolean rulePrimeMinister(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY, short flagPlayer) {byte[] tmpStepAbs = this.calStepAbs(posSrcX, posSrcY, posDstX, posDstY);if (Const.LIMIT_STEP_PRIME_MINISTER == tmpStepAbs[0] + tmpStepAbs[1] && tmpStepAbs[0] == tmpStepAbs[1]) {// 步长之和绝对值必须是4,且步长绝对值必须相等if (Const.BLOCK != chessboard[(posSrcX + posDstX) / (1 + 1)][(posSrcY + posDstY) / (1 + 1)]) {// 判断到目标点的中间点是否有棋子阻碍return false;}return Const.FLAG_R == flagPlayer && Const.RANGE_CAMP[0][1] < posDstX||Const.FLAG_B == flagPlayer && posDstX < Const.RANGE_CAMP[1][0];}return false;}@Overridepublic boolean ruleChariot(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY) {if (posDstX != posSrcX && posDstY == posSrcY) {// 判断是否为垂直移动if (posSrcX < posDstX) {return 0 == getHinderCount(posSrcX, posDstX, posSrcY, Const.MOVE_VERTICAL);} else {return 0 == getHinderCount(posDstX, posSrcX, posSrcY, Const.MOVE_VERTICAL);}} else if (posDstX == posSrcX && posDstY != posSrcY) {// 判断是否为水平移动if (posSrcY < posDstY) {return 0 == getHinderCount(posSrcY, posDstY, posSrcX, Const.MOVE_HORIZONTAL);} else {return 0 == getHinderCount(posDstY, posSrcY, posSrcX, Const.MOVE_HORIZONTAL);}}return false;}@Overridepublic boolean ruleCavalry(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY) {byte[] tmpStep = this.calStep(posSrcX, posSrcY, posDstX, posDstY);byte[] tmpStepAbs = new byte[]{(byte) Math.abs(tmpStep[0]), (byte) Math.abs(tmpStep[1])};if (Const.LIMIT_STEP_CAVALRY != tmpStepAbs[0] + tmpStepAbs[1] || 0 == tmpStepAbs[0] || 0 == tmpStepAbs[1]) {return false;}// 判断棋子目标位置是水平运动还是垂直运动if (tmpStepAbs[0] < tmpStepAbs[1]) {// 由于calStep()函数是利用目标位置和源位置作差,故大于零的情况说明目标位置在右if (0 < tmpStep[1]) {// 判断棋子原位置右边是否为空return Const.BLOCK == chessboard[posSrcX][posSrcY + 1];} else {return Const.BLOCK == chessboard[posSrcX][posSrcY - 1];}} else if (tmpStepAbs[1] < tmpStepAbs[0]) {// 由于calStep()函数是利用目标位置和源位置作差,故大于零的情况说明目标位置在下方if (0 < tmpStep[0]) {return Const.BLOCK == chessboard[posSrcX + 1][posSrcY];} else {return Const.BLOCK == chessboard[posSrcX - 1][posSrcY];}}return false;}@Overridepublic boolean ruleBattery(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY) {byte hinderCount = -1;if (posDstX != posSrcX && posDstY == posSrcY) {// 判断是否为垂直移动if (posSrcX < posDstX) {hinderCount = getHinderCount(posSrcX, posDstX, posSrcY, Const.MOVE_VERTICAL);} else {hinderCount = getHinderCount(posDstX, posSrcX, posSrcY, Const.MOVE_VERTICAL);}} else if (posDstX == posSrcX && posDstY != posSrcY) {// 判断是否为水平移动if (posSrcY < posDstY) {hinderCount = getHinderCount(posSrcY, posDstY, posSrcX, Const.MOVE_HORIZONTAL);} else {hinderCount = getHinderCount(posDstY, posSrcY, posSrcX, Const.MOVE_HORIZONTAL);}}return 1 == hinderCount && Const.BLOCK != chessboard[posDstX][posDstY] ||0 == hinderCount && Const.BLOCK == chessboard[posDstX][posDstY];}@Overridepublic boolean ruleInfantry(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY, short flagPlayer) {byte[] tmpStepAbs = this.calStepAbs(posSrcX, posSrcY, posDstX, posDstY);// 步数和必须等于1if (1 != tmpStepAbs[0] + tmpStepAbs[1]) {return false;}// 如果只是垂直移动是允许的,但是只许进不许退if (0 == tmpStepAbs[1]) {return Const.FLAG_R == flagPlayer && posDstX < posSrcX ||Const.FLAG_B == flagPlayer && posSrcX < posDstX;}// 如果想要水平移动必须跨过楚河汉界return Const.FLAG_R == flagPlayer && posSrcX < Const.RANGE_CAMP[1][0] ||Const.FLAG_B == flagPlayer && Const.RANGE_CAMP[0][1] < posSrcX;}}

后记

没啥好说的,注释都写了大部分我想写的内容,便于大家理解,其他不想写的注解我也没写(懒),这个项目其实从2020年8月5日就写了,当然,当时也写的差不多啦,但是代码冗余、耦合性高、乱成一坨xxx,最后因为我去做别的事情,就再也没理过,但是终究是没完成,于是这几天就顺带完成下(当然是重新写了)。

这个工程主要是方便理解思路,可以很便捷的移植到其他编程语言中,不过换句话来说,有谁会没事去理解这思路呢,网上现成的代码包括可视化界面音效一大堆,直接拿来用就好,所以说到底,只是我想做一个属于我自己的项目而已,当然之前还有许多项目没完成,后续可能有机会也会逐个完善。

正值新春佳节,祝大家新春快乐!虎年大吉!


Bye

Java-命令行版中国象棋相关推荐

  1. java中国象棋棋盘放置棋子,JAVA简易文字版中国象棋

    大二时制作的JAVA简易文字版中国象棋,现在放出,希望大家喜欢! // Java core packages import java.awt.*; import java.awt.event.*; / ...

  2. Java实现单人版中国象棋小游戏的实现,具有时间设置,认输,悔棋,求和,自动判断输赢功能。

    说明:这个小游戏是小马猿用了一个多月,自己一边学习一边码出来的,接近3000行的代码,所有算法都是自己不断摸索得到的,作为小马猿第一个游戏成品,现在分享给各位小伙伴.如果伙伴们在使用这份代码发现有什么 ...

  3. java编辑2048小游戏_Java 制作命令行版 2048小游戏

    Java 命令行版 2048小游戏(2020年8月14日) 制作背景 大二即将开学,从头开始学习了一个多月的java,对二维数组的操作稍微熟悉了一些.于是想做一个简单的2048来试一试. 众所周知,2 ...

  4. java斗地主发牌教学,命令行版的斗地主你玩过没?

    本文适合有 Java 基础知识的人群,跟着本文可使用和快速搭建命令行斗地主项目. 本文作者:HelloGitHub-秦人 HelloGitHub 推出的<讲解开源项目>系列,今天给大家带来 ...

  5. Java 命令行编译项目

    如果是用Exlipse, 第三方的包可以放在eclipse文件夹的jre包的lib文件夹中! (初学者的一些总结-高手们勿喷哈-) 原因: 以前一直用Eclispe编程环境运行Java.非常舒服,就像 ...

  6. jdk解压版_命令行版的斗地主你玩过没?

    相信大家都玩过斗地主游戏,或在现实中斗地主或在电脑和手机上斗地主,但你想过用命令行界面进行斗地主吗? 先来张图体验一下: 是不是觉得挺有意思,下面就带大家一起玩一下吧~部署命令行版斗地主 1 环境准备 ...

  7. python刷网易云_牛逼了!用Python开发的命令行版网易云音乐,Github获8300颗星!...

    大家好,我是程序员G哥 最近在逛Github发现了一个非常有趣的库musicbox,是用纯Python打造的,收获了8300颗星.Python语言简单易学,好玩有趣,身边越来越多的小伙伴都开始学习Py ...

  8. 康威生命游戏java_Java Python 康威生命游戏 - 命令行版

    Java & Python 康威生命游戏 - 命令行版(2020年7月23日) 制作背景 高二的时候看霍金的<大设计>最后几页的时候看到里面提到了康威生命游戏,介绍了它的规则,感觉 ...

  9. csvtk:高效命令行版极简dplyr

    写在前面 什么时候写 csvtk 呀,csvtk 也借鉴了些 datamash 的东西. 之前写 datamash 的使用教程 linux 极简统计分析工具 datamash 必看教程,收到了一位读者 ...

最新文章

  1. 禅道826版本SQL注入,登录绕过以及禅道826后台GetShell的小技巧
  2. python中的reduce、lambda函数
  3. 利用小波融合对由聚焦失败导致的图像模糊进行修复
  4. 【面向工业界】京东NLP落地应用实战
  5. java基础:12.7 对象流 ObjectInputStream、ObjectOutputStream
  6. UVA - 572 Oil Deposits
  7. 2020 大厂研发岗薪酬排名出炉,看完我真的拖后腿了。。。
  8. 毕设日志——tensorboardX无法连接的问题
  9. java 并发多线程显式锁概念简介 什么是显式锁 多线程下篇(一)
  10. 腾讯app看视频看不了显示服务器没有返回,腾讯视频TV版暂停服务怎么办?教你方法!...
  11. 北漂四年,25K,程序员,我依然单身!
  12. VsCode超实用插件推荐,让你的开发效率火力全开
  13. nginx 过滤某个url请求
  14. mp2格式怎么转换mp3?
  15. 微软TTS服务器,微软TTS,Neospeech TTS 简单使用
  16. 【git】统计每个人的代码行数
  17. 互联网因特网计算机网络的区别,因特网和互联网的区别?
  18. html图片文字环绕
  19. 气象数据的可视化展现形式
  20. 【005】C++数据类型之实型(浮点数)、有符号数以及无符号数

热门文章

  1. 如何快速上手小程序开发(史上最全)
  2. group by 和where可以一起使用吗
  3. 高通发布了全球最领先的5G基带芯片,不过华为将很快反超
  4. 解决 Vue3.0 globalThis is note defined
  5. 同一局域网下多台电脑共享文件夹
  6. 编程篇 - esp 8266物联网开发板 - 给板子编写并且烧录程序, 整点有意思的效果
  7. libcurl linux 静态链接库_Linux ubuntu OpenSSL + curl 静态库编译及使用
  8. QDateTime类
  9. matlab约当消去法,Gauss-Jordan 高斯约当消去法
  10. 高等代数 线性映射(第9章)4 约当标准型与相抵标准形