简易五子棋

简介

使用Java实现简易五子棋

规则介绍

游戏使用一个标准的15×15方格的棋盘,双方分别用黑白两种颜色的棋子进行对战。黑子先行,双方轮流在空棋盘的交叉点上落子,每人一次只能落一子。游戏的目标是让自己的五颗棋子连成一线,这条线可以是横线、竖线、对角线或斜线。如果一方的五颗棋子按照上述规则连成一线,这一方就获胜并结束游戏。

(1)对局双方各执一色棋子。

(2)空棋盘开局。

(3)黑先、白后,交替下子,每次只能下一子。

(4)棋子下在棋盘的空白点上,棋子下定后不得移动或拿走。

(5)黑方的第一枚棋子必须下在天元点上,即中心交叉点"

功能设计

  • 重新开始
    用户操作【重新开始】功能,弹窗询问是否确定重新开始,如果是则将所有数据重新初始化,否则什么也不做。
  • 悔棋
    用户操作【悔棋】功能,恢复上一步的操作,如果已无上一步操作或者游戏结束,不允许悔棋并弹窗提示。
  • 退出游戏
    用户操作【退出游戏】功能,关闭该应用程序。
  • 帮助
    菜单栏添加玩法提示,引导用户使用。
  • 坐标校准
    由于棋子需下在网格线上,交叉点的坐标很小,故鼠标很难精准点击在符合的坐标上,那么就需要对用户点击的坐标进行校准,将其坐标校准为最贴近的符合坐标。
  • 坐标可行性及输赢判断
    用户落子时,判断该坐标是否可用(是否已有棋子),如果不可行,弹窗提示,否则,判断输赢并且刷新页面绘制棋子。如果某一方获胜,提示游戏结束,禁止继续落下棋子。
  • 输赢判断算法
    以落下棋子坐标出发,向上下、左右、左上右下、右上左下四个方向延伸,朝一个方向至多延伸五次,若有同色棋子,则计数器加一,最终判断计数器是否大于等于5,如果是则获得胜利。

实现

附上如下实现代码

java 复制代码
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Objects;
import java.util.Stack;

public class Gobang extends JPanel {

    boolean op = false; //true-white false black
    boolean win = false;

    static final int SCREEN_WIDTH = 700;
    static final int SCREEN_HEIGHT = 700;

    static final int UNIT_SIZE = 50;
    static final int GAME_UNITS = SCREEN_WIDTH / UNIT_SIZE;

    boolean[][] black;
    boolean[][] white;

    Graphics g;
    Point checkPoint;
    Stack<Point> opStack;
    MouseListener mouseListener;

    JMenuBar menuBar;

    Gobang() {
        this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
        this.setFocusable(true);
        this.addKeyListener(new MyKeyAdapter());
        init();
    }

    /**
     * 初始化网格
     */
    public void initCheckerboard() {
        if (Objects.isNull(g)) {
            return;
        }
        g.setColor(Color.BLACK);
        for (int i = 0; i < SCREEN_WIDTH; i += UNIT_SIZE) {
            g.drawLine(i, 0, i, SCREEN_HEIGHT);
        }
        for (int i = 0; i < SCREEN_HEIGHT; i += UNIT_SIZE) {
            g.drawLine(0, i, SCREEN_WIDTH, i);
        }
    }

