Turm von Hanoi

ftComputing : Programme für die fischertechnik-Interfaces und -konstruktionskästen
  
ftComputing.de
Home
Back
Sitemap
Index
Links
Impressum
Mail

 

Java-Lösung für den Turm von Hanoi

Der Hanoi Robot ist ein Eigenbau der auf dem Schweißroboter-Chassis des Computing Starter Kits basiert. Er entspricht hier weitgehend dem Hanoi Robot von 1984. Allerdings wurde die Technik modernisiert :

M1 : Säulenmotor mit Impulsrad und Endtaster an I1, Impulstaster an I2

M2 : Armmotor mit Endtaster oben I3 und unten I4

M3 : Gefedert gelagerter Magnet (neuere Version bei Knobloch : 32363 - 17,20 Euro)

Gestapelt werden die Eisenscheiben, die im Bild vorn (PosA) neben dem Winkel liegen. Verdrahtet wurde "fliegend". Der Magnet ist an einem Grundbaustein 15 befestigt, der wiederum durch zwei Stein 7.5 gestützt wird. Hier stecken im Grundbaustein zwei Stahlachsen mit geriffelten Enden. Man kann aber auch zwei schwarze Klipp-Achsen 45 nehmen, die man so verdreht, dass sie klemmen, die Feder auf der Achse dient der Schönheit, es geht auch ohne sie.

Downloads : workSpace34.zip
Lösung für Java 6 - BlueJ und das Robo Interface und ftcomputing.robo.jar.
Siehe auch "Über das Scheibenschichten"

Es werden zwei Lösungen vorgestellt :
- Eine BlueJ-typische Experimentierversion mit einer Klasse die von FishFace abgeleitet wurde. Ohne void main() dafür sind aber alle Klassen public
- Eine Produktionsversion mit void main() und einer inneren Klasse - von FishFace abgeleitet - die die Zugriffe auf das Robo Interface kapselt. Nur public static main() ist öffentlich, alle anderen Methoden sind private.

Klasse HanoiTower Teil 1

import ftcomputing.robo.*;
public class HanoiTower extends FishFace {
  public final int mSaule = Mot.M1;
  public final int mArm = Mot.M2;
  public final int iArmOben = Inp.I3;
  public final int iArmUnten = Inp.I4;
  public final int mGreifer = Mot.M3;
  public int robPos;

  public HanoiTower() {
    openInterface(0, 0);
    System.out.println("HanoiTower USB-Verbindung OK");
  }
  public void action() {
    System.out.println("Fährt auf Grundstellung");
    grundstellung();
    System.out.println("Bei der Arbeit");
    hanoi(3, 5, 45, 75);
    ende();
  }
  public void hanoi(int n, int pAnf, int pMit, int pEnd) {
    if(n == 1) {
      ziehe(pAnf, pEnd);
    } else {
        hanoi(n-1, pAnf, pEnd, pMit);
        ziehe(pAnf, pEnd);
        hanoi(n-1, pMit, pAnf, pEnd);
    }
  }

Die Klasse HanoiTower wird mit extends FishFace definiert. Dadurch wird der Zugriff auf das Robo Interface noch einfacher. Im Konstruktor wird schon mal eine Verbindung zum Interface hergestellt. action() ist die Methode der Wahl, wenn man das ganze Programm "auf einen Rutsch"  starten will. Wenn man es genauer wissen will, kann man die Methoden von Teil 2 einzeln - und immer schön in vernünftiger Reihenfolge - aufrufen. 
hanoi(3, 5, 45, 75); enthält die eigentliche Hanoi-Logik mit zweimal - recht unscheinbar - ziehe(..) um den Robot zur Arbeit zu bewegen.

Klasse HanoiTower Teil 2

public void ende() {
  closeInterface();
  System.out.println("USB-Verbindung beendet");
}
public void grundstellung() {
  setMotor(mGreifer, Dir.Off);
  armHeben();
  robPos = 9999;
  sauleNach(0);
  System.out.println("Auf Grundstellung");
}
public void ziehe(int von, int nach) {
  sauleNach(von);
  armSenken();
  setMotor(mGreifer, Dir.On);
  armHeben();
  sauleNach(nach);
  armSenken();
  setMotor(mGreifer, Dir.Off);
  armHeben();
}
public void sauleNach(int zielPos) {
  if(robPos < zielPos) 
    setMotor(mSaule, Dir.Right, Speed.Full, zielPos-robPos);
  else 
    setMotor(mSaule, Dir.Left, Speed.Full, robPos-zielPos);
  waitForMotors(0, mSaule);
  robPos = zielPos;
}
public void armSenken() {
  setMotor(mArm, Dir.Left);
  waitForInput(iArmUnten);
  setMotor(mArm, Dir.Off);
}
public void armHeben() {
  setMotor(mArm, Dir.Right);
  waitForInput(iArmOben);
  setMotor(mArm, Dir.Off);
}}

