//HAHAHA.java -- Graphics Hanoi, by tsaiwn@cs.nctu.edu.tw //javac HAHAHA.java //java HAHAHA //HTML: import java.io.*; import java.applet.*; import java.awt.*; import java.awt.event.*; import java.net.*; import java.util.*; public class HAHAHA extends Applet { static Frame f = null; static boolean isApplet = true; gHanoiOK ghok = null; ControlPanel cp = null; public static void main(String xxx[ ]) { isApplet = false; // from Java application f = new Frame("Tower of Hanoi"); HAHAHA hhh = new HAHAHA( ); f.add("Center", hhh); f.setSize(gHanoiOK.X_SIZE+10, gHanoiOK.Y_SIZE+50); //510,550 f.setVisible(true); f.addWindowListener(new WCC( )); //Then, Simulate the Applet behavior hhh.init( ); hhh.start( ); }//main public void init( ) { ghok = new gHanoiOK(f); cp = new ControlPanel(ghok); setLayout(new BorderLayout( ) ); add("Center", ghok); add("South", cp); // the Control Panel if(f!=null) { f.validate( ); f.setVisible(true); } ghok.init( ); ghok.start( ); }//init( public void start( ) { if(!isApplet) ghok.main( ); // is application else ghok.winHanoi(5); // isApplet }//start( public HAHAHA( ) { }// HAHAHA constructor }//class HAHAHA class gHanoiOK extends Panel implements MouseListener { //some constants for later use public static final int X_SIZE=500, Y_SIZE=500; public static final int Y_TOP = 28; public static final Color bgColor = Color.white; public static final int DELAY_TIME = 50; // 50/1000 second public static final int MOVE_STEP = 58; // 58 points public static final int MIN_STEP = 6; // 6 points public static final int MAX_STEP = 158; // 88 points public static final int MAX_DELAY = 88; // 88 milli sec public static final int MIN_DELAY = 1; public static Color haColor = Color.red; // variable public static boolean speedUp = false; // speed up? public static int delayTime = DELAY_TIME; public static int step = MOVE_STEP; int kkk=0; // used in paint( boolean alreadyDone = false; // used in Applet mode boolean isRunning = false; // doing Hanoi ? /// int nDisks = 0; // number of Disks int nPeg[ ] = new int[3]; // height of the 3 piles int xPosition[ ] = new int[3]; // central position of 3 pegs int aX=38, aY= (Y_SIZE - 50); int bX=(aX+(X_SIZE-50)/3), bY=aY; int cX=(bX+(X_SIZE-50)/3), cY=aY; int thick=22; // for disk thick int width=33; // for disk width int wUnit=2; // 128/nDisks == 2 Graphics bufferGraphics=null; // 用來畫在記憶體的圖 Image img=null; // The image that will contain everything that .. /// .. has been drawn on bufferGraphics. /// .. will be used in paint( ) Dimension dim=null; // To get the width and height of the applet. PrintStream cout=null; // 使其類似 C++ 的 BufferedReader cin = null; public void init( ) { addMouseListener(this); // handle MouseEvent if(f==null) cout = System.out; // from Applet }//init( public void start( ) { prepareIO( ); prepareImagePaper( ); }//start Frame f=null; //Panel myPanel; public static void main(String xxx[ ]) { Frame f = new Frame("Tower of Hanoi"); gHanoiOK gok = new gHanoiOK(f); f.add("Center", gok); f.setSize(X_SIZE, Y_SIZE); //500,500 f.setVisible(true); gok.init( ); // simulate Applet gok.start( ); gok.repaint( ); // force to initilize img gok.prepareIO( ); gok.main( ); }//main // constructors, one for from main( public gHanoiOK( Frame f ) { this.f = f; addMouseListener(this); // handle MouseEvent if(f==null) cout = System.out; // from Applet }// gHanoiOK public gHanoiOK( ) { // for Applet to call this addMouseListener(this); // handle MouseEvent } void prepareIO( ){ try{ cout = System.out; if(f==null) return; // is Applet cin = new BufferedReader( new InputStreamReader(System.in) ); }catch(Exception e) { System.exit(38); } }//prepareIO( void prepareImagePaper( ) { // Create an offscreen image to draw on , same size setVisible(true); dim = getSize( ); // obtain the width and height if(dim.width == 0) return; // not ready, maybe later img = createImage(dim.width, dim.height); bufferGraphics = img.getGraphics( ); // use Graphics methods to draw, this is double buffer return; }// prepareImagePaper // hanoi.c -- Hanoi tower, @CopyLeft by tsaiwn@csie.nctu.edu.tw //Hanoi Tower(河內之塔) -- 神奇的 Recursive -- 自己叫自己 ! //關於 Hanoi Tower, 請用 http://gogle.com 打入 "hanoi tower" 查詢 //注意用雙引號夾住如 "hanoi tower" 查到的才是你要的 :-) //或是直接看這 http://en.wikipedia.org/wiki/Tower_of_Hanoi /// C/C++ requires declaraction before use the function //void moveOne(int px, int py, int ); //3rd parameter is the disk# void hanoi(int n, int pa, int pc, int pb) { if(n==0) return; // there is no disk if(shouldDie) return; // my Master want me to die :-( hanoi(n-1, pa, pb, pc); // 先幫我把上面 n-1 disks 搬走 moveOne(pa, pc, n); /// move the n-th disk from pa to pc hanoi(n-1, pb, pc, pa); // move n-1 disks from pb to pc } // hanoi void moveOne(int px, int py, int n ) { // the n-th disk moveG(px, py, n); // Graphics version String p123[ ] = {"th", "st", "nd", "rd"}; // -th, Fir-st, // .. Seco-nd, 3-rd int k = n%10; if(k!=1 && k!=2 && k!=3) k = 0; // -th cout.printf("Move %d-%s disk from %c to %c \n", n, p123[k], px, py); }//moveOne( int getInt( ) { // static char buf[99]; String buf = null; int ans=0; // fgets(buf, sizeof(buf), stdin); ans=atoi(buf); try { buf = cin.readLine( ); ans = Integer.parseInt(buf); }catch(Exception e) { ans = 3; } // 如果把 ans = 3; 改為 ans = 64; 你認為會做多久? ? ? return ans; // 若一秒只能搬一個動作, // ..則需約 600 billion(六千億) (580+) years }// getInt( void haInit(int n) { nDisks = n; if(n==0) n=2; nPeg[0] = n; // initially all Disks on Peg-0 nPeg[1] = nPeg[2] = 0; // no Disks on Peg-1 and Peg-2 thick = (Y_SIZE -50)/n; // thick of each Disk if(thick > 20) thick = 20; // disk thick limit wUnit = 128/n; // Disk width = wUnit * Disk#number if(wUnit < 2) wUnit=2; // minimum 2 points xPosition[0] = aX + 64; // central position aX + 128/2 xPosition[1] = bX + 64; xPosition[2] = cX + 64; // clearTower( ); if(f != null) { f.validate( ); f.setVisible(true); } initDisks( ); // draw the initial Disks if(f != null) f.setVisible(true); // f.show( ); }// haInit( void doHanoi(int n) { if(dim == null || img==null || bufferGraphics == null) { prepareImagePaper( ); // create img paper if( bufferGraphics == null) return; // give up System.out.println("doHanoi.."); } if(n >= 65) return; shouldDie = false; isRunning = true; pause=false; haInit(n); hanoi(n, 'A', 'C', 'B'); // move n disks on peg 'A' to peg 'C' isRunning = false; }// doHanoi( int main( ) { // original main program in C Language int n; int firstRun = 1; while(true) { if(firstRun != 0) { firstRun = 0; f.setVisible(false); // hide the window }//if cout.print(" n=? "); n = getInt( ); // read an int into n if(n<0) break; f.setVisible(true); //f.requestFocus(true); doHanoi(n); try{Thread.sleep(58);}catch(Exception e) { } //f.toBack( ); //f.setVisible(false); // hide the window }//while f.dispose( ); cout.println("Bye bye..."); System.exit(0); return 0; }// int main( ) ////////////////////////// void winHanoi(int n) { // do hanoi from Applet/Window if(alreadyDone) return; // run only once alreadyDone = true; pause=false; shouldDie=false; System.out.println("applet paint..."); //doHanoi(n); // 不要這樣做, 會陷入很久的 ... //啟動另一個 thread Sister 去做才不會被卡住 new Sister(this, n).start( ); // this 就是目前這 class 的物件 ( gHanoiOK ) }// winHanoi( void clearTower( ){ //initial status if(dim == null || img==null || bufferGraphics == null) { repaint( ); // force to prepare img/bufgraphics if(dim==null) repaint( ); // try to prepare img again if(img==null) repaint( ); if(img==null) return; // still not READY yet } setBackground(Color.white); bufferGraphics.clearRect(0,0,dim.width,dim.width); bufferGraphics.setColor(Color.pink); bufferGraphics.fillRect(aX-3, aY, 128+6, 18); bufferGraphics.fillRect(bX-3, bY, 128+6, 18); bufferGraphics.fillRect(cX-3, cY, 128+6, 18); bufferGraphics.setColor(new Color(99, 58, 233)); bufferGraphics.setFont(new Font("Courier", Font.BOLD, 18) ); bufferGraphics.drawString( "Tower of Hanoi, by tsaiwn@csie.nctu.edu.tw",20,20); repaint( ); // have paint( ) to draw the image to screen try{ Thread.sleep(delayTime); }catch(Exception e){ } }//clearTower // rewrite update( ) : // Always required for good double-buffering. // This will cause the Applet not to first wipe off previous // ..drawings but to immediately repaint. // The wiping off may also causes flickering. // Update is called automatically when repaint( ) is called. // comment out the update( ) function then try to run again public void update(Graphics g) { paint(g); } // 直接叫 paint( ) public void paint(Graphics g) { if(dim == null || img==null || bufferGraphics == null) { // This is the first call to here. //System.out.println("paint...img null..."); prepareImagePaper( ); return; }//if g.drawImage(img, 0, 0, this); // 此時才畫到螢幕 //System.out.println(" paint "+ ++kkk); //for debug }//paint( void initDisks( ) { // draw the original Disks if( img==null || bufferGraphics == null) return; int x, y; for(int n= nDisks; n>=1; --n) { bufferGraphics.setColor(haColor); x = xPosition[0] - wUnit*n/2; y = aY - (nDisks -n+1)*thick; bufferGraphics.fillRect(x, y, wUnit*n, thick); }//for repaint( ); try{Thread.sleep(delayTime);}catch(Exception e){ } } void moveG(int px, int py, int n ) { // the n-th disk if(shouldDie) return; //if(img==null) return; // not ready yet if(dim == null || img==null || bufferGraphics == null){ repaint( ); if(dim == null || img==null ) repaint( ); if(bufferGraphics == null) repaint( ); if(bufferGraphics == null) return; // give up :-) } int from = px - 'A'; int to = py - 'A'; width = wUnit * n; // width of n-th disk if(width > 128) width = 128; // limit the width moveYUp(from, to, n); // 編號 n 的 Disk moveX(from, to, n); moveYDown(from, to, n); --nPeg[from]; // from == 0? 1? 2? ++nPeg[to]; //System.out.print("moveG--- "); }//moveG( void moveThere(int xs, int ys, int xt, int yt, int width, int thick) { if(shouldDie && dieIM) return; // my Master want me to die :-( checkPause( ); bufferGraphics.setColor(Color.white); // 擦掉 bufferGraphics.fillRect(xs, ys, width, thick); bufferGraphics.setColor(haColor); // 然後畫新的 bufferGraphics.fillRect(xt, yt, width, thick); repaint( ); // 顯示到螢幕 try{Thread.sleep(delayTime);}catch(Exception e){ } }//moveThere void moveYUp(int fm, int to, int n ) { // the n-th disk // fm, to : 0, 1, 2 int x,y, y1, y2; x = xPosition[fm] - width/2; y2 = Y_TOP; y1 = aY - nPeg[fm]*thick; // y-position of n-th disk // move y from y1 to y2 y = y1; while(y > y2+step) { int y0 = y; y -= step; moveThere(x, y0, x, y, width, thick); }//while( moveThere(x, y, x, y2, width, thick); //提起到正上方 }//moveYUp void moveX(int fm, int to, int n ) { // the n-th disk int x,y, x1, x2; y = Y_TOP; x1 = xPosition[fm] - width/2; x2 = xPosition[to] - width/2;; //boolean toRight = true; // move from Left to Right int rightLeft = 1; // 往右 ; fm ==> to if(x2 < x1) rightLeft = -1; // 往左 to <== fm x = x1; // move x from x1 to x2 while( 38==38 ) { // BUG fixed int xtemp = x + step * rightLeft; // 向右走向左走? if(rightLeft == 1) { // fm ====> to if(xtemp > x2) break; }else{ // to <====== fm if(xtemp < x2) break; } int x0 = x; x = xtemp; // 注意我們用臨時的 xtemp moveThere(x0, y, x, y, width, thick); }//while( moveThere(x, y, x2, y, width, thick); // 到目的堆正上方 }//moveX void moveYDown(int fm, int to, int n ) { // the n-th disk int x,y, y1, y2; x = xPosition[to] - width/2;; y1 = Y_TOP; y2 = aY - (nPeg[to]+1)*thick; // destination y = y1; // move y from y1 to y2 while(y < y2-step) { int y0 = y; y += step; moveThere(x, y0, x, y, width, thick); }//while( moveThere(x, y, x, y2, width, thick); // 到定位 }// moveYDown void checkPause( ) { while(true) { // Loop untill "Continue" if( !pause ) break; try{ Thread.sleep(18); }catch(Exception e){ } }//while }//checkPause( ////////////////////////////////////////////////////////// /// handling MouseEvent public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { step = MAX_STEP + 25; delayTime = MIN_DELAY; } public void mouseExited(MouseEvent e) { step = MIN_STEP; delayTime = DELAY_TIME; // in milli seconds } public void mouseClicked(MouseEvent e) { if(e.getButton( ) == MouseEvent.BUTTON3) { // Right Button step = 1+(step/3); delayTime = delayTime*2; speedUp = !speedUp; // toggle }//if if(speedUp){ faster( ); }else{ slower( ); }//if( }//mouseClicked public void faster( ) { // moving faster ++step; if(step > MAX_STEP) step = MAX_STEP; --delayTime; if(delayTime < MIN_DELAY) delayTime = MIN_DELAY; if(delayTime == MIN_DELAY && step==MAX_STEP) speedUp = false; }//faster public void slower( ) { // moving slower --step; if(step < MIN_STEP) step = MIN_STEP; ++delayTime; if(delayTime > MAX_DELAY) delayTime = MAX_DELAY; if(delayTime == MAX_DELAY && step==MIN_STEP) speedUp = true; }//slower boolean pause = false; // "Pause/Continue" ? boolean shouldDie = false; // "Stop" pressed boolean dieIM = false; // "StopIM" pressed } // class gHanoiOK //////////////////////////////////////////////////////////// class ControlPanel extends Panel implements ItemListener, ActionListener { gHanoiOK target; int nDisks = 2; public ControlPanel(gHanoiOK target) { this.target = target; target.setBackground(Color.white); target.setForeground(Color.red); // setLayout(new FlowLayout()); setBackground(Color.yellow); MyTimeBTN tm; add(tm=new MyTimeBTN( )); // for showing time new Thread(tm).start( ); // start my Timer addColorSelector( ); addStartButton( ); addChoices( ); addMoreButtons( ); }// ControlPanel( void addStartButton( ) { Button b = new Button("Start"); b.setBackground(Color.pink); b.setForeground(Color.black); add(b); b.addActionListener(this); // "Start" Button }// addStartButton( void addChoices( ) { Choice shapes = new Choice(); // choice List shapes.addItemListener(this); shapes.addItem("1"); shapes.addItem("2"); shapes.addItem("3"); shapes.addItem("5"); shapes.addItem("8"); shapes.addItem("16"); shapes.addItem("24"); shapes.addItem("32"); shapes.addItem("50"); shapes.addItem("64"); shapes.setBackground(Color.lightGray); shapes.select("5"); nDisks = 5; add(shapes); }// static Button bFast=null, bSlower=null; void addMoreButtons( ) { Button bq = new Button("Quit"); bq.setForeground(Color.red); add(bq); bq.addActionListener(this); Panel p = new Panel( ); p.add(bFast = new Button("+")); // Faster bFast.addActionListener(this); p.add(bSlower = new Button("-")); // Slower bSlower.addActionListener(this); add(p); p.setBackground(new Color(238,168,158)); //p.setBackground(bFast.getBackground( )); Button bStop = new Button("Stop"); bStop.setForeground(Color.red); bStop.addActionListener(this); add(bStop); bStop = new Button(STOP_IM); bStop.setFont(new Font("Courier", Font.BOLD, 12) ); bStop.setForeground(Color.blue); bStop.addActionListener(this); add(bStop); bStop = new Button(PAUSE_M); bStop.setFont(new Font("Courier", Font.BOLD, 10) ); bStop.setForeground(Color.red); bStop.addActionListener(this); add(bStop); }// addMoreButtons( void addColorSelector( ) { Panel p = null; CheckboxGroup group = new CheckboxGroup(); Checkbox b; p = new Panel( ); p.add(b = new Checkbox(null, group, true)); b.addItemListener(this); b.setForeground(Color.red); add(p); p.setBackground(Color.red); p = new Panel( ); p.add(b = new Checkbox(null, group, false)); b.addItemListener(this); b.setForeground(Color.green); add(p); p.setBackground(Color.green); p = new Panel( ); p.add(b = new Checkbox(null, group, false)); b.addItemListener(this); b.setForeground(Color.blue); add(p); p.setBackground(Color.blue); /// target.haColor = Color.red; // initial color }// addColorSelector( public void paint(Graphics g) { Rectangle r = getBounds(); g.setColor(Color.lightGray); g.draw3DRect(0, 0, r.width, r.height, true); int n = getComponentCount( ); for(int i=0; i= 65 ) nDisks = 8; String acCommand = e.getActionCommand( ); if(acCommand.equals("Quit") ) System.exit(0); else if(acCommand.equals("Start") ) pbStart( ); else if(acCommand.equals("Stop") ) pbStop( ); else if(acCommand.equals(STOP_IM) ) pbStopIM( ); else if(acCommand.equals(PAUSE_M) ) pbPause(e); else if(acCommand.equals(CONTINUE_M) ) pbContinue(e); }//actionPerformed private void pbStart( ) { //target.doHanoi(nDisks); // not work to triger event Sister hanoi = new Sister(target, nDisks); hanoi.start( ); // 啟動另一個 Thread 去做 hanoi(nDisks) // event handler 結束後其他 event 才有用 } // pbStart( private void pbStop( ) { target.pause = false; target.shouldDie = true; target.dieIM = false; }// pbStop( private void pbStopIM( ) { target.pause = false; target.shouldDie = target.dieIM = true; //try{Thread.sleep(8);}catch(Exception ee) { } }// pbStopIM( private void pbPause(ActionEvent e) { if( !target.isRunning ) return; // 沒在 Run target.shouldDie = target.dieIM = false; target.pause = true; ((Button)e.getSource()).setLabel(CONTINUE_M); }// pbStopIM( private void pbContinue(ActionEvent e) { //if( !target.isRunning ) return; // 沒在 Run target.shouldDie = target.dieIM = false; if(target.speedUp) target.faster( ); else target.slower( ); //try{Thread.sleep(8);}catch(Exception ee) { } target.pause = false; ((Button)e.getSource()).setLabel(PAUSE_M); }// pbStopIM( public final String STOP_IM = "立即停"; // "StopIM" public final String PAUSE_M = "Pause"; // "暫停" public final String CONTINUE_M = "Continue"; // "繼續" }//class ControlPanel // 這個 Thread 用來執行 gHanoiOK 的 doHanoi(nDisks); class Sister extends Thread { gHanoiOK target; int nDisks = 3; boolean putOnly = false; static Sister him=null; // static variable is shared Sister( gHanoiOK target, int nDisks){ this.target = target; this.nDisks = nDisks; }//Sister // 注意 Thread 的啟動點是 run( ), 要用 .start( ) 啟動! public void run( ) { int cnt = activeCount( ); //if(him!=null && him.isAlive()) return; //do nothing // 只允許一個 Thread 做 hanoi, 不然會亂七八糟糕 if( target.isRunning ) return; // ignore it when moving if(him == null) { // 若 user 一直按 Start ? him = this; target.delayTime = 3; target.step = 2; target.isRunning = true; target.pause = false; target.shouldDie = target.dieIM = false; if(putOnly) target.haInit(nDisks); // doNot move else target.doHanoi(nDisks); target.isRunning = false; him = null; // 讓下次又可做 doHanoi( ) }// if }//run( Sister( gHanoiOK target, int nDisks, boolean putOnly){ // new this constructor will initialize Disks ONLY this.target = target; this.nDisks = nDisks; this.putOnly = true; }//Sister }//class Sister class WCC extends WindowAdapter{ // 用來關閉窗 Only public void windowClosing(WindowEvent e){System.exit(0);} } class MyTimeBTN extends Canvas implements Runnable { MyTimeBTN( ) { setSize(50,15); setVisible(true); } public void paint(Graphics g) { String t = "" + new Date( ); g.drawString(t.substring(10,19), 0, 12); } public void run( ) { while (true) { try { Thread.sleep(58); } catch (Exception e) { } repaint(); } }//run( }//MyTimeBTN