0

I'm learning Java these days, my first project is to create a "Go board", 9 * 9 rows and columns, and place black and white stones on the intersections.

I created a board with 9 * 9 lines and columns, now I have to create black and white stones using the JButton component.

Other than the color, size, and position of the button on the first row (setLayout), I was unable to turn the button into a circle and place the stone on the intersection points.

From multiple searches for related guides, I have noticed that there is some unique structure that I am not familiar with for creating and designing buttons.

And now my question comes in - what is the code structure I need to create in order to produce a button in the shape of a circle, size 65 * 65, in black or white? Do I need to create a new class for this? How and where should I integrate JPanel?

public class Main {

    public static void main(String[] args) {
        Board board = new Board(900, 900, "Go board");
    }
}

import java.awt.*;
import javax.swing.*;

public class Board extends JPanel {
    private int width;
    private int height;
    private String title;

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Board(int width, int height, String title) {
        super();
        this.width = width;
        this.height = height;
        this.title = title;
        this.initBoard();

    }

    public Board() {
        super();
    }

    public void initBoard() {
        JFrame f = new JFrame(this.getTitle());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // f.getContentPane().setBackground(Color.getHSBColor(25, 75, 47));
        f.setSize(this.getWidth(), this.getHeight());
        // f.setLocation(550, 25);
        f.add(this, BorderLayout.CENTER);
        f.setVisible(true);

        JButton stone = new JButton("    ");
        f.add(stone);
        f.setLayout(new FlowLayout());
        stone.setBackground(Color.BLACK.darker());
        stone.setBorder(BorderFactory.createDashedBorder(getForeground()));
        stone.setPreferredSize(new Dimension(65, 65));

    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        for (int i = 0; i < 10; i++) {
            g.drawLine(0, 10 + (i * ((this.getWidth() - 20) / 9)), this.getWidth(),
                    10 + (i * ((this.getWidth() - 20) / 9)));
            g.drawLine(10 + (i * ((this.getHeight() - 20) / 9)), 0, 10 + (i * ((this.getHeight() - 20) / 9)),
                    this.getHeight());
        }
    }
}

Before uploading the post, I read the following posts:

Note: I do not want to access posts that explain how to produce a "Go board", the learning process in this context is my goal.