    public void initMenu() {
        menuBar = new JMenuBar();
        JMenu menu = new JMenu("菜单");
        JMenuItem restart;
        (restart = new JMenuItem("重新开始")).addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int res = JOptionPane.showConfirmDialog(null, "请确定要重新开始吗?", "", JOptionPane.OK_CANCEL_OPTION);
                if (res == 0) {
                    init();
                }
            }
        });
        menu.add(restart);

        JMenuItem regretChess;
        (regretChess = new JMenuItem("悔棋")).addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                regretChess();
            }
        });
        menu.add(regretChess);


        JMenuItem exit;
        (exit = new JMenuItem("退出游戏")).addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
        menu.add(exit);
        menuBar.add(menu);

        JMenu helpMenu = new JMenu("帮助");
        JMenuItem playWay;
        (playWay = new JMenuItem("玩法")).addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(null, "游戏使用一个标准的15×15方格的棋盘,双方分别用黑白两种颜色的棋子进行对战。\n" +
                        "黑子先行,双方轮流在空棋盘的交叉点上落子,每人一次只能落一子。\n" +
                        "游戏的目标是让自己的五颗棋子连成一线,这条线可以是横线、竖线、对角线或斜线。\n" +
                        "如果一方的五颗棋子按照上述规则连成一线,这一方就获胜并结束游戏。\n" +
                        "(1)对局双方各执一色棋子。\n" +
                        "(2)空棋盘开局。\n" +
                        "(3)黑先、白后,交替下子,每次只能下一子。\n" +
                        "(4)棋子下在棋盘的空白点上,棋子下定后不得移动或拿走。\n" +
                        "(5)黑方的第一枚棋子必须下在天元点上,即中心交叉点");
            }
        });
        helpMenu.add(playWay);

        JMenuItem about;
        (about = new JMenuItem("关于")).addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(null, "无聊时候花一点时间写着玩的");
            }
        });
        helpMenu.add(about);

        menuBar.add(helpMenu);
    }

    /**
     * 游戏数据初始化
     */
    public void init() {
        initCheckerboard();
        initMenu();

        black = new boolean[GAME_UNITS + 1][GAME_UNITS + 1];
        white = new boolean[GAME_UNITS + 1][GAME_UNITS + 1];

        op = false;
        win = false;
        checkPoint = null;

        opStack = new Stack<>();

        super.addMouseListener(mouseListener = new MyMouseAdapter());
        repaint();
    }

    /**
     * 校准鼠标
     *
     * @param x
     * @return
     */
    public double calibration(double x) {
        if (x % UNIT_SIZE > UNIT_SIZE / 2) {
            x = ((int) x / UNIT_SIZE + 1) * UNIT_SIZE;
        } else {
            x = ((int) x / UNIT_SIZE) * UNIT_SIZE;
        }
        return x;
    }

    /**
     * 绘制棋子
     */
    public void drawChessPieces() {
        String tip = null;
        if (Objects.nonNull(checkPoint)) {
            //存记录
            int x = (int) checkPoint.getX();
            int y = (int) checkPoint.getY();
            if (op) {
                white[x / UNIT_SIZE][y / UNIT_SIZE] = true;
            } else {
                black[x / UNIT_SIZE][y / UNIT_SIZE] = true;
            }

            tip = judge();

            op = !op;
            checkPoint = null;
        }

        for (int i = 0; i < black.length; i++) {
            for (int j = 0; j < black.length; j++) {
                if (black[i][j]) {
                    g.setColor(Color.BLACK);
                    g.fillOval(i * UNIT_SIZE - UNIT_SIZE / 2, j * UNIT_SIZE - UNIT_SIZE / 2, UNIT_SIZE, UNIT_SIZE);
                }
            }
        }

        for (int i = 0; i < white.length; i++) {
            for (int j = 0; j < white.length; j++) {
                if (white[i][j]) {
                    g.setColor(Color.WHITE);
                    g.fillOval(i * UNIT_SIZE - UNIT_SIZE / 2, j * UNIT_SIZE - UNIT_SIZE / 2, UNIT_SIZE, UNIT_SIZE);
                }
            }
        }

        if (win && Objects.nonNull(tip)) {
            g.setFont(new Font("Ink Free", Font.BOLD, 40));
            g.setColor(Color.RED);
            g.drawString(tip, (SCREEN_WIDTH - getFontMetrics(g.getFont()).stringWidth(tip)) / 2, SCREEN_HEIGHT / 2);
            super.removeMouseListener(mouseListener);
        }
    }

    public String judge() {
        String tip = op ? tip = "White Win!" : "Black Win!";
        boolean[][] opArr = op ? white : black;
        int x = (int) checkPoint.getX() / UNIT_SIZE;
        int y = (int) checkPoint.getY() / UNIT_SIZE;
        int tempX = x;
        int tempY = y;
        int count = 0;
        //判断横向
        while (x >= 0 && x > tempX - 5) {
            if (opArr[x][y]) {
                count++;
                x--;
                if (x >= 0) {
                    continue;
                }
            }
            x = tempX + 1;
            break;

        }

        while (x <= GAME_UNITS && x < tempX + 5) {
            if (opArr[x][y]) {
                count++;
                x++;
                continue;
            }
            break;
        }
        if (count >= 5) {
            win = true;
            return tip;
        }

        //判断纵向
        x = tempX;
        y = tempY;
        count = 0;
        while (x >= 0 && y >= 0 && x <= GAME_UNITS && y <= GAME_UNITS
                && y > tempY - 5) {

            if (opArr[x][y]) {
                count++;
                y--;
                if (y >= 0) {
                    continue;
                }
            }
            y = tempY + 1;
            break;

        }

        while (x >= 0 && y >= 0 && x <= GAME_UNITS && y <= GAME_UNITS
                && y < tempY + 5) {
            if (opArr[x][y]) {
                count++;
                y++;
                continue;
            }
            break;

        }
        if (count >= 5) {
            win = true;
            return tip;
        }

        //判断左斜向
        x = tempX;
        y = tempY;
        count = 0;
        while (x >= 0 && y >= 0 && x <= GAME_UNITS && y <= GAME_UNITS
                && y > tempY - 5 && x > tempX - 5) {

            if (opArr[x][y]) {
                count++;
                x--;
                y--;
                if (x >= 0 && y >= 0) {
                    continue;
                }
            }

            x = tempX + 1;
            y = tempY + 1;
            break;

        }

        while (x >= 0 && y >= 0 && x <= GAME_UNITS && y <= GAME_UNITS
                && y < tempY + 5 && x < tempY + 5) {
            if (opArr[x][y]) {
                count++;
                y++;
                x++;
                continue;
            }
            break;

        }
        if (count >= 5) {
            win = true;
            return tip;
        }

        //判断右斜向
        x = tempX;
        y = tempY;
        count = 0;
        while (x >= 0 && y >= 0 && x <= GAME_UNITS && y <= GAME_UNITS
                && y > tempY - 5 && x < tempX + 5) {
            if (opArr[x][y]) {
                count++;
                x++;
                y--;
                if (y >= 0 && x <= GAME_UNITS) {
                    continue;
                }
            }
            x = tempX - 1;
            y = tempY + 1;
            break;

        }

        while (x >= 0 && y >= 0 && x <= GAME_UNITS && y <= GAME_UNITS
                && y <= tempY + 5 && x >= tempX - 5) {
            if (opArr[x][y]) {
                count++;
                y++;
                x--;
                continue;
            }
            break;

        }
        if (count >= 5) {
            win = true;
            return tip;
        }

        return null;
    }

    public void regretChess() {
        if (win) {
            JOptionPane.showMessageDialog(null, "游戏结束无法悔棋!");
            return;
        }
        if (opStack.isEmpty()) {
            JOptionPane.showMessageDialog(null, "无效操作,已无上一步棋!");
            return;
        }
        Point point = opStack.pop();
        if (Objects.isNull(point)) {
            return;
        }
        if (op) {
            black[(int) point.getX() / UNIT_SIZE][(int) point.getY() / UNIT_SIZE] = false;
        } else {
            white[(int) point.getX() / UNIT_SIZE][(int) point.getY() / UNIT_SIZE] = false;
        }
        op = !op;
        repaint();
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        this.g = g;

        initCheckerboard();
        drawChessPieces();
    }

    /**
     * 自定义鼠标适配器
     */
    public class MyMouseAdapter extends MouseAdapter {
        @Override
        public void mouseClicked(MouseEvent e) {
            super.mouseClicked(e);
            checkPoint = e.getPoint();
            System.out.println("点击坐标x:" + checkPoint.getX() + " y:" + checkPoint.getY());
            checkPoint.setLocation(calibration(checkPoint.getX()), calibration(checkPoint.getY()));
            System.out.println("校准坐标x:" + checkPoint.getX() + " y:" + checkPoint.getY());

            //去除无效点击
            if (black[(int) checkPoint.getX() / UNIT_SIZE][(int) checkPoint.getY() / UNIT_SIZE]
                    || white[(int) checkPoint.getX() / UNIT_SIZE][(int) checkPoint.getY() / UNIT_SIZE]) {
                JOptionPane.showMessageDialog(null, "无效操作,\n此处已有棋子!");
                return;
            }

            opStack.push(checkPoint);
            repaint();
        }
    }

    /**
     * 自定义按键适配器
     */
    public class MyKeyAdapter extends KeyAdapter {
        public void keyPressed(KeyEvent e) {
            int keyCode = e.getKeyCode();
            if (KeyEvent.VK_1 == keyCode) {
                System.out.println("重新开始");
                init();
            }
            if (KeyEvent.VK_2 == keyCode) {
                System.out.println("悔棋");
                regretChess();
            }
            if (KeyEvent.VK_ESCAPE == keyCode) {
                System.out.println("退出游戏");
                System.exit(0);
            }
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setTitle("五子棋");
        Gobang gobang = new Gobang();
        frame.setJMenuBar(gobang.menuBar);
        frame.add(gobang);
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

}

效果展示

相关推荐
小灰灰__8 分钟前
IDEA加载通义灵码插件及使用指南
java·ide·intellij-idea
夜雨翦春韭12 分钟前
Java中的动态代理
java·开发语言·aop·动态代理
程序媛小果32 分钟前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
追风林37 分钟前
mac m1 docker本地部署canal 监听mysql的binglog日志
java·docker·mac
芒果披萨1 小时前
El表达式和JSTL
java·el
duration~2 小时前
Maven随笔
java·maven
zmgst2 小时前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql
跃ZHD2 小时前
前后端分离,Jackson,Long精度丢失
java
blammmp2 小时前
Java:数据结构-枚举
java·开发语言·数据结构
暗黑起源喵2 小时前
设计模式-工厂设计模式
java·开发语言·设计模式