What is the best way to build a Minesweeper Board in a JFrame?

拜拜、爱过 提交于 2020-01-14 07:02:47

问题


I am just trying to construct a gameboard with tiles to work with. I got it to work by painting the tile into rows and columns with BufferedImage, but I am going to need to constantly update the GUI as the game is played.

So I tried to add the tile image to an ImageIcon, then to a JLabel array so that I could set the position. After that I would add the labels to the gui JPanel, but they had these default gaps between them.

Am I approaching this incorrectly? I am very new to Swing and Java

    public static void main (String[] args) throws IOException{
    int NUMROWS = 10;
    int NUMCOLS = 10;
    int NUMMINES = 10;

    int[] mineList = GameBoard.setMines(NUMMINES, NUMROWS, NUMCOLS);
    int[][] cellNums = GameBoard.setCellNum(NUMROWS, NUMCOLS);
    boolean[][] mineField = GameBoard.getMineField(mineList, cellNums);
    int[][] adjacentMineVals = GameBoard.getAdjacentMineVals(cellNums, mineField);
    ImageIcon img = new ImageIcon(ImageIO.read(new File("GraphicFile\\Cell.png")));
    JLabel[][] label = new JLabel[NUMROWS][NUMCOLS];

    for (int i = 0; i < NUMROWS; i++){
        for (int j = 0; j < NUMCOLS; j++){
            label[i][j] = new JLabel();
            label[i][j].setIcon(img);
            label[i][j].setLocation(i*img.getIconHeight(), j*img.getIconWidth());
        }
    }
    JFrame frame = buildFrame();
    int fX = 2*frame.getInsets().left;
    int fY = (frame.getInsets().top + frame.getInsets().bottom);

    JPanel GUI = new JPanel();
    GUI.setSize(NUMCOLS*img.getIconWidth(), NUMROWS*img.getIconHeight());
    for (int i = 0; i < NUMCOLS; i++){
        for (int j = 0; j < NUMROWS; j ++){
            GUI.add(label[i][j]);

        }
    }
    frame.setSize(NUMCOLS*img.getIconWidth() + fX, NUMROWS*img.getIconHeight() + fY);
    frame.add(GUI);

}

public static JFrame buildFrame(){
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
    return frame;
}

Here's what it gives me https://i.stack.imgur.com/rX3yM.png

Update, to new version. Previous problem has been solved by using a grid and putting the images into the buttons as icons

Here is what I have. It is not putting the label with the text onto the button that has been pressed. I debugged, and its receiving input for coords and for text, but its just not painting it onto the panel

 public MinesweeperGraphics() throws IOException {
    GUI.setOpaque(false);

    for (int i = 0; i < NUMROWS; i++){
        for (int j = 0; j < NUMCOLS; j++){
            buttons[i][j] = new JButton();
            buttons[i][j].setIcon(tileUp);
            buttons[i][j].setBorder(null);
            buttons[i][j].addActionListener(this);
            buttons[i][j].setPressedIcon(tilePressed);

            GUI.add(buttons[i][j]);
        }
    }

    frame.add(GUI, BorderLayout.CENTER);
    frame.add(reset, BorderLayout.NORTH);
    reset.addActionListener(this);
    frame.setResizable(false);
    frame.pack();
    GUI.setLayout(null);

}

@Override
public void actionPerformed(ActionEvent e) {
    if (e.getSource().equals(reset)){
        for (int i = 0; i < NUMROWS; i++){
            for (int j = 0; j < NUMCOLS; j++){
                buttons[i][j].setIcon(tileUp);
            }
        }
    }else {
        for (int i = 0; i < buttons.length; i++) {
            for (int j = 0; j < buttons.length; j++) {
                if (e.getSource().equals(buttons[i][j])){
                    if (mineField[i][j] == false){
                        buttons[i][j].setIcon(tileEmpty);
                        numberText.setOpaque(false);
                        numberText.setSize(buttons[i][j].getWidth(), buttons[i][j].getHeight());
                        numberText.setText("a");
                        numberText.setLocation(buttons[i][j].getLocation());
                        GUI.add(numberText);

                    } else if (mineField[i][j] == true){
                        buttons[i][j].setIcon(tileExplodedMine);
                    }
                    //isn't working here
                    buttons[i][j].setEnabled(false);
                }
            }
        }
    }
}

回答1:


The best way to build a Mine Sweeper game would be to put buttons with an action listener into a grid layout. The buttons will respond to both keyboard and mouse input & can display icons. The grid layout can organise them on-screen.

Following is an MCVE of buttons in a GridLayout. Different to as described above is that it:

  • Does not add action listeners
  • Uses a MineFieldModel
  • Uses text instead of icons.

