Java實現(xiàn)中國象棋的示例代碼
前言
中國象棋是起源于中國的一種棋,屬于二人對抗性游戲的一種,在中國有著悠久的歷史。由于用具簡單,趣味性強,成為流行極為廣泛的棋藝活動。
中國象棋使用方形格狀棋盤,圓形棋子共有32個,紅黑二色各有16個棋子,擺放和活動在交叉點上。雙方交替行棋,先把對方的將(帥)“將死”的一方獲勝。
中國象棋是一款具有濃郁中國特色的益智游戲,新增的聯(lián)網(wǎng)對戰(zhàn),趣味多多,聚會可以約小朋友一起來挑戰(zhàn)。精彩的對弈讓你感受中國象棋的博大精深。
《中國象棋》游戲是用java語言實現(xiàn),采用了swing技術(shù)進行了界面化處理,設(shè)計思路用了面向?qū)ο笏枷搿? 人機對弈基于極大極小值搜索算法。
主要需求
按照中國象棋的規(guī)則,實現(xiàn)紅黑棋對戰(zhàn),要有AI對手,可以玩家跟AI的對弈,也可以兩個玩家自己玩。
主要設(shè)計
1、尋找棋盤界面和對應(yīng)的棋子圖片,程序設(shè)計棋盤界面和功能菜單
2、設(shè)計不同的棋子的移動邏輯
3、棋子移動時,要有音效
4、設(shè)計對手AI的邏輯算法,這里運用了極大極小值搜索算法,設(shè)置不同的搜索深度AI(智能不同)
5、對局開始前,雙方棋子在棋盤上的擺法。
6、對局時,由執(zhí)紅棋的一方先走,雙方輪流走一步。
7、輪到走棋的一方,將某個棋子從一個交叉點走到另一個交叉點,或者吃掉對方的棋子而占領(lǐng)其交叉點,都算走了一著。
8、雙方各走一著,稱為一個回合。
9、走一著棋時,如果己方棋子能夠走到的位置有對方棋子存在,就可以把對方棋子吃掉而占領(lǐng)那個位置。
10、一方的棋子攻擊對方的帥(將),并在下一著要把它吃掉,稱為“照將”,或簡稱“將”。“照將”不必聲明。被“照將”的一方必須立即“應(yīng)將”,即用自己的著法去化解被“將”的狀態(tài)。如果被“照將”而無法“應(yīng)將”,就算被“將死”。
11、特別設(shè)計了人機對弈,人人對弈,還有AI對AI對弈
功能截圖
游戲開始
游戲菜單設(shè)置
移動效果
代碼實現(xiàn)
棋盤面板設(shè)計
@Slf4j public class BoardPanel extends JPanel implements LambdaMouseListener { /** * 用于標記棋盤走棋痕跡 */ private final transient TraceMarker traceMarker; /** * 當前走棋開始坐標位置對應(yīng)棋子 */ 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("已經(jīng)存在勝利者: {}, 無法走棋", 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); // 如果不符合規(guī)則則直接返回 final Piece[][] pieces = situation.generatePieces(); if (!curFromPiece.piece.role.rule.check(pieces, pointerPart, stepBean.from, stepBean.to)) { // 如果當前指向棋子是本方棋子 log.warn("不符合走棋規(guī)則"); 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); // 如果走棋后, 導(dǎo)致兩個 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; } } // 當前棋子無棋子或者為對方棋子, 且符合規(guī)則, 可以走棋 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("命令未發(fā)送成功: {} ==> {}", 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); } }
命令執(zhí)行器, 用于處理走棋中的命令
@Slf4j public class CommandExecutor { /** * 異步調(diào)用線程, 來處理走棋命令 */ private final CtrlLoopThreadComp ctrlLoopThreadComp; private final BoardPanel boardPanel; /** * 是否持續(xù)運行標記 */ 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; /** * 下一步驟命令的參數(shù) */ private Object nextParamObj; private volatile boolean isRun; /** * @param commandType 命令類型 */ public void sendCommand(@NonNull CommandType commandType) { sendCommand(commandType, null); } /** * @param commandType 命令類型 * @param paramObj 命令參數(shù) */ public synchronized void sendCommand(@NonNull CommandType commandType, Object paramObj) { this.nextCommand = commandType; this.nextParamObj = paramObj; sustain = false; this.ctrlLoopThreadComp.startOrWake(); } /** * 只有在 線程沒有運行的情況下, 才能添加成功 * * @param commandType 命令類型 * @param paramObj 命令參數(shù) * @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("執(zhí)行命令[{}]發(fā)生異常", 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("已經(jīng)決出勝方!"); } 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("已經(jīng)決出勝方, AI執(zhí)行暫停!"); 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("已經(jīng)決出勝方, AI執(zhí)行暫停!"); 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); } } /** * 命令支持枚舉(以下命令應(yīng)當使用同一個線程運行, 一個事件結(jié)束之后, 另一個事件才能開始運行.) */ @SuppressWarnings("java:S115") public enum CommandType { SuspendCallBackOrAiRun("停止撤銷|AI計算"), CallBackOneTime("撤銷一步"), SustainCallBack("持續(xù)撤銷"), AiRunOneTime("AI計算一步"), SustainAiRun("AI持續(xù)運行"), 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, 哪怕是微不足道的差距都可能導(dǎo)致發(fā)生錯誤 */ private static final int MIN = -MAX; /** * 根據(jù)棋子數(shù)量, 動態(tài)調(diào)整搜索深度 * * @param pieceNum 棋子數(shù)量 * @return 調(diào)整搜索深度差值 */ public static int searchDeepSuit(final int pieceNum) { // 根據(jù)棋子數(shù)量, 動態(tài)調(diào)整搜索深度 if (pieceNum > 20) { return -2; } else if (pieceNum <= 4) { return 4; } else if (pieceNum <= 8) { return 2; } return 0; } /** * 生成待選的列表,就是可以下子的空位, 如果 deep > 2 則對搜索結(jié)果進行排序. * * @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 考慮下此處添加至集合的做法 在計算時 是否有優(yōu)化空間. 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, 則對結(jié)果進行排序 // 排序后的結(jié)果, 進入極大極小值搜索算法時, 容易被剪枝. 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; // 進入循環(huán)之前計算好循環(huán)內(nèi)使用常量 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); } // 這里添加進所有的分數(shù) bestPlace.add(new DoubleBean<>(score, item)); if (score > best) { // 找到一個更好的分,就把以前存的位子全部清除 best = score; } } /* 排序后返回 */ // 這樣排序是正確的, 可以有效消減數(shù)量 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) { // 找到一個更好的分,就更新分數(shù) best = score; } if (score > alphaBeta) { // alpha剪枝 break; } } ListPool.localPool().addListToStepBeanListPool(stepBeanList); return -best; } /** * 奇數(shù)層是電腦(max層)thisSide, 偶數(shù)層是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); // 進入循環(huán)之前計算好循環(huán)內(nèi)使用常量 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) { // 步數(shù)越少, 分值越大 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) { // 找到相同的分數(shù), 就添加這一步 bestPlace.add(item); } if (score > best) { // 找到一個更好的分,就把以前存的位子全部清除 best = score; bestPlace.clear(); bestPlace.add(item); } } ListPool.end(); ListPool.localPool().addListToStepBeanListPool(stepBeanList); return bestPlace; } /** * 奇數(shù)層是電腦(max層)thisSide, 偶數(shù)層是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); // 進入循環(huán)之前計算好循環(huán)內(nèi)使用常量 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) { // 步數(shù)越少, 分值越大 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()) { // 找到相同的分數(shù), 就添加這一步 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) { // 步數(shù)越少, 分值越大 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) { // 找到一個更好的分,就更新分數(shù) best = score; } if (score > alphaBeta) { // alpha剪枝 break; } } ListPool.localPool().addListToStepBeanListPool(stepBeanList); return -best; } }
總結(jié)
通過此次的《中國象棋》游戲?qū)崿F(xiàn),讓我對swing的相關(guān)知識有了進一步的了解,對java這門語言也有了比以前更深刻的認識。
java的一些基本語法,比如數(shù)據(jù)類型、運算符、程序流程控制和數(shù)組等,理解更加透徹。java最核心的核心就是面向?qū)ο笏枷?,對于這一個概念,終于悟到了一些。
到此這篇關(guān)于Java實現(xiàn)中國象棋的示例代碼的文章就介紹到這了,更多相關(guān)Java中國象棋內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springcloud安裝rabbitmq并配置延遲隊列插件的過程詳解
本期主要講解如何利用docker快速安裝rabbitmq并且配置延遲隊列插件,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-05-05springboot中EasyPoi實現(xiàn)自動新增序號的方法
本文主要介紹了EasyPoi實現(xiàn)自動新增序號,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09