前言

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

中国象棋使用方形格状棋盘,圆形棋子共有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 鼠标按压事件对象*/@Overridepublic 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: formif (curFromPiece == null) {// 当前焦点位置有棋子且是本方棋子if (pointerPiece != null && pointerPiece.piece.part == pointerPart) {// 本方棋子, 同时是from指向curFromPiece = pointerPiece;traceMarker.setMarkFromPlace(pointerPlace);// 获取toListMyList<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 : "当前焦点位置有棋子且是本方棋子 之前指向了对方棋子";// 更新 curFromPiececurFromPiece = 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));}}@Overridepublic 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 567log.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落子命令");@Getterprivate 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最核心的核心就是面向对象思想,对于这一个概念,终于悟到了一些。

源码获取

源码下载地址:传送门------->

点赞,评论,关注博主后,私聊博主免费获取
需要技术指导,写项目程序,等更多服务请联系博主

今天是持续写作的第 8 / 100 天。
可以关注我,点赞我、评论我、收藏我啦。

JAVA 实现《中国象棋》游戏相关推荐

  1. 一文教你用java实现儿时的超级玛丽游戏

    导读:近年来,Java作为一种新的编程语言,以其简单性.可移植性和平台无关性等优点,得到了广泛地应用.J2SE称为Java标准版或Java标准平台.J2SE提供了标准的SDK开发平台.利用该平台可以开 ...

  2. java实现儿时的超级玛丽游戏

    导读:近年来,Java作为一种新的编程语言,以其简单性.可移植性和平台无关性等优点,得到了广泛地应用.J2SE称为Java标准版或Java标准平台.J2SE提供了标准的SDK开发平台.利用该平台可以开 ...

  3. java简单通讯录的实现02person类_用java实现简单的小游戏(你一定玩过)

    用java实现简单的小游戏(你一定玩过) 对于java初学者来说,通过一些学习小游戏来对swing学习以及对java基础的学习是一个好的方法,同时也给学习带来了很多的乐趣,接下来就给大家分享一个jav ...

  4. java实现简单窗体小游戏----球球大作战

    java实现简单窗体小游戏----球球大作战 需求分析 1.分析小球的属性: ​ 坐标.大小.颜色.方向.速度 2.抽象类:Ball ​ 设计类:BallMain-创建窗体 ​ BallJPanel- ...

  5. main java game,playgame 一个JAVA编写的飞行小游戏,有基本完整的 框架,适合初学者参照学习 Other s 其他 238万源代码下载- www.pudn.com...

    文件名称: playgame下载 收藏√  [ 5  4  3  2  1 ] 开发工具: Java 文件大小: 7050 KB 上传时间: 2013-06-06 下载次数: 3 提 供 者: Lyq ...

  6. 简易贪吃蛇小游戏java版_用GUI实现java版贪吃蛇小游戏

    本文实例为大家分享了java版贪吃蛇小游戏的具体代码,供大家参考,具体内容如下 项目结构 新建一个JFrame窗口,作为程序入口 public class GameStart{ public stat ...

  7. [Leedcode][JAVA][第45题][跳跃游戏 II][贪心算法]

    [问题描述][Leedcode][JAVA][第45题][跳跃游戏 II] 输入: [2,3,1,1,4] 输出: 2 解释: 跳到最后一个位置的最小跳跃数是 2.从下标为 0 跳到下标为 1 的位置 ...

  8. java演练 猜奇偶小游戏开发 DB游戏必输的设计

    java演练 猜奇偶小游戏开发 DB游戏必输的设计 阶段一,视频 https://www.ixigua.com/6870390946270446088?logTag=J_BVJOm_LIpQ-hWYY ...

  9. 递归走迷宫java,java递归实现的迷宫游戏

    java递归实现的迷宫游戏 public class Migong { private int gard[][]={  {1,1,1,1,0,1,1,1}, {0,0,0,1,1,1,1,1}, {1 ...

  10. 推箱子游戏的java设计思路_用JAVA实现一个推箱子游戏

    技术应用 TECHNOLOGY AND MARKET Vol. 26,No. 2,2019 用 JAVA 实现一个推箱子游戏 马寅璞1,孔阳坤2 ( 1. 南京信息工程大学计算机软件学院物联网工程 1 ...

最新文章

  1. html-webpack-plugin 多页面,html-webpack-plugin
  2. zookeeper原理与使用
  3. Openjudge NOI题库 ch0111/04 网线管理
  4. 数据结构与算法--求1~n能组成的所有二叉搜索树的排列
  5. json-tree api_什么是JSON处理(JSON-P API)?
  6. Android学习笔记---23_网络通信之网络图片查看器
  7. IntelliJ IDEA导入maven项目
  8. 「代码随想录」121. 买卖股票的最佳时机【贪心】【动态规划】力扣/leetcode详解
  9. linux服务器打印400错误,Nginx过一段时间出现400 Bad Request 错误解决方法
  10. KinhDown(度盘PC下载器)
  11. 特殊的空格-ASCII码值160
  12. Hybird方案-概述
  13. 微信棋牌平台开发架设HTML5手机端页面缩放搭建教程
  14. 如何使用Blender制作360度全景图和全景视频?
  15. 不安全的文件上传基本原理(Unsafe file upload)
  16. 远程连接内网电脑访问数据
  17. TDD--01--ATDD、UTDD
  18. RAID设备的数据恢复
  19. solr mysql 增量索引_solr中实现MySQL数据全量索引和增量索引
  20. apk反编译和重新打包流程

热门文章

  1. HTML表格(HTML 表格的使用,收藏这一篇就够了)
  2. linux反编译lua工具,lua脚本编译及反编译工具下载
  3. 记一次:java实现excel转图片
  4. win11没有扫雷了?咱用python做一个(效果演示+源码分享)
  5. JAVA菜鸟入门(8) Java的Final关键字
  6. [网络安全自学篇] 六十.Cracer第八期——(2)五万字总结Linux基础知识和常用渗透命令
  7. qq等级查询php源码,最新沉沦QQ等级代挂系源码统开源分享
  8. 小米无线路由器经常连接不上网络连接服务器,小米路由器频繁掉线详细解决方法...
  9. lammps计算聚合物例子_lammps计算金属扩散
  10. cad快速看图2020|cad迷你画图2020 附安装教程