简易五子棋

简介

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

}

效果展示

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript-函数

学习目标&#xff1a; 掌握函数 学习内容&#xff1a; 为什么需要函数函数使用函数传参函数返回值函数细节补充函数作用域匿名函数案例 为什么需要函数&#xff1a; 函数&#xff1a;function 是被设计为执行特定任务的代码块。说明&#xff1a;函数可以把具有相同或相似逻辑…

探索交互的本质:从指令到界面的演进与Linux基础指令的深入剖析

目录 1.指令 vs 界面//选读 1.1交互的需求 满足需求的第一阶段-指令 满足需求的第二阶段-界面 1.2 指令 和 界面交互 区别 2.操作系统介绍 2.1 举例说明 驱动软件层 2.2 为什么要有操作系统&#xff1f; 0x03 为什么要进行指令操作&#xff1f; 3.Linux基本指令 l…

Linux基础命令[29]-chown

文章目录 1. chown 命令说明2. chown 命令语法3. chown 命令示例3.1 修改属主3.2 修改属组3.3 修改属主和属组3.4 修改文件夹所属 4. 总结 1. chown 命令说明 chown&#xff1a;更改文件的用户或用户组&#xff0c;需要 root 用户或 sudo 权限的用户执行该命令。基本信息如下&…

深度学习(PyTorch)批注理解,建议边学可以边看这个笔记

前言 动手学习深度学习&#xff0c;内容丰富&#xff0c;但是对于初学者有很多晦涩难懂的地方&#xff0c;我将日常更新这篇文章以截图的形式&#xff0c;每天高强度学习四五个小时&#xff0c;精力缺乏&#xff0c;我认为&#xff0c;如果想学习这个深度学习&#xff0c;你需…

微信公众号打通与登录的实现

今天实现一下与微信公众号进行对接&#xff0c;通过扫描二维码的方式来进行注册与登录&#xff0c;获取用户的微信唯一标识作为用户的username&#xff0c;下面我们开始编写。 骨架建立&#xff1a; 建包&#xff1a; 第一步还是先将骨架建好&#xff0c;与网关骨架差不多&a…

堆栈溢出的攻击 -fno-stack-protector stack smash 检测

在程序返回的一条语句堆栈项目处&#xff0c;用新函数的起始地址覆盖&#xff0c;将会跳转到执行新函数。 现在系统对这个行为做了判断&#xff0c;已经无法实施这类攻击或技巧。 1&#xff0c;测试代码 #include <stdio.h> void cc() {printf("I am cc( )\n"…

Boom 3D软件下载及安装教程

简介&#xff1a; Boom 3D是适用于Mac和Windows系统的专业音效增强软件&#xff0c;旨在通过播放器&#xff0c;媒体或流媒体服务等介质&#xff0c;在不同类型的耳机上以3D环绕效果播放媒体内容。您无需使用昂贵的耳机或其他附加环绕音效增强器即可感受3D环绕音乐。 安 装 包…

【Python推导式秘籍】:一行代码的艺术,高效数据处理之道

文章目录 &#x1f68b;Python推导式&#x1f680;一、列表推导式&#x1f308;1. 了解推导式❤️2. 实践&#x1f4a5;3. 总结 &#x1f680;二、字典推导式&#x1f308;1. 了解字典推导式❤️2. 实践&#x1f4a5;3. 总结 &#x1f680;三、集合推导式&#x1f308;1. 了解集…

liunx常见指令

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 二、安装环境 1.租借服务器 2.下载安装 XShell 3.使用xshll登录服务器 三、Linux基础命令 一、文件和命令 ​编辑1、cd 命令 2、pwd 命令 3、ls 命令 4、cp 命令 …

人工智能GPU互联技术分析,芯片巨头UALink向英伟达NVLink开战

芯片巨头组团&#xff0c;向英伟达NVLink开战 八大科技巨头——AMD、博通、思科、Google、惠普企业、英特尔、Meta及微软——联合推出UALink&#xff08;Ultra Accelerator Link&#xff09;技术&#xff0c;为人工智能数据中心网络设定全新互联标准。此举旨在打破Nvidia的市场…

响应式德米拉数字内容交易系统素材下载站模板

