所以我正在尝试动画常见的难题河内塔。我已经写了在控制台中执行此操作的算法,但我想制作一个JApplet,在我要求磁盘数量后弹出并动画正在解决的难题。如果有帮助,这是我的算法代码。只是寻找一些指令,不需要写出整个代码。谢谢。
这是我的算法代码。
public class TowerofHanoi extends JFrame{
static int count= 0;
public void move(int n, String start, String auxiliary, String end) {
if (n == 1) {
count++;
System.out.println(start + " -> " + end);
} else {
count++;
move(n - 1, start, end, auxiliary);
System.out.println(start + " -> " + end);
move(n - 1, auxiliary, start, end);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
TowerofHanoi towersOfHanoi = new TowerofHanoi();
System.out.print("Enter number of discs: ");
Scanner scanner = new Scanner(System.in);
int discs = scanner.nextInt();
towersOfHanoi.move(discs, "A", "B", "C");
System.out.println("This puzzle took "+count+" moves.");
}
public void paint(Graphics g) {
g.drawRect (10, 10, 200, 200);
}
public TowerofHanoi(){
setPreferredSize(new Dimension(WIDTH, HEIGHT));
}
}
这是我的JApplet代码。
public class Graphics_TOH {
public static void main(String[] args) {
// TODO Auto-generated method stub
JFrame frame = new JFrame ("Draw Person");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
TowerofHanoi panel = new TowerofHanoi ();
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
这个问题实际上对我来说有点有趣,因为它与我最讨厌的事情有关——大多数编程语言依赖调用堆栈的方式使得重用你的那个漂亮的小mobile()
函数变得太难了。
要制作这种动画,您需要:
当然,对你来说棘手的部分是第3步。
假设你想每秒画一个动作。刷新发生了,你得到当前时间,发现从动画开始到现在已经4.234秒了。你计算出你进入第4步是0.234秒,所以你想在第4步的23.4%的时间里画出来。
为了做到这一点,您需要知道:在移动4期间,哪些磁盘在哪些钉上是静态的,哪些磁盘正在移动,它的源钉和它的目标钉。
修复你的递归移动函数来跟踪所有这些是很容易的,但是因为它会一次生成所有的移动,所以没有办法让它具体告诉你移动4。
要解决这个问题,您基本上有3个选择:
a)你可以在开始时调用你的移动函数,并让它记录所有的移动。然后当你不得不画画时,你可以通过记录的移动前进到正确的移动。这很容易,而且可能很实用。当然,记录一个大谜题的所有移动需要大量内存(20张光盘的1M条目),但是制作动画也需要大量时间(一次移动/秒1周),所以你无论如何都不会制作大谜题的动画。
b)你可以在一个单独的线程中执行递归移动函数,该线程在每次重绘时与渲染线程交换信息。这实际上是多人游戏中非常常见的等待,无论如何,游戏状态都必须“实时”跟踪。对你来说,这比它的价值更麻烦。
c)你可以用非递归的方式重写你的hanoi解决方案。然后你可以实现一个迭代器。这很像解决方案(a),但是你会在必要时推进迭代器生成新的动作,而不是迭代预先记录的动作。好处是你不需要提前生成和存储所有的动作。
如果是我,我会做解决方案(c),因为我很乐意将递归解决方案转换为具有单独堆栈的迭代解决方案。如果你对这种事情不满意,那么你可能想做do(a)并将所有移动塞入ArrayList。
作为准备措施,创建“磁盘”类型的对象,将信息保存在它们所在的编号磁盘上,然后它们的“x”和“y”将根据它们所在的塔和所在的编号来决定。
要使整个过程动画化,您需要创建所有移动的堆栈,并在递归调用之间到达sysout时继续推动它。
堆栈完成后,稍后将其提取出来,弹出它并读取哪个磁盘何时移动到哪个塔。在磁盘中,您将有一个更改它的塔号的功能。
磁盘数组将显示自身,对于每个磁盘,您都包含一个show()函数,其中您在指定的塔上绘制矩形乘以一个常数并在一定的高度,这里为了省略复杂性,省略了高度的变化。
这就是你所需要的。从字面上看,只需将上述行转换为代码,你就完成了。如果你想要适当的高度,你必须创建3个堆栈来存放磁盘,并在从存储所有移动的主堆栈中提取信息时弹出和推送。显示功能现在将包括有关塔及其堆栈位置的参数,这就是你要绘制矩形的地方。
受您的问题启发,我编写了高度易于理解的JavaScript代码(运行在p5. js框架上),它将完全教您如何在河内塔的简单程序中处理磁盘。
访问此链接:我的素描
var amount =22;
Stack = [];
disks = [];
Sos= [];
A = [];
B = [];
C = [];
var frR =5;
var slider;
function Disk(n){
this.n=n;
this.tower=1;
this.x=10+this.tower*200;
this.y=100+n*12;
this.show = function(i,j){
rectMode(CENTER);
fill(map(n,0,amount,1,350),260,260);
rect(-100+(j+1)*350,300-i*12,10 +n*20,10);
}
}
function setup() {
createCanvas(1200, 600);
slider=createSlider(1,80,frR);
slider.position(20,400);
Hanoi(amount,1,2,3);
frameRate(frR);
colorMode(HSB);
Sos.push(A);
Sos.push(B);
Sos.push(C);
for(var i =0 ; i < amount; i++){
disks.push(new Disk(i));
}
for(var i =amount-1 ; i >=0; i--){
Sos[0].push(disks[i]);
}
}
function draw(){
if(frameCount < Stack.length)
drawIt();
}
function drawIt() {
background(0);
frR=slider.value();
frameRate(frR);
push();
fill(255);
noStroke();
text("Select Speed :",20,380);
textSize(20);
text("Steps taken : " + frameCount+ " / " + Stack.length + " ." ,450,450);
text("[ " + amount + " disks ]", 520,490);
rect(500,308,1800,2);
for(var j =0 ; j< 3; j++)
rect(-100+(j+1)*350, 188,2,240);
pop();
for(var j =0 ; j< 3; j++){
for(var i =0 ; i < Sos[j].length; i++){
Sos[j][i].show(i,j);
}
}
var current = Stack[frameCount-1];
Sos[current[1]-1].pop();
Sos[current[2]-1].push(disks[current[0]]);
}
function Hanoi(n, from, to , via)
{
if (n==0) return;
Hanoi(n-1, from, via , to);
//createP(n + " from" + from + " to " + to);
Stack.push([n,from,to]);
Hanoi(n-1, via, to , from);
}