说明
- 博客日期:2022年2月5日13点14分星期六
- 无界面版中国象棋,是基于
JDK 17
的版本,虽然说是无界面,但其实还是能看到命令行界面
的; - 本工程尽可能采用模块化编程,并没有一个完全体,如有需要可以自行组装,默认组装了一个简单功能;
- 已有的功能包括棋子移动规则、将军规则、移动棋子等,计时、悔棋、死棋等功能按需自行完善;
- 本工程主要供参考棋子移动规则实现思路,后期可能移植到开发板上打发时间(摸鱼)。
- 项目仓库: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: 4
Kotlin: 213-1.5.10-release-949-IJ6461.79
Java Development Kit 17
一、效果
-
先手是红方,当红方执棋完毕后,交换显示为黑方,只需要输入起始坐标和目标坐标即可,注意,行坐标优先。
-
棋子移动不符合移动规范触发规则限制。
-
将军!当棋子监测到可以吃掉对方将帅即认为被将军。
-
如果被将军的一方还不肯阻挡被将,那么游戏可能结束,如果按下
y
或Y
则可以重开游戏,因为测试是在一个循环内。
二、测试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 {
@Override
public 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]);
}
@Override
public 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]);
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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];
}
@Override
public boolean ruleInfantry(byte posSrcX, byte posSrcY, byte posDstX, byte posDstY, short flagPlayer) {
byte[] tmpStepAbs = this.calStepAbs(posSrcX, posSrcY, posDstX, posDstY);
// 步数和必须等于1
if (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