Mit grundstellung() wird der Magnet abgeschaltet, der Arm angehoben und die Säule auf Home gefahren. Mit robPos = 9999 wird sauleNach() eine -fiktive - Position weit von Home vorgegaukelt.

ziehe(..) fährt den Robot von der aktuellen Position zur Hol-Position, armSenken(), Scheibe packen, armHeben() ...

sauleNach() fährt eine Position ab Home an. Die Positionsangabe erfolgt in Impulsen ab Home. Intern wird anhand der aktuellen Position die zu fahrende Position bestimmt und angefahren setMotor(...) - waitForMotors(..). Anschließend noch robPos auf den neuen Wert gesetzt.

Klasse HanoiTowerMain

import ftcomputing.robo.*;
public class HanoiTowerMain {
  HanoiRobot hr;
  public static void main() {
    HanoiTowerMain ht = new HanoiTowerMain();
    ht.Action();
  }
  private void Action() {
    hr = new HanoiRobot();
    System.out.println("HanoiTower wird gestartet");
    hr.openInterface(0, 0);
    System.out.println("Fährt auf Grundstellung");
    hr.grundstellung();
    System.out.println("Bei der Arbeit");
    hanoi(3, 5, 45, 75);
    System.out.println("--- FINIS ---");
    hr.closeInterface();
  }
  private void hanoi(int n, int pAnf, int pMit, int pEnd) {
    if(n == 1) {
      hr.ziehe(pAnf, pEnd);
    } else {
        hanoi(n-1, pAnf, pEnd, pMit);
        hr.ziehe(pAnf, pEnd);
        hanoi(n-1, pMit, pAnf, pEnd);
      }
  }
Die Klasse HanoiTower umfaßt die Methode Action() für die "Rahmenhandlung und die Methode hanoi() für die Hanoi-Logik. Der Zugriff auf den Hanoi-Robot erfolgt über die innere Klasse HanoiRobot, die hier auch instanziert wird. static main() dient dem Start des Console-Programmes.

In Methode Action() wird die Verbindung zum Robo Interface (openInterface, erstes an USB) hergestellt und der Robot auf grundstellung() gefahren. Dann folgt die - rekursive - Hanoi-Lösung und noch das closeInterface().

Zur Hanoi-Logik siehe das Scheibenschichten, hier nur der Vermerk der Robot wird über die harmlose Methode ziehe() der Klasse HanoiRobot bewegt. Die angegebenen Parameter beim Aufruf von hanoi(..) stehen für die Anzahl Scheiben und die drei Scheiben-Positionen. Gezählt wird dabei ab Home in Impulsen.

Klasse HanoiRobot

Interne Klasse von HanoiTowerMain abgeleitet von FishFace.

private class HanoiRobot extends FishFace {
  private final int mSaule = Mot.M1;
  private final int mArm = Mot.M2;
  private final int iArmOben = Inp.I3;
  private final int iArmUnten = Inp.I4;
  private final int mGreifer = Mot.M3;
  private int robPos;

  public void grundstellung() {    ...  }
  public void ziehe(int von, int nach) {    ...  }
  private void sauleNach(int zielPos) { ... }
  private void armSenken() { ... }
  private void armHeben() { ... }
    setMotor(mArm, Dir.Right);
    waitForInput(iArmOben);
    setMotor(mArm, Dir.Off);
  }
}}
Zuerst symbolische Konstanten für die beteiligten Interface-Anschlüsse, zusätzlich die Variable robPos mit der aktuellen Position der Robot-Säule. Gezählt wird in Impulsen ab Home (Endtaster an I1).

Ansonsten wie schon bei HanoiTower gehabt.

Hier eine  Lösung für den Robo TX Controller 

Stand : 18.04.2010