Java Swing实现五子棋程序
首先感谢08年MLDN出的这个培训视频,我把代码和文档整理了一下,发布出来给需要学习Swing的朋友。
源码地址:
https://gitee.com/indexman/gobang
一、知识点准备:
JFrame:窗体
JOptionPane:对话框
MouseListener:鼠标事件
Graphics:绘制二维图像
Thread:线程类
二、五子棋游戏的功能:
- 在点击鼠标时,可以在相应的位置显示棋子。
- 可以自动判断游戏是否结束,是否黑方或白方已经胜利
- 对游戏时间进行设置,判断是否超出规定时间
三、游戏开发步骤
1.开发游戏界面
计算棋盘中每一条线的间距:这里用的是19X19的围棋棋盘,
总宽度为360px, 分成18份,每份是20px
2.在棋盘上的鼠标点击位置,显示一个棋子
黑子:一个实心的黑圆表示
白子:一个空心的黑圆+一个实心的白圆表示
repaint() 方法表示重新执行一次paint()方法。
3.保存之前下过的棋子
通过一个二维数组来保存之前下过的所有棋子。
4.判断游戏胜负
依据 五子棋 的基本游戏规则,判断是否有同一颜色的棋子连城5个。
完成五子棋游戏的核心算法
这里可以把核心算法总结成一个灵活的方法。
提示信息的保存
5.处理屏幕闪烁问题
使用双缓冲技术
6.实现各个按钮的功能
开始游戏:重新开始游戏
游戏设置: 设置倒计时时间
游戏说明:用来说明游戏规则和操作
认输:某一方放弃游戏,投子认负
关于:显示游戏制作者
退出:退出程序
-----------------------------------------------------分割线------------------------------------------------------
四、代码部分
1.启动类
package com.laoxu.game;
import com.laoxu.game.view.GobangFrame;
import com.laoxu.game.view.MyFrame;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
GobangFrame frame = new GobangFrame();
//MyFrame myFrame = new MyFrame();
//JOptionPane
/*JOptionPane.showMessageDialog(myFrame,"我的信息");
int result = JOptionPane.showConfirmDialog(myFrame,"确定要开始游戏吗?","",0);
if(result==0){
JOptionPane.showMessageDialog(myFrame,"游戏开始");
}
if(result==1){
JOptionPane.showMessageDialog(myFrame,"游戏结束");
}
if(result==2){
JOptionPane.showMessageDialog(myFrame,"重新选择");
}
String username = JOptionPane.showInputDialog("请输入你的姓名:");
System.out.println(username);
JOptionPane.showMessageDialog(myFrame,"输入的姓名为:"+username);*/
//System.out.println(System.getProperty("user.dir")+"/src/image/background.jpg");
}
}
2.五子棋类
package com.laoxu.game.view;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
* @description: 五子棋主界面
* @author: luohanye
* @create: 2019-03-21
**/
public class GobangFrame extends JFrame implements MouseListener,Runnable {
//获取当前平面分辨率,例如:1920x1080
int swidth = Toolkit.getDefaultToolkit().getScreenSize().width;
int sheight = Toolkit.getDefaultToolkit().getScreenSize().height;
BufferedImage image = null;
//保存坐标值
int x = 0;
int y = 0;
//保存下过的棋子 0-没有棋子 1-黑子 2-白子
int[][] allChess = new int[19][19];
// 标识当前应该是黑子还是白子
boolean isBlack = true;
// 控制游戏是否可以玩
boolean canPlay = true;
// 保存游戏信息
String message = "黑方先行";
// 保存最多拥有多少时间(秒)
int maxTime = 0;
// 做倒计时的线程类
Thread t = new Thread(this);
// 保存黑方与白方的剩余时间
int blackTime = 0;
int whiteTime = 0;
// 保存双方剩余时间的显示信息
String blackMessage = "无限制";
String whiteMessage = "无限制";
public GobangFrame() {
//设置标题
this.setTitle("五子棋");
int width = 500, height = 500;
this.setSize(width, height);
//设置窗体出现的位置
this.setLocation((swidth - width) / 2, (sheight - height) / 2);
//窗口大小不可变
this.setResizable(false);
//定义关闭动作
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//添加监听器
this.addMouseListener(this);
//窗体可见
this.setVisible(true);
//加载背景图片
try {
image = ImageIO.read(new File(System.getProperty("user.dir")+"/src/image/background.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
// 启动线程
t.start();
t.suspend();
// 刷新屏幕,防止开始游戏时出现无法显示的情况.
this.repaint();
}
public void paint(Graphics g) {
//双缓冲技术防止屏幕闪烁
BufferedImage bi = new BufferedImage(500,500,BufferedImage.TYPE_INT_RGB);
Graphics g2 = bi.createGraphics();
g2.setColor(Color.BLACK);
//绘制背景
g2.drawImage(image, 0, 20, this);
g2.setFont(new Font("黑体", Font.BOLD, 20));
g2.drawString("游戏信息:"+message, 130, 60);
//输出时间信息
g2.setFont(new Font("宋体", 0, 14));
g2.drawString("黑方时间:" + blackMessage, 30, 470);
g2.drawString("白方时间:" + whiteMessage, 260, 470);
//绘制棋盘
/**
* X=10 Y=70
* X=370 Y=430
* X=370 Y=70
* X=10 Y=430
*/
for (int i = 0; i < 19; i++) {
g2.drawLine(10, 70 + 20 * i, 370, 70 + 20 * i);
g2.drawLine(10 + 20 * i, 70, 10 + 20 * i, 430);
}
//标注点位
g2.fillOval(68, 128, 4, 4);
g2.fillOval(308, 128, 4, 4);
g2.fillOval(308, 368, 4, 4);
g2.fillOval(68, 368, 4, 4);
g2.fillOval(68, 248, 4, 4);
g2.fillOval(308, 248, 4, 4);
g2.fillOval(188, 128, 4, 4);
g2.fillOval(188, 368, 4, 4);
g2.fillOval(188, 248, 4, 4);
//绘制棋子
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
//黑子
if (allChess[i][j] == 1) {
int tempX = i * 20 + 10;
int tempY = j * 20 + 70;
g2.fillOval(tempX - 7, tempY - 7, 14, 14);
}
//白子
if (allChess[i][j] == 2) {
int tempX = i * 20 + 10;
int tempY = j * 20 + 70;
g2.setColor(Color.WHITE);
g2.fillOval(tempX - 7, tempY - 7, 14, 14);
g2.setColor(Color.BLACK);
g2.drawOval(tempX - 7, tempY - 7, 14, 14);
}
}
}
g.drawImage(bi,0,0,this);
}
//鼠标按键在组件上按下时调用。
@Override
public void mousePressed(MouseEvent e) {
System.out.println("X=" + e.getX() + " Y=" + e.getY());
if (canPlay) {
x = e.getX();
y = e.getY();
//判断点击是否在棋盘内
if (x >= 10 && x <= 370 && y >= 70 && y <= 430) {
x = (x - 10) / 20;
y = (y - 70) / 20;
if (allChess[x][y] == 0) {
if (isBlack) {
allChess[x][y] = 1;
isBlack = false;
message="轮到白方";
} else {
allChess[x][y] = 2;
isBlack = true;
message="轮到黑方";
}
//判断游戏是否结束
boolean isWin = this.checkWin();
if (isWin) {
JOptionPane.showMessageDialog(this, "游戏结束,"
+ (allChess[x][y] == 1 ? "黑方" : "白方") + "获胜!");
canPlay = false;
}
}
this.repaint();
}
}
// 点击 【开始游戏】 按钮
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=70 && e.getY()<=100){
int result = JOptionPane.showConfirmDialog(this,"是否重新开始游戏?","",0);
if(result==0){
//1.清空棋盘
allChess = new int[19][19];
//另一种方式:
/*for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
allChess[i][j] = 0;
}
}*/
//2.重置游戏信息
message = "黑方先行";
//3.将下一步下棋的人改为黑方
isBlack = true;
//4.可以游戏标志改为true
canPlay = true;
//5.重置黑白双方时间限制
blackTime = maxTime;
whiteTime = maxTime;
if (maxTime > 0) {
blackMessage = maxTime / 3600 + ":"
+ (maxTime / 60 - maxTime / 3600 * 60) + ":"
+ (maxTime - maxTime / 60 * 60);
whiteMessage = maxTime / 3600 + ":"
+ (maxTime / 60 - maxTime / 3600 * 60) + ":"
+ (maxTime - maxTime / 60 * 60);
t.resume();
} else {
blackMessage = "无限制";
whiteMessage = "无限制";
}
//6.重新绘制窗体
this.repaint();
}
}
//点击 【游戏设置】 按钮
if (e.getX() >= 400 && e.getX() <= 470 && e.getY() >= 120
&& e.getY() <= 150) {
String input = JOptionPane
.showInputDialog("请输入游戏的最大时间(单位:分钟),如果输入0,表示没有时间限制:");
try {
maxTime = Integer.parseInt(input) * 60;
if (maxTime < 0) {
JOptionPane.showMessageDialog(this, "请输入正确信息,不允许输入负数!");
}
if (maxTime == 0) {
int result = JOptionPane.showConfirmDialog(this,
"设置完成,是否重新开始游戏?");
if (result == 0) {
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
allChess[i][j] = 0;
}
}
// 另一种方式 allChess = new int[19][19];
message = "黑方先行";
isBlack = true;
blackTime = maxTime;
whiteTime = maxTime;
blackMessage = "无限制";
whiteMessage = "无限制";
this.canPlay = true;
this.repaint();
}
}
if (maxTime > 0) {
int result = JOptionPane.showConfirmDialog(this,
"设置完成,是否重新开始游戏?");
if (result == 0) {
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
allChess[i][j] = 0;
}
}
// 另一种方式 allChess = new int[19][19];
message = "黑方先行";
isBlack = true;
blackTime = maxTime;
whiteTime = maxTime;
blackMessage = maxTime / 3600 + ":"
+ (maxTime / 60 - maxTime / 3600 * 60) + ":"
+ (maxTime - maxTime / 60 * 60);
whiteMessage = maxTime / 3600 + ":"
+ (maxTime / 60 - maxTime / 3600 * 60) + ":"
+ (maxTime - maxTime / 60 * 60);
t.resume();
this.canPlay = true;
this.repaint();
}
}
} catch (NumberFormatException e1) {
JOptionPane.showMessageDialog(this, "请正确输入信息!");
}
}
//点击 【游戏说明】 按钮
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=170 && e.getY()<=200){
JOptionPane.showMessageDialog(this,"这是一个五子棋游戏程序,黑白双方轮流下棋,当某一方连到五子时游戏结束。");
}
//点击 【认输】 按钮
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=270 && e.getY()<=300){
int result = JOptionPane.showConfirmDialog(this,"是否确认认输?","",0);
if(result==0){
if(isBlack){
JOptionPane.showMessageDialog(this,"黑方已经认输,游戏结束!");
}else{
JOptionPane.showMessageDialog(this,"白方已经认输,游戏结束!");
}
//停止游戏
canPlay = false;
}
}
//点击 【关于】 按钮
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=320 && e.getY()<=350){
JOptionPane.showMessageDialog(this,"本游戏由老徐制作,有问题请联系老徐。");
}
//点击 【退出】 按钮
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=370 && e.getY()<=400){
JOptionPane.showMessageDialog(this,"游戏结束!");
System.exit(0);
}
}
private boolean checkWin() {
boolean flag = false;
//统计相连棋子数
//横向
int count = 1;
int color = allChess[x][y];
/*int i = 1;
while (color == allChess[x + i][y]) {
count++;
i++;
}
i = 1;
while (color == allChess[x - i][y]) {
count++;
i++;
}
if (count >= 5) {
flag = true;
}
//竖向
int count2 = 1;
int i2 = 1;
while (color == allChess[x][y+i2]) {
count2++;
i2++;
}
i2 = 1;
while (color == allChess[x][y-i2]) {
count2++;
i2++;
}
if (count2 >= 5) {
flag = true;
}
// 右上+左下
int count3 = 1;
int i3 = 1;
while (color == allChess[x+i3][y-i3]) {
count3++;
i3++;
}
i3 = 1;
while (color == allChess[x-i3][y+i3]) {
count3++;
i3++;
}
if (count3 >= 5) {
flag = true;
}
// 左上+右下
int count4 = 1;
int i4 = 1;
while (color == allChess[x-i4][y-i4]) {
count4++;
i4++;
}
i4 = 1;
while (color == allChess[x+i4][y+i4]) {
count4++;
i4++;
}
if (count4 >= 5) {
flag = true;
}
*/ //判断横向
count = this.checkCount(1,0,color);
if(count>=5){
flag = true;
}else {
//判断纵向
count = this.checkCount(0,1,color);
if(count>=5){
flag = true;
}else {
//判断右上、左下
count = this.checkCount(1, -1, color);
if (count >= 5) {
flag = true;
} else {
//判断右下、左上
count = this.checkCount(1, 1, color);
if (count >= 5) {
flag = true;
}
}
}
}
return flag;
}
private int checkCount(int xChange, int yChange, int color){
int count = 1;
int tempX = xChange;
int tempY = yChange;
while ((x+xChange>=0 && x+xChange <= 18 && y+yChange>=0 && y+yChange <= 18 )&&color == allChess[x+xChange][y+yChange]){
count++;
if(xChange != 0){
xChange++;
}
if(yChange!=0){
if(yChange>0){
yChange++;
}else {
yChange--;
}
}
}
xChange = tempX;
yChange = tempY;
while ((x-xChange>=0 && x-xChange <= 18 && y-yChange>=0 && y-yChange <= 18 )&&color == allChess[x-xChange][y-yChange]){
count++;
if(xChange != 0){
xChange++;
}
if(yChange!=0){
if(yChange>0){
yChange++;
}else {
yChange--;
}
}
}
return count;
}
@Override
public void run() {
// 判断是否有时间限制
if (maxTime > 0) {
while (true) {
if (isBlack) {
blackTime--;
if (blackTime == 0) {
JOptionPane.showMessageDialog(this, "黑方超时,游戏结束!");
}
} else {
whiteTime--;
if (whiteTime == 0) {
JOptionPane.showMessageDialog(this, "白方超时,游戏结束!");
}
}
blackMessage = blackTime / 3600 + ":"
+ (blackTime / 60 - blackTime / 3600 * 60) + ":"
+ (blackTime - blackTime / 60 * 60);
whiteMessage = whiteTime / 3600 + ":"
+ (whiteTime / 60 - whiteTime / 3600 * 60) + ":"
+ (whiteTime - whiteTime / 60 * 60);
this.repaint();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(blackTime + " -- " + whiteTime);
}
}
}
// 鼠标单击事件
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
Java Swing实现五子棋程序的更多相关文章
- java swing 双人五子棋源代码
import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Toolkit; impo ...
- Java Swing 第01记 Hello Word
首先来一个Java Swing的HelloWord程序. package cn.java.swing.chapter03; import javax.swing.JButton; import jav ...
- Java Swing快速构建窗体应用程序
以前接触java感觉其在桌面开发上,总是不太方便,没有一个好的拖拽界面布局工具,可以快速构建窗体. 最近学习了一下NetBeans IDE 8.1,感觉其窗体设计工具还是很不错的 , 就尝试一下做了一 ...
- 解决 GTK+/GNOME 3 环境下 Java Swing 程序使用本地 GTK+ 主题时菜单无边框 bug 的方法
在 GTK+/GNOME 3 环境下采用默认的 Adwaita 主题时,Java Swing 程序如果使用本地 GTK+ 主题会出现菜单无边框的 bug,这个问题也可能在其他常用的 GTK+ 主题中出 ...
- Java swing项目-图书管理系统(swing+mysql+jdbc) 总结
(一)java Swing的学习. (1)学习如何安装windowbuilder插件的安装. <1>在eclipse中点击help <2>在help的下拉选中选择install ...
- Java Swing的进化
摘 要:Swing已是一个比较老的工具集了,在美观的用户界面出来之前需要开发很长时间.它缺少一些你在开发富UI时所需的组件.幸运地是,像 Substance,SwingX及Java Look-and_ ...
- Java Swing事件处理机制
Java Swing的事件处理机制 Swing GUI启动后,Java虚拟机就启动三个线程,分别为主线程,事件派发线程(也是事件处理线程)和系统工具包线程. 主线程 :负责创建并显示该程序的初始界面: ...
- Java Swing界面编程(1)
写多了jsp,对于页面式的系统已经写烦了,本人也開始着手于java swing的学习,作为菜鸟,仅想用博客记录下我的swing学习的历程.话不多说,首先開始我的第一个窗体化程序. 下面给出源码: pa ...
- GUI编程笔记(java)02:java.awt和java.swing包的区别
1. java.awt和java.swing两者的概述 java.awt:(java的标准包) Abstract Window ToolKit (抽象窗口工具包),需要调用本地 ...
- Java Swing 使用总结(转载)
随笔转载自:此去经年ぢ 地址:http://www.cnblogs.com/FLFL/p/5369756.html 1. GUI编程引言 以前的学习当中,我们都使用的是命令交互方式: 例如:在 ...
随机推荐
- [转帖]clickHouse单机模式安装部署(RPM安装)
关于版本和系统的选择 操作系统:Centos-7 ClickHouse: rpm 在安装,20.x 安装前的准备 CentOS7 打开文件数限 在 /etc/security/limits.conf ...
- [转帖]HTTP状态码、请求方法、响应头信息
https://www.cnblogs.com/pachongshangdexuebi/p/5279608.html HTTP状态码 当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求. ...
- [转帖]2.20 Native Operating System Tools
https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr020.html#BABBHHIE 2.20 ...
- SQLSERVER 数据库根据LCK_M_S对应的waitsorce 查看被锁的表信息的简单方法
公司一个开发大牛召冠总搞过一个 DMSQLMONITOR 工具 能够识别Oracle以及SQLSERVER 数据库的锁和事务等问题, 非常好用 今天环境出现了不可用的情况, 所以这边着急进行一下问题分 ...
- 【JS 逆向百例】网洛者反爬练习平台第一题:JS 混淆加密,反 Hook 操作
关注微信公众号:K哥爬虫,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后 ...
- 解决Chrome翻译无法使用
截止2022年11月3日自己ping出的ip不可用了 可以用以下ip 172.217.215.90 172.253.115.90 142.250.126.90 142.250.10.90 142.25 ...
- Leetcode 2题 两数相加
题目链接 https://leetcode-cn.com/problems/add-two-numbers/ 题目描述 给你两个非空的链表,表示两个非负的整数.它们每位数字都是按照逆序的方式存储的,并 ...
- 强大的AWS lambda
AWS强大的lambda 自从几年前换工作后,我所参与的项目一直都是基于AWS云服务的架构,我慢慢对serverless的相关基础建设有了一定了解和实践经验.其中lambda是我心中最强大的serve ...
- 策略模式学习,使用go实现策略模式
策略模式 定义 优点 缺点 使用场景 代码实现 策略模式和工厂模式的区别 参考 策略模式 定义 策略模式定义了算法家族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化,不会影响到客户端的使用 ...
- GPTs prompts灵感库:创意无限,专业级创作指南,打造吸睛之作的秘诀
GPTs prompts灵感库:创意无限,专业级创作指南,打造吸睛之作的秘诀 优质prompt展示 1.1 极简翻译 中英文转换 你是一个极简翻译工具,请在对话中遵循以下规则: - Prohibit ...