如何根据二维数组上的特定位置获取网格单元的状态

How to obtain the state of grid cells based on a specific location on a 2D array

考虑具有 n rowsn columns 的二维网格(此处为 75x75)。单击鼠标在每个单元格中绘制符号(标记)。下面的代码用于在单元格内绘制网格线和符号:

class DrawCanvas extends JPanel{

        @Override
        public void paintComponent(Graphics g){
            super.paintComponent(g);
            setBackground(Color.WHITE);

            //Lines
            g.setColor(Color.BLACK);
            for(int ligne = 1; ligne < ROWS; ++ligne){
                g.fillRoundRect(0, cellSize * ligne - halfGridWidth, canvasWidth - 1,
                        gridWidth, gridWidth, gridWidth);
            }
            for(int colonne = 1; colonne < COLS; ++colonne){
                g.fillRoundRect(cellSize * colonne - halfGridWidth, 0
                        , gridWidth, canvasHeight - 1,
                        gridWidth, gridWidth);
            }

            //Symbols
            Graphics2D g2d = (Graphics2D)g;
            g2d.setStroke(new BasicStroke(symbolStrokeWidth,
                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 
            for(int ligne = 0; ligne < ROWS; ++ligne){
                for(int colonne = 0; colonne < COLS; ++colonne){
                    int x1 = colonne * cellSize + cellPadding;
                    int y1 = ligne * cellSize + cellPadding;

                    if(board[ligne][colonne] == Token.CERCLE_ROUGE){
                        g2d.setColor(Color.RED);
                        g2d.drawOval(x1, y1, symbolSize, symbolSize);
                        g2d.fillOval(x1, y1, symbolSize, symbolSize);
                    } else
                        if(board[ligne][colonne] == Token.CERCLE_BLEU){
                            int x2 = colonne * cellSize + cellPadding;
                            g2d.setColor(Color.BLUE);
                            g2d.drawOval(x1, y1, symbolSize, symbolSize);
                            g2d.fillOval(x2, y1, symbolSize, symbolSize);
                        }
                }

            }

使用下面的代码,我可以找到给定单元格的所有邻居:

private void neighbours(int  col, int row) {

     //find all serouding cell by adding +/- 1 to col and row 
    for (int colNum = col - 1 ; colNum <= (col + 1) ; colNum +=1  ) {

        for (int rowNum = row - 1 ; rowNum <= (row + 1) ; rowNum +=1  ) {

             //if not the center cell 
            if(! ((colNum == col) && (rowNum == row))) {

                //make sure it is within  grid
                if(withinGrid (colNum, rowNum)) {
                    System.out.println("Neighbor of "+ col+ " "+ row + " - " + colNum +" " + rowNum );
                }
            }
        }
    }
}

 //define if cell represented by colNum, rowNum is inside grid
private boolean withinGrid(int colNum, int rowNum) {

    if((colNum < 0) || (rowNum <0) ) {
        return false;    //false if row or col are negative
    }
    if((colNum >= COLS) || (rowNum >= ROWS)) {
        return false;    //false if row or col are > 75
    }
    return true;
}

考虑单元格内容:

public enum Token{
VIDE, CERCLE_BLEU, CERCLE_ROUGE
}

现在我想知道是否有一种方法可以确定单元格包含什么:它是否为空:Token.VIDE、具有 Token.CERCLE_BLEU 或具有 Token.CERCLE_ROUGE. 以及如何实现那。

更新: 下面是我的代码:

import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;


public final class Pha extends JFrame {

    public static int ROWS = 75;
    public static int COLS = 75;


    public static int cellSize = 15; 
    public static int canvasWidth = cellSize * COLS + (ROWS *4) ;
    public static int canvasHeight = cellSize * ROWS ; 
    public static int gridWidth = 1; 
    public static int halfGridWidth = gridWidth / 2;

    public static int cellPadding = cellSize / 5;
    public static int symbolSize = cellSize - cellPadding * 2; 
    public static int symbolStrokeWidth = 3; 

    public enum GameState{
        JOUE, NUL, CERCLE_ROUGE_GAGNE, CERCLE_BLEU_GAGNE
    }

    private GameState actualState;

    public enum Token{
        VIDE, CERCLE_ROUGE, CERCLE_BLEU
    }

    private Token actualPlayer;

    private Token[][] board;
    private final DrawCanvas canvas; 
    private JLabel statusBar; 

    public Pha(){

        canvas = new DrawCanvas(); 
        canvas.setPreferredSize(new Dimension(canvasWidth, canvasHeight));


        canvas.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) { 
        int x = e.getX();
        int y = e.getY();

        int selectedRow = y / cellSize;
        int selectedCol;
            selectedCol = x / cellSize;


        if(actualState == GameState.JOUE){
            if(selectedRow >= 0 && selectedRow < ROWS && selectedCol >= 0
                    && selectedCol < COLS &&
                    board[selectedRow][selectedCol] == Token.VIDE){
                board[selectedRow][selectedCol] = actualPlayer; 
                updateGame(actualPlayer, selectedRow, selectedCol); 
                actualPlayer = (actualPlayer == Token.CERCLE_BLEU)? Token.CERCLE_ROUGE : Token.CERCLE_BLEU;

                neighbours(selectedRow, selectedCol);
            }
        } else { 
            initGame(); 
        }

        repaint();
    }

  });

    statusBar = new JLabel("  ");
    statusBar.setFont(new Font(Font.DIALOG_INPUT, Font.ITALIC, 15));
    statusBar.setBorder(BorderFactory.createEmptyBorder(2, 5, 4, 5));

    Container cp = getContentPane();
    cp.setLayout(new BorderLayout());
    cp.add(canvas, BorderLayout.EAST);
    cp.add(statusBar, BorderLayout.NORTH);

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    pack(); 
    setTitle("Pha par esQmo");
    setVisible(true); 

    board = new Token[ROWS][COLS]; 
    initGame();   
}

    public void initGame(){
        for(int ligne = 0; ligne < ROWS; ++ligne){
            for(int colonne = 0; colonne < COLS; ++colonne){
                board[ligne][colonne] = Token.VIDE; 
            }
        }
        actualState = GameState.JOUE;
        actualPlayer = Token.CERCLE_ROUGE;
    }

    public void updateGame(Token theSeed, int ligneSelectionnee, int colonneSelectionnee) {
      if (aGagne(theSeed, ligneSelectionnee, colonneSelectionnee)) {  
         actualState= (theSeed == Token.CERCLE_ROUGE) ? GameState.CERCLE_ROUGE_GAGNE : GameState.CERCLE_BLEU_GAGNE;
      } else if (estNul()) { 
         actualState = GameState.CERCLE_BLEU_GAGNE;       
      }

   }
 public boolean estNul() {
      /*for (int row = 0; row < ROWS; ++row) {
         for (int col = 0; col < COLS; ++col) {
            if (board[row][col] == Token.VIDE) {
               return false; 
            }
         }
      }*/
      return false; 
   }

   public boolean aGagne(Token token, int ligneSelectionnee, int colonneSelectionnee) {
      return false; 

}


   public void neighbours(int  row, int col) {

    for (int colNum = col - 1 ; colNum <= (col + 1) ; colNum +=1  ) {

        for (int rowNum = row - 1 ; rowNum <= (row + 1) ; rowNum +=1  ) {

            if(!((colNum == col) && (rowNum == row))) {

                if(withinGrid (rowNum, colNum )) {

                    System.out.println("Neighbor of "+ row + " " + col + " is " + rowNum +" " + colNum );

                }
            }
        }
    }
}

private boolean withinGrid(int colNum, int rowNum) {

    if((colNum < 0) || (rowNum <0) ) {
        return false;
    }
    if((colNum >= COLS) || (rowNum >= ROWS)) {
        return false;
    }
    return true;
}

class DrawCanvas extends JPanel{

        @Override
        public void paintComponent(Graphics g){ //Invoqué via repaint()
            super.paintComponent(g); //Pour remplir l'arriere plan
            setBackground(Color.WHITE); //Defini la couleur de l'arriere plan


            g.setColor(Color.BLACK);
            for(int ligne = 1; ligne < ROWS; ++ligne){
                g.fillRoundRect(0, cellSize * ligne - halfGridWidth, canvasWidth - 1,
                        gridWidth, gridWidth, gridWidth);
            }
            for(int colonne = 1; colonne < COLS; ++colonne){
                g.fillRoundRect(cellSize * colonne - halfGridWidth, 0
                        , gridWidth, canvasHeight - 1,
                        gridWidth, gridWidth);
            }


            Graphics2D g2d = (Graphics2D)g;
            g2d.setStroke(new BasicStroke(symbolStrokeWidth,
                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 
            for(int ligne = 0; ligne < ROWS; ++ligne){
                for(int colonne = 0; colonne < COLS; ++colonne){
                    int x1 = colonne * cellSize + cellPadding;
                    int y1 = ligne * cellSize + cellPadding;

                    if(board[ligne][colonne] == Token.CERCLE_ROUGE){
                        g2d.setColor(Color.RED);
                        g2d.drawOval(x1, y1, symbolSize, symbolSize);
                        g2d.fillOval(x1, y1, symbolSize, symbolSize);
                    } else
                        if(board[ligne][colonne] == Token.CERCLE_BLEU){
                            int x2 = colonne * cellSize + cellPadding;
                            g2d.setColor(Color.BLUE);
                            g2d.drawOval(x1, y1, symbolSize, symbolSize);
                            g2d.fillOval(x2, y1, symbolSize, symbolSize);
                        }
                }

            }

            if(actualState == GameState.JOUE){
                if(actualPlayer == Token.CERCLE_ROUGE){
                    statusBar.setText("ROUGE, c'est votre tour");
                    statusBar.setForeground(Color.RED);

                } else {
                    statusBar.setText("BLEU, c'est votre tour");
                    statusBar.setForeground(Color.BLUE);
                    statusBar.addMouseMotionListener(null);
                }
            } else
                if(actualState == GameState.NUL){
                    statusBar.setForeground(Color.yellow);
                    statusBar.setText("Match nul! Cliquez pour rejouer");
                } else
                    if(actualState == GameState.CERCLE_ROUGE_GAGNE){
                        statusBar.setText("Le jouer X a remporté la partie, cliquez pour rejouer");
                        statusBar.setForeground(Color.RED);
                    } else
                        if(actualState == GameState.CERCLE_BLEU_GAGNE){
                            statusBar.setForeground(Color.BLUE);
                            statusBar.setText("Le joueur O a remporté la partie, cliquez pour rejouer");
                        }
        }
    }

    public static void main(String[] args){
        SwingUtilities.invokeLater(() -> {
            Pha pha = new Pha();
        });
    }
}

如评论中所述,考虑为程序创建模型-视图-控制器结构,该结构使用接收状态变化通知的侦听器。

例如,您可以使用相同的枚举来表示细胞状态:

public enum Token {
    VIDE, CERCLE_BLEU, CERCLE_ROUGE
}

然后模型可以包含令牌网格,或者如果它需要更复杂的东西,例如令牌和邻居计数,您可以创建一个单元格 class,其中包含令牌加上蓝色和胭脂邻居,然后让模型包含这些的网格。模型必须能够接受侦听器,然后在其状态发生变化时通知这些侦听器。对于 Swing,我经常为此使用 Java bean SwingPropertyChangeSupport,因为您可以将 PropertyChangeListeners 添加到该对象,然后当您调用它的 fireXxxx(...) 方法时,所有侦听器都会收到通知。您还需要提供 class public 允许添加(也可能删除)侦听器的方法。例如这个创建网格、添加侦听器、允许获取和设置网格单元状态并通知侦听器网格单元更改的简单模型:

import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;

public class Model {
    public static final int ROWS = 72;
    public static final int COLS = ROWS;
    public static final String TOKEN = "token";
    private Token[][] grid = new Token[ROWS][COLS];
    private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);

    public Model() {
        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[r].length; c++) {
                grid[r][c] = Token.VIDE;
            }
        }
    }

