Game of Life GUI

Hallo zusammen:)

Folgendes Problem:

Habe erfolgreich Conway’s Game of Life programmiert.
Nächster Schritt soll die Verarbeitung in GUI sein, soweit so gut.
Habe bereits ein Frame erstellt und “Startbutton” sowie Header hinzugefügt.
Jetzt gehts daran die Consolenausgabe in den Frame zu überführen, an der Stelle komme ich ins stocken.

Life.java


import java.util.Random;

public class Life {

	public static int Zeilen = 10;
	public static int Spalten = 10;
	public static int spielmarken = 25;

	public static char spielfeld[][] = new char[Zeilen][Spalten];

	public Life() {
		for (int i = 0; i < Zeilen; i++) {
			for (int j = 0; j < Spalten; j++) {
				spielfeld**[j] = '0';

			}

		}

	}

	// public void anfangspopulation(Random zufall) {
	// for (int i = 0; i < zufall.length; i++) {
	// spielfeld[zufall**[0]][zufall**[1]] = '1';
	//
	// }
	// }

	public void zufallBakterien() {
		Random zufall = new Random();

		for (int i = 0; i <= spielmarken; i++) {
			int z1 = zufall.nextInt(Zeilen);
			int z2 = zufall.nextInt(Spalten);
			spielfeld[z1][z2] = '1';
		}
	}

	public void druckeSpielfeld() {
		System.out.println();
		System.out.println();
		for (int i = 0; i < Zeilen; i++) {
			for (int j = 0; j < Spalten; j++) {
				System.out.print(" " + spielfeld**[j]);
				// System.out.println('
');
				//
			}
			System.out.println();
		}
		System.out.println();
	}

	public void nextGeneration() {
		char newWorld[][] = new char[Zeilen][Spalten];
		for (int i = 0; i < Zeilen; i++) {
			for (int j = 0; j < Spalten; j++) {
				switch (nachbarn(i, j)) {
				case 3:
					if (dead(i, j))
					{
						newWorld**[j] = '1';
					} else if (alive(i, j))
					  {
						newWorld**[j] = '1';
					  }
					break;

				case 2:
					if (alive(i, j)) 
					{
						newWorld**[j] = '1';
						
					} else if (dead(i, j))
					  {
						newWorld**[j] = '0';
					  }
					break;

				default:
					newWorld**[j] = '0';
					break;
				}

			}
		}
		for (int i = 0; i < Zeilen; i++) {
			for (int j = 0; j < Spalten; j++) {
				spielfeld**[j] = newWorld**[j];

			}
		}
	}

	public boolean dead(int i, int j) {
		if (spielfeld**[j] == '0') {
			return true;
		} else
			return false;
	}

	public boolean alive(int i, int j) {
		if (spielfeld**[j] == '1') {
			return true;
		} else
			return false;
	}

	public int border(int i) {
		if (i <= 0) {
			return 0;
		} else if (i >= Zeilen) {
			return (Zeilen - 1);
		} else
			return i;

	}

	private int nachbarn(int i, int j) {
		int iNachbarn = 0;
		int iZeilenstart = border(i - 1);
		int iSpaltenstart = border(j - 1);

		while (iZeilenstart <= border(i + 1)) {
			while (iSpaltenstart <= border(j + 1)) {
				if (alive(iZeilenstart, iSpaltenstart)
						&& !((iZeilenstart == i) && (iSpaltenstart == j))) {
					iNachbarn++;
				}
				iSpaltenstart++;
			}
			iZeilenstart++;
			iSpaltenstart = border(j - 1);
		}

		return iNachbarn;
	}

}

LifeAusführen.java



public class LifeAusführen  {

	public static void druckeNextGeneration(Life l, int n) {
		while (n > 0) {
			l.druckeSpielfeld();
			l.nextGeneration();
			n--;
		}
	}
	public static void spiele(){
		Life life = new Life();
		life.zufallBakterien();
		druckeNextGeneration(life, 10);
}
	public static void main(String[] args) {

		new Fensterausgabe();
		

	}
}

Fensterausgabe.java

package Game;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;

@SuppressWarnings("serial")
public class Fensterausgabe extends JFrame {

	private final JLabel m_oHeading;
	private final Box m_oMainbox;
	private final Life m_oLifeBox;
	private final JButton m_oStart;
	

	public Fensterausgabe() {
		super();
		m_oMainbox = new Box(BoxLayout.Y_AXIS);
		m_oHeading = new JLabel("Game of Life");
		m_oStart = new JButton("Start");
		m_oLifeBox = new Life();

		createFrame();

	}

	protected void createFrame() {
		setSize(800, 600);
		setLocation(100, 100);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		getMainbox().add(Box.createRigidArea(new Dimension(0, 25)));
		getMainbox().add(getHeading());
		getHeading().setAlignmentX(CENTER_ALIGNMENT);
		getMainbox().add(getLifeBox());                                  //The method add(Component) in the type Container is not applicable for the arguments 
		getMainbox().add(getStart());
		getStart().addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				LifeAusführen.spiele();
			}
		});
		getStart().setAlignmentY(BOTTOM_ALIGNMENT);
		getStart().setAlignmentX(CENTER_ALIGNMENT);

		this.add(getMainbox());
		setVisible(true);

	}

	public JLabel getHeading() {
		return m_oHeading;
	}

	public Box getMainbox() {
		return m_oMainbox;
	}

	public JButton getStart() {
		return m_oStart;
	}

	public Life getLifeBox() {
		return m_oLifeBox;
	}
}

