App Entwicklung mit dem Android SDK: Mastermind
Mastermind-Projektvorlagen:
Tag 1: Mastermind-Projektdatei zum Importieren in Eclipse.
Tag 4: Vollständige Referenz-Mastermind-Implementierung zum Importieren in Eclipse.

Aufgaben und Hinweise zur Implementierung von Mastermind:
Hinweis zur Ermittelung der Exakten (rot) und Korrekten (weiß)
Hinweis zum timergesteuerten Ändern des Hintergrundbildes
Singleplayer Modus
Strategien
Multiplayer Modus

Links zur Android-Entwicklungsumgebung:
Android SDK (Startseite)
Eclipse (Entwickler-GUI)
Eclipse ADT plugin
Einrichtung des ADT plugins für Eclipse

Links zum Thema Mastermind:
Wikipedia-Artikel "Mastermind"
Maturaarbeit über Mastermind (Kurzbesprechung der Lösungstrategien)

Links zur Java-Programmierung:
Buch: Java ist auch eine Insel
Buch: Thinking in Java (3rd Edition)
Oracle Java Tutorials
Java API Specifications
Android Developer Resources

Dokumente der Einführungsveranstaltung:
Mastermind: Spielregeln und Strategien
Eclipse und das Eclipse ADT Plugin
GUI-Programmierung mit dem Android SDK
CIP-Pool-Merkzettel

Informationen über die Verwendung der CIP-Pool-Rechner:
Im CIP-Pool: Erste Schritte mit Eclipse
Im CIP-Pool: Generelle Hinweise
Im CIP-Pool: Fehler "Re-installation failed due to different application signatures" beim Ausführen einer Anwendung auf dem Test-Handy



Hinweis zur Ermittelung der Exakten (rot) und Korrekten (weiß)
Input: int-Arrays a1 und a2

   * Die Berechnung der Anzahl der Exakten ist relativ klar:
       Zähle, wie oft a1[i] == a2[i]
   
   * Zur Berechnung der Korrekten:
       - Gehe Farbenweise vor.
       - Für eine bestimmte Farbe c: Bestimme die Anzahl c1, mit der
         c in a1 vorkommt, und die Anzahl c2, mit der c in a2 vorkommt.
       - m = min(c1, c2) ist die Anzahl der Korrekten + Exakten für c.
       - Summiere für alle Farben c die entsprechenden m auf.
       - Subtrahiere davon die Anzahl der Exakten. Das Ergebnis ist
         die Anzahl der Korrekten.

   * Der entsprechende Java-Code sieht wie folgt aus:
		public static int computeRed(int[] a1, int[] a2) {
			if (a1.length != a2.length)
				throw new ArrayStoreException("Arrays a1 and a2 must have the same size.");
			int red = 0;
			for (int i=0; i<a1.length; i++) {
				if (a1[i] == a2[i])
					red++;
			}
			return red;
		}
		
		public static int computeWhite(int[] a1, int[] a2, int[] colorValues) {
			if (a1.length != a2.length)
				throw new ArrayStoreException("Arrays a1 and a2 must have the same size.");
			int whiteAndRed = 0;
			for (int c=0; c<colorValues.length; c++) {
				int counter1 = 0, counter2 = 0;
				int currColor = colorValues[c];
				for (int i=0; i<a1.length; i++) {
					if (a1[i] == currColor)
						counter1++;
					if (a2[i] == currColor)
						counter2++;
				}
				whiteAndRed += Math.min(counter1, counter2);
			}
			int white = whiteAndRed - computeRed(a1, a2);
			return white;
		}

Zurück nach oben





Hinweis zum timergesteuerten Ändern des Hintergrundbildes
			// in header
			import android.os.Handler;
			
			// in MasterMind class
			private Handler handler;
			
			// in onCreate(Bundle)
			handler = new Handler();
			
			// where you want to start changing the background
			Runnable runnable = new Runnable() {
				public void run() {
					for (int i = 0; i < colors.length; i++) {
						final int value = i;
						try {
							Thread.sleep(2000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						handler.post(new Runnable() {
							public void run() {
								findViewById(R.id.Board).setBackgroundColor(colors[value]);
							}
						});
					}
				}
			};
			new Thread(runnable).start();

Zurück nach oben


Singleplayer Modus
Aufgaben: Singleplayer Modus
0. Zur Terminologie:
   Das Spielfeld besteht aus:
   * ColorSelector: Hier wählt der Spieler die Farbe aus, die er einem Feld
     im ColorSetter zuordnen will.
   * ColorSetter: Hier wird die Farbkombination zusammengestellt, die den
     nächsten Rateversuch (Guess) bestimmt.
   * Board: Das Board ist der restliche Teil des Spielfeldes, in dem die
     verschiedenen Rateversuche zusammen mit den Antworten (Answers)
     dargestellt werden.
     Jede Guess+Answer-Kombination wird in einer 'row' zusammengefasst.
   * Für die Aufgabenbewältigung reicht es (in der Regel) nur die angegebene
   	  Klasse zu betrachten!

1. Passe die Layoutdatei "res/layout/main.xml" an,
   so dass es z.B. sechs Farben im ColorSelector, vier Eingabefelder im
   ColorSetter und zehn Rows im Board gibt.
   Passe die Layoutdatei "res/layout/row.xml" an: In jeder Row soll es vier
   ImageViews für die Darstellung eines Rateversuchs geben.
   Zudem soll in jeder Row die berechnete Antwort in einem 2x2 Feld dargestellt werden.
   Dies lässt sich mit ein bisschen Kopieren & Einfügen & Editieren
   lösen (am schnellsten in der XML-Datei).

2. Die folgenden Aufgaben sind in src/de.uni_passau.fim/MasterMind.java zu lösen.
   Listener-Handling: Jedem klickbaren Feld im Layout wird ein Listener zugeordnet.
   Wird ein Feld angeklickt, so wird der Listener aufgerufen,
   dieser stößt dann seinerseits alle notwendigen Aktionen an.
   Es gibt insgesamt 4 Listener: Jeweils einen für die beiden Buttons, 
   einen für den ColorSetter und einen für den ColorSelector (bereits implementiert).
   a) Die Listener für die Buttons müssen in onCreate(Bundle) noch den 
      Buttons hinzugefügt werden. Die Aktionen, die durch klicken der Buttons 
      ausgeführt werden sollen, sind recht umfangreich und werden deshalb
      in eigene Methoden ausgelagert (Aufgabe 3 bzw. Aufgabe 1 im Abschnitt 
      Strategie). Schau Dir z.B. an, wie der colorSelectorListener am Anfang
      von onCreate(Bundle) zu einer RadioGroup hinzugefügt wird und imitiere
      das Vorgehen für die beiden Buttons.
   b) colorSetterListener: Wird ein Feld im ColorSetter gedrückt, so soll diesem
      die gerade ausgewählte currentColor hinzugefügt werden.
   Sonstiges:
   * In dieser Aufgabe müssen nur wenige Zeilen Code hinzugefügt werden. Die
     Ziel der Aufgabe ist es, das Zusammenspiel zwischen GUI und Code zu verstehen,
     und die Klassen kennenzulernen, die für dieses Zusammenspiel verantwortlich sind.
   * Um Debug-Messages auszugeben kann die Methode System.out.println(String) verwendet
     werden. Die Ausgaben erscheinen dann in der LogCat-View (Window > Show View > 
     LogCat). Lege darin einen Filter an, der nur die Ausgaben der Application 
     "de.uni_passau.fim" anzeigt.

3. Als nächstes sollen die Aktionen implementiert werden, die beim Klick auf den
   "Check"-Button ausgeführt werden sollen. Diese könnten direkt in die Methode
   checkButtonListener.onClick(View) geschrieben werden, der Übersichtlichkeit
   halber werden wir die Schritte jedoch in die Methode updateBoard(...) auslagern.

   Um die updateBoard()-Methode ausfüllen zu können, muss man zuerst überlegen,
   welche Aktionen bei einem Klick auf den "Check"-Button überhaupt durchzuführen sind:
   
   a) Hole die aktuellen Farben aus dem 'ColorSetter' in ein int-Array.
   b) Berechne eine Antwort durch Vergleich mit dem Array "quest". Es müssen zwei Werte
      berechnet werden: Die Anzahl der korrekten Farben am korrekten Platz und
      die Anzahl der verbleibenden korrekten Farben am falschen Platz. Diese beiden
      Werte sind Grundlage der Antwort.
   c) In der GUI muss auf dem Spielbrett nun die neue geratene Kombination und die Antwort
      angezeigt werden. Hierzu ist es hilfreich sich zu merken, welche Zeile im 
      Board die aktuelle ist. Diesen Zähler erhöht man anschließend.
   d) Überprüfe schließlich, ob der Spieler gewonnen oder verloren hat.
      Ein Dialog informiert den Spieler gegebenenfalls (siehe Aufgabe 4b).

