/*
* *** FILENAME: hanoi.java
* *** Robert Stack
* *** UC Berkeley Extn Java Programming EDP#306472
* *** email: rstack@rstack.com
* ***
* *** Assignment #3 25 Mar 1999
* *** Towers of Hanoi GUI implementation
* *** Implemented per project requirements, plus an extra option
* *** for the user to specify the number of disks (between 1 and 10).
* *** Implemented using JDK 1.2, Win NT platform
* *** GUI displayed/tested using Applet Viewer from JDK 1.2
* *** Also tested with MS Internet Explorer 4.0, version 4.72.2106.8
* *** Also available at http://www.rstack.com/hanoi/hanoi.html
*/
/* Note: "hanoi.html" basic file looks as follows:
* ---------------------------------------------------------------------------
<*HTML>
<*BODY>
<*APPLET CODE=hanoi.class WIDTH=400 HEIGHT=400>
<*/APPLET>
<*/BODY>
<*/HTML>
* ---------------------------------------------------------------------------
*/
//3456789012345678901234567890123456789012345678901234567890123456789012345678
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.*;
// hanoi contains applet Init which also defines two button
// press anonymous inner classes, one for Reset and one for
// Move. It also contains a Disk class for disk information
// and methods to set and read that information. There is
// also a paint method for redrawing the graphical portion
// of the display, and a move disk logic method.
public class hanoi extends Applet {
private TextArea textA; // text output display
private Checkbox peg_a_src;
private Checkbox peg_b_src;
private Checkbox peg_c_src;
private Checkbox peg_a_dest;
private Checkbox peg_b_dest;
private Checkbox peg_c_dest;
private Disk[] diska; // disk characteristics object array
private Font font;
private static final int MAX_DISKS = 10; // maximum # disks permitted
private int numDisks;
private static final int INIT_NUM_DISKS = 4; // initial number of disks
private TextField textNDisks;
private static final int WIDTH_FACTOR = 8; // how wide disks show
// Disks indicate what peg and postition, mostly independent of graphics
// settings which are in paint(). Note that pegWidth is a graphical
// width in pixels, however.
class Disk {
private int pegNum; // 1, 2, or 3 for A, B, or C
private int pegStep; // which position: 1 at base, 2 next up, etc.
private int pegWidth; // graphical width set by constructor
Disk(int num, int step) {
pegNum = num;
pegStep = step;
pegWidth = 0;
}
void setPegNum(int num) {
pegNum = num;
}
void setPegStep(int step) {
pegStep = step;
}
void setPegWidth(int width) {
pegWidth = width;
}
int getPegNum() {
return pegNum;
}
int getPegStep() {
return pegStep;
}
int getPegWidth() {
return pegWidth;
}
}
// Create GUI, define button press event methods, initialize
public void init() {
setLayout(new BorderLayout());
setBackground(Color.white);
font = new Font("Helvetica", Font.BOLD, 24);
Panel p1 = new Panel();
p1.setLayout(new GridLayout(1, 3));
p1.setBackground(Color.green);
add("South", p1);
// Create A-B-C checkboxes in panels and labels
CheckboxGroup src_group = new CheckboxGroup();
peg_a_src = new Checkbox("A", false, src_group);
peg_b_src = new Checkbox("B", false, src_group);
peg_c_src = new Checkbox("C", false, src_group);
Panel pSrc = new Panel();
pSrc.setLayout(new GridLayout(3, 1));
pSrc.add(peg_a_src);
pSrc.add(peg_b_src);
pSrc.add(peg_c_src);
CheckboxGroup dest_group = new CheckboxGroup();
peg_a_dest = new Checkbox("A", false, dest_group);
peg_b_dest = new Checkbox("B", false, dest_group);
peg_c_dest = new Checkbox("C", false, dest_group);
Panel pDest = new Panel();
pDest.setLayout(new GridLayout(3, 1));
pDest.add(peg_a_dest);
pDest.add(peg_b_dest);
pDest.add(peg_c_dest);
Panel pChecks = new Panel();
pChecks.setLayout(new GridLayout(1, 2));
Label srcLabel = new Label("Source:");
Label destLabel = new Label("Dest:");
pChecks.add(pSrc);
pChecks.add(pDest);
Panel pCheckL = new Panel();
pCheckL.setLayout(new FlowLayout());
pCheckL.add(srcLabel);
pCheckL.add(destLabel);
Panel pCheckArea = new Panel();
pCheckArea.setLayout(new FlowLayout());
pCheckArea.add(pCheckL);
pCheckArea.add(pChecks);
// Move button and button press action
Button bMove = new Button("MOVE");
bMove.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Insert disk movement message "backwards", including disks that
// are involved in the move
textA.insert("\n", 0);
int i1 = 0;
int i2 = 0;
if (peg_a_dest.getState() == true) {
textA.insert("A", 0);
i2 = 1;
}
if (peg_b_dest.getState() == true) {
textA.insert("B", 0);
i2 = 2;
}
if (peg_c_dest.getState() == true) {
textA.insert("C", 0);
i2 = 3;
}
if (peg_a_src.getState() == true) {
textA.insert("A->", 0);
i1 = 1;
}
if (peg_b_src.getState() == true) {
textA.insert("B->", 0);
i1 = 2;
}
if (peg_c_src.getState() == true) {
textA.insert("C->", 0);
i1 = 3;
}
// Actual move occurs in here, and a message string such as "Disk
// moved" or "Failed" returned to display to the user.
textA.insert(towerMove(i1, i2) + " ", 0);
repaint();
}
});
// Reset button just places all pegs on first tower upon
// button action
Button bReset = new Button("RESET");
bReset.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int i1;
for (i1 = 0; i1 < numDisks; i1++) {
diska[i1].setPegNum(1);
diska[i1].setPegStep(i1+1);
}
repaint();
// Get the number of disks from the # disks text input. Reset peg
// widths which may grow or shrink; make sure entry is not too large
// or small. Non-numeric entry resets to initial # of disks.
try {
numDisks = Integer.parseInt(textNDisks.getText());
}
catch (IllegalArgumentException ex) {
numDisks = INIT_NUM_DISKS;
}
if (numDisks > MAX_DISKS) {
numDisks = MAX_DISKS;
}
if (numDisks < 1) {
numDisks = 1;
}
textNDisks.setText("" + numDisks);
SetDiskWidths();
textA.insert("All disks RESET" + "\n", 0);
}
});
// Create then initially set the number of disks
Label textL = new Label("# disks:");
textNDisks = new TextField("0");
textNDisks.setText("" + INIT_NUM_DISKS);
numDisks = Integer.parseInt(textNDisks.getText());
// Create buttons panel
Panel pOpts = new Panel();
pOpts.setLayout(new FlowLayout());
pOpts.add(bMove);
pOpts.add(bReset);
pOpts.add(textL);
pOpts.add(textNDisks);
textA = new TextArea("", 10, 40);
// Add panels to overall control panel
p1.add(pCheckArea);
p1.add(pOpts);
p1.add(textA);
// Initial disk object creation: create all of them now even if
// some aren't used for the initial number of disks
diska = new Disk[MAX_DISKS];
int i3;
for (i3 = 0; i3 < MAX_DISKS; i3++) {
diska[i3] = new Disk(1, i3 + 1);
}
SetDiskWidths();
}
// Upon redraw event this redraws based on current disk information
public void paint(Graphics g) {
// Draw titles in drawing area
g.setColor(Color.blue);
g.setFont(font);
g.drawString("Towers of Hanoi", 105, 25);
g.drawString("A", 95, 190);
g.drawString("B", 195, 190);
g.drawString("C", 295, 190);
// Draw pegs with bases as simple black lines
g.setColor(Color.black);
g.fillRect(0, 200, 400, 4);
g.fillRect(60, 160, 84, 4);
g.fillRect(100, 40, 4, 124);
g.fillRect(160, 160, 84, 4);
g.fillRect(200, 40, 4, 124);
g.fillRect(260, 160, 84, 4);
g.fillRect(300, 40, 4, 124);
// Draw disks based on which peg they are on and their
// position on the pegs
g.setColor(Color.red);
int i1;
for (i1 = 0; i1 < numDisks; i1++) {
g.fillRect(diska[i1].getPegNum() * 100 - diska[i1].getPegWidth() / 2 +2,
160 - diska[i1].getPegStep() * 10, diska[i1].getPegWidth(), 9);
}
}
// Set disk widths for when the number of disks is set or reset
public void SetDiskWidths() {
int i1;
for (i1 = 0; i1 < numDisks; i1++) {
diska[i1].setPegWidth((numDisks - i1) * WIDTH_FACTOR);
}
}
// Move from one peg to another: return status string
public String towerMove(int pegSrc, int pegDest) {
if (pegSrc == 0 || pegDest == 0)
return "No source/dest";
if (pegSrc == pegDest)
return "Same pegs";
//get source peg disk number
int i1;
for (i1 = numDisks - 1; i1 >= 1 - 1; i1--) {
if (diska[i1].getPegNum() == pegSrc)
break;
}
if (i1 < 0)
return "No source disk";
int i2;
for (i2 = numDisks - 1; i2 >= 1 - 1; i2--) {
if (diska[i2].getPegNum() == pegDest)
break;
}
// See if a larger base or no disk is on the destination
// peg
int pegWidth1;
int pegWidth2;
pegWidth1 = diska[i1].getPegWidth();
if (i2 >= 0) {
pegWidth2 = diska[i2].getPegWidth();
if (pegWidth1 >= pegWidth2) {
return "Disk too large";
}
}
// The disk can be moved to the new peg
if (i2 >= 0)
diska[i1].setPegStep(diska[i2].getPegStep() + 1);
else
diska[i1].setPegStep(1);
diska[i1].setPegNum(pegDest);
return "Moved disk";
}
}
Back to Towers of Hanoi applet