Finishing Keno Java code? - java

This is what I am having trouble with. I am making a Keno game out of a Crystals code I was given. So far, I have been able to choose 20 of the 80 and turn them red. However, I have the button set to when I press it it says "Game is a draw" and the window closes.
I am trying to put in a code where it will randomly draw 20 numbers without repeating, and then they turn yellow, however, if the CPU and the human match a number, it will turn green. I have no idea how to start this code or how to go about doing it. Here is what I have:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
import java.lang.String;
public class Kenogame {
// constants
public static final int WIDTH = 4;
public static final int HEIGHT = 20;
public static final int INITIAL = 0;
public static final int RED = 1;
public static final int YELLOW = 3;
public static final int CHECKED = 4;
private int turn = RED; // to track which player should play next
private int[][] playerGrid; // to record each player's move
private int[][] shadowGrid; // to keep track of which atoms have been FOUND
private int[][] crystalGrid; // to extract a single crystal from playerGrid
private int row, column; // position of most recently added atom
private int lowX, lowY, highX, highY; // corner coordinates of current crystal
private int player1Score = 0;
private int player2Score = 0;
// GUI related fields
private JButton[] buttonArray;
// private JTextField scoreField1;
// private JTextField scoreField2;
// private JLabel labelRED; // Label "Red" on GUI
// private JLabel labelYELLOW; // Label "Yellow" on GUI
private JLabel labelTurn; // Label displays whose turn is next
private int numberToSelect = 20;
Kenogame() {
createGUIAndPlay();
}
private void createGUIAndPlay() {
final JFrame f = new JFrame();
// create the panels
JPanel topPanel = new JPanel(new BorderLayout());
JPanel buttonPanel = new JPanel(new GridLayout(WIDTH, HEIGHT));
JPanel labelPanel = new JPanel();
// represents the 2D grid of buttons on the GUI
buttonArray = new JButton[WIDTH * HEIGHT];
// stores the positions of atoms in both player's crystals
playerGrid = new int[WIDTH][HEIGHT];
// shadowGrid keeps track of which atoms have been found
shadowGrid = new int[WIDTH][HEIGHT];
// used to store a crystal to determine if it is a perfect crystal
crystalGrid = new int[WIDTH][HEIGHT];
JButton endGameButton = new JButton("Start Draw");
// labelRED = new JLabel("Red");
// scoreField1 = new JTextField(3);
// scoreField1.setEditable(false);
//labelYELLOW = new JLabel("Yellow");
labelTurn = new JLabel(Integer.toString(numberToSelect), Label.LEFT);
Dimension dim = labelTurn.getPreferredSize();
labelTurn.setPreferredSize(new Dimension(dim.width + 100, dim.height + 10));
// scoreField2 = new JTextField(3);
// scoreField2.setEditable(false);
// scoreField1.setText("0");
// scoreField2.setText("0");
// create the buttons on which players will make their moves
for (int i = 0; i < HEIGHT * WIDTH; i++) {
buttonArray[i] = new JButton(Integer.toString(i + 1));
buttonPanel.add(buttonArray[i]);
}
final Color buttColor = buttonArray[0].getBackground();
// add the action listener to the buttons
for (int i = 0; i < HEIGHT * WIDTH; i++) {
buttonArray[i].setActionCommand(Integer.toString(i));
buttonArray[i].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JButton button = (JButton) e.getSource();
if (button.getBackground() == Color.RED) {
button.setBackground(buttColor);
numberToSelect++;
} else {
if (numberToSelect > 0) {
button.setBackground(Color.RED);
numberToSelect--;
}
}
// button.setEnabled(false);
int buttonIndex = Integer.valueOf(button.getActionCommand());
row = buttonIndex / WIDTH;
column = buttonIndex % WIDTH;
// playMove();
labelTurn.setText(Integer.toString(numberToSelect));
// updateGUI();
}
});
}
endGameButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String s;
// Need to add "ball draw here" for Keno
// 20 random numbers between 1 and 80
// each time a "ball" matches one selected on the Keno ticket add to a counter
// at end look up payout for the number of matches corresponding to the counter
if (player1Score > player2Score)
s = "RED wins the game";
else if (player1Score < player2Score)
s = "YELLOW wins the game";
else
s = "Game is a draw";
JOptionPane.showMessageDialog(f, s, "Game Over", JOptionPane.PLAIN_MESSAGE);
System.exit(1);
}
});
labelPanel.add(endGameButton);
labelPanel.add(labelTurn);
topPanel.add(labelPanel, BorderLayout.NORTH);
topPanel.add(buttonPanel, BorderLayout.CENTER);
f.add(topPanel);
f.setSize(1000, 400);
f.setTitle("Keno");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
private void playMove() {
playerGrid[row][column] = turn;
if (turn == RED) {
turn = YELLOW;
} else {
turn = RED;
}
}
private void initialize() {
highX = highY = Integer.MIN_VALUE;
lowX = lowY = Integer.MAX_VALUE;
for (int row = 0; row < HEIGHT; row++)
for (int column = 0; column < WIDTH; column++) {
crystalGrid[row][column] = INITIAL;
}
}
public static void main(String[] args) {
new Keno();
}
}
// green for a match, yellow for a no-match.
I have 4 lines towards the bottom where I am supposed to insert the code. I would really appreciate it if someone could give me a hand on this.

To draw 20 non-repeating numbers, you can do the following:
put your 80 numbers in a collection.
shuffle the collection
the elements are now in random order and you can loop over the collection to get the first 20 elements.
If you give me more details on what you are stuck, I can provide further information.

Related