4. Abschließend ist noch ein bisschen Feinschliff nötig:
   a) Der Check-Button soll nur klickbar sein, wenn alle Felder eine Farbe haben.
   b) Die Dialoge am Ende des Spiels sollten sinnvolle Informationen anzeigen.
      Danach kann der Spieler zwischen einer neuen Runde wählen oder das Spiel 
      verlassen.
   c) Erweitere das Spiel beliebig. Z.B. könnte die gesuchte "quest" zum Schluss
      angezeigt werden.

Zurück nach oben


Strategien
1. Implementiere die Strategie "Lineare Suche" in der Methode
   computeAndSetHint().
   - Wie kann das systematische Aufzählen aller Codes realisiert werden?
   - Schreibe eine Methode, die überprüft, ob ein gegebener Code mit
     allen bisher gespielten Codes+Antworten konsistent ist. Vorüberlegung:
     Was bedeutet es, dass ein Code mit genau einem gegebenen Code-Antwort-Paar
     konsistent ist?
   Tipp: Schau Dich im Package *.strategies um, insbesondere die
   Klasse Utils enthält ein paar nützliche Funktionen. Um diese zu nutzen
   musst du allerdings die Klassen Guess, Answer und Configuration verwenden.
   - Bewerte die Strategie. Wie verhält sie sich im schlechtesten Fall, wie
     im Durchschnitt?

2. Implementiere eine evolutionäre Strategie.
   - Die Klasse strategies.EvolutionaryStrategy enthält bereits ein grobes
     Framework, so dass du dich voll auf die Mutations- und Fitness-
     Funktionen konzentrieren kannst (die Selektion ist bereits vorgegeben).
     Suche nach "TODO" und "NotYetImplementedException" um die fehlenden
     Stücke zu identifizieren.
   - Implementiere die Fitness-Funktion computeFitness(...). 
     Die Fitness muss vom typ int sein, je größer desto fitter. Am fittesten
     sollen die Codes sein, die mit allen bislang gespielten Konfiguraionen
     Konsistent sind.
   - Implementiere die Mutations- und Crossover-Funktionen
     onePointCrossover(...), twoPointCrossover(...), switchRandomColor(...) und
     permuteRandomPositions(...). Du kannst Dir natürlich auch weitere
     Mutationen überlegen.
   - In makeNewGeneration(...) müssen nun die Mutationen und Kreuzungen noch
     aufgerufen werden. Ob eine bestimmte Mutation stattfindet oder nicht,
     kann von einer Mutationsrate abhängig sein. Somit kann man z.B. damit
     experimentieren, welche Mutationen besonders effektiv sind.
   - Bewerte die Strategie. Wie verhält sie sich im schlechtesten Fall, wie
     im Durchschnitt?

3. Implementiere und bewerte weitere Strategien oder verfeinere die obigen.
   Falls dein Mastermind verschiedene Farb- und Steckplatz-Variationen
   realisiert:
   Wie verhalten sich die Strategien für die verschiedenen Varianten bzgl.
   Laufzeit und Speicher? Wie performant laufen die Strategien auf dem Handy?

Zurück nach oben


Multiplayer Modus
1. Button in der Klasse StartScreen reaktivieren.

2. In der Mulitplayer Klasse den Code hinzufügen,
   der nötig ist um nach anderen Bluetooth Handys zu suchen
   und diese zu verwalten.

3. In der Multiplayer Klasse muss der Message Handler so erweitert
   werden, dass
   - beim Client das Spielbrett im Multiplayer Modus angezeigt wird.
   - im Server der Vorschlag des Clients verifiziert wird.
   - im Client die Darstellung des Spielbretts mit korrekten/inkorrekten
     Steinen aktualisiert wird.

4. Beliebige eigene Erweiterungen!

Zurück nach oben


Einrichtung des ADT plugins für Eclipse:
Installiere zuerst Eclipse. Dann muss das Android SDK sowie das ADT Eclipse-Plug-in installiert werden, folge hierzu den Anweisungen auf der SDK-Download Seite.
1) Root-Pfad zum Android SDK setzen:
     Window -> Preferences -> Android -> SDK Location