import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class MineSweeper {

    private JComponent ui = null;
    private MineFieldModel mineFieldModel;
    Color[] colors = {
        Color.BLUE,
        Color.CYAN.darker(),
        Color.GREEN.darker(),
        Color.YELLOW.darker(),
        Color.ORANGE.darker(),
        Color.PINK.darker(),
        Color.MAGENTA,
        Color.RED
    };
    public final static String BOMB = new String(Character.toChars(128163));
    JButton[][] buttons;
    int size = 16;

    MineSweeper() {
        initUI();
    }

    public final void initUI() {
        if (ui != null) {
            return;
        }

        ui = new JPanel(new BorderLayout(4, 4));
        ui.setBorder(new EmptyBorder(4, 4, 4, 4));

        mineFieldModel = new MineFieldModel(16, 40);

        JPanel mineFieldContainer = new JPanel(new GridLayout(
                size, size));
        ui.add(mineFieldContainer, BorderLayout.CENTER);
        int in = 5;
        Insets insets = new Insets(in, in, in, in);
        Font f = getCompatibleFonts().firstElement().deriveFont(16f);
        buttons = new JButton[size][size];
        for (int ii = 0; ii < size; ii++) {
            for (int jj = 0; jj < size; jj++) {
                JButton b = new JButton();
                b.setMargin(insets);
                b.setFont(f);
                b.setText("?");
                if (mineFieldModel.isExposed(ii, jj)) {
                    if (mineFieldModel.isBomb(ii, jj)) {
                        b.setForeground(Color.red);
                        b.setForeground(Color.BLACK);
                        b.setText(BOMB);
                    } else if (mineFieldModel.countSurroundingMines(ii, jj) > 0) {
                        int count = mineFieldModel.countSurroundingMines(ii, jj);
                        if (count > 0) {
                            b.setForeground(colors[count - 1]);
                            b.setText("" + count);
                        }
                    } else {
                        b.setText("");
                    }
                }
                mineFieldContainer.add(b);
            }
        }
    }

    private static Vector<Font> getCompatibleFonts() {
        Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
        Vector<Font> fontVector = new Vector<>();

        for (Font font : fonts) {
            if (font.canDisplayUpTo("12345678" + BOMB) < 0) {
                fontVector.add(font);
            }
        }

        return fontVector;
    }

    public JComponent getUI() {
        return ui;
    }

    public static void main(String[] args) {
        Runnable r = () -> {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (Exception useDefault) {
            }
            MineSweeper o = new MineSweeper();

            JFrame f = new JFrame(o.getClass().getSimpleName());
            f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            f.setLocationByPlatform(true);

            f.setContentPane(o.getUI());
            f.pack();
            f.setMinimumSize(f.getSize());

            f.setVisible(true);
        };
        SwingUtilities.invokeLater(r);

    }
}

class MineFieldModel {

    public int size;
    /**
     * Records bomb locations.
     */
    boolean[][] mineField;
    /**
     * Records whether this location has been exposed.
     */
    boolean[][] fieldPlaceExposed;
    int numberMines;
    Random r = new Random();

    MineFieldModel(int size, int numberMines) {
        this.size = size;
        this.numberMines = numberMines;

        mineField = new boolean[size][size];
        fieldPlaceExposed = new boolean[size][size];
        ArrayList<Point> locations = new ArrayList<>();
        for (int ii = 0; ii < this.size; ii++) {
            for (int jj = 0; jj < size; jj++) {
                mineField[ii][jj] = false;
                // must change this to false for the actual game.
                fieldPlaceExposed[ii][jj] = true;
                Point p = new Point(ii, jj);
                locations.add(p);
            }
        }
        Collections.shuffle(locations, r);
        for (int ii = 0; ii < numberMines; ii++) {
            Point p = locations.get(ii);
            mineField[p.x][p.y] = true;
        }
    }

    public boolean isBomb(int x, int y) {
        return mineField[x][y];
    }

    public boolean isExposed(int x, int y) {
        return fieldPlaceExposed[x][y];
    }

    public int getSize() {
        return size;
    }

    public int countSurroundingMines(int x, int y) {
        int lowX = x - 1;
        lowX = lowX < 0 ? 0 : lowX;
        int highX = x + 2;
        highX = highX > size ? size : highX;

        int lowY = y - 1;
        lowY = lowY < 0 ? 0 : lowY;
        int highY = y + 2;
        highY = highY > size ? size : highY;

        int count = 0;
        for (int ii = lowX; ii < highX; ii++) {
            for (int jj = lowY; jj < highY; jj++) {
                if (ii != x || jj != y) {
                    if (mineField[ii][jj]) {
                        count++;
                    }
                }
            }
        }

        return count;
    }
}


来源:https://stackoverflow.com/questions/41771970/what-is-the-best-way-to-build-a-minesweeper-board-in-a-jframe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!