    public Token getToken(int row, int col) {
        return grid[row][col];
    }

    public void setToken(Token token, int row, int col) {
        Token oldValue = grid[row][col];
        Token newValue = token;
        grid[row][col] = token;
        int index = row * grid[row].length + col;
        pcSupport.fireIndexedPropertyChange(TOKEN, index, oldValue, newValue);
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(listener);
    }

    public void addPropertyChangeListener(String name, PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(name, listener);
    }

    public int getRows() {
        return ROWS;
    }

    public int getCols() {
        return COLS;
    }

}

关键方法是这个:

    public void setToken(Token token, int row, int col) {
        Token oldValue = grid[row][col];
        Token newValue = token;
        grid[row][col] = token;
        int index = row * grid[row].length + col;
        pcSupport.fireIndexedPropertyChange(TOKEN, index, oldValue, newValue);
    }

设置网格单元格的标记后,它会获取旧值和新值并告诉 属性 控制支持对象通知所有侦听器,传递相关信息。我有点作弊并使用了索引 属性 更改,因此我可以传入一个也包含行和列数值的索引,但我也可以创建一个简单的 Java class 来保存所有相关的新价值信息。

我的视图 class 也有一个二维网格或数组,但这里是 JLabel 的网格,包含 ImageIcon 的标签,蓝色或红色圆圈彩色图标或空白占位符图标大小与彩色图标相同。我已经给这个 class 一个将令牌状态转换为适当的 ImageIcon 的映射,并创建了 3 个 ImageIcon,每个令牌类型一个。

