简易五子棋

news2025/6/20 8:17:46

简介

使用Java实现简易五子棋

规则介绍

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

(1)对局双方各执一色棋子。
(2)空棋盘开局。
(3)黑先、白后,交替下子,每次只能下一子。
(4)棋子下在棋盘的空白点上,棋子下定后不得移动或拿走。
(5)黑方的第一枚棋子必须下在天元点上,即中心交叉点"

功能设计

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

实现

附上如下实现代码

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);
    }

}

效果展示

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1823754.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BSCI验厂是什么?

BSCI验厂是指Business Social Compliance Initiative&#xff0c;即倡议商界遵守社会责任组织对BSCI组织成员的全球供应商进行的社会责任审核。以下是对BSCI验厂的详细介绍&#xff1a; 背景介绍&#xff1a; 2003年3月&#xff0c;总部设在比利时布鲁塞尔的对外贸易协会FTA正式…

618网络机顶盒哪个牌子好?内行盘点网络机顶盒排行榜

因工作原因每天都在跟各种类型的网络机顶盒打交道&#xff0c;最近超多朋友问我网络机顶盒哪个牌子好&#xff0c;不知道如何挑选网络机顶盒&#xff0c;我将要分享目前最值得入手的网络机顶盒排行榜&#xff0c;想买网络机顶盒可以看看以下这些品牌&#xff1a; ★泰捷WEBOX 6…

在ubuntu16中下载VMware Tools工具

一、打开植入 二、开始安装 打开驱动放置的默认位置 在这里打开终端&#xff1b;添加到/home/你的用户名/Downloand/中 进入后解压 然后进去解压后的文件 终端输入 sudo ./vmware-install.pl 开始安装前的配置&#xff08;很麻烦跟着输就行&#xff09; 继续 出现如上…

机器学习笔记:focal loss

1 介绍 Focal Loss 是一种在类别不平衡的情况下改善模型性能的损失函数最初在 2017 年的论文《Focal Loss for Dense Object Detection》中提出这种损失函数主要用于解决在有挑战性的对象检测任务中&#xff0c;易分类的负样本占据主导地位的问题&#xff0c;从而导致模型难以…

蓝牙耳机怎么连接电脑?轻松实现无线连接

蓝牙耳机已经成为许多人生活中不可或缺的一部分&#xff0c;不仅可以方便地连接手机&#xff0c;还能轻松连接电脑&#xff0c;让我们在工作和娱乐时享受无线的自由。然而&#xff0c;对于一些用户来说&#xff0c;将蓝牙耳机与电脑连接可能会遇到一些问题。本文将介绍蓝牙耳机…

JAVA面试常见面试问题02

1、jvm 内存结构图 对jvm直观的了解 答&#xff1a; jvm是一个抽象执行环境&#xff0c;它通过加载字节码、实现跨平台运行、管理内存&#xff08;包括自动垃圾回收&#xff09;、优化代码执行等&#xff0c;为Java程序提供运行时支持 2、内存泄漏 和 内存溢出 答&#xf…

错题记录(小测)

单选 错题1 错题2 错题3 代码题 反转链表 链表的回文结构

C++语法08 数据类型之间的强制转换

目录 强制类型转换 强制类型转换格式 整型转换成浮点型 整型转换成浮点型其他写法 训练&#xff1a;糖果奖励 糖果奖励参考代码 浮点型转换成整型 浮点型转换成整型其他写法 训练&#xff1a;分离小数 分离小数参考代码 强制类型转换 强制类型转换&#xff0c;就是把…

碳中和研究院OLED透明屏2x2整机项目方案

一、项目背景 随着全球气候变化和环境问题的日益严重&#xff0c;碳中和成为各国政府和企业的重要议题。为了响应这一趋势&#xff0c;黑龙江碳中和研究院计划引入先进的OLED透明屏技术&#xff0c;以展示其研究成果和碳中和理念。本项目旨在为该研究院提供一套高质量的OLED透明…

