發新話題

[教學]踩地雷遊戲

[教學]踩地雷遊戲

複製內容到剪貼板
代碼:
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class Mine extends JFrame implements MouseListener
{
    // 按鈕
    private static MineLabel mb[];
    private static Container container;
    private static JPanel statusPanel;
    private JPanel mainPanel;
    private static JButton resetButton;

    // 炸彈數
    private static int mines;
    // 寬高
    private static int width, height;
    // 按鈕總數
    private static int buttonCount;
    // 使用者點選炸彈的數目
    private static int clickedMineCount;
    // 點對的炸彈數目
    private static int correctClickedMineCount;

    // 影像
    // 按右鍵, 表示這裡有炸彈
    Icon iconHasMine = new ImageIcon("hasMine.jpg");
    // 空的
    Icon iconEmpty = new ImageIcon("empty.jpg");
    // 採到炸彈
    Icon iconGetMine = new ImageIcon("getMine.jpg");
    // 猜錯
    Icon iconError = new ImageIcon("error.jpg");
    // 炸彈
    Icon iconMine = new ImageIcon("mine.jpg");
    // 開始
    Icon iconStart = new ImageIcon("start.gif");
    // 按住滑鼠
    Icon iconPush = new ImageIcon("push.gif");
    // 按鈕掛了
    Icon iconOver = new ImageIcon("over.gif");
    // 預設
    Icon iconDefault = new ImageIcon("default.jpg");
    // 贏了
    Icon iconWin = new ImageIcon("win.gif");

    Icon iconNum[] =
    {
        new ImageIcon("empty.jpg"),
        new ImageIcon("1.jpg"),
        new ImageIcon("2.jpg"),
        new ImageIcon("3.jpg"),
        new ImageIcon("4.jpg"),
        new ImageIcon("5.jpg"),
        new ImageIcon("6.jpg"),
        new ImageIcon("7.jpg"),
        new ImageIcon("8.jpg")
    };

    // 寬高炸彈數
    public Mine( int w , int h , int m )
    {
        super("採地雷"); //標題

        initFrame( w , h , m );

        initButton();

        setAroundMinesCount();

        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        pack();  //調整視窗大小至最適合的尺寸
        setResizable(false);
        setVisible(true);  //設定視窗為可見
    }

    // 初始寬, 高, 炸彈數的值
    private void initFrame( int w , int h , int m )
    {
        height = h;
        width = w;
        mines = m;
        buttonCount = width * height ;

        container = getContentPane();
        container.setLayout( new BorderLayout() );

        resetButton = new JButton( iconStart );
        resetButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                resetButton.setIcon( iconStart );
                mainPanel.removeAll();
                initButton();
                setAroundMinesCount();
                mainPanel.validate();
            }
        });

        mainPanel = new JPanel( new GridLayout( height , width , 1 , 1 ) );
        statusPanel = new JPanel( new BorderLayout() );
        statusPanel.add( resetButton );
        container.add( mainPanel , BorderLayout.CENTER );
        container.add( statusPanel , BorderLayout.SOUTH );
    }

    // 初始按鈕
    private void initButton()
    {
        mb = new MineLabel[ buttonCount ];
        clickedMineCount = 0;
        correctClickedMineCount = 0;

        // 隨機指定炸彈的座標位置
        Set mineIndex = new HashSet();

        while( true )
        {
            int k = ( int )( Math.random() * 10000 ) % ( buttonCount ) ;
            mineIndex.add( new Integer(k) );

            if(mineIndex.size() == mines)
                break;
        }

        // 初始按鈕, 同時根據 Set 裡指定的座標
        // 指定該座標是否有炸彈
        for( int i = 0 ; i < buttonCount ; i++ )
        {
            mb[i] = new MineLabel( iconDefault , i );
            mb[i].addMouseListener( this );

            if( mineIndex.contains( new Integer( i ) ) )
                mb[i].setHasMine( true );

            mainPanel.add( mb[i] );
        }
    }

    // 炸彈 label
    class MineLabel extends JLabel
    {
        private boolean hasMine;    // 這個位置是否放置炸彈
        private boolean userHasMine;    // 使用者是否設定這個位置是炸彈
        private int aroundMineCount;    // 這個位置四周炸彈的數目
        private int mineIndex;  // 該座標的索引值
        private boolean beenClicked;  // 是否被案過了
        public int upIndex , downIndex , rightIndex, leftIndex;
        public int leftUpIndex , rightUpIndex , leftDownIndex , rightDownIndex;

        MineLabel( Icon ic, int mg )
        {
            super(ic);
            mineIndex = mg;
            upIndex = mineIndex - width;
            downIndex = mineIndex + width;
            leftIndex = mineIndex - 1;
            rightIndex = mineIndex + 1;
            leftUpIndex = upIndex - 1;
            rightUpIndex = upIndex + 1;
            leftDownIndex = downIndex - 1;
            rightDownIndex = downIndex + 1;
        }
        // 座標值
        int getMineIndex()
        {
            return mineIndex;
        }

        // 指定這個位置是否有炸彈
        void setHasMine( boolean hm )
        {
            hasMine = hm;
        }
        boolean getHasMine()
        {
            return hasMine;
        }

        // 設定使用者點選的位置為有炸彈
        void setUserHasMine(boolean uhm)
        {
            userHasMine = uhm;
        }
        boolean getUserHasMine()
        {
            return userHasMine;
        }

        // 該座標四周炸彈的數目
        void setAroundMineCount( int amc )
        {
            aroundMineCount = amc;
        }
        int getAroundMineCount()
        {
            return aroundMineCount;
        }

        // 是否已經被點選過了
        void setBeenClicked( boolean bc )
        {
            beenClicked = bc;
        }
        boolean getBeenClicked()
        {
            return beenClicked;
        }

        // 展開四周 aroundMineCount == 0 的位置
        public void checkArround( MineLabel mb )
        {
            ArrayList cache = new ArrayList();
            // 自己四周的位置: null 靠邊 && 已經點過了 && 是炸彈,那麼就不處理該位置了
            if( mb.getUp() != null && mb.getUp().getBeenClicked() == false && mb.getUp().getHasMine() == false)
                cache.add( mb.getUp() );
            if( mb.getDown() != null && mb.getDown().getBeenClicked() == false && mb.getDown().getHasMine() == false)
                cache.add( mb.getDown() );
            if( mb.getLeft() != null && mb.getLeft().getBeenClicked() == false && mb.getLeft().getHasMine() == false)
                cache.add( mb.getLeft() );
            if( mb.getRight() != null && mb.getRight().getBeenClicked() == false && mb.getRight().getHasMine() == false)
                cache.add( mb.getRight() );

            // 先處理自己
            if( mb.getBeenClicked() == false )
            {
                mb.setIcon( iconNum[ mb.getAroundMineCount() ] );
                mb.setBeenClicked( true );
                if( mb.getUserHasMine() == true )
                {
                    mb.setUserHasMine( false );
                    clickedMineCount--;
                }
            }

            // 自己等於零就需要展開
            if( mb.getAroundMineCount() == 0 )
            {
                for( int i = 0 ; i< cache.size() ; i++ )
                {
                    MineLabel m = ( MineLabel )cache.get( i );
                    // 取得該位置四周炸彈的數字, 大於 0 就設定為beenChicked == true
                    if( m.getAroundMineCount() > 0 )
                        m.setIcon( iconNum[ m.getAroundMineCount() ] );
                    // 四周炸彈的數字等於 0  設定 iconEmpty, 再呼叫一次自己
                    else if(m.getAroundMineCount() == 0)
                    {
                        m.setIcon(iconEmpty);
                        m.checkArround(m);  // call self
                    }
                    // 設定已經點過
                    m.setBeenClicked( true );
                    // 因為已經展開 使用者設定有炸彈, 那麼就取消..
                    if( m.getUserHasMine() == true )
                    {
                        clickedMineCount--;
                        m.setUserHasMine(false);
                    }
                }
            }
        }

      
        //取得四周鄰的 MineLabel 物件
        //null 表示自己已經在邊界, 沒有特定的鄰居

        // 取得上面的座標
        private MineLabel getUp()
        {
            return ( upIndex < 0 ? null : mb[ upIndex ] );
        }
        // 取得下面的座標
        private MineLabel getDown()
        {
            return (downIndex >= buttonCount ? null : mb[ downIndex ] );
        }
        // 取得左邊的座標
        private MineLabel getLeft()
        {
            // 座標靠左邊
            if( ( mineIndex % width ) == 0 )
                return null;
            else
                return mb[ leftIndex ];
        }
        // 取得右邊的座標
        private MineLabel getRight()
        {
            // 座標靠右邊
            if( ( mineIndex % width ) == ( width - 1 ) )
                return null;
            else
                return mb[rightIndex];
        }
        // 取得左上座標
        private MineLabel getLeftUp()
        {
            // 上面或者左邊已經是邊邊, 那麼自己也是邊邊了
            if( ( getUp() == null ) || ( getLeft() == null ) )
                return null;
            else
                return mb[ leftUpIndex ];
        }
        // 取得右上座標
        private MineLabel getRightUp()
        {
            // 上面或者左邊已經是邊邊, 那麼自己也是邊邊了
            if( ( getUp() == null ) || ( getRight() == null ) )
                return null;
            else
                return mb[ rightUpIndex ];
        }
        // 取得左下座標
        private MineLabel getLeftDown() {
            // 上面或者左邊已經是邊邊, 那麼自己也是邊邊了
            if( ( getDown() == null ) || ( getLeft() == null))
                return null;
            else
                return mb[ leftDownIndex ];
        }
        // 取得右下座標
        private MineLabel getRightDown()
        {
            // 上面或者左邊已經是邊邊, 那麼自己也是邊邊了
            if( ( getDown() == null ) || ( getRight() == null ) )
                return null;
            else
                return mb[ rightDownIndex ];
        }
    }

    // 要先確定炸彈的位置已經 ok ...
    // 計算全部的每一個座標四周炸彈的數目
    private void setAroundMinesCount()
    {
        for( int i = 0 ; i < buttonCount ; i++ )
        {
            int count = 0;
            if(mb[i].getUp() != null && mb[i].getUp().getHasMine() == true)
                count++;
            if(mb[i].getDown() != null && mb[i].getDown().getHasMine() == true)
                count++;
            if(mb[i].getLeft() != null && mb[i].getLeft().getHasMine() == true)
                count++;
            if(mb[i].getRight() != null && mb[i].getRight().getHasMine() == true)
                count++;
            if(mb[i].getLeftUp() != null && mb[i].getLeftUp().getHasMine() == true)
                count++;
            if(mb[i].getRightUp() != null && mb[i].getRightUp().getHasMine() == true)
                count++;
            if(mb[i].getLeftDown() != null && mb[i].getLeftDown().getHasMine() == true)
                count++;
            if(mb[i].getRightDown() != null && mb[i].getRightDown().getHasMine() == true)
                count++;
            mb[i].setAroundMineCount( count );
        }
    }

    // 顯示每一個座標四周的炸彈數
    private void viewMineGrid()
    {
        for(int i=0; i<buttonCount; i++)
        {
            if(mb[i].getHasMine())// 有炸彈
                mb[i].setIcon( iconMine );
            else// 數字
                mb[i].setText( Integer.toString( mb[i].getAroundMineCount() ) );

            mb[i].removeMouseListener( this );
        }
    }

    // Game over
    public void GameOver( int overIndex )
    {
        resetButton.setIcon( iconOver );
        for( int i = 0; i < buttonCount ; i++)
        {
            if( mb[i].getHasMine() == true )
            {
                if(i == overIndex)
                    mb[i].setIcon( iconGetMine );
                else
                    mb[i].setIcon( iconMine );
            }
            if(mb[i].getUserHasMine() == true && mb[i].getHasMine() == false)
                mb[i].setIcon( iconError );

            mb[i].removeMouseListener( this );
        }
    }


    public void GamePassed()
    {
        for(int i = 0 ; i < buttonCount ; i++)
            mb[i].removeMouseListener( this );

        resetButton.setIcon( iconWin );
        JOptionPane.showMessageDialog( this , "恭喜你贏了!");

    }

    // mouse listener
    public void mouseClicked( MouseEvent e ) { }
    public void mouseEntered( MouseEvent e ){ }
    public void mouseExited( MouseEvent e ) { }
    public void mousePressed( MouseEvent e )
    {
        MineLabel mb = ( MineLabel )e.getSource();
        if( mb.getBeenClicked() == false )
        {
            resetButton.setIcon( iconPush );
            mb.setIcon( iconEmpty );
        }
    }
    public void mouseReleased( MouseEvent e )
    {
        resetButton.setIcon( iconStart );
        MineLabel mb = ( MineLabel )e.getSource();
        // 還沒有被設定過
        if( mb.getBeenClicked() == false )
        {
            // 點選左鍵
            if( e.getButton() == MouseEvent.BUTTON1 )
            {
                // 點到炸彈@@... game over
                if(mb.getHasMine())
                    GameOver(mb.getMineIndex());
                // 看看是四周有多少個炸彈
                else
                    // 遞回展開
                    mb.checkArround(mb);
            }

            // 使用者點滑鼠右鍵,標記為可能有炸彈
            else if( e.getButton() == MouseEvent.BUTTON3 )
            {
                // 還沒設定有炸彈
                if( mb.getUserHasMine() == false && clickedMineCount < mines )
                {
                    mb.setIcon( iconHasMine );
                    mb.setUserHasMine( true );
                    clickedMineCount++;
                    // 猜對了
                    if( mb.getHasMine() )
                    {
                        correctClickedMineCount++;
                        if( correctClickedMineCount == mines )
                            GamePassed();
                    }
                // 已經設定為有炸彈
                }
                else if( mb.getUserHasMine() )
                {
                    mb.setIcon( iconDefault );
                    mb.setUserHasMine( false );
                    clickedMineCount--;
                }
            }
        }      
    }

    public static void main(String[] args)
    {
        String str1 = JOptionPane.showInputDialog( "請輸入寬度\n" );
        String str2 = JOptionPane.showInputDialog( "請輸入高度\n" );
        String str3 = JOptionPane.showInputDialog( "請輸入地雷數\n" );

        int w = Integer.parseInt( str1 );
        int h = Integer.parseInt( str2 );
        int m = Integer.parseInt( str3 );

        new Mine( w , h , m );
    }
}
heavenruler:下次分享請愛用CODE語法包起來增進程式可讀性,謝謝

[ 本帖最後由 philxyz0316 於 2006-8-8 20:38 編輯 ]

TOP

發新話題

本站所有圖文均屬網友發表,僅代表作者的觀點與本站無關,如有侵權請通知版主會盡快刪除。