我创建我的 2D 网格,将 VIDE 图标添加到网格中的每个 JLabel,并向每个 JLabel 添加一个 MouseListener。请注意,鼠标侦听器 不会 更改 JLabel 的状态,而是它所做的只是通知侦听器已发生鼠标按下及其位置。这里的关键是让 GUI 或 view 尽可能地愚蠢。

GUI 还具有允许控件更改由单个 JLabel 单元格显示的图标的方法:

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.EnumMap;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class View {

    private static final int ICON_W = 12;
    public static final String CELL_SELECTION = "cell selection";
    private int rows;
    private int cols;
    private JPanel mainPanel = new JPanel();
    private JLabel[][] grid;
    private Map<Token, Icon> iconMap = new EnumMap<>(Token.class);
    private int selectedRow;
    private int selectedCol;

    public View() {
        iconMap.put(Token.VIDE, createIcon(new Color(0, 0, 0, 0)));
        iconMap.put(Token.CERCLE_BLEU, createIcon(Color.BLUE));
        iconMap.put(Token.CERCLE_ROUGE, createIcon(Color.RED));
    }

    private Icon createIcon(Color color) {
        BufferedImage img = new BufferedImage(ICON_W, ICON_W, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = img.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(color);
        g2.fillOval(1, 1, ICON_W - 2, ICON_W - 2);
        g2.dispose();

        return new ImageIcon(img);
    }

    public void createGrid(int rows, int cols) {
        MyMouse myMouse = new MyMouse();
        this.setRows(rows);
        this.cols = cols;
        mainPanel.setLayout(new GridLayout(rows, cols, 1, 1));
        mainPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
        mainPanel.setBackground(Color.BLACK);
        grid = new JLabel[rows][cols];
        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[r].length; c++) {
                grid[r][c] = new JLabel(iconMap.get(Token.VIDE));
                grid[r][c].addMouseListener(myMouse);
                grid[r][c].setOpaque(true);
                grid[r][c].setBackground(Color.WHITE);
                mainPanel.add(grid[r][c]);
            }
        }
    }

    public JPanel getMainPanel() {
        return mainPanel;
    }

    public int getSelectedRow() {
        return selectedRow;
    }

    public int getSelectedCol() {
        return selectedCol;
    }

    public void setCell(Token token, int row, int col) {
        grid[row][col].setIcon(iconMap.get(token));
    }

    public int getRows() {
        return rows;
    }

    public void setRows(int rows) {
        this.rows = rows;
    }

    private class MyMouse extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            JLabel selection = (JLabel) e.getSource();
            for (int r = 0; r < grid.length; r++) {
                for (int c = 0; c < grid[r].length; c++) {
                    if (selection == grid[r][c]) {
                        selectedRow = r;
                        selectedCol = c;
                        int index = r * grid[r].length + c;
                        mainPanel.firePropertyChange(CELL_SELECTION, -1, index);
                    }
                }
            }
        }
    }
}

