Conways Game of Life

Hi

ich habe gestern zum Spaß versucht “Conways Game of Life” (23/3) zu schreiben.

Bei Conways game of Life gibt es viele Zellen. Eine leere Zelle wird “belebt” wenn 3 angrenzende Zellen belebt sind.
Eine lebende Zelle bleibt am Leben wenn sie 2 oder 3 lebende Nachbarn hat.

Funktioniert so weit eigentlich auch ganz gut, aber irgendwas scheint mit meinen Tod/GeburtsBedingungen nicht zu stimmen, denn keine der oszillierende Objekte funktioniert bei mir und die Ergebnisse sehen immer anders aus als sie eigentlich sollten.

Hier der Code der Klasse in der die neue Generation berechnet wird:

Level.java
[spoiler]```package cmdmole.game.life;

public class Level {

private Zelle[][] zellen;
 private Zelle[][] zellenTmp;

private int width;
private int height;



public Level(int width, int height)
{
	zellen = new Zelle[width][height];
	zellenTmp = new Zelle[width][height];
	
	
	
	int chance = (width*height) / 400;
	
	for(int x = 0; x < width; x++)
	{
		for(int y = 0; y < height; y++)
		{
			/*if(Component.rand.nextInt(chance) == 0)
				zellen[x][y] = new Zelle(true);
			else*/
				zellen[x][y] = new Zelle(false);
		}
	}
	
	
	
	zellen[89][90] = new Zelle(true); // Bilnker Figur, funktioniert nicht
	zellen[90][90] = new Zelle(true);
	zellen[91][90] = new Zelle(true);
	
	/*zellen[90][90] = new Zelle(true); // Figur aus Wikipedia, sieht auch anders aus als erwartet
	zellen[90][91] = new Zelle(true);
	zellen[89][90] = new Zelle(true);
	zellen[90][89] = new Zelle(true);
	zellen[91][89] = new Zelle(true);*/
	
	
	
	
	
	this.width = width;
	this.height = height;
}

public void tick()
{
	
	zellenTmp = zellen;
	
	
	
	for(int x = 0; x < height; x++)
	{
		for(int y = 0; y < width; y++)
		{
			if(zellenTmp[x][y] == null)
			{
				throw new NullPointerException();
				
			}
			else
			{
				
				if(zellenTmp[x][y].isAlive())
				{
					
					if(!this.canSurvive(x, y))
					{
						zellen[x][y].kill();
						break;
					}
					
				} else {
					
					if(this.canBeRevived(x, y))
					{
						zellen[x][y].revive();
					}
					
				}
				
			}
			
			
			
		}
	}
	
	
	//zellenTmp = null;
}


private boolean canSurvive(int x, int y)
{
	int n = getNeighbourCount(x, y);
	if(n == 2 || n == 3) return true;
	else return false;
}

private boolean canBeRevived(int x, int y)
{
	if(getNeighbourCount(x, y) == 3) return true;
	else return false;
}

// hier liegt vermutlich der Fehler. Habe aber schon versucht die Methode ganz neu zu schreiben, ist aber immmer noch genauso wie vorher.
// das auskommentierte ist der alte Code
private int getNeighbourCount(int x, int y)
{
int counter = 0;

	int startX = (x <= 0 ? x : x-1);
	int startY = (y <= 0 ? y : y-1);
	
	int endX = (x >= 179 ? x : x+1);
	int endY = (y >= 179 ? y : y+1);
	
	for(int xO = startX; xO <= endX; xO++)
	{
		for(int yO = startY; yO <= endY; yO++)
		{
			if(zellenTmp[xO][yO].isAlive())
			{
				counter++;
			}
		}
	}
	
	if(zellenTmp[x][y].isAlive()){
		counter --;
	} 
	
	
	
	/**
	for(int xO = (x > 0 ? (x >= 179 ? 2 : -1) : 1); xO <= 1; xO+=2)
	{
		for(int yO = (y > 0 ? (y >= 179 ? 2 : -1) : 1); yO <= 1; yO+=2)
		{
			//System.out.println("xO :  " + xO + " ,  y: " + yO);
			if(zellenTmp[x+xO][y+yO].isAlive()){
				counter++;
			}
		}
	}
	
	for(int xO = (x > 0 ? (x >= 179 ? 2 : -1) : 1); xO <= 1; xO+=2)
	{
		if(zellenTmp[x+xO][y].isAlive()){
			counter++;
		}
	}
	
	for(int yO = (y > 0 ? (y >= 179 ? 2 : -1) : 1); yO <= 1; yO+=2)
	{
		if(zellenTmp[x][y+yO].isAlive()){
			counter++;
		}
	}*/
	
	
	
	return counter;
}

public Zelle[][] getZellen()
{
	return zellen;
}

}




Und auch noch die "Zelle" Klasse

Zelle.java
[spoiler]```package cmdmole.game.life;

public class Zelle {
	
	private boolean living;
	
	public Zelle(boolean al)
	{
		living = al;
	}
	
	
	public boolean isAlive()
	{
		return living;
	}
	
	public void kill()
	{
		living = false;
	}
	
