Hallo,

damit dieses Subforum auch mal genutzt wird, hier eine Aufgabe: Es soll Conway's Game of Life implementiert werden.
Dazu ersteinmal grundlegende Informationen aus dem Wikipedia-Artikel:

Das Spiel des Lebens von John Horton Conway ist ein System, das auf einem zweidimensionalen zelluären Automaten basiert.
Das Spielfeld ist in Spalten und Zeilen unterteilt. Jedes Quadrat ist eine Zelle, die zwei Zustände haben kann. Entweder lebt sie, oder sie ist tot. Jede Zelle hat acht Nachbarn (oben, unten, links, rechts und die diagonal angrenzenden Zellen) und diese muss jede Zelle auch kennen, denn die Folgegeneration wird unter Beachtung nachfolgender Regeln erzeugt:
  • Eine tote Zelle mit genau drei lebenden Nachbarn wird in der Folgegeneration leben
  • Lebende Zellen mit weniger als zwei lebenden Nachbarn sterben in der nächsten Generation
  • Lebende Zellen mit zwei oder drei lebenden Nachbarn bleiben auch in der Folgegeneration am leben
  • Lebende Zellen mtt mehr als drei Nachbarn sterben in der Folgegeneration


Aufgaben

Anfänger: Schreibe eine Klasse für das Spiel des Lebens. Als eine kleine Orientierung sei ein unvollständiges Interface gegeben, das es zu implementieren gilt. Außerdem soll eine beliebige Anfangsgeneration manuell erstellt werden und die ersten 5 Generationen auf der Konsole ausgegeben werden.
Code:
public interface GameOfLifeModel {
    // Anzahl Zeilen des Spielfeldes
    public static final int ROWS = 60;
    
    // Anzahl Spalten des Spielfeldes
    public static final int COLS = 80;
    
    // Kantenlänge eines Quadrates (Zelle)
    public static final int CELL_SIZE = 10;
    
    // Berechnet die Folgegeneration und speichert diese.
    public void nextGeneration();
    
    // Gibt die aktuelle Generation zurück.
    // das zweidimensionale boolean-Array ist eine mögliche Form
    // der internen Repräsentation.
    public boolean[][] getGeneration();
}
Fortgeschritten:
  • Je größer das Spielfeld, desto besser. Es gibt eine Möglichkeit, ein unendliches Spielfeld zu simulieren, in dem man es torus-förmig macht.
  • Erstelle ein GUI, das folgende Funktionalitäten besitzt:
    • Der Benutzer kann die Generation "zeichnen"
    • Es gibt Buttons für die Berechnung und Anzeige der Folgegeneration und zum Zurücksetzen (alle Zellen sind tot)
    • Optional: der Benutzer kann eine Animation starten und die Geschwindigkeit einstellen. Die "Animation" soll dann automatisch die Folgegeneration berechnen und anzeigen, bis der Benutzer die Animation stoppt.



Wer auch die fortgeschrittenen Aufgaben löst, muss natürlich die Lösung der Konsolenausgabe nicht posten.

Meine Lösung:
Spoiler:Java7/JavaFx

Spoiler:GameOfLifeMain.java

Code:
package gameoflife;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class GameOfLifeMain extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("UI_GameOfLife.fxml"));
        Scene scene = new Scene(root);
        stage.setTitle("Conway's Game of Life");
        stage.setScene(scene);
        stage.setResizable(false);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Spoiler:UI_GameOfLife.fxml

Code:
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.canvas.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.shape.*?>
<?import javafx.scene.text.*?>

<AnchorPane id="AnchorPane" minHeight="634.0" prefHeight="660.0" prefWidth="820.0" style="-fx-background-color: #f3f3f3;" xmlns:fx="http://javafx.com/fxml" fx:controller="gameoflife.UI_GameOfLifeController">
  <children>
    <HBox id="HBox" alignment="CENTER" layoutX="10.0" layoutY="10.0" spacing="20.0">
      <children>
        <HBox id="HBox" alignment="CENTER" spacing="10.0">
          <children>
            <Button id="nextGenerationButton" mnemonicParsing="false" onAction="#nextGenerationButtonFired" text="Nächste Generation" />
            <Button mnemonicParsing="false" onAction="#resetButtonFired" text="Reset" />
          </children>
        </HBox>
        <HBox id="HBox" alignment="CENTER" spacing="5.0">
          <children>
            <Label text="Generation:" />
            <Text fx:id="generationCount" strokeType="OUTSIDE" strokeWidth="0.0" text="0" />
          </children>
        </HBox>
      </children>
    </HBox>
    <HBox id="HBox" alignment="CENTER" layoutX="438.0" spacing="15.0" AnchorPane.topAnchor="10.0">
      <children>
        <HBox id="HBox" alignment="CENTER" spacing="5.0">
          <children>
            <Button mnemonicParsing="false" onAction="#startButtonFired" text="Start" />
            <Button mnemonicParsing="false" onAction="#stopButtonFired" text="Stop" />
          </children>
        </HBox>
        <HBox id="HBox" alignment="CENTER" spacing="5.0">
          <children>
            <Label text="Speed:" />
            <Slider fx:id="slider" majorTickUnit="10.0" min="1.0" minorTickCount="1" />
          </children>
        </HBox>
      </children>
    </HBox>
    <BorderPane id="generationPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" style="
" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0">
      <center>
        <Canvas fx:id="canvas" height="600.0" onMouseDragged="#mouseControl" onMousePressed="#mouseControl" width="800.0" />
      </center>
    </BorderPane>
  </children>
</AnchorPane>