控制者 class 试图将它们整合在一起。它获得一个模型和一个视图,并将它们与侦听器连接在一起。视图监听器监听鼠标点击,然后改变模型的状态。模型侦听器侦听其状态的变化,然后更改视图显示的内容。

import java.beans.IndexedPropertyChangeEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JFrame;

public class Control {
    private Model model;
    private View view;

    public Control(Model model, View view) {
        this.model = model;
        this.view = view;

        view.createGrid(model.getRows(), model.getCols());

        view.getMainPanel().addPropertyChangeListener(new ViewListener());
        model.addPropertyChangeListener(Model.TOKEN, new ModelListener());
    }

    private class ViewListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getPropertyName().equals(View.CELL_SELECTION)) {
                int row = view.getSelectedRow();
                int col = view.getSelectedCol();

                Token token = model.getToken(row, col);
                if (token == Token.VIDE) {
                    token = Token.CERCLE_BLEU;
                } else if (token == Token.CERCLE_BLEU) {
                    token = Token.CERCLE_ROUGE;
                } else {
                    token = Token.VIDE;
                }
                model.setToken(token, row, col);
            }
        }
    }

    private class ModelListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            IndexedPropertyChangeEvent iEvt = (IndexedPropertyChangeEvent)evt;
            int index = iEvt.getIndex();
            int row = index / Model.COLS;
            int col = index % Model.COLS;
            Token token = (Token) evt.getNewValue();
            view.setCell(token, row, col);
        }
    }

    public void start() {
        JFrame frame = new JFrame("MVC");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(view.getMainPanel());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

}

