软件编程
位置:首页>> 软件编程>> java编程>> Java实现中国象棋的示例代码

Java实现中国象棋的示例代码

作者:小虚竹and掘金  发布时间:2021-08-10 21:56:03 

标签:Java,象棋

前言

中国象棋是起源于中国的一种棋,属于二人对抗 * 的一种,在中国有着悠久的历史。由于用具简单,趣味性强,成为流行极为广泛的棋艺活动。

中国象棋使用方形格状棋盘,圆形棋子共有32个,红黑二色各有16个棋子,摆放和活动在交叉点上。双方交替行棋,先把对方的将(帅)“将死”的一方获胜。

中国象棋是一款具有浓郁中国特色的益智游戏,新增的联网对战,趣味多多,聚会可以约小朋友一起来挑战。精彩的对弈让你感受中国象棋的博大精深。

《中国象棋》游戏是用java语言实现,采用了swing技术进行了界面化处理,设计思路用了面向对象思想。, 人机对弈基于极大极小值搜索算法。

主要需求

按照中国象棋的规则,实现红黑棋对战,要有AI对手,可以玩家跟AI的对弈,也可以两个玩家自己玩。

主要设计

1、寻找棋盘界面和对应的棋子图片,程序设计棋盘界面和功能菜单

2、设计不同的棋子的移动逻辑

3、棋子移动时,要有音效

4、设计对手AI的逻辑算法,这里运用了极大极小值搜索算法,设置不同的搜索深度AI(智能不同)

5、对局开始前,双方棋子在棋盘上的摆法。

6、对局时,由执红棋的一方先走,双方轮流走一步。

7、轮到走棋的一方,将某个棋子从一个交叉点走到另一个交叉点,或者吃掉对方的棋子而占领其交叉点,都算走了一着。

8、双方各走一着,称为一个回合。

9、走一着棋时,如果己方棋子能够走到的位置有对方棋子存在,就可以把对方棋子吃掉而占领那个位置。

10、一方的棋子攻击对方的帅(将),并在下一着要把它吃掉,称为“照将”,或简称“将”。“照将”不必声明。被“照将”的一方必须立即“应将”,即用自己的着法去化解被“将”的状态。如果被“照将”而无法“应将”,就算被“将死”。

11、特别设计了人机对弈,人人对弈,还有AI对AI对弈

功能截图

游戏开始

Java实现中国象棋的示例代码

游戏菜单设置

Java实现中国象棋的示例代码

移动效果

Java实现中国象棋的示例代码

代码实现

棋盘面板设计


@Slf4j
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, 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 鼠标按压事件对象
    */
   @Override
   public void mouseReleased(MouseEvent e) {
       // 位置
       Place pointerPlace = ChessDefined.convertLocationToPlace(e.getPoint());
       if (pointerPlace == null) {
           return;
       }
       if (situation.winner() != null) {
           log.warn("已经存在胜利者: {}, 无法走棋", situation.winner());
           return;
       }
       // 当前走棋方
       @NonNull 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.isBossF2FAfterStep(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));
       }
   }

@Override
   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) / 2;
       int y = (fHeight - imgHeight) / 2;
       // 520 576 514 567
       log.debug(String.format("%s,%s,%s,%s,%s,%s", imgWidth, imgHeight, fWidth, fHeight, x, y));
       g.drawImage(img, 0, 0, null);
   }

}

命令执行器, 用于处理走棋中的命令


@Slf4j
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(@NonNull CommandType commandType) {
       sendCommand(commandType, null);
   }

/**
    * @param commandType 命令类型
    * @param paramObj    命令参数
    */
   public synchronized void sendCommand(@NonNull CommandType commandType, Object paramObj) {
       this.nextCommand = commandType;
       this.nextParamObj = paramObj;
       sustain = false;
       this.ctrlLoopThreadComp.startOrWake();
   }

/**
    * 只有在 线程没有运行的情况下, 才能添加成功
    *
    * @param commandType 命令类型
    * @param paramObj    命令参数
    * @return 是否添加成功
    */
   public synchronized boolean sendCommandWhenNotRun(@NonNull 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[0];
               Place to = (Place) params[1];
               PlayerType type = (PlayerType) params[2];
               Application.context().locatePiece(from, to, type);
               sendCommand(CommandExecutor.CommandType.SustainAiRunIfNextIsAi);
               break;
           default:
               throw new ShouldNotHappenException("未处理的命令: " + commandType);
       }
   }

