Frage von danmii, 34

Ich brauche dringend Hilfe bei einer Perlaufgabe. Kann mir jemand helfen?

Guten Tag!

Meine Aufgabe ist es das Spiel: Türme von Hanoi zu simulieren. Bei dem Spiel gibt es 3 Stäbe (A, B und C) und "n" Scheiben. Das Ziel ist es alle Scheiben die am Anfang auf Stab A gestapelt sind auf Stapel C zu bekommen. Dabei ist zu beachten, dass immer nur eine Scheibe bewegt werden darf und nie eine Scheibe auf einer anderen Scheibe liegen darf, wenn die oben liegende Scheibe größer ist. (Also nur kleine Scheiben auf größere) Ich habe folgendes Codebeispiel gefunden: ############################################################# #!/usr/bin/perl -w use strict; use warnings;

print("Wie viele Scheiben sollen bei dem Spiel: Türme von Hanoi verwendet werden?\n");

my $Scheibenanzahl = ; chomp $Scheibenanzahl;
if (!($Scheibenanzahl =~ /^[0-9]+$/)) { print "Das war keine Zahl\n"; }

sub move { my $anzahl = shift; my $stab_a = shift; my $stab_b = shift; my $stab_c = shift;
if ($anzahl > 0) { move($anzahl - 1, $stab_a, $stab_c, $stab_b);
move($anzahl - 1, $stab_b, $stab_a, $stab_c); } }

move($Scheibenanzahl, 'Turm A', 'Turm B', 'Turm C'); ############################################################# Dieses gibt die erledigten Schritte direkt aus. Mein Ziel ist es diese Schritte grafisch darzustellen...

Etwa so:

    |

   ||||           ||

  |||||||       |||||||||

--------- --------- ----------

Habt ihr einen Lösungsvorschlag oder ein Konzept was mich schnell zum gewünschten Ergebnis bringt? Ich komme nicht weiter.

Vielen Dank schonmal im Vorraus! MFG DAN

Antwort
von ralphdieter, 21

Dein Programm gibt überhaupt keinen Schritt aus. In der Funktion move fehlt der Teil, der tatsächlich eine Scheibe bewegt. so ruft sie nur sich selbst auf und macht im Endeffekt nichts.

Ich denke, dass die Stäbe die Information enthalten müssen, welche Scheiben gerade darauf liegen. Der Algorithmus wird dann etwa so gestartet:

move($Scheibenanzahl, '54321', '', '');

Um solche Stäbe graphisch auszugeben, würde ich so vorgehen:

$stab_a = "542";
$stab_b = "";
$stab_c = "31";

for (my $i=$Scheibenanzahl-1; $i>=0; --$i)
{
 printf( " %*s : %*s : %*s\n"
, $Scheibenanzahl, $i<length $stab_a ? "X" x substr($stab_a,$i,1) : "|"
, $Scheibenanzahl, $i<length $stab_b ? "X" x substr($stab_b,$i,1) : "|"
, $Scheibenanzahl, $i<length $stab_c ? "X" x substr($stab_c,$i,1) : "|"
);
}

Sieht dann etwa so aus:

     | :     | :     |
| : | : |
XX : | : |
XXXX : | : X
XXXXX : | : XXX

Das lässt sich sicher noch kompakter schreiben und graphisch aufpeppen...

Kommentar von danmii ,

Wie würde diese Variante denn in meinem Code aussehen?

Kommentar von ralphdieter ,

Klatsche die for-Schleife einfach in die move()-Funktion rein, direkt hinter die beiden rekursiven move()-Aufrufe.

Zwischen den beiden fehlt allerdings noch der Code zum Bewegen eines Steins...

Kommentar von danmii ,

Also in etwa so? :)

Kommentar von danmii ,

print("Wie viele Scheiben sollen bei dem Spiel: Türme von Hanoi verwendet werden?\n");
my $Scheibenanzahl = ;
chomp $Scheibenanzahl;
if (!($Scheibenanzahl =~ /^[0-9]+$/))
{
print "Das war keine Zahl\n";
}

sub move {
my $anzahl = shift; my $stab_a = shift; my $stab_b = shift; my $stab_c = shift;
for (my $i=$Scheibenanzahl-1; $i>=0; --$i) {
 printf "%*s : %*s : %*s\n"

, $Scheibenanzahl, .......

...

      );

}

if ($anzahl > 0) {
move($anzahl - 1, $stab_a, $stab_c, $stab_b);
print("Verschiebe oberste Scheibe von $stab_a nach $stab_c \n");
move($anzahl - 1, $stab_b, $stab_a, $stab_c);
     }
}

move($Scheibenanzahl, '54321', '', '');

Kommentar von danmii ,

Hast du einen Vorschlag wie ich am besten die Steine verschieben könnte?

Kommentar von danmii ,

Ich wäre dir wirklich unglaublich dankbar wenn du den kompletten Code geben würdest. Ich stehe einfach gerade etwas auf dem Schlauch :(

Kommentar von ralphdieter ,

Ich stehe einfach gerade etwas auf dem Schlauch :(

Darf ich raten: Du hast keinen Plan :-/

Ich habe mal 'rumprobiert: @board ist ein globales Array, das die 3 Stäbe als Strings enthält. move() erhält nur noch die Indizes darauf.

Zwischen den beiden rekursiven move() wird nun das globale board geändert (und ausgegeben). Das sieht dann fertig so aus:

@board = ( scalar(reverse 1..$Scheibenanzahl), '', '' );

sub move
{
my $anzahl = shift;
my $von = shift;
my $via = shift;
my $nach = shift;

if ($anzahl > 0)
{
move($anzahl-1, $von, $nach, $via);

$board[$nach] .= chop $board[$von];

for (my $i=$Scheibenanzahl-1; $i>=0; --$i)
{
printf( " %*s : %*s : %*s\n"
, $Scheibenanzahl, $i<length $board[0] ? "X" x substr($board[0], $i, 1) : "|"
, $Scheibenanzahl, $i<length $board[1] ? "X" x substr($board[1], $i, 1) : "|"
, $Scheibenanzahl, $i<length $board[2] ? "X" x substr($board[2], $i, 1) : "|"
);
}
print "\n";

move($anzahl-1, $via, $von, $nach);
}
}

move($Scheibenanzahl, 0..2);

Bei mir tut's — aber ich muss dazusagen, dass das mein erstes perl-Script ist. Ein Profi wird sich da wohl die Haare raufen.

Kommentar von ralphdieter ,

Eben fällt mir auf:

reverse 1..$Scheibenanzahl

arbeitet auf dem String der einzelnen Ziffern. Das geht bei mehr als 9 Scheiben natürlich in die Hose ("211101987654321"). Aber ich habe keine Ahnung, wie man in perl eine Liste aus Zahlen umdreht...

Antwort
von danmii, 28

Die Formatierung ist etwas misslungen... Die Darstellung soll drei Türme mithilfe von Textzeichen darstellen.

Keine passende Antwort gefunden?

Fragen Sie die Community