| |
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
|