根据b站黑马程序员的视频教程写的,做了一些改进和优化,例如直接随机打乱存在无法复原的情况,要对初始状态进行一定的校验,如果无解就重新打乱.保证游戏有解.游戏我也做成了安装包,想体验可以下载游玩.
下面是游戏的主体代码,继承JFrame
实现GUI,实现KeyListener, ActionListener
接口,对鼠标和键盘的操作进行监听.
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.*;
import java.awt.event.*;
import java.net.URI;
import java.util.Random;
//游戏的主界面
public class GameJFrame extends JFrame implements KeyListener, ActionListener {
//记录所有图片的索引
private int[][] Array = {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}, {12, 13, 14, 15}};
//记录空白的坐标
private int x = 0, y = 0;
//记录图片的路径
private String path = "image\\animal\\animal3\\";
//记录走的步数
private int step = 0;
//创建选项下面的条目对象
private JMenuItem replayItem = new JMenuItem("重新游戏");
private JMenuItem closeItem = new JMenuItem("关闭游戏");
private JMenuItem accountItem = new JMenuItem("主播粉丝群");
private JMenuItem blogItem = new JMenuItem("主播博客");
private JMenuItem girl = new JMenuItem("美女");
private JMenuItem animal = new JMenuItem("动物");
private JMenuItem sport = new JMenuItem("运动");
Random rand = new Random();
public GameJFrame() {
//初始化窗口
InitJframe();
//初始化菜单
InitMenu();
//初始化数据
InitData();
//初始化图片
InitImage();
//让界面显示出来,写在最后
setVisible(true);
}
//实现打乱图片的效果
public void InitData() {
//初始化一维数组
int[] tempArr = new int[16];
for (int i = 0; i < tempArr.length; i++) {
tempArr[i] = i;
}
while (true) {
//Fisher–Yates 洗牌算法打乱数组
for (int i = tempArr.length - 1; i >= 0; i--) {
int index = rand.nextInt(i + 1);
int temp = tempArr[index];
tempArr[index] = tempArr[i];
tempArr[i] = temp;
}
//计算逆序数和0所在的行,保证游戏有解
int temp_count = 0;
for (int i = 0; i < tempArr.length; i++) {
if (tempArr[i] == 0) {
continue;
}
for (int j = i + 1; j < tempArr.length; j++) {
if (tempArr[j] == 0) {
continue;
}
if (tempArr[i] > tempArr[j]) {
temp_count++;
}
}
}
//将一维数组的数据转移到4X4的二维数组
for (int i = 0; i < tempArr.length; i++) {
if (tempArr[i] == 0) {
x = i / 4;
y = i % 4;
}
Array[i / 4][i % 4] = tempArr[i];
System.out.print(tempArr[i] + ",");
}
System.out.println();
temp_count += (4 - x);
if (temp_count % 2 == 0) {
break;
}
}
}
public void InitImage() {
//清空之前存在的元素
this.getContentPane().removeAll();
//判断是否胜利,添加胜利图片
if (victory()) {
JLabel win_picture = new JLabel(new ImageIcon(".\\image\\win.png"));
win_picture.setBounds(203, 283, 197, 73);
getContentPane().add(win_picture);
}
JLabel step_count = new JLabel("步数:" + step);
step_count.setBounds(50, 30, 100, 20);
getContentPane().add(step_count);
//添加游戏图片
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
int num = Array[i][j];
//获取图片的url
String url = path + num + ".jpg";
//创建一个图片imageIcaon对象
ImageIcon icon = new ImageIcon(url);
//创建一个JLable对象(管理容器)
JLabel jl = new JLabel(icon);
//指定图片位置
jl.setBounds(105 * j + 83, 105 * i + 134, 105, 105);
//给图片添加边框
//0代表图片凸起来
//1代表图片凹下去
jl.setBorder(new BevelBorder(1));
//把管理容器添加到界面
getContentPane().add(jl);
}
}
//添加背景图片
ImageIcon bg = new ImageIcon(".\\image\\background.png");
JLabel background = new JLabel(bg);
background.setBounds(40, 40, 508, 560);
getContentPane().add(background);
//刷新界面
getContentPane().repaint();
}
public void InitJframe() {
//设置界面的宽和高
setSize(603, 680);
//设置界面的标题
setTitle("拼图单机版 V1.0");
//设置界面置顶
setAlwaysOnTop(true);
//设置窗口不可拉伸
setResizable(false);
//设置界面居中显示
setLocationRelativeTo(null);
//设置关闭模式
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//取消默认的居中放置,取消之后才会按照x,y坐标放置图片
setLayout(null);
//添加键盘监听事件
addKeyListener(this);
}
public void InitMenu() {
//初始化菜单
//创建整个的菜单对象
JMenuBar menuBar = new JMenuBar();
//创建菜单上的两个选项的对象(功能,关于我们)
JMenu functionJmenu = new JMenu("功能");
JMenu aboutJmenu = new JMenu("关于作者");
JMenu charge = new JMenu("更换图片");
//将每一个选项下面的条目添加到选项当中
functionJmenu.add(charge);
functionJmenu.add(replayItem);
functionJmenu.add(closeItem);
aboutJmenu.add(accountItem);
aboutJmenu.add(blogItem);
charge.add(girl);
charge.add(animal);
charge.add(sport);
//给条目绑定事件
girl.addActionListener(this);
animal.addActionListener(this);
sport.addActionListener(this);
replayItem.addActionListener(this);
closeItem.addActionListener(this);
accountItem.addActionListener(this);
blogItem.addActionListener(this);
//将两个选项添加到菜单里
menuBar.add(functionJmenu);
menuBar.add(aboutJmenu);
//将菜单添加到整个界面
setJMenuBar(menuBar);
}
@Override
public void keyTyped(KeyEvent e) {
}
//按下不松调用的方法
@Override
public void keyPressed(KeyEvent e) {
//游戏结束不能进行其他操作
if (victory()) {
return;
}
int code = e.getKeyCode();
if (code == 65) {
//删除全部图片
this.getContentPane().removeAll();
//加载完整的图片
JLabel all = new JLabel(new ImageIcon(path + "all.jpg"));
all.setBounds(83, 134, 420, 420);
this.getContentPane().add(all);
//加载背景图片
ImageIcon bg = new ImageIcon(".\\image\\background.png");
JLabel background = new JLabel(bg);
background.setBounds(40, 40, 508, 560);
getContentPane().add(background);
//刷新界面
getContentPane().repaint();
}
}
//按下松开后调用的方法
@Override
public void keyReleased(KeyEvent e) {
//游戏结束不能再进行其他操作
if (victory()) {
return;
}
int code = e.getKeyCode();
//左37,上38,右39,下40
if (code == 37) {
if (y + 1 < 4) {
Array[x][y] = Array[x][y + 1];
Array[x][y + 1] = 0;
y++;
step++;
InitImage();
System.out.println("向左移动");
} else {
System.out.println("向左移动失败");
}
} else if (code == 38) {
if (x + 1 < 4) {
Array[x][y] = Array[x + 1][y];
Array[x + 1][y] = 0;
x++;
step++;
InitImage();
System.out.println("向上移动");
} else {
System.out.println("向上移动失败");
}
} else if (code == 39) {
if (y - 1 >= 0) {
Array[x][y] = Array[x][y - 1];
Array[x][y - 1] = 0;
y--;
step++;
InitImage();
System.out.println("向右移动");
} else {
System.out.println("向右移动失败");
}
} else if (code == 40) {
if (x - 1 >= 0) {
Array[x][y] = Array[x - 1][y];
Array[x - 1][y] = 0;
x--;
step++;
InitImage();
System.out.println("向下移动");
} else {
System.out.println("向下移动失败");
}
} else if (code == 65) {
InitImage();
} else if (code == 80) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
Array[i][j] = i * 4 + j + 1;
}
}
Array[3][3] = 0;
InitImage();
}
}
//判断是否胜利
public boolean victory() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (i == 3 && j == 3) {
continue;
}
if (Array[i][j] != i * 4 + j + 1) {
return false;
}
}
}
return true;
}
//鼠标点击事件
@Override
public void actionPerformed(ActionEvent e) {
Object obj = e.getSource();
if (obj == replayItem) {
step = 0;
InitData();
InitImage();
System.out.println("重新游戏");
} else if (obj == closeItem) {
System.exit(0);
System.out.println("关闭游戏");
} else if (obj == accountItem) {
try {
Desktop desktop = Desktop.getDesktop();
desktop.browse(new URI("https://qm.qq.com/q/dgv5awkpDG"));
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("粉丝群");
} else if (obj == blogItem) {
try {
Desktop desktop = Desktop.getDesktop();
desktop.browse(new URI("https://homeofmjr.top"));
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("博客");
} else if (obj == girl) {
int number = rand.nextInt(11) + 1;
path = "image\\girl\\girl" + number + "\\";
step = 0;
InitData();
InitImage();
System.out.println("美女图片");
} else if (obj == animal) {
int number = rand.nextInt(8) + 1;
path = "image\\animal\\animal" + number + "\\";
step = 0;
InitData();
InitImage();
System.out.println("动物图片");
} else if (obj == sport) {
int number = rand.nextInt(10) + 1;
path = "image\\sport\\sport" + number + "\\";
step = 0;
InitData();
InitImage();
System.out.println("运动图片");
}
}
}
下面是程序的主入口
public class App {
public static void main(String[] args) {
new GameJFrame();
}
}
评论区