1. 程式人生 > >java大作業之拼圖遊戲

java大作業之拼圖遊戲

這個拼圖遊戲是幫同學做的,還是挺不錯的,實現功能包括:自動選取圖片,自動任意切割圖片,且保證生成的一定有解,還有倒計時功能。

還寫了那個迪傑斯特拉演示的,過兩天再發上來,畢竟要考試了(預習了...)

先說下如何保證有解,兩種方法:1,先切割然後自己後臺讓空格自己隨機移動。

                                                             2,生成全排列,然後判斷是否有解:

一個N*M數碼是否有解存在以下結論:
1. 如果M為奇數,那麼上下移動,左右移動都不會改變序列的逆序值的奇偶性,所以如果begin個end兩個狀態的逆序值奇偶性一樣就有解!
2. 如果M為偶數,左右移動不會改變序列的逆序值的奇偶性,上下移動一次改變奇偶性一次,所以如果begin的空格的行數i與end的空格的行數j的絕對值之差與begin和end的逆序值的絕對值之差的奇偶性一樣則有解。
3.其他情況無解!
這個其實做poj那道8數碼問題應該會有人提到,所以我一開始想到的就是第二種思路,第一種是後來聽同學做的,感覺更加方便。

自動選取圖片比較簡單,套一個JFileChooser就可以,進度條用JProgressBar 難點在於這邊這個進度條的執行緒要和遊戲的有關聯,我是通過將它的value設為靜態成員變數實現的。

