I wrote a Reversi program that makes a gui filled with a 2d array of buttons. there are several methods including validMoves() which makes the valid moves setEnabled(true); and shows a red dot so the user knows which buttons are active. it also flips the correct tiles when you pick which tile you want to move.
the problem i am running into is that the gui doesn't wait for the player to make a move. i wrote an ai which basically is the exact same as the player it finds the valid moves, finds which one will flip the most tiles and returns it in the format "x*y" which my method will then substring into x and y and make the move and flip the tiles.
i tried thread.sleep() and wait() and a while (!hasMoved) and a bunch of other things it just doesn't want to cooperate.
with the wait() i run into an infinite loop bc the gui is there but nothing is in it. however when i comment the wait out it shows up exactly as it should.
package reversi;
/*
* bugs need to make it check the edges too // it goes out of bounds
* refresh scores doesnt work
*
* add clear all readys after someone does a move
*/
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JApplet;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Timer;
/**
* #author 13ponchera
* the board for reversi that will play the game
*/
public class ReversiBoard extends JApplet
{
private int score1;
private int score2;
private int turn;
private Icon p1 = new ImageIcon("images/p1.png");
private Icon p2 = new ImageIcon("images/p2.png");
private Icon bGround = new ImageIcon("images/bGround.png");
private Icon ready = new ImageIcon("images/ready.png");
// will help the action listener determin which players turn it is
private int x; // the number of buttons
private JButton[][] buttons;
private JPanel settingPanel =
new JPanel();
private JButton newGame =
new JButton("New Game?");
private JLabel score1A;
private JLabel score2A;
private JPanel gameBoard =
new JPanel();
private AI ai = new AI(bGround,p1,p2);
private boolean hasMoved;
private Timer t;
/**
* Initialization method that
* will be called after the applet is loaded
* into the browser.
* sets up everything
*/
public void init()
{
score1 = 0;
score2 = 0;
// sets the viewer to say the score
score1A = new JLabel("Player: " + score1);
score2A = new JLabel("Computer:" + score2);
//adds components to the setting panel
settingPanel.add(newGame);
newGame.addActionListener(new NewGameListener());
settingPanel.add(score1A);//initialized at 0
settingPanel.add(score2A);
//creates a game board of any size as long as it is an even number
String a = JOptionPane.showInputDialog
(null, "please enter an even integer for x");
x = Integer.parseInt(a) + 2;
if ((x % 2 == 0) && (x > 4))
{
gameBoard = new JPanel(new GridLayout(x, x, 4, 4));
buttons = new JButton[x][x];
}// add 2 to x then set the edges to a new picture
else
{
//asks for a new number untill number is even
while ((x % 2 != 0) && (x < 4))
{
String responce = JOptionPane.showInputDialog
(null, "Must be an even "
+ "number rows and collums \n\tplease enter x");
x = Integer.parseInt(responce) + 2;
}
gameBoard = new JPanel(new GridLayout(x, x, 4, 4));
buttons = new JButton[x][x];
}
//creates the buttons for each slot and puts them into a 2d array
for (int c = 0; c < x; c++)
{
for (int d = 0; d < x; d++)
{
buttons[c][d] = new JButton();
buttons[c][d].setIcon(bGround);
// adds the background default pict
buttons[c][d].addActionListener
(new ButtonListener(buttons[c][d],c,d));
// adds a generic button listener
buttons[c][d].setEnabled(false);
// the buttons are non clickable except for the valid moves
gameBoard.add(buttons[c][d]);
buttons[c][d].setIcon(bGround);
buttons[c][d].setDisabledIcon(bGround);
// adds the buttons to the jpanel
}
}
//makes the edges a different color
// adds the jpanel with all of the buttons into the content pane
buttons[x/2][x/2]
.setIcon(p1);
buttons[x/2][x/2]
.setDisabledIcon(p1);
buttons[(x/2) - 1][(x/2) - 1]
.setIcon(p1);
buttons[(x/2) - 1][(x/2) - 1]
.setDisabledIcon(p1);
refreshTable();
// makes the middle upper left and lower right white pieces
buttons[(x/2) - 1][(x/2)]
.setIcon(p2);
buttons[x/2][(x/2) - 1]
.setIcon(p2);
buttons[(x/2) - 1][(x/2)]
.setDisabledIcon(p2);
buttons[x/2][(x/2) - 1]
.setDisabledIcon(p2);
refreshTable();
// makes the midle upper right and lower left black pieces
getContentPane()
.add(gameBoard, BorderLayout.CENTER);
getContentPane();//burp the applet
// after two days of hitting my head on the keyboard
getContentPane()
.add(settingPanel, BorderLayout.SOUTH);
//adds the setting panel (new game button
//and scores to the content pane)
refreshTable();
t = new Timer();
hasMoved = false;
validMoves();
refreshTable();
synchronized(t)
{
while(!hasMoved)
{
try
{
t.wait(50);
}
catch (InterruptedException ex)
{
Logger.getLogger(ReversiBoard.class.getName())
.log(Level.SEVERE, null, ex);
}
}
}
refreshTable();
aiTurn();
// make the edges a different picture
}
/**
* refreshes the table so that pictures will appear
*/
public void refreshTable()
{
for (int a = 0; a < x; a++)
{
for (int b = 0; b < x; b++)
{
buttons[a][b].repaint();
}
}
setScores();
score1A.setText
("Player: " + score1);
score2A.setText
("Computer: " + score2);
gameBoard.repaint();
settingPanel.repaint();
}
/**
* finds all the valid moves and sets the
* buttons enabled and gives them an identifiable picture
*/
public void validMoves()
{
int c = 0;
int d = 0;
// temperary values so search can go on uninterupted
for(int a = 1; a < x - 1; a++)
{
for(int b = 1; b < x - 1; b++)
{
//looks at every tile
if (buttons[a][b].getIcon() ==bGround)
{
//if it is a background child look to
//see if there is an enemy peace adjacent
if (buttons[a + 1][b].getIcon() == p2)
{
// keeps checking that direction
//until it runs to the end of the
//board or hits an ally
// c is the cordinates of where it finds
c = a + 1;
while ((buttons[c][b].getIcon() == p2)
&& (c < x)
&& (c > 0))
{
if (buttons[c + 1][b].getIcon() == p1)
{
buttons[a][b].setEnabled(true);
buttons[a][b]
.setIcon(ready);
//JOptionPane.showMessageDialog(null,"");
}
c++;
}
}
//see if there is an enemy peace adjacent
if (buttons[a - 1][b].getIcon() == p2)
{
// keeps checking that direction
//until it runs to the end of the
//board or hits an ally
// c is the cordinates of where it finds
c = a - 1;
while ((buttons[c][b].getIcon() == p2)
&& (c < x)
&& (c > 0))
{
if (buttons[c - 1][b].getIcon() == p1)
{
buttons[a][b].setEnabled(true);
buttons[a][b]
.setIcon(ready);
//JOptionPane.showMessageDialog(null,"");
}
c--;
}
}
if (buttons[a][b + 1].getIcon() == p2)
{
// keeps checking that direction
//until it runs to the end of the
//board or hits an ally
// c is the cordinates of where it finds
c = b + 1;
while ((buttons[a][c].getIcon() == p2)
&& (c < x)
&& (c > 0))
{
if (buttons[a][c + 1].getIcon() == p1)
{
buttons[a][b].setEnabled(true);
buttons[a][b]
.setIcon(ready);
//JOptionPane.showMessageDialog(null,"");
}
c++;
}
}
if (buttons[a][b - 1].getIcon() == p2)
{
// keeps checking that direction
//until it runs to the end of the
//board or hits an ally
// c is the cordinates of where it finds
c = b - 1;
while ((buttons[a][c].getIcon() == p2)
&& (c < x)
&& (c > 0))
{
if (buttons[a][c - 1].getIcon() == p1)
{
buttons[a][b].setEnabled(true);
buttons[a][b]
.setIcon(ready);
//JOptionPane.showMessageDialog(null,"");
}
c--;
}
}
//starts checking diagonals
if (buttons[a + 1][b + 1].getIcon() == p2)
{
// keeps checking that direction
//until it runs to the end of the
//board or hits an ally
// c is the cordinates of where it finds
c = a + 1;
d = b + 1;
while ((buttons[c][d].getIcon() == p2)
&& (c < x)
&& (c > 0)
&& (d < x)
&& (d > 0))
{
if (buttons[c + 1][d + 1].getIcon() == p1)
{
buttons[a][b].setEnabled(true);
buttons[a][b]
.setIcon(ready);
// JOptionPane.showMessageDialog(null,"");
}
c++;
d++;
}
}
if (buttons[a - 1][b - 1].getIcon() == p2)
{
// keeps checking that direction
//until it runs to the end of the
//board or hits an ally
// c is the cordinates of where it finds
c = a - 1;
d = b - 1;
while ((buttons[c][d].getIcon() == p2)
&& (c < x)
&& (c > 0)
&& (d < x)
&& (d > 0))
{
if (buttons[c - 1][d -1].getIcon() == p1)
{
buttons[a][b].setEnabled(true);
buttons[a][b]
.setIcon(ready);
//JOptionPane.showMessageDialog(null,"");
}
c--;
d--;
}
}
if (buttons[a - 1][b + 1].getIcon() == p2)
{
// keeps checking that direction
//until it runs to the end of the
//board or hits an ally
// c is the cordinates of where it finds
c = a - 1;
d = b + 1;
while ((buttons[c][d].getIcon() == p2)
&& (c < x)
&& (c > 0)
&& (d < x)
&& (d > 0))
{
if (buttons[c - 1][d + 1].getIcon() == p1)
{
buttons[a][b].setEnabled(true);
buttons[a][b]
.setIcon(ready);
// JOptionPane.showMessageDialog(null,"");
}
c--;
d++;
}
}
if (buttons[a + 1][b - 1].getIcon() == p2)
{
// keeps checking that direction
//until it runs to the end of the
//board or hits an ally
// c is the cordinates of where it finds
c = a + 1;
d = b - 1;
while ((buttons[c][d].getIcon() == p2)
&& (c < x)
&& (c > 0)
&& (d < x)
&& (d > 0))
{
if (buttons[c + 1][d - 1].getIcon() == p1)
{
buttons[a][b].setEnabled(true);
buttons[a][b]
.setIcon(ready);
// JOptionPane.showMessageDialog(null,"");
}
c++;
d--;
}
}
}
}
}
refreshTable();
/*try
{
Thread.sleep(50); // 30 seconds NA
}
catch (InterruptedException ex)
{
Logger.getLogger(ReversiBoard.class.getName())
.log(Level.SEVERE, null, ex);
}*/
}
/**
* calculates the scores of each player
*/
/**
*
* #return boolean if game is over
*/
public boolean winner()
{
refreshTable();
if (!anyMove(1) && !anyMove(0))
return true;
//joptionpan the winner (if score1 > score 2).... and vv
return false;
}
/**
* #param player 1 for player 1 for computer
* #return boolean if there is a move
*/
public void clearReady()
{
for (int a = 0; a < x; a++)
{
for (int b = 0; b < x; b++)
{
if (buttons[a][b].getIcon() == ready)
{
buttons[a][b].setIcon(bGround);
buttons[a][b].setEnabled(false);
buttons[a][b].setDisabledIcon(bGround);
}
}
}
}
private void makeMove(String move)
{
int temp = 0;
char[] f = move.toCharArray();
for(int i = 0; i < f.length; i++)
{
if (f[i] == ('*'))
temp = i;
}
int x = Integer.parseInt(move.substring(0,temp));
int y = Integer.parseInt(move.substring(temp + 1));
buttons[x][y].setIcon(p2);
buttons[x][y].setEnabled(false);
buttons[x][y].setDisabledIcon(p2);
flip(1,x,y);
}
private void aiTurn()
{
String move = ai.makeMove(buttons);
// string move is a string with cordinates seperated by a *
makeMove(move);
turn++;
}
class ButtonListener implements ActionListener
{
private JButton a;
private int x;
private int y;
private ButtonListener(JButton jButton, int c, int d)
{
a = jButton;
x = c;
y = d;
}
public void actionPerformed(ActionEvent e)
{
//must differentiate between p1 and p2
switch (turn % 2)
{
case 0:
a.setIcon(p1);
a.setEnabled(false);
a.setDisabledIcon(p1);
flip(0,x,y);
turn++;
clearReady();
hasMoved = true;
/*synchronized(t)
{
t.notifyAll();
}*/
refreshTable();
break;
case 1:
a.setIcon(p2);
a.setEnabled(false);
a.setDisabledIcon(p2);
flip(1,x,y);
turn++;
clearReady();
hasMoved = true;
/*synchronized(t)
{
t.notifyAll();
}*/
refreshTable();
break;
}
}
}
}
See below how your GUI application should behave (more or less) :
create the widgets
register listeners to the widgets
wait for user action
update the Gui to display user's move
call the AI to do its move and display it
go back to point 3
You already have done the points 1 and 2 done in your init method. You should remove the last part of this method (the block with the while(!hasMoved) loop) : it's the point 3, and it is handled by the Applet after the init is finished. You have nothing to do.
When the user clicks on a JButton (provided the button is enabled), the method actionPerformed of ButtonListener is called: its the start of point 4, you should update your widgets (refreshTable() in your code), and make the AI move (aiTurn(), point 5). When the method actionPerformed is finsihed, the JApplet goes back automatically to point 3, you've nothing to do.
I don't know Applets very well, so I can't be more precise. You should search for JApplet examples/tutorials with similar behaviors as yours. Remember you should not have infinite loops in your code waiting for user's input : it's handled by the JApplet.
Related
I am a beginner in Java as well as OOP.
I am creating a simulator of "a box with particles". Here's what required in the program:
A box with fixed WIDTH & HEIGHT with the pattern of "-" and " | "
Particles with x, y position (0 <= x <= WIDTH; 0 <= y <= HEIGHT). Symbol: *
Enum Direction of 8 directions
move() all particles in random direction and create a new particle if any of them collide (same position)
What I've been strugging to find the answer is that how can I create random number of particles with a loop and can still work on them outside the loop? Because I want each created particle remain after an iteration for further move() execution. Is there any syntax for this kind of access?
Here's what I've tried as this sometimes output 2, 3 particles, sometimes none:
public Box() {
particle = new Particle();
for (int i = 0; i <= HEIGHT + 1; i++) {
for (int j = 0; j <= WIDTH + 1; j++) {
if (i == 0 || i == HEIGHT + 1 && i != particle.getY()){
System.out.print("-");
} else {
if (j == particle.getX() && i == particle.getY()) {
System.out.print("*");
} else if (j == 0 || j == WIDTH + 1 && j != particle.getX()) {
System.out.print("|");
} else if (i != 0 && i != HEIGHT + 1) {
System.out.print(" ");
}
}
}
System.out.println("");
}
}
Coding is easier if you break the large task into smaller tasks.
I separated the creation of the particles from the display of the particles. That way, I could focus on one part of the task at a time.
Here's the output I generated.
|------------------|
| * |
| |
| * * * * |
| * * |
| * * |
| * * * * |
| * |
| * * |
|------------------|
All I did was generate the initial condition. I'm leaving it up to you to move the particles and check for collisions. I'm assuming that once a particular particle moves south, as an example, it continues south until it hits another particle or the wall. Hitting the wall would change the direction to north.
My Eclipse generated the hashCode and equals methods of the Particle class. These methods are essential for the Set contains method to work. I used a Set so there would be no duplicate particles. Because the particles are generated randomly, there might not be all maximum particles in the simulation.
In the generateParticles method, I get a random w between 1 and WIDTH - 1, inclusive, and a random h between 1 and HEIGHT -1, inclusive. This ensures that all particles created are inside the "box".
Here's the complete runnable example code.
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
public class ParticleSimulator {
public static void main(String[] args) {
ParticleSimulator ps = new ParticleSimulator();
ps.displaySimulation();
}
private static int WIDTH = 20;
private static int HEIGHT = 10;
private Random random;
private Set<Particle> particles;
public ParticleSimulator() {
this.random = new Random();
this.particles = generateParticles(20);
}
private Set<Particle> generateParticles(int maximum) {
Set<Particle> particles = new HashSet<>();
for (int i = 0; i < maximum; i++) {
int x = random.nextInt(WIDTH - 1) + 1;
int y = random.nextInt(HEIGHT - 1) + 1;
Particle particle = createParticle(x, y);
particles.add(particle);
}
return particles;
}
public void displaySimulation() {
for (int h = 0; h < HEIGHT; h++) {
for (int w = 0; w < WIDTH; w++) {
if (w == 0 || w == (WIDTH - 1)) {
System.out.print('|');
continue;
}
if (h == 0 || h == (HEIGHT - 1)) {
System.out.print('-');
continue;
}
Particle particle = createParticle(w, h);
if (particles.contains(particle)) {
System.out.print('*');
} else {
System.out.print(' ');
}
}
System.out.println();
}
}
private Particle createParticle(int x, int y) {
Particle particle = new Particle();
particle.setX(x);
particle.setY(y);
return particle;
}
public class Particle {
private int x;
private int y;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result +
getEnclosingInstance().hashCode();
result = prime * result + x;
result = prime * result + y;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Particle other = (Particle) obj;
if (!getEnclosingInstance().equals(
other.getEnclosingInstance()))
return false;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
private ParticleSimulator getEnclosingInstance() {
return ParticleSimulator.this;
}
}
}
If you don't know what Conway's game of life is check this, it is a zero player game, and a cellular automation. https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
I need help debugging the following problems in my code. If necessary, I can edit this question to include older versions of my program, which are not intended to expand.
I will also accept tips to improve my program, including (but not limited to) time efficiency, (off topic however, as it is not the main question) and incorrect use of existing methods. (My paint() method)
Problems:
Examine Glider A in image 1. (Expected behavior) The whole point of the program was to make the cells shrink in the JFrame and add another row or column of dead cells to the ArrayList<ArrayList<Boolean>> grid. This glider, unfortunately does not cause this to happen. Instead, the result (Unexpected behavior) was that it simply "flattened into a square" as shown in image 2 (the square marked with an A).
Examine Glider B in image 1. (Expected behavior) Seeing the results Glider A in image 1 made me think that glider B would end the same. However, this is not remotely true. Look at glider B in image 2. It, for some reason, does not even reach the border. (Unexpected)
What this program is for and meant to do: Conway's game of life is on an infinite 2D plane, so I wanted to replicate this. My other programs used a fixed array size, but this used nested ArrayLists to expand, and truly be infinite (until the memory limit comes)
Unfortunately, this expansion program simply fails to expand the border. (The border is actually smaller than it looks. Basically, (pretending my ArrayList> grid is an array) grid[0][0 to grid.length-1] inclusive, grid[grid.length-1][0 to grid.length-1] inclusive, and grid[1 to grid.length-1][0] inclusive, and grid[1 to grid.length-1][grid.length-1] inclusive.) Instead of doing the specified steps to make room, glider A in image 1 flattened against the border. (The border is beneath the grid of cells, where grey lines are not present)
Image 1 Image 2
These images correspond with each other; glider A from image 1 Heading produces the square in image 2, also
marked as A. If you see any words describing "line 74," pointing toward "line 74" or words like "UP" (without any other meaning) in comments in the code please tell me, so I can remove them. That was a separate, already fixed bug.
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.Timer;
public class Expansion {
static int delay = 50;
static SquarePaint sp = new SquarePaint();
static int K = 75; // An integer used below to create the starting setup for the grid.
static{
// 001 101 011
boolean[][] array = new boolean [300][200];
// array[30][30]
array[K][K+2] = true;
array[K+1][K] = true;
array[K+1][K+2] = true;
array[K+2][K+1] = true;
array[K+2][K+2] = true;
sp.define(array); // Uses array to make first setup
}
public static void main (String[] args){
// It is possible to put this information in a constructor instead
JFrame j = new JFrame();
j.add(sp); // Must add a custom paint component overriding the paint component,
//a class could and will do this
j.setVisible(true);
j.setSize(2000, 1000);
j.setResizable(false);
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static class SquarePaint extends Component{
static boolean VALUE = false;
public static final int screenX = 1000, screenY = 500;
static int cellSize = 5;
int cellsX = screenX / cellSize, cellsY = screenY / cellSize, frames = 0;
ArrayList<ArrayList<Boolean>> grid = new ArrayList<ArrayList<Boolean>>(); ////
Timer timer = new Timer(delay, new ActionListener(){
#Override
public void actionPerformed(ActionEvent A) {
//UPDATE FUNCTION
frames++;
boolean [][] oldGrid = new boolean[cellsX][cellsY];
for(int i=0; i<cellsX && i<grid.get(1).size(); i++){
for(int j=0; j<cellsY && j<grid.get(1).size(); j++){
oldGrid[i][j] = grid.get(i).get(j); // <<Line 74>><<Line 74>><<Line 74>>
}
}
/* These are notes
* How would I:
* Expand LinkedLists, for example
(Grid in a picture, 0 = variable, + = new cell)
0 0 0 +
0 0 0 +
0 0 0 +
0 0 0 +
* Adjust existing variables (and poorly coded methods if they exist) to suit this new change
*/
for(int i=1; i<cellsX-2; i++) {
for(int j=1; j<cellsY-2; j++) { // TODO
// System.out.println(i + " " + j);
int nearbyCells = nearbyCells(oldGrid, i, j);
/////////////////////////////////////////////
if(oldGrid[i][j]){
// If the cell is alive
// and is beside the no interact border, expand it by 1
// (Basically, to prevent ArrayOutOfBoundsException s,
// (the ArrayList equivalent) only cells from 1 to cellsX-1(-1)/cellsY-1(-1)
// are counted,)
// meaning that a whole row/column of cells is ignored and is not interacted with,
// until the grid expands.
ArrayList <Boolean> l = new ArrayList<Boolean>();
for(int k=0; k<cellsX; k++){
l.add(false);
}
if(i <= 1){ // You may notice that the above comments are wrong.
// This is because I thought this might be a small chance for it to succeed
// while I was frustrated this was not working.
grid.add(0, l);
cellSize = screenX / (cellSize + 1);
cellsX++;
VALUE = true;
repaint();
System.out.println("i<=5");
}
if(i >= cellsX-1){
grid.add(l);
cellSize = screenX / (cellSize + 1);
cellsX++;
VALUE = true;
repaint();
System.out.println("i>=cellsX");
}
if(j <= 1) {
for(int k=0; k<cellsY; k++){
grid.get(k).add(false);
}
cellSize = screenY / (cellSize + 1);
cellsY++;
VALUE = true;
System.out.println("j<=5");
repaint();
}
if(j >= cellsY-1) {
for(int k=0; k<cellsY; k++){
grid.get(k);
}
cellSize = screenY / (cellSize + 1);
cellsY++;
VALUE = true;
System.out.println("j>cellsY");
repaint();
}
//corner cases aren't a problem, it'll add to both sides
// && (i == 1 || i == cellsX-1 || j == 1 || j == cellsY-1)
/* i.e.
* 0 = dead cells
* 1 = annoying cells in the corner
* + = new cells
* First, it'll do this
* + + +
* 0 1 1
* 0 1 1 // Note that the square is a "still life form" meaning that it will
* 0 0 0 // simply sit there and do nothing for infinite generations if
* // untouched
* But the if statements above will return true more than once, so
* 0 0 0 +
* 0 1 1 +
* 0 1 1 +
* 0 0 0 +
*/
}
/////////////////////////////////////////////
if (oldGrid[i][j] && !(nearbyCells == 2 || nearbyCells == 3)){
// if it is alive, sustain rules
grid.get(i).set(j, false);
}
else if (!oldGrid[i][j] && nearbyCells == 3){ // if it is dead, birth rules
grid.get(i).set(j, true);
}
}
}
repaint(); // never erase, note that in bigger applications I assume that they
// draw to pictures that get slapped onto the screen so the paint function doesn't
// get called 9999 times and slow the fps down
}
});
int nearbyCells(boolean[][] oldGrid, int i, int j) { // A method that calculates how many cells are
//alive near it, i.e.
/* 0 = Dead
* 1 = Alive
* "+" = center
* 1 0 0
* 0 + 1
* 0 0 1
* If you called nearbyCells on the center in the example, then it would return 3.
*/
int nearbyCells = 0;
if(oldGrid[i+1][j+1]) nearbyCells++;
if(oldGrid[i+1][j]) nearbyCells++;
if(oldGrid[i+1][j-1]) nearbyCells++;
if(oldGrid[i][j+1]) nearbyCells++;
if(oldGrid[i][j-1]) nearbyCells++; // i, j is where the to-be-changed cell is, skip it
if(oldGrid[i-1][j+1]) nearbyCells++;
if(oldGrid[i-1][j]) nearbyCells++;
if(oldGrid[i-1][j-1]) nearbyCells++;
return nearbyCells;
}
public SquarePaint(boolean[][] grid){
define(grid);
timer.start();
}
public SquarePaint(){
timer.start();
}
public void define (boolean[][] grid){ // define cannot be called twice, not intended
for(int i=0; i<cellsX; i++){
ArrayList<Boolean> l = new ArrayList<Boolean>();
for(int j=0; j<cellsY; j++){
l.add(grid[i][j]);
}
this.grid.add(l);
}
}
#Override
public void paint(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, screenX, screenY); // Warning: Color must be set
// each time if the paint command is ever to be called again
for(int i=0; i<cellsX-1; i++){
for(int j=0; j<cellsY-1; j++){
try{
if(grid.get(i).get(j)){
g.setColor(Color.WHITE);
g.fillRect(i * cellSize, j * cellSize, cellSize, cellSize);
} // Basically, 2 for loops
}// Nested for loops draw each cell on the potentially expanding ArrayList "grid" of cells
catch(java.lang.IndexOutOfBoundsException e){
System.out.println(i + " " + j);
e.getStackTrace();
}
g.setColor(Color.GRAY);
g.drawRect(i * cellSize, j * cellSize, cellSize, cellSize);
}
}
}
}
}
I have no idea what the logic in the middle of your application is supposed to be doing. Shouldn't be that complicated. This runs the simulation in another thread and so perhaps helps address performance.
public class GameOfLife extends JFrame {
private static final long serialVersionUID = 1L;
private Thread thread;
private SimulationThread simulationThread;
public GameOfLife() {
super("GameOfLife");
}
class MyPanel extends JPanel {
private static final long serialVersionUID = 1L;
public MyPanel() {
setBorder(BorderFactory.createLineBorder(Color.black));
}
public Dimension getPreferredSize() {
return new Dimension(1000, 1000);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
simulationThread.drawScreenItems(g2d);
}
}
/**
* Create the GUI and show it. For thread safety, this method should be invoked
* from the event-dispatching thread.
*/
public void createAndShowGUI() {
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyPanel myPanel = new MyPanel();
add(myPanel);
// Display the window.
pack();
setLocationRelativeTo(null);
setVisible(true);
// Create and set up the window.
// create thread with genetic reproduction callback code.
simulationThread = new SimulationThread(myPanel);
thread = new Thread(simulationThread);
thread.start();
}
public static void main(String[] args) {
final GameOfLife swingGa = new GameOfLife();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
swingGa.createAndShowGUI();
}
});
}
}
public class SimulationThread implements Runnable {
private MyPanel myPanel;
public boolean run = true;
static final int GAME_SIZE = 200;
boolean[][] cells;
boolean[][] newCells;
public SimulationThread(MyPanel myPanel) {
this.myPanel = myPanel;
cells = new boolean[GAME_SIZE][GAME_SIZE];
for (int i = 1; i < cells.length - 1; i++) {
for (int j = 1; j < cells[0].length - 1; j++) {
cells[i][j] = Math.random() > 0.5;
}
}
newCells = new boolean[GAME_SIZE][GAME_SIZE];
}
#Override
public void run() {
while (run) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Iterate through the array, follow game of life rules
for (int i = 1; i < cells.length - 1; i++) {
for (int j = 1; j < cells[0].length - 1; j++) {
int surrounding = 0;
if (cells[i - 1][j - 1]) surrounding++;
if (cells[i - 1][j]) surrounding++;
if (cells[i - 1][j + 1]) surrounding++;
if (cells[i][j - 1]) surrounding++;
if (cells[i][j + 1]) surrounding++;
if (cells[i + 1][j - 1]) surrounding++;
if (cells[i + 1][j]) surrounding++;
if (cells[i + 1][j + 1]) surrounding++;
newCells[i][j] = false;
if (cells[i][j]) {
// Cell is alive, Can the cell live? (2-3)
if ((surrounding == 2) || (surrounding == 3)) {
newCells[i][j] = true;
}
} else {
// Cell is dead, will the cell be given birth? (3)
if (surrounding == 3) {
newCells[i][j] = true;
}
}
}
}
synchronized(cells) {
for (int i = 1; i < cells.length - 1; i++) {
for (int j = 1; j < cells[0].length - 1; j++) {
cells[i][j] = newCells[i][j];
}
}
}
myPanel.repaint();
}
}
public void drawScreenItems(Graphics2D g2d) {
synchronized(cells) {
for (int i = 1; i < cells.length - 1; i++) {
for (int j = 1; j < cells[0].length - 1; j++) {
if (cells[i][j])
g2d.fillRect(i * (1000 / GAME_SIZE), j * (1000 / GAME_SIZE), 1000 / GAME_SIZE, 1000 / GAME_SIZE);
}
}
}
}
}
trying to create a Conways Game of life, but apparently the shapes are not like they have to be. Perhaps someone can help me find the issue.
For example the glider :
- X - - - -
- - X X - -
- X X - - -
- - - - - -
becomes this
- - X X - -
- X - - - -
X X X - - -
- X X X - -
but should be like this :
- - X - - -
- - - X - -
- X X X - -
- - - - - -
And my code looks like this
public Frame(int x, int y) {
setWidth(x);
setHeight(y);
if (x<1)
frame = null;
else if (y<1)
frame = null;
else {
frame = new String [x][y];
for (int i=0; i<frame.length; i++) {
for (int j=0; j<frame[i].length; j++) {
frame [i][j] = DEAD;
}
}
} // else
} // construktor
public Integer getNeighbourCount(int x, int y) {
Frame cell = new Frame(getHeight(), getWidth());
int counter = 0;
if(frame[x][y].equals(ALIVE))
{
counter = counter - 1;
}
for(int i=x-1; i<=x+1;i++){
if(i<frame.length && i>0){
for(int j=y-1; j<=y+1;j++){
if(j<frame[i].length && j>0){
if (frame[i][j]==ALIVE) {
counter++;
}
}
}
}
}
return counter;
}
public Frame nextFrame() {
// Returns next frame
Frame cell = new Frame(getWidth(), getHeight());
//cell.frame = new String[getWidth()][getHeight()];
for(int i = 0; i < frame.length; i++){
for(int j =0; j <frame[i].length;j++){
int n = getNeighbourCount(i,j);
if(cell.frame[i][j]==null) {
cell.frame[i][j] = DEAD;
}
if (isAlive(i, j) && n < 2 || n > 3) {
cell.frame[i][j] = DEAD;
}
if (isAlive(i, j) && n == 3 || n == 2){
cell.frame[i][j] = ALIVE;
}
if(!isAlive(i, j) && n == 3) {
cell.frame[i][j] = ALIVE;
}
if(isAlive(i, j) && n > 3){
cell.frame[i][j] = DEAD;
}
frame[i][j] = cell.frame[i][j];
}
}
cell.toString();
return cell;
}
`
Full code http://pastebin.com/LMwz724H
Here's a solution that works - using an enum for each cell and getting the i/j and x/y stuff right (I think). It certainly generates the correct first iteration:
static class GameOfLife {
final int w;
final int h;
State[][] frame;
enum State {
Dead, Alive;
}
public GameOfLife(int w, int h) {
this.w = w;
this.h = h;
frame = new State[h][w];
}
public void alive(int x, int y) {
frame[y][x] = State.Alive;
}
public void tick() {
frame = nextGeneration();
}
private int surroundingPopulation(int x, int y) {
int pop = 0;
for (int i = y - 1; i <= y + 1; i++) {
for (int j = x - 1; j <= x + 1; j++) {
// On frame - vertically.
if ((i >= 0 && i < h)
// On frame horizontally.
&& (j >= 0 && j < w)
// Alive
&& (frame[i][j] == State.Alive)
// Not the center.
&& (i != y || j != x)) {
pop += 1;
}
}
}
return pop;
}
private State[][] nextGeneration() {
State[][] next = new State[h][w];
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int pop = surroundingPopulation(x, y);
// Any live cell
if (frame[y][x] == State.Alive) {
if (pop < 2) {
// ... with fewer than two live neighbours dies, as if caused by under-population.
next[y][x] = State.Dead;
} else if (pop > 3) {
// ... with more than three live neighbours dies, as if by overcrowding.
next[y][x] = State.Dead;
} else {
// ... with two or three live neighbours lives on to the next generation.
next[y][x] = State.Alive;
}
} else {
// Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
if (pop == 3) {
next[y][x] = State.Alive;
}
}
}
}
return next;
}
#Override
public String toString() {
StringBuilder s = new StringBuilder();
for (State[] row : frame) {
for (State c : row) {
s.append(c == State.Alive ? "X" : " ");
}
s.append("\r\n");
}
return s.toString();
}
}
public void test() {
GameOfLife g = new GameOfLife(6, 6);
g.alive(1, 0);
g.alive(2, 1);
g.alive(3, 1);
g.alive(1, 2);
g.alive(2, 2);
System.out.println("Before:\r\n" + g);
g.tick();
System.out.println("After:\r\n" + g);
}
I believe the problem is that you are copying the new value as you iterate through the loop. This means neighbours are using the value from the next tick rather than the current one.
You can fix this by waiting until you calculated all new values in your new frame: cell.frame and then iterate through the frame again and copy from cell.frame to frame.
An alternative (better in my view) is to have away of cloning a frame during construction. Then you could change your nextFrame method to create a clone of frame and use the clone to set the new values in frame.
You are changing the DEAD and ALIVE frames while you iterate through the grid. You need to store the coordinates which should die or become alive and perform that afterwards.
Store the coordinates in two ArrayLists (dead, alive). The first and second position is the x and y axis, and change those coordinates according to whether they should become alive or not.
Here's a snippet from a simple test I wrote a while back. As others have mentioned, don't change values on an active board while still reading them. Instead, clone the board and make changes to the copy while reading the current board.
Another problem I bumped into a few times was iterating over y, then x for each y, but referring to x,y when accessing a point. It feels back to front :)
// Rules:
// 1) Any live cell with fewer than two live neighbours dies, as if caused by under-population.
// 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, as if by overcrowding.
// 4) Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
void mutateGrid() {
// Copy existing grid into the next generation's grid
boolean[][] mutatedGrid = new boolean[gridXWidth][gridYHeight];
for (int i = 0; i < gridXWidth; i++) {
System.arraycopy(grid[i], 0, mutatedGrid[i], 0, gridYHeight);
}
// Start mutation rules
for (int y = 0; y < gridYHeight; y++) {
for (int x = 0; x < gridXWidth; x++) {
int liveNeighbours = countLiveNeighbours(x,y);
if (liveNeighbours < 2 || liveNeighbours > 3) {
mutatedGrid[x][y] = false;
}
else if (liveNeighbours == 3) {
mutatedGrid[x][y] = true;
}
}
}
grid = mutatedGrid;
}
int countLiveNeighbours(int x, int y) {
int count = 0;
for (int j = y-1; j <= y+1; j++) {
for (int i = x-1; i <= x+1; i++) {
if (i < 0 || j < 0 || i >= gridXWidth || j >= gridYHeight){
continue;
}
if (grid[i][j]) {
count++;
}
}
}
count -= grid[x][y]?1:0; // remove self from count
return count;
}
In my program you can place pictures on the applet if you have the required minerals
you start with 400 and each one is 100 minerals so you place 3 and then on the 4th it will delete all the others
public int Minerals
//Pylons
int xCoord[];
int yCoord[];
int numSquare;
boolean firstPaint;
public void init()
{
Minerals = 400;
pylon = getImage(getDocumentBase(),"image/pylon.png");
//pylons
xCoord = new int[100];
yCoord = new int[100];
numSquare = 0;
firstPaint = true;
}
public void paint(Grapics g)
{
pylons(g);
}
public void pylons(Graphics g)
{
//Building the pylons
if(Minerals >= 100)
{
for (int k = 0; k < numSquare; k++)
{
g.drawImage(pylon,xCoord[k],yCoord[k],85,85,this);
//Makes cursor normal.
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
//Checks if buildPylon is 1 if so it will draw the infoScreen and then set the cursor to pylon
if(buildPylon == 1)
{
g.drawImage(pylon,510,820,100,100,this);
//Use the custom cursor
setCursor(cursor);
}
}
private void handlePylonPlacement()
{
//This takes away 100 minerals and will add 9 population and make buildPylon 0 again
if(decrementMinerals(100))
addPopMax(9);
buildPylon = 0;
}
private boolean decrementMinerals(int amount)
{
//This Is where it takes away the minerals
if(Minerals - amount >= 0) // prevent situation where you go into negative minerals
{
Minerals -= amount;
return true;
}
else
return false;
}
private void addPopMax(int amount)
{
//Add the population (9)
if(popMax + amount <= 72) // restrict addition to pop-max to a sane upper bound
popMax += amount;
}
public boolean mouseDown(Event e, int x, int y) {
//Makes the ints xCoord2 to equal x and same for the other
xCoord2 = x;
yCoord2 = y;
repaint();
if (numClicks == 10) {//Title screen
numClicks++;
repaint();
}
//Checks to see if buildPylon == 1 then builds the pylon if its in the correct place.
if(buildPylon == 1)
{
if(x > 1 && x < 1275 && y > 711 && y < 948) // Checks to see if the person clicked in this area
{}
else if(x > 378 && x < 876 && y < 568 && y < 705) // Checks to see if the person clicked in this area
{}else if (Minerals >= 100)
{
xCoord[numSquare] = x;
yCoord[numSquare] = y;
numSquare++;
handlePylonPlacement(); // call your new handler
repaint();
}
}
return true;
}
Instead of it deleting them i want the pylons to stay painted on the screen... any help?
In handlePylonPlacement(), did you mean
private void handlePylonPlacement()
{
//This takes away 100 minerals and will add 9 population and make buildPylon 0 again
if (decrementMinerals(100)) {
addPopMax(9);
buildPylon = 0;
}
}
?
My board correctly detects groups with less than 3 neighbors and kills them off, but doesn't seem to detect and give birth to cells with 3 neighbors.
Any thoughts?
If I haven't provided enough information let me know, and I can paste more of the code, but I think this is all of the relevant parts.
Thank you in advance for any advice offered.
public boolean getCell(int row, int col) {
boolean state = board[row][col];
int neighbors = 0;
for (int x = row-1; x <= row+1; x++) {
for (int y = col-1; y <= col+1; y++) {
// don't include this
if ((x != row || y != col) && x != -1 && y != -1
&& x != NROWSCOLS && y != NROWSCOLS) {
if (board[x][y] == ALIVE){
neighbors ++;
}
}
}
}
if (neighbors > 3 || neighbors < 2)
state = DEAD;
else if(neighbors == 3)
state = ALIVE;
return state;
}
Here is the lifeCycle method requested.
/** Process one life cycle of the cellular automaton
*
*/
public void lifeCycle() {
for (int x = 0; x < NROWSCOLS ; x++) {
for (int y = 0; y < NROWSCOLS; y++) {
getCell(x,y);
}
}
generations ++;
}
I've attached the LifeGUI for reference, but this code is provided and not intended for me to change.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class LifeGUI extends JPanel {
// game instance variables
private Life board; // game board
// GUI components
private JLabel generationsLived;
private JButton resetButton, cycleButton; // reset control and cycle control
private Cell[][] cells; // board cells for display
/** Construct new Life game with a graphical user interface */
public LifeGUI() {
// create and initialize game board and display representation
board = new Life();
cells = new Cell[Life.NROWSCOLS][Life.NROWSCOLS];
// set layout for game display
setLayout(new BorderLayout());
// Create board cells and add to display
JPanel boardPanel = new JPanel();
boardPanel.setLayout(new GridLayout(Life.NROWSCOLS, Life.NROWSCOLS));
for (int row = 0; row < Life.NROWSCOLS; row++) {
for (int col = 0; col < Life.NROWSCOLS; col++) {
cells[row][col] = new Cell(Life.DEAD, row, col);
boardPanel.add(cells[row][col]);
}
}
add(boardPanel, BorderLayout.CENTER);
// Set up 2 buttons
// a reset button so it starts a new game when clicked
// a cycle button to tell the Life automaton to live one cycle
resetButton = new JButton("New Game");
resetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
board.newGame();
updateDisplay();
}
});
cycleButton = new JButton("Live One Cycle");
cycleButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
board.lifeCycle();
updateDisplay();
}
});
// Put the buttons and the generation count display on the screen
JPanel buttonPanel = new JPanel();
buttonPanel.add(resetButton);
buttonPanel.add(cycleButton);
generationsLived = new JLabel(" Generations Lived: " , JLabel.RIGHT);
buttonPanel.add(generationsLived);
add(buttonPanel, BorderLayout.SOUTH);
// show initial display
updateDisplay();
}
/** Update display to match game state. */
public void updateDisplay() {
// update count display
generationsLived.setText(" Generations Lived: " + board.getGenerationCount());
// update board display
for (int row = 0; row < Life.NROWSCOLS; row++) {
for (int col = 0; col < Life.NROWSCOLS; col++) {
cells[row][col].setState(board.getCell(row,col));
}
}
repaint();
}
/** Create new game and a window to display it */
private static void test() {
JFrame f = new JFrame("The Game of Life"); // top-level window
LifeGUI l = new LifeGUI();
f.getContentPane().add(l);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(600,600);
f.validate();
f.setVisible(true);
f.toFront();
}
public static void main(String[] args) {
// To support stand-alone application
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
LifeGUI.test();
}
});
}
}
public boolean getCell(int row, int col) {
boolean state = board[row][col];
int neighbors = 0;//you are summing the alive neighbours
//keep var declaration outside the iteration over them
for (int x = Math.max(0,row-1); x < Math.min(row+2,NROWSCOLS); x++) {
for (int y = Math.max(0,col-1); y < Math.min(col+2,NROWSCOLS); y++) {
//using min and max to ensure x and y remain between 0 and NROWSCOLS
//so no IOBException
if (board[x][y] == ALIVE){
neighbors ++;
}
}
}
}
if (neighbors > 3 || neighbors < 2)//only do the check when you are finished counting the alive neighbours
state = DEAD;
else if(neighbors == 3)
state = ALIVE;
return state;
}
check your loop is not correct.
for (int x = row-1; x <= row; x++)
it will always go to board[row] index. which is out of bound . same thing for other loop
Please check this
at Life.getCell(Life.java:63)
The problem is in the line 63 of Life.java
I don't know if the problem is there but check this line:
if (x != row || y != col && x != -1 && y != -1
&& x != NROWSCOLS && y != NROWSCOLS) {
because is not doing what you think it's doing beacuse of the operator precedence,
please, add some parentheses.
Like this
if ((x != row || y != col) && x != -1 && y != -1
&& x != NROWSCOLS && y != NROWSCOLS) {
The || will not work as intended without adding perenthisis.
Current code will do this:
if (x != row || (y != col && x != -1 && y != -1 && x != NROWSCOLS && y != NROWSCOLS))
You prob want this
if ((x != row || y != col) && x != -1 && y != -1 && x != NROWSCOLS && y != NROWSCOLS)
or this
if (x != row && y != col && x != -1 && y != -1 && x != NROWSCOLS && y != NROWSCOLS)