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

 更新时间:2022年02月07日 10:41:17   作者:小虚竹and掘金  
中国象棋是起源于中国的一种棋,属于二人对抗性游戏的一种,在中国有着悠久的历史。由于用具简单,趣味性强,成为流行极为广泛的棋艺活动。本文将利用Java实现这一经典游戏,需要的可以参考一下

Java技术迷

前言

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

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

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

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

主要需求

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

主要设计

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

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

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

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

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

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

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

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

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

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

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

功能截图

游戏开始

游戏菜单设置

移动效果

代码实现

棋盘面板设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
@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);
    }
 
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
@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;
        }
    }
 
}

核心算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
@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中国象棋内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

蓄力AI

微信公众号搜索 “ 脚本之家 ” ,选择关注

程序猿的那些事、送书等活动等着你

原文链接:https://juejin.cn/post/7061502423596007432

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 reterry123@163.com 进行投诉反馈,一经查实,立即处理!

相关文章

  • Java创建对象的四种方式详解

    Java创建对象的四种方式详解

    这篇文章主要介绍了Java创建对象的四种方式详解,如果我们不想利用默认构造器来创建java对象,而想利用指定的构造器来创建java对象,则需要利用Construtor对象,每个Construtor对应一个构造器,需要的朋友可以参考下
    2023-11-11
  • 使用IDEA搭建MyBatis环境详细过程

    使用IDEA搭建MyBatis环境详细过程

    这篇文章主要介绍了使用IDEA搭建MyBatis环境的相关知识,包括创建项目的过程及导入mybatis的核心jar包的详细说明,本文通过图文实例代码相结合给大家介绍的非常详细,需要的朋友可以参考下
    2021-05-05
  • spring状态机模式使用小结

    spring状态机模式使用小结

    说起Spring状态机,大家很容易联想到这个状态机和设计模式中状态模式的区别是啥呢?没错,Spring状态机就是状态模式的一种实现,在介绍Spring状态机之前,让我们来看看设计模式中的状态模式,需要的朋友可以参考下
    2024-04-04
  • IntelliJ IDEA 小技巧之Bookmark(书签)的使用

    IntelliJ IDEA 小技巧之Bookmark(书签)的使用

    这篇文章主要介绍了IntelliJ IDEA 小技巧之Bookmark(书签)的使用,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • Java IO字符流缓冲区实现原理解析

    Java IO字符流缓冲区实现原理解析

    这篇文章主要介绍了Java IO字符流缓冲区实现原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • Spring Boot中使用Swagger3.0.0版本构建RESTful APIs的方法

    Spring Boot中使用Swagger3.0.0版本构建RESTful APIs的方法

    Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务,这篇文章主要介绍了Spring Boot中使用Swagger3.0.0版本构建RESTful APIs的方法,需要的朋友可以参考下
    2022-11-11
  • java实现建造者模式(Builder Pattern)

    java实现建造者模式(Builder Pattern)

    这篇文章主要为大家详细介绍了java实现建造者模式Builder Pattern,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • Java 单链表数据结构的增删改查教程

    Java 单链表数据结构的增删改查教程

    这篇文章主要介绍了Java 单链表数据结构的增删改查教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • JAVA设计模式---单例模式你知道吗

    JAVA设计模式---单例模式你知道吗

    这篇文章主要给大家介绍了关于Java单例模式,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2021-09-09
  • java日期处理工具类

    java日期处理工具类

    这篇文章主要为大家详细介绍了java日期处理工具类,其次还介绍了日期处理的基础知识,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-05-05

最新评论