01 //HAHAHA.java  -- Graphics Hanoi, by tsaiwn@cs.nctu.edu.tw
   02 //javac HAHAHA.java
   03 //java HAHAHA
   04 //HTML: <applet code="HAHAHA.class" width=508 height=500>
   05 import java.io.*;
   06 import java.applet.*;
   07 import java.awt.*;
   08 import java.awt.event.*;
   09 import java.net.*;
   10 import java.util.*;
   11 
   12 public class HAHAHA extends Applet {
   13    static Frame f = null;
   14    static boolean isApplet = true;
   15    gHanoiOK ghok = null;
   16    ControlPanel cp = null;
   17 
   18    public static void main(String xxx[ ]) {
   19       isApplet = false;  // from Java application
   20       f = new Frame("Tower of Hanoi");
   21       HAHAHA hhh = new HAHAHA( );
   22       f.add("Center", hhh); 
   23       f.setSize(gHanoiOK.X_SIZE+10, gHanoiOK.Y_SIZE+50);  //510,550
   24       f.setVisible(true); f.addWindowListener(new WCC( ));
   25       //Then, Simulate the Applet behavior
   26       hhh.init( );
   27       hhh.start( );
   28    }//main
   29 
   30    public void init( ) {
   31       ghok = new gHanoiOK(f);
   32       cp = new ControlPanel(ghok);
   33       setLayout(new BorderLayout( ) );
   34       add("Center", ghok);
   35       add("South", cp);   // the Control Panel
   36       if(f!=null) { f.validate( ); f.setVisible(true); }
   37       ghok.init( );
   38       ghok.start( );
   39    }//init(
   40    public void start( ) {
   41       if(!isApplet) ghok.main( );  // is application
   42       else  ghok.winHanoi(5);   // isApplet
   43    }//start(
   44 
   45    public HAHAHA( ) {
   46    }// HAHAHA constructor
   47 }//class HAHAHA
   48 
   49 class gHanoiOK extends Panel implements MouseListener {
   50   //some constants for later use
   51   public static final int X_SIZE=500, Y_SIZE=500;
   52   public static final int Y_TOP = 28;
   53   public static final Color bgColor = Color.white;
   54   public static final int DELAY_TIME = 50; // 50/1000 second
   55   public static final int MOVE_STEP = 58; // 58 points
   56   public static final int MIN_STEP = 6; // 6 points
   57   public static final int MAX_STEP = 158; // 88 points
   58   public static final int MAX_DELAY = 88; // 88 milli sec
   59   public static final int MIN_DELAY = 1; 
   60 
   61   public static Color haColor = Color.red;   // variable
   62   public static boolean speedUp = false;  // speed up?  slower down?
   63   public static int delayTime = DELAY_TIME; 
   64   public static int step = MOVE_STEP;
   65   int kkk=0;  // used in paint(
   66   boolean  alreadyDone = false;  // used in Applet mode
   67   boolean  isRunning = false; // doing Hanoi ?
   68 
   69   ///
   70   int nDisks = 0;  // number of Disks
   71   int nPeg[ ] = new int[3];    // height of the 3 piles
   72   int xPosition[ ] = new int[3];  // central position of 3 pegs
   73   int aX=38, aY= (Y_SIZE - 50);
   74   int bX=(aX+(X_SIZE-50)/3), bY=aY;
   75   int cX=(bX+(X_SIZE-50)/3), cY=aY;
   76 
   77   int thick=22; // for disk thick
   78   int width=33; // for disk width
   79   int wUnit=2;  // 128/nDisks == 2
   80 
   81   Graphics bufferGraphics=null;  // 用來畫在記憶體的圖  
   82   Image img=null;  // The image that will contain everything that ..
   83               /// .. has been drawn on bufferGraphics.
   84               /// .. will be used in paint( ) 
   85   Dimension dim=null;  // To get the width and height of the applet. 
   86 
   87   PrintStream cout=null;      // 使其類似 C++ 的 <iostream>
   88   BufferedReader cin = null;
   89 
   90    public void init( ) {
   91       addMouseListener(this); // handle MouseEvent
   92       if(f==null) cout = System.out;   // from Applet
   93    }//init(
   94 
   95    public void start( ) {
   96        prepareIO( );
   97        prepareImagePaper( );
   98    }//start
   99 
  100    Frame f=null;
  101   //Panel myPanel;
  102 
  103    public static void main(String xxx[ ]) {
  104       Frame f = new Frame("Tower of Hanoi");
  105       gHanoiOK gok = new gHanoiOK(f);
  106       f.add("Center", gok); 
  107       f.setSize(X_SIZE, Y_SIZE);  //500,500
  108       f.setVisible(true); 
  109       gok.init( );  // simulate Applet
  110       gok.start( );
  111       gok.repaint( ); // force to initilize img
  112       gok.prepareIO( );
  113       gok.main( );
  114    }//main
  115 
  116   // constructors, one for from main(
  117  public gHanoiOK( Frame f ) {
  118     this.f = f;
  119     addMouseListener(this); // handle MouseEvent
  120     if(f==null) cout = System.out;   // from Applet
  121  }// gHanoiOK
  122 
  123  public gHanoiOK( ) {   // for Applet to call this
  124      addMouseListener(this); // handle MouseEvent
  125  }
  126 
  127  void prepareIO( ){
  128     try{
  129        cout = System.out;
  130        if(f==null) return;  // is Applet
  131        cin = new BufferedReader(
  132                new InputStreamReader(System.in) );
  133     }catch(Exception e) { System.exit(38); }
  134  }//prepareIO(
  135 
  136 
  137 
  138   void prepareImagePaper( ) {
  139       // Create an offscreen image to draw on , same size
  140       setVisible(true);
  141       dim = getSize( );  // obtain the width and height
  142       if(dim.width == 0) return;  // not ready, maybe later
  143       img = createImage(dim.width, dim.height); 
  144       bufferGraphics = img.getGraphics( );  
  145       // use Graphics methods to draw, this is double buffer
  146       return;
  147   }// prepareImagePaper
  148 
  149 // hanoi.c  -- Hanoi tower, @CopyLeft by tsaiwn@csie.nctu.edu.tw
  150 //Hanoi Tower(河內之塔) -- 神奇的 Recursive -- 自己叫自己 !
  151 //關於 Hanoi Tower, 請用 http://gogle.com 打入 "hanoi tower"  查詢
  152 //注意用雙引號夾住如 "hanoi tower" 查到的才是你要的 :-)
  153 //或是直接看這  http://en.wikipedia.org/wiki/Tower_of_Hanoi
  154 /// C/C++ requires declaraction before use the function
  155 //void moveOne(int px, int py, int ); //3rd parameter is the disk#
  156 void hanoi(int n, int pa, int pc, int pb) {
  157     if(n==0) return;    // there is no disk
  158     if(shouldDie) return;  // my Master want me to die :-(
  159     hanoi(n-1, pa, pb, pc);  // 先幫我把上面 n-1 disks 搬走
  160     moveOne(pa, pc, n);   /// move the n-th disk from pa to pc
  161     hanoi(n-1, pb, pc, pa); // move n-1 disks from pb to pc
  162 } // hanoi
  163 void moveOne(int px, int py, int n ) {  // the n-th disk
  164    moveG(px, py, n);  // Graphics version
  165    String p123[ ] = {"th", "st", "nd", "rd"}; // -th, Fir-st, 
  166                                // .. Seco-nd, 3-rd
  167    int k = n%10;
  168    if(k!=1  && k!=2 && k!=3) k = 0;  // -th
  169    cout.printf("Move %d-%s disk from %c  to  %c \n", 
  170                                n,  p123[k],   px, py);
  171 }//moveOne(
  172 
  173 int getInt( ) {   // static char buf[99];
  174    String buf = null;
  175    int ans=0;
  176    // fgets(buf, sizeof(buf), stdin); ans=atoi(buf);
  177    try {
  178       buf = cin.readLine( );
  179       ans = Integer.parseInt(buf);
  180    }catch(Exception e) { ans = 3; }
  181     // 如果把 ans = 3; 改為 ans = 64; 你認為會做多久? ? ?
  182    return ans;  // 若一秒只能搬一個動作, 
  183                 //  ..則需約 600 billion(六千億) (580+) years
  184 }// getInt(
  185 
  186 void haInit(int n) {
  187     nDisks = n; if(n==0) n=2;
  188     nPeg[0] = n; // initially all Disks on Peg-0
  189     nPeg[1] = nPeg[2] = 0;   // no Disks on Peg-1 and Peg-2
  190     thick = (Y_SIZE -50)/n;   // thick of each Disk
  191     if(thick > 20) thick = 20; // disk thick limit
  192     wUnit = 128/n;      // Disk width = wUnit * Disk#number
  193     if(wUnit < 2) wUnit=2;   // minimum 2 points
  194     xPosition[0] = aX + 64;   // central position aX + 128/2
  195     xPosition[1] = bX + 64;
  196     xPosition[2] = cX + 64;
  197     //
  198     clearTower( );
  199     if(f != null) { f.validate( ); f.setVisible(true); }
  200     initDisks( );  // draw the initial Disks
  201     if(f != null)  f.setVisible(true);  // f.show( );
  202 }// haInit(
  203 
  204 
  205 
  206 void doHanoi(int n) {
  207     if(dim == null  || img==null || bufferGraphics == null) {
  208          prepareImagePaper( );  //  create img paper
  209          if( bufferGraphics == null) return;  // give up
  210          System.out.println("doHanoi..");
  211     }
  212     if(n >= 65) return;
  213     shouldDie = false; isRunning = true; pause=false;
  214     haInit(n); 
  215     hanoi(n, 'A', 'C', 'B'); // move n disks on peg 'A' to peg 'C'
  216     isRunning = false;
  217 }// doHanoi(
  218 
  219 
  220 int main( ) {  // original main program in C Language
  221     int n;  int firstRun = 1;
  222     while(true) {
  223        if(firstRun != 0) {
  224             firstRun = 0;
  225             f.setVisible(false);  // hide the window
  226        }//if
  227        cout.print(" n=? ");
  228        n = getInt( );  // read an int into n
  229        if(n<0) break;
  230        f.setVisible(true);  //f.requestFocus(true);
  231        doHanoi(n);
  232         try{Thread.sleep(58);}catch(Exception e) {  }
  233        //f.toBack( ); //f.setVisible(false);  // hide the window
  234     }//while
  235     f.dispose( ); cout.println("Bye bye...");
  236     System.exit(0); return 0;
  237 }// int main( )
  238 //////////////////////////
  239 
  240   void winHanoi(int n) {   // do hanoi from Applet/Window
  241       if(alreadyDone) return;  // run only once
  242       alreadyDone = true; pause=false; shouldDie=false;
  243       System.out.println("applet paint...");
  244       //doHanoi(n);  // 不要這樣做, 會陷入很久的 ...
  245       //啟動另一個 thread Sister 去做才不會被卡住
  246       new Sister(this, n).start( );
  247            // this 就是目前這 class 的物件 ( gHanoiOK )
  248   }// winHanoi(
  249 
  250   void clearTower( ){  //initial status
  251      if(dim == null  || img==null || bufferGraphics == null) {
  252          repaint( ); // force to prepare img/bufgraphics
  253          if(dim==null) repaint( );  // try to prepare img again
  254          if(img==null) repaint( );
  255          if(img==null) return;  // still not READY yet
  256      }
  257      setBackground(Color.white);
  258      bufferGraphics.clearRect(0,0,dim.width,dim.width);
  259      bufferGraphics.setColor(Color.pink); 
  260      bufferGraphics.fillRect(aX-3, aY, 128+6, 18); 
  261      bufferGraphics.fillRect(bX-3, bY, 128+6, 18);
  262      bufferGraphics.fillRect(cX-3, cY, 128+6, 18);
  263      bufferGraphics.setColor(new Color(99, 58, 233));
  264      bufferGraphics.setFont(new Font("Courier", Font.BOLD, 18) );
  265      bufferGraphics.drawString(
  266         "Tower of Hanoi, by tsaiwn@csie.nctu.edu.tw",20,20);
  267      repaint( );  // have paint( ) to draw the image to screen
  268      try{ Thread.sleep(delayTime); }catch(Exception e){ }
  269   }//clearTower
  270 
  271 
  272 
  273 
  274   // rewrite update( ) : 
  275   //    Always required for good double-buffering.
  276   // This will cause the Applet not to first wipe off previous 
  277   // ..drawings but to immediately repaint. 
  278   // The wiping off  may also causes flickering. 
  279   // Update is called automatically when repaint( ) is called. 
  280 
  281   // comment out the update( ) function then try to run again
  282   public void update(Graphics g) { paint(g); }  // 直接叫 paint( )
  283 
  284   public void paint(Graphics g) {
  285      if(dim == null  || img==null || bufferGraphics == null) {
  286         // This is the first call to here.
  287         //System.out.println("paint...img null...");
  288         prepareImagePaper( );
  289         return;
  290      }//if
  291      g.drawImage(img, 0, 0, this);   // 此時才畫到螢幕
  292      //System.out.println(" paint "+ ++kkk); //for debug
  293   }//paint(
  294 
  295   void initDisks( ) {   // draw the original Disks
  296     if( img==null || bufferGraphics == null) return;
  297     int x, y;
  298     for(int n= nDisks; n>=1; --n) {
  299         bufferGraphics.setColor(haColor); 
  300         x = xPosition[0] - wUnit*n/2;
  301         y = aY - (nDisks -n+1)*thick;
  302         bufferGraphics.fillRect(x, y, wUnit*n, thick);
  303     }//for
  304     repaint( );
  305     try{Thread.sleep(delayTime);}catch(Exception e){ }
  306    }
  307 
  308   void moveG(int px, int py, int n ) {  // the n-th disk
  309      if(shouldDie) return;
  310      //if(img==null) return;  // not ready yet
  311      if(dim == null  || img==null || bufferGraphics == null){
  312          repaint( );
  313          if(dim == null  || img==null ) repaint( );
  314          if(bufferGraphics == null) repaint( );
  315          if(bufferGraphics == null) return;  // give up :-)
  316      }
  317      int from = px - 'A';
  318      int to = py - 'A';
  319      width = wUnit * n;   // width of n-th disk
  320      if(width > 128) width = 128;  // limit the width
  321      moveYUp(from, to, n);   // 編號 n 的 Disk
  322      moveX(from, to, n);
  323      moveYDown(from, to, n);
  324      --nPeg[from];  // from == 0? 1? 2?
  325      ++nPeg[to];
  326      //System.out.print("moveG--- ");
  327   }//moveG(
  328 
  329   void moveThere(int xs, int ys, int xt, int yt, 
  330              int width, int thick) {
  331      if(shouldDie && dieIM) return;  // my Master want me to die :-(
  332      checkPause( );
  333      bufferGraphics.setColor(Color.white);    // 擦掉 
  334      bufferGraphics.fillRect(xs, ys, width, thick); 
  335      bufferGraphics.setColor(haColor);    // 然後畫新的 
  336      bufferGraphics.fillRect(xt, yt, width, thick); 
  337      repaint( );  // 顯示到螢幕 
  338      try{Thread.sleep(delayTime);}catch(Exception e){ }
  339   }//moveThere
  340 
  341 
  342   void moveYUp(int fm, int to, int n ) {  // the n-th disk
  343      // fm, to : 0, 1, 2
  344      int x,y, y1, y2;
  345      x = xPosition[fm] - width/2;
  346      y2 = Y_TOP;
  347      y1 = aY - nPeg[fm]*thick;  // y-position of n-th disk
  348      // move y from y1 to y2
  349      y = y1;
  350      while(y > y2+step) { 
  351         int y0 = y;
  352         y -= step;
  353         moveThere(x, y0, x, y, width, thick);
  354      }//while(
  355      moveThere(x, y, x, y2, width, thick);   //提起到正上方 
  356   }//moveYUp
  357 
  358   void moveX(int fm, int to, int n ) {  // the n-th disk
  359      int x,y, x1, x2;
  360      y = Y_TOP;
  361      x1 = xPosition[fm]  - width/2;  
  362      x2 = xPosition[to]  - width/2;;
  363      //boolean toRight = true;   // move from Left to Right
  364      int rightLeft = 1;  // 往右 ; fm ==> to
  365      if(x2 < x1)  rightLeft = -1;   // 往左  to <== fm
  366      x = x1;  // move x from x1 to x2
  367      while( 38==38 ) {  //  BUG fixed
  368         int xtemp = x + step * rightLeft;  // 向右走向左走?
  369         if(rightLeft == 1) {     // fm ====>  to
  370            if(xtemp > x2) break;
  371         }else{    // to <====== fm
  372            if(xtemp < x2) break;
  373         } 
  374         int x0 = x;
  375         x = xtemp;   // 注意我們用臨時的 xtemp
  376         moveThere(x0, y, x, y, width, thick);
  377      }//while(
  378      moveThere(x, y, x2, y, width, thick);  // 到目的堆正上方 
  379   }//moveX
  380 
  381   void moveYDown(int fm, int to, int n ) {  // the n-th disk
  382      int x,y, y1, y2;
  383      x = xPosition[to]  - width/2;;
  384      y1 = Y_TOP;
  385      y2 = aY - (nPeg[to]+1)*thick;    // destination
  386      y = y1;   // move y from y1 to y2
  387      while(y < y2-step) {
  388         int y0 = y;
  389         y += step;
  390         moveThere(x, y0, x, y, width, thick);
  391      }//while(
  392      moveThere(x, y, x, y2, width, thick);  // 到定位
  393   }// moveYDown
  394 
  395 
  396   void checkPause( ) { 
  397       while(true) {    // Loop untill "Continue"
  398           if( !pause ) break;
  399           try{ Thread.sleep(18); }catch(Exception e){ }
  400       }//while
  401   }//checkPause(
  402 
  403  //////////////////////////////////////////////////////////
  404 /// handling MouseEvent
  405   public void mousePressed(MouseEvent e) { }
  406 
  407   public void mouseReleased(MouseEvent e) { }
  408 
  409 
  410   public void mouseEntered(MouseEvent e) {
  411      step = MAX_STEP + 25;
  412      delayTime = MIN_DELAY;
  413   }
  414 
  415   public void mouseExited(MouseEvent e) { 
  416      step = MIN_STEP;
  417      delayTime = DELAY_TIME;   // in milli seconds
  418   }
  419 
  420   public void mouseClicked(MouseEvent e) { 
  421      if(e.getButton( ) == MouseEvent.BUTTON3) { // Right Button
  422          step = 1+(step/3); delayTime = delayTime*2;
  423          speedUp = !speedUp;  // toggle
  424      }//if
  425      if(speedUp){
  426           faster( );
  427      }else{
  428           slower( );
  429      }//if(
  430   }//mouseClicked
  431 
  432   public void faster( ) {   // moving faster
  433       ++step; if(step > MAX_STEP) step = MAX_STEP;
  434       --delayTime; if(delayTime < MIN_DELAY)  delayTime = MIN_DELAY;
  435       if(delayTime == MIN_DELAY && step==MAX_STEP) speedUp = false;
  436   }//faster
  437 
  438   public void slower( ) {   // moving slower
  439       --step; if(step < MIN_STEP) step = MIN_STEP;
  440       ++delayTime; if(delayTime > MAX_DELAY) delayTime = MAX_DELAY;
  441       if(delayTime == MAX_DELAY && step==MIN_STEP) speedUp = true;
  442   }//slower
  443   boolean pause = false;      // "Pause/Continue" ?
  444   boolean shouldDie = false;     // "Stop" pressed
  445   boolean dieIM = false;     // "StopIM" pressed
  446 } // class gHanoiOK
  447 ////////////////////////////////////////////////////////////
  448 
  449 
  450 class ControlPanel extends Panel 
  451                  implements ItemListener, ActionListener {
  452     gHanoiOK  target;
  453     int nDisks = 2;
  454 
  455     public ControlPanel(gHanoiOK target) {
  456         this.target = target;
  457         target.setBackground(Color.white);
  458         target.setForeground(Color.red);
  459         //
  460         setLayout(new FlowLayout());
  461         setBackground(Color.yellow);
  462         MyTimeBTN tm; add(tm=new MyTimeBTN( ));  // for showing time
  463         new Thread(tm).start( ); // start my Timer
  464         addColorSelector( );
  465         addStartButton( );
  466         addChoices( );
  467         addMoreButtons( );
  468     }// ControlPanel(
  469 
  470     void addStartButton( ) {
  471         Button b = new Button("Start");
  472         b.setBackground(Color.pink);
  473         b.setForeground(Color.black);
  474         add(b);
  475         b.addActionListener(this);  // "Start" Button
  476     }// addStartButton( 
  477 
  478 
  479 
  480     void addChoices( ) {
  481         Choice shapes = new Choice();  // choice List
  482         shapes.addItemListener(this);
  483         shapes.addItem("1"); shapes.addItem("2");
  484         shapes.addItem("3"); shapes.addItem("5");
  485         shapes.addItem("8"); shapes.addItem("16");
  486         shapes.addItem("24"); shapes.addItem("32");
  487         shapes.addItem("50"); shapes.addItem("64");
  488         shapes.setBackground(Color.lightGray);
  489         shapes.select("5"); nDisks = 5;
  490         add(shapes);
  491     }//
  492 
  493     static Button bFast=null, bSlower=null;
  494     void addMoreButtons( ) {
  495         Button bq = new Button("Quit");
  496         bq.setForeground(Color.red);
  497         add(bq);
  498         bq.addActionListener(this);
  499         Panel p = new Panel( );
  500          p.add(bFast = new Button("+"));  //  Faster
  501          bFast.addActionListener(this);
  502          p.add(bSlower = new Button("-"));  //  Slower
  503          bSlower.addActionListener(this);
  504         add(p); p.setBackground(new Color(238,168,158));
  505          //p.setBackground(bFast.getBackground( ));
  506         Button bStop = new Button("Stop");
  507         bStop.setForeground(Color.red);
  508         bStop.addActionListener(this);
  509         add(bStop);
  510         bStop = new Button(STOP_IM);
  511         bStop.setFont(new Font("Courier", Font.BOLD, 12) );
  512         bStop.setForeground(Color.blue);
  513         bStop.addActionListener(this);
  514         add(bStop);
  515         bStop = new Button(PAUSE_M);
  516         bStop.setFont(new Font("Courier", Font.BOLD, 10) );
  517         bStop.setForeground(Color.red);
  518         bStop.addActionListener(this);
  519         add(bStop);
  520     }//  addMoreButtons( 
  521 
  522     void addColorSelector( ) {
  523         Panel p = null;
  524         CheckboxGroup group = new CheckboxGroup();
  525         Checkbox b;
  526         p = new Panel( );
  527           p.add(b = new Checkbox(null, group, true));
  528           b.addItemListener(this);
  529           b.setForeground(Color.red);
  530           add(p); p.setBackground(Color.red);
  531         p = new Panel( );
  532           p.add(b = new Checkbox(null, group, false));
  533           b.addItemListener(this);
  534           b.setForeground(Color.green);
  535           add(p); p.setBackground(Color.green);
  536         p = new Panel( );
  537           p.add(b = new Checkbox(null, group, false));
  538           b.addItemListener(this);
  539           b.setForeground(Color.blue);
  540           add(p); p.setBackground(Color.blue);
  541         ///
  542         target.haColor = Color.red;   // initial color
  543     }// addColorSelector( 
  544 
  545 
  546     public void paint(Graphics g) {
  547         Rectangle r = getBounds();
  548         g.setColor(Color.lightGray);
  549         g.draw3DRect(0, 0, r.width, r.height, true);
  550         int n = getComponentCount( );
  551         for(int i=0; i<n; i++) {
  552             Component comp = getComponent(i);
  553             if (comp instanceof Checkbox) {
  554                 Point loc = comp.getLocation();
  555                 Dimension d = comp.getSize();
  556                 g.setColor(comp.getForeground());
  557                 g.drawRect(loc.x-1, loc.y-1, d.width+1, d.height+1);
  558             }//if
  559         }//for
  560     }//paint(
  561 
  562   public void itemStateChanged(ItemEvent e) {
  563     if (e.getSource() instanceof Checkbox) {
  564        Color ccc = ((Component)e.getSource()).getForeground( );
  565        if(ccc == Color.green) ccc = new Color(33, 168, 33);
  566        target.haColor = ccc;  // user wants this color :-)
  567        new Sister(target, nDisks, true).start( ); // initDisks
  568     } else if (e.getSource() instanceof Choice) {
  569        String choice = (String) e.getItem();
  570        try{  nDisks = Integer.parseInt(choice);
  571        }catch(Exception e22) { nDisks = 3; }
  572     }//else if :: Choice
  573   }// itemStateChanged
  574 
  575   public void actionPerformed(ActionEvent e) {
  576       if (e.getSource() == bFast) target.faster( );
  577       if (e.getSource() == bSlower) target.slower( );
  578       if(nDisks >= 65 ) nDisks = 8;
  579       String acCommand = e.getActionCommand( );
  580       if(acCommand.equals("Quit") ) System.exit(0);
  581       else if(acCommand.equals("Start") ) pbStart( );
  582       else if(acCommand.equals("Stop") ) pbStop( );
  583       else if(acCommand.equals(STOP_IM) ) pbStopIM( );
  584       else if(acCommand.equals(PAUSE_M) ) pbPause(e);
  585       else if(acCommand.equals(CONTINUE_M) ) pbContinue(e);
  586   }//actionPerformed
  587 
  588   private void pbStart( ) { 
  589       //target.doHanoi(nDisks);  // not work to triger event
  590       Sister hanoi = new Sister(target, nDisks);
  591       hanoi.start( ); // 啟動另一個 Thread 去做 hanoi(nDisks)
  592       // event handler 結束後其他 event 才有用
  593   } // pbStart( 
  594 
  595   private void pbStop( ) { 
  596       target.pause = false;
  597       target.shouldDie = true; target.dieIM = false;
  598   }// pbStop( 
  599 
  600   private void pbStopIM( ) { 
  601       target.pause = false;
  602       target.shouldDie = target.dieIM = true;
  603       //try{Thread.sleep(8);}catch(Exception ee) { }
  604   }// pbStopIM( 
  605 
  606   private void pbPause(ActionEvent e) { 
  607       if( !target.isRunning ) return;  // 沒在 Run
  608       target.shouldDie  =  target.dieIM = false;
  609       target.pause = true;
  610       ((Button)e.getSource()).setLabel(CONTINUE_M);
  611   }// pbStopIM( 
  612 
  613 
  614   private void pbContinue(ActionEvent e) { 
  615       //if( !target.isRunning ) return;  // 沒在 Run
  616       target.shouldDie =  target.dieIM = false;
  617       if(target.speedUp) target.faster( ); else target.slower( );
  618       //try{Thread.sleep(8);}catch(Exception ee) { }
  619       target.pause = false;
  620       ((Button)e.getSource()).setLabel(PAUSE_M);
  621   }// pbStopIM( 
  622 
  623   public final String STOP_IM = "立即停";  // "StopIM"
  624   public final String PAUSE_M = "Pause";  // "暫停"
  625   public final String CONTINUE_M = "Continue";  // "繼續"
  626 }//class ControlPanel
  627 
  628   // 這個 Thread 用來執行 gHanoiOK 的 doHanoi(nDisks);
  629 class Sister extends Thread {
  630      gHanoiOK  target;
  631      int nDisks = 3;
  632      boolean putOnly = false;
  633      static Sister him=null;   // static variable is shared
  634 
  635    Sister( gHanoiOK  target, int nDisks){
  636       this.target = target;
  637       this.nDisks = nDisks;
  638    }//Sister
  639    // 注意 Thread 的啟動點是 run( ), 要用 .start( ) 啟動!
  640    public void run( ) {
  641       int cnt = activeCount( );
  642       //if(him!=null && him.isAlive()) return;  //do nothing
  643       // 只允許一個 Thread 做 hanoi, 不然會亂七八糟糕 
  644       if( target.isRunning ) return; // ignore it when moving
  645       if(him == null) {   // 若 user 一直按 Start ?
  646          him = this;
  647          target.delayTime = 3;  target.step = 2;
  648          target.isRunning = true; target.pause = false; 
  649          target.shouldDie = target.dieIM = false;
  650          if(putOnly) target.haInit(nDisks); // doNot move
  651          else target.doHanoi(nDisks);
  652          target.isRunning = false;
  653          him = null;  // 讓下次又可做 doHanoi( )
  654       }// if
  655    }//run(
  656    Sister( gHanoiOK  target, int nDisks, boolean putOnly){
  657       // new this constructor will initialize Disks ONLY
  658       this.target = target; this.nDisks = nDisks; this.putOnly = true;
  659    }//Sister
  660 }//class Sister
  661 
  662 class WCC extends WindowAdapter{   // 用來關閉窗 Only
  663    public void windowClosing(WindowEvent e){System.exit(0);}
  664 }
  665 
  666 class MyTimeBTN extends Canvas implements Runnable {
  667     MyTimeBTN( ) { setSize(50,15); setVisible(true);  }
  668     public void paint(Graphics g) {
  669        String t = "" + new Date( );
  670        g.drawString(t.substring(10,19), 0, 12);
  671     }
  672     public void run( ) {
  673         while (true) {
  674             try { Thread.sleep(58); } catch (Exception e) { }
  675             repaint();
  676         }
  677     }//run(
  678 }//MyTimeBTN