	public void revive()
	{
		living = true;
	}

}


```[/spoiler]



Mfg
Oneric

Wenn ich das richtig lese, ist klar, warum es nicht funktioniert: Du musst erst einmal über das ganze Feld gehen und die neuen Zustände ermitteln und merken, und dann in einem zweiten Schritt die neuen Werte setzen. Wenn du gleich nach dem Berechnen den Wert auch setzt, änderst du damit ja die Nachbarschafts-Bedingungen für die Zellen, die noch nicht bearbeitet wurden.

Hatte mal das gleiche Problem, wurde hier gelöst:
http://forum.byte-welt.net/threads/10074-Game-of-Life-Strukturen-funktionieren-nicht

Apropos: http://www.youtube.com/watch?v=C2vgICfQawE

Nur mal kurz reingesehen:

Du machst in Level.java:64

   {
      throw new NullPointerException();             
   } else
   {
      if(zellenTmp[x][y].isAlive())
         {
            [...]
         } else {
            [...]
         }```

Wieso überprüfst du auf null und wirfst dann eine NPE? Das wäre doch genau dasselbe Verhalten, als wenn du gar nicht überprüfen würdest.

Ok,
Ich habe das jetzt ein wenig geändert so dass eigentlich “zellen” nicht mehr geändert werden sollte. Das Ergebnis sieht tatsächlich anders aus, aber immer noch nicht so wie es sein sollte :frowning:

Hier ist die neue Level Klasse:


package cmdmole.game.life;

public class Level {
	
	private Zelle[][] zellen;
	
	
	private int width;
	private int height;
	
	
	
	public Level(int width, int height)
	{
		zellen = new Zelle[width][height];
		//zellenTmp = new Zelle[width][height];
		
		
		
		int chance = (width*height) / 400;
		
		for(int x = 0; x < width; x++)
		{
			for(int y = 0; y < height; y++)
			{
				/*if(Component.rand.nextInt(chance) == 0)
					zellen[x][y] = new Zelle(true);
				else*/
					zellen[x][y] = new Zelle(false);
			}
		}
		
		/**zellen[89][90] = new Zelle(true);
		zellen[90][90] = new Zelle(true);
		zellen[90][89] = new Zelle(true);
		zellen[89][89] = new Zelle(true);
		
		zellen[89][88] = new Zelle(true);
		zellen[90][91] = new Zelle(true);*/
		
		zellen[89][90] = new Zelle(true);
		zellen[90][90] = new Zelle(true);
		zellen[91][90] = new Zelle(true);
		
		/*zellen[90][90] = new Zelle(true);
		zellen[90][91] = new Zelle(true);
		zellen[89][90] = new Zelle(true);
		zellen[90][89] = new Zelle(true);
		zellen[91][89] = new Zelle(true);*/
		
		/**zellen[80][93] = new Zelle(true);
		zellen[81][93] = new Zelle(true);
		zellen[80][92] = new Zelle(true);
		zellen[81][92] = new Zelle(true);
		
		zellen[98][93] = new Zelle(true);
		zellen[97][93] = new Zelle(true);
		zellen[98][92] = new Zelle(true);
		zellen[97][92] = new Zelle(true);*/
		
		
		
		this.width = width;
		this.height = height;
	}
	
	public void tick()
	{
		 Zelle[][] zellenTmp;
		
		zellenTmp = new Zelle[width][height];
		
		for(int x = 0; x < height; x++)
		{
			for(int y = 0; y < width; y++)
			{
				zellenTmp[x][y] = new Zelle(zellen[x][y].isAlive());
			}
		}
		
		
		for(int x = 0; x < height; x++)
		{
			for(int y = 0; y < width; y++)
			{
				if(zellen[x][y] == null)
				{
					System.err.println("Null-PointerException");
					
				}
				else
				{
					
					if(zellen[x][y].isAlive())
					{
						
						if(!this.canSurvive(x, y))
						{
							zellenTmp[x][y].kill();
							break;
						}
						
					} else {
						
						if(this.canBeRevived(x, y))
						{
							zellenTmp[x][y].revive();
						}
						
					}
					
				}
				
				
				
			}
		}
		
		for(int x = 0; x < height; x++)
		{
			for(int y = 0; y < width; y++)
			{
				zellen[x][y] = new Zelle(zellenTmp[x][y].isAlive());
			}
		}
		
		zellenTmp = null;
	}
	
	
	
	
	private boolean canSurvive(int x, int y)
	{
		int n = getNeighbourCount(x, y);
		if(n == 2 || n == 3) return true;
		else return false;
	}
	
	private boolean canBeRevived(int x, int y)
	{
		if(getNeighbourCount(x, y) == 3) return true;
		else return false;
	}
	
