JavaScriptsSpiele

Streichholzspiel 2

Nim gegen den Computer — mit frei einstellbaren Regeln: Wer das letzte Hölzchen nimmt, gewinnt oder verliert, ganz wie ihr wollt.

Eine weitere Version des bekannten Streichholzspiels hat uns unser User Laplace geschickt. Hier könnt ihr fast alles selbst einstellen: ob derjenige gewinnt oder verliert, der das letzte Hölzchen nimmt, wie viele Hölzchen pro Zug höchstens genommen werden dürfen, wie viele überhaupt auf dem Tisch liegen und wer anfängt — Computer oder Spieler.

So wird gespielt: Stellt die Parameter ein und klickt auf „START". Danach tragt ihr in das kleine Feld ein, wie viele Hölzchen ihr nehmen wollt, und bestätigt mit „NEHMEN" oder der Eingabetaste. Der Computer zieht sofort nach und sagt euch, wie viele er genommen hat. Über „ABBRUCH" beendet ihr eine Partie vorzeitig, und die Statistik daneben zählt Siege, Niederlagen und Abbrüche mit. Wer keine Lust auf Einstellen hat, klickt auf „Zufällige Parameter" — dann würfelt der Rechner alles aus.

Zum Spiel selbst: Es handelt sich um eine sogenannte Nim-Variante, genauer um ein Subtraktionsspiel. Solche Spiele sind mathematisch vollständig durchschaut, das heißt: Es gibt eine Gewinnstrategie, und wer sie kennt und am Zug ist, gewinnt zwangsläufig. Sie beruht auf Restklassen — bei höchstens b Hölzchen pro Zug rechnet man mit dem Rest bei Division durch b + 1. Genau diese Rechnung steckt im Script, und deshalb ist der Computer hier ein perfekter Gegner: Verliert er, dann nur, weil ihr eine Ausgangsstellung erwischt habt, aus der auch er nicht mehr gewinnen kann. Eure eigentliche Aufgabe ist es also, die richtigen Parameter zu wählen und den ersten entscheidenden Zug nicht zu vergeben.

Wenn euch das Prinzip gefällt, probiert auch das Würfelspiel „Wer kommt an die 50?" und Tic Tac Tile — beides Spiele, bei denen Rechnen mehr hilft als Glück.

Spiele Baujahr 2008 läuft in deinem Browser

Derjenige, der das/die letzte/n Hölzchen nimmt, …

Wer soll anfangen?

Eingesandt von Laplace

So funktioniert das Script

Das Script ist mit rund 250 Zeilen umfangreich, aber sehr regelmäßig gebaut: Es gibt zwei Prüffunktionen für die Eingabefelder, eine Funktion für den Spielerzug, eine für den Computerzug, eine fürs Zeichnen der Hölzchen und eine, die das Spielfeld wieder in den Ausgangszustand versetzt. Der ganze Spielstand steckt in einer Handvoll Variablen — wir schauen uns die wichtigsten Stellen an.

let a;   // 1 = wer das letzte nimmt, verliert; 0 = er gewinnt
let b;   // höchstens so viele Hölzchen je Zug
let c;   // true = der Computer fängt an
let d;   // Hölzchen zu Beginn
let x;   // Hölzchen, die gerade noch liegen

Das sind die Spielregeln in fünf Buchstaben. a, b und d stellt ihr über das Formular ein, x zählt während der Partie herunter. Solche Ein-Buchstaben-Namen sind heute verpönt — für das Verständnis lohnt es, sie beim Lesen im Kopf durch die Bedeutung zu ersetzen.

function B_in(Zahl) {
  if (isNaN(Zahl) || Zahl.length > 2 || Zahl <= 1 || Zahl === "" || Zahl > 49) {
    melden("Sie müssen eine Ganzzahl zwischen 2 und 49 eingeben!");
    feldB.value = "";
    fehler = true;
    return;
  }
  b = Number(Zahl);
}

