因为课程的关系,要交作业就写了这个小游戏。
下面先看看架构
对于程序的难点:
- 对于拼图是否存在解的处理: (查到大佬结论)N×N的棋盘,N为奇数时,与八数码问题相同。逆序奇偶同性可互达
N为偶数时,空格每上下移动一次,奇偶性改变。称空格位置所在的行到目标空格所在的行步数为空格的距离(不计左右距离),若两个状态的可相互到达,则有,两个状态的逆序奇偶性相同且空格距离为偶数,或者,逆序奇偶性不同且空格距离为奇数数。否则不能。
也就是说,当此表达式成立时,两个状态可相互到达:(状态1奇偶性状态2奇偶性)(空格距离%2==0)。 - 切图:因为一开始不知道可以通过JAVA实现切图(JAVA弱渣)一直捣鼓用PPT,PS各种瞎搞,后来知道后大骂自己一句傻逼。
- 自动寻路算法:原本设想是速学一波启发式搜索赶在交作业前完成的,结果还是高估了自己的智商,到现在依旧没有掌握该算法的精髓。简单写了广搜实现的复原,不过还是得抓紧时间将启发式搜索补好,广搜处理复杂度过不了3阶以上的拼图。
剩下的就只要基本掌握某一门语言基础语法,随便搞搞应该就可以完成了。
下面上代码:
Puzzle窗体类
package demo;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import javax.swing.*;
public class Puzzle extends JFrame {
static HashMap<Integer, Puzzle> hash = new HashMap<Integer, Puzzle>(); //哈希表存储游戏窗口
static GamePanel gamePanel; //游戏面板
static TopPanel topPanel; //原图展示面板+功能面板
static int _n; //n阶拼图
public static void main(String[] args) {
// TODO Auto-generated method stub
init();
}
public Puzzle(int n, boolean isMove) {
super();
this.setTitle("Puzzle"); //设置游戏名称
this.setSize(426,760); //设置窗体大小
this.setResizable(false); //设置窗体大小无法修改
this.setLocationRelativeTo(null); //将游戏窗口设置在中间
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //关闭游戏窗口设置
this._n = n;
gamePanel = new GamePanel(this._n, isMove); //建立游戏面板传递N阶拼图
topPanel = new TopPanel(hash); //建立游戏功能面板并传递游戏窗体
this.add(gamePanel,BorderLayout.CENTER); //将面板加入窗体
this.add(topPanel,BorderLayout.NORTH);
this.setVisible(true); //将窗体可视
hash.put(0, this);
}
public static void init() {
Puzzle Game = new Puzzle(3, true); //创建游戏窗体
}
}
TopPanel面板类
package demo;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.HashMap;
import javax.swing.JPanel;
import javax.swing.border.TitledBorder;
public class TopPanel extends JPanel{
final String[] src = {"img\\Original.jpg"};
public TopPanel(HashMap<Integer, Puzzle> hash) {
// TODO Auto-generated constructor stub
super();
this.setBorder(new TitledBorder(null, "",TitledBorder.DEFAULT_JUSTIFICATION,TitledBorder.DEFAULT_POSITION, null, null));
ImgPanel panel1 = new ImgPanel(src[0]);
FuncPanel panel2 = new FuncPanel(hash);
this.add(panel1);
this.add(panel2);
}
}
ImgPanel面板类
package demo;
import java.awt.Dimension;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
//鍘熷浘闈㈡澘
public class ImgPanel extends JPanel {
public ImgPanel(String src) {
// TODO Auto-generated constructor stub
super();
this.setPreferredSize(new Dimension(300,300));
final JLabel img = new JLabel(new ImageIcon(src));
this.add(img);
}
}
FuncPanel面板类
package demo;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.util.HashMap;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
//功能面板
public class FuncPanel extends JPanel {
final String[] names = {"开始游戏", "下一关卡", "重置游戏", "复原拼图"}; //游戏功能项
public FuncPanel(HashMap<Integer, Puzzle> hash) {
// TODO Auto-generated constructor stub
super();
this.setPreferredSize(new Dimension(100,300)); //设置面板大小
final FlowLayout flowLayout = new FlowLayout(FlowLayout.CENTER, 0, 36); //设置流布局
this.setLayout(flowLayout);
JButton start = new JButton();
start.setText(this.names[0]);
this.add(start);
start.addActionListener(new ActionListener() { //重新开始游戏,起点为3阶拼图
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
JFrame newGame = new JFrame();
newGame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
final FlowLayout Flow = new FlowLayout();
Flow.setAlignment(FlowLayout.CENTER);
Flow.setHgap(30);
newGame.getContentPane().setLayout(Flow);
newGame.setResizable(false);
newGame.setSize(200,100);
JLabel text = new JLabel();
text.setText("NewGame Start!!!");
text.setSize(100,30);
newGame.getContentPane().add(text);
JButton label = new JButton("确定");
label.setBounds(10, 10, 100, 23);
newGame.getContentPane().add(label);
label.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
newGame.dispatchEvent(new WindowEvent(newGame,WindowEvent.WINDOW_CLOSING) );
Puzzle Game = hash.get(0);
Game.setVisible(false);
Game = new Puzzle(3, true);
}
});
newGame.setLocationRelativeTo(null);
newGame.setVisible(true);
}
});
JButton nextGame = new JButton();
nextGame.setText(this.names[1]);
this.add(nextGame);
nextGame.addActionListener(new ActionListener() { //进入下一卡关,当通关5阶拼时将不再进入下一卡关并给予游戏者提醒
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
Puzzle Game = hash.get(0);
if(Game.gamePanel.chack()) {
if(Game._n == 5) {
JFrame tipGame = new JFrame();
tipGame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
final FlowLayout Flow = new FlowLayout();
Flow.setAlignment(FlowLayout.CENTER);
Flow.setHgap(30);
tipGame.getContentPane().setLayout(Flow);
tipGame.setResizable(false);
tipGame.setSize(200,100);
JLabel text = new JLabel();
text.setText("抱歉,您已经通过所有关卡!!!");
text.setSize(100,30);
tipGame.getContentPane().add(text);
JButton label = new JButton("确定");
label.setBounds(10, 10, 100, 23);
tipGame.getContentPane().add(label);
label.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tipGame.dispatchEvent(new WindowEvent(tipGame,WindowEvent.WINDOW_CLOSING) );
}
});
tipGame.setLocationRelativeTo(null);
tipGame.setVisible(true);
}
else {
Game.setVisible(false);
Game = new Puzzle(Game._n+1, true);
}
}
else {
JFrame rtGame = new JFrame();
rtGame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
final FlowLayout Flow = new FlowLayout();
Flow.setAlignment(FlowLayout.CENTER);
Flow.setHgap(30);
rtGame.getContentPane().setLayout(Flow);
rtGame.setResizable(false);
rtGame.setSize(200,100);
JLabel lowerTip = new JLabel();
lowerTip.setText("抱歉,您暂未通过本关卡!!!");
lowerTip.setSize(100,30);
rtGame.getContentPane().add(lowerTip);
JButton label = new JButton("确定");
label.setBounds(10, 10, 100, 23);
rtGame.getContentPane().add(label);
label.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
rtGame.dispatchEvent(new WindowEvent(rtGame,WindowEvent.WINDOW_CLOSING) );
}
});
rtGame.setLocationRelativeTo(null);
rtGame.setVisible(true);
}
}
});
JButton reset = new JButton();
reset.setText(names[2]);
this.add(reset);
reset.addActionListener(new ActionListener() { //重置游戏
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
Puzzle Game = hash.get(0);
Game.gamePanel.reSet( Game.gamePanel._n);
}
});
JButton repair = new JButton();
repair.setText(names[3]);
this.add(repair);
repair.addActionListener(new ActionListener() { //复原拼图,自动寻路
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
//------------------------------------自动寻路算法-------------------------------------------
Puzzle Game = hash.get(0);
if(Game.gamePanel._n==3) {
int[][] dir = {{1,0},{0,1},{-1, 0},{0,-1}};
boolean[] vis = new boolean[362881];
for(int i = 0; i < 362881; i++) vis[i] = false;
int head = 0, tail = 1;
AiArr[] array = new AiArr[370000];
AiArr[] res = new AiArr[370000];
int idx = 0, n = Game.gamePanel._n;
for(int i = 0; i < n*n; i++) {
if(Game.gamePanel.rondomArr[i] == n*n-1) {
idx = i;
break;
}
}
array[0] = new AiArr(Game.gamePanel.rondomArr, idx, -1, -1);
vis[array[0].calc()] = true;
if(vis[1] == true) {
Game.gamePanel.winGame();
}
else {
while(head!=tail) {
AiArr k = array[head];
int x = (k._idx)/n, y = (k._idx)%n;
for(int i = 0; i < 4; i++) {
int tx = x+dir[i][0], ty = y+dir[i][1];
if(tx < 0 || tx >= n || ty < 0 || ty >= n) continue;
AiArr tmp = k.move(i, head);
if(vis[tmp.calc()]==true) continue;
array[tail] = tmp;
vis[array[tail].calc()] = true;
if(vis[1] == true) {
break;
}
tail+=1;
}
if(vis[1] == true) break;
head++;
}
int t = 0;
res[t] = array[tail];
while(res[t]._node!=-1) {
t+=1;
res[t] = array[res[t-1]._node];
}
final int gbk = t;
new Thread(new Runnable(){
@Override
public void run() {
try {
for(int i = gbk-1; i >=0; i--) {
Game.gamePanel.moveSet(res[i]._dir, res[i+1]._idx);
Thread.sleep(300);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
else {
JFrame tipWin = new JFrame();
tipWin.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
final FlowLayout Flow = new FlowLayout();
Flow.setAlignment(FlowLayout.CENTER);
Flow.setHgap(30);
tipWin.getContentPane().setLayout(Flow);
tipWin.setResizable(false);
tipWin.setSize(200,100);
JLabel text = new JLabel();
text.setText("功能暂未实现!!!");
text.setSize(100,30);
tipWin.getContentPane().add(text);
JButton label = new JButton("确定");
label.setBounds(10, 10, 100, 23);
tipWin.getContentPane().add(label);
label.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tipWin.dispatchEvent(new WindowEvent(tipWin,WindowEvent.WINDOW_CLOSING) );
}
});
tipWin.setLocationRelativeTo(null);
tipWin.setVisible(true);
}
}
});
}
}
寻路算法结构类
package demo;
public class AiArr {
int[] _arr;
int _idx;
int _dir;
int _node;
private int[][] __dir = {{1,0},{0,1},{-1, 0},{0,-1}};
public AiArr() {
}
public AiArr(int[] arr, int idx, int dir, int node) {
_arr = arr;
_idx = idx;
_dir = dir;
_node = node;
}
public AiArr move(int dir, int node) {
int x = _idx/3, y = _idx%3;
int tx = x+__dir[dir][0], ty = y+__dir[dir][1];
int t = _arr[x*3+y];
int[] newArr = new int[9];
for(int i = 0; i < 9; i++) newArr[i] = _arr[i];
newArr[x*3+y] = newArr[tx*3+ty];
newArr[tx*3+ty] = t;
return new AiArr(newArr, tx*3+ty, dir, node);
}
public int fac(int n) {
int x = 1;
for(int i = 1; i <= n; i++) x*=i;
return x;
}
public int calc() {
int len = 9, ans = 0;
for(int i = 0; i < len; i++) {
int Count = 0;
for(int j = i+1; j < len; j++) {
if(_arr[i] > _arr[j]) {
Count+=1;
}
}
ans+=(Count*fac(len-i-1));
}
return ans+1;
}
}
GamePanel面板类
package demo;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class GamePanel extends JPanel{
final String[] srcs = {"img\\easy\\", "img\\mediun\\", "img\\hard\\"};
String src;
JButton[] icons;
JButton[] TMP;
final JButton[] buttons;
int _n;
int[] rondomArr;
int[] rondomTmpArr;
int[][] dir = {{1,0},{0,1},{-1, 0},{0,-1}};
public GamePanel(int n, boolean vis) {
// TODO Auto-generated constructor stub
_n = n;
this.removeAll();
final GridLayout gridLayout = new GridLayout(n, n);
gridLayout.setHgap(0);
gridLayout.setVgap(0);
this.setLayout(gridLayout);
src = srcs[n-3];
buttons = new JButton[n*n];
for(int row = 0; row < n; row++) {
for(int col = 0; col < n; col++) {
String s = src+row+col+".jpg";
buttons[row*n+col] = new JButton(new ImageIcon(s));
}
}
icons = new JButton[n*n];
TMP = new JButton[n*n];
rondomArr = rondom(n*n);
rondomTmpArr = new int[n*n];
for(int i = 0; i < n*n; i++) {
rondomTmpArr[i] = rondomArr[i];
icons[i] = buttons[rondomArr[i]];
icons[i].setName(""+i);
TMP[i] = new JButton(icons[i].getIcon());
this.add(icons[i]);
icons[i].addActionListener(new MoveButtonAction());
}
}
public static int[] rondom(int n) {
Random rondom = new Random();
int[] rondomArrT = new int[n];
boolean[] vis = new boolean[n];
int Count = -1, its = 1;
while(Count%2!=0 && (((int)Math.sqrt(n)-its)%2 == 1 || n%2==1)) {
for(int i = 0; i < n; i++) vis[i] = false;
for(int i = 0; i < n; i++) {
rondomArrT[i] = rondom.nextInt(n);
while(vis[rondomArrT[i]] == true) {
rondomArrT[i] = rondom.nextInt(n);
if(vis[rondomArrT[i]] == false) {
break;
}
}
vis[rondomArrT[i]] = true;
}
Count = 0;
for(int i = 0; i < n; i++) {
if(rondomArrT[i] == n-1) {
its = i/(int)Math.sqrt(n);
continue;
}
for(int j = 0; j < i; j++) {
if(rondomArrT[i] < rondomArrT[j] && rondomArrT[j]!=n-1) {
Count+=1;
}
}
}
}
return rondomArrT;
}
public void reSet(int n) {
for(int i = 0; i < n*n; i++) {
icons[i].setIcon(TMP[i].getIcon());
rondomArr[i] = rondomTmpArr[i];
}
}
public void moveSet(int xdir, int idx) { //移动设置
JButton tmp = new JButton(icons[idx].getIcon());
int tx = dir[xdir][0]+idx/_n, ty = dir[xdir][1]+idx%_n;
icons[idx].setIcon(icons[tx*_n+ty].getIcon());
icons[tx*_n+ty].setIcon(tmp.getIcon());
int TMP = rondomArr[idx];
rondomArr[idx] = rondomArr[tx*_n+ty];
rondomArr[tx*_n+ty] = TMP;
winGame();
}
class MoveButtonAction implements ActionListener { //移动判断
public void actionPerformed(ActionEvent e) {
if(chack()) return;
JButton moveButton = (JButton)e.getSource();
int idx = Integer.parseInt(moveButton.getName());
int x = idx/_n, y = idx%_n;
String target = src+(_n-1)+(_n-1)+".jpg";
for(int i = 0; i < 4; i++) {
int tx = x+dir[i][0], ty = y+dir[i][1];
if(tx < 0 || tx >= _n || ty < 0 || ty >= _n) continue;
String original = new String(icons[tx*_n+ty].getIcon()+"");
if(original.equals(target)) {
moveSet(i, idx);
winGame();
break;
}
}
}
}
public boolean chack() { //检测是否通关
// TODO Auto-generated method stub
for(int i = 0; i < _n*_n; i++) {
String s = src+(i/_n)+(i%_n)+".jpg";
if(s.equals(icons[i].getIcon()+"")==false) {
return false;
}
}
return true;
}
public void winGame() { //通关提示通知窗口
if(chack()) {
JFrame newGame = new JFrame();
newGame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
final FlowLayout Flow = new FlowLayout();
Flow.setAlignment(FlowLayout.CENTER);
Flow.setHgap(30);
newGame.getContentPane().setLayout(Flow);
newGame.setResizable(false);
newGame.setSize(200,100);
JLabel text = new JLabel();
text.setText("恭喜你,通关成功!!!");
text.setSize(100,30);
newGame.getContentPane().add(text);
JButton label = new JButton("确定");
label.setBounds(10, 10, 100, 23);
newGame.getContentPane().add(label);
label.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
newGame.dispatchEvent(new WindowEvent(newGame,WindowEvent.WINDOW_CLOSING) );
System.out.println();
}
});
newGame.setLocationRelativeTo(null);
newGame.setVisible(true);
}
}
}
展示部分: