HUBEI NORMAL UNIVERSITY
Java程序设计
Hannoi塔游戏
论文题目 学 生 指导教师 所在院系 专业名称 完成时间
Hannoi塔游戏
第 1 页
摘 要
《JAVA语言实用教程》是计算机相关专业的必修专业基础课程,其实践性、应用性很强。实践教学环节是必不可少的一个重要环节。本课程的程序设计专题实际是计算机相关专业学生学习完《JAVA语言实用教程》课程后,进行的一次全面的综合训练,JAVA语言实用教程的设计目的是加深对理论教学内容的理解和掌握,使学生较系统地掌握程序设计及其在网络开发中的广泛应用,基本方法及技巧,为学生综合运用所学知识,利用软件工程为基础进行软件开发、并在实践应用方面打下一定基础。
第 2 页
目 录
一、课程设计任务及要求 ................................................................. 4
1、课程设计任务 ........................................................................................... 4 2、设计要求 ................................................................................................... 4
二、需求分析 ..................................................................................... 5
1、系统需求分析 ........................................................................................... 5 2、系统概述 ................................................................................................... 5 3、系统运行环境 ........................................................................................... 6
三、设计思路 ..................................................................................... 6
概要设计 ......................................................................................................... 7
四、详细设计 ..................................................................................... 7
package Hannoi; ........................................................................................... 7
HannoiWindow类 ................................................................................................... 7
AutoMoveDisc类 ................................................................................................. 15 Disc类 ................................................................................................................... 21 HandleMouse类 .................................................................................................... 22 Tower类 ................................................................................................................ 27 TowerPoint 类....................................................................................................... 31
五、运行调试与分析讨论 ............................................................... 33 六、设计体会与小结 ....................................................................... 36
1.设计体会 ................................................................................................... 36
2.小结 ........................................................................................................... 37
参考文献 ........................................................................................... 37 致谢 .................................................................................................... 38
第 3 页
一、课程设计任务及要求
1、课程设计任务
设计GUI界面的Hannoi塔游戏,用户可以通过拖动鼠标移动各个塔上的盘子,程序也可以自动演示盘子的移动过程。 2、设计要求
(1)有三个表示塔的对象,分别命名为A、B和C。A塔上有若干个盘子,盘子的大小不等,并按着大小顺序依次摆放在A塔上,大盘在下,小盘在上。用户可以用鼠标拖动盘子,把A 塔上的盘子全部移动到另外两个塔中的任何一个塔上。要求每次只能移动一个盘子,在任何时候不允许大盘压在小盘的上面。 (2)用户也可以选择让程序自动演示。选择自动演示后,程序将以动画形式演示把A塔上的盘子全部移到C塔的过程,并将移动过程以文本形式显示在一个文本区中。 (3)可以由用户自己设置盘子数量和盘子大小。 (4)可以改变盘子的颜色、背景颜色以及背景音乐。
第 4 页
(5)游戏分为初、中、高三个等级。
(6)可以记录游戏时间并且设有从新开始按钮。
二、需求分析
1、系统需求分析
使用java面向对象语言设计汉诺塔游戏,此游戏要求: 1)界面要求:
(1)尽量使用图形界面实现,要符合日常软件使用规范来设计菜单和界面。
(2)如果无法实现图形界面,则在命令行方式下也需要提供菜单,方便用户操作(盘子可以自己规定)。 2)其他要求:可以保存自己的自动演示记录 3)每次移动一个盘子,小的只能叠在大的上面。 4)把所有碟子从A塔柱全部移到C塔柱上
如果A盘上的盘子全都移动到了C塔柱上,则游戏成功;否则失败。
(1)标识符命名遵循Windows命名规范。
(2)能够注意各种异常处理,注重提高程序运行效率。 2、系统概述
“汉诺塔”游戏是利用面向对象语言java写的一个简单的游
第 5 页
戏,它的目标是把最左边塔柱上的盘子按一定的规则移动到最右过的塔柱上去,规则如下:
(1)有三根塔柱A,B,C。A杆上有若干盘子(由小到大排列) (2)需要把A上的盘子搬到C上面去(由小到大排列) (3)每次只能移动一个盘子且大的盘子不能叠加到小的盘子上面
3、系统运行环境
任何具有
jre运行环境的计算机,可在netbeans和eclipse
中运行。
三、设计思路
我设计的Hannoi塔,除了要编写的6个java文件(HannoiWindow、AutoMoveDisc、Disc、HandleMouse、Tower、TowerPoint)所给出的类外,还需要java系统提供的一些重要的类,如JButton,JCheckBox等。汉诺塔中有三个座,名字分别为A,B,C。刚开始的时候A塔上有3个大小不等的盘子,这些盘子从座底到座顶按着大小顺序依次摆放在A座上。用户可以用鼠标选中盘子,然后通过拖动鼠标来移动盘子。释放鼠标来放置该盘子。程序要求用户在移动盘子的过程中,不允许把大盘子放置在小盘子的上面,用户最终要完成的是把A座上的全部盘子移动到B座或C座上。用户可以通过Hannoi塔界面提供的改变
第 6 页
盘子数目功能来改变盘子的数目,同时可以改变盘子的大小以及改变盘子和界面的背景颜色,而且还可以选择控制背景音乐的播放。用户可以通过单击Hannoi塔界面上提供的按钮,让程序自动完成把A座上的盘子全部移动到C座上。用户在移动盘子的过程中,可以随时单击Hannoi塔界面上提供的按钮,重新开始。 概要设计
主要要实现的功能有: 完成汉诺塔的“手工移动演示”;完成汉诺塔的“自动移动演示”;增加计时功能,即显示用户完成移动盘子所花费的时间;用户可以设置最大和最小盘子的大小;用户可以设置盘子的数目;用户可以设置盘子的颜色、背景颜色以及背景音乐;用户可以将自动移动盘子的文本信息保存到文件中。
四、详细设计
package Hannoi;
HannoiWindow类
/* HannoiWindow类
* 该类主要功能是设置主界面的布局、相关按钮和监听事件 * */
import java.applet.Applet; import java.applet.AudioClip; import javax.swing.*; import java.awt.*;
import java.awt.event.*;
第 7 页
import java.io.File;
import java.net.MalformedURLException; import java.net.URL;
import javax.swing.JOptionPane;
public class HannoiWindow extends JFrame implements ActionListener{
Tower tower=null; //Tower创建的tower容器,刻画了汉诺塔的结构,放置在主界面的中心
int amountOfDisc=3; //确定tower对象中“盘子”数目(默认为3个) int number=0; Color discColor;
HandleMouse handleMouseTime; Timer time=new Timer(1000,this);
char []towerName={'A','B','C'}; //容器tower中三个座的名字 JMenuBar bar; //菜单棒
JMenu menuGrade,menuDiscNum,menuMM,menuColor; //
boolean tag = true;
File musicfile=new File(\"七里香.mid\");
URL url;
AudioClip clip;
JMenu menumusic; //
JMenuItem
oneGradeItem,twoGradeItem,threeGradeItem,discColorItem,backColorItem,maxDiscWidth,minDiscWidth,DiscNum,musicstart,musicstop;
JButton renew=null;
JButton autoButton=null; JPanel center=new JPanel(); JTextField text;
//----------------------
public JRadioButton cyan,red,green,blue,yellow; public ButtonGroup buttonGroup; //----------------------
HannoiWindow(){ //构造函数,设置主界面布局 super(\"汉诺塔小游戏 (Hannoi Game)\"); tower=new Tower(towerName);
tower.setAmountOfDisc(amountOfDisc);//设置盘子的数量
第 8 页
tower.setMaxDiscWidth(120);//设置最大盘子的宽度 tower.setMinDiscWidth(50);//设置最小盘子的宽度 tower.setDiscHeight(16);//设置盘子的高度 tower.putDiscOnTower();//放置盘子到塔上
add(tower,BorderLayout.CENTER);//增添菜单选项
bar=new JMenuBar();
menuGrade=new JMenu(\"选择关卡\"); menuColor=new JMenu(\"变换颜色\"); menuDiscNum=new JMenu(\"盘子数量\"); menuMM=new JMenu(\"盘子大小\");
oneGradeItem=new JMenuItem(\"第一关_初级\"); twoGradeItem=new JMenuItem(\"第二关_中级\"); threeGradeItem=new JMenuItem(\"第三关_高级\"); discColorItem=new JMenuItem(\"盘子颜色\"); backColorItem=new JMenuItem(\"背景颜色\"); maxDiscWidth=new JMenuItem(\"最大盘子宽度\"); minDiscWidth=new JMenuItem(\"最小盘子宽度\"); DiscNum=new JMenuItem(\"设置盘子数量\");
menuGrade.add(oneGradeItem);//将用户的操作设置返回给相应的变量 menuGrade.addSeparator();
menuGrade.add(twoGradeItem); menuGrade.addSeparator();
menuGrade.add(threeGradeItem); menuColor.add(discColorItem); menuColor.add(backColorItem); menuMM.add(maxDiscWidth); menuMM.add(minDiscWidth); menuDiscNum.add(DiscNum); bar.add(menuGrade); bar.add(menuDiscNum); bar.add(menuMM); bar.add(menuColor); setJMenuBar(bar); //加入代码
menumusic=new JMenu(\"设置背景音乐\"); musicstart=new JMenuItem(\"背景音乐开启\"); musicstop=new JMenuItem(\"背景音乐关闭\");
第 9 页
menumusic.add(musicstart); menumusic.add(musicstop); bar.add(menumusic); try { url=musicfile.toURL(); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } clip=Applet.newAudioClip(url); clip.play(); //
setJMenuBar(bar); //加入下2行代码
musicstart.addActionListener(this); musicstop.addActionListener(this); //
oneGradeItem.addActionListener(this); twoGradeItem.addActionListener(this); threeGradeItem.addActionListener(this); DiscNum.addActionListener(this);
maxDiscWidth.addActionListener(this); minDiscWidth.addActionListener(this); discColorItem.addActionListener(this); backColorItem.addActionListener(this); renew=new JButton(\"重新开始\");
renew.addActionListener(this);
autoButton=new JButton(\"自动演示\");
text=new JTextField(\"您的游戏时间:0秒\
autoButton.addActionListener(this); JPanel north=new JPanel();
String mess=\"将全部盘子从 \"+towerName[0]+\" \"+towerName[1]+
\" 座或 \"+towerName[2]+\" 座\";
JLabel hintMess=new JLabel(mess,JLabel.CENTER);
north.add(renew); north.add(autoButton);
第 10 页
座搬运到 north.add(hintMess); north.add(text);
add(north,BorderLayout.NORTH); //----------------------
buttonGroup=new ButtonGroup(); cyan = new JRadioButton(\"原色\"); red = new JRadioButton(\"红色\"); green = new JRadioButton(\"绿色\"); blue = new JRadioButton(\"蓝色\"); yellow =new JRadioButton(\"黄色\");
cyan.setFont(new Font(\"楷体_GB2312\ red.setFont(new Font(\"楷体_GB2312\ green.setFont(new Font(\"楷体_GB2312\ blue.setFont(new Font(\"楷体_GB2312\ yellow.setFont(new Font(\"楷体_GB2312\
buttonGroup.add(cyan); buttonGroup.add(red); buttonGroup.add(green); buttonGroup.add(blue); buttonGroup.add(yellow); rHandler h=new rHandler(); //创建监听器
cyan.addItemListener(h); red.addItemListener(h); green.addItemListener(h); blue.addItemListener(h); yellow.addItemListener(h); JPanel southColor=new JPanel(); southColor.add(cyan); southColor.add(red); southColor.add(green); southColor.add(blue); southColor.add(yellow); add(southColor,BorderLayout.SOUTH); setResizable(false); //---------------------- setResizable(false); setVisible(true);
setBounds(300,150,680,480); validate();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); time.start(); }
public void actionPerformed(ActionEvent e){ //各种动作事件的监听处理
if(e.getSource()==oneGradeItem||e.getSource()==twoGradeItem||e.getSource()==threeGradeItem){ if(e.getSource()==oneGradeItem) amountOfDisc=3;//设置初级盘子
第 11 页
为3个 if(e.getSource()==twoGradeItem) amountOfDisc=4;//设置中级盘子为4个 if(e.getSource()==threeGradeItem) amountOfDisc=5; //设置高级盘子为5个
if(time.isRunning()==false) {time.restart();handleMouseTime.ALL=0;} number=0; text.setText(\"您的游戏时间:\"+number+\"秒\");
tower.setAmountOfDisc(amountOfDisc); tower.putDiscOnTower(); }else if(e.getSource()==renew){ //\"重新开始\"按钮动作监听处理方法
if(time.isRunning()==false) {time.restart();handleMouseTime.ALL=0;} number=0;//初始化计时器为0
text.setText(\"您的游戏时间:\"+number+\"秒\");//读计数器时间
tower.setAmountOfDisc(amountOfDisc); tower.putDiscOnTower();
}else if(e.getSource()==autoButton){ //\"自动演示\"按钮动作监听处理方法
tower.setAmountOfDisc(amountOfDisc); tower.putDiscOnTower();
int x=this.getBounds().x+this.getBounds().width; int y=this.getBounds().y;
tower.getAutoMoveDisc().setLocation(x,y);
tower.getAutoMoveDisc().setSize(380,this.getBounds().height); tower.getAutoMoveDisc().setVisible(true);
}else if(e.getSource()==discColorItem||e.getSource()==backColorItem){ Color Col=getBackground(); Col=JColorChooser.showDialog(this, \"颜色变换调色板\
discColor=Col;
if(e.getSource()==discColorItem){
if(time.isRunning()==false) {time.restart();handleMouseTime.ALL=0;} number=0;
text.setText(\"您的游戏时间:\"+number+\"秒\"); tower.disccolor(discColor);
tower.setAmountOfDisc(amountOfDisc);
第 12 页
tower.putDiscOnTower(); }else if(e.getSource()==backColorItem){ tower.setBackground(Col); JOptionPane.showMessageDialog(null, \"背景颜色也可以用窗口5个单选按钮变换!\消息提示框\
}
}else if(e.getSource()==DiscNum){ int n; String strNum=JOptionPane.showInputDialog(\"请输入您要的盘子数量(有效长度1~10)\");
n=Integer.parseInt(strNum); if(n<=10&&n>=1){ if(time.isRunning()==false) {time.restart();handleMouseTime.ALL=0;} number=0; text.setText(\"您的游戏时间:\"+number+\"秒\");
amountOfDisc=n; tower.setAmountOfDisc(amountOfDisc); tower.putDiscOnTower(); }
}else if(e.getSource()==maxDiscWidth){ int width; String str=JOptionPane.showInputDialog(\"请输入最大盘子的长度(有效长度100~180)\");
width=Integer.parseInt(str); if(width<=180&&width>=100){ if(time.isRunning()==false) {time.restart();handleMouseTime.ALL=0;} number=0; text.setText(\"您的游戏时间:\"+number+\"秒\");
tower.setMaxDiscWidth(width); tower.setAmountOfDisc(amountOfDisc); tower.putDiscOnTower(); }
}else if(e.getSource()==minDiscWidth){ int width; String str=JOptionPane.showInputDialog(\"请输入最小盘子的长度(有效
第 13 页
长度10~80)\");
width=Integer.parseInt(str); if(width<=80&&width>=10){ if(time.isRunning()==false) {time.restart();handleMouseTime.ALL=0;} number=0; text.setText(\"您的游戏时间:\"+number+\"秒\");
tower.setMinDiscWidth(width); tower.setAmountOfDisc(amountOfDisc); tower.putDiscOnTower(); } } if(e.getSource()==time){ if(handleMouseTime.Start==1){text.setText(\"您的游戏时间:\"+number+\"秒\");number++;}
if(handleMouseTime.ALL==1) { time.stop(); handleMouseTime.Start=0; JOptionPane.showMessageDialog(null, \"祝贺您,闯关成功!\消息框\
} }
////////////////////////////
else if(e.getSource()==musicstart) {
if(tag == false) { try { url=musicfile.toURL(); } catch (MalformedURLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); }
clip=Applet.newAudioClip(url); clip.play(); tag = true; }
第 14 页
}
else if(e.getSource()==musicstop) {
if(tag == true) {
clip.stop(); tag = false; } }
////////////////////////////// validate(); }
public class rHandler implements ItemListener{ public void itemStateChanged(ItemEvent event){ if(cyan.isSelected()) tower.setBackground(new Color(200,236,236)); else if(red.isSelected()) tower.setBackground(Color.red); else if(green.isSelected()) tower.setBackground(Color.green); else if(blue.isSelected()) tower.setBackground(Color.blue); else if(yellow.isSelected()) tower.setBackground(Color.yellow); } }
public static void main(String args[]){ new HannoiWindow(); } }
AutoMoveDisc类
/* AutoMoveDisc类
* 该类实现了ActionListener接口,创建对象 autoMoveDisc 是Tower的成员之一 * */
import java.io.*; import java.awt.*;
第 15 页
import java.awt.event.*; import javax.swing.*;
import javax.swing.filechooser.*;
import javax.swing.text.BadLocationException;
public class AutoMoveDisc extends JDialog implements ActionListener{ int amountOfDisc=3; //盘子的数量
TowerPoint [] pointA,pointB,pointC; //存放Tower容器中的塔点对象的引用; char [] towerName; //存放Tower容器中的towername的引用 Container con; //Container容器对象,用来存放tower对象引用 StringBuffer moveStep; //存放移动盘子的步骤,用连续两个字符表示 JTextArea showStep; //显示盘子移动步骤的文本区 JButton bStart,bStop,bContinue,bClose,bSave; //相关按钮 Timer time; //时间计时器
int i=0,number=0;
AutoMoveDisc(Container con){ //构造函数,完成界面布局 setModal(true);
setTitle(\"自动演示搬盘子过程:\"); this.con=con;
moveStep=new StringBuffer(); time=new Timer(1000,this); time.setInitialDelay(10);
showStep=new JTextArea(15,20); bStart=new JButton(\"演示\"); bStop=new JButton(\"暂停\"); bContinue=new JButton(\"继续\"); bSave=new JButton(\"保存\");
bClose=new JButton(\"关闭\"); bStart.addActionListener(this); bStop.addActionListener(this); bContinue.addActionListener(this); bSave.addActionListener(this);
bClose.addActionListener(this); JPanel south=new JPanel();
south.setLayout(new FlowLayout()); south.add(bStart); south.add(bStop); south.add(bContinue); south.add(bSave);
第 16 页
south.add(bClose);
add(new JScrollPane(showStep),BorderLayout.CENTER); add(south,BorderLayout.SOUTH);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); towerName=new char[3];
addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ time.stop(); setVisible(false); } }; }
public void setPointA(TowerPoint [] pointA){
//将tower容器中的pointA对象的引用传递给该对象的pointA,以便autoMoveDisc对象自动移动踏上的盘子disc
this.pointA=pointA; }
public void setPointB(TowerPoint [] pointB){ this.pointB=pointB; }
public void setPointC(TowerPoint [] pointC){ this.pointC=pointC; }
public void setTowerName(char name[]){//给每一个座tower命名
if(name[0]==name[1]||name[0]==name[2]||name[1]==name[2]){ towerName[0]='A'; towerName[1]='B'; towerName[2]='C'; }
else
towerName=name; }
public void setAmountOfDisc(int n){ //设置盘子数目变量 amountOfDisc的值 amountOfDisc=n; }
public void actionPerformed(ActionEvent e) { //实现文本显示盘子移动步骤事件 if(e.getSource()==time){
第 17 页
number++;
char cStart,cEnd;
if(i<=moveStep.length()-2){ cStart=moveStep.charAt(i); cEnd=moveStep.charAt(i+1);
showStep.append(\"(\"+number+\")从\"+cStart+\"座搬一个盘子到\"+cEnd+\"座\\n\");
autoMoveDisc(cStart,cEnd); }
i=i+2;
if(i>=moveStep.length()-1){ time.stop(); } }
else if(e.getSource()==bStart){ if(moveStep.length()==0){ if(time.isRunning()==false){ i=0;
moveStep=new StringBuffer();
setMoveStep(amountOfDisc,towerName[0],towerName[1],towerName[2]); number=0;
time.start(); } } }
else if(e.getSource()==bStop){ if(time.isRunning()==true) time.stop(); }
else if(e.getSource()==bContinue){ if(time.isRunning()==false) time.restart(); }
else if(e.getSource()==bSave){ JFileChooser save=new JFileChooser(); int SV=save.showSaveDialog(null); if(SV==JFileChooser.APPROVE_OPTION){
第 18 页
File file=save.getSelectedFile(); try{
BufferedWriter writer=new BufferedWriter(new FileWriter(file.getPath())); writer.write(showStep.getText()); writer.flush(); writer.close(); }catch(FileNotFoundException fnoe){} catch(IOException ioe){} } }
else if(e.getSource()==bClose){ time.stop();
setVisible(false); } }
private void setMoveStep(int amountOfDisc,char one,char two,char three){ //记录盘子移动步骤的递归算法
if(amountOfDisc==1){
moveStep.append(one);
moveStep.append(three); } else{
setMoveStep(amountOfDisc-1,one,three,two); moveStep.append(one); moveStep.append(three);
setMoveStep(amountOfDisc-1,two,one,three); } }
private void autoMoveDisc(char cStart,char cEnd){ //盘子从 cStar 座自动移动到 cEnd 座的实现 Disc disc=null;
if(cStart==towerName[0]){ //盘子属于A座
for(int i=0;i pointA[i].setHaveDisc(false); //设置该塔点为起始点,并置空 break; } 第 19 页 } } if(cStart==towerName[1]){ //盘子属于B座 for(int i=0;i for(int i=0;i if(cEnd==towerName[0]){ //查找盘子拖动到的目标座,是否为A座 for(i=0;i if(i>0){ endPoint=pointA[i-1]; break; } else if(i==0) break; } } if(i==pointA.length) endPoint=pointA[pointA.length-1]; } if(cEnd==towerName[1]){ for(i=0;i if(pointB[i].isHaveDisc()==true){ if(i>0){ endPoint=pointB[i-1]; break; } else if(i==0) break; } } if(i==pointB.length) endPoint=pointB[pointB.length-1]; } if(cEnd==towerName[2]){ for(i=0;i else if(i==0) break; } } if(i==pointC.length) endPoint=pointC[pointC.length-1]; } if(endPoint!=null&&disc!=null){ endPoint.putDisc(disc,con); endPoint.setHaveDisc(true); } } } Disc类 /* Disc类 * javax.swing包中JButton的子类,负责实现Hannoi中的“盘子“视图 */ 第 21 页 import javax.swing.*; import java.awt.*; public class Disc extends JButton{ int number; //创建盘子上的数字号码,以数字大小来确定盘子的大小,数字大的盘子大于数字小的盘子 TowerPoint point; //TowerPoint类对象,是盘在”塔点“,用于标明盘子所在的塔点位置 Disc(Color col){ setBackground(col); //设置盘子的背景颜色 } public void setNumber(int n){ //设置盘子上的数字号码 number=n; } public int getNumber(){ //返回盘子上的数字号码 return number; } public void setPoint(TowerPoint p){ //设置盘子所在的塔点位置 point=p; } public TowerPoint getPoint(){//返回盘子的塔点位置 return point; } } HandleMouse类 /* HandleMouse类 * 该类主要实现MouseListener好MouseMotionListener接口,其中创建的对象 handleMouse为tower容器的成员之一, * 主要功能是:监视tower容器中Disc盘子对象上的鼠标事件。(当用户用鼠标单击tower中的盘子disc,并拖动鼠标 * 时,handleMouse对象负责给出移动盘子的有关算法) * */ import java.awt.event.*; import java.awt.*; import javax.swing.Timer; public class HandleMouse implements MouseListener,MouseMotionListener { //有MouseListener,MouseMotionListener两个接口 第 22 页 TowerPoint [] pointA,pointB,pointC; //塔点TowerPoint数组,分别用来存放tower容器中的塔点对象的引用 TowerPoint startPoint=null,endPoint=null; //用来存放鼠标事件拖动盘子的起始塔点和目标塔点; int leftX,leftY,x0,y0,timeNum; //记录移动盘子的塔点坐标 boolean move=false,countTime=false; //记录盘子的移动状态,是否移动 static int ALL=0,Start=0; Container con; //Container容器对象,用来存放tower对象引用; HandleMouse(Container con){ //构造方法,将tower容器的引用传递给handleMouse对象中的容器con,以便handleMouse对象在con中移动盘子 this.con=con; } public void setPointA(TowerPoint [] pointA){ //将tower容器中的pointA对象的引用传递给该对象中的pointA,以便handleMouse对象移动塔点上的盘子 this.pointA=pointA; } public void setPointB(TowerPoint [] pointB){ this.pointB=pointB; } public void setPointC(TowerPoint [] pointC){ this.pointC=pointC; } public void mousePressed(MouseEvent e){ //实现盘子按下鼠标监听事件的相关操作 move=false; //盘子处于未移动(静止状态) Disc disc=null; disc=(Disc)e.getSource(); //获取触动鼠标事件的盘子 startPoint=disc.getPoint(); //获取移动盘子的起始塔点 x0=e.getX(); //起始塔点的横坐标 y0=e.getY(); //起始塔点的纵坐标 int m=0; for(int i=0;i if(m>0&&(pointA[m-1].isHaveDisc()==false)){ //如果移动第二、三层盘子时,必须确保该塔点上没有盘子 第 23 页 move=true; //盘子开始移动 break; } else if(m==0){ //m=0,即移动的盘子是A座最上端的盘子,可以移动; move=true; //盘子开始移动 break; } } } for(int i=0;i if(m>0&&(pointB[m-1].isHaveDisc()==false)){ move=true; break; } else if(m==0){ move=true; break; } } } for(int i=0;i if(m>0&&(pointC[m-1].isHaveDisc()==false)){ move=true; break; } else if(m==0){ move=true; break; } } } } 第 24 页 public void mouseMoved(MouseEvent e){} public void mouseDragged(MouseEvent e){ //实现盘子拖动鼠标监听事件 Disc disc=null; disc=(Disc)e.getSource(); //获取触动鼠标事件的盘子 leftX=disc.getBounds().x; //移动盘子的横坐标 leftY=disc.getBounds().y; //移动盘子的纵坐标 int x=e.getX(); int y=e.getY(); leftX=leftX+x; leftY=leftY+y; if(move==true) disc.setLocation(leftX-x0,leftY-y0); } public void mouseReleased(MouseEvent e){ //实现移动盘子的鼠标释放监听事件 Disc disc=null; disc=(Disc)e.getSource(); //获取触动鼠标事件的盘子 Rectangle rect=disc.getBounds();//拖动盘子的信息记录在矩形rect中 boolean location=false; //盘子是否放置状态 int x=-1,y=-1; for(int i=0;i endPoint=pointA[i]; //若坐标 x,y 在该塔点上,则该塔点为目标塔点 if(i==pointA.length-1&&endPoint.isHaveDisc()==false){ //如果是最底层塔点,且该塔点上没有盘子,可以直接放下 location=true; break; } else if(i //该塔点上所要放盘子的数号小于下一层的盘子的数号,这是汉诺塔的规则 >disc.getNumber()){ location=true; break; 第 25 页 } } } for(int i=0;i else if(i } } } for(int i=0;i else if(i } } 第 26 页 } if(endPoint!=null&&location==true){ //目标塔点非空,且盘子已放置为真 endPoint.putDisc(disc,con); //放置拖动盘子到目标位置 startPoint.setHaveDisc(false); //将起始塔点上的盘子置空 } else startPoint.putDisc(disc,con); //否则,拖动失败,将盘子放置起始塔点处 int bNum=0,cNum=0; for(int i=0;i public void mouseEntered(MouseEvent e){} public void mouseExited(MouseEvent e){} public void mouseClicked(MouseEvent e){} } Tower类 /* Tower类 * 继承了javax.swing包中JPanel容器的一个子类,创建对象 tower 是HannoiWindow窗口的成员之一, * 被添加到HannoiWidow窗口中心位置,是整一个视图的框架概况 */ import javax.swing.*; import java.awt.*; public class Tower extends JPanel{ int amountOfDisc=3; //确定Tower对象中的盘子数量,即确定Tower对象中Disc类型数组disc的长度 Disc [] disc; //Disc数组,每一个单元中包含一个Disc对象,用于存放多个盘子 Int maxDiscWidth,minDiscWidth,discHeight; 第 27 页 //maxDiscWidth,minDiscWidth用于记录最大、最小盘子的宽度。discHeight记录每一个盘子的高度; char [] towerName; //char数组的towerName,用于确定tower中每一个座的名字,默认为3个:A、B和C TowerPoint [] pointA,pointB,pointC; //三个座上的塔点数组,用于确定每一个座上的每一个塔点信息 HandleMouse handleMouse; //创建鼠标时间监视器,用来监视disc数组中的Disc对象上的触发的鼠标事件 AutoMoveDisc autoMoveDisc;//用于创建AutoMoveDisc 自动演示对话框 Color color; Tower(char [] towerName){ //Tower 类构造函数,设置tower对象座的名字 handleMouse=new HandleMouse(this); //初始化座上的鼠标监视器 this.towerName=towerName; //设置tower对象座的名字 setLayout(null); //设置不采用任何布局方式 setBackground(new Color(200,236,236)); //设置背景颜色 } public void disccolor(Color col){color=col;} public void setAmountOfDisc(int number){ //设置一个tower对象的盘指数 if(number<=1) amountOfDisc=1; //如果参数 number小于等于1,盘指数设为 1; else amountOfDisc=number; } public void setMaxDiscWidth(int m){ //设置最大盘子的宽度 maxDiscWidth=m; } public void setMinDiscWidth(int m){ //设置最小盘子的宽度 minDiscWidth=m; } public void setDiscHeight(int h){ //设置盘子的高度 discHeight=h; } public AutoMoveDisc getAutoMoveDisc(){//获得自动演示 autoMoveDisc 的返回值 return autoMoveDisc; } public void putDiscOnTower(){//将盘子放置在某一个座上 removeDisk();//移除了该座上的盘子 int n=(maxDiscWidth-minDiscWidth)/amountOfDisc; 第 28 页 disc=new Disc[amountOfDisc]; for(int i=0;i int diskwidth=minDiscWidth+i*n; disc[i].setSize(diskwidth,discHeight); //每一个 disc 的宽度 diskwidth ,高度 diskHeight disc[i].addMouseListener(handleMouse); //添加鼠标监听事件 disc[i].addMouseMotionListener(handleMouse); } pointA=new TowerPoint[amountOfDisc]; //每一个座上都声明添加若干个塔点 pointB=new TowerPoint[amountOfDisc]; pointC=new TowerPoint[amountOfDisc]; int vertialDistance=discHeight; //获取disc的高度,以便确定塔点纵坐标 for(int i=0;i vertialDistance=vertialDistance+discHeight; //纵坐标自增 } vertialDistance=discHeight; //B座的第一个塔点纵坐标设置为与A座第一个塔点水平 for(int i=0;i vertialDistance=discHeight; for(int i=0;i for(int i=0;i handleMouse.setPointA(pointA); //将tower容器中的pointA对象的引用传递给handleMouse的pointA,以便handleMouse对象可以移动塔点上的盘子disc handleMouse.setPointB(pointB); handleMouse.setPointC(pointC); 第 29 页 autoMoveDisc=new AutoMoveDisc(this); //声明自动演示对话框变量 autoMoveDisc autoMoveDisc.setTowerName(towerName); //设置座名 A B C autoMoveDisc.setAmountOfDisc(amountOfDisc); //设置盘子数目变量 amountOfDisc的值 autoMoveDisc.setPointA(pointA); //将tower容器中的pointA对象的引用哦该传递给该对象至哦哦那个的pointA,以便autoMoveDisc对象自动移动踏上的盘子disc autoMoveDisc.setPointB(pointB); autoMoveDisc.setPointC(pointC); validate(); //确保组件具有有效的布局 repaint(); } public void removeDisk(){ //移除tower容器中的盘子 if(pointA!=null){ //如果塔点 pointA 的盘指 disc 不为空,即塔点有盘子 for(int i=0;i pointB[i].removeDisc(pointB[i].getDiscOnPoint(),this); pointC[i].removeDisc(pointC[i].getDiscOnPoint(),this); } } } public void paintComponent(Graphics g){ //绘画出塔点的位置和其他相关视图 super.paintComponent(g); //调用父类方法paintComponent() int x1,y1,x2,y2; //坐标变量 x1=pointA[0].getX(); //获取座A上第一个塔点横坐标 y1=pointA[0].getY()-discHeight/2; //获取座A上的第一个塔点的纵坐标,同时减去一半的盘子高度 x2=pointA[amountOfDisc-1].getX();//获取座A上第三个塔点横坐标 y2=pointA[amountOfDisc-1].getY()+discHeight/2; //获取座A上的第三个塔点的纵坐标,同时加上盘子的一半高度 g.drawLine(x1,y1,x2,y2); //画出经过座A三个塔点的直线 x1=pointB[0].getX(); y1=pointB[0].getY()-discHeight/2; x2=pointB[amountOfDisc-1].getX(); y2=pointB[amountOfDisc-1].getY()+discHeight/2; g.drawLine(x1,y1,x2,y2); 第 30 页 x1=pointC[0].getX(); y1=pointC[0].getY()-discHeight/2; x2=pointC[amountOfDisc-1].getX(); y2=pointC[amountOfDisc-1].getY()+discHeight/2; g.drawLine(x1,y1,x2,y2); g.setColor(Color.BLACK); x1=pointA[amountOfDisc-1].getX()-maxDiscWidth/2; //获取座A第三个塔点的横坐标,同时向左移动 最大盘子宽度的一半距离 y1=pointA[amountOfDisc-1].getY()+discHeight/2; //获取座A第三个塔点的纵坐标,同时,向下移动盘子高度的一半距离 x2=pointC[amountOfDisc-1].getX()+maxDiscWidth/2; //获取座C第三个塔点的横坐标,同时,向右移动最大盘子宽度等等一般距离 y2=pointC[amountOfDisc-1].getY()+discHeight/2; //获取座C第三个塔点的纵坐标,同时 int length=x2-x1,height=6; //设置座底下的长矩形宽度和高度 g.fillRect(x1,y1,length,height); //用以上坐标画出实心矩形 int size=5; for(int i=0;i pointA[amountOfDisc-1].getX(),pointA[amountOfDisc-1].getY()+50); g.drawString(towerName[1]+\"座\ pointB[amountOfDisc-1].getX(),pointB[amountOfDisc-1].getY()+50); g.drawString(towerName[2]+\"座\ pointC[amountOfDisc-1].getX(),pointC[amountOfDisc-1].getY()+50) } } TowerPoint 类 /* TowerPoint 类 * 用来表明塔点的位置坐标,位置通过tower容器的pointComponent(Graphics g)方法绘制。绘制形状为一个个小圆点 */ import java.awt.*; 第 31 页 public class TowerPoint{ int x,y; //塔点位置坐标变量 x,y boolean haveDisc; //布尔型变量,用于判断该塔点位置是否有盘子 Disc disc=null; //引用变量,用于存放当前位于该塔点的盘子的引用,如果disc存放null,表示没有盘子 public TowerPoint(int x,int y){ //构造函数,创建塔点对象,设置塔点坐标 this.x=x; this.y=y; } public boolean isHaveDisc(){//判断当前塔点是否有盘子 return haveDisc; //有盘子时,返回true;无盘子时,返回false } public void setHaveDisc(boolean boo){ //通过参数boo值设置当前塔点是否有盘子 haveDisc=boo; } public int getX(){ //返回当前塔点的x轴坐标 return x; } public int getY(){ //返回当前塔点的y轴坐标 return y; } public boolean equals(TowerPoint p){ //判断当前塔点和参数塔点坐标是否吻合 if(p.getX()==this.getX()&&p.getY()==this.getY()) //坐标比较 return true; //坐标相同,塔点一致 else return false; //坐标不同,塔点异同 } public void putDisc(Component com,Container con){ //放置参数com的盘子到当前塔点 disc=(Disc)com; //参数 com 指定的盘子disc con.setLayout(null); //容器con 加载为空 con.add(disc); //容器加载 com 指定的盘子 disc int w=disc.getBounds().width; //w 存放盘子disc的宽度 int h=disc.getBounds().height; //h 存放盘子disc的高度 disc.setBounds(x-w/2,y-h/2,w,h); //将盘子挪动,放置到以当前塔点为中心的坐标位置上 haveDisc=true; //调用 haveDisc,设置其值为true,表示当前塔点已有盘子 disc.setPoint(this); //设置移动之后盘子的塔点信息 con.validate();//容器 con 信息生效 第 32 页 } public Disc getDiscOnPoint(){ //访问当前塔点的盘子 return disc; //返回当前塔点的盘子 } public void removeDisc(Component com,Container con){ //移除当前塔点的盘子 if(com!=null) //判断当前塔点上的是否有盘子 con.remove(com); //信息非空,有盘子,移除该塔点的盘子 con.validate(); //确保组件具有有效的布局 } } 五、运行调试与分析讨论 主菜单: 第 33 页 用户自己演示结果: 第 34 页 完成游戏: 系统自动演示结果: 第 35 页 改进想法:增加排行榜功能 六、设计体会与小结 1.设计体会 这个Hannoi塔游戏就是有用户自己游戏功能,自动演示功能,改变盘子数目功能、改变盘子大小功能、改变背景颜色功能、改变盘子颜色功能、计时器功能以及保存信息到文件功能,参阅了大量的资料,也不停的向同学们请教,最后通过努力终于把这个程序成功搞定.自然这期间除了辛苦也让我明白了很多,有些小问题也是不容忽视的,就如公共类只能有一个,而且必须要有.及类名必须要同公共类名相同.否则程序就无法运行。 第 36 页 2.小结 经过编写这个Hannoi塔游戏,我认识到应该注意细节问题,虽然是很小的问题,但可以提高自己编程的能力,尤其是在图形界面的布局以及时间动态相应编程,而且还可以培养自己编程的严谨性,同时还可以为以后的编程积累经验。除此之外让我对java变成语言又有了更深一步的了解,曾经对于一些类名的似是而非,现在通过编写这个程序让我逐渐变得明朗了许多,能轻松分清并创建父类子类,以及一些类和方法的用途,总之通过这次课程设计真的让我学到了很多。在以后的学习中我会更加注重动手能力的训练。 参考文献 (1)丁振凡.《 Java语言实用教程(第2版)[M]》 清华大学出版社. 2008.1 (2)丁振凡《Java语言实用教程实验指导》北京邮电大学出版社 2007-7 (3)杨晓燕《Java面向对象程序设计》电子工业出版社 2012-2 (4)参考网址:http//www.CSDN.net 第 37 页 致谢 课程设计完成期间,我得到了老师、同学和朋友等多方面的支持和帮助,他们在我课设过程中都给予了我极大的帮助,在此对他们表示最衷心的感谢!拙作虽己完成,但由于我才疏学浅,时间和参考资料的有限,难免有些不足之处,恳请老师海涵! 第 38 页 第 39 页 因篇幅问题不能全部显示,请点此查看更多更全内容