前言
中國象棋是起源於中國的一種棋,屬於二人對抗性遊戲的一種,在中國有著悠久的歷史。由於用具簡單,趣味性強,成為流行極為廣泛的棋藝活動。
中國象棋使用方形格狀棋盤,圓形棋子共有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最核心的核心就是面向物件思想,對於這一個概念,終於悟到了一些。
作者:小虛竹and掘金
連結:https://juejin.cn/post/7061502423596007432
來源:稀土掘金
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。