前言
中国象棋 是起源于中国的一种棋,属于二人对抗性游戏的一种,在中国有着悠久的历史。由于用具简单,趣味性强,成为流行极为广泛的棋艺活动。
中国象棋使用方形格状棋盘,圆形棋子共有32个,红黑二色各有16个棋子,摆放和活动在交叉点上。双方交替行棋,先把对方的将(帅)“将死”的一方获胜。
中国象棋是一款具有浓郁中国特色的益智游戏,新增的联网对战,趣味多多,聚会可以约小朋友一起来挑战。精彩的对弈让你感受中国象棋的博大精深。
《中国象棋》游戏是用 java 语言实现,采用了 swing 技术进行了界面化处理,设计思路用了面向对象思想。, 人机对弈基于极大极小值搜索算法。
主要需求
按照中国象棋的规则,实现红黑棋对战,要有AI对手,可以玩家跟AI的对弈,也可以两个玩家自己玩。
主要设计
1、寻找棋盘界面和对应的棋子图片,程序设计棋盘界面和功能菜单
2、设计不同的棋子的移动逻辑
3、棋子移动时,要有音效
4、设计对手 AI 的逻辑算法,这里运用了极大极小值搜索算法,设置不同的搜索深度AI(智能不同)
5、对局开始前,双方棋子在棋盘上的摆法。 6、对局时,由执红棋的一方先走,双方轮流走一步。 7、轮到走棋的一方,将某个棋子从一个交叉点走到另一个交叉点,或者吃掉对方的棋子而占领其交叉点,都算走了一着。 8、双方各走一着,称为一个回合。 9、走一着棋时,如果己方棋子能够走到的位置有对方棋子存在,就可以把对方棋子吃掉而占领那个位置。 10、一方的棋子攻击对方的帅(将),并在下一着要把它吃掉,称为“照将”,或简称“将”。“照将”不必声明。被“照将”的一方必须立即“应将”,即用自己的着法去化解被“将”的状态。如果被“照将”而无法“应将”,就算被“将死”。
11、特别设计了人机对弈,人人对弈,还有AI对AI对弈
功能截图
游戏开始
游戏菜单设置
移动效果
代码实现
棋盘面板设计
public class BoardPanel extends JPanel implements LambdaMouseListener { | |
/** | |
* 用于标记棋盘走棋痕迹 | |
*/ private final transient TraceMarker traceMarker; | |
/** | |
* 当前走棋开始坐标位置对应棋子 | |
*/ private transient ChessPiece curFromPiece; | |
/** | |
* 场景 | |
*/ private transient Situation situation; | |
/** | |
* Create the panel. | |
*/ public BoardPanel() { | |
setBorder(new EmptyBorder(, 5, 5, 5)); | |
setLayout(null); | |
// 初始化标记符 | |
traceMarker = new TraceMarker(BoardPanel.this); | |
// 添加鼠标事件 | |
addMouseListener(this); | |
} | |
/** | |
* 更新标记 | |
*/ public void updateMark(Place from, Place to) { | |
// 更新标记 | |
curFromPiece = null; | |
// 更改标记 | |
traceMarker.endedStep(from, to); | |
} | |
/** | |
* 初始化所有标记 | |
*/ public void initMark() { | |
traceMarker.initMarker(); | |
} | |
/** | |
* 添加棋子 | |
*/ public void init(Situation situation) { | |
this.situation = situation; | |
// 移除所有组件 | |
this.removeAll(); | |
// 添加棋子 | |
situation.getPieceList().forEach(it -> add(it.getComp())); | |
situation.getSituationRecord().getEatenPieceList().forEach(it -> add(it.getComp())); | |
// 初始化标记符 | |
traceMarker.initMarker(); | |
repaint (); | |
} | |
/** | |
* @param e 鼠标按压事件对象 | |
*/ | |
public void mouseReleased(MouseEvent e) { | |
// 位置 | |
Place pointerPlace = ChessDefined.convertLocationToPlace(e.getPoint()); | |
if (pointerPlace == null) { | |
return; | |
} | |
if (situation.winner() != null) { | |
log.warn("已经存在胜利者: {}, 无法走棋", situation.winner()); | |
return; | |
} | |
// 当前走棋方 | |
Part pointerPart = situation.getNextPart(); | |
// 当前焦点棋子 | |
ChessPiece pointerPiece = situation.getChessPiece(pointerPlace); | |
// 通过当前方和当前位置判断是否可以走棋 | |
// step: form | |
if (curFromPiece == null) { | |
// 当前焦点位置有棋子且是本方棋子 | |
if (pointerPiece != null && pointerPiece.piece.part == pointerPart) { | |
// 本方棋子, 同时是from指向 | |
curFromPiece = pointerPiece; | |
traceMarker.setMarkFromPlace(pointerPlace); | |
// 获取toList | |
MyList<Place> list = curFromPiece.piece.role.find(new AnalysisBean(situation.generatePieces()), pointerPart, pointerPlace); | |
traceMarker.showMarkPlace(list); | |
ChessAudio.CLICK_FROM.play(); | |
log.info("true -> 当前焦点位置有棋子且是本方棋子"); | |
final ListPool listPool = ListPool.localPool(); | |
listPool.addListToPool(list); | |
return; | |
} | |
log.warn("warning -> from 焦点指示错误"); | |
return; | |
} | |
if (pointerPlace.equals(curFromPiece.getPlace())) { | |
log.warn("false -> from == to"); | |
return; | |
} | |
// 当前焦点位置有棋子且是本方棋子 | |
if (pointerPiece != null && pointerPiece.piece.part == pointerPart) { | |
assert curFromPiece.piece.part == pointerPart : "当前焦点位置有棋子且是本方棋子 之前指向了对方棋子"; | |
// 更新 curFromPiece | |
curFromPiece = pointerPiece; | |
traceMarker.setMarkFromPlace(pointerPlace); | |
MyList<Place> list = curFromPiece.piece.role.find(new AnalysisBean(situation.generatePieces()), pointerPart, pointerPlace); | |
traceMarker.showMarkPlace(list); | |
ChessAudio.CLICK_FROM.play(); | |
log.info("true -> 更新 curFromPiece"); | |
ListPool.localPool().addListToPool(list); | |
return; | |
} | |
final StepBean stepBean = StepBean.of(curFromPiece.getPlace(), pointerPlace); | |
// 如果不符合规则则直接返回 | |
final Piece[][] pieces = situation.generatePieces(); | |
if (!curFromPiece.piece.role.rule.check(pieces, pointerPart, stepBean.from, stepBean.to)) { | |
// 如果当前指向棋子是本方棋子 | |
log.warn("不符合走棋规则"); | |
return; | |
} | |
// 如果达成长拦或者长捉, 则返回 | |
final StepBean forbidStepBean = situation.getForbidStepBean(); | |
if (forbidStepBean != null && forbidStepBean.from == stepBean.from && forbidStepBean.to == stepBean.to) { | |
ChessAudio.MAN_MOV_ERROR.play(); | |
log.warn("长拦或长捉"); | |
return; | |
} | |
AnalysisBean analysisBean = new AnalysisBean(pieces); | |
// 如果走棋后, 导致两个 BOSS 对面, 则返回 | |
if (!analysisBean.isBossFFAfterStep(curFromPiece.piece, stepBean.from, stepBean.to)) { | |
ChessAudio.MAN_MOV_ERROR.play(); | |
log.warn("BOSS面对面"); | |
return; | |
} | |
/* 模拟走一步棋, 之后再计算对方再走一步是否能够吃掉本方的 boss */ if (analysisBean.simulateOneStep(stepBean, bean -> bean.canEatBossAfterOneAiStep(Part.getOpposite(pointerPart)))) { | |
ChessAudio.MAN_MOV_ERROR.play(); | |
log.warn("BOSS 危险"); | |
if (!Application.config().isActiveWhenBeCheck()) { | |
return; | |
} | |
} | |
// 当前棋子无棋子或者为对方棋子, 且符合规则, 可以走棋 | |
Object[] objects = new Object[]{stepBean.from, stepBean.to, PlayerType.PEOPLE}; | |
final boolean sendSuccess = Application.context().getCommandExecutor().sendCommandWhenNotRun(CommandExecutor.CommandType.LocationPiece, objects); | |
if (!sendSuccess) { | |
log.warn("命令未发送成功: {} ==> {}", CommandExecutor.CommandType.LocationPiece, Arrays.toString(objects)); | |
} | |
} | |
public void paintComponent(Graphics g) { | |
super.paintComponent(g); | |
Image img = ChessImage.CHESS_BOARD.getImage(); | |
int imgWidth = img.getWidth(this); | |
int imgHeight = img.getHeight(this);// 获得图片的宽度与高度 | |
int fWidth = getWidth(); | |
int fHeight = getHeight();// 获得窗口的宽度与高度 | |
int x = (fWidth - imgWidth) /; | |
int y = (fHeight - imgHeight) /; | |
// 576 514 567 | |
log.debug(String.format("%s,%s,%s,%s,%s,%s", imgWidth, imgHeight, fWidth, fHeight, x, y)); | |
g.drawImage(img,, 0, null); | |
} | |
} |
命令执行器, 用于处理走棋中的命令
public class CommandExecutor { | |
/** | |
* 异步调用 线程, 来处理走棋命令 | |
*/ private final CtrlLoopThreadComp ctrlLoopThreadComp; | |
private final BoardPanel boardPanel; | |
/** | |
* 是否持续运行标记 | |
*/ private volatile boolean sustain; | |
public CommandExecutor(BoardPanel boardPanel) { | |
this.boardPanel = boardPanel; | |
this.ctrlLoopThreadComp = CtrlLoopThreadComp.ofRunnable(this::loop) | |
.setName("CommandExecutor") | |
.catchFun(CtrlLoopThreadComp.CATCH_FUNCTION_CONTINUE); | |
} | |
/** | |
* 下一步骤命令 | |
*/ private CommandType nextCommand; | |
/** | |
* 下一步骤命令的参数 | |
*/ private Object nextParamObj; | |
private volatile boolean isRun; | |
/** | |
* @param commandType 命令类型 | |
*/ public void sendCommand( CommandType commandType) { | |
sendCommand(commandType, null); | |
} | |
/** | |
* @param commandType 命令类型 | |
* @param paramObj 命令参数 | |
*/ public synchronized void sendCommand( CommandType commandType, Object paramObj) { | |
this.nextCommand = commandType; | |
this.nextParamObj = paramObj; | |
sustain = false; | |
this.ctrlLoopThreadComp.startOrWake(); | |
} | |
/** | |
* 只有在 线程 没有运行的情况下, 才能添加成功 | |
* | |
* @param commandType 命令类型 | |
* @param paramObj 命令参数 | |
* @return 是否添加成功 | |
*/ public synchronized boolean sendCommandWhenNotRun( CommandType commandType, Object paramObj) { | |
if (isRun) { | |
return false; | |
} | |
sendCommand(commandType, paramObj); | |
return true; | |
} | |
private void loop() { | |
final CommandType command; | |
final Object paramObj; | |
synchronized (this) { | |
command = this.nextCommand; | |
paramObj = this.nextParamObj; | |
this.nextCommand = null; | |
this.nextParamObj = null; | |
} | |
if (command != null) { | |
isRun = true; | |
try { | |
log.debug("处理事件[{}] start", command.getLabel()); | |
consumerCommand(command, paramObj); | |
log.debug("处理事件[{}] end ", command.getLabel()); | |
} catch ( Exception e) { | |
log.error("执行命令[{}]发生异常", command.getLabel(), e); | |
new Thread(() -> JOptionPane.showMessageDialog(boardPanel, e.getMessage(), e.toString(), JOptionPane.ERROR_MESSAGE)).start(); | |
} | |
} else { | |
this.ctrlLoopThreadComp.pause(); | |
isRun = false; | |
} | |
} | |
/** | |
* 运行 | |
*/ private void consumerCommand(final CommandType commandType, Object paramObj) { | |
switch (commandType) { | |
case SuspendCallBackOrAiRun: | |
break; | |
case CallBackOneTime: | |
Application.context().rollbackOneStep(); | |
break; | |
case AiRunOneTime: | |
if (Application.context().aiRunOneTime() != null) { | |
log.debug("已经决出胜方!"); | |
} | |
break; | |
case SustainCallBack: | |
sustain = true; | |
while (sustain) { | |
if (!Application.context().rollbackOneStep()) { | |
sustain = false; | |
break; | |
} | |
Throws .con(Application.config().getComIntervalTime(), Thread::sleep).logThrowable(); | |
} | |
break; | |
case SustainAiRun: | |
sustain = true; | |
while (sustain) { | |
if (Application.context().aiRunOneTime() != null) { | |
log.debug("已经决出胜方, AI执行暂停!"); | |
sustain = false; | |
break; | |
} | |
Throws.con(Application.config().getComIntervalTime(), Thread::sleep).logThrowable(); | |
} | |
break; | |
case SustainAiRunIfNextIsAi: | |
sustain = true; | |
while (sustain) { | |
// 如果下一步棋手不是 AI, 则暂停 | |
if (!PlayerType.COM.equals(Application.config().getPlayerType(Application.context().getSituation().getNextPart()))) { | |
sustain = false; | |
log.debug("下一步棋手不是 AI, 暂停!"); | |
} else if (Application.context().aiRunOneTime() != null) { | |
log.debug("已经决出胜方, AI执行暂停!"); | |
sustain = false; | |
} else { | |
Throws.con(Application.config().getComIntervalTime(), Thread::sleep).logThrowable(); | |
} | |
} | |
break; | |
case LocationPiece: | |
final Object[] params = (Object[]) paramObj; | |
Place from = (Place) params[]; | |
Place to = (Place) params[]; | |
PlayerType type = (PlayerType) params[]; | |
Application.context().locatePiece(from, to, type); | |
sendCommand(CommandExecutor.CommandType.SustainAiRunIfNextIsAi); | |
break; | |
default: | |
throw new ShouldNotHappenException("未处理的命令: " + commandType); | |
} | |
} | |
/** | |
* 命令支持枚举(以下命令应当使用同一个线程运行, 一个事件结束之后, 另一个事件才能开始运行.) | |
*/ | |
public enum CommandType { | |
SuspendCallBackOrAiRun("停止撤销|AI计算"), | |
CallBackOneTime("撤销一步"), | |
SustainCallBack("持续撤销"), | |
AiRunOneTime("AI计算一步"), | |
SustainAiRun("AI持续运行"), | |
SustainAiRunIfNextIsAi("COM角色运行"), | |
LocationPiece("ui落子命令"); | |
private final String label; | |
CommandType(String label) { | |
this.label = label; | |
} | |
} | |
} |
核心算法
public class AlphaBeta { | |
private static final int MAX =_000_000; | |
/** | |
* 这里要保证 Min + Max =, 哪怕是微不足道的差距都可能导致发生错误 | |
*/ private static final int MIN = -MAX; | |
/** | |
* 根据棋子数量, 动态调整搜索深度 | |
* | |
* @param pieceNum 棋子数量 | |
* @return 调整搜索深度差值 | |
*/ public static int searchDeepSuit(final int pieceNum) { | |
// 根据棋子数量, 动态调整搜索深度 | |
if (pieceNum >) { | |
return -; | |
} else if (pieceNum <=) { | |
return; | |
} else if (pieceNum <=) { | |
return; | |
} | |
return; | |
} | |
/** | |
* 生成待选的列表,就是可以下子的空位, 如果 deep > 则对搜索结果进行排序. | |
* | |
* @param analysisBean 棋盘分析对象 | |
* @param curPart 当前走棋方 | |
* @param deep 搜索深度 | |
* @return 可以下子的空位集合 | |
*/ private static MyList<StepBean> geneNestStepPlaces(final AnalysisBean analysisBean, final Part curPart, final int deep) { | |
final Piece[][] pieces = analysisBean.pieces; | |
// 是否杀棋 | |
MyList<StepBean> stepBeanList = ListPool.localPool().getAStepBeanList(); | |
for (int x =; x < ChessDefined.RANGE_X; x++) { | |
for (int y =; y < ChessDefined.RANGE_Y; y++) { | |
final Piece fromPiece = pieces[x][y]; | |
if (fromPiece != null && fromPiece.part == curPart) { | |
final Place from = Place.of(x, y); | |
// TO DO 考虑下此处添加至集合的做法 在计算时 是否有优化空间. | |
final MyList<Place> list = fromPiece.role.find(analysisBean, curPart, from); | |
if (list.isEmpty()) { | |
ListPool.localPool().addListToPool(list); | |
continue; | |
} | |
final Object[] elementData = list.eleTemplateDate(); | |
for (int i =, len = list.size(); i < len; i++) { | |
stepBeanList.add(StepBean.of(from, (Place) elementData[i])); | |
} | |
ListPool.localPool().addListToPool(list); | |
} | |
} | |
} | |
// 是否排序, 如果搜索深度大于, 则对结果进行排序 | |
// 排序后的结果, 进入极大极小值搜索算法时, 容易被剪枝. | |
if (deep >) { | |
orderStep(analysisBean, stepBeanList, curPart); | |
} | |
return stepBeanList; | |
} | |
/** | |
* 对 空位列表 进行排序, 排序后的空位列表, 进入极大极小值搜索算法时, 容易被剪枝. | |
* | |
* @param analysisBean 棋盘分析对象 | |
* @param stepBeanList 可以下子的空位列表 | |
* @param curPart 当前走棋方 | |
*/ private static void orderStep(final AnalysisBean analysisBean, final MyList<StepBean> stepBeanList, final Part curPart) { | |
final Piece[][] srcPieces = analysisBean.pieces; | |
// 进入循环之前计算好循环内使用常量 | |
MyList<DoubleBean<Integer, StepBean>> bestPlace = ListPool.localPool().getADoubleBeanList(); | |
// 对方棋手 | |
final Part oppositeCurPart = Part.getOpposite(curPart); | |
int best = MIN; | |
final Object[] objects = stepBeanList.eleTemplateDate(); | |
for (int i =; i < stepBeanList.size(); i++) { | |
final StepBean item = (StepBean) objects[i]; | |
final Place to = item.to; | |
// 备份 | |
final Piece eatenPiece = srcPieces[to.x][to.y]; | |
int score; | |
// 判断是否胜利 | |
if (eatenPiece != null && eatenPiece.role == Role.BOSS) { | |
score = MAX; | |
} else { | |
// 走棋 | |
final int invScr = analysisBean.goForward(item.from, to, eatenPiece); | |
DebugInfo.incrementAlphaBetaOrderTime(); | |
// 评分 | |
score = negativeMaximumWithNoCut(analysisBean, oppositeCurPart, -best); | |
// 退回上一步 | |
analysisBean.backStep(item.from, to, eatenPiece, invScr); | |
} | |
// 这里添加进所有的分数 | |
bestPlace.add(new DoubleBean<>(score, item)); | |
if (score > best) { // 找到一个更好的分,就把以前存的位子全部清除 | |
best = score; | |
} | |
} | |
/* 排序后返回 */ // 这样排序是正确的, 可以有效消减数量 | |
bestPlace.sort((o, o2) -> o2.getO1() - o1.getO1()); | |
stepBeanList.clear(); | |
bestPlace.forEach(dou -> stepBeanList.add(dou.getO())); | |
ListPool.localPool().addListToDoubleBeanListPool(bestPlace); | |
} | |
/** | |
* 负极大值搜索算法(不带剪枝算法) | |
* | |
* @param analysisBean 局势分析对象 | |
* @param curPart 当前走棋方 | |
* @return 负极大值搜索算法计算分值 | |
*/ private static int negativeMaximumWithNoCut(AnalysisBean analysisBean, Part curPart, int alphaBeta) { | |
//. 初始化各个变量 | |
final Piece[][] pieces = analysisBean.pieces; | |
int best = MIN; | |
//. 生成待选的列表,就是可以下子的列表 | |
MyList<StepBean> stepBeanList = geneNestStepPlaces(analysisBean, curPart,); | |
final Object[] objects = stepBeanList.eleTemplateDate(); | |
for (int i =, len = stepBeanList.size(); i < len; i++) { | |
final StepBean item = (StepBean) objects[i]; | |
Place from = item.from; | |
Place to = item.to; | |
// 备份 | |
Piece eatenPiece = pieces[to.x][to.y]; | |
int score; | |
// 判断是否胜利 | |
if (eatenPiece != null && eatenPiece.role == Role.BOSS) { | |
score = MAX; | |
} else { | |
// 走棋 | |
final int invScr = analysisBean.goForward(from, to, eatenPiece); | |
DebugInfo.incrementAlphaBetaOrderTime(); | |
score = analysisBean.getCurPartEvaluateScore(curPart); | |
// 退回上一步 | |
analysisBean.backStep(from, to, eatenPiece, invScr); | |
} | |
if (score > best) { // 找到一个更好的分,就更新分数 | |
best = score; | |
} | |
if (score > alphaBeta) { // alpha剪枝 | |
break; | |
} | |
} | |
ListPool.localPool().addListToStepBeanListPool(stepBeanList); | |
return -best; | |
} | |
/** | |
* 奇数层是电脑(max层)thisSide, 偶数层是human(min层)otherSide | |
* | |
* @param srcPieces 棋盘 | |
* @param curPart 当前走棋方 | |
* @param deep 搜索深度 | |
* @param forbidStep 禁止的步骤(长捉或长拦) | |
* @return 下一步的位置 | |
*/ public static Set<StepBean> getEvaluatedPlace(final Piece[][] srcPieces, final Part curPart, final int deep, final StepBean forbidStep) { | |
//. 初始化各个变量 | |
final AnalysisBean analysisBean = new AnalysisBean(srcPieces); | |
//. 获取可以下子的空位列表 | |
MyList<StepBean> stepBeanList = geneNestStepPlaces(analysisBean, curPart, deep); | |
//. 移除不该下的子 | |
stepBeanList.remove(forbidStep); | |
// 进入循环之前计算好循环内使用常量 | |
Set<StepBean> bestPlace = new HashSet<>(); | |
int best = MIN; | |
// 对方棋手 | |
final Part oppositeCurPart = Part.getOpposite(curPart); | |
// 下一深度 | |
final int nextDeep = deep -; | |
log.debug("size : {}, content: {}", stepBeanList.size(), stepBeanList); | |
final Object[] objects = stepBeanList.eleTemplateDate(); | |
for (int i =, len = stepBeanList.size(); i < len; i++) { | |
StepBean item = (StepBean) objects[i]; | |
final Place to = item.to; | |
// 备份 | |
final Piece eatenPiece = srcPieces[to.x][to.y]; | |
int score; | |
// 判断是否胜利 | |
if (eatenPiece != null && eatenPiece.role == Role.BOSS) { | |
// 步数越少, 分值越大 | |
score = MAX + deep; | |
} else { | |
// 走棋 | |
final int invScr = analysisBean.goForward(item.from, to, eatenPiece); | |
// 评分 | |
if (deep <=) { | |
score = analysisBean.getCurPartEvaluateScore(curPart); | |
} else { | |
score = negativeMaximum(analysisBean, oppositeCurPart, nextDeep, -best); | |
} | |
// 退回上一步 | |
analysisBean.backStep(item.from, to, eatenPiece, invScr); | |
} | |
if (score == best) { // 找到相同的分数, 就添加这一步 | |
bestPlace.add(item); | |
} | |
if (score > best) { // 找到一个更好的分,就把以前存的位子全部清除 | |
best = score; | |
bestPlace.clear(); | |
bestPlace.add(item); | |
} | |
} | |
ListPool.end(); | |
ListPool.localPool().addListToStepBeanListPool(stepBeanList); | |
return bestPlace; | |
} | |
/** | |
* 奇数层是电脑(max层)thisSide, 偶数层是human(min层)otherSide | |
* | |
* @param srcPieces 棋盘 | |
* @param curPart 当前走棋方 | |
* @param deep 搜索深度 | |
* @param forbidStep 禁止的步骤(长捉或长拦) | |
* @return 下一步的位置 | |
*/ public static Set<StepBean> getEvaluatedPlaceWithParallel(final Piece[][] srcPieces, final Part curPart, final int deep, final StepBean forbidStep) { | |
//. 初始化各个变量 | |
final AnalysisBean srcAnalysisBean = new AnalysisBean(srcPieces); | |
//. 获取可以下子的空位列表 | |
MyList<StepBean> stepBeanList = geneNestStepPlaces(srcAnalysisBean, curPart, deep); | |
//. 移除不该下的子 | |
stepBeanList.remove(forbidStep); | |
// 进入循环之前计算好循环内使用常量 | |
final Set<StepBean> bestPlace = new HashSet<>(); | |
final AtomicInteger best = new AtomicInteger(MIN); | |
// 对方棋手 | |
final Part oppositeCurPart = Part.getOpposite(curPart); | |
// 下一深度 | |
final int nextDeep = deep -; | |
log.debug("size : {}, content: {}", stepBeanList.size(), stepBeanList); | |
Arrays.stream(stepBeanList.toArray()).parallel().filter(Objects::nonNull).map(StepBean.class::cast).forEach(item -> { | |
log.debug("并行流 ==> Thread : {}", Thread.currentThread().getId()); | |
final Piece[][] pieces = ArrayUtils.deepClone(srcPieces); | |
final AnalysisBean analysisBean = new AnalysisBean(pieces); | |
final Place to = item.to; | |
// 备份 | |
final Piece eatenPiece = pieces[to.x][to.y]; | |
int score; | |
// 判断是否胜利 | |
if (eatenPiece != null && eatenPiece.role == Role.BOSS) { | |
// 步数越少, 分值越大 | |
score = MAX + deep; | |
} else { | |
// 走棋 | |
final int invScr = analysisBean.goForward(item.from, to, eatenPiece); | |
// 评分 | |
if (deep <=) { | |
score = analysisBean.getCurPartEvaluateScore(curPart); | |
} else { | |
score = negativeMaximum(analysisBean, oppositeCurPart, nextDeep, -best.get()); | |
} | |
// 退回上一步 | |
analysisBean.backStep(item.from, to, eatenPiece, invScr); | |
} | |
if (score == best.get()) { // 找到相同的分数, 就添加这一步 | |
synchronized (bestPlace) { | |
bestPlace.add(item); | |
} | |
} | |
if (score > best.get()) { // 找到一个更好的分,就把以前存的位子全部清除 | |
best.set(score); | |
synchronized (bestPlace) { | |
bestPlace.clear(); | |
bestPlace.add(item); | |
} | |
} | |
ListPool.end(); | |
}); | |
ListPool.localPool().addListToStepBeanListPool(stepBeanList); | |
ListPool.end(); | |
return bestPlace; | |
} | |
/** | |
* 负极大值搜索算法 | |
* | |
* @param analysisBean 局势分析对象 | |
* @param curPart 当前走棋方 | |
* @param deep 搜索深度 | |
* @param alphaBeta alphaBeta 剪枝分值 | |
* @return 负极大值搜索算法计算分值 | |
*/ private static int negativeMaximum(AnalysisBean analysisBean, Part curPart, int deep, int alphaBeta) { | |
//. 初始化各个变量 | |
final Piece[][] pieces = analysisBean.pieces; | |
int best = MIN; | |
// 对方棋手 | |
final Part oppositeCurPart = Part.getOpposite(curPart); | |
// 下一深度 | |
final int nextDeep = deep -; | |
//. 生成待选的列表,就是可以下子的列表 | |
final MyList<StepBean> stepBeanList = geneNestStepPlaces(analysisBean, curPart, deep); | |
final Object[] objects = stepBeanList.eleTemplateDate(); | |
for (int i =, len = stepBeanList.size(); i < len; i++) { | |
final StepBean item = (StepBean) objects[i]; | |
Place from = item.from; | |
Place to = item.to; | |
// 备份 | |
Piece eatenPiece = pieces[to.x][to.y]; | |
int score; | |
// 判断是否胜利 | |
if (eatenPiece != null && eatenPiece.role == Role.BOSS) { | |
// 步数越少, 分值越大 | |
score = MAX + deep; | |
} else { | |
// 走棋 | |
final int invScr = analysisBean.goForward(from, to, eatenPiece); | |
// 评估 | |
if (deep <=) { | |
score = analysisBean.getCurPartEvaluateScore(curPart); | |
} else { | |
score = negativeMaximum(analysisBean, oppositeCurPart, nextDeep, -best); | |
} | |
// 退回上一步 | |
analysisBean.backStep(from, to, eatenPiece, invScr); | |
} | |
if (score > best) { // 找到一个更好的分,就更新分数 | |
best = score; | |
} | |
if (score > alphaBeta) { // alpha剪枝 | |
break; | |
} | |
} | |
ListPool.localPool().addListToStepBeanListPool(stepBeanList); | |
return -best; | |
} | |
} |
总结
通过此次的《中国象棋》游戏实现,让我对swing的相关知识有了进一步的了解,对java这门语言也有了比以前更深刻的认识。
java的一些基本语法,比如数据类型、运算符、程序流程控制和数组等,理解更加透彻。java最核心的核心就是面向对象思想,对于这一个概念,终于悟到了一些。