# Memory fertig!



## Neolity (9. Jan 2007)

Den alten Beitrag lasse ich mal stehen...

Hier jetzt der Link zur fertigen Fassung:

rapidshare.com/files/11872696/Memory.rar.html

_________________________________________________________________________________________________

Hallo,

ich habe angefangen ein Memory Spiel zu programmieren.
Erstmal grundsätzlich wie die ganze Sache aufgebaut ist:

Ich habe mit Gimp 10 "Gegenstände, Symbole etc." gezeichnet. Diese Grafiken liegen als .jpeg vor. Über die Qualität der Dinger wird bitte nicht diskutiert. Sind zum einen nur vorläufig und zum anderen bin ich nicht sonderlich begabt in Kunst! 

Diese Grafiken werden als ImageIcons eingeladen und zunächst sortiert in ein Array gepackt. Durch eine Methode zufall() werden sie dann durcheinander gewirbelt und in ein 2. ImageIcon Array gepackt.

Das Spielfeld wird durch ein Array von Buttons dargestellt, genannt btf. Standartmäßig haben diese Buttons das ImageIcon deck.jpeg (Einfach eine neutrale blau weiße Textur). Wird auf einen Button geklickt, wird die Methode spielzug() aufgerufen. Hier kommt nun mein Problem.
Für meine Begriffe gibt es 2 Möglichkeiten das eigentliche Memory Spiel umzusetzen. Einmal die etwas unelegante Version, die aber dem Benutzer ein längeres Betrachten der Bilder ermöglicht. Das würde ich so umsetzen, dass der Benutzer auf 2 Buttons klickt, die entsprechenden ImageIcons angezeigt werden und der Benutzer, wenn er sich die Bilder lange genug eingeprägt hat, durch einen Klick auf "weiter" die ImageIcons wieder verdecken lässt. Natürlich nur, wenn sie nicht gleich sind.
Die 2. Variante wäre, dass der Benutzer wieder 2 Buttons anklickt, die entsprechenden ImageIcons angezeigt werden, nun aber bereits nach einer definierten Zeit automatisch wieder verdeckt werden. Für mich die elegantere Fassung und die an der ich mich versucht habe.
Das Problem ist nun aber, dass bei mir nicht erst das 2. Bild angezeigt wird, gewartet wird und dann wieder verdeckt wird, sondern erst gewartet wird und dann mit einem Schlag auf- und sofort wieder verdeckt wird. Das Bild ist praktisch gar nicht zu sehen.

Ist wahrscheinlich nicht ganz einfach zu verstehen. Deshalb mal meine spielzug Methode ganz ausführlich kommentiert:


```
public void spielzug(int x, int y)
{
	int i; // Variable für meine Warteschleife
	btf[x][y].setIcon(ii[x][y]); // Hier wird dem Button das entsprechende Icon hinzugefügt
	if (zug == 1) // Prüfung, ob das erste oder 2. Kärtchen umgedreht wird
	{
		zug++; 
		var1 = x; // Die Werte von x und y müssen gespeichert werden, damit man nach dem 2. Kärtchen
		var2 = y; // umdrehen vergleichen kann.
	}
	else if (btf[x][y] != btf[var1][var2]) // Die else if Anweisung ist nötig, damit der Benutzer nicht 2 mal auf den              
                                                            // gleichen Button klickt und dies als gleiche Karte gezählt wird
	{
		if (ii[x][y] == ii[var1][var2]) // Wenn die beiden Karten gleich sind, dann:
		{
			i = 0;
			while (i < 100000000) // Warteschleife, die nicht funktioniert bzw. vor dem ersten Schritt ausgeführt
			{                              // wird
				i++;
			}
			btf[x][y].setIcon(leer);                   // Icons beider Buttons werden geleert und Buttons abgeschaltet
			btf[var1][var2].setIcon(leer);
			btf[x][y].setEnabled(false);
			btf[var1][var2].setEnabled(false);
		}
		else
		{
			i = 0;
			while (i < 100000000)
			{
				i++;
			}				
			btf[x][y].setIcon(deck);              // Kärtchen werden wieder verdeckt
			btf[var1][var2].setIcon(deck);
		}
		zug = 1;
		var1 = var2 = 0;
	}
	x = y = 0;
}
```

Ich hoffe das hilft und verdeutlicht wo das Problem liegt. Bei Bedarf kann ich auch mal die ganze .java Datei hochladen. Ist aber alles noch etwas provisorisch!

Vielen Dank!


----------



## Wildcard (9. Jan 2007)

Was du da machst ist busy waiting.
Das hat nicht nur 100% CPU Auslastung zur Folge sondern ist auch noch von der Hardware abhängig und blockiert obendrein deine GUI.
Einfachste Lösung für dich:
Schau dir den javax.swing.Timer an.


----------



## Neolity (10. Jan 2007)

Hallo,

vielen Dank für deine Antwort. Nur leider bringt das noch immer nicht den gewünschten Effekt!
Die Methode sieht nun so aus:


