JAVA編寫拼圖小遊戲帶自動尋路算法

因爲課程的關係,要交作業就寫了這個小遊戲。
下面先看看架構
在這裏插入圖片描述

對於程序的難點:

  • 對於拼圖是否存在解的處理: (查到大佬結論)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);	
		}
	}
}


展示部分:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章