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.
== Das Affenpuzzle (Hexenpuzzle) ==
Legen Sie die Karten in einem Feld 3x3 Karten so, dass alle Teile zusammenpassen.
Auf Karten Klicken, um sie zu drehen. Drag und Drop, um sie zu verschieben.
'''Hinweis: Die Zahlen auf den Karten dienen nur der späteren Adressierung bei einer Lösung durch den Computer - die Lesbarkeit gibt absichtlich keine Rückschlüsse auf eine korrekte Lösung.'''
<html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js" integrity="sha512-uto9mlQzrs59VwILcLiRYeLKPPbS/bT71da/OEBYEwcdNUk8jYIy+D176RYoop1Da+f9mvkYrmj5MCLZWEtQuA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css" integrity="sha512-aOG0c6nPNzGk+5zjwyJaoRUgCdOrfSDhmMID2u4+OIslr0GjpLKo7Xm0Ao3xmpM4T8AmIouRkqwj1nrdVsLKEQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<style>
.card {
position:absolute;
width:128px;
height:128px;
border:1px solid black;
background-image:url(https://programmingwiki.de/images/5/50/Admin_Hexenpuzzle.png);
background-size: 310% 310%;
box-shadow:2px 2px 8px rgba(0,0,0,0.2);
flex-shrink:0;
cursor:pointer;
-webkit-transition: -webkit-transform .2s ease-in-out;
-ms-transition: -ms-transform .2s ease-in-out;
transition: transform .2s ease-in-out;
}
.card0 {
left:0;
top:0;
background-position: 0.5% 1%;
}
.card1 {
left:128px;
top:0;
background-position: 49.5% 1%;
}
.card2 {
left:256px;
top:0;
background-position: 98% 1%;
}
.card3 {
background-position: 0.5% 49.5%;
left:0;
top:128px;
}
.card4 {
background-position: 49.5% 49.5%;
left:128px;
top:128px;
}
.card5 {
background-position: 98% 49.5%;
left:256px;
top:128px;
}
.card6 {
background-position: 0.5% 98%;
left:0;
top:256px;
}
.card7 {
background-position: 49.5% 98%;
left:128px;
top:256px;
}
.card8 {
background-position: 98% 98%;
left:256px;
top:256px;
}
.card.dragging {
box-shadow:4px 4px 10px rgba(0,0,0,0.5);
}
.card.rotate0 {
}
.card.rotate90 {
transform:rotate(90deg);
}
.card.rotate180 {
transform:rotate(180deg);
}
.card.rotate270 {
transform:rotate(270deg);
}
.containerCards{
position:relative;
background:#f3f3f3;
border:1px solid black;
width:100%;
height:800px;
display:flex;
flex-wrap:wrap;
}
</style>
<div class="containerCards">
<div class="card card0 rotate0"></div>
<div class="card card1 rotate0"></div>
<div class="card card2 rotate0"></div>
<div class="card card3 rotate0"></div>
<div class="card card4 rotate0"></div>
<div class="card card5 rotate0"></div>
<div class="card card6 rotate0"></div>
<div class="card card7 rotate0"></div>
<div class="card card8 rotate0"></div>
</div>
<script>
$('.card').draggable({ containment: "parent", grid: [ 32, 32 ],
start: function() { $(this).addClass("dragging");},
stop: function() { $(this).removeClass("dragging");},
drag: function(e, ui) {
}
});
$('.card').on('mousedown', function(event) {
$('.card').css('z-index','1');
$( this ).css('z-index','1000');
});
$('.card').on('click', function(event) {
if($(this).hasClass("dragging")) return;
if($(this).hasClass("rotate0"))
$(this).removeClass("rotate0").addClass("rotate90");
else
if($(this).hasClass("rotate90"))
$(this).removeClass("rotate90").addClass("rotate180");
else
if($(this).hasClass("rotate180"))
$(this).removeClass("rotate180").addClass("rotate270");
else
if($(this).hasClass("rotate270"))
$(this).removeClass("rotate270").addClass("rotate0");
});
</script>
</html>
<html>
<style>
.hexe { display:none}
.hexe img { max-width:100%; width:500px; height:auto}
</style>
</html>
<div class="hexe">
[[Bild:Admin_Hexenpuzzle.png]]
</div>
== 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 Werte = {
KopfRot: 1, BauchRot:2,
KopfGelb: 4, BauchGelb:5,
KopfBlau: 7, BauchBlau:8,
KopfGruen:10, BauchGruen:11
}
// die 9 Karten wie im Bild oben angezeigt als Listen:
var Karten = [];
Karten[0] = [Werte.KopfRot,Werte.BauchGruen,Werte.BauchGelb,Werte.KopfBlau];
Karten[1] = [Werte.KopfGruen,Werte.BauchRot,Werte.BauchBlau,Werte.KopfBlau];
Karten[2] = [Werte.BauchRot,Werte.BauchBlau,Werte.KopfGruen,Werte.KopfBlau];
Karten[3] = [Werte.KopfGelb,Werte.BauchRot,Werte.BauchGelb,Werte.KopfGruen];
Karten[4] = [Werte.KopfGelb,Werte.BauchGruen,Werte.BauchRot,Werte.KopfBlau];
Karten[5] = [Werte.BauchRot,Werte.KopfGruen,Werte.KopfBlau,Werte.BauchGelb];
Karten[6] = [Werte.BauchBlau,Werte.BauchGelb,Werte.KopfGruen,Werte.KopfRot];
Karten[7] = [Werte.KopfRot,Werte.BauchGruen,Werte.BauchGelb,Werte.KopfGruen];
Karten[8] = [Werte.BauchBlau,Werte.BauchRot,Werte.KopfBlau,Werte.KopfGelb];
var loesungen = 0;
var tests = 0;
var Kartenfeld = [];
var Platziert = [];
// Initialisierung mit leeren Daten
for(var i=0; i < 9; i++){
Kartenfeld[i] = -1;
Platziert[i] = false;
}
// die eigentliche Suchfunktion ist rekursiv und ruft sich selbst mit der nächsten Karte auf
function suchen(feld,gesetzt,platz){
for(var k=0; k < Karten.length; k++){
if(!gesetzt[k]){
feld[platz] = k;
gesetzt[k] = true;
for(var r=0; r<4; r++){
tests++;
if(kartePruefen(feld,platz)){
if(platz == Karten.length-1){
loesungen++;
console.log("Lösung:\n"+spielfeldAusgeben(feld));
}
suchen(feld,gesetzt,platz+1);
}
// Karten drehen, indem die 4 Zahlen der Karte um eine Stelle verschoben werden
var p = Karten[k].shift();
Karten[k].push(p);
}
feld[platz] = -1;
gesetzt[k]=false;
}
}
}
// für eine Karte wird getestet, ob alle Nachbarkarten daran passen
// wurde noch keine Nachbarkarte bestimmt (-1) dann akzeptieren wir die Karte
// feld = alle Karten auf dem Spielfeld
// platz = position der Karte auf dem Spielfeld, die getestet wird (0 1 2 / 3 4 5 / 6 7 8)
function kartePruefen(feld, platz){
if(platz == 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(platz == 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(platz == 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(platz == 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(platz == 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(platz == 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(platz == 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(platz == 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(platz == 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;
}
}
// 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){
for(k in Werte){
if(Werte[k] == n) {
var s = k;
while (s.length < 10) s = s + " ";
return s;
}
}
}
var s = "";
var zeilen = Math.round(Math.sqrt(Kartenfeld.length));
for(var i=0; i < Kartenfeld.length; i++){
s += "[ Karte "+Kartenfeld[i] +" ↑ "+lesbar(Karten[Kartenfeld[i]][0])+" ] ";
if(i % zeilen == zeilen-1) s += "\n";
}
return (s);
}
suchen(Kartenfeld,Platziert,0);
console.log("Es wurden "+tests+" Tests gemacht und "+loesungen+" Lösungen gefunden.");
</run>
<div class="hexe">
[[Bild:Admin_Hexenpuzzle.png]]
</div>