I was working on a Conway's game of life clone because it is good practice, but I ran into a problem. I see there are pixels deleting and rebirthing, but all the pixels just spread out the very end of the screen and then other pixels are rebirthing, but it is just idling at that point.
Here are some screenshots:
I'll show you some of my code for the logic of this. It is all handled in the change method:
package main;
import java.awt.Color;
import java.awt.Graphics;
public class Functions {
public static int pixelsize=6,gridwidth=800/6,gridheight=600/6;
static int[][] pixels = new int[gridwidth][gridheight];
static boolean first = true;
public static void change(){
for(int i = 0; i < gridwidth; i++){
for(int j = 0; j < gridheight; j++){
int neighbors = 0;
//check each cell
try{
if(pixels[i+1][j] == 1){neighbors++;}
if(pixels[i-1][j] == 1){neighbors++;}
if(pixels[i+1][j-1] == 1){neighbors++;}
if(pixels[i][j+1] == 1){neighbors++;}
if(pixels[i][j-1] == 1){neighbors++;}
if(pixels[i+1][j+1] == 1){neighbors++;}
if(pixels[i-1][j-1] == 1){neighbors++;}
if(pixels[i-1][j+1] == 1){neighbors++;}
}catch(ArrayIndexOutOfBoundsException e){
}
if(neighbors == 3 || neighbors == 2 ){
pixels[i][j] = 1;
}else if(neighbors < 2 || neighbors >= 4){
pixels[i][j] = 0;
}
}
}
}
public static void render(Graphics g){
for(int i = 0; i < gridwidth;i++){
for(int j = 0; j < gridheight; j++){
if(pixels[i][j] == 1){
g.setColor(Color.red);
g.fillRect(i*6, j*6, 6, 6);
}
}
}
}
}
thanks for all the help. Sadly it still isn't working correctly.Now it is doing the same thing but in diamond formation like so:
The main problem I do see here is the fact that you are updating values while discovering them.
You should cache your whole grid (or at least neighbours count) before updating, otherwise, while you update element at (x, y), you are alterating the neighbours count for element that are successive as (x+1,y), (x+1,y+1), (x,y+1) by counting the outcome of current iteration.
For example you could update a separate array called cachedPixels like so:
for(int i = 0; i < gridwidth; i++){
for(int j = 0; j < gridheight; j++){
int neighbors = 0;
// find the proper boundaries
int minI = Math.max(0, i - 1);
int maxI = Math.min(gridwidth, i + 2)
int minJ = Math.max(0, j - 1);
int maxJ = Math.min(gridheight, j + 2)
for (int i2 = minI; i2 < maxI; i2++) {
for (int j2 = minJ; j2 < maxJ; j2++) {
if (i2 != i || j2 != j) {
if (pixels[i2][j2] == 1) {
neighbors++;
}
}
}
}
if (neighbors == 2 || neighbors == 3) {
cachedPixels[i][j] = 1;
} else {
cachedPixels[i][j] = 0; // probably not even necessary as 0 is default value
}
}
}
Then after completing this process for the entire array set using the arraycopy function:
for (int i = 0; i < length; i++) {
System.arraycopy(cachedPixels[i], 0, pixels[i], 0, cachedPixels[i].length);
}
Simply setting pixels = cachedPixels would point "pixels" to the "cachedPixels" array and so changing one would change the other, and the system would collapse.
P.S. The rules you're using for your GoL are not the same as John H. Conway's. Cells always live on the next time step if they have 3 neighbours, and live on the next time step with 2 neighbours only if they are alive in this time step, else they die:
cachedPixels[i][j] = 0; // Default value - death.
if (neighbors == 3) {
cachedPixels[i][j] = 1;
} else if (thisCell == 1 && neighbors == 2) {
cachedPixels[i][j] = 1;
}
Your newborn pixels should not be counted as neighbours for this move, and your dying pixels should be counted. It can be achieved by having another values, say 2 and 3, for newborn and dying pixels. After processing the field, you commit its state by replacing a value for newborn with 1 and a value for dying with 0.
Related
So this question is really 2 questions in 1 relating to an assignment. I have to make a program that reads a map from a file containing land, water, sandbag, and house tiles into a 2d array and GUI displaying the map.
When a button is pressed the water tiles spread to all land and house tiles that are touching it (horizontal, vertical, and diagonal) and then displays a message that tells you if the house was flooded or not.
This has to be done via a recursive method, I have a program that works for the most part utilizing 8 if statements which I believe is incredibly inefficient and also doesn't really fit the assignment requirements. How could I make this recursive?
My second problem which I think is related to the way I approached the flooding question is that my method that checks if the house is still standing always displays the house was flooded message even if the house was standing.
I've been trying to fix this for days, any insight would be much appreciated. (I also previously had a problem where having for loops in my checkForHouse method would cause my program to freeze and have to be closed through task manager, it randomly stopped doing this even though I didn't change anything not sure if that helps).
public static void Flood(JLabel[][] labelArray, JPanel panel) {
//Timer that allows the GUI to update slowly so progression can be seen
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
boolean running = true;
while(running) {
//Checks through the entire array to find water tiles
for (int i = 0; i < labelArray.length; i++) {
for (int j = 0; j < labelArray[0].length; j++) {
if (labelArray[i][j].getText().equals("W")) {
if(i > 0 && (labelArray[i-1][j].getText().equals("L") || labelArray[i-1][j].getText().equals("H"))) {//Checks the tile left of the water tile
labelArray[i-1][j].setText("W");
labelArray[i-1][j].setBackground(Color.BLUE);
}
else if(j > 0 && (labelArray[i][j-1].getText().equals("L") ||labelArray[i][j-1].getText().equals("H"))) {//Checks the tile above the water tile
labelArray[i][j-1].setText("W");
labelArray[i][j-1].setBackground(Color.BLUE);
}
else if(i < labelArray.length-1 && (labelArray[i+1][j].getText().equals("L") || labelArray[i+1][j].getText().equals("H"))){//Checks the tile to the right of the water tile
labelArray[i+1][j].setText("W");
labelArray[i+1][j].setBackground(Color.BLUE);
}
else if(j < labelArray[0].length-1 && (labelArray[i][j+1].getText().equals("L") || labelArray[i][j+1].getText().equals("H"))) {//Checks the tile below the water tile
labelArray[i][j+1].setText("W");
labelArray[i][j+1].setBackground(Color.BLUE);
}
else if(j < labelArray[0].length-1 && i < labelArray.length-1 && (labelArray[i+1][j+1].getText().equals("L") || labelArray[i+1][j+1].getText().equals("H"))) {//Checks the tile to the bottom right of the water tile
labelArray[i+1][j+1].setText("W");
labelArray[i+1][j+1].setBackground(Color.BLUE);
}
else if(j > 0 && i > 0 && (labelArray[i-1][j-1].getText().equals("L") || labelArray[i-1][j-1].getText().equals("H"))) {//Checks the tile to the top left of the water tile
labelArray[i-1][j-1].setText("W");
labelArray[i-1][j-1].setBackground(Color.BLUE);
}
else if(j > 0 && i < labelArray.length-1 && (labelArray[i+1][j-1].getText().equals("L") || labelArray[i+1][j-1].getText().equals("H"))) {//Checks the tile to the top right of the water tile
labelArray[i+1][j-1].setText("W");
labelArray[i+1][j-1].setBackground(Color.BLUE);
}
else if(j < labelArray[0].length-1 && i > 0 && (labelArray[i-1][j+1].getText().equals("L") || labelArray[i-1][j+1].getText().equals("H"))) {//Checks the tile to the bottom left of the water tile
labelArray[i-1][j+1].setText("W");
labelArray[i-1][j+1].setBackground(Color.BLUE);
}
}
}
}
running = false;
checkForHouse(labelArray);
}
}
};
new Timer(250, taskPerformer).start();
}
public static void checkForHouse (JLabel[][] labelArray) {
for (int r =0; r<labelArray.length; r++) {
for (int c =0;c<labelArray[0].length; c++) {
if(labelArray[r][c].getText().equals("H")) {
textArea.setText(safeHouse);
}
else {
textArea.setText(floodedHouse);
}
}
}
}
Updated based on comment
water can flow all the way to boundary
water can flow to adjacent land or adjacent house and they become water
water can not flow to adjacent sandbag
based on point 3, water surrounded on all sides by sandbag will not flow
traverse recursively from every newly filled land or house
using 2 for loop, iterate over all the cells
for any cell that has 'W', call another function
the another function will recursively update any adjacent land or house
final int[] x = new int[] {-1, -1, -1, 0, 0, 1, 1, 1};
final int[] y = new int[] {-1, 0, 1, -1, 1, -1 0, 1};
final char HOUSE = 'H'
final char LAND = 'L';
final char WATER = 'W';
final char WATERED_HOUSE = 'A';
final char WATERED_LAND = 'B';
final char SANDBAG = 'S'; // not used and is not needed
void process(char[][] input) {
for (int row = 0; row < input.length; row++) {
for (int col = 0; col < input[row].length; col++) {
if (input[row][col] == WATER) {
processNeighbor(input, row, col);
}
}
}
// if a cell is WATERED_HOUSE, then update text (if not done in processNeighbor function)
for (int row = 0; row < input.length; row++) {
for (int col = 0; col < input[row].length; col++) {
if (input[row][col] == WATERED_HOUSE) {
// set text
}
// if needed for WATERED_HOUSE and WATERED_LAND,
// change back to WATER and LAND respectively
}
}
}
void processNeighbor(char[][] input, final int row, final int col) {
for (int adj = 0; adj < adjCount; adj++) {
final int hor = col + x[adj];
final int ver = row + y[adj];
// stop recursion if neighbor is a not any one of house or land.
if (!isValid(input, ver, hor) || !(input[ver][hor] == LAND || input[ver][hor] == HOUSE)) {
continue;
}
// modify state to help recursion to end
// if modification is not allowed, then maintain separate visited state
if (input[ver][hor] == HOUSE) {
input[ver][hor] = WATERED_HOUSE;
// note: can set flooded here or in a separate loop later
} else if (input[ver][hor] == LAND) {
input[ver][hor] = WATERED_LAND;
}
// process any unprocessed neighbor recursively
processNeighbor(input, ver, hor);
}
}
// check whether given cell is within grid
boolean isValid(char[][] input, final int row, final int col) {
return row >= 0 && row < input.length && col >= 0 && col < input[row].length;
}
Original
This following logic assumes that water will flow only to 1 adjacent tile. Is that a valid assumption?
separation of logic and bfs
Use additional labels. 'A' can represent watered house.
using additional label simplifies the code and avoid forward modifications of 'H' to 'W', which the subsequent iterations will consider as 'W' incorrectly (while its actually a 'H' with 'W' and not initially a 'W')
2 loops in another outer function which calls another function if 'W' is seen at the cell
the second function will run a for loop with directions and updates the adjancent locations if they are 'H'
finally run one more 2 loop and change all 'A' to 'W' and change color
you can add similar check for land also
// can make this an unmodifiable list to avoid accidental modification
// LU L LD U D RU R RD
final int[] x = new int[] {-1, -1, -1, 0, 0, 1, 1, 1};
final int[] y = new int[] {-1, 0, 1, -1, 1, -1 0, 1};
final int adjCount = 8;
final char WATER = 'W';
final char HOUSE = 'H'
final char WATERED_HOUSE = 'A';
int process(char[][] input) {
int count = 0; // this is just to track and for any optimization
// initial processing to process cells adjacent to water
for (int row = 0; row < input.length; row++) {
for (int col = 0; col < input[row].length; col++) {
if (WATER == input[row][col]) {
count += processAdjacents(input, row, col);
}
}
}
final int modified = count;
// second round to set watered house to water and color
for (int row = 0; row < input.length; row++) {
// if needed can use count == 0 to break;
for (int col = 0; col < input[row].length; col++) {
// if needed can use count == 0 to break;
if (WATERED_HOUSE == input[row][col]) {
input[row][col] = WATER;
// set background color also
count--;
}
}
}
return modified;
}
// process adjacent cells of water cell
int processAdjacents(char[][] input, final int row, final int col) {
int count = 0;
for (int adj = 0; adj < adjCount; adj++) {
final int hor = col + x[adj];
final int ver = row + y[adj];
if (isValid(input, ver, hor) && HOUSE == input[ver][hor]) {
input[ver][hor] = WATERED_HOUSE;
count++;
}
}
return count;
}
// check whether given cell is within grid
boolean isValid(char[][] input, final int row, final int col) {
return row >= 0 && row < input.length && col >= 0 && col < input[row].length;
}
Disclaimer
Untested code and typed in SO
Did not use DFS
land not handled
This code is for a game of life application.
My code promts a user for a text file the container rows and columns.
The file is read and entered into a 2D array.
The array is then pass to my nextGeneration method and this prints the generation.
I need to pass the future array back to the nextGeneration as many times a required per the users input.
I've been struggling all day on how to pass the nextGeneration the "future" array once it has gone through the first generation.
Any help would be greatly appreciated. Thanks.
static void nextGeneration(int grid[][], int M, int N, int NumberofGenerations)
{
int[][] future = new int[M][N];
// Loop through every cell
for (int l = 1; l < M - 1; l++)
{
for (int m = 1; m < N - 1; m++)
{
// finding no Of Neighbours that are alive
int aliveNeighbours = 0;
for (int i = -1; i <= 1; i++)
for (int j = -1; j <= 1; j++)
aliveNeighbours += grid[l + i][m + j];
// The cell needs to be subtracted from
// its neighbours as it was counted before
aliveNeighbours -= grid[l][m];
// Implementing the Rules of Life
// Cell is lonely and dies
if ((grid[l][m] == 1) && (aliveNeighbours < 2))
future[l][m] = 0;
// Cell dies due to over population
else if ((grid[l][m] == 1) && (aliveNeighbours > 3))
future[l][m] = 0;
// A new cell is born
else if ((grid[l][m] == 0) && (aliveNeighbours == 3))
future[l][m] = 1;
// Remains the same
else
future[l][m] = grid[l][m];
}
}
if (NumberofGenerations != 0)
{
return NumberofGenerations -1 * nextGeneration(future[l][m], 20, 20,NumberofGenerations -1);
// recursive call
else
return 1;
}
System.out.println("Next Generation");
for (int i = 0; i < M; i++)
{
for (int j = 0; j < N; j++)
{
if (future[i][j] == 0)
System.out.print(" ");
else
System.out.print("*");
}
System.out.println();
}
}
Generally speaking - Recursion is a good idea but not good for implementation, it can usually be achieved iteratively and takes much more time and memory than an iterative way. Therefore I will show you how to implement your algorithm iteratively.
Pseudo code:
1. Call nextGeneration
2. loop 1 to NumberofGenerations
2.1. init future
2.2. put next generation in future
2.3. print future
2.4. copy future to grid
Since your code creates the next generation regardless of what in future array, it will work.
You already have steps 1, 2.1, 2.2, 2.3, all you need to do is to remove your recursive call, add a loop (stage 2.) and copy future to grid (stage 2.4).
Copy code:
for (int i=0; i < grid.length; i++)
for (int j=0; j < grid[i].length; j++)
grid[i][j] = future[i][j];
I want to memory-efficient this (the game of life code of shiffman in the nature of code book). how can change the below code to have only two arrays and constantly swap them, writing the next set of states into whichever one isn’t the current array?
class GOL {
int w = 8;
int columns, rows;
int[][] board;
GOL() {
// Initialize rows, columns and set-up arrays
columns = width / w;
rows = height / w;
board = new int[columns][rows];
//next = new int[columns][rows];
// Call function to fill array with random values 0 or 1
init();
}
void init() {
for (int i = 1; i < columns - 1; i++) {
for (int j = 1; j < rows - 1; j++) {
board[i][j] = (int) random(2);
}
}
}
// The process of creating the new generation
void generate() {
int[][] next = new int[columns][rows];
// Loop through every spot in our 2D array and check spots neighbors
for (int x = 1; x < columns - 1; x++) {
for (int y = 1; y < rows - 1; y++) {
// Add up all the states in a 3x3 surrounding grid
int neighbors = 0;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
neighbors += board[x + i][y + j];
}
}
// A little trick to subtract the current cell's state since
// we added it in the above loop
neighbors -= board[x][y];
// Rules of Life
if ((board[x][y] == 1) && (neighbors < 2)) next[x][y] = 0;
else if ((board[x][y] == 1) && (neighbors > 3)) next[x][y] = 0;
else if ((board[x][y] == 0) && (neighbors == 3)) next[x][y] = 1;
else next[x][y] = board[x][y];
}
}
// Next is now our board
board = next;
}
// This is the easy part, just draw the cells, fill 255 for '1', fill 0 for '0'
void display() {
for (int i = 0; i < columns; i++) {
for (int j = 0; j < rows; j++) {
if ((board[i][j] == 1)) fill(0);
else fill(255);
stroke(0);
rect(i * w, j * w, w, w);
}
}
}
}
You might not like this, but the honest answer is: don't bother.
how can change the below code to have only two arrays and constantly swap them, writing the next set of states into whichever one isn’t the current array
This is already exactly what the code does.
The Game of Life requires two arrays. If you're coming up against real performance issues, then look for other areas of improvement. Focusing on the array is a red herring.
There's an old saying: premature optimization is the root of all evil. In other words, you shouldn't waste time trying to fix code before it's broken.
One obvious thing you might improve is: why are you using an int[] array instead of a boolean[] array? You only need to store two states: alive or dead, so using int values seems unnecessary. You'll save a little bit of memory if you switch to a boolean[] array, but again, you probably won't even notice the improvement.
I've created a 12x12 grid, and within this grid I have created 'infected' tiles, when one infected tile is surrounded by other infected tiles, the surrounded cell becomes a diseased tile. I was wondering if there was a nice way of checking adjacent, in bounds cells for their value?
public static void diseaseTiles() {
int i, j;
for(i = 0; i < 12; i++) {
for(j = 0; j < 12; j++) {
if(myGrid[i][j] == 'I'){
int left, right, up, down;
if(i == 0) {
left = 1;
}
if(i == 11) {
right = 1;
}
if(j == 0) {
up = 1;
}
if(j == 11) {
down = 1;
}
//this is where I've gotten stuck
//I was going to use the above int's to determine
//whether or not the adjacent tile in that direction
//should be checked (they're the border of the grid)
}
}
}
}
You can check if a cell is infected by taking advantage of short-circuited evaluation:
boolean leftInfected = (i!=0) && myGrid[i-1][j]=='I';
boolean rightInfected = (i!=11) && myGrid[i+1][j]=='I';
boolean topInfected = (j!=0) && myGrid[i][j-1]=='I';
boolean bottomInfected = (j!=11) && myGrid[i][j+1]=='I';
After that, you could check if all four are infected:
if (leftInfected && rightInfected && topInfected && bottomInfected) {
myGrid[i][j] = 'D';
}
You might consider padding your array with a boundary one cell deep around each edge, so your 12x12 would become a 14x14. Make each cell in the boundary infected. You can then loop over the interior cells (ie rows 1:12, cols 1:12) without having to check that the cell is at the edge all the time.
I believe a better option is to define a helper array that defines the valid neighbors and iterate using it. Advantage of my approach is that it makes it very easy to change which are the valid neighbors(say you want to have 8 neighbors allowing to go by diagonal instead of only 4):
int moves[][] = {{-1,0}, {0, -1}, {1, 0}, {0, 1} };
for (int i = 0; i < 12; i++) {
for (int j = 0; j < 12; j++) {
for (int l = 0; l < 4 /* moves.length */; ++l) {
int ti = i + move[l][0];
int tj = j + move[l][1];
if (ti <= 0 || ti >= 12 || tj < 0 || tj >= 12) {
continue;
}
// cell (ti, tj) is valid neighbor do something with it.
}
}
}
I've been lurking and found heaps of great information form here, however the last few days I have been stuck and haven't been able to find help with my issue so I thought id post.
I have some homework and I have to make the contents of my array drop down to the bottom row. If i rotate the grid the items should still drop down to the bottom row and if i eat an object from the bottom row, everything above it in that column should drop down too.
Any help is greatly appreciated.
Here is a demo video of what should happen:
http://youtu.be/CB07vN-C_-Y
This is what i have so far:
`public class Assignment
{
// This method should return a *new copy* of
// the 2D cell matrix, with entries rotated clockwise
// The original matrix should not be changed
public static int[][] rotateClockwise(int[][] cells)
{
int w = cells.length;
int h = cells[0].length;
int[][] matrix = new int[h][w];
for (int i = 0; i < h; ++i)
{
for (int j = 0; j < w; ++j)
{
matrix[i][j] = cells[j][h - i - 1];
}
}
return matrix;
}
// This method should return a *new copy* of
// the 2D cell matrix, with entries rotated anti-clockwise
// The original matrix should not be changed
public static int[][] rotateAnticlockwise(int[][] cells)
{
int w = cells.length;
int h = cells[0].length;
int[][] matrix = new int[h][w];
for (int i = 0; i < h; ++i)
{
for (int j = 0; j < w; ++j)
{
matrix[i][j] = cells[w - j - 1][i];
}
}
return matrix;
}
// This method should return a *new copy* of the array, except
// that if there is a 0 that has a non-zero in the preceding
// slot in the array, then those two entries should be swapped
// See ProgrammingProject.pdf for an example
// The original array should not be changed
public static int[] dropOne(int[] column)
{
return column; // this will compile but gives the wrong result
}
}`
I'd model a column as a Queue<Icon> col = new LinkedList<Icon>(); there's an outline here for Queue<Segment> and a complete example here for Queue<Bauble>. You can peek() at the head (bottom) of the queue; if it's empty, you remove() a block from the column and add() it to the tail (top).
Addendum: You might start with this example, drop the getGray(), change the layout to new GridLayout(0, 1). Then, instead of shuffle(list), you'd cycle the queue.
for(int i = 0; i < arrayWidth; i++) {
boolean reachedZero = false;
for( int j = 0; j < arrayHeight; j++) {
if(array[i][j] == 1 && reachedZero == true) {
while( j >=0 && array[i][j - 1] == 0) {
array[i][j-1] = array[i][j];
array[i][j] = 0;
j--;
reachedZero = false;
}
j--; // Maybe an error here, it's late
if( array[i][j] == 0) {
reachedZero = true;
}
}
}
This was posted by a lovely redditor (RankWeis) from the /learnprogramming sub-reddit.
http://www.reddit.com/r/learnprogramming/comments/126597/java_help_needed_on_adding_a_gravity_effect_to/