a.v
  • 3
  • 3
  • Generally, you create a logical model of a Go board using a plain Java getter / setter class. You use a drawing JPanel to create the Go board in the GUI and draw circles to represent the stones. The Oracle tutorial, [Creating a GUI With Swing](https://docs.oracle.com/javase/tutorial/uiswing/index.html), will show you the steps to creating a Swing GUI. Skip the Netbeans section. – Gilbert Le Blanc Aug 23 '21 at 13:43
  • Creating a board of circles using custom painting (overriding `paintComponent`) is one way to go. Building the board using `JButton`s (or `JLable`s) , typically place using `GridLayout` is a different one (See an example [here](https://stackoverflow.com/a/62191460/3992939)) – c0der Aug 23 '21 at 16:15
  • 1
    *"what is the code structure I need to create in order to produce a button in the shape of a circle, size 65 * 65, in black or white?"* I'd instead use a square button formed using a circular image, with the button decorations & margin removed. For the removed parts, see [this answer](https://stackoverflow.com/a/10862262/418556). The grid of the Go board would consist of 9 other different icons. 4 x `T` shapes in each direction, 4 x corner icons, and a `+` icon for all the rest of the buttons. So ultimately it would be a 9x9 grid layout of buttons, adjusted to look like the model describe by.. – Andrew Thompson Aug 23 '21 at 17:20
  • 1
    .. @GilbertLeBlanc in the first comment. *"Do I need to create a new class for this?"* No. A plain old Java `JButton` will do, it just needs to be adjusted to need (using its existing methods). *"How and where should I integrate `JPanel`?"* What? I'm not sure I understand that, but the game board would be a (again standard) `JPanel` with a 9 x 9 `GridLayout`. – Andrew Thompson Aug 23 '21 at 17:23

2 Answers2

1

Use a JPanel with a 9x9 GridLayout and ad to it JButtons configured to your need as demonstrated in the following very basic mre:

import java.awt.*;
import java.awt.image.*;
import javax.swing.*;

public class GridOfButtons extends JPanel {

    private static final int ROWS = 9, COLS = 9, SIZE = 65, BORDER = 2;
    private static final Color BOARD_COLOR = Color.BLACK;

    public GridOfButtons() {

        setLayout(new GridLayout(ROWS, COLS, BORDER, BORDER));
        setBackground(BOARD_COLOR);

        StonesFactory factory = new StonesFactory(SIZE);
        boolean isBlack = false;

        for (int col = 0; col < COLS; col++) {
            for (int row = 0; row < ROWS; row++) {
                add(factory.makeButton(isBlack));
                isBlack = !isBlack;
            }
        }

        this.initBoard();
    }

    public void initBoard() {
        JFrame f = new JFrame("Board Of Buttons");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLayout(new GridBagLayout());
        f.add(this);
        f.pack();
        f.setVisible(true);
    }

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

class StonesFactory{

    private static final Color STONE = Color.YELLOW, WHITE_STONE = Color.WHITE, BLACK_STONE = Color.BLACK;
    private final int size;
    private final ImageIcon whiteIcon, blackIcon;

    public StonesFactory(int size) {
        this.size = size;
        whiteIcon = new ImageIcon(createImage(false));
        blackIcon = new ImageIcon(createImage(true));
    }

    JButton makeButton(boolean isBlack){
        JButton stone = new JButton();
        stone.setPreferredSize(new Dimension(size, size));
        stone.setBackground(STONE);
        stone.setIcon(isBlack ? blackIcon : whiteIcon);
        return stone;
    }

    //construct image for button's icon
    private BufferedImage createImage(boolean isBlack) {
        BufferedImage img = new BufferedImage(size , size,  BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = img.createGraphics();
        g2.setColor(isBlack ? BLACK_STONE : WHITE_STONE);
        g2.fillOval(0,0,size,size);
        g2.dispose();
        return img;
    }
}

(Run it online)


enter image description here



Alternatively you can produce the board by custom painting of a JPanel. This will make the individual "stones" not clickable and more difficult to modify:

import java.awt.*;
import javax.swing.*;

public class GridByPainting extends JPanel {

    private static final int ROWS = 9, COLS = 9, SIZE = 65, BORDER = 2;
    private static final Color BOARD_COLOR = Color.BLACK, STONE = Color.YELLOW,
            WHITE_STONE = Color.WHITE, BLACK_STONE = Color.BLACK;
    private final Dimension size;

    public GridByPainting() {
        int x = BORDER + COLS*(SIZE + BORDER);
        int y = BORDER + ROWS*(SIZE + BORDER);
        size = new Dimension(x,y);
        this.initBoard();
    }

    @Override
    public Dimension getPreferredSize() {
        return size;
    }
    public void initBoard() {
        JFrame f = new JFrame("Grid By Painting");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLayout(new GridBagLayout());
        f.add(this);
        f.pack();
        f.setVisible(true);
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        int width = getWidth(); int height = getHeight();
        int stoneWidth = (width - BORDER) / COLS  - BORDER;
        int stoneHeight = (height -BORDER)/ ROWS - BORDER ;

        //draw board
        g.setColor(BOARD_COLOR);
        g.fillRect(0, 0, width, height);

        boolean isBlack = true;
        //draw square stones
        for (int col = 0; col < COLS; col++) {
            for (int row = 0; row < ROWS; row++) {
                int x = BORDER + col*(stoneWidth + BORDER);
                int y = BORDER + row*(stoneHeight + BORDER);
                g.setColor(STONE);
                g.fillRect(x, y, stoneWidth, stoneHeight);
                //draw circle
                g.setColor(isBlack ? BLACK_STONE : WHITE_STONE);
                isBlack = !isBlack;
                g.fillOval(x, y, stoneWidth, stoneHeight);
            }
        }
    }

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

(Run it online)


enter image description here

c0der
  • 18,467
  • 6
  • 33
  • 65
0

It seems like you skipped over some of the important parts of the Oracle tutorial, Creating a GUI With Swing.

Here's my comment from August 23rd, just 10 days ago.

Generally, you create a logical model of a Go board using a plain Java getter / setter class. You use a drawing JPanel to create the Go board in the GUI and draw circles to represent the stones. The Oracle tutorial, Creating a GUI With Swing, will show you the steps to creating a Swing GUI. Skip the Netbeans section.

So, where's your logical model? Where's your drawing JPanel?

Here's a quick GUI I created.

Go Board

My code has a logical model. My code has a drawing JPanel.

The first thing I did was create a plain Java getter / setter class to hold a logical representation of a Go Board. I named this the Board class.

The next thing I did was start my Swing GUI with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.

I used the run method of my Runnable class to create the JFrame. The JFrame methods must be called in a specific order. This is the order I use for all my Swing applications.

I separate the creation of the JFrame from the creation of any subsequent JPanels. I do this to keep my code organized, easy to read, and easy to understand.

I extend a JPanel to create the drawing JPanel. I do this so I can override the paintComponent method of the JPanel class. The drawing JPanel draws (paints) the board state. That's all. Nothing else. Another class will take care of adding pieces to the logical Go board and repainting the drawing JPanel.

The MoveListener class implements a MouseListener (extends a MouseAdapter) to respond to mouse clicks on the Go board. The MoveListener class keeps track of whose turn it is. In a more elaborate version of a Go board, you would have another plain Java getter / setter class to keep track of the game state.

Here's the complete runnable code. I made all the classes inner classes so I could post this code as one block.

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class GoBoard implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new GoBoard());
    }
    
    private Board board;
    
    private DrawingPanel drawingPanel;
    
    public GoBoard() {
        this.board = new Board();
    }

    @Override
    public void run() {
        JFrame frame = new JFrame("Go Board");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        this.drawingPanel = new DrawingPanel(board);
        frame.add(drawingPanel, BorderLayout.CENTER);
        
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }
    
    public class DrawingPanel extends JPanel {

        private static final long serialVersionUID = 1L;
        
        private final int margin, pieceRadius, lineSpacing;
        
        private Board board;
        
        public DrawingPanel(Board board) {
            this.board = board;
            
            this.margin = 60;
            this.pieceRadius = 40;
            this.lineSpacing = 100;
            this.setBackground(new Color(0x993300));
            int width = 8 * lineSpacing + margin + margin;
            this.setPreferredSize(new Dimension(width, width));
            this.addMouseListener(new MoveListener(board));
        }
        
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            paintHorizontalLines(g2d);
            paintVerticalLines(g2d);
            paintPieces(g2d);
        }
        
        private void paintHorizontalLines(Graphics2D g2d) {
            int x = margin;
            int y1 = margin;
            int y2 = getHeight() - margin;
            
            g2d.setColor(Color.YELLOW);
            g2d.setStroke(new BasicStroke(3f));
            for (int index = 0; index < 9; index++) {
                g2d.drawLine(x, y1, x, y2);
                x += lineSpacing;
            }
        }
        
        private void paintVerticalLines(Graphics2D g2d) {
            int x1 = margin;
            int x2 = getWidth() - margin;
            int y = margin;
            
            g2d.setColor(Color.YELLOW);
            g2d.setStroke(new BasicStroke(3f));
            for (int index = 0; index < 9; index++) {
                g2d.drawLine(x1, y, x2, y);
                y += lineSpacing;
            }
        }
        
        private void paintPieces(Graphics2D g2d) {
            int[][] b = board.getBoard();
            for (int row = 0; row < b.length; row++) {
                for (int column = 0; column < b[row].length; column++) {
                    int x = column * lineSpacing + margin;
                    int y = row * lineSpacing + margin;
                    if (b[row][column] == 1) {
                        g2d.setColor(Color.BLACK);
                        g2d.fillOval(x - pieceRadius, y - pieceRadius, 
                                pieceRadius + pieceRadius, pieceRadius + pieceRadius);
                    } else if (b[row][column] == 2) {
                        g2d.setColor(Color.WHITE);
                        g2d.fillOval(x - pieceRadius, y - pieceRadius, 
                                pieceRadius + pieceRadius, pieceRadius + pieceRadius);
                    }
                }
            }
        }
        
    }
    
    public class MoveListener extends MouseAdapter {
        
        private boolean isBlackTurn = true;
        
        private Board board;
        
        public MoveListener(Board board) {
            this.board = board;
        }

        @Override
        public void mouseReleased(MouseEvent event) {
            Point point = event.getPoint();
            int margin = 60;
            int pieceRadius = 40;
            int lineSpacing = 100;
            int column = (point.x - margin + pieceRadius) / lineSpacing;
            int row = (point.y - margin + pieceRadius) / lineSpacing;
            int piece = (isBlackTurn) ? 1 : 2;
            board.setPiece(piece, row, column);
            drawingPanel.repaint();
            isBlackTurn = !isBlackTurn;
        }
        
    }
    
    public class Board {
        
        private int[][] board;
        
        public Board() {
            this.board = new int[9][9];
        }
        
        /**
         * <p>
         * This method inserts a piece on the board.
         * </p>
         * 
         * @param piece  - 1 for black, 2 for white
         * @param row    - row of piece
         * @param column - column of piece
         */
        public void setPiece(int piece, int row, int column) {
            this.board[row][column] = piece;
        }

        public int[][] getBoard() {
            return board;
        }
        
    }

}
Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111