JavaScriptsSpiele

Mastermind

Das Klassiker-Knobelspiel: In zwölf Versuchen einen vierstelligen Geheimcode aus den Ziffern 1 bis 8 erraten.

Bei „Mastermind“, eingeschickt von unserem User „Vollautomatisch“, geht es darum, in höchstens zwölf Zügen einen vierstelligen Zahlencode zu erraten. Jede Stelle ist eine Ziffer zwischen 1 und 8 — Wiederholungen sind erlaubt, der Code kann also auch „3 3 7 3“ lauten.

Ihr tippt in die Felder der untersten Reihe vier Ziffern und klickt auf „Bewerten“. Der Computer legt daraufhin rechts neben der Reihe seine Bewertung ab: Für jede komplett richtige Ziffer — richtige Zahl an der richtigen Stelle — bekommt ihr einen roten Punkt, für jede Ziffer, die zwar vorkommt, aber an einer anderen Stelle steht, einen grauen. Aus welcher Position welcher Punkt stammt, verrät der Computer nicht. Danach wird die Reihe gesperrt und die nächste darüber ist an der Reihe.

Ein Beispiel: Der geheime Code lautet 1 3 5 7, ihr tippt 7 7 3 4. Es gibt zwei graue Punkte — eine der beiden Siebenen kommt vor, steht aber falsch, und mit der Drei ist es genauso. Aus solchen Teilinformationen lässt sich der Code Zug um Zug einkreisen. Wer nach zwölf Reihen nicht fertig ist, bekommt die Lösung angezeigt; über den Knopf „Lösen“ könnt ihr sie euch auch vorher verraten lassen.

Mastermind wurde 1970 von Mordechai Meirowitz als Brettspiel mit farbigen Stiften veröffentlicht, geht aber auf das viel ältere Papier-und-Bleistift-Spiel „Bulls and Cows“ zurück. Mathematisch ist es gut untersucht: Für die klassische Variante mit sechs Farben und vier Positionen genügen fünf Züge, um jeden Code sicher zu knacken. Diese Version hier ist mit acht Ziffern etwas schwieriger.

Wer lieber eine einzelne Zahl statt einer Kombination sucht, ist beim Zahlenraten und beim Zahlenraten 2 richtig. Als weitere Kopfnuss aus demselben Fach empfehlen sich Lights Out und Die Türme von Hanoi.

Spiele Baujahr 2007 läuft in deinem Browser

Unterste Reihe ausfüllen, dann „Bewerten“ — roter Punkt: Ziffer und Platz stimmen, grauer Punkt: Ziffer kommt vor, steht aber woanders.

Eingesandt von Vollautomatisch

So funktioniert das Script

Das Script hat vier Aufgaben: Es würfelt zu Beginn den Geheimcode aus, baut das Spielbrett aus zwölf Reihen zusammen, bewertet nach jedem Klick die aktuelle Reihe und schaltet danach eine Reihe weiter. Den Spielstand — also die Nummer der Reihe, in der gerade gespielt wird, und die vier geheimen Ziffern — merkt es sich nicht in Variablen, sondern in versteckten Eingabefeldern. Das ist alte Schule, funktioniert aber bis heute und hat den Vorteil, dass man sich den Stand im Browser ansehen kann.

const a = Math.floor(Math.random() * 8 + 1);
document.getElementById("losung_a").value = a;

Math.random() liefert eine Zufallszahl zwischen 0 und (knapp unter) 1. Mal 8 genommen ergibt das eine Zahl zwischen 0 und knapp 8, plus 1 also zwischen 1 und knapp 9. Math.floor() schneidet die Nachkommastellen ab — übrig bleibt eine ganze Zahl von 1 bis 8. Vier solche Ziffern werden gezogen und in die versteckten Felder losung_a bis losung_d geschrieben. Wollt ihr das Spiel leichter machen, setzt die 8 auf 6 herunter.

for (let i = 12; i > 0; i--) {
  brett += '<tr id="reihe_' + i + '">';
  for (let j = 1; j < 5; j++) {
    brett += '<td><input type="text" id="feld_' + i + '_' + j + '" class="steckfeld" maxlength="1"></td>';
  }
}

