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.
Related
How would I go about checking if any of the Rects I've drawn (or filled, if you will) with my drawChessSquare() method have been clicked? I have tried looking into the processMouseEvent() method but I'm not sure how I would get that to work. For your information, it currently prints out an 8x8 chessboard, and repaints it in an update method, which is run in a while loop in a separate main class. GitHub here just in case you need to look closer: https://github.com/ewanh26/Chess This is my code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
public class Board extends JFrame {
public static int WIDTH;
public static int HEIGHT;
public Board(int WIDTH, int HEIGHT, String TITLE) {
super(TITLE);
Board.WIDTH = WIDTH;
Board.HEIGHT = HEIGHT;
setBackground(Color.black);
JPanel contentPane = new JPanel();
setContentPane(contentPane);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
contentPane.add(new Checker());
pack();
setVisible(true);
}
public void update() {
repaint();
}
static class Checker extends JPanel {
private final Dimension preferredSize = new Dimension(Board.WIDTH, Board.HEIGHT);
private void drawChessSquare(int w, int h, int x, int y, Graphics g, Color color) {
g.setColor(color);
g.fillRect(x, y, w, h);
}
#Override
public Dimension getPreferredSize() {
return preferredSize;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
boolean black = true;
for (int i = 0; i < 64; i++) {
int x = 0;
int y = 0;
if (i < 8) {
assert true;
} else if (i < 8 * 2) {
y = Board.HEIGHT / 8;
} else if (i < 8 * 3) {
y = (Board.HEIGHT / 8) * 2;
} else if (i < 8 * 4) {
y = (Board.HEIGHT / 8) * 3;
} else if (i < 8 * 5) {
y = (Board.HEIGHT / 8) * 4;
} else if (i < 8 * 6) {
y = (Board.HEIGHT / 8) * 5;
} else if (i < 8 * 7) {
y = (Board.HEIGHT / 8) * 6;
} else {
y = (Board.HEIGHT / 8) * 7;
}
if (i % 8 == 0) {
assert true;
} else if (i == 1 || (i - 1) % 8 == 0) {
x = Board.WIDTH / 8;
} else if (i == 2 || (i - 2) % 8 == 0) {
x = (Board.WIDTH / 8) * 2;
} else if (i == 3 || (i - 3) % 8 == 0) {
x = (Board.WIDTH / 8) * 3;
} else if (i == 4 || (i - 4) % 8 == 0) {
x = (Board.WIDTH / 8) * 4;
} else if (i == 5 || (i - 5) % 8 == 0) {
x = (Board.WIDTH / 8) * 5;
} else if (i == 6 || (i - 6) % 8 == 0) {
x = (Board.WIDTH / 8) * 6;
} else if (i == 7 || (i - 7) % 8 == 0) {
x = (Board.WIDTH / 8) * 7;
}
if (black) {
if (!((i-7) % 8 == 0)) black = !black;
drawChessSquare(Board.WIDTH / 8, Board.HEIGHT / 8, x, y, g, Color.black);
} else {
if (!((i-7) % 8 == 0)) black = !black;
drawChessSquare(Board.WIDTH / 8, Board.HEIGHT / 8, x, y, g, Color.orange);
}
}
}
}
}```
Here is a heavily commented runnable explaining one way you can do it. It also includes Mouse events that demonstrates a few things you can do. Remove the comments when you're done if you like....there is waaaay too much of it.
The demo displays text within each JLabel which indicates the name provided to that particular JLabel's name property during form creation. These names will most likely come in handy as your game development progresses.
In the image below, look at the title bar as each board square is clicked upon with the mouse pointer.
This demo utilizes an Array of JLabels and, as I'm sure you already know, you can add images to JLabels as well as set text, background and foreground colors, borders, etc. Of course you also have several different events for you to use. Ugggg...using JLabels for this sort of thing is just another option I suppose. Try it, play with it for a while and see what you think.
Here is the runnable code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class ChessBoardDemo extends JFrame {
private JLabel[] boardLabel;
private int gridWidth = 8; // X axis (horizintal)
private int gridHeight = 8; // Y axis (vertical)
private int squarePixelSize = 50; // 50 x 50 pixels.
private Color hoverColor = Color.decode("#DDCCFF");
private Color beforeHoverColor;
private Color borderColor = Color.WHITE;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ChessBoardDemo().startApp(args);
}
});
}
private void startApp(String[] args) {
initialize();
}
private void initialize() {
setAlwaysOnTop(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setLayout(new GridLayout(gridHeight, gridWidth));
// Setup the Board Squares (JLabels)
// Create an Array of JLabels
boardLabel = new JLabel[gridWidth * gridHeight];
MouseHandler mouseHandler = new MouseHandler();
/* Each JLabel will have a name set into the Name property.
These names will come in handy at some point during
development. Rows are designated from A to H and columns
are designated as 1 to 8 (for an 8 by 8 grid for example). */
int labelNameAsciiStartLetter = 65; // A
// A Flag used to stagger the colors as in a checkers or chess board.
boolean stagger = false;
/* Will hold the background color for each JLabel square as the board
is generated. */
Color squareColor;
// Counter used for applying the proper name value to each JLabel Square.
int columnCounter = 0;
for (int i = 0; i < boardLabel.length; i++) {
// Is the value in i an even number? (0 is considered an even number here).
if (i % 2 == 0) {
/* Yes... then color the JLabel backgound red unless
stagger is true in which case turn it black. Ternary
Operator is used here. */
squareColor = !stagger ? Color.RED : Color.BLACK;
}
else {
/* NO... then color the JLabel backgound black unless
stagger is true in which case turn it red. Ternary
Operator is used here. */
squareColor = !stagger ? Color.BLACK : Color.RED;
}
//Increment the column counter.
columnCounter++;
// Make this array element a new JLabel.
boardLabel[i] = new JLabel();
// Designate the appropreate name to its' name property.
boardLabel[i].setName(Character.toString((char)labelNameAsciiStartLetter)
+ String.valueOf(columnCounter));
/* Set the foreground text color. You most likely don't
want the text anyways. It's just here for demo. */
boardLabel[i].setForeground(Color.LIGHT_GRAY);
/* Set the text Horizontal alignmemnt. You most likely
don't want the text anyways. It's just here for demo. */
boardLabel[i].setHorizontalAlignment(JLabel.CENTER);
/* Set the text in JLabel. You most likely don't want
the text anyways. It's just here for demo. */
boardLabel[i].setText(boardLabel[i].getName());
// Set the JLabel as opaque so as to see the background color.
boardLabel[i].setOpaque(true);
// If you want a border around each square then un-comment the next line.
//boardLabels[i].setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
// Set the current JLabel backgound color.
boardLabel[i].setBackground(squareColor);
// Set the current JLabel size (in pixels).
boardLabel[i].setPreferredSize(new Dimension(squarePixelSize, squarePixelSize));
boardLabel[i].addMouseListener(mouseHandler);
getContentPane().add(boardLabel[i]);
// If we reached our grid width...
if (columnCounter == gridWidth) {
// Toggle the stagger flag to its oposite. False to true or true to false.
stagger = !stagger;
// Increment to the next ASCII letter code (ex: 65 [A] to 66 [B] etc).
labelNameAsciiStartLetter++;
// Reset the column counter to 0 in prep for next row.
columnCounter = 0;
}
}
// Let the layout manager setup all the components in proper order.
pack();
// Make the JFrame form visible.
setVisible(true);
// Place the form at center screen (must be after setVisible).
setLocationRelativeTo(null);
}
/* Mouse Handler inner class to to take care of the mouse events
for all the JLabels that make up the board. */
private class MouseHandler extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent evt) {
// Get the JLabel that the mouse was clicked on.
JLabel source = (JLabel) evt.getSource();
// Indicate it in the JFrame Title Bar.
setTitle("You clicked on the JLabel named: " + source.getName());
}
#Override
public void mouseEntered(MouseEvent evt) {
// When the mouse pointer enters a JLabel's boundry...
// Get the JLabel that the mouse pointer has entered on.
JLabel label = (JLabel) evt.getSource();
// Get its' current backgound color.
beforeHoverColor = label.getBackground();
/* Un-comment the following line if you want to
change the backgound color on mouse hover. */
//label.setBackground(hoverColor);
// Place a border around the JLabel the mouse is hovering over.
label.setBorder(BorderFactory.createLineBorder(borderColor, 1));
}
#Override
public void mouseExited(MouseEvent evt) {
// When the mouse pointer exits a JLabel's boundry...
// Get the JLabel that the mouse pointer has exited from.
JLabel label = (JLabel) evt.getSource();
/* Un-comment the following line if you want to
change the JLabel backgound color to its original
state when mouse leaves the JLabel. */
//label.setBackground(beforeHoverColor);
/* Remove the border from around the JLabel when the
mouse pointer exits. */
label.setBorder(null);
}
}
}
I'm attempting to build Minesweeper in Java (Using Eclipse, if that matters for any reason whatsoever) and while making it, I've encountered an issue I haven't had before.
The window that's supposed to show up whenever a mine is pressed is opened... A lot. The window is only meant to open once and prompt the user to either continue or give up.
Important to note this only occurs whenever a mine is pressed on the top row of buttons. Not entirely sure if this is important, but the amount of times a window pops-up is 11. There are also no error codes given.
The following chunk of code is for whenever a user clicks on a mine.
//These are here just for clarification
private int continues = 3;
private Listen l = new Listen();
private class Listen extends MouseAdapter implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
Object source = e.getSource();
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (source == mines[i][j])
{
//Though probably not optimal, I created mines based around a number referred to as difficulty
if (minePlacement[i][j] <= difficulty)
{
Frame f = new Frame();
Label L = new Label("You have " + continues + " continues remaining.");
Button B = new Button ("Try Again?");
Label La = new Label("You're out of continues...");
Button But = new Button("Exit");
f.setLayout(new FlowLayout());
B.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
f.dispose();
}
});
if (continues <= 0)
{
f.add(La);
f.add(But);
/*
For clarification,
This actionListener is used
solely to do System.exit(0);
*/
But.addActionListener(li);
f.setSize(250,125);
}
else
{
f.add(L);
f.add(B);
f.setSize(250,100);
}
f.setLocationRelativeTo(null);
f.setAlwaysOnTop(true);
f.setVisible(true);
f.setTitle("You Hit A Mine!");
continues--;
}
This following chunk of code is used in order to build the board itself.
for (int i = 0; i < rows; i++)
{
int x = (int) ((Math.random() + .1) * 10);
minePlacement[i][0] = x;
mines[i][0] = new Button();
mines[i][0].addMouseListener(l);
mines[i][0].addActionListener(l);
add(mines[i][0]);
for (int j = 0; j < cols; j++)
{
int y = (int) ((Math.random() + .1) * 10);
minePlacement[i][j] = y;
mines[i][j] = new Button();
mines[0][j].addMouseListener(l);
mines[0][j].addActionListener(l);
mines[i][j].addMouseListener(l);
mines[i][j].addActionListener(l);
add(mines[i][j]);
}
I've tried looking for other posts which may answer my question, though as it stands, I have failed to find any other posts that have been coded in Java. I've mainly found them in C++ or C, to which I don't understand how the solution was brought about.
I'd like to know if there's any way to set a limit on the amount of windows on the screen, or if there's a way to prevent the window from popping up more times than expected.
Thanks in advance!
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 have created a game where the player collects pods on the game board. I need to make an evasive maneuver so that after five turns when the player is one space away from the pod, the pod will transport 10 spaces away from the player. I believe my counter is not working and that is why the evasive maneuver is not working.
public class Pod {
int podPositionX;
int podPositionY;
String podDirection;
int layoutWidth;
int layoutHeight;
boolean podVisable = true;
int playerX;
int playerY;
int count = 0;
public Pod(int x, int y, String direction, int width, int height){
podPositionX = x;
podPositionY = y;
podDirection = direction;
layoutWidth = width;
layoutHeight = height;
count=count+1;
}
//Inspector Methods?
// Will get the the pods positon and will return it
public int getX()
{
return podPositionX;
}
//This method returns the current Y coordinate of the pod.
public int getY()
{
return podPositionY;
}
//This method returns true if the pod is visible, false otherwise. Once the
//pod has been caught, it should always return false.
public boolean isVisible(){
if (playerX == podPositionX && playerY == podPositionY && podVisable == true){
podVisable = false;}
return podVisable;
}
// to move pod diagonally across screen//
public void move(){
//Calling invasive methods!!
while (count>5 && count < 100){
podPositionX=transportX();
podPositionY=transportY();
}
/****************** To make pod move **************/
if (podDirection.equals("NW")|| podDirection.equals("NE")){
podPositionY = podPositionY + 1;}
if (podDirection.equals("SW")|| podDirection.equals("SE")){
podPositionY = podPositionY-1;}
if (podDirection.equals("NE")|| podDirection.equals("SE")){
podPositionX = podPositionX+1;}
if(podDirection.equals("NW")|| podDirection.equals("SW")){
podPositionX = podPositionX-1;}
/****************To make Pod bounce off wall******************/
//make pod bounce off left wall
if (podPositionX <= 1){
if (podDirection.equals("SW")){
podDirection = "SE";}
if (podDirection.equals("NW")){
podDirection = "NE";}
}
//make pod bounce off top
if (podPositionY >= layoutHeight-1){
if (podDirection.equals("NW")){
podDirection = "SW";}
if (podDirection.equals("NE")){
podDirection = "SE";}
}
//make pod bounce off right wall
if (podPositionX >= layoutWidth-1){
if (podDirection.equals("NE")){
podDirection = "NW";}
if (podDirection.equals("SE" )){
podDirection = "SW";}
}
//make pod bounce off bottom wall
if (podPositionY <= 1){
if (podDirection.equals("SW")){
podDirection = "NW";}
if (podDirection.equals("SE")){
podDirection = "NE";}
}
}
// to get player x and y positions
public void playerAt(int x, int y){
playerX = x;
playerY = y;
}
//envasive maneuver so that after 5 turns the pod can be transported away from the player if it is 1 spot away from the player.
//then the count is set to 100 so it will exit the loop and not be able to transport the pod again.
public int transportX(){
if (podPositionX == playerX -1 || podPositionX == playerX +1){
podPositionX= playerX +10;
count = 100;}
return podPositionX;
}
public int transportY(){
if (podPositionY == playerY -1 || podPositionY == playerY +1){
podPositionY= playerY +10;
count=100;}
return podPositionY;
}
}
the code my teacher provided us. Can no be touched so i can not put a counter in this file.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Project1 extends JFrame implements ActionListener {
// Member variables for visual objects.
private JLabel[][] board; // 2D array of labels. Displays either # for player,
// * for pod, or empty space
private JButton northButton, // player presses to move up
southButton, // player presses to move down
eastButton, // player presses to move right
westButton; // player presses to move left
// Current width and height of board (will make static later).
private int width = 15;
private int height = 9;
// Current location of player
private int playerX = 7;
private int playerY = 4;
// Pod object stored in array for efficiency
private Pod[] pods;
int podCount = 4;
public Project1() {
// Construct a panel to put the board on and another for the buttons
JPanel boardPanel = new JPanel(new GridLayout(height, width));
JPanel buttonPanel = new JPanel(new GridLayout(1, 4));
// Use a loop to construct the array of labels, adding each to the
// board panel as it is constructed. Note that we create this in
// "row major" fashion by making the y-coordinate the major
// coordinate. We also make sure that increasing y means going "up"
// by building the rows in revers order.
board = new JLabel[height][width];
for (int y = height-1; y >= 0; y--) {
for (int x = 0; x < width; x++) {
// Construct a label to represent the tile at (x, y)
board[y][x] = new JLabel(" ", JLabel.CENTER);
// Add it to the 2D array of labels representing the visible board
boardPanel.add(board[y][x]);
}
}
// Construct the buttons, register to listen for their events,
// and add them to the button panel
northButton = new JButton("N");
southButton = new JButton("S");
eastButton = new JButton("E");
westButton = new JButton("W");
// Listen for events on each button
northButton.addActionListener(this);
southButton.addActionListener(this);
eastButton.addActionListener(this);
westButton.addActionListener(this);
// Add each to the panel of buttons
buttonPanel.add(northButton);
buttonPanel.add(southButton);
buttonPanel.add(eastButton);
buttonPanel.add(westButton);
// Add everything to a main panel attached to the content pane
JPanel mainPanel = new JPanel(new BorderLayout());
getContentPane().add(mainPanel);
mainPanel.add(boardPanel, BorderLayout.CENTER);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
// Size the app and make it visible
setSize(300, 200);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Auxiliary method to create game setup
createGame();
}
// Auxiliary method used to create board. Sets player, treasure, and walls.
public void createGame() {
// Construct array of Pod objects
pods = new Pod[podCount];
// Construct each Pod in the array, passing it its initial location,
// direction of movement, and the width and heigh of board. This will
// later be modified to be done at random.
pods[0] = new Pod(1, 5, "NE", width, height);
pods[1] = new Pod(2, 1, "SW", width, height);
pods[2] = new Pod(12, 2, "NW", width, height);
pods[3] = new Pod(13, 6, "SE", width, height);
// Call method to draw board
drawBoard();
}
// Auxiliary method to display player and pods in labels.
public void drawBoard() {
// "Erase" previous board by writing " " in each label
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
board[y][x].setText(" ");
}
}
// Get location of each pod and write * into that label. We only
// do this for pods not yet caught.
for (int p = 0; p < podCount; p++) {
if (pods[p].isVisible()) {
board[pods[p].getY()][pods[p].getX()].setText("*");
}
}
// Write the player onto the board.
board[playerY][playerX].setText("#");
}
public void actionPerformed(ActionEvent e) {
// Determine which button was pressed, and move player in that
// direction (making sure they don't leave the board).
if (e.getSource() == southButton && playerY > 0) {
playerY--;
}
if (e.getSource() == northButton && playerY < height-1) {
playerY++;
}
if (e.getSource() == eastButton && playerX < width-1) {
playerX++;
}
if (e.getSource() == westButton && playerX > 0) {
playerX--;
}
// Move the pods and notify the pods about player location.
for (int p = 0; p < podCount; p++) {
pods[p].move();
pods[p].playerAt(playerX, playerY);
}
// Redraw the board
drawBoard();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Project1 a = new Project1();
}
}
I am looking at this quickly now. You said the problem is with the count variable. I see that you are incrementing it in the constructor i.e
public Pod(int, int, String, int, int){
count = count + 1;
}
why not put it in the move method. That way every time the move method is used the count variable will be incremented by one.
public void move(){
this.count++;
}
Also it is very hard to see exactly what you think your code is doing. I tried to run it and nothing happens. Sometimes it is good to print something to the console.
I am trying not to draw a button top of another.
I draw first button. Everything is fine
I try to draw the second button top of the first one. It does not let
me. Everything is fine.
I try to draw the third button top of the second button. it does not
let me. Everything is fine.
The problem is it remembers only the last button i draw. When i try to draw on top of the first button after drawing the third button; it let me do it.
What can i do that it will remember all the buttons.
Button class:
class Node {
int node_x; // x Position
int node_y; // y Position
int node_w; // width
int node_h; // height
String node_name; // name of the node
boolean mouseOverButton; // if mouse is over button
Node(int temp_node_x, int temp_node_y, int temp_node_number) {
node_x = temp_node_x;
node_y = temp_node_y;
node_w = 100;
node_h = 36;
node_name = Integer.toString(temp_node_number); //integer to string
mouseOverButton = false;
}
void display() {
rectMode(CENTER);
if (mouseOverButton) {
fill (YELLOW);
}else {
fill(MID_GRAY);
}
rect(node_x, node_y, node_w, node_h); //button
fill(BLACK); //text color black
text(node_name, node_x, node_y+6); //button text
rectMode(CORNER); // reset
}
boolean overButton() {
if (mouseX >= node_x -50 && mouseX <= node_x + 50 && mouseY >= node_y -18 && mouseY <= node_y + 18) {
mouseOverButton = true;
//Returns true back to main program
return mouseOverButton;
}
else {
mouseOverButton = false;
//Returns false back to main program
return mouseOverButton;
}
}
mousePressed
if (mouseY >= 25 && current_tab == "Node") {
if ( ((mouseX > 50) && (mouseY > 50)) && ((mouseX < 490) && (mouseY < 340)) ) { // Do not create the node edge of the window
for (int i = 0; i < listofNodes.size();i++) { // Loop through the ArrayList.
Node currentNode = (Node) listofNodes.get(i); // To be able to access the methods for the ArrayList element at the position 'i'
if (currentNode.overButton()) {
onit = true;
}
else {
onit = false;
}
}
if (!onit) {
listofNodes.add(new Node(mouseX, mouseY, nodecounter++)); // create a new button
}
}
}
All i had to do is add a break when onit was true