Die Prüffunktionen laufen bei jeder Änderung des Feldes und noch einmal beim Start. Sie fangen alles ab, was keine sinnvolle Zahl ist: Buchstaben (isNaN, „ist keine Zahl"), zu lange Eingaben, zu kleine und zu große Werte. Im Fehlerfall wird das Feld geleert und ein Merker fehler gesetzt, den startit() danach abfragt — so verhindert das Script, dass eine Partie mit unsinnigen Regeln beginnt. Erst am Ende, wenn alles stimmt, wird der Wert mit Number() in eine echte Zahl verwandelt und gespeichert.

const o = (x - a) % (b + 1);

if (o === 0) {
  n = GetRandom(1, b);
} else {
  for (let i = 1; i <= b; i++) {
    if ((x - i - a) % (b + 1) === 0) {
      n = i;
    }
  }
}

Das ist das Gehirn des Gegners und zugleich die vollständige Gewinnstrategie dieses Spieltyps. Der Rest-Operator % liefert den Rest einer Division. Die Zielstellung des Computers ist: Nach seinem Zug sollen so viele Hölzchen liegen bleiben, dass (x − a) ohne Rest durch b + 1 teilbar ist. Warum b + 1? Weil der Gegner höchstens b nehmen kann — der Computer ergänzt dessen Zug also immer auf genau b + 1 und hält die Stellung damit fest in seiner Hand. Die Schleife sucht schlicht die Zahl i zwischen 1 und b, die das erreicht.

Und wenn o schon 0 ist? Dann steht der Computer selbst in der Verliererstellung und kann nichts mehr retten — dann nimmt er mit GetRandom(1, b) einfach irgendetwas und hofft auf einen Fehler des Spielers. Genau das ist eure einzige Chance in diesem Spiel: die Partie so einzurichten, dass der Computer schon zu Beginn in dieser Lage ist.

function zeigerest() {
  for (let i = 1; i <= max; i++) {
    hoelzer[i - 1].height = 0;
  }
  for (let i = 1; i <= x; i++) {
    hoelzer[i - 1].height = 38;
  }
}

So einfach wird gezeichnet: Auf der Seite liegen von Anfang an 100 gleiche Hölzchen-Bilder. Die erste Schleife setzt alle auf die Höhe 0 — ein Bild ohne Höhe ist unsichtbar —, die zweite gibt den ersten x Bildern wieder ihre volle Höhe von 38 Pixeln. Diese Technik ist älter als jedes Zeichen-Element im Browser und funktioniert bis heute: Man baut alles einmal auf und blendet nur noch um.

function melden(text) {
  ausgabe.innerHTML += (ausgabe.innerHTML ? "<br>" : "") + text;
}

Das Original meldete jeden Zug mit einem alert()-Fenster, das man wegklicken musste. Hier sammelt melden() stattdessen alle Meldungen einer Runde untereinander in der Ergebnisfläche. Der kurze Ausdruck mit dem Fragezeichen ist ein Bedingungsoperator: Steht schon etwas in der Fläche, kommt zuerst ein Zeilenumbruch dazu, sonst nicht. Vor jedem Spielerzug leert meldungLeeren() die Fläche wieder.

function loeschen() {
  ...
  knopfStart.style.visibility = "visible";
  unshow();
  anfang[0].disabled = false;
  feldB.readOnly = false;
  ...
}

Nach jeder Partie stellt loeschen() die Bedienung zurück: Die Parameterfelder werden wieder freigegeben, die Zugknöpfe verschwinden, der START-Knopf kommt zurück. Beachtet den Unterschied zwischen visibility = "hidden" und display: nonevisibility macht ein Element unsichtbar, lässt seinen Platz aber frei, sodass beim Ein- und Ausblenden nichts springt.

Zum Anpassen: Die beiden Meldungen win und lost stehen ganz oben und lassen sich frei formulieren. Die Obergrenze max = 99 begrenzt die Hölzchen und passt zu den 100 vorbereiteten Bildern — wollt ihr mehr, müsst ihr auch die 100 in der Aufbauschleife erhöhen. Und wer dem Computer eine Chance zum Danebengreifen geben will, ersetzt in cnimm() die Suchschleife durch n = GetRandom(1, b): Dann zieht er nur noch zufällig.

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