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

目录
  • 前言
  • 主要设计
  • 功能截图
  • 代码实现
  • 总结

前言

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

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

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

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

主要需求

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

主要设计

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

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

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

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

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

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

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

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

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

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

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

功能截图

游戏开始

游戏菜单设置

移动效果

代码实现

棋盘面板设计

@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;
    }

}

总结

通过此次的《中国象棋》游戏实现,让我对swing的相关知识有了进一步的了解,对java这门语言也有了比以前更深刻的认识。

java的一些基本语法,比如数据类型、运算符、程序流程控制和数组等,理解更加透彻。java最核心的核心就是面向对象思想,对于这一个概念,终于悟到了一些。

到此这篇关于Java实现中国象棋的示例代码的文章就介绍到这了,更多相关Java中国象棋内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java编程实现国际象棋棋盘

    本文实例为大家分享了java编程实现国际象棋棋盘的具体代码,供大家参考,具体内容如下 问题描述: 打印出国际象棋棋盘(黑白交错) 问题分析: 棋盘由八块黑白相间的方块组成,通过swing编程实现.其中用标签来实现方块,在方块中填充黑或白色.通过i,j来遍历行和列,以i和j的值来判断填充什么颜色 代码分析 import javax.swing.*; import java.awt.*; public class _2ChessBoard { public static void main(Stri

  • java实现象棋小游戏

    本文实例为大家分享了java实现象棋小游戏的具体代码,供大家参考,具体内容如下 用Eclipse编写 java环境1.8jdk 代码如下 package xiangqi象棋; /** *中国象棋Java版V3.0 *源文件:Chess.java *添加功能:实现了当前棋局的保存 **/ import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; import java.io.*; //

  • java实现简单网络象棋游戏

    本文实例为大家分享了java实现网络象棋游戏的具体代码,供大家参考,具体内容如下 游戏规则: 1.将/帅:不能出田字格,不能走斜线,只能前进后退向左向右,每次只走一格: 2.士/仕:不能出田字格,只能走斜线,每次只走一格: 3.象/相:只能走田字格,中间防止蹩脚,不能有棋: 4.马:只能走日,(这个比较麻烦,且看下图标识) 5.车:车只能走直线,这个很好理解,不细说了: 6.炮: 情况一:纯走路-->中间和目的地都不能有棋 情况二:吃棋-–>中间要有一颗棋,目标也有棋,且是敌方的棋,毕竟不能自

  • java绘制国际象棋与中国象棋棋盘

    JAVA API 中的绘制图形类的paint()方法,我们可以轻松绘制中国象棋与国际象棋的棋盘.详见代码:  一.中国象棋棋盘代码 import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; public class ChineseChese extends Frame{

  • Java棋类游戏实践之中国象棋

    本文实例讲述了java实现的中国象棋游戏代码,分享给大家供大家参考,具体代码如下 一.实践目的: 1.鼠标点击.拖动等事件的应用与区别 2.棋谱文件的保存与读取 3.完善象棋的规则. 二.实践内容: 中国象棋历史悠久,吸引了无数的人研究,现对中国象棋的对战和实现棋谱的制作做如下的设计和说明,供大家参考学习. 1.机机对弈,红方先手.在符合规则的情况下拖动棋子到目的地,松鼠标落子. 人人对弈图 2.制作棋谱,选择制作棋谱菜单后,对弈开始,并记录了下棋过程. 选择"制作棋谱"菜单 棋谱制作

  • java使用swing绘制国际象棋棋盘

    本文实例为大家分享了java使用swing绘制国际象棋棋盘的具体代码,供大家参考,具体内容如下 1.完整代码 import java.awt.Color; import java.awt.Point; import javax.swing.BorderFactory; import javax.swing.JFrame; import javax.swing.JLabel; public class guo_ji_xiang_qi_qipan { public static void main(

  • java打印国际象棋棋盘的方法

    本文实例为大家分享了java打印出国际象棋棋盘的具体代码,供大家参考,具体内容如下 问题分析 观察国际象棋棋盘的图案,可以发现其中的规律:棋盘由八行八列黑白方块相间组成,可以用i控制行,j来控制列,根据i+j的和的变化来控制输出黑方块还是白方块.这里我们使用了Swing的标签组件JLable,通过设置JLabel组件的背景色和透明度来实现黑白方块. (1)确定程序框架 这是一个绘图案例,这里我们采用JFrame创建窗口,然后在窗口里添加JLabel标签组件,通过前面的问题分析可知,JLabel标

  • Javafx实现国际象棋游戏

    本文实例为大家分享了Javafx实现国际象棋游戏的具体代码,供大家参考,具体内容如下 基本规则 棋子马设计"日"的移动方式 兵设计只能向前直走,每次只能走一格.但走第一步时,可以走一格或两格的移动方式 请为后设计横.直.斜都可以走,步数不受限制,但不能越子的移动方式. 车只能横向或者竖向行走 国王是在以自己为中心的九宫格内行走 骑士只能走对角线 项目目录结构 UML类图关系 以骑士为例 实现基本功能 吃子 不能越子 游戏结束提示 基本移动策略 背景音乐 效果 控制器 PressedAc

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

    目录 前言 主要设计 功能截图 代码实现 总结 前言 中国象棋是起源于中国的一种棋,属于二人对抗性游戏的一种,在中国有着悠久的历史.由于用具简单,趣味性强,成为流行极为广泛的棋艺活动. 中国象棋使用方形格状棋盘,圆形棋子共有32个,红黑二色各有16个棋子,摆放和活动在交叉点上.双方交替行棋,先把对方的将(帅)“将死”的一方获胜. 中国象棋是一款具有浓郁中国特色的益智游戏,新增的联网对战,趣味多多,聚会可以约小朋友一起来挑战.精彩的对弈让你感受中国象棋的博大精深. <中国象棋>游戏是用java语

  • 基于Matlab实现中国象棋的示例代码

    目录 设置变量 绘图 绘制棋盘 绘制棋子 棋子移动规则 判断是否可以移动 移动棋子 吃子 设置变量 nRowNum = 8; % 画布行数 nColNum = 9; % 画布列数 offset_x = 0;% 红车坐标起点 offset_y = 0;% 红车坐标起点 chess_name = {{'帥','仕','相','马','車','炮','兵'},{'將','仕','象','马','車','炮','卒'}}; chess_type = [5 4 3 2 1 2 3 4 5 6 6 7 7

  • Java实现中国象棋游戏

    目录 一.界面 二.按钮 三.加棋子 四.实现棋子的移动 五.判断胜负 六.按钮“开始游戏”和“重新开始”的实现 七.加规则 八.轮次 九.悔棋 十.背景 及 提示 本文实例为大家分享了Java实现中国象棋游戏的具体代码,供大家参考,具体内容如下 实现一个小游戏需要知道从哪里下手,一步步实现和完善,对于一个中国象棋的小游戏,我们可以按这样的顺序展开: 一.界面 下棋的棋盘首先要准备好,这就是一个合适大小合适比例合适位置的界面,然后在窗体上画上(没错drawLine的那种画上)n条直线和斜线,具体

  • Java连接postgresql数据库的示例代码

    本文介绍了Java连接postgresql数据库的示例代码,分享给大家,具体如下: 1.下载驱动jar 下载地址:https://jdbc.postgresql.org/download.html 2.导入jar包 新建lib文件夹,将下载的jar驱动包拖到文件夹中. 将jar驱动包添加到Libraries 3.程序代码如下:HelloWorld.java package test; import java.sql.Connection; import java.sql.DriverManage

  • java 生成文字图片的示例代码

    本文主要介绍了java 生成文字图片的示例代码,分享给大家,具体如下: import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO;

  • Java动态规划之编辑距离问题示例代码

    动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移.一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划. 动态规划实际上是一类题目的总称,并不是指某个固定的算法.动态规划的意义就是通过采用递推(或者分而治之)的策略,通过解决大问题的子问题从而解决整体的做法.动态规划的核心思想是巧妙的将问题拆分成多个子问题,通过计算子问题而得到整体问题的解.而子问题又可以拆分成更多的子问题,从而用类似递推迭代的方法解决要求的问题.问题描述: 对于序列S和T,

  • Java的静态类型检查示例代码详解

    关于静态类型检查和动态类型检查的解释: 静态类型检查:基于程序的源代码来验证类型安全的过程: 动态类型检查:在程序运行期间验证类型安全的过程: Java使用静态类型检查在编译期间分析程序,确保没有类型错误.基本的思想是不要让类型错误在运行期间发生. 在各色各样的编程语言中,总共存在着两个类型检查机制:静态类型检查和动态类型检查. 静态类型检查是指通过对应用程序的源码进行分析,在编译期间就保证程序的类型安全. 动态类型检查是在程序的运行过程中,验证程序的类型安全.在Java中,编译期间使用静态类型

  • Java随机生成身份证完整示例代码

    身份证算法实现 1.号码的结构 公民身份号码是特征组合码, 由十七位数字本体码和一位校验码组成. 排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码  三位数字顺序码和一位数字校验码. 2.地址码(前六位数) 表示编码对象常住户口所在县(市.旗.区)的行政区划代码,按GB/T2260的规定执行. 3.出生日期码(第七位至十四位) 表示编码对象出生的年.月.日,按GB/T7408的规定执行,年.月.日代码之间不用分隔符. 4.顺序码(第十五位至十七位) 表示在同一地址码所标识的区域范围内,

  • java实现基因序列比较的示例代码

    设计算法,计算两给定基因序列的相似程度. 人类基因由4种核苷酸,分别用字母ACTG表示.要求编写一个程序,按以下规则比较两个基因序列并确定它们的相似程度.即给出两个基因序列AGTGATG和GTTAG,它们有多相似呢?测量两个基因相似度的一种方法称为对齐.使用对齐方法可以在基因的适当位置加入空格,让两个基因的长度相等,然后根据基因的分值矩阵计算分数. 看了很多代码基本上都是用c++或者c写的,但是习惯性写java就用java实现一下 基本的思路就是,和背包问题差不多,实现还是模仿填表的形式去实现的

  • 用java实现跳动的小球示例代码

    实现效果为一个小球接触左右侧时,会反向的运动. import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.paint.Colo

随机推荐