/*
 * SAD.java
 *
 * Created on May 2, 2003, 7:55 PM
 */

package games.mineseeker;

/**
 *
 * @author  walter
 */
public class SAD extends javax.swing.JFrame {
    static final javax.swing.ImageIcon [] icons = new javax.swing.ImageIcon [14];
    static final int iEMPTY = 0;
    static final int iBLANK = 9;
    static final int iBOMB = 10;
    static final int iFLAG = 11;
    static final int iNOTBOMB = 12;
    static final int iNOTFLAG = 13;
    static final int NF = 30*16;
    static final int NM = 99;
    int [] fields = new int [NF];
    int [] known = new int [NF];
    
    /** Creates new form SAD */
    public SAD() {
        java.util.Arrays.fill(icons,null);
        icons [iBLANK] = new javax.swing.ImageIcon(getClass().getResource("/games/mineseeker/blank.gif"));
        icons [iBOMB] = new javax.swing.ImageIcon(getClass().getResource("/games/mineseeker/bomb.gif"));
        icons [iEMPTY] = new javax.swing.ImageIcon(getClass().getResource("/games/mineseeker/empty.gif"));
        icons [iFLAG] = new javax.swing.ImageIcon(getClass().getResource("/games/mineseeker/flag.gif"));
        icons [iNOTBOMB] = new javax.swing.ImageIcon(getClass().getResource("/games/mineseeker/notBomb.gif"));
        icons [iNOTFLAG] = new javax.swing.ImageIcon(getClass().getResource("/games/mineseeker/notFlag.gif"));
        initComponents();
        newGame();
    }
    
    private void newGame() {
        java.util.Arrays.fill(fields,iEMPTY);
        java.util.Arrays.fill(known,iBLANK);
        int ml = NM;
        while (ml > 0) {
            int i = (int) (Math.random() * (double) NF);
            if (i >= 0 && i < NF && fields[i] == iEMPTY) {
                fields [i] = iBOMB;
                ml --;
            }
        }
        showKnown();
    }
    