2) Komponenten installieren (eventuell bereits bei der SDK Installation geschehen):
     Window -> Android SDK Manager -> Available Packages

   Folgende Komponenten werden benötigt:
     * Tools (SDK Tools und SDK Platform-tools) 
     * Android 4.0, API 14 oder neuer

3) Standard Virtual Device einrichten:
     Window -> AVD Manager -> New...

   Folgende Einträge im "Create new Android Virtual Device"-Dialog:
     Name: StdAVD
     Target: Android 4.0 - API Level 14 (oder neuer)
     SD Card: Size: 16 MiB
     Hardware: -> New...
               Property: SD Card support
     (Die restlichen Voreinstellungen können übernommen werden.)

Weitere Informationen zum Installationsprozess sind auf den Seiten http://developer.android.com/sdk/eclipse-adt.html und http://developer.android.com/training/basics/firstapp/index.html zu finden.

Zurück nach oben


Im CIP-Pool: Erste Schritte mit Eclipse
1) Eclipse starten: Im Starmenü unten links nach "konsole" suchen und
   das Programm "Konsole" starten. Im erscheinenden Shell-Fenster dann
   "eclipse" eingeben. 
   Der Workspace kann belassen werden. 

2) Root-Pfad zum Android SDK setzen:
     * Möglichkeit 1: Ihr werdet nach dem ersten Start von Eclipse aufgefordert, den
                      Pfad zu setzen.
     * Möglichkeit 2: Ihr setzt den Pfad später unter
                      Window -> Preferences -> Android -> SDK Location

   Der Pfad lautet in jedem Fall "/usr/local/android-sdk-linux_x86".

3) Standard Virtual Device einrichten:
     Window -> AVD Manager -> New...

   Folgende Einträge im "Create new Android Virtual Device"-Dialog:
     Name: StdAVD
     Target: Android 4.0 - API Level 14
     SD Card: Size: 16 MiB
     Hardware: -> New...
               Property: SD Card support
     (Die restlichen Voreinstellungen können übernommen werden.)
   
   Dann "Create AVD".

4) Projektdatei herunterladen: Mastermind-Projektdatei

5) Projekt in Eclipse importieren:
     File -> (Import ->)  Import... -> General -> Existing Projects into Workspace -> Next

     Select archive file: -> Browse...
     Im nächsten Dialog die heruntergeladene Projektdatei auswählen.

     Im Feld unter "Projects:" ist nun das zu importierende Projekt bereits
     ausgewählt. Jetzt nur noch den "Finish"-Button anklicken.

6) Im importierten Projekt unter "src > de.uni-passau.fim" die Datei "MasterMind.java" öffnen.

7) Den Emulator starten: 
    Run -> Run -> Android Application
    
  Es kann etwas dauern, bis der Emulator bereit ist. (Beim allerersten Mal startet MasterMind evtl.
  nicht sofort, da man irgendwelche Android-Informationen bestätigen muss.)
  
  Läuft der Emulator einmal, so kann das Fenster offen bleiben. Nach Code-Änderungen einfach
  die Datei speichern und über "Run -> Run" das Programm in den laufenden Emulator laden.

Zurück nach oben


Im CIP-Pool: Generelle Hinweise
Nach dem Einloggen erscheinen einige Dialogfenster mit Statusmeldungen.
   Diese können ignoriert/geschlossen werden.

Zurück nach oben


Im CIP-Pool: Fehler "Re-installation failed due to different application signatures" beim Ausführen einer Anwendung auf dem Test-Handy
Auf dem Test-Handy existiert eine Anwendung mit gleichem Namen, aber
unterschiedlicher Signatur. Die gleichnamige Anwendung kann wie folgt vom
Test-Handy deinstalliert werden:

1) Eine Shell starten: Im Starmenü unten links nach "konsole" suchen und
   das Programm "Konsole" starten.

2) In der Shell mit folgendem Befehl die Anwendung vom Test-Handy entfernen.
   "XYZ" ist hierbei mit dem Namen aus der Fehlermeldung (z.B.
   "de.uni_passau.fim.MasterMind") zu ersetzen:
     /usr/local/android-sdk-linux_x86/platform-tools/adb uninstall "XYZ"

Zurück nach oben