域策略笔记

域策略 导航 文章目录 域策略导航一、设置客户端壁纸二、重定向用户配置文件路径三、部署网络打印机四、部署共享文件夹为网络驱动器五、通过域策略推送软件安装六、通过域策略限制软件的使用通过路径进行限制通过进程限制 七、通过域策略将文件添加白名单八、通过域策略添加可…

什么是 Solana 节点?

什么是 Solana 节点&#xff1f; 验证交易和维护安全是任何项目进入权益证明领域时必须执行的强制性活动。如果没有节点运营商&#xff08;验证器和 RPC 节点&#xff09;&#xff0c;Solana 网络就无法实现这些目标。 让我们看看这两种类型的节点有何区别。 共识验证者 角色&…

GenICam标准(三)

系列文章目录 GenICam标准&#xff08;一&#xff09; GenICam标准&#xff08;二&#xff09; GenICam标准&#xff08;三&#xff09; 文章目录 系列文章目录6、缓存7、识别并判断相机描述文件的版本7.1. 格式的版本7.2. 相机描述文件的版本7.3. 识别并缓存相机描述文件 参考…

2024年6月14日 (周五) 叶子游戏新闻

期刊杂志: 聚合读者、意林、知音、故事会、花火以及国内各大知名报纸电子版&#xff0c;无需付费即可观看各种免费资源 WPS免登录一键修改器: 去除烦人的登录且能正常使用 EA招募退伍军人重塑下一代《战地》游戏EA正通过“雇用我们的英雄”计划&#xff08;HOH&#xff09;雇用…

OpenAI函数调用:使用Assistants API函数工具的一个示例

Are you looking to expand GPTs capabilities? Check out this tutorial for a complete example of an AI Assistant that can send emails whenever we ask it to. 您是否希望扩展GPT的功能&#xff1f;查看这个教程&#xff0c;它提供了一个完整的示例&#xff0c;展示了…

多模态革新:Ferret-v2在高分辨率图像理解与细粒度交互的突破

在多模态大模型&#xff08;MLLMs&#xff09;的研究中&#xff0c;如何将视觉理解能力与语言模型相结合&#xff0c;以实现更精细的区域描述和推理&#xff0c;是一个重要的研究方向。先前的工作如Ferret模型&#xff0c;通过整合区域理解能力&#xff0c;提升了模型在对话中的…

开源大模型的新星:ChatGPT-Next-Web 项目解析与推荐

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

蓝卓为中小制造企业注入数字化转型活力

随着劳动力成本上升,原材料价格上涨,企业生产成本逐年增加&#xff0c;市场竞争越来越激烈&#xff0c;传统的中小制造企业面临着巨大的压力。 通过数字化转型应对环境的变化已成为行业共识&#xff0c;在数字化的进程中&#xff0c;中小企业首要考虑生存问题&#xff0c;不能…

Sklearn中逻辑回归建模

分类模型的评估 回归模型的评估方法&#xff0c;主要有均方误差MSE&#xff0c;R方得分等指标&#xff0c;在分类模型中&#xff0c;我们主要应用的是准确率这个评估指标&#xff0c;除此之外&#xff0c;常用的二分类模型的模型评估指标还有召回率&#xff08;Recall&#xff…

串口屏介绍

一、串口屏简介 串口屏&#xff08;Serial LCD/Serial TFT Display&#xff09;是一种集成了串行通讯功能的显示屏&#xff0c;广泛应用于各种嵌入式系统、工业控制、人机界面&#xff08;HMI&#xff09;等领域。该显示屏通过串口&#xff08;如UART、RS232、RS485等&#xf…

记录一下PHP使用微信小程序支付

记录一下PHP使用微信小程序支付V3版本经历 官方文档&#xff1a;https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_0.shtml 请详细查看文档中小程序支付接入前准备&#xff08;https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml&#xff…