    private boolean isBomb(int i) {
        return i >= 0 && i < NF && fields[i] == iBOMB;
    }
    private boolean isFlag(int i) {
        return i >= 0 && i < NF && known[i] == iFLAG;
    }
    private boolean isBlank(int i) {
        return i >= 0 && i < NF && known[i] == iBLANK;
    }
    private static int [] nAll = new int [] { -31,-30,-29,-1,1,29,30,31 };
    private static int [] nNoL = new int [] { -30,-29,1,30,31 };
    private static int [] nNoR = new int [] { -31,-30,-1,29,30 };
    private int neighbors(int i) {
        int n = 0;
        int [] nn;
        if (i % 30 == 0) { // no left neighbors
            nn = nNoL;
        } else if (i % 30 == 29) { // no right neigbors
            nn = nNoR;
        } else {
            nn = nAll;
        }
        for (int j = 0; j < nn.length; j ++) {
            if (isBomb(i+nn[j])) n ++;
        }
        return n;
    }
    private int flagsAround(int i) {
        int n = 0;
        int [] nn;
        if (i % 30 == 0) { // no left neighbors
            nn = nNoL;
        } else if (i % 30 == 29) { // no right neigbors
            nn = nNoR;
        } else {
            nn = nAll;
        }
        for (int j = 0; j < nn.length; j ++) {
            if (isFlag(i+nn[j])) n ++;
        }
        return n;
    }
    private int blanksAround(int i) {
        int n = 0;
        int [] nn;
        if (i % 30 == 0) { // no left neighbors
            nn = nNoL;
        } else if (i % 30 == 29) { // no right neigbors
            nn = nNoR;
        } else {
            nn = nAll;
        }
        for (int j = 0; j < nn.length; j ++) {
            if (isBlank(i+nn[j])) n ++;
        }
        return n;
    }
    private void showKnown() {
        getContentPane().removeAll();
        for (int i = 0; i < NF; i ++) {
            javax.swing.JLabel lb;
            if (known [i]>=iBLANK) {
                lb = new javax.swing.JLabel(icons[known [i]]);
            } else {
                int n = neighbors(i);
                if (n>0) {
                    lb = new javax.swing.JLabel("  "+n);
                    lb.setForeground(java.awt.Color.RED);
                } else {
                    lb = new javax.swing.JLabel(icons[iEMPTY]);
                }
            }
            lb.setName(Integer.toString(i));
            getContentPane().add(lb);
        }
        pack();
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    private void initComponents() {//GEN-BEGIN:initComponents
        menuBar = new javax.swing.JMenuBar();
        fileMenu = new javax.swing.JMenu();
        miNewGame = new javax.swing.JMenuItem();
        exitMenuItem = new javax.swing.JMenuItem();
        helpMenu = new javax.swing.JMenu();
        contentsMenuItem = new javax.swing.JMenuItem();
        aboutMenuItem = new javax.swing.JMenuItem();

        getContentPane().setLayout(new java.awt.GridLayout(16, 30));

        setTitle("Search And Disarm");
        addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                formMouseClicked(evt);
            }
        });

        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                exitForm(evt);
            }
        });

        fileMenu.setText("File");
        miNewGame.setText("New game");
        miNewGame.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                miNewGameActionPerformed(evt);
            }
        });

        fileMenu.add(miNewGame);

        exitMenuItem.setText("Exit");
        exitMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                exitMenuItemActionPerformed(evt);
            }
        });

        fileMenu.add(exitMenuItem);

        menuBar.add(fileMenu);

        helpMenu.setText("Help");
        contentsMenuItem.setText("Contents");
        contentsMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                contentsMenuItemActionPerformed(evt);
            }
        });

        helpMenu.add(contentsMenuItem);

        aboutMenuItem.setText("About");
        aboutMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                aboutMenuItemActionPerformed(evt);
            }
        });

        helpMenu.add(aboutMenuItem);

        menuBar.add(helpMenu);

        setJMenuBar(menuBar);

        pack();
    }//GEN-END:initComponents
    
    private void aboutMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_aboutMenuItemActionPerformed
        javax.swing.JOptionPane.showMessageDialog(this, "<html><h1>The story.</h1><hr>"+
        "It is the year "+(java.util.Calendar.getInstance().get(java.util.Calendar.YEAR)+1)+".<br>"+
        "All the world has succumbed to the Evil Bogus Gater. The Internet consists only of machines running<br>"+
        "UnmentionableWare(tm).<p>"+
        "You, hidden far away and equiped with only a PDA, are all which is left of the free world.<p>"+
        "In a desparate attempt you manage to override the PDA's UnmentionableWare(tm) with an old version of<br>"+
        "a truly free OS. The last change you have to is play a certain game on each of the corrupted machines<br>"+
        "around the world. In its normal form this is impossible: the cunning Evil Bogus Gater has, as always,<br>"+
        "provided insufficient information to solve the problem.<p>"+
        "But this <i>FREE</i> version of the game at least helps you by applying the information you <i>DO</i> have<br>"+
        "to solve the problem as far as possible.<br>"+
        "A concept so utterly foreign to the Evil Bogus Gater, you might actually succeed against all odds!<p>"+
        "</html>"
        );
        
    }//GEN-LAST:event_aboutMenuItemActionPerformed
    
    private void contentsMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_contentsMenuItemActionPerformed
        javax.swing.JOptionPane.showMessageDialog(this, "<html><ul>"+
        "<li>Click LEFT to reveal a square."+
        "<li>Click RIGHT to mark (with a flag) a square with a bomb."+
        "<li>The numbers show the number of neighboring bombs."+
        "<li>Note that the AI solver will probably kill you if you mark a wrong flag!"+
        "</ul></html>"
        );
        
    }//GEN-LAST:event_contentsMenuItemActionPerformed
    
    private void miNewGameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_miNewGameActionPerformed
        newGame();
    }//GEN-LAST:event_miNewGameActionPerformed
    
    private boolean addToKnown(int i) throws Exception {
        if (i<0 || i >= NF || known[i]!=iBLANK) return false;
        known[i]=fields[i];
        if (known[i]==iBOMB) {
            throw new Exception("Killed by AI!");
        }
        return true;
    }
    private boolean setFlag(int i) {
        if (i<0 || i >= NF || known[i]!=iBLANK) return false;
        known[i]=iFLAG;
        return true;
    }
    
    private void solve() throws Exception {
        for (int i = 0; i < NF; i ++) {
            if (known [i] != iBLANK && known [i] != iFLAG && (neighbors(i) - flagsAround(i) == 0)) {
                boolean c = false;
                int [] nn;
                if (i % 30 == 0) { // no left neighbors
                    nn = nNoL;
                } else if (i % 30 == 29) { // no right neigbors
                    nn = nNoR;
                } else {
                    nn = nAll;
                }
                for (int j = 0; j < nn.length; j ++) {
                    if (addToKnown(i+nn[j])) c = true;
                }
                if (c) solve();
            }
        }
        for (int i = 0; i < NF; i ++) {
            if (known [i] != iBLANK && known [i] != iFLAG && (neighbors(i) - flagsAround(i) == blanksAround(i))) {
                boolean c = false;
                int [] nn;
                if (i % 30 == 0) { // no left neighbors
                    nn = nNoL;
                } else if (i % 30 == 29) { // no right neigbors
                    nn = nNoR;
                } else {
                    nn = nAll;
                }
                for (int j = 0; j < nn.length; j ++) {
                    if (setFlag(i+nn[j])) c = true;
                }
                if (c) solve();
            }
        }
    }
    
    /** Dead.
     *
     * Show errors and start new game.
     */
    private void dead(String message) {
        for (int j = 0; j < NF; j ++) {
            if (known [j] == iFLAG && fields [j] != iBOMB) {
                known [j] = iNOTBOMB;
            }
            if (known [j] == iBLANK && fields [j] == iBOMB) {
                known [j] = iNOTFLAG;
            }
        }
        showKnown();
        javax.swing.JOptionPane.showMessageDialog(this, "<html><h1>"+message+"</h1><hr>You are dead.<hr></html>");
        newGame();
    }
    
    private void formMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseClicked
        try {
            javax.swing.JLabel lb = (javax.swing.JLabel) findComponentAt(evt.getPoint());
            // System.out.println(""+evt+" is " +lb+"\n"+lb.getName());
            int i = Integer.valueOf(lb.getName()).intValue();
            if (evt.getButton() == evt.BUTTON1) {
                known [i] = fields [i];
                if (known [i] == iBOMB) {
                    dead("You clicked on a bomb!");
                    return;
                }
            } else {
                known [i] = iFLAG;
            }
            try {
                solve();
            } catch (Exception ex) {
                dead(ex.getMessage());
                return;
            }
            showKnown();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        int n = 0;
        for (int i = 0; i < NF; i ++) {
            if (known [i] == iFLAG) n ++;
        }
        if (n == NM) {
            javax.swing.JOptionPane.showMessageDialog(this,"You Survived!");
            newGame();
        }
    }//GEN-LAST:event_formMouseClicked
    
    private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitMenuItemActionPerformed
        exitForm (null);
    }//GEN-LAST:event_exitMenuItemActionPerformed
    
    /** Exit the Application */
    private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm
        try {
            System.exit(0);
        } catch (java.security.AccessControlException nope) {
            dispose ();
        }
    }//GEN-LAST:event_exitForm
    
    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        new SAD().show();
    }
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JMenu fileMenu;
    private javax.swing.JMenuItem exitMenuItem;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JMenuItem miNewGame;
    private javax.swing.JMenuItem aboutMenuItem;
    private javax.swing.JMenuItem contentsMenuItem;
    private javax.swing.JMenu helpMenu;
    // End of variables declaration//GEN-END:variables
    
}
