java大作業之拼圖遊戲
阿新 • • 發佈:2019-02-12
這個拼圖遊戲是幫同學做的,還是挺不錯的,實現功能包括:自動選取圖片,自動任意切割圖片,且保證生成的一定有解,還有倒計時功能。
還寫了那個迪傑斯特拉演示的,過兩天再發上來,畢竟要考試了(預習了...)
先說下如何保證有解,兩種方法:1,先切割然後自己後臺讓空格自己隨機移動。
2,生成全排列,然後判斷是否有解:
一個N*M數碼是否有解存在以下結論: 1. 如果M為奇數,那麼上下移動,左右移動都不會改變序列的逆序值的奇偶性,所以如果begin個end兩個狀態的逆序值奇偶性一樣就有解! 2. 如果M為偶數,左右移動不會改變序列的逆序值的奇偶性,上下移動一次改變奇偶性一次,所以如果begin的空格的行數i與end的空格的行數j的絕對值之差與begin和end的逆序值的絕對值之差的奇偶性一樣則有解。 3.其他情況無解!
自動選取圖片比較簡單,套一個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);
}
}