Spoiler:UI_GameOfLifeController.java

Code:
package gameoflife;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.animation.Animation;
import javafx.animation.Transition;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Slider;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.util.Duration;

public class UI_GameOfLifeController implements Initializable {
    private GameOfLifeModel model;
    private Animation timer;
    
    @FXML private Canvas canvas;
    @FXML private Text generationCount;
    @FXML private Slider slider;

    @FXML
    private void nextGenerationButtonFired(ActionEvent ae) {
        timer.stop();
        model.nextGeneration();
        drawGeneration();
    }

    @FXML
    private void startButtonFired(ActionEvent ae) {
        timer.play();
    }

    @FXML
    private void stopButtonFired(ActionEvent ae) {
        timer.stop();
    }

    @FXML
    private void resetButtonFired(ActionEvent ae) {
        timer.stop();
        model.initDefaultGeneration();
        drawGeneration();
    }

    @FXML
    private void mouseControl(MouseEvent me) {
        int x = (int)me.getX();
        int y = (int)me.getY();
        if (canvas.contains(x, y)) {
            int xc = x / GameOfLifeModel.CELL_SIZE;
            int yc = y / GameOfLifeModel.CELL_SIZE;
            if (me.isSecondaryButtonDown()) {
                model.setCell(yc, xc, false);
            } else if (me.isPrimaryButtonDown()) {
                model.setCell(yc, xc, true);
            } 
            drawGeneration();
        }
    }

    private void drawGeneration() {
        final GraphicsContext gc = canvas.getGraphicsContext2D();
        final int ROWS = GameOfLifeModel.ROWS;
        final int COLS = GameOfLifeModel.COLS;
        final int CELL_SIZE = GameOfLifeModel.CELL_SIZE;
        final boolean[][] generation = model.getGeneration();
        gc.setFill(Color.GRAY);
        gc.setStroke(Color.BLACK);
        gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
        gc.setFill(Color.WHITE);
        int x = 0, y = 0;
        for (int row = 0; row < ROWS; ++row) {
            for (int col = 0; col < COLS; ++col) {
                if (generation[row][col]) {
                    gc.fillRect(x, y, CELL_SIZE, CELL_SIZE);
                }
                x += CELL_SIZE;
            }
            x = 0;
            y += CELL_SIZE;
        }
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        model = new GameOfLifeModel();
        final IntegerProperty speed = new SimpleIntegerProperty(1);
        slider.valueProperty().bindBidirectional(speed);
        generationCount.textProperty().bind(model.generationCountProperty().asString());
        timer = new Transition() {
            {
                setCycleDuration(Duration.millis(500));
                setCycleCount(Animation.INDEFINITE);
            }
            @Override
            protected void interpolate(double d) {
                model.nextGeneration();
                drawGeneration();
                setRate(speed.get());
            }
        };
        drawGeneration();
    }
}

Spoiler:GameOfLifeModel.java

Code:
package gameoflife;

import java.util.Arrays;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;

public class GameOfLifeModel {
    public static final int CELL_SIZE = 10;  
    public static final int ROWS = 60;
    public static final int COLS = 80;
    
    private boolean[][] generation;
    private IntegerProperty generationCount;
    
    public GameOfLifeModel() {
        generation = new boolean[ROWS][COLS];
        generationCount = new SimpleIntegerProperty(0);
        initDefaultGeneration();
    }

    public final void initDefaultGeneration() {
        for (int row = 0; row < ROWS; ++row) {
            for (int col = 0; col < COLS; ++col) {
                generation[row][col] = false;
            }
        }
        generationCount.set(0);
    }

    public void nextGeneration() {
        boolean[][] newGen = new boolean[ROWS][COLS];
        for (int row = 0; row < ROWS; ++row) {
            for (int col = 0; col < COLS; ++col) {
                int neighbours = countNeighbours(row, col);
                if (!getCell(row, col)) {
                    if (neighbours == 3) {
                        setCell(newGen, row, col, true);
                    }
                } else {
                    if (neighbours < 2) {
                        setCell(newGen, row, col, false);
                    } else if (neighbours <= 3) {
                        setCell(newGen, row, col, true);
                    } else {
                        setCell(newGen, row, col, false);
                    }
                }
            }
        }
        if (!Arrays.equals(newGen, generation)) {
            generation = newGen;
            generationCount.set(generationCount.get() + 1);
        }
    }
    
    public boolean[][] getGeneration() {
        return generation;
    }
    
    public IntegerProperty generationCountProperty() {
        return generationCount;
    }
    
    public void setCell(int row, int col, boolean alive) {
        setCell(generation, row, col, alive);
    }

    private int countNeighbours(int row, int col) {
        int counter = 0;
        for (int i = -1; i <= 1; i++) {
            for (int k = -1; k <= 1; k++) {
                if (getCell(row + i, col + k)) {
                    counter++;
                }
            }
        }
        if (getCell(row, col)) {
            counter--;
        }
        return counter;
    }

    private boolean getCell(int row, int col) {
        row %= ROWS;
        col %= COLS;
        row += row < 0 ? ROWS : 0;
        col += col < 0 ? COLS : 0;
        return generation[row][col];
    }

    private void setCell(boolean[][] newGen, int row, int col, boolean alive) {
        row %= ROWS;
        col %= COLS;
        row += row < 0 ? ROWS : 0;
        col += col < 0 ? COLS : 0;
        newGen[row][col] = alive;
    }
}


Spoiler:Download (JAR)

Mit der linken Maustaste lebende Zellen zeichnen. Mit der rechten Maustaste töten.
GameOfLife.jar