Affenpuzzle

Aus ProgrammingWiki

Wiki-Quelltext dieser Seite.

<language>Javascript</language>
Das Affenpuzzle (oder auch Hexenpuzzle) ist ein kombinatorisches Rätsel. Es besteht aus 9, 16, 25 oder mehr Karten die in einer Matrix angeordnet werden. Ziel des Spiels ist es die Karten so zu legen und zu drehen, das alle Teile (hier der Hexen) zusammenpassen.

[[Bild:Admin_Hexenpuzzle.png]]

== Lösung mit dem Computer ==
Würde man alle möglichen Kombinationen durchprobieren, wären das bei 3x3 bereits 95126814720 Kartenstellungen. Auch mit einem leistungsstarken Computer dauert das mehrere Stunden, diese alle durchzuprobieren.

Zum Glück müssen wir aber nicht alle Kombinationen ausprobieren. Sollte schon die erste zur zweiten Karte nicht passen, müssen wir die restlichen Kombinationen mit diesem Anfang ja gar nicht weiterverfolgen. Der nachfolgende Algorithmus nutzt das aus und legt zunächst eine Karte aufs Feld. Bei der zweiten Karte wird bereits geprüft, ob diese zur ersten passt und nur dann mit der Suche fortgesetzt.

<run id="5d9f6eb55a360">
// wir definieren die 8 verschiedenen Bilder mit Zahlen
// die Abstände der Zahlen sind so gewählt, dass wir durch Subtraktion 
// immer einen Abstand von 1 haben, wenn die beiden Teile zusammenpassen

var KopfRot = 1, BauchRot = 2;
var KopfGelb = 4, BauchGelb = 5;
var KopfBlau = 7, BauchBlau = 8;
var KopfGruen = 10, BauchGruen = 11;

// die 9 Karten wie im Bild oben angezeigt als Listen:

var Karten = [];
Karten[0] = [KopfRot,BauchGruen,BauchGelb,KopfBlau];
Karten[1] = [KopfGruen,BauchRot,BauchBlau,KopfBlau];
Karten[2] = [BauchRot,BauchBlau,KopfGruen,KopfBlau];

Karten[3] = [KopfGelb,BauchRot,BauchGelb,KopfGruen];
Karten[4] = [KopfGelb,BauchGruen,BauchRot,KopfBlau];
Karten[5] = [BauchRot,KopfGruen,KopfBlau,BauchGelb];

Karten[6] = [BauchBlau,BauchGelb,KopfGruen,KopfRot];
Karten[7] = [KopfRot,BauchGruen,BauchGelb,KopfGruen];
Karten[8] = [BauchBlau,BauchRot,KopfBlau,KopfGelb];

// Ausgabe einer Lösung - die Karten wie im Bild oben sind durchnummeriert 
// von links nach rechts bzw. oben nach unten
// Ausgabe der Karte als Zahl + welches Bild nach oben zeigen muss.
function spielfeldAusgeben(Kartenfeld){
  function lesbar(n){
    if(n == KopfRot)    return "KR";
    if(n == BauchRot)   return "BR";
    if(n == KopfGelb)   return "KG";
    if(n == BauchGelb)  return "BG";
    if(n == KopfBlau)   return "KB";
    if(n == BauchBlau)  return "BB";
    if(n == KopfGruen)  return "KG";
    if(n == BauchGruen) return "BG";
  }
  var s = "";
  var zeilen = Math.round(Math.sqrt(Kartenfeld.length));

  for(var i=0; i < Kartenfeld.length; i++){
    s += "[ "+Kartenfeld[i] +"("+lesbar(Karten[Kartenfeld[i]][0])+") ]";
    if(i % zeilen == zeilen-1) s += "\n";
  }
  return (s);
}

