简易五子棋

简介

使用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 blackboolean 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() {@Overridepublic 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() {@Overridepublic void actionPerformed(ActionEvent e) {regretChess();}});menu.add(regretChess);JMenuItem exit;(exit = new JMenuItem("退出游戏")).addActionListener(new ActionListener() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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 {@Overridepublic 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.mzph.cn/diannao/27706.shtml

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

相关文章

2024面试题【vue2】

1.关于生命周期 1.1生命周期有哪些&#xff1f;发送请求是在created还是mounted beforeCreate&#xff1a;创建之前&#xff08;el、data和message都还是undefined,不可用的&#xff09; created&#xff1a;创建完毕&#xff08;能读取到数据data的值,但是DOM还没生成&#x…

Web前端轮播结束:技术揭秘与用户体验挑战

Web前端轮播结束&#xff1a;技术揭秘与用户体验挑战 在Web前端开发中&#xff0c;轮播图作为一种常见的页面元素&#xff0c;承载着展示图片、传递信息的重要功能。然而&#xff0c;当轮播结束时&#xff0c;如何处理这一状态&#xff0c;却是一个既具挑战又充满困惑的问题。…

一分钟制作爆火的治愈插画,让插画来替你说出心声

超火的AI治愈插画来了&#xff0c;有趣的文字搭配上有趣的图&#xff0c;无论是发朋友圈还是发小红书&#xff0c;这效果简直无敌。 下面是我刚生成的&#xff0c;快来看看效果吧。 这个工具&#xff0c;国内可用&#xff0c;可以直接上手&#xff0c;不需要什么技术&#xff0…

C# Winform 侧边栏,切换不同页面

在项目中我们经常遇到需要在主界面上切换不同子页面的需求&#xff0c;常用做法是左侧显示子页面菜单&#xff0c;用户通过点击左侧菜单&#xff0c;实现右边子页面的展示。 实例项目实现&#xff1a; 项目左侧侧边栏实现FlowLayoutPanel使用显示不同子窗体 实例链接&#xf…

全网首发:教你如何直接用4090玩转最新开源的stablediffusion3.0

1.stablediffusion的概述&#xff1a; Stable Diffusion&#xff08;简称SD&#xff09;近期的动态确实不多&#xff0c;但最新的发展无疑令人瞩目。StableCascade、Playground V2.5和Stableforge虽然带来了一些更新&#xff0c;但它们在SD3面前似乎略显黯然。就在昨晚&#x…

04.VisionMaster 机器视觉找圆工具

VisionMaster 机器视觉找圆工具 定义 先检测出多个边缘点然后拟合成圆形&#xff0c;可用于圆的定位与测量 注意&#xff1a;找圆工具 最好和【位置修正】模块一起使用。具体可以看下面的示例。 参数说明&#xff1a; 扇环半径&#xff1a;圆环ROI的内外圆半径 边缘类型&a…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] K小姐的服务交换接口失败率分析 (100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 📎在线评测链接 K小姐的服务交换接口失败率分析(100分) 🌍 评测功能需要订…

基于.Net 框架实现WebSocket 简单通信——服务端

新建项目 创建一个.Net 框架的控制台程序。 添加包 项目 → 管理 NuGet 程序包打开包管理窗口&#xff0c;添加SuperWebSocket 程序包。 实现 项目 → 添加类打开添加新项窗口&#xff0c;添加一个C#类。 启动监听 WebSocketServer socket new WebSocketServer();Conso…

Web前端教学实录:深入剖析前端技术的奥秘

Web前端教学实录&#xff1a;深入剖析前端技术的奥秘 在数字化时代&#xff0c;Web前端技术已成为构建现代化网站和应用程序不可或缺的一环。然而&#xff0c;对于初学者来说&#xff0c;前端技术的复杂性和多样性往往令人望而生畏。为了帮助广大学习者更好地掌握Web前端技术&…

PyTorch 数学运算-Tensor基本操作

用如下 a b 进行运算演示 >>> a tensor([[0.7967, 0.5056, 0.7963],[0.8603, 0.7029, 0.7590]]) >>> b tensor([[0.6923, 0.0411, 0.8713],[0.0483, 0.2948, 0.3286]])一般加减乘除运算&#xff1a; add/mimus/multiply/divide >>…

【github】项目的代码仓库重命名

问题 有时候&#xff0c;我们先创建了远端项目仓库&#xff0c;然后就把相关code上传到远端项目仓库。 可能需要结合实际情况对远端项目仓库进行重命名。 当前仓库名称v_ttc&#xff0c;如何将他修改成v_datejs 操作步骤 1、在 GitHub.com 上&#xff0c;导航到存储库的主页…

数据库的字符集和校对规则

数据库的字符集和校对规则是数据库管理系统&#xff08;DBMS&#xff09;中处理字符数据的重要组成部分。以下是对数据库字符集和校对规则的详细解释&#xff1a; 字符集&#xff08;Character Set&#xff09; 1. 定义 字符集是一套用于表示文本字符的编码集合。它规定了如…

(金融:货币兑换)编写一个程序,提示用户输入从美元到人民币的兑换汇率。

&#xff08;金融:货币兑换)编写一个程序&#xff0c;提示用户输入从美元到人民币的兑换汇率。提示用户输入0表示从美元兑换为人民币&#xff0c;输入1表示从人民币兑换为美元。继而提示用户输入美元数量或者人民币数量&#xff0c;分别兑换为另外一种货币。下面是运行示例: pa…

Nginx之Stream(TCP/UDP)负载均衡

Nginx 的 TCP/UDP 负载均衡是应用 Stream 代理模块&#xff08;ngx_stream_proxy_module&#xff09;和 Stream 上游模块&#xff08;ngx_stream_upstream_module&#xff09;实现的。Nginx 的 TCP 负载均衡与 LVS 都是四层负载均衡的应用&#xff0c;所不同的是&#xff0c;LV…

TCP/IP协议深入解析,初学者必看!

简介 在信息技术飞速发展的今天&#xff0c;网络已成为人类社会不可或缺的部分。实现网络中计算机相互通信的关键之一便是TCP/IP协议。作为互联网的基础&#xff0c;TCP/IP协议确保了全球范围内的数据交换和信息共享。 TCP/IP&#xff08;传输控制协议/网际协议&#xff09;是…

LeetCode 2813.子序列最大优雅度

给你一个长度为 n 的二维整数数组 items 和一个整数 k 。 items[i] [profiti, categoryi]&#xff0c;其中 profiti 和 categoryi 分别表示第 i 个项目的利润和类别。 现定义 items 的 子序列 的 优雅度 可以用 total_profit distinct_categories^2 计算&#xff0c;其中 t…

Linux Mint 21.3简介

Linux Mint 21.3是一个更新版本&#xff0c;其中包含了许多新特性和改进。以下是一些主要更新内容&#xff1a; 1. Cinnamon 6.0桌面环境&#xff1a;Linux Mint 21.3采用了最新的Cinnamon 6.0桌面环境&#xff0c;带来了新的功能和改进&#xff0c;例如支持Wayland会话&#…

通勤路上的美好伴侣:倍思H1s头戴式蓝牙耳机

在繁忙的都市生活中,通勤往往占据了人们大量的时间。而在这个过程中,无尽的嘈杂声——公交车的播报声、地铁的轰鸣声、街头的喧嚣——往往成为我们心情的干扰源。在这样的环境下,一款优质的头戴式蓝牙耳机,会让我们的通勤之旅变得更加愉快和舒适。 通勤路上要更舒适—— 倍思…

道可云元宇宙每日资讯|微软称GPT的重点将转向商业和企业场景

道可云元宇宙每日简报&#xff08;2024年6月14日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 微软称GPT的重点将转向商业和企业场景 近日&#xff0c;微软于官网宣布将于2024年7月10日起停止其AI工具Copilot GPTs服务&#xff0c;同时将删除所有由用户创建的既…

LDR6023S:革新USB Type-C接口的完美伴侣

一、引言 随着科技的发展&#xff0c;USB Type-C接口以其高速传输、正反插等特性逐渐取代了传统的USB接口。而在这一背景下&#xff0c;LDR6023S作为一款USB Type-C转音频快充芯片&#xff0c;凭借其卓越的性能和广泛的应用场景&#xff0c;成为了市场上备受瞩目的产品。本文将…