```
public void spielzug(int x, int y)
	{
		int i;
		javax.swing.Timer timer = new javax.swing.Timer(2000, warte);
		timer.setRepeats(false);
		btf[x][y].setIcon(ii[x][y]);
		if (zug == 1)
		{
			zug++;
			var1 = x;
			var2 = y;
		}
		else if (btf[x][y] != btf[var1][var2])
		{
			if (ii[x][y] == ii[var1][var2])
			{
				timer.start();
				//btf[x][y].setIcon(leer);
				//btf[var1][var2].setIcon(leer);
				btf[x][y].setEnabled(false);
				btf[var1][var2].setEnabled(false);
			}
			else
			{				
				timer.start();
				btf[x][y].setIcon(deck);
				btf[var1][var2].setIcon(deck);
			}
			zug = 1;
			var1 = var2 = 0;
		}
		x = y = 0;
	}
```

Oberhalb davon ist noch eine actionPerformed Methode. Syntaktisch dürfte das alles stimmen. Bloß an meinem Problem hat sich nichts geändert. Ich stelle mir im Grunde diese 3 Schritte vor:
- Bild wird aufgedeckt (ImageIcon dem Button zugewiesen)
- Timer läuft ab
- Nach ablauf des Timers werden beide Bilder abgedeckt bzw. die Buttons abgestellt, wenn beide Bilder gleich sind.


----------



## Wildcard (10. Jan 2007)

1. Dreh die Karte um
2. starte den Timer
3. wenn der Timer actionPerformed aufruft verdeckst du die Karte wieder


----------



## Neolity (10. Jan 2007)

Wie kann ich denn der action performed methode var1 und var2 bzw. x und y übergeben?


----------



## Wildcard (10. Jan 2007)

Leg entsprechende Member-Variablen an.


----------



## Neolity (10. Jan 2007)

Hätte man auch von selbst drauf kommen können! 

Vielen Dank für deine Hilfe! 

Wenn ich die Energie noch aufbringe, dann wird zumindest die voll spielbare erste Beta  heute nacht noch fertig!


----------



## Neolity (13. Jan 2007)

So, prinzipiell ist das Spiel fertig, aber es gibt da noch ein kleineres Problem:

Ich habe den Code jetzt auf 2 Dateien verteilt. Eine memory.java und eine memoryGrafik.java Diese beiden Dateien liegen in einem Unterordner memory und sind in einem package memory.
Eine Ebene darüber habe ich eine Datei memoryStarten.java, die das package importiert und den Konstruktor von memory.java aufruft, so dass das Memory gestartet wird. Das Problem ist nun, dass beim Starten von memoryStarten folgende Fehlermeldung auftritt:

Exception in Thread "main" java.lang.NullPointerException
    at memory.memory.zufall(memory.java:46)
    at memory.memoryGrafik.(init)(memoryGrafik.java:88)
    at memory.memory.(init)(memory.java:26)
    at memory.memoryStarten.main(memoryStarten.java:7)

Ich weiß was eine NullPointerException ist, aber warum tritt die hier auf? Als der gesamte Code noch in einer Datei lag, gab es da auch kein Problem?!

Hier noch die zufall Methode, auf die verwiesen wird:


```
public void zufall()
	{
		Random r = new Random();
		int num1, num2;
		boolean pruef;

		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < 5; j++)
			{
				pruef = false;
				while (pruef == false)
				{
					num1 = num2 = 0;
					num1 = 0 + Math.abs(r.nextInt()) % 4;
					num2 = 0 + Math.abs(r.nextInt()) % 5;
					if (grafik.ii[num1][num2] == grafik.leer)
					{
						grafik.ii[num1][num2] = grafik.ii_sortiert[i][j];
						pruef = true;
					}
				}
			}
		}
	}
```

Hier hab ich auch mal das ganze Archiv hochgeladen, damit man sich das alles im Zusammenhang anschauen kann. Enthält:
- Ordner images (enthält die Bilder)
- Ordner memory (enthält die angesprochenen beiden Java Dateien memory.java und memoryGrafik.java)
- memoryStarten.java

rapidshare.com/files/11565322/Memory.rar.html

Wer den Fehler oben findet, der sieht natürlich auch zuerst das (fast) fertige Spiel!  So als kleinen Anreiz hinterher geschoben!


----------



## Apo (13. Jan 2007)

habs mir mal kurz angeschaut und zum laufen gebracht ...
ich habe nur folgendes verändert.

in der memory.java


```
public memory()
	{
		grafik = new memoryGrafik(this);
		grafik.zufall();
	}
```

und in der memoryGrafik.java

```
public memoryGrafik(final memory spiel)
	{
		super("Memory");
		.......
		ii_sortiert[3][3] = tisch;
		ii_sortiert[3][4] = tisch;	
	}
	
	public void zufall()
	{
		spiel.zufall();

		setVisible(true);	
	}
```


----------



## moormaster (13. Jan 2007)

Mir sind da auch nen paar Dinge aufgefallen, die man vielleicht verbessern sollte:

erstens:

memoryGrafik.java