// für eine Karte wird getestet, ob die Nachbarkarten daran passen
// wurde noch keine Nachbarkarte bestimmt (-1) dann akzeptieren wir die Karte
function kartePruefen(nummer, feld){ 
 if(nummer == 0){
   if(feld[1] != -1 && Math.abs(Karten[feld[0]][1] - Karten[feld[1]][3]) != 1) return false;
   if(feld[3] != -1 && Math.abs(Karten[feld[0]][2] - Karten[feld[3]][0]) != 1) return false;
   return true;
 } 
 if(nummer == 1){
   if(feld[2] != -1 && Math.abs(Karten[feld[1]][1] - Karten[feld[2]][3]) != 1) return false;
   if(feld[4] != -1 && Math.abs(Karten[feld[1]][2] - Karten[feld[4]][0]) != 1) return false;
   if(feld[0] != -1 && Math.abs(Karten[feld[1]][3] - Karten[feld[0]][1]) != 1) return false;
   return true;
 } 
 if(nummer == 2){
   if(feld[5] != -1 && Math.abs(Karten[feld[2]][2] - Karten[feld[5]][0]) != 1) return false;
   if(feld[2] != -1 && Math.abs(Karten[feld[2]][3] - Karten[feld[1]][1]) != 1) return false;
   return true;
 }
 if(nummer == 3){
   if(feld[0] != -1 && Math.abs(Karten[feld[3]][0] - Karten[feld[0]][2]) != 1) return false;
   if(feld[4] != -1 && Math.abs(Karten[feld[3]][1] - Karten[feld[4]][3]) != 1) return false;
   if(feld[6] != -1 && Math.abs(Karten[feld[3]][2] - Karten[feld[6]][0]) != 1) return false;
   return true;
 }
 if(nummer == 4){
   if(feld[1] != -1 && Math.abs(Karten[feld[4]][0] - Karten[feld[1]][2]) != 1) return false;
   if(feld[5] != -1 && Math.abs(Karten[feld[4]][1] - Karten[feld[5]][3]) != 1) return false;
   if(feld[7] != -1 && Math.abs(Karten[feld[4]][2] - Karten[feld[7]][0]) != 1) return false;
   if(feld[3] != -1 && Math.abs(Karten[feld[4]][3] - Karten[feld[3]][1]) != 1) return false;
   return true;
 }
 if(nummer == 5){
   if(feld[2] != -1 && Math.abs(Karten[feld[5]][0] - Karten[feld[2]][2]) != 1) return false;
   if(feld[8] != -1 && Math.abs(Karten[feld[5]][2] - Karten[feld[8]][0]) != 1) return false;
   if(feld[4] != -1 && Math.abs(Karten[feld[5]][3] - Karten[feld[4]][1]) != 1) return false;
   return true;
 }
 if(nummer == 6){
   if(feld[3] != -1 && Math.abs(Karten[feld[6]][0] - Karten[feld[3]][2]) != 1) return false;
   if(feld[7] != -1 && Math.abs(Karten[feld[6]][1] - Karten[feld[7]][3]) != 1) return false;
   return true;
 }
 if(nummer == 7){
   if(feld[4] != -1 && Math.abs(Karten[feld[7]][0] - Karten[feld[4]][2]) != 1) return false;
   if(feld[8] != -1 && Math.abs(Karten[feld[7]][1] - Karten[feld[8]][3]) != 1) return false;
   if(feld[6] != -1 && Math.abs(Karten[feld[7]][3] - Karten[feld[6]][1]) != 1) return false;
   return true;
 } 
 if(nummer == 8){
   if(feld[5] != -1 && Math.abs(Karten[feld[8]][0] - Karten[feld[5]][2]) != 1) return false;
   if(feld[7] != -1 && Math.abs(Karten[feld[8]][3] - Karten[feld[7]][1]) != 1) return false;
   return true;
 }
}

function karteDrehen(k){
  var p = k.shift(); // erstes Element wegnehmen
  k.push(p); // hinten wieder einfügen
}

var loesungen = 0;
var tests = 0;
var Kartenfeld = [];
var gesetzt = [];

for(var i=0; i < 9; i++){
  Kartenfeld[i] = -1;
  gesetzt[i] = false;
}

// die eigentliche Suchfunktion ist rekursiv und ruft sich selbst mit der nächsten Karte auf
function suchen(Feld){
  for(var k=0; k < Karten.length; k++){
    if(!gesetzt[k]){
      Kartenfeld[Feld] = k;
      gesetzt[k] = true;
      for(var r=0; r<4; r++){
        tests++;
	if(kartePruefen(Feld,Kartenfeld)){
          if(Feld == Karten.length-1){
            loesungen++;
            console.log("Lösung:\n"+spielfeldAusgeben(Kartenfeld));
          }
          suchen(Feld+1);
        }
        karteDrehen(Karten[k]);
      }
      Kartenfeld[Feld] = -1;
      gesetzt[k]=false;
    }
  }
}

suchen(0);

console.log("Es wurden "+tests+" Tests gemacht und "+loesungen+" Lösungen gefunden.");
</run>