Das Spielbrett entsteht nicht als HTML im Dokument, sondern wird im Script als langer Text zusammengesetzt und am Ende mit innerHTML in den leeren Behälter <div id="mm-feld"> gehängt. Die äußere Schleife zählt rückwärts von 12 nach 1, damit Reihe 1 ganz unten landet — dort wird angefangen. Die innere Schleife setzt die vier Eingabefelder einer Reihe. Entscheidend sind die zusammengebauten id-Namen: feld_3_2 ist die zweite Stelle in der dritten Reihe. Über dieses Namensschema findet das Script später jedes Feld wieder, ohne sich eine Liste merken zu müssen.

if (pos1 === document.getElementById("losung_a").value) {
  bewertung += "Y"; pos1 = ""; wurf[0] = ""; w1 = 1;
}

Die Bewertung läuft in zwei Durchgängen. Der erste sucht die Volltreffer: Steht in Stelle 1 dieselbe Ziffer wie in losung_a, gibt es ein "Y". Wichtig ist, was danach passiert — sowohl der Tipp (pos1) als auch die geheime Ziffer (wurf[0]) werden auf den Leerstring gesetzt, also durchgestrichen. So kann dieselbe Ziffer im zweiten Durchgang nicht noch einmal als „kommt vor“ gezählt werden. w1 merkt sich den Treffer; sind am Ende alle vier w-Merker auf 1, ist das Spiel gewonnen.

for (let i = 0; i < 4; i++) {
  if ((pos1 === wurf_kopie[0]) && (wurf_kopie[0] !== "")) { pos1 = ""; wurf_kopie[0] = ""; bewertung += "H"; }
  else if ((pos2 === wurf_kopie[0]) && (wurf_kopie[0] !== "")) { pos2 = ""; wurf_kopie[0] = ""; bewertung += "H"; }
  wurf_kopie.shift();
}

Der zweite Durchgang sucht die Ziffern, die zwar im Code vorkommen, aber falsch stehen. Er nimmt sich reihum jede noch übrige geheime Ziffer vor — shift() wirft dabei nach jedem Durchlauf das erste Element aus dem Array, sodass wurf_kopie[0] immer die nächste zu prüfende Ziffer ist. Die else if-Kette sorgt dafür, dass jede geheime Ziffer höchstens einen grauen Punkt erzeugt, und jeder Tipp höchstens einmal verbraucht wird. Ohne diese Buchhaltung bekäme man bei doppelten Ziffern zu viele Punkte.

while (bewertung.indexOf("K") > -1) {
  bewertung = bewertung.replace("K", '<span style="color: #cc0000; font-weight: bold;">' + zeichen.value + '</span>');
}

Am Ende ist bewertung eine kurze Buchstabenfolge wie "YYH". Sie wird in zwei Schritten in sichtbare Punkte übersetzt: erst werden alle Y zu K und alle H zu L, dann jedes K in einen roten und jedes L in einen grauen Punkt. Der Umweg über die Zwischenbuchstaben ist nötig, weil replace() ohne weitere Angaben nur das erste Vorkommen ersetzt und der eingesetzte HTML-Text sonst selbst wieder durchsucht würde. Welches Zeichen als Punkt erscheint, steht im versteckten Feld zeichen — tauscht dort das · gegen ein oder ein *, wenn euch die Punkte zu klein sind.

if (k === reihe + 1) { document.getElementById("reihe_" + k).style.backgroundColor = "#ece9d8"; }
else { document.getElementById("reihe_" + k).style.backgroundColor = "#ffffff"; }

Nach jeder Bewertung färbt reiheActiv() die nun aktive Reihe ein und alle anderen weiß, und die eben bewertete Reihe bekommt einen kräftigen Rahmen. Die Eingabefelder der fertigen Reihe werden mit disabled = true gesperrt, damit man nicht nachträglich schummelt, und der Cursor springt per select() in die erste Stelle der neuen Reihe. Die beiden Farbwerte sind ein guter Startpunkt zum Anpassen — sie dürfen jeder gültige CSS-Farbwert sein.

Script für die eigene Homepage

Kopiert euch den kompletten Code und fügt ihn an der Stelle eurer Seite ein, an der das Script erscheinen soll. Er läuft ohne weitere Dateien und ohne fremde Server.

Mehr aus der Kategorie Spiele