Nano Hash - криптовалюты, майнинг, программирование

Как лучше всего построить доску Minesweeper Board в JFrame?

Я просто пытаюсь построить игровое поле с плитками для работы. Я заставил его работать, раскрашивая плитку в строки и столбцы с помощью BufferedImage, но мне нужно будет постоянно обновлять графический интерфейс по мере игры.

Поэтому я попытался добавить изображение плитки в ImageIcon, а затем в массив JLabel, чтобы установить положение. После этого я бы добавил метки в графический интерфейс JPanel, но между ними были эти промежутки по умолчанию.

Я подхожу к этому неправильно? Я очень новичок в Swing и 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;
}

Вот что мне дает https://i.stack.imgur.com/rX3yM.png

Обновить, до новой версии. Предыдущая проблема была решена с помощью сетки и размещения изображений на кнопках в виде значков.

Вот что у меня есть. Это не размещение метки с текстом на нажатой кнопке. Я отладил, и он получает ввод для координат и текста, но просто не рисует его на панели.

 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);
                }
            }
        }
    }
}

  • Лучший способ создать игру Mine Sweeper — поместить кнопки с прослушивателем действий в сетку. Кнопки реагируют на ввод с клавиатуры и мыши и могут отображать значки. Макет сетки может организовать их на экране. 20.01.2017
  • Общий совет: 1) Чтобы быстрее получить помощь, опубликуйте минимально воспроизводимый пример или Короткий, автономный, правильный пример. 2) Одним из способов получения изображений для примера является горячая ссылка на изображения, которые можно увидеть в этих вопросах и ответах. 20.01.2017
  • @AndrewThompson Какие возможны кнопки? Могут ли они показывать разные изображения .png вместо изображений по умолчанию? 20.01.2017
  • Какие кнопки возможны? Могут ли они отображать другие изображения .png вместо изображений по умолчанию? Этот графический интерфейс состоит из четырех кнопок (и 5 этикетки). 21.01.2017
  • @AndrewThompson Этот код немного выше моего понимания. Я понимаю, что вы поместили изображение на 4 кнопки так, чтобы казалось, что они соответствуют расположению кнопок компаса. Итак, я бы создал сетку JButtons, а затем поместил бы над ней сетку ImageIcons? Вот в чем моя проблема. Я не могу понять, как избавиться от этих промежутков между JLabels. 21.01.2017
  • Я бы начал разделять вашу модель и представления на отдельный класс. Взгляните на Model-View-Controller для более подробной информации 21.01.2017
  • @MadProgrammer Правильно, у меня есть модель, которая создает мины и доску и проверяет соседние мины в другом моем классе. Я пытаюсь создать представление, но у меня практически нет опыта работы с Swing. Я хотел создать представление до того, как попаду в actionlisteners. Может быть, я должен просто сделать сетку из кнопок. 21.01.2017
  • @Austen Я могу придумать как минимум 3 основных способа сделать это, используя JButtons и GridLayout или GridBagLayout было бы самым простым 21.01.2017
  • ..Я понимаю, что вы поместили изображение поверх 4 кнопок, чтобы казалось, что они соответствуют расположению кнопок компаса Нет! Изображение (и оно фактически использует два разных изображения, одно для «нормального» состояния кнопки, другое с красной рамкой для сфокусированного состояния) в качестве значков изображения для самих кнопок. 21.01.2017
  • @AndrewThompson Блин, тогда у меня есть вопросы по этому поводу. Таким образом, вы берете субизображение из изображения кнопки компаса и используете его в качестве значка для обычной кнопки. Затем возьмите еще одно изображение и сделайте красный контур, чтобы использовать его в качестве значка нажатой кнопки, верно? Каково назначение переменной count? 21.01.2017
  • Какова цель переменной count? Начиная с верхнего левого угла, затем двигаясь слева направо сверху вниз, каждая вторая позиция является кнопкой (их 4), а каждая другая позиция является этикетка (5 шт.). Атрибут count позволяет легко определить, должна ли быть кнопка или метка, используя тест, показанный в count%2==1, то есть true для нечетных чисел и false для четных чисел. 21.01.2017
  • @AndrewThompson Интересно, так что он как бы разбирает изображение на 9 фрагментов и переназначает их либо на соответствующую кнопку, либо на метку. Тогда в моем коде, если я использую gridlayout на JPanel, а затем добавлю метки с изображениями, избавится ли он от пробелов? Очевидно, что теперь я буду использовать сетку кнопок, но просто чтобы посмотреть, была ли это моя проблема. 21.01.2017
  • Ага. Вы, кажется, получили понимание этого. :) Важная часть ссылки на этот пример заключалась в том, чтобы показать, что кнопка может стать фактически «невидимой», и пользователь видит только значок (вспомогательное изображение), который установлен для нее. 21.01.2017
  • Что ж, это сработало очень хорошо! Я собираюсь связать мою сгенерированную доску с графикой. Вопрос однако. У меня есть цикл, который изменяет нажатую кнопку на новый значок, также известный как пустая ячейка. Как я могу удалить прослушиватель действий или эффективно сделать кнопку ненажимаемой после этого? Я пробовал burront.removeActionListener(this) внутри actionPerformed void, но он не работает 21.01.2017
  • Совет: вы можете пометить кого-то, чтобы он получил уведомление о вашем ответе, добавив @, а затем его имя, например @AndrewThompson, вы можете вызвать метод setEnabled(false);, чтобы отключить ваш JButton, извините, я разговариваю по телефону и не могу' Ссылку на документы сейчас не кидайте, но можете поискать в гугле 21.01.2017
  • @Frakcool, о, круто, спасибо! Таким образом, кажется, что кнопка убирается с панели, с тех пор она теряет значок, показывающий, что она была нажата. Я надеялся, что смогу сделать его недоступным для нажатия, но сохранить значок. Кроме того, я пытаюсь наложить текст, показывающий цифру поверх нажатой кнопки, но я не могу заставить JLabel отображаться на панели, даже после того, как я изменил макет обратно на нуль. 21.01.2017
  • Для более быстрой помощи опубликуйте допустимый минимальный воспроизводимый пример, даже после того, как я изменил макет обратно на null это худшее, что вы могли бы сделать, пожалуйста, опубликуйте свой код 21.01.2017
  • @Frakcool я написал как ответ 21.01.2017
  • @Остен НЕТ! Не публикуйте ответ, если он не отвечает на ваш вопрос, вместо этого отредактируйте свой вопрос и добавьте его туда 21.01.2017
  • @Frakcool там. Какие-нибудь мысли? 21.01.2017
  • @Frakcool, поэтому я исправил это, изменив нажатый значок на нуль после того, как он был нажат. Но теперь я пытаюсь найти хороший способ добавить текстовую цифру к плиткам, которые были нажаты и вокруг которых есть определенное количество мин. Но я не могу добавить jlabel на панель после того, как все кнопки были добавлены 21.01.2017
  • Как только кнопка нажата, я бы добавил значок, показывающий число. Один метод может создать значок с нарисованным на нем текстом (числом). 21.01.2017

Ответы:


1

Лучший способ создать игру Mine Sweeper — поместить кнопки с прослушивателем действий в сетку. Кнопки реагируют на ввод с клавиатуры и мыши и могут отображать значки. Макет сетки может организовать их на экране.

Ниже приведен MCVE кнопок в файле GridLayout. Отличие от описанного выше в том, что он:

  • Не добавляет слушателей действий
  • Использует MineFieldModel
  • Использует текст вместо значков.

введите здесь описание изображения

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;
    }
}
23.01.2017
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

Как написать эффективное резюме
Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..