/*
 * 踩地雷 
 * FileName: Mine2.java
 * Author:   Shiuh-Sheng Yu
 * Date:     5/19/1999
 * Last Update: 5/05/2002
 */
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class Mine2 extends Component implements MouseListener, ActionListener {
    private byte[] map; // 以一維陣列來取代二維,以減少物件的使用
    private int x, y;
    private Frame f;
    private int[] directions;
    // 前四個bits用來記錄此格的資料, 後面四個bits用來記錄旁邊的地雷數
    private static final byte BOMB = 0x10; // 用以紀錄本格有地雷
    private static final byte VISIT = 0x20; // 用以紀錄已按左鍵
    private static final byte MARK = 0x40; //  用以紀錄已按右鍵
    private static final byte NEB = 0x0f; // 用以取出旁邊的地雷數
    void checkWin() {
        int width = x + 2;
        for (int i = 1; i <= y; i++) { // 由上而下
            int tmp = i * width;
            for (int j = 1; j <= x; j++) { // 由左而右
                int tt = tmp + j;
                if ((((map[tt] & MARK) > 0) && ((map[tt] & BOMB) == 0)) || (((map[tt] & MARK) == 0) && ((map[tt] & BOMB) > 0))) {
                    return;
                }
            }
        }
        showAll();
        showWin();
    }
    void showAll() {
        int width = x + 2;
        for (int i = 1; i <= y; i++) { // 由上而下
            int tmp = i*width;
            for (int j=1; j<=y; j++) { // 由左而右
                int tt = tmp + j;
                if ((map[tt] & BOMB) != 0) { // 畫上標示
                    map[tt] |= MARK;
                } else {
                    map[tt] = (byte)((map[tt] | VISIT) & ~MARK); // 設為按過, 再把標示拿掉
                }
            }
        }
        repaint();
    }
    void showWin() {
        Dialog d;
        (d = new Dialog(f, true)).addWindowListener(new CloseWindow(d));
        ((Button)d.add(new Button("恭喜過關"))).addActionListener(new CloseWindow(d));
        d.pack();
        d.show();
    }
    public Dimension getPreferredSize() {
        return new Dimension(22*x,22*y);
    }
    void reset() {
        map = new byte[(x+2)*(y+2)];
        int width = x + 2;
        for (int i = 0; i <= y + 1; i++) { // 由上而下
            int tmp = i * width;
            for (int j = 0; j <= x + 1; j++) { // 由左而右
               int tt = tmp + j;
               if (i==0 || i == (x + 1) || j == 0 || j == (y + 1)) {
                   map[tt] = VISIT; // boundary
               } else {
                   map[tt] = 0; // normal position
               }
            }
        }
        // 15%為地雷
        for (int i = 0, bombNumber = (int)(x*y*0.15); i < bombNumber;) { // setup bombs
            int row = (int)(Math.random()*y)+1;
            int col = (int)(Math.random()*x)+1;
            int tt = row * width + col;
            if ((map[tt] & BOMB) == 0) { // has no bomb yet
                i++;
                map[tt] |= BOMB;
            }
        }
        // 計算旁邊的地雷數
        for (int i = 1; i <= y; i++) { // 由上而下
            int tmp = i * width;
            for (int j = 1; j <= x; j++) { // 由左而右
                int tt = tmp + j;
                for (int k = 0; k < 8; k++) {
                    if ((map[tt + directions[k]] & BOMB) != 0) { // 旁有地雷
                        map[tt]++; // 後面四個bit用來記錄旁邊的地雷數
                    }
                }
            }
        }
        repaint();
    }
    // 畫出盤面
    public void paint(Graphics g) {
        g.setColor(Color.red);
        for (int i = 1; i <= x; i++) { // 畫縱線
            g.drawLine(i*20, 0, i*20, 200);
        }
        for (int j = 1; j <= y; j++) { // 畫橫線
            g.drawLine(0, j*20, 200, j*20);
        }
        int width = x + 2;
        // 畫出每一格目前的狀態
        for (int i = 1; i <= y; i++) { // 由上而下
            int tmp = i * width;
            for (int j = 1; j <= x; j++) { // 由左而右
                int tt = tmp + j;
                if ((map[tt] & VISIT) != 0) { // 已經翻過來了
                    g.setColor(Color.blue);
                    if ((map[tt] & NEB) != 0) { // 旁有地雷, show出地雷數
                        g.drawString(Integer.toString(map[tt] & NEB),  j * 20 - 15, i * 20 - 5);
                    } else { // 沒有地雷, 填空白
                        g.fillRect((j - 1) * 20, (i - 1) * 20, 20, 20);
                    }
                } else if ((map[tt] & MARK) != 0) { // 已用右鍵做記號
                    g.setColor(Color.green);
                    g.fillOval((j - 1) * 20, (i - 1) * 20, 20, 20);
                }
            }
        }
    }
    public void actionPerformed(ActionEvent e) {
        String command = e.getActionCommand();
        if (command.equals("Exit")) {
            System.exit(0);
        } else if (command.equals("New Game")) {
            reset();
        }  else {
            System.out.println("Unknown Command");
        }
    }
    public Mine2(int x, int y) {
        MenuBar mb;
        Menu m;
        this.x = x;
        this.y = y;
        int width = y + 2;
        directions = new int[8];
        directions[0] = 1;
        directions[1] = -1;
        directions[2] = width;
        directions[3] = -width;
        directions[4] = -width - 1;
        directions[5] = -width + 1;
        directions[6] = width - 1;
        directions[7] = width + 1;
        (f = new Frame("踩地雷")).add(this).addMouseListener(this);
        f.addWindowListener(new CloseWindow(f,true));
        f.setMenuBar(mb=new MenuBar());
        mb.add(m = new Menu("File"));
        m.add(new MenuItem("New Game")).addActionListener(this);
        m.add(new MenuItem("Exit")).addActionListener(this);
        reset();
        f.pack();
        f.show();
    }
    void setTouched(int tt) {
        map[tt] |= VISIT;
        if ((map[tt] & NEB) == 0) { // no bomb around, auto flip
            for (int i = 0; i < 8; i++) {
                if ((map[tt + directions[i]] & VISIT) == 0) { // 已翻開就不用再做了
                    setTouched(tt + directions[i]);
                }
            }
        }
    }
    public void mouseClicked(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    public void mousePressed(MouseEvent e) {
        int row = e.getY()/20 + 1;
        int col = e.getX()/20 + 1;
        int tt = row*(y + 2) + col;
        if (row > x || col > y || (map[tt] & VISIT) != 0) { // 超出邊界或已經翻過就不必處理
            return;
        }
        if (e.isMetaDown()) { // 按右鍵, 作個標記就好了
            map[tt] ^= MARK;
        } else if ((map[tt] & BOMB) != 0) { // 按到地雷了
            showAll();
            return;
        } else {
            setTouched(tt);
        }
        checkWin();
        repaint();
    }
    public static void main(String[] argv) {
        new Mine2(10,10);
    }
}