How to Randomize Placement of a JLabel on a GridLayout of JButtons?

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:
limit the number of "H"'s there are on the board to be 32, 16 "White" and 16 "black" and
randomize the placement throughout the board.
Any ideas will be helpful!
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) {
...
}
}
Create an ArrayList with 64 Integers numbered from 0 - 63
Use Collections.shuffle(...) to shuffle the numbers randomly
Take the first 16 values from the ArrayList and add white pieces to the board based on the integer value.
Take the next 16 values from the ArrayList and add the black pieces to the board.
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 Pieces 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
How to make draggable components with ImageIcon
JLayeredPanel layout manager free moving objects
But, needs drives wants 😉

While buttons are changing color every second, how do I make the button I press stop while the other buttons continue switching colors?

I am creating a grid of 4 by 2 buttons and I want them to continuously change color. A button will stop changing colors once I press it. I have having trouble figuring how to make that one button stop changing colors if I press on it and have other buttons to continuously change color. If I press another button, that button stops changing color as well.
public class Hw1 extends JPanel {
static JFrame jf;
static JPanel jp;
static JButton jb;
public static void main(String[] args) {
jf = new JFrame("Hello World!");
int xAxis = 500;
int yAxis = 300;
jf.setSize(xAxis, yAxis);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jp = new JPanel();
int num = 8;
JButton[] buttonList = new JButton[num];
int count = 0;
// creates buttons and initially assigns random color
for(int i = 0; i < 2 ; i++){
for(int j = 0; j < 4; j++){
jb = new JButton("press me!");
jb.setBounds(i * (xAxis/2), j * (yAxis/4), xAxis/2, yAxis/4);
jb.setVisible(true);
jb.setOpaque(true);
int primeR = (int)(Math.random() * 255+ 0);
int primeG = (int)(Math.random() * 255 + 0);
int primeB = (int)(Math.random() * 255 + 0);
Color random = new Color(primeR, primeG, primeB);
jb.setBackground(random);
// adds action listener to each button aka checks if button is pressed
jb.addActionListener(new ActionListener(){
// action if button is pressed
//if pressed change stop changing colors
public void actionPerformed(ActionEvent ae) {
JButton theButton = (JButton)ae.getSource();
theButton.setBackground(random);
}
});
buttonList[count] = jb;
count++;
jp.add(jb);
}
}
jf.add(jp);
jf.setVisible(true);
for (int k = 0; k < buttonList.length; k++) {
new Thread(){
public void run(){
while(true){
try {
sleep(1000);
}
catch (InterruptedException ex) {
}
for (int i = 0; i < buttonList.length; i++) {
int primeR = (int)(Math.random() * 255);
int primeG = (int)(Math.random() * 255);
int primeB = (int)(Math.random() * 255);
Color random = new Color(primeR, primeG, primeB);
buttonList[i].setBackground(random);
}
}
}
}.start();
}
}
}
you need to keep state of button.
If you have grid of 4 button, make an array of it's state buttonState = new Boolean[2][2]
when button on position x,y is pressed, set value of [x][y] to true. check buttonState before changing color of button.
OR
Overwrite JButton and add bolean field pressed to it, and set it to true in onClick, and based on that value change or not change color

Java Game of life wrap around

i have an up and running version of the game of life, but one thing i can not figure out is how to wrap around the Grid or Board, i am guessing is has to be something to do with the neighbor counts and the grid, i need a way to indicate that the array wraps.
The rules :
The universe of the Game of Life is an infinite two-dimensional
orthogonal grid of square cells,
each of which is in one of two possible states, live or dead.
Every cell interacts with its eight neighbors, which are the cells
that are directly horizontally,
vertically, or diagonally adjacent. At each step in time, the
following transitions occur:
1.Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
2.Any live cell with more than three live neighbours dies, as if by overcrowding.
3.Any live cell with two or three live neighbours lives on to the next generation.
4.Any dead cell with exactly three live neighbours becomes a live cell.
The initial pattern constitutes the seed of the system. The first
generation is created by applying the above rules simultaneously to
every cell in the seed—births and deaths happen simultaneously.
Here's some of the code that will be relating to the grid/board; Called
CellsGrid Cells;
GameOfLife2(int nbRow, int nbCol) {
super(" New GameOfLife");
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// create the labels (2 more on each size) these wont be shown
// but will be used in calculating the cells alive around
Cells = new CellsGrid[nbRow+2][nbCol+2];
for(int r = 0; r < nbRow+2; r++) {
for(int c = 0; c < nbCol+2; c++) {
Cells[r][c] = new CellsGrid();
}
}
for(int r = 1; r < nbRow+1; r++) {
for(int c = 1; c < nbCol+1; c++) {
panel.add(Cells[r][c]);
Cells[r][c].addNeighbour(Cells[r-1][c]); // North
Cells[r][c].addNeighbour(Cells[r+1][c]); // South
Cells[r][c].addNeighbour(Cells[r][c-1]); // West
Cells[r][c].addNeighbour(Cells[r][c+1]); // East
Cells[r][c].addNeighbour(Cells[r-1][c-1]); // North West
Cells[r][c].addNeighbour(Cells[r-1][c+1]); // North East
Cells[r][c].addNeighbour(Cells[r+1][c-1]); // South West
Cells[r][c].addNeighbour(Cells[r+1][c+1]); // South East
}
}
if(!gameRunning)
return;
++generation;
CellsIteration.setText("Generation: " + generation);
for(int r = 0; r < Cells.length; r++) {
for(int c = 0; c < Cells[r].length; c++) {
Cells[r][c].checkState();
}
}
for(int r = 0; r < Cells.length; r++) {
for(int c = 0; c < Cells[r].length; c++) {
Cells[r][c].updateState();
}
}
}
void checkState() {
// number alive around
int NumNeighbours = 0; // number alive neighbours
// see the state of my neighbour
for(int i = 0; i < numNeighbours; i++)
NumNeighbours += neighbour[i].state;
// newState
if(state == 1) { // if alive
if(NumNeighbours < 2) // 1.Any live cell with fewer than two live neighbours dies
newState = 0;
if(NumNeighbours > 3) // 2.Any live cell with more than three live neighbours dies
newState = 0;
}
else {
if(NumNeighbours == 3) // 4.Any dead cell with exactly three live neighbours becomes a live cell
newState = 1;
}
}
full code:
package com.ggl.life;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
public class GameOfLife2 extends JFrame implements ActionListener {
/**
*
*/
public static Random random = new Random();
private static final long serialVersionUID = 1L;
static final Color[] color = {Color.YELLOW, Color.BLACK};
// size in pixel of every label
static final int size = 15;
static final Dimension dim = new Dimension(size, size);
static final int GenDelay = 200;
// the cells labels
private CellsGrid[][] Cells;
// timer that fires the next generation
private Timer timer;
// generation counter
private int generation = 0;
private JLabel CellsIteration = new JLabel("Generation: 0");
// the 3 buttons
private JButton clearBtn = new JButton("Clear"),
PauseBtn = new JButton("Pause"),
StartBtn = new JButton("Start");
// the slider for the speed
// state of the game (running or pause)
private boolean gameRunning = false;
// if the mouse is down or not
private boolean mouseDown = false;
GameOfLife2(int nbRow, int nbCol) {
super(" New GameOfLife");
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// create the labels (2 more on each size) these wont be shown
// but will be used in calculating the cells alive around
Cells = new CellsGrid[nbRow+2][nbCol+2];
for(int r = 0; r < nbRow+2; r++) {
for(int c = 0; c < nbCol+2; c++) {
Cells[r][c] = new CellsGrid();
}
}
// panel in the center with the labels
JPanel panel = new JPanel(new GridLayout(nbRow, nbCol, 1, 1));
panel.setBackground(Color.BLACK);
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
// add each label (not the one on the border) to the panel and add to each of them its neighbours
for(int r = 1; r < nbRow+1; r++) {
for(int c = 1; c < nbCol+1; c++) {
panel.add(Cells[r][c]);
Cells[r][c].addNeighbour(Cells[r-1][c]);
//Cells[r][c].addNeighbour(getCellSafe(r-1, c)); // North
Cells[r][c].addNeighbour(Cells[r+1][c]); // South
//Cells[r][c].addNeighbour(getCellSafe(r+1, c));
Cells[r][c].addNeighbour(Cells[r][c-1]); // West
//Cells[r][c].addNeighbour(getCellSafe(r, c-1));
Cells[r][c].addNeighbour(Cells[r][c+1]); // East
//Cells[r][c].addNeighbour(getCellSafe(r, c+1));
Cells[r][c].addNeighbour(Cells[r-1][c-1]); // North West
//Cells[r][c].addNeighbour(getCellSafe(r-1, c-1));
Cells[r][c].addNeighbour(Cells[r-1][c+1]); // North East
//Cells[r][c].addNeighbour(getCellSafe(r-1, c+1));
Cells[r][c].addNeighbour(Cells[r+1][c-1]); // South West
//Cells[r][c].addNeighbour(getCellSafe(r+1, c-1));
Cells[r][c].addNeighbour(Cells[r+1][c+1]); // South East
//Cells[r][c].addNeighbour(getCellSafe(r+1, +c));
}
}
// now the panel can be added
add(panel, BorderLayout.CENTER);
// the bottom panel with the buttons the generation label and the slider
// this panel is formed grid panels
panel = new JPanel(new GridLayout(1,3));
// another panel for the 3 buttons
JPanel buttonPanel = new JPanel(new GridLayout(1,3));
clearBtn.addActionListener(this);
buttonPanel.add(clearBtn);
PauseBtn.addActionListener(this);
PauseBtn.setEnabled(false); // game is pause the pause button is disabled
buttonPanel.add(PauseBtn);
StartBtn.addActionListener(this);
buttonPanel.add(StartBtn);
// add the 3 buttons to the panel
panel.add(buttonPanel);
// the generation label
CellsIteration.setHorizontalAlignment(SwingConstants.CENTER);
panel.add(CellsIteration);
// in the JFrame
add(panel, BorderLayout.NORTH);
// put the frame on
setLocation(20, 20);
pack(); // adjust to the window size
setVisible(true);
// start the thread that run the cycles of life
timer = new Timer(GenDelay , this);
}
private CellsGrid getCellSafe(int r0, int c0) {
int r = r0 % Cells.length; // Cells.length is effectively nbRow
if (r < 0) r += Cells.length; // deal with how % works for negatives
int c = c0 % Cells[0].length; // Cells[0].length is effectively nbCol
if (c < 0) c += Cells[0].length; // deal with how % works for negatives
return Cells[r][c];
}
//end of game of life
// called by the Timer and the JButtons
public synchronized void actionPerformed(ActionEvent e) {
// test the JButtons first
Object o = e.getSource();
// the clear button
if(o == clearBtn) {
timer.stop(); // stop timer
gameRunning = false; // flag gamme not running
PauseBtn.setEnabled(false); // disable pause button
StartBtn.setEnabled(true); // enable go button
// clear all cells
for(int r = 1; r < Cells.length ; r++) {
for(int c = 1; c < Cells[r].length ; c++) {
Cells[r][c].clear();
}
}
// reset generation number and its label
generation = 0;
CellsIteration.setText("Generation: 0");
return;
}
// the pause button
if(o == PauseBtn) {
timer.stop(); // stop timer
gameRunning = false; // flag not running
PauseBtn.setEnabled(false); // disable myself
StartBtn.setEnabled(true); // enable go button
return;
}
// the go button
if(o == StartBtn) {
PauseBtn.setEnabled(true); // enable pause button
StartBtn.setEnabled(false); // disable myself
gameRunning = true; // flag game is running
timer.setDelay(GenDelay);
timer.start();
return;
}
// not a JButton so it is the timer
// set the delay for the next time
timer.setDelay(GenDelay);
// if the game is not running wait for next time
if(!gameRunning)
return;
++generation;
CellsIteration.setText("Generation: " + generation);
for(int r = 0; r < Cells.length; r++) {
for(int c = 0; c < Cells[r].length; c++) {
Cells[r][c].checkState();
}
}
for(int r = 0; r < Cells.length; r++) {
for(int c = 0; c < Cells[r].length; c++) {
Cells[r][c].updateState();
}
}
}
//end of action
// to start the whole thing as a Java application
public static void main(String[] arg) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new GameOfLife2(50, 50);
}
});
}
// A class that extends JLabel but also check for the neigbour
// when asked to do so
class CellsGrid extends JLabel implements MouseListener {
/**
*
*/
private static final long serialVersionUID = 1L;
private int state, newState;
private int numNeighbours;
private CellsGrid[] neighbour = new CellsGrid[8]; // array of total neighbours with possibility of 8
CellsGrid() {
state = newState = 0; // Dead
setOpaque(true); // so color will be showed
setBackground(color[0]); //set colour of dead cell
addMouseListener(this); // to select new LIVE cells
this.setPreferredSize(dim); //set size a new cells
}
// to add a neighbour
void addNeighbour(CellsGrid n) {
neighbour[numNeighbours++] = n;
}
// to see if I should live or not
void checkState() {
// number alive around
int NumNeighbours = 0; // number alive neighbours
// see the state of my neighbour
for(int i = 0; i < numNeighbours; i++)
NumNeighbours += neighbour[i].state;
// newState
if(state == 1) { // if alive
if(NumNeighbours < 2) // 1.Any live cell with fewer than two live neighbours dies
newState = 0;
if(NumNeighbours > 3) // 2.Any live cell with more than three live neighbours dies
newState = 0;
}
else {
if(NumNeighbours == 3) // 4.Any dead cell with exactly three live neighbours becomes a live cell
newState = 1;
}
}
// after the run switch the state to new state
void updateState() {
if(state != newState) { // do the test to avoid re-setting same color for nothing
state = newState;
setBackground(color[state]);
}
}
// called when the game is reset/clear
void clear() {
if(state == 1 || newState == 1) {
state = newState = 0;
setBackground(color[state]);
}
}
#Override
public void mouseClicked(MouseEvent arg0) {
}
// if the mouse enter a cell and it is down we make the cell alive
public void mouseEntered(MouseEvent arg0) {
if(mouseDown) {
state = newState = 1;
setBackground(color[1]);
}
}
#Override
public void mouseExited(MouseEvent arg0) {
}
// if the mouse is pressed on a cell you register the fact that it is down
// and make that cell alive
public void mousePressed(MouseEvent arg0) {
mouseDown = true;
state = newState = 1;
setBackground(color[1]);
}
// turn off the fact that the cell is down
public void mouseReleased(MouseEvent arg0) {
mouseDown = false;
}
}
}
As UnholySheep you need to learn % operator. % is the way to calculate reminder of division aka modulo. See https://en.wikipedia.org/wiki/Modulo_operation
The simplest way to apply it here is something like this. First introduce method getCellSafe
private CellsGrid getCellSafe(int r0, int c0) {
int r = r0 % Cells.length; // Cells.length is effectively nbRow
if (r < 0) r += Cells.length; // deal with how % works for negatives
int c = c0 % Cells[0].length; // Cells[0].length is effectively nbCol
if (c < 0) c += Cells[0].length; // deal with how % works for negatives
return Cells[r][c];
}
and then use this method instead direct access such as
Cells[r][c].addNeighbour(getCellSafe(r-1, c); // North
and also get rid of your + 2 in your nbCol and nbRow
Update: Fix for L-shape
The bug for "L-shape" is in your copy of line for "South East" where you converted c+1 into +c
Cells[r][c].addNeighbour(Cells[r+1][c+1]); // South East
//Cells[r][c].addNeighbour(getCellSafe(r+1, +c));
Bigger improvements
Generally your code is pretty bad. There are a lot of logic duplications (see DRY principle) and other bad code such as huge ActionListener. Here is my attempt to clean it up a bit:
public class GameOfLife2 extends JFrame
{
/**
*
*/
public static Random random = new Random();
static final Color[] color = {Color.YELLOW, Color.BLACK};
// size in pixel of every label
static final int cellSize = 15;
static final Dimension cellDim = new Dimension(cellSize, cellSize);
static final int GenDelay = 200;
// the cells labels
private CellsGrid[][] Cells;
// that fires the next generation
private Timer timer;
// generation counter
private int generation = 0;
private JLabel CellsIteration = new JLabel("Generation: 0");
// the 3 buttons
private JButton clearBtn = new JButton("Clear");
private JButton PauseBtn = new JButton("Pause");
private JButton StartBtn = new JButton("Start");
private JButton StepBtn = new JButton("Step");
// the slider for the speed
// state of the game (running or pause)
private boolean gameRunning = false;
GameOfLife2(int nbRow, int nbCol)
{
super("New GameOfLife");
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
Cells = new CellsGrid[nbRow][nbCol];
for (int r = 0; r < nbRow; r++)
{
for (int c = 0; c < nbCol; c++)
{
Cells[r][c] = new CellsGrid();
}
}
// panel in the center with the labels
JPanel panel = new JPanel(new GridLayout(nbRow, nbCol, 1, 1));
panel.setBackground(Color.BLACK);
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
// add each label (not the one on the border) to the panel and add to each of them its neighbours
for (int r = 0; r < nbRow; r++)
{
for (int c = 0; c < nbCol; c++)
{
CellsGrid curCell = Cells[r][c];
panel.add(curCell);
for (int dr = -1; dr <= 1; dr++)
{
for (int dc = -1; dc <= 1; dc++)
{
CellsGrid neighbor = getCellSafe(r + dr, c + dc);
if (neighbor != curCell)
curCell.addNeighbour(neighbor);
}
}
}
}
// now the panel can be added
add(panel, BorderLayout.CENTER);
// the bottom panel with the buttons the generation label and the slider
Box headerPanel = Box.createHorizontalBox();
Box buttonPanel = Box.createHorizontalBox();
// old pre-Java 8 syntax
//clearBtn.addActionListener(new ActionListener()
//{
// #Override
// public void actionPerformed(ActionEvent e)
// {
// clearGame();
// }
//});
clearBtn.addActionListener((e) -> clearGame()); // holy Java 8 lambda syntax
buttonPanel.add(clearBtn);
PauseBtn.addActionListener((e) -> stopGame());
buttonPanel.add(PauseBtn);
StartBtn.addActionListener((e) -> startGame());
buttonPanel.add(StartBtn);
StepBtn.addActionListener((e) -> stepToNextGeneration());
buttonPanel.add(StepBtn);
// the generation label
CellsIteration.setHorizontalAlignment(SwingConstants.CENTER);
headerPanel.add(Box.createHorizontalStrut(10));
headerPanel.add(buttonPanel);
headerPanel.add(Box.createHorizontalStrut(10));
headerPanel.add(Box.createHorizontalGlue());
headerPanel.add(Box.createHorizontalStrut(10));
headerPanel.add(CellsIteration);
headerPanel.add(Box.createHorizontalGlue()); // if you want "generation" label closer to center
headerPanel.add(Box.createHorizontalStrut(10));
// in the JFrame
add(headerPanel, BorderLayout.NORTH);
// put the frame on
setLocation(20, 20);
gameRunning = false;
generation = 0;
updateGenerationTitle();
updateButtonsState();
pack(); // adjust to the window size
setMinimumSize(new Dimension(600, 500));
setVisible(true);
// start the thread that run the cycles of life
timer = new Timer(GenDelay, (e) -> onTimerStep());
}
private CellsGrid getCellSafe(int r0, int c0)
{
int r = r0 % Cells.length; // Cells.length is effectively nbRow
if (r < 0) r += Cells.length; // deal with how % works for negatives
int c = c0 % Cells[0].length; // Cells[0].length is effectively nbCol
if (c < 0) c += Cells[0].length; // deal with how % works for negatives
return Cells[r][c];
}
private void updateButtonsState()
{
PauseBtn.setEnabled(gameRunning);
StartBtn.setEnabled(!gameRunning);
StepBtn.setEnabled(!gameRunning);
}
private void updateGenerationTitle()
{
CellsIteration.setText("Generation: " + generation);
}
private void startGame()
{
gameRunning = true; // flag game is running
updateButtonsState();
timer.setDelay(GenDelay);
timer.start();
}
private void stopGame()
{
timer.stop(); // stop timer
gameRunning = false; // flag not running
updateButtonsState();
}
private void clearGame()
{
stopGame();
// clear all cells
for (int r = 0; r < Cells.length; r++)
{
for (int c = 0; c < Cells[r].length; c++)
{
Cells[r][c].clear();
}
}
// reset generation number and its label
generation = 0;
updateGenerationTitle();
}
private void stepToNextGeneration()
{
++generation;
updateGenerationTitle();
for (int r = 0; r < Cells.length; r++)
{
for (int c = 0; c < Cells[r].length; c++)
{
Cells[r][c].checkState();
}
}
for (int r = 0; r < Cells.length; r++)
{
for (int c = 0; c < Cells[r].length; c++)
{
Cells[r][c].updateState();
}
}
}
private void onTimerStep()
{
if (gameRunning)
stepToNextGeneration();
}
// to start the whole thing as a Java application
public static void main(String[] arg)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new GameOfLife2(50, 50);
}
});
}
// A class that extends JLabel but also check for the neigbour
// when asked to do so
static class CellsGrid extends JLabel implements MouseListener
{
private boolean alive = false;
private boolean newAlive = false;
private final ArrayList<CellsGrid> neighbours = new ArrayList<>(8); // array of total neighbours with possibility of 8
CellsGrid()
{
setOpaque(true); // so color will be showed
addMouseListener(this); // to select new LIVE cells
setPreferredSize(cellDim); //set size a new cells
updateColor();
}
// to add a neighbour
void addNeighbour(CellsGrid n)
{
neighbours.add(n);
}
// to see if I should live or not
void checkState()
{
// number alive around
int NumNeighbours = 0; // number alive neighbours
// see the state of my neighbour
for (CellsGrid neighbour : neighbours)
NumNeighbours += neighbour.alive ? 1 : 0;
// 1. Any live cell with fewer than two live neighbours dies
// 2. Any live cell with two or three live neighbours lives on to the next generation.
// 3. Any live cell with more than three live neighbours dies
// 4. Any dead cell with exactly three live neighbours becomes a live cell
if (alive)
{
newAlive = (NumNeighbours == 2) || (NumNeighbours == 3);
}
else
{
newAlive = (NumNeighbours == 3);
}
}
// after the run switch the state to new state
void updateState()
{
alive = newAlive;
updateColor();
}
// called when the game is reset/clear
void clear()
{
alive = newAlive = false;
updateColor();
}
private void updateColor()
{
setBackground(color[alive ? 1 : 0]);
}
#Override
public void mouseClicked(MouseEvent arg0)
{
}
// if the mouse enter a cell and it is down we make the cell alive
#Override
public void mouseEntered(MouseEvent e)
{
boolean mouseDown = (e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0;
if (mouseDown)
{
alive = newAlive = true;
updateColor();
}
}
#Override
public void mouseExited(MouseEvent e)
{
}
#Override
public void mousePressed(MouseEvent arg0)
{
// state = newState = true;
alive = newAlive = !newAlive; //invert state is more useful, so you can clear something
updateColor();
}
#Override
public void mouseReleased(MouseEvent arg0)
{
}
}
}
This code seems to work including Glider flying diagonally with wrapping around the field.
Some notes:
I split your single ActionListener into independent ones for each thing and used Java 8 syntax to make them short and nice at registration
I introduced a few updateXyz and stepToNextGeneration methods to drastically reduce code duplication
MouseEvent already provides information whether button is down or not.
I added "Step" button that was needed for me to debug your code.
There are other layouts besides GridLayout
There is not much sense in defining serialVersionUID in UI (JComponent) classes as they are effectively not serializable (although they do implement Serializable interface). See also Swing components and serialization
Update with more answers for comments
If I wanted to have some pre-made initial state, I'd made a simple class (more like structute) Point with just two fields row and column and had a List or array of them, then did clear and iterated over the collection to make alive those cells that are in the list. If I wanted to go even further, I'd probably added one more wrapping class (structure) SavedGame or something to save width and height and a collection of Points; and made whole UI dynamic instead of building it once in constructor; and then added ability to save/read such configuration from some file
As for random, there is a java.util.Random class with useful int nextInt(int n) method. So I'd ask the user how many random cells should be made alive and then run a loop till that number and using Random generated new alive cells by generating row and column. Note that if you want to fill significant portion of cells, you might need to check if the next randomly cell you selected is already alive and if yes, "roll the dice" one more time. If you want to fill even more, you'll need to change algorithm to more complicated or filling 90% of cells might take forever. One idea might be to generated single cell index in range (0; row * height - count of already alive cells) rather than independent row and column and then just go through cells starting from top-left to bottom-right counting only dead cells and making alive corresponding cell. In such way you make sure that each new alive cell takes the same amount of time to generate.

How should I drag a JLabel from one JPanel in a JFrame onto a JTextField in another JPanel in the same JFrame?

In short, I want to set the text of a JLabel to be that of a JTextField in a JPanel (pnlUser) and then drag the JLabel across the screen from JPanel onto another JTextField in another JPanel (pnlGrid).
Here are the details.
I have written a "Solitaire Scrabble" program. The user can either position the text cursor in a grid cell (a JTextField in pnlGrid) and type a letter that is in the list of "User letters" (a JTextField in pnlUser) OR the user can simulate dragging a letter from "User letters" and dropping it into the destination grid cell in pnlGrid.
I say "simulate" because the selected letter is not actually dragged across the screen. I use the mouse pointer HAND_CURSOR to make the drag/drop as real as possible, but I haven't figured out how to make the HAND_CURSOR "grab" the letter and physically drag the letter across the board to its destination.
As it is, the letter is highlighted but left in the "User letters" area while the HAND_CURSOR moves along the grid during the drag operation. When it gets to the destination cell in pnlGrid and the mouse button is released, the letter is erased from "User letters" and suddenly appears in the grid cell.
So the letter is more or less "teleported" (beam me up, Scotty) from "User letters" to a grid cell. This is too abstract. I want the user letter to be at the tip of the HAND_CURSOR's pointing finger and be dragged along the grid into the grid cell where it will be dropped, as shown in the 3 pictures below.
I've successfully made it happen in a small test program (source below) using JLayeredPane, but I can't make it happen in the game. But I knew nothing about JLayeredPane until two days ago so I don't really know what I'm doing. (I adapted an Oracle tutorial program that demos JLayeredPane.)
I just read about the "glass pane" and thought it would maybe be easier to implement until I downloaded the source for that demo, which is quite long, so since it's totally new and will be even harder to adapt.
So I thought before I spend more hours in frustration I should ask:
Is a JLayeredPane or a setGlassPane approach appropriate? Is there an easier or better way to drag a JLabel from one JPanel onto another another JPanel?
(The approach in the program is to determine which "User letter" is being pointed at, store that letter in a JLabel, and then make sure that during mouseDragged the HAND_CURSOR fingertip is right at the bottom center of the letter.)
package mousemoveletter;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import static java.awt.Color.*;
import java.awt.event.*;
import static javax.swing.SwingUtilities.invokeLater;
public class LayeredPaneDemo extends JPanel
{
private static final int USER7 = 7;
static Cursor HAND = new Cursor(Cursor.HAND_CURSOR);
static Cursor ARROW = new Cursor(Cursor.DEFAULT_CURSOR);
private static JLayeredPane layeredPane;
private static JLabel lblToMove;
private static JPanel pnlUser;
private static JPanel pnlGrid;
private static final JTextField[] txtUser = new JTextField[USER7];
public LayeredPaneDemo() // constructor
{
pnlGrid = new JPanel();
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(240, 240));
pnlGrid.setSize(140, 140);
pnlGrid.setBorder(new EtchedBorder(RED, GREEN));
pnlGrid.setBackground(YELLOW);
lblToMove = new JLabel("XXX");
lblToMove.setSize(new Dimension(40,40));
layeredPane.add(pnlGrid, 0,0);
layeredPane.add(lblToMove, new Integer(0), -1);
add(layeredPane);
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("LayeredPaneDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new LayeredPaneDemo();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
makeUser();
frame.add(pnlUser);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
invokeLater(new Runnable()
{
public void run() {
createAndShowGUI();
}
});
}
private static void makeUser(){
pnlUser = new JPanel(new GridLayout(1,USER7));
pnlUser.setPreferredSize(new Dimension(225, 50));
pnlUser.setBackground(Color.green);
pnlUser.setBorder(BorderFactory.createLineBorder(Color.BLUE));
for(int k = 0; k < USER7; k++)
{
txtUser[k] = new JTextField("" + (char)(Math.random()*26+65));
txtUser[k].setName("" + k);
txtUser[k].setEditable(false);
txtUser[k].addMouseMotionListener(new MouseMotionAdapter()
{
public void mouseDragged(MouseEvent e)
{
lblToMove.setCursor(HAND);
int w = Integer.parseInt(e.getComponent().getName());
lblToMove.setText(txtUser[w].getText());
layeredPane.setLayer(lblToMove, 0, 0);
lblToMove.setLocation(e.getX() + (e.getComponent().getWidth())*w,
e.getY() + layeredPane.getHeight() - e.getComponent().getHeight()/2);
};
});
txtUser[k].addMouseListener(new MouseAdapter()
{
public void mouseReleased(MouseEvent e)
{
lblToMove.setCursor(ARROW);
}
});
pnlUser.add(txtUser[k]);
}
}
}
Thanks to #trashgod, I figured it out by following his links to this example and variation; I adapted the drag/drop of the chessboard found there to my own particular needs for "Scrabble".
The code below is not final code for my Solitaire Scrabble program, but proof-of-concept, possibly usable by others wishing to drag a cell from a 1xN grid onto a MxM grid.
package components;
import java.awt.*;
import static java.awt.BorderLayout.NORTH;
import static java.awt.BorderLayout.SOUTH;
import java.awt.event.*;
import static java.lang.Integer.parseInt;
import javax.swing.*;
import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE;
import javax.swing.event.MouseInputAdapter;
public class ChessBoard //implements MouseListener, MouseMotionListener
{
static Point parentLocation;
int homeRow, homeCol; // where to restore moved user letter if dropped on occupied cell
static int N = 11; // NxN 'chessboard' squares
static int S = 44; // square dimensions: SxS
static int W ; // chessboard dimensions: WxW
static int USER7 = 7;
static Font dragFont;
static JFrame frame;
JLayeredPane layeredPane;
static JPanel gamePanel, // encompasses both pnlGrid and pnlUser
pnlGrid,
pnlUser;
JLabel userDragLetter = new JLabel(); // main item to drag around or restore if needed
int xAdjustment, yAdjustment; // how to locate drops accurately
String userLetters[] ;
public ChessBoard() // constructor
{
W = S*N;
dragFont = new Font("Courier", Font.PLAIN, S);
userLetters = new String[USER7];
for (int i = 0; i < USER7; i++)
userLetters[i] = "" + (char)(65 + Math.random()*26);
Dimension gridSize = new Dimension(W, W);
Dimension userSize = new Dimension(W, S);
Dimension gameSize = new Dimension(W, (W + S));
frame = new JFrame();
frame.setSize(new Dimension(gameSize)); // DO NOT USE PREFERRED
layeredPane = new JLayeredPane();
layeredPane.setPreferredSize( gameSize ); // NO PREFERRED => NO GRID!
gamePanel = new JPanel();
// **EDIT** LOSE THIS LINE gamePanel.setLayout(new BorderLayout());
gamePanel.setPreferredSize(gameSize);
pnlGrid = new JPanel();
pnlGrid.setLayout(new GridLayout(N, N));
pnlGrid.setPreferredSize( gridSize );
pnlGrid.setBounds(0, 0, gridSize.width, gridSize.height);
pnlUser = new JPanel();
pnlUser.setLayout(new GridLayout(1, N));
pnlUser.setPreferredSize(userSize);
pnlUser.setBounds(0, gridSize.height, userSize.width, userSize.height);
layeredPane.add(pnlGrid, JLayeredPane.DEFAULT_LAYER); // panels to drag over
layeredPane.add(pnlUser, JLayeredPane.DEFAULT_LAYER); // " "
for (int i = 0; i < N; i++){
for (int j = 0; j < N; j++){
JPanel square = new JPanel();
square.setBackground( (i + j) % 2 == 0 ? Color.red : Color.white );
pnlGrid.add( square );
}
}
for (int i = 0; i < N; i++) {
JPanel square = new JPanel(new BorderLayout());
square.setBackground(Color.YELLOW);
pnlUser.add(square);
}
for (int i = 0; i < USER7; i++)
addPiece(i, 0, userLetters[i]);
gamePanel.addMouseListener(new MouseInputAdapter()
{
public void mousePressed (MouseEvent e){mousePressedActionPerformed (e);}
public void mouseReleased(MouseEvent e){mouseReleasedActionPerformed(e);}
});
gamePanel.addMouseMotionListener(new MouseMotionAdapter()
{
public void mouseDragged(MouseEvent me){mouseDraggedActionPerformed(me);}
});
// **EDIT: LOSE THE NEXT TWO LINES AND REPLACE BY THE LINE AFTER THEM**
// gamePanel.add(layeredPane, NORTH);
// gamePanel.add(pnlUser, SOUTH);
gamePanel.add(layeredPane);
}
private void addPiece(int col, int row, String glyph) {
JLabel piece = new JLabel(glyph, JLabel.CENTER);
piece.setFont(dragFont);
JPanel panel = (JPanel) pnlUser.getComponent(col + row * N);
piece.setName("piece " + glyph + " # " + row + " " + col);
panel.add(piece);
}
void mousePressedActionPerformed(MouseEvent e)
{
userDragLetter = null; // signal that we're not dragging if no piece is in the square
gamePanel.setCursor(new Cursor(Cursor.HAND_CURSOR));
Component c = pnlGrid.findComponentAt(e.getX(), e.getY());
if(c != null)
return; // Illegal to click pnlGrid
c = pnlUser.findComponentAt(e.getX(), e.getY() - pnlGrid.getHeight());
if(c == null | c instanceof JPanel)
return; // letter already played; can't drag empty cell
parentLocation = c.getParent().getLocation();
xAdjustment = parentLocation.x - e.getX();
yAdjustment = parentLocation.y - e.getY() + gamePanel.getHeight() - pnlUser.getHeight();
userDragLetter = (JLabel)c;
userDragLetter.setPreferredSize(new Dimension(S, S)); // prevent 2 letters in a square
userDragLetter.setLocation(e.getX() + xAdjustment, e.getY() + yAdjustment);
layeredPane.add(userDragLetter, JLayeredPane.DRAG_LAYER);
homeRow = parseInt(userDragLetter.getName().substring(10,11)); // save restore location
homeCol = parseInt(userDragLetter.getName().substring(12,13));
}
void mouseDraggedActionPerformed(MouseEvent me)
{
if (userDragLetter == null)
return; // nothing to drag
int x = me.getX() + xAdjustment; // make sure grid cell will be chosen in-bounds
int xMax = layeredPane.getWidth() - userDragLetter.getWidth();
x = Math.min(x, xMax);
x = Math.max(x, 0);
int y = me.getY() + yAdjustment;
int yMax = layeredPane.getHeight() - userDragLetter.getHeight();
y = Math.min(y, yMax);
y = Math.max(y, 0);
if(y >= pnlGrid.getHeight())
return; // can't drag to location off grid
userDragLetter.setLocation(x, y);
}
void mouseReleasedActionPerformed(MouseEvent e)
{
//**EDIT: CHANGED NEXT LINE**
gamePanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
if (userDragLetter == null)
return; // nothing to drag; nothing to release
// Make sure the chess piece is no longer painted on the layered pane
userDragLetter.setVisible(false);
layeredPane.remove(userDragLetter);
userDragLetter.setVisible(true);
int xMax = layeredPane.getWidth() - userDragLetter.getWidth();
int x = Math.min(e.getX(), xMax);
x = Math.max(x, 0);
int yMax = layeredPane.getHeight()- userDragLetter.getHeight();
int y = Math.min(e.getY(), yMax);
y = Math.max(y, 0);
Component c = pnlGrid.findComponentAt(x, y); // find deepest nested child component
if(c == null) // then grid cell is unoccupied so ...
c = pnlUser.findComponentAt(x, y); // see if there's a letter there ...
if(c == null | (c instanceof JLabel)){ // and if illegal or there is one, put it back...
userDragLetter.setLocation(parentLocation.x + xAdjustment,
parentLocation.y + yAdjustment + gamePanel.getHeight());
userDragLetter.setVisible(true);
addPiece(homeCol, homeRow,userDragLetter.getName().substring(6,7));
layeredPane.remove(userDragLetter);
return;
}
else // but if NO letter ...
{
Container parent = (Container)c;
parent.add( userDragLetter ); // put one in the grid cell
parent.validate();
}
userDragLetter.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
public static void main(String[] args)
{
new ChessBoard();
frame.add(gamePanel);
frame.setDefaultCloseOperation( DISPOSE_ON_CLOSE );
// frame.setResizable( false );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}

Custom JButton not sizing properly?

I am creating a matching game using Netbeans, but not the GUI editor (it sucks). So, basically, I created a new class, called Card, that extends the JButton class. Upon construction, the button's size is set to 100px by 100px and an icon is set. When I add the button to a JPanel in a GridBagLayout, it is not the intended size.
Here is some of my code:
JFRAME CLASS:
package matchinggame;
... imports ...
public class MatchingGameWindow extends JFrame {
Card[] cards = new Card[16]; //16 game cards
public MatchingGameWindow() {
...
//Create new game panel (for the cards)
JPanel gamePanel = new JPanel(new GridBagLayout());
//gamePanel.setSize(500,500); removed as it is not needed.
...
this.add(gamePanel, BorderLayout.CENTER);
//Create 16 card objects
cards = createCards();
//Create new grid bag constraints
GridBagConstraints gbc = new GridBagConstraints();
//Add the cards to the game panel
int i=0;
for (int y = 0; y < 4; y++) {
gbc.gridy = y;
for (int x = 0; x < 4; x++) {
gbc.gridx = x;
gamePanel.add(cards[i], gbc);
i++;
}
}
}
public final Card[] createCards() {
Card[] newCards = new Card[16];
//New choices array
ArrayList<Integer> choices = new ArrayList();
int[] choiceValues = {0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7};
//Add the initial choice values to the arraylist
for (int i=0; i < choiceValues.length; i++) {
choices.add(choiceValues[i]);
}
//Create 16 cards
for (int i=0; i < 16; i++) {
//Initial value of -1 for error checking
int iconIndex = -1;
//Loop until card is created
while (iconIndex == -1) {
//Get a random number from 0 - 7
Random r = new Random();
int rInt = r.nextInt(8);
//If the random number is one of the choices
if (choices.contains(rInt)) {
//the icon # will be the random number
iconIndex = rInt;
//Get rid of that choice (it is used up)
choices.remove(new Integer(rInt));
//Create a new Card in the Card[]
newCards[i] = new Card(i,iconIndex);
//If all the choices are gone
} else if (choices.isEmpty()){
iconIndex = -1; //done creating this card (breaks out of loop)
}
}
}
//Return the created cards
return newCards;
}
}
CARD CLASS:
package matchinggame;
import javax.swing.ImageIcon;
import javax.swing.JButton;
public class Card extends JButton {
final static ImageIcon defaultIcon = new ImageIcon("cardback.jpg");
ImageIcon secretIcon;
boolean isRevealed = false;
...
public Card(final int cardIndex, int secretIconIndex) {
//Size is 100px by 100px
setSize(100, 100);
//Default icon is card back image
setIcon(defaultIcon);
//Get the secret icon behind the back of the card
secretIcon = icons[secretIconIndex];
}
}
And using this code I get a result of this:
Any ideas as to what I am doing wrong here?
EDIT:
I overrided the getPreferredSize method like Hovercraft Full Of Eels said, and it worked!
I added this code in the Card class:
#Override
public Dimension getPreferredSize() {
return new Dimension(100,100);
}
and got my desired result:
Now I must be doing something wrong with the icons, as they are not showing up as they should.
You should not use setSize(...) in the class's constructor but rather override the class's getPreferredSize() method to return a Dimension(100, 100). And in fact you should have setSize(...) no-where in your program. Instead use decent layout managers, call pack() on the JFrame after adding all components and before setting it visible, and let the layout managers size the GUI appropriately.

Categories