自動切割圖片是谷歌到一串程式碼(貌似第二個就是,只不過它那個是存成相片,我這個直接儲存為Imagecon即可,稍微修改了下.

puzzle類

public class puzzle extends JFrame implements ActionListener{
	Game g;
	private JButton jb,jb2,jb3;//依次為開始遊戲,顯示正確圖片
	private JTextField jt,jt2;//讀入切割的行數和列數
	JProgressBar progressbar;//進度條
	Progress p;
	String file;
	public puzzle(){
		setTitle("我的拼圖");//應用標題
		setLayout(null);
		setBounds(0,0,900,700);//拼圖程式的範圍
		g= new Game();//如果以後選擇圖片就從主介面選擇好url把url傳過去(game有多個建構函式)
		g.setBounds(0,0,600,600);
		Container container=getContentPane();
	    jb = new JButton("開始遊戲");
	    jb.setBounds(650,50,200,100);
	    jb.addActionListener(this);
	    jb2 = new JButton("顯示正確圖片");
	    jb2.setBounds(650,175,200,100);
	    jb2.addActionListener(this);
	    jb3=new JButton("選擇圖片");
	    jb3.setBounds(650,300,200,100);
	    jb3.addActionListener(this);
	    JLabel jl= new JLabel("橫向切割數量");
	    jl.setBounds(650,400,200,50);
	    container.add(jl);
	    jt=new JTextField("");
	    jt.setBounds(650,450,200,50);
	    JLabel jl2=new JLabel("縱向切割數量");
	    jl2.setBounds(650,500,200,50);
	    container.add(jl2);
	    jt2=new JTextField("");
	    jt2.setBounds(650,550,200,50);
	    progressbar =new JProgressBar();
	    progressbar.setBounds(0,600,800,75);
	    progressbar.setMinimum(0);
	    progressbar.setMaximum(100);
	    progressbar.setValue(0);
	    progressbar.setBackground(Color.blue);
	    progressbar.setBorderPainted(true);
	    container.add(progressbar);
	    container.add(jt);
	    container.add(jt2);
	    container.add(jb);
	    container.add(jb2);
	    container.add(jb3);
	    container.add(g);
	    container.setBackground(Color.white);
	    setVisible(true);
	    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	  }
	 public void actionPerformed(ActionEvent e){
		 int n,m;
	     if (e.getSource()==jb){//如果是開始遊戲(獲得n行,m列)
			String s1=jt.getText();
			String s2=jt2.getText();
			if (s1.matches("\\d+")&&s2.matches("\\d+"))//如果是整數
			{
			n=Integer.parseInt(s1);
			m=Integer.parseInt(s2);
			//System.out.println(s1+""+s2);
			//new picturecut(n,m);
			if(file==null)
			new alert4();
			else
			{
			g.start(n,m,file);//傳入n行,m列
			if (p==null)//只開啟一次
			{
			p=new Progress(progressbar);
			p.start();
			}
			else //如果之前執行緒已開啟
			{  
				if ((Progress.value>=100)||(Progress.value<0))//如果之前那個執行緒跑完了
				{
					progressbar.setValue(0);
				    Progress.value=0;
				    
				}
				else
				//System.out.println("新的p");//關閉之前的執行緒,開啟新的新的執行緒的方法,不要開啟新的執行緒,直接用之前的那個,只不過修改裡面的value值。
				{   
					progressbar.setValue(0);
				    Progress.value=0;
				}
			}
			}
			
			}
			else
				new alert3();
		   }
		 if (e.getSource()==jb2){//如果是顯示圖片
			g.Display(file);
		 }
		 if (e.getSource()==jb3){
			 JFileChooser  fileChooser;
			 {
			 fileChooser =new JFileChooser();
			 FileFilter filter =new FileNameExtensionFilter("影象檔案(只能是PNG或JPG)", "JPG","PNG");
			 fileChooser.setFileFilter(filter);
			 }
			 int i=fileChooser.showOpenDialog(getContentPane());
			 if (i==JFileChooser.APPROVE_OPTION){
				 File SelectedFile=fileChooser.getSelectedFile();
				 file=SelectedFile.getAbsolutePath();
				 System.out.println(file);
				 g.redraw(file);
				 progressbar.setValue(0);
				 Progress.value=-10000;
			 }
		 }
	 }
	public static void main(String []args){
		new firstapplet();
		new puzzle();
	}
 

}
class Progress extends Thread{
	
	static int value=0;
	private JProgressBar progressBar;
	public Progress(JProgressBar progressBar)
	{
		this.progressBar=progressBar;
	}
	public void run()
	{  
		while(value<=100)
		{
			try{
				Thread.sleep(1000);
				}catch(InterruptedException e){
			e.printStackTrace();
		}
			value++;
			if (value==100)
			{
				new alert2();
			}
			progressBar.setIndeterminate(false);
			progressBar.setValue(value);
			}
	}
	
	
}

Game類
package puzzle;
import java.awt.Button;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.io.File;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Game extends JPanel implements MouseListener{//建立開始就放張圖片,並且打亂,點選開始遊戲計時並展示圖片,當跑到最終結果時退出執行緒
	private ImageIcon imagecon;
	private ImageIcon []pi;
	Thread runner;
	boolean ok=false;
	int time;//判斷是第幾次呼叫repaint函式
	int []a;//數組裡儲存著第i個位置放著第幾張切割的圖片如果是9則為空著的圖片.
	int currentx;//當前為空的橫座標
	int currenty;//當前為空的縱座標
	int number;//當前是第幾個位置為空
    int n;//n行
	int m;//m列
	public Game(){
		 imagecon=new ImageIcon("./picture/bg2.png");//記錄完整圖片
		 time=1;
		 repaint();
	}
	public void init(int n,int m){//初始化圖片位置資訊(1~9個位置儲存9張圖片中的12張),要保證逆序對為偶數(注意計算機n行m列與人是顛倒的)
		int flag,c;
		boolean f=true;
		pi=new ImageIcon[n*m+1];
		while(f)//若m為奇數,則逆序對為偶數可行,如果為偶數,則空格行數的差值與逆序對差值奇偶性一樣
		{
			System.out.println("n為"+n+"m為"+m);
		for (int i=1;i<=n*m;i++)
		{    
			a[i]=(int)(1+Math.random()*(n*m-1+1));//隨機一個1到n*m的數
			flag=1;
			for (int j=1;j<i;j++)
				if (a[i]==a[j])//判重
					flag=0;
			if (flag==0)//如果不符合
				  i--;
		}
		int sum=0;//計算逆序對
		number=0;
		for (int i=1;i<=n*m;i++)
		{    
			if (a[i]==n*m)
			{
				number=i;//記錄下是第幾個為空
			    continue;
			}
			 for (int j=1;j<i;j++)
			 {
				if (j==number)
			    continue;
			 if (a[j]>a[i])
				   sum++;
			 }
		}
		System.out.println(sum);
		if ((sum%2==0)&&(m%2==1))//如果是奇數有解
		 f=false;
		else
		 if ((m%2==0)&&(n-(number-1)/m-1)%2==sum%2)//如果是偶數
		 f=false;
	   for (c=1;c<=n;c++)//不能一樣
			if (a[c]!=c)
				break;
		  if (c==n+1)   f=true;
		}
		for (int i=1;i<=n*m;i++)
		System.out.println(a[i]);
	}
	@Override
	 public void paint(Graphics g){
		 if (time==1)
		 {
		 Image r=imagecon.getImage();
		 g.drawImage(r,0,0,600,600,this);//畫出原來的完整圖案
		 }
		 else  if (!isfinish()){
		    g.clearRect(0,0,600,600);
		    for (int i=1;i<=n*m;i++)
		    {
		    if (a[i]!=n*m)
		    {
		    int temp=a[i];//第i個位置是第原來的第a[i]塊圖片	 
		    Image r=pi[temp].getImage();
			g.drawImage(r,(i-1)%m*(600/m),(i-1)/m*(600/n),600/m,600/n,this);
			}
		    else
		    {
		    currentx=(i-1)%m*(600/m);
		    currenty=(i-1)/m*(600/n);//空格的橫縱左上角座標
		    g.clearRect((i-1)%m*(600/m),(i-1)/m*(600/n),600/m,600/n);//設定第9為空格
		    }
		    }
 }
		 else{
			 g.clearRect(0,0,600,600);
			 for (int i=1;i<=n*m;i++)//全部輸出
			 {   
				 Image r=pi[i].getImage();
				 g.drawImage(r,(i-1)%m*(600/m),(i-1)/m*(600/n),600/m,600/n,this);
			 }
			 Progress.value=-10000;//遊戲結束讓進度條設為負值
			 new alert();
		 }
	}  
	public void start(int n,int m,String file)//傳入m行n列,開始切圖
		{  
		   this.n=n;
		   this.m=m;
		   a=new int[n*m+1];//n行m列
		   init(n,m);
		   String srcImageFile=file;
		   int cols,rows;
		   cols=m;
		   rows=n;
		   Progress.value=0;
		   if (time==1)
		   time=2;
		   try{
	            // 讀取源影象
	            BufferedImage bi = ImageIO.read(new File(srcImageFile));
	            // 源圖寬度
	            int srcWidth = bi.getWidth(); 
	            // 源圖高度
	            int srcHeight = bi.getHeight(); 
	            if (cols >= 1 && rows >=1){
	                Image image = bi.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
	                // 切片橫向數量
	                // 切片縱向數量
	                // 計算切片的橫向和縱向數量
	                double destWidth = srcWidth / cols;
	                double destHeight = srcHeight /rows;
	                Image img;
	                ImageFilter cropFilter;
	                // 迴圈建立切片
	                for (int i = 0; i < rows; i++){
	                    for (int j = 0; j < cols; j++){
	                        // 四個引數分別為影象起點座標和寬高
	                        cropFilter = new CropImageFilter((int)(j * destWidth), (int)(i * destHeight),
	                                (int)((j+1)* destWidth), (int)((i+1) * destHeight));
	                        img = Toolkit.getDefaultToolkit().createImage(
	                                        new FilteredImageSource(image.getSource(), cropFilter));
	                        BufferedImage tag = new BufferedImage((int)destWidth, (int)destHeight,
	                                BufferedImage.TYPE_INT_RGB);
	                        Graphics g = tag.getGraphics();
	                        g.drawImage(img, 0, 0, null); // 繪製縮小後的圖
	                        g.dispose();
	                        int c=j+1+i*cols;
	                        pi[c]=new ImageIcon(tag);
	                    }
	                }
	            }
	                    }catch (Exception e){
	                        e.printStackTrace();
	                    }
		   this.addMouseListener(this);
		   repaint();
		}
	  public boolean isfinish(){//說明已經完成了
			for (int i=1;i<=n*m;i++)
			{
				if (a[i]!=i)
				return false;
			}
			return true;
		}
	  @Override
	   public void mousePressed(MouseEvent e){
		   int tempx,tempy;
		   System.out.println("滑鼠觸控");
		   tempx=e.getX();//當前所在位置
		   tempy=e.getY();//
		   System.out.println(tempx);
		   System.out.println(tempy);
		   System.out.println("原來的長度為:"+currentx+"原來的高度:"+currenty);
		   if ((tempx-currentx>=0)&&(tempx-currentx<=600/m)&&(currenty-tempy<=600/n)&&(currenty-tempy>=0)){//如果點選按鈕在當前的上方
					int temp=a[number];//上下交換自己的圖片
					a[number]=a[number-m];
					a[number-m]=temp;
					number=number-m;//更新當前所在的位置
					currentx=tempx/(600/m)*(600/m);//第幾行
					currenty=tempy/(600/n)*(600/n);//第幾列
					System.out.println("向上邊"+currentx+"  "+currenty);
  				}
				 else if ((currentx-tempx>=0)&&(currentx-tempx<=600/m)&&(tempy-currenty>=0)&&(tempy-currenty<=600/n)){//如果點選按鈕在當前的左邊
					 int temp=a[number];
					 a[number]=a[number-1];
					 a[number-1]=temp;
					 number=number-1;
					 currentx=tempx/(600/m)*(600/m);
					 currenty=tempy/(600/n)*(600/n);
					 System.out.println("向左邊"+currentx+"  "+currenty);
				}
				 else if ((tempx-currentx<=2*(600/m))&&(tempx-currentx>=600/m)&&(tempy-currenty>=0)&&(tempy-currenty<=600/n)){//如果點選按鈕在當前的右邊
					 int temp=a[number];
					 a[number]=a[number+1];
					 a[number+1]=temp;
					 number=number+1;
					 currentx=tempx/(600/m)*(600/m);
					 currenty=tempy/(600/n)*(600/n);
					 System.out.println("向右邊"+currentx+"  "+currenty);
				 }
				 else if ((tempy-currenty>=600/n)&&(tempy-currenty<=2*(600/n))&&(tempx-currentx>=0)&&(tempx-currentx<=600/m)){//如果點選按鈕在當前的下邊
					 int temp=a[number];
					 a[number]=a[number+m];
					 a[number+m]=temp;
					 number=number+m;
					 currentx=tempx/(600/m)*(600/m);
					 currenty=tempy/(600/n)*(600/n);
					 System.out.println("向下邊"+currentx+"  "+currenty);
				 }
			repaint();//至於要在每一次最後重新繪製即可
  }    
		@Override
		public void mouseClicked(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}
		@Override
		public void mouseEntered(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}
		@Override
		public void mouseExited(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}
		@Override
		public void mouseReleased(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}
		public void Display(String file){
			String s=file;
			new display(s);
		}
		public void redraw(String s) {
		    imagecon=new ImageIcon(s);//記錄完整圖片
			time=1;
			repaint();
		}
		public class display extends  JDialog{//展示圖片
			Congra jp;
			public display(String s){
				setTitle("展示圖片");
				setLayout(null);
				setBounds(400,300,300,300);
				Container container=getContentPane();
				JLabel jl= new JLabel("原圖片");
			    jl.setBounds(50,50,300,200);
				container.add(jl);
				container.setBackground(Color.white);
				jp=new Congra(s);
				jp.setBounds(0,0,250,250);
				container.add(jp);
			    setVisible(true);
			}
		}
     public class alert extends JDialog {
			ImageIcon li;
			public alert(){
				win g=new win();
				g.setBounds(0,0,400,400);
				setTitle("you win!");
				setLayout(null);
				setBounds(400,300,450,400);
				Container container=getContentPane();
				container.add(g);
				container.setBackground(Color.white);
			    setVisible(true);
			    }
			
	}
     public class win extends JPanel{
    	 ImageIcon li;
    	 public win(){
    		 li=new ImageIcon("./picture/you win.png");
    		 repaint();
    	 }
    	 public void paint(Graphics g)
    	 {
    		 Image r=li.getImage();
    	     g.drawImage(r,0,0,400,400,this);
    	 }
     }
	
}


主要就是這兩個,接下去就是先彈窗(所謂健壯性麼....)

alert2類

public class alert2 extends JDialog {
	 public alert2(){
		setTitle("遊戲結束!");
		setLayout(null);
		setBounds(400,300,450,400);
		Container container=getContentPane();
		container.setBackground(Color.white);
		JLabel jl= new JLabel("時間到,遊戲結束!");
	    jl.setBounds(50,50,200,100);
		container.add(jl);
	    setVisible(true);
	    }
	
}
alert3類
public class alert3 extends JDialog {
	 public alert3(){
		setTitle("注意");
		setLayout(null);
		setBounds(400,300,300,200);
		Container container=getContentPane();
		container.setBackground(Color.white);
		JLabel jl= new JLabel("請輸入正確的橫向縱向切割數量!");
	    jl.setBounds(50,50,200,100);
		container.add(jl);
	    setVisible(true);
	    }
	
}

alert4類
public class alert4 extends JDialog {
	 public alert4(){
		setTitle("注意!");
		setLayout(null);
		setBounds(400,300,300,200);
		Container container=getContentPane();
		container.setBackground(Color.white);
		JLabel jl= new JLabel("請先選擇圖片");
	    jl.setBounds(50,50,200,100);
		container.add(jl);
	    setVisible(true);
	    }
	
}