最后是主要的 class 开始一切

import javax.swing.SwingUtilities;

public class PhaMvcMain {

    public static void main(String[] args) {
        // run all on the Swing event thread
        SwingUtilities.invokeLater(() -> {
            Model model = new Model();
            View view = new View();
            Control control = new Control(model, view);
            control.start();
        });
    }
}

board 数组中,您为每个单元格存储 Token
要检索此信息,您可以更改

System.out.println("Neighbor of "+ row + " " + col + " is " + rowNum +" " + colNum );

neighbors()到:

System.out.println("Neighbor of "+ row + " " + col + " is " + rowNum +" " + colNum +
                        " Contains "+ board[rowNum][colNum]);

单击中间单元格 (17,19):

打印出来:

Neighbor of 17 19 is 16 18 Contains CERCLE_ROUGE
Neighbor of 17 19 is 17 18 Contains CERCLE_BLEU
Neighbor of 17 19 is 18 18 Contains CERCLE_ROUGE
Neighbor of 17 19 is 16 19 Contains CERCLE_BLEU
Neighbor of 17 19 is 18 19 Contains CERCLE_BLEU
Neighbor of 17 19 is 16 20 Contains CERCLE_ROUGE
Neighbor of 17 19 is 17 20 Contains CERCLE_BLEU
Neighbor of 17 19 is 18 20 Contains CERCLE_ROUGE