```
ImageIcon deck = new ImageIcon("images//deck.jpeg");
```

Wieso steht da ein Doppel-Slash? Ein einfacher Slash tut es in diesem Fall auch. Da der Konstruktor an dieser Stelle einen Dateinamen erwartet würde ich wegen der Plattformunabhänngigkeit sowieso eher File.separator anstelle eines festen Trennzeichens nehmen.

zweitens:
memoryStarten.java ist in der rar von http://rapidshare.com/files/11565322/Memory.rar.html nicht compiliert


----------



## Neolity (13. Jan 2007)

Die Doppel Slashes hab ich mal weggemacht. Ich hatte in Erinnerung, dass es mal mit einfachen / nicht geklappt hatte und mir geraten wurde // zu verwenden.

Ich hab gerade alles nochmal bei mir kompiliert, aber der Fehler ist immer noch da!


----------



## moormaster (13. Jan 2007)

Bei einfachen \ kann ich mir das gut vorstellen, weil das \ innerhalb eines "..." Ausdrucks als Escape-Zeichen dient und wenn man tatsächlich ein \ im String haben will, muss man eben \\ schreiben. Bei / hingegen ist das völlig unproblematisch; da reicht eins 

Mit Apo's Änderungen konnte ich das ganze kompilieren... also laufen tuts schon, wenn es auch noch Bugs hat. (durch wildes herumklicken in den Karten, bleiben einige Karten aufgedeckt, die nicht aufgedeckt sein sollten )

Meine lauffähige (kompilierbare) Version:

http://rapidshare.com/files/11583515/Memory.zip.html

geändert wurde neben Apo's Änderungen:

- // gegen File.separator ausgetauscht
- Klassennamen beginnen mit Großbuchstaben
- Quelltext und binary Ordner sind getrennt


----------



## Neolity (14. Jan 2007)

Muss mal gerade gestehen, dass ich Apo's Eintrag total übersehen habe! 

Kommt davon, wenn man während man CS spielt hier immer mal wieder kurz vorbeischaut... 

Aber mit Apo's Änderungen funktioniert das ganze jetzt! 

Dazu aber noch ein paar Fragen:

- Der erste Teil hinzufügen von "grafik.zufall();" leuchtet mir ein. meldet ja die NullPointerException bei der Zufallsmethode. So verhindert man praktisch, dass die 0 ist.

- Der 2. Teil hingegen nicht mehr! Warum diese 2. zufall Methode und warum wird das ganze erst darin sichtbar gemacht?

Die Sache mit dem wilden rumklicken ist mir schon bekannt. Werde ich mir noch was einfallen lassen...

Wie kann ich denn Quelltext und binary trennen? Aus irgendwelchen Gründen muss ich immer die .class Dateien löschen, damit Änderungen, die ich am Quelltext vorgenommen habe, auch Wirkung zeigen.


----------



## moormaster (14. Jan 2007)

Neolity hat gesagt.:
			
		

> Wie kann ich denn Quelltext und binary trennen? Aus irgendwelchen Gründen muss ich immer die .class Dateien löschen, damit Änderungen, die ich am Quelltext vorgenommen habe, auch Wirkung zeigen.



Getrennt wird es eigentlich nur dadurch, dass du einen 2. Ordner, "bin", zusätzlich zum Quelltextordner hast. Das wird beim compilieren entsprechend in den Parametern angegeben, damit der Compiler die neuen .class Dateien im bin Ordner (-d Parameter) ablegt und auch bestehende .class Dateien dort surch (-classpath Parameter). Bestehende unkompilierte Klassen findet der compiler durch den -sourcepath Parameter.

Dass du die class-Dateien immer lösche musst, ist eigenartig. Es kann aber sein, dass es Situationen gibt, wo irgendwelche Leichen von .class Dateien herumliegen und dann das Programm Dinge macht, die gar nicht mehr im Quelltext stehen.

Zum Beispiel, wenn man einen Klassennamen geändert hat und es immernoch stellen gibt, die den altem Namen benutzen, um auf die Klasse zuzugreifen. Dann würde eine alte compilierte Version mit dem alten Namen aufgerufen werden. Spätestens dann sollte man die class Dateien einfach mal komplett neu compilieren und die alten vorher löschen 

Ansonsten sollten Änderungen nach dem Compilieren sich auch sofort auswirken.


----------



## Apo (14. Jan 2007)

Du könntest einfach eine IDE wie Eclipse nutzen und schon müssten die Probleme von wegen erstmal wieder alles löschen weg sein.


----------



## Neolity (14. Jan 2007)

Vielen Dank euch beiden! 

Eclipse wollte ich mir sowieso bald mal anschauen. Ansonsten setze ich jetzt mal den Haken, weil ja alles läuft.
Im Laufe des Tages kümmere ich mich dann nochmal um das "wilde rumklicken", kommentiere den Quelltext und perfektioniere vielleicht noch das eine oder andere und dann ist die Sache für mich abgeschlossen. 

Die fertige Version gibt es dann natürlich nochmal hier!


----------