Wäre sehr nice wenn Ihr mir eine kleine Hilfestellung geben könntet:)
FaJo

Swing bzw. GUI Programmierung allgemein ist schon etwas komplexer.
Klar kann man nicht einfach eine beliebige Klasse auf einer GUI darstellen. Wie soll das funktionieren? Swing verwendet hierfür “fertige” Komponenten.
Wie soll das aussehen? Soll der Text einfach nur in die GUI geschrieben werden statt in die Konsole? Hierfür könnte man eine JTextArea nehmen und bei der Ausgabe statt auf die Konsole auf die JTextArea schreiben.

Ja gut, funktioniert nicht hab ich ja auch schon festgestellt :smiley:
Fürs erste reicht mir die reine Textausgabe, danach werde ich dann nach Erweiterungn schauen.
JTextArea alles kla, vielen Dank fürs erste:)
Jetzt kanns vorerst weitergehen…
Bin für weitere Anregungen die mich nach vorne bringen offen :slight_smile:

Falls eine JTextArea reicht, könntest du den OutputStream umleiten ([japi]System#setOut(java.io.PrintStream)[/japi]) mithilfe von [japi]PipedOutputStream[/japi] und [japi]PipedInputStream[/japi]. Schöner wäre es, wenn du dir vorher MVC angeschaut hättest. Dann hättest du jetzt ein Datenmodel das du in einer eigenen Komponente zeichnen könntest.

Generell solltest du dir aber auch mal Codeconventions anschauen. Dein Code schreit förmlich danach. Hier mal ein paar Sachen die mir beim überfliegen aufgefallen sind:
[ul]
[li] Felder/Variablen lowerCamelCase[/li][li] Diese _ verwendet man nur in Konstanten die dann UPPERCASE geschrieben werden. In Namen von Variablen haben die nichts verloren[/li][li] DEnglisch vermeiden[/li][li] Umlaute im Code sollte man vermeiden.[/li][/ul]

Vielen Dank für die Anregungen:)
Codeconventionen sind mir noch weitgehend unbekannt, werde aber daran Arbeiten.
Bin grade erst am Anfang meiner Coder-Karriere, habe dementsprechend noch einiges zu lernen…

[quote=FaJo]Codeconventionen sind mir noch weitgehend unbekannt, werde aber daran Arbeiten.
Bin grade erst am Anfang meiner Coder-Karriere, habe dementsprechend noch einiges zu lernen…[/quote]

Es empfielt sich solche Sachen zu beginn an gleich mitzulernen. Denn wie so oft: das falsche gewöhnt man sich leicht an, aber nur schwer wieder ab.

Ein Model (Ansatz) das man ausbauen könnte, wäre mit der Klasse Life ja bereits gegeben.

Da Du vermutlich dem Wachstum Deiner Bakterienkultur zuschauen willst, musst du Dich noch mit Thread, SwingWorker, TimerTask o.ä. beschäftigen. Denn eine GUI verhält sich da etwas anders als die Textausgabe auf einer Konsole.

PS. Das static bei den Klassen Variablen würde ich noch entfernen. static möglichst versuchen zu vermeiden.

da du wahrscheinlich sowieso irgendwann mit bildern arbeiten wollen wirst,
empfiehlt es sich zum beispiel ein gridlayout mit jpanels anzulegen oder eben für den anfang mit
jlabels zu füllen…

[quote=mymaksimus]da du wahrscheinlich sowieso irgendwann mit bildern arbeiten wollen wirst,
empfiehlt es sich zum beispiel ein gridlayout mit jpanels anzulegen oder eben für den anfang mit
jlabels zu füllen…[/quote]Ich denke nicht, weil man vom GridLayout mit vielen Componenten nur schlecht zu einem Bild kommt.

Zuerst steht mal die Frage nach der Performanz. Die alle Positionen bei jeder Generation neu berecnen und zu zeichen ist bei einem 50x50 Feld ja noch kein Problem, aber bei einem 1500x1500 Feld sieht das schon anders aus. Meinen Vorschlag könnte man mit 10 zusätzlichen Codezeilen so optimieren, dass nur solche Felder neu berechnet werden, die sich in der nächsten Generation tatsächlich ändern können. Damit berechnet mein Rechner das 1500x1500 Feld mit einem r-Pentomino in duchscnittlich 3 Mikrosekunden. (für die Berechnung aller 2,25Mio Felder braucht er fast ne ganze Sekunde pro Generation.) Aber wer will denn schon in Java mit Objekten arbeiten…

Jedenfalls würde ich dringend zu einem Ansatz raten, bei dem jedes Feld eine Referenz auf das darzustellende Bild hat selbst weis, für welches Pixel es verantwortlich ist und dieses eben nur dann ändert, wenn es notwendig ist.

bye
TT