★模板说明★ 该数字交易系统设计非常完美&#xff0c;两种响应式模式&#xff0c;可打开边栏模式和盒子模式&#xff1b;八种网站颜色&#xff0c;四种风格颜色可供用户自行选择&#xff0c;还可在网站选背景图片&#xff1b;完美的分成系统、充值功能、个人中心等等都以html…

企业网站建设方案

企业网站建设方案是企业推广和宣传的重要工具&#xff0c;可以帮助企业树立良好的形象&#xff0c;吸引更多的客户和合作伙伴。一个好的企业网站应该具备用户友好的界面设计、快速的加载速度、完善的信息分类和搜索功能、优质的内容和多样化的互动体验。下面将从以下几个方面介…

基于51单片机太阳能热水器设计

基于51单片机太阳能热水器 &#xff08;仿真&#xff0b;程序&#xff09; 功能介绍 具体功能&#xff1a; 1.LCD1602显示屏第一行显示温度&#xff0c;第二行显示温度下限&#xff1b; 2.按键可以设置温度的下限&#xff0c;控制出水&#xff1b; 3.当温度低于设置下限值…

大数据实训项目(小麦种子)-03、大数据环境Hadoop、Mapreduce、Hive、Hbase、HDFS搭建服务及调试

文章目录 前言一、Linux系统Centos7安装配置JDK8二、Linxu系统Centos7中搭建Hadoop3.1.0服务下载地址服务1&#xff1a;详细步骤&#xff08;初始化与启动dfs服务&#xff09;详细步骤配置环境变量 服务2&#xff1a;Hadoop(YARN)环境搭建 三、Linux系统搭建Hive3.1.2服务前提条…

大数据实训项目(小麦种子)-04、大数据实训项目JavaWeb环境搭建

文章目录 前言运行前准备工作1、安装Hadoop3.1.0配置winutils原因描述配置方式注意点&#xff08;hadoop.dll拷贝System32目录下&#xff09; 2、hive运行报错&#xff08;The dir: /tmp/hive on HDFS should be writable. &#xff09; 项目环境搭建参考资料 前言 博主介绍&a…

windows 共享给linux 的使用方法

windows 作为服务器&#xff0c;linux作为客户端进行文件共享&#xff0c;有3种方法&#xff1a;samba nfs&#xff08;网络硬盘&#xff09;虚拟机共享&#xff08;VirtualBox vboxsf&#xff09;。 Samba 共享&#xff1a; 打开【控制面板】-->【启动或关闭windows功能】…

STM32定时器篇——Systick定时器的使用(实现delay延时函数)

一、Systick定时器的简介&#xff1a; Systick定时器就是系统滴答定时器&#xff0c;一个24 位的倒计数定时器对于CM3,CM4内核芯片&#xff0c;都有Systick定时器。当Systick计到0时&#xff0c;将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中…

LIMS(实验室)信息管理系统源码:系统构架组成与功能实现

LIMS&#xff08;实验室&#xff09;信息管理系统源码&#xff1a;系统构架组成与功能实现 采用先进的计算机网络技术、数据库技术和标准化的实验室管理思想&#xff0c;组成一个全面、规范的管理体系&#xff0c;为实现分析数据网上调度、分析数据自动采集、快速分布、信息共…

海外仓标签管理策略:海外仓系统标签管理设置方法与注意事项

通常来说&#xff0c;一个海外仓每天都会有成千上万的货物进出。只有应用了合适的标签管理策略&#xff0c;才能让这些货物有序&#xff0c;高效的流转。 你就可以随时掌握货物的位置和移动情况&#xff0c;更好的了解库存信息。标签的应用也有利于合理的规划仓库空间&#xf…

甘肃这款饼子很火 你是否有吃过呢

白吉饼那独特的外形&#xff0c;圆圆的十分可爱。&#x1f44f;它的表皮酥脆&#xff0c;内里绵软&#xff0c;麦香四溢。&#x1f60b;拿在手里沉甸甸的&#xff0c;就知道用料十足。 无论是直接吃&#xff0c;感受那纯粹的面香&#xff0c;还是夹上腊汁肉&#xff0c;变成美味…