	private int getNeighbourCount(int x, int y)
	{
		int counter = 0;
		
		int startX = (x <= 0 ? x : x-1);
		int startY = (y <= 0 ? y : y-1);
		
		int endX = (x >= 179 ? x : x+1);
		int endY = (y >= 179 ? y : y+1);
		
		for(int xO = startX; xO <= endX; xO++)
		{
			for(int yO = startY; yO <= endY; yO++)
			{
				if(zellen[xO][yO].isAlive())
				{
					counter++;
				}
			}
		}
		
		if(zellen[x][y].isAlive()){
			counter --;
		} 
		
		
		/**try{
			if(zellen[x-1][y-1].isAlive()) counter++;
		if(zellen[x][y-1].isAlive()) counter++;
		if(zellen[x+1][y-1].isAlive()) counter++;
		
		if(zellen[x-1][y].isAlive()) counter++;
		if(zellen[x+1][y].isAlive()) counter++;
		
		if(zellen[x-1][y+1].isAlive()) counter++;
		if(zellen[x][y+1].isAlive()) counter++;
		if(zellen[x+1][y+1].isAlive()) counter++;
		} catch(ArrayIndexOutOfBoundsException ex){
			System.err.println("Exception: " + ex.toString());
		}*/
		
		
		
		return counter;
	}
	
	public Zelle[][] getZellen()
	{
		return zellen;
	}

}


Und hier das Ergebnis wenn ich eine “Blinker” Figur erstelle. Eigentlich sollte sich die ersten beiden Bilder immer wieder wiederholen, aber bei mir ändert es sich in der 3 Generation.

Was ist jetzt immer noch falsch ?

[QUOTE=Shadoka]Nur mal kurz reingesehen:
Wieso überprüfst du auf null und wirfst dann eine NPE? Das wäre doch genau dasselbe Verhalten, als wenn du gar nicht überprüfen würdest.[/QUOTE]

Das war nur vorübergehend für Testzwecke.

Mfg
Oneric

Also ich finde ja:
dann klappt’s hervorragend
[spoiler]```import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

public class ConwaysGameOfLife {
static class Cell {
private final Collection neighbours = new HashSet<>();
private boolean isAlive;
private boolean willLive;

    public void willLive() {
        int livingNeigbous = 0;
        for (Cell neighbour : neighbours) {
            livingNeigbous += neighbour.isAlive ? 1 : 0;
        }
        if (isAlive) {
            willLive = 2 == livingNeigbous
                    || 3 == livingNeigbous;
        } else {
            willLive = 3 == livingNeigbous;
        }
    }

    public void nextGeneration() {
        isAlive = willLive;
    }

    public void addNeighbour(Cell neigbour) {
        neighbours.add(neigbour);
    }

    @Override
    public String toString() {
        return isAlive ? "*" : ".";
    }
}

private List<Cell> cells = new ArrayList<>();
private final int fieldEdgeSize;

public ConwaysGameOfLife(int filedEdgeSize) {
    this.fieldEdgeSize = filedEdgeSize;
    Cell[][] field = new Cell[filedEdgeSize][filedEdgeSize];
    for (int i = 0; i < field.length; i++) {
        for (int j = 0; j < field.length; j++) {
            Cell cell = new Cell();
            cells.add(cell);
            field**[j] = cell;
        }
    }
    for (int i = 0; i < field.length; i++) {
        for (int j = 0; j < field.length; j++) {
            field**[j].addNeighbour(field[previous(i)][previous(j)]);
            field**[j].addNeighbour(field**[previous(j)]);
            field**[j].addNeighbour(field[next(i)][previous(j)]);
            field**[j].addNeighbour(field[previous(i)][j]);
            field**[j].addNeighbour(field[next(i)][j]);
            field**[j].addNeighbour(field[previous(i)][next(j)]);
            field**[j].addNeighbour(field**[next(j)]);
            field**[j].addNeighbour(field[next(i)][next(j)]);
        }
    }
}

public void play() {
    //@formatter:off 
    initCells(new int[][] {{0,5},{1,6},{2,4},{2,5},{2,6}});// gleiter
    initCells(new int[][] {{2,12},{2,13},{2,14}});// Blinker
    //@formatter:on
    run();
}

private int next(int ordinate) {
    return ordinate + 1 < fieldEdgeSize ? ordinate + 1 : 0;
}

private void init(int position) {
    cells.get(position).isAlive = true;
}

private int previous(int ordinate) {
    return ordinate > 0 ? ordinate - 1 : fieldEdgeSize - 1;
}

public static void main(String[] args) {
    new ConwaysGameOfLife(30).play();
}

private void initCells(int[][] activeCells) {
    for (int[] coordinate : activeCells) {
        init(coordinate[0]
                * fieldEdgeSize
                + coordinate[1]);
    }
}

private void run() {
    int generation = 0;
    while (10000 > generation++) {
        createNextGeneration();
        printBoard(generation);
        slowGame();
    }
}

private void createNextGeneration() {
    for (Cell cell : cells) {
        cell.willLive();
    }
    for (Cell cell : cells) {
        cell.nextGeneration();
    }
}

private void printBoard(int generation) {
    for (Cell cell : cells) {
        if (0 == cells.indexOf(cell)
                % fieldEdgeSize) {
            System.out.println();
        }
        System.out.print(cell);
    }
    System.out.println("

generation "
+ generation
+ " ===================================================================================");
}

private void slowGame() {
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

}```[/spoiler]

bye
TT