/**
    * 命令支持枚举(以下命令应当使用同一个线程运行, 一个事件结束之后, 另一个事件才能开始运行.)
    */
   @SuppressWarnings("java:S115")
   public enum CommandType {
       SuspendCallBackOrAiRun("停止撤销|AI计算"),
       CallBackOneTime("撤销一步"),
       SustainCallBack("持续撤销"),
       AiRunOneTime("AI计算一步"),
       SustainAiRun("AI持续运行"),
       SustainAiRunIfNextIsAi("COM角色运行"),
       LocationPiece("ui落子命令");

@Getter
       private final String label;

CommandType(String label) {
           this.label = label;
       }
   }

}

核心算法



@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Slf4j
public class AlphaBeta {

private static final int MAX = 100_000_000;
   /**
    * 这里要保证 Min + Max = 0, 哪怕是微不足道的差距都可能导致发生错误
    */
   private static final int MIN = -MAX;

/**
    * 根据棋子数量, 动态调整搜索深度
    *
    * @param pieceNum 棋子数量
    * @return 调整搜索深度差值
    */
   public static int searchDeepSuit(final int pieceNum) {
       // 根据棋子数量, 动态调整搜索深度
       if (pieceNum > 20) {
           return -2;
       } else if (pieceNum <= 4) {
           return 4;
       } else if (pieceNum <= 8) {
           return 2;
       }
       return 0;
   }

/**
    * 生成待选的列表,就是可以下子的空位, 如果 deep > 2 则对搜索结果进行排序.
    *
    * @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 = 0; x < ChessDefined.RANGE_X; x++) {
           for (int y = 0; 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 = 0, len = list.size(); i < len; i++) {
                       stepBeanList.add(StepBean.of(from, (Place) elementData[i]));
                   }
                   ListPool.localPool().addListToPool(list);
               }
           }
       }
       // 是否排序, 如果搜索深度大于2, 则对结果进行排序
       // 排序后的结果, 进入极大极小值搜索算法时, 容易被剪枝.
       if (deep > 2) {
           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 = 0; 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((o1, o2) -> o2.getO1() - o1.getO1());

stepBeanList.clear();
       bestPlace.forEach(dou -> stepBeanList.add(dou.getO2()));

ListPool.localPool().addListToDoubleBeanListPool(bestPlace);
   }

/**
    * 负极大值搜索算法(不带剪枝算法)
    *
    * @param analysisBean 局势分析对象
    * @param curPart      当前走棋方
    * @return 负极大值搜索算法计算分值
    */
   private static int negativeMaximumWithNoCut(AnalysisBean analysisBean, Part curPart, int alphaBeta) {
       // 1. 初始化各个变量
       final Piece[][] pieces = analysisBean.pieces;
       int best = MIN;
       // 2. 生成待选的列表,就是可以下子的列表
       MyList<StepBean> stepBeanList = geneNestStepPlaces(analysisBean, curPart, 1);

final Object[] objects = stepBeanList.eleTemplateDate();
       for (int i = 0, 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) {
       // 1. 初始化各个变量
       final AnalysisBean analysisBean = new AnalysisBean(srcPieces);
       // 2. 获取可以下子的空位列表
       MyList<StepBean> stepBeanList = geneNestStepPlaces(analysisBean, curPart, deep);
       // 3. 移除不该下的子
       stepBeanList.remove(forbidStep);
       // 进入循环之前计算好循环内使用常量
       Set<StepBean> bestPlace = new HashSet<>();
       int best = MIN;
       // 对方棋手
       final Part oppositeCurPart = Part.getOpposite(curPart);
       // 下一深度
       final int nextDeep = deep - 1;
       log.debug("size : {}, content: {}", stepBeanList.size(), stepBeanList);
       final Object[] objects = stepBeanList.eleTemplateDate();
       for (int i = 0, 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 <= 1) {
                   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) {
       // 1. 初始化各个变量
       final AnalysisBean srcAnalysisBean = new AnalysisBean(srcPieces);
       // 2. 获取可以下子的空位列表
       MyList<StepBean> stepBeanList = geneNestStepPlaces(srcAnalysisBean, curPart, deep);
       // 3. 移除不该下的子
       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 - 1;
       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 <= 1) {
                   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) {
       // 1. 初始化各个变量
       final Piece[][] pieces = analysisBean.pieces;
       int best = MIN;
       // 对方棋手
       final Part oppositeCurPart = Part.getOpposite(curPart);
       // 下一深度
       final int nextDeep = deep - 1;
       // 2. 生成待选的列表,就是可以下子的列表
       final MyList<StepBean> stepBeanList = geneNestStepPlaces(analysisBean, curPart, deep);

final Object[] objects = stepBeanList.eleTemplateDate();
       for (int i = 0, 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 <= 1) {
                   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;
   }

}

来源:https://juejin.cn/post/7061502423596007432

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com