Reputation: 1
I am trying to make a chessboard, that will randomize the place of its pieces throughout the board.
Below is what I have so far
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
public class ChessBoard extends JFrame {
JLayeredPane layeredpane;
JPanel chessboard;
JButton[][] chessboardButtons;
Color black;
JLabel [][] chessboardLabels;
UIManager Ui;
ChessBoard() {
Dimension Size = new Dimension(600, 600);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
pack();
setResizable(true);
setLocationRelativeTo(null);
setVisible(true);
setSize(600, 600);
setTitle("Chess Board");
layeredpane = new JLayeredPane();
getContentPane().add(layeredpane);
layeredpane.setPreferredSize(Size);
chessboard = new JPanel();
layeredpane.add(chessboard, JLayeredPane.DEFAULT_LAYER);
chessboard.setLayout(new GridLayout(8, 8));
chessboard.setPreferredSize(Size);
chessboard.setBounds(0, 0, Size.width, Size.height);
Ui = new UIManager();
chessboardButtons = new JButton[8][8];
black = Color.black;
ButtonHandler handler = new ButtonHandler();
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
chessboardButtons[i][j] = new JButton();
chessboardButtons[i][j].setBorderPainted(false);
if ((i + j) % 2 != 0) {
chessboardButtons[i][j].setBackground(black);
chessboardButtons[i][j].setOpaque(true);
}
chessboard.add(chessboardButtons[i][j]);
chessboardButtons[i][j].addActionListener(handler);
}
}
chessboardLabels = new JLabel[8][8];
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
chessboardLabels[i][j] = new JLabel();
chessboardLabels[i][j].setFont(new Font("Ariel", Font.BOLD, 20));
chessboardLabels[i][j].setText("H");
chessboardLabels[i][j].setHorizontalAlignment(JLabel.CENTER);
chessboardLabels[i][j].setVerticalAlignment(JLabel.CENTER);
chessboardLabels[i][j].setOpaque(true);
chessboardButtons[i][j].add(chessboardLabels[i][j]);
if(chessboardButtons[i][j].getBackground() == Color.black) {
chessboardLabels[i][j].setBackground(Color.black);
chessboardLabels[i][j].setForeground(Color.white);
}
}
}
}
private class ButtonHandler implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == chessboardButtons[0][0]) {
System.out.println("Button 0,0");
}
if (e.getSource() == chessboardButtons[0][1]) {
System.out.println("Button 0,1");
}
}
}
}
Right now I have the letter H filling all my buttons. What I need it to do is to:
Any ideas will be helpful!
Upvotes: 0
Views: 308
Reputation: 347234
First, I would start by decoupling parts of the system, this will provide you with a lot more flexibility.
For example, you want to decouple the "visual" from the "virtual". Components aren't managed in a "grid", instead, they are maintained in a list, so, you want some way you can quickly and easily ascertain the location of various components on the screen and how the relate to the "virtual" concept of the game or grid.
This is at the core concept of "model-view-controller", where the model represents a "grid" of "pieces" and the view is used to visually represent the model to the user. So you end up with a little bit of translation going on.
Now, you can do something like...
int row = (value / 8);
int col = (value % 8);
which, given a component index, will give you the row/col that they represent, but I'm also lazy 😉, so, I'm going to isolate the concept of a Piece
...
public class Piece extends JPanel {
private JLabel label;
private Point cell;
public Piece(int index) {
setLayout(new GridBagLayout());
label = new JLabel(Integer.toString(index));
label.setForeground(Color.RED);
add(label);
setOpaque(false);
}
public void setCell(Point cell) {
this.cell = cell;
}
public Point getCell() {
return cell;
}
}
This does several things for me, first, it gives me a simple building block which can be used to represent a piece of data as well as maintain the "virtual" position of the piece, so I can easily look it up, independently.
It also divorces the piece from the board, which will make it easier to maintain (IMHO).
Next, I build the board...
setLayout(new GridLayout(8, 8));
int index = 0;
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
JPanel panel = new JPanel(new GridBagLayout()) {
@Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
};
// Indexed via x/y
cells[col][row] = panel;
if (index % 2 == 0) {
panel.setBackground(Color.WHITE);
} else {
panel.setBackground(Color.BLACK);
}
add(panel);
index++;
}
index++;
}
There's nothing really fancy here, it's just GridLayout
with a bunch of color panels laid out on it.
The "fancy" part is the idea that, instead of using something complicated, like JLayeredPane
, I'm simply going to add the Piece
s directly to each cell.
Which leads us to the heart of the problem, how to randomise the position of the cells. Essentially, I'm going to create a list of numbers from 0 to 63 inclusive, randomise the list and then pop each number of off the list till I'm done.
Now, you could use an array, but filling an array with random numbers is not a simple task (especially if you want to guarantee uniqueness 😉)
// Fill a list of numbers
int totalCells = 8 * 8;
List<Integer> locations = new ArrayList<>(totalCells);
for (int value = 0; value < totalCells; value++) {
locations.add(value);
}
// Randomise the list
Collections.shuffle(locations);
// For al the white pieces, randomise their positions
for (index = 0; index < 16; index++) {
int value = locations.remove(0);
// Virtual coordinates
int row = (value / 8);
int col = (value % 8);
Point cell = new Point(col, row);
Piece piece = new Piece(index);
whitePieces[index] = piece;
piece.setCell(cell);
// Get the component offset by the value (physical)
JPanel cellPane = (JPanel) getComponent(value);
cellPane.add(piece);
}
// Now you can continue with the black pieces, just like above
// and because you've removed the "used" cell indexes from the
// list, you won't end up with duplicate positions
Now you're probably scratching your head over all this. But simply put, when you want to move a "piece", you simply remove it from it's current parent container, calculate the position ((row * 8) + col
), get the new parent component (like above) and add it, simple.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Test extends JFrame {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel[][] cells = new JPanel[8][8];
private Piece[] whitePieces = new Piece[16];
public TestPane() {
setLayout(new GridLayout(8, 8));
int index = 0;
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
JPanel panel = new JPanel(new GridBagLayout()) {
@Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
};
// Indexed via x/y
cells[col][row] = panel;
if (index % 2 == 0) {
panel.setBackground(Color.WHITE);
} else {
panel.setBackground(Color.BLACK);
}
add(panel);
index++;
}
index++;
}
int totalCells = 8 * 8;
List<Integer> locations = new ArrayList<>(totalCells);
for (int value = 0; value < totalCells; value++) {
locations.add(value);
}
Collections.shuffle(locations);
for (index = 0; index < 16; index++) {
int value = locations.remove(0);
int row = (value / 8);
int col = (value % 8);
Point cell = new Point(col, row);
Piece piece = new Piece(index);
whitePieces[index] = piece;
piece.setCell(cell);
JPanel cellPane = (JPanel) getComponent(value);
cellPane.add(piece);
}
}
}
public class Piece extends JPanel {
private JLabel label;
private Point cell;
public Piece(int index) {
setLayout(new GridBagLayout());
label = new JLabel(Integer.toString(index));
label.setForeground(Color.RED);
add(label);
setOpaque(false);
}
public void setCell(Point cell) {
this.cell = cell;
}
public Point getCell() {
return cell;
}
}
}
Oh, and just in case it's important, you can make use of component based drag-n-drop as well
But, needs drives wants 😉
Upvotes: 1
Reputation: 324128
Upvotes: 1
Reputation: 35011
I tried exactly this years ago, and I ran into several problems. One of the biggest is that dragging a piece from one square (JComponent) to another hard to do, because each JComponent has its bounds, and its Graphics clipping prevents you from drawing outside of these bounds. There are workarounds, like adding an Image to a higher level in the JLayeredPane, but this is still very hard to get right.
You really want to make you GUI a custom JComponent/JPanel that draws the whole board and can get mouse events across the whole board.
But before that, the right place to start is by creating a ChessModel that encapsulates the logic of the game. If you do that first and test it thoroughly, adding a GUI on top of that is easier than the other way around.
public class ChessModel {
char[][] board = new char[8][8];
...
public Point[] getPossibleMoves(Point pieceLocation) {
...
}
}
Upvotes: 2