I need to print out a grid in a windows and I need button under the grid. So that I can control the action on the grid, but at the moment I don't get the grid printed out only the buttons
// Create and initialize cell
public Cell[][] cell = new Cell[rows][cols];
protected BorderPane getPane() {
HBox paneForButtons = new HBox(20);
//button start
startB.setOnAction(e -> {
if( startB.isPressed() == false){
System.out.println("start");
}
});
//button stop
stopB.setOnAction(e -> {
if( stopB.isPressed() == false){
System.out.println("stop");
}
});
//button left
leftB.setOnAction(e -> text.getTransforms().add(new Rotate(-rotate, text.getX(), text.getY())));
//button right
rightB.setOnAction(e -> text.getTransforms().add(new Rotate(rotate, text.getX(), text.getY())));
//fiel to get the moves
moveFeld.setPromptText("1,2 or 3 steps");
Button absenden = new Button();
absenden.setOnAction(e -> {
ReadInput2();
if( moveFeld.getText() != null && !moveFeld.getText().isEmpty()){
System.out.println("Moves are " + moveFeld.getText().toString());
}
});
absenden.setText("enter");
paneForButtons.getChildren().addAll(startB, stopB, leftB, rightB, moveFeld, absenden);
paneForButtons.setAlignment(Pos.CENTER);
paneForButtons.setStyle("-fx-border-color: green");
BorderPane pane = new BorderPane();
pane.setBottom(paneForButtons);
//pane.setBottom(paneForButtons);
// Pane to hold cell
GridPane pane2 = new GridPane();
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
pane2.add(cell[i][j] = new Cell(), j, i);
pane2.setAlignment(Pos.CENTER);
pane.setCenter(pane2);
return pane;
}
public static class Cell extends Pane {
public Cell() {
setStyle("-fx-border-color: black");
this.setPrefSize(800, 800);
}
}
I'm calling the method getPane in my void so I think there is not problem with that you guys have any idea how I get the grid above my buttons.
Now to part two the grid can be painted however the user wants I putted in 5 for now but in the future it should be mutable
Related
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
I am trying to build a battle ship application. So far I managed to display both fields for the player and the enemy. A field consists of 10 x 10 Rectangles - 10 HBox'es in one VBox. This is the method that creates me a field:
private Rectangle[][] field;
public VBox CreateField(){
VBox vbox = new VBox(2);
for(int i = 0; i < this.columns; ++i){
HBox hbox = new HBox(2);
for(int j = 0; j < this.rows; ++j){
this.array[j][i] = 0;
this.field[i][j] = new Rectangle(this.height*j, this.width*i, this.height, this.width);
this.field[i][j].setStroke(Color.BLACK);
this.field[i][j].setFill(Color.LIGHTGRAY);
hbox.getChildren().add(this.field[i][j]);
}
vbox.getChildren().add(hbox);
}
return vbox;
}
This is the result:
I need to receive the Index of a Rectangle when the user clicks one of it.
Can you guys give me an example / code snipped how to accomplish my problem?
I recommend using a GridPane instead of HBoxes and a VBox. You could copy the loop indices to final local variables to access them from a anonymus EventHandler class/lambda expression created inside the loop body or you could use GridPane.getRowIndex/GridPane.getColumnIndex on the GridPane children:
public GridPane CreateField(){
GridPane grid = new GridPane();
grid.setVgap(2);
grid.setHgap(2);
EventHandler<MouseEvent> handler = evt -> {
Node source = (Node) evt.getSource();
int row = GridPane.getRowIndex(source);
int column = GridPane.getColumnIndex(source);
...
};
for(int i = 0; i < this.columns; i++){
for(int j = 0; j < this.rows; j++){
Rectangle rect = new Rectangle(this.height*j, this.width*i, this.height, this.width);
this.array[j][i] = 0;
this.field[i][j] = rect;
rect.setStroke(Color.BLACK);
rect.setFill(Color.LIGHTGRAY);
rect.setOnMouseClicked(handler);
grid.add(rect, j, i);
}
}
return grid;
}
You could also use
final int finalI = i;
final int finalJ = j;
rect.setOnMouseClicked(evt -> {
// TODO: use finalI and finalJ here
});
in the inner loop instead.
Lol, got it faster work than I thought. Here is my solution (How can I get the indexes of each button clicked for my program?)
private EventHandler<? super MouseEvent> createTileHandler(int x, int y) {
return event -> tileHandler(x, y);
}
private void tileHandler (int x, int y){
System.out.println(String.format("Clicked tile at (%d,%d)", x, y));
}
public VBox CreateField(){
VBox vbox = new VBox(2);
for(int i = 0; i < this.columns; ++i){
HBox hbox = new HBox(2);
for(int j = 0; j < this.rows; ++j){
this.array[j][i] = 0;
this.field[i][j] = new Rectangle(this.height*j, this.width*i, this.height, this.width);
this.field[i][j].setStroke(Color.BLACK);
this.field[i][j].setFill(Color.LIGHTGRAY);
this.field[i][j].setOnMouseClicked(createTileHandler(i,j));
hbox.getChildren().add(this.field[i][j]);
}
vbox.getChildren().add(hbox);
}
return vbox;
}
This returns:
Clicked tile at (3,2)
Clicked tile at (3,2)
Clicked tile at (4,4)
Clicked tile at (3,0)
Clicked tile at (8,8)
Clicked tile at (9,9)
Clicked tile at (0,0)
Clicked tile at (0,1)
Clicked tile at (0,2)
When I click one Shape.
Apologizing for this questions ..
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.
I want it so every time a button is pressed in my 4x4 grid, that it increments moves by 1 . This is creating a 4x4 layout of buttons. Each time any of those buttons are pressed, I want moves to increment. Basically I'm creating the memory game, where you flip cards over to match each other. I just have to keep count of the total amount of moves a player does to solve the puzzle.
private int moves = 0;
private GridPane makeGridPane(){
ConcentrationModel c = new ConcentrationModel();
GridPane grid = new GridPane();
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth( 50 );
grid.getColumnConstraints().addAll(col1, col1, col1, col1);
RowConstraints row1 = new RowConstraints();
row1.setPercentHeight( 50 );
grid.getRowConstraints().addAll(row1, row1, row1, row1);
for(int row = 0; row < 4; row ++){
for(int col = 0; col < 4; col++){
Button btn = new Button();
ImageView image = new ImageView(c.getCards().get(0).getImage());
image.setFitWidth(WIDTH/4);
image.setFitHeight(HEIGHT/4);
btn.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
btn.setGraphic(image);
grid.add(btn, col, row);
}
}
return grid;
}
You can create a single event handler and reuse it for all the buttons. Since you probably want the buttons to do other things too, I would recommend adding this as an event handler, instead of using the convenience method setOnAction(...):
EventHandler<ActionEvent> incrementMovesHandler = e -> moves++ ;
for(int row = 0; row < 4; row ++){
for(int col = 0; col < 4; col++){
Button btn = new Button();
btn.addEventHandler(ActionEvent.ACTION, incrementMovesHandler);
// ...
}
}
This is actually pretty simple. All you have to do is add this bit of code just before your grid.add(...):
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
moves++;
}
});
Or, equivalently, the Java 8 version:
btn.setOnAction((ActionEvent e) -> {moves++;});
(I can't currently test this, but it should work. If not, lemme know and I'll try to fix it.)
If you're concerned about memory, Christian points out below that this creates a new instance of EventHandler for every button. While this may not be too terrible, it's probably a bad habit to get into. The best way to handle (no pun intended) this is by making an object before your for loop:
EventHandler<ActionEvent> eh = new EventHandler<>() {
#Override public void handle(ActionEvent event) {
moves++;
}
};
Then, for each of your buttons, instead of the top-most code, you'd simply write:
btn.setOnAction(eh);
That way, a single EventHandler is being created and used to handle all the events. You'll want to use this one if you need to create more than just a few buttons, both because it's faster (doesn't need to allocate memory for each object) and more memory-efficient (...it, uh, doesn't need to allocate the memory for each object). In this case, I think it's pretty trivial, but it's good to know nonetheless.
You need to implement the ActionListener interface in your class.
Then in the actionPerformed function, just increment the moves variable.
You just have to call btn.addActionListener(this); for each of the button that
you created.
public class Sample extends JFrame implements ActionListener {
public Sample ()
{
}
private GridPane makeGridPane()
{
ConcentrationModel c = new ConcentrationModel();
GridPane grid = new GridPane();
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth( 50 );
grid.getColumnConstraints().addAll(col1, col1, col1, col1);
RowConstraints row1 = new RowConstraints();
row1.setPercentHeight( 50 );
grid.getRowConstraints().addAll(row1, row1, row1, row1);
for(int row = 0; row < 4; row ++){
for(int col = 0; col < 4; col++){
Button btn = new Button();
ImageView image = new ImageView(c.getCards().get(0).getImage());
image.setFitWidth(WIDTH/4);
image.setFitHeight(HEIGHT/4);
btn.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
btn.setGraphic(image);
btn.addActionListener(this);
grid.add(btn, col, row);
}
}
return grid;
}
public void actionPerformed(ActionEvent e)
{
moves++;
}
}
I am coding a minesweeper game. The buttons should display the numbers 0,1 or 2 when clicked to display how many mines are adjacent. I recently changed the layout to gridlayout and since then all I get when I click on a button is '...' with the exception of the discovery of a mine which changes the button to the bomb gif.
Can anyone tell me how to get the numbers back on click?
Here is the code to create the button Array:
JPanel gridPanel = new JPanel(new GridLayout(boardsize, boardsize));
buttons = new JButton[boardsize][boardsize];
mineBoard = new int[9][9];
for (int a = 0; a < boardsize; a++)
for (int b = 0; b < boardsize; b++) {
buttons[a][b] = new JButton("");
gridPanel.add(buttons[a][b]);
buttons[a][b].addMouseListener(new MouseListener(a,b));
setx(a);
sety(b);
settried(false);
setmine(false);
}
contentPane.add(gridPanel, BorderLayout.CENTER);
When the user clicks a button:
// This method takes in an x and y value and defines what should happen when the user clicks there.
public void click(int row, int col) {
if(mineBoard[row][col] == Mine) {
buttons[row][col].setIcon( new ImageIcon( "images/bomb.gif" ) );
lose();
} else {
score += 1;
updatescore();
buttons[row][col].setText("" + numAdjMines(mineBoard, row, col));
mineBoard[row][col] = UncoveredEmpty;
buttons[row][col].setText(Character.toString(getUserChar(mineBoard[row][col])));
if(numAdjMines(mineBoard, row, col) == Empty) {
for(int dr = -1; dr <= 1; dr ++) {
for(int dc = -1; dc <= 1; dc++) {
if(row+dr >= 1 && row+dr < 10 &&
col+dc >= 1 && col+dc < 10) {
if(mineBoard[row+dr][col+dc] == Empty) {
click(row+dr,col+dc);
}
}
}
}
}
}
}
MouseLister Class:
//ACTION WHEN USER CLICKS ON A BUTTON
private class MouseListener extends MouseAdapter {
private int x = 0;
private int y = 0;
public MouseListener(int row, int col) {
this.x = row;
int i = 0;
this.y = col;
}
public void mouseClicked(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON1) {
if((mineBoard[x][y] == Empty) && (Game.this.gamegoing == true)) {
Game.this.click(x, y);
} else if(mineBoard[x][y] == Mine) {
buttons[x][y].setIcon( new ImageIcon( "images/bomb.gif" ) );
Game.this.lose();
}} else if(e.getButton() == MouseEvent.BUTTON3) {
Game.this.buttons[x][y].setText("F");
}
}
}
You can try to add a few spaces to your default text of the button, something like that:
buttons[a][b] = new JButton(" ");
Or you can try to pack your window after you set the text on the button.
When a JButton only dislays dots it means that there is not enough space to write down the tekst.
So the solution would be to tell Gridlayout to make the buttons larger.
I'd recomend that you find test your game with a very big windows and then try to find the point where your windows is big enough that Gridlayout en JButton agree that the tekst can be placed.
Then you should see how big your buttons are at that point and then either:
Tell GridLayout how big your buttons should be (by calling setPrefferedSize() that should work)
Or use another layout manager that does not give you this problem.
P.S. A your bomb picture appear because picures are handled in a different way. They are always displayed I belive.
I hope this solves it for you.