网管联盟 | 网管论坛 | 网管u家 | 网管博客 | 网管软件 | 网管求职 | 小游戏 | 网管搜索 | 网管原创 | 网管聚合 | 网管读摘 | 网管焦点 | 世界素材 | 会员投稿 | 会员中心 
中国网管联盟
Windows Linux Cisco 网络技术 数据库 黑客攻防 DotNet Java PHP 认证 新闻资讯 服务器 存储资讯 网络设备 网管学堂 技术专题 焦点 网吧频道
 当前位置: > bitsCN.com > JAVA > 核心技术 > 高级编程 > Java源代码案例--骑士巡游问题  

Java源代码案例--骑士巡游问题

2006-03-14  作者:BitsCN整理  来源:中国网管联盟  点评 投稿 收藏

  本文展示了一个KT(Knight’s Tour)小程序, 用来演示一个限制版的骑士巡游问题。 骑士并不是从任何一个方格开始, 而是从角落上的四个方格之一开始。这个applet的界面如图1所示:

resized image 网管u家u.bitscn@com


  图1: KT的界面由一个棋盘, 一个选择开始方格的组合框和一个开始游历的按钮组成


  在启动巡游之前, 先从组合框中选择骑士开始的角落。 程序响应会让骑士显示在正确的角落上(默认情况下骑士在最左上角)。 然后单击"Take the Tour"(开始巡游)按钮来开始整个巡游过程。 按钮和组合框在巡游过程中都将被禁止。巡游过程是怎么样的呢? 图2展现了一系列的线段(轨迹), 每一个线段都是随着骑士在棋盘的行动从上一个方格的中心到当前方格的中心。
                

网管联盟bitsCN_com

image

网管bitscn_com


  图2: 巡游从左上角开始

  现在你已经看到了这个小程序的界面和巡游过程, 让我们开始学习它的源代码吧。

  清单1.  KT.java


网管联盟bitsCN_com

// KT.java

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.net.URL;
import java.util.ArrayList;

public class KT extends Applet
{
   // 线程延迟以毫秒为单位

   public final static int DELAY = 500;

   // 开始骑士巡游线程

   private Thread thd;

   // 初始化小程序

   public void init ()
   {
      // 创建一个标签对象来标明小程序的标题

      Label lblTitle = new Label ("Knight's Tour", Label.CENTER);
      lblTitle.setFont (new Font ("Arial", Font.BOLD, 18));

      // 把标签对象加到小程序的面板容器

      add (lblTitle);

      // 创建一个ChessBoard对象,它具有显示一个棋盘、移动骑士到
// 任何方格并留下骑士巡游轨迹的能力. 网管u家u.bitsCN.com

      final ChessBoard cb = new ChessBoard (this);

      //把ChessBoard对象加入到小程序的面板容器

      add (cb);

      // 创建一个Panel对象来保存Label,Choice和按钮对象.

      Panel pnl = new Panel ();

      //创建一个标签来标明Choice对象并把它添加到Panel中

      pnl.add (new Label ("Choose starting position:"));

      //创建一个Choice对象,用来选择骑士的开始位置(棋盘的四个角落)
//并把它添加到Panel中.

      final Choice c = new Choice ();
      c.add ("Upperleft corner");
      c.add ("Upperright corner");

      //创建Choice的item listener,这个监听器按选择结果来重设骑士的开始位置.
中国网管论坛bbs.bitsCN.com


      c.addItemListener (new ItemListener ()
                         {
                             public void itemStateChanged (ItemEvent e)
                             {
                                Choice c = (Choice) e.getSource ();

                                if (c.getSelectedIndex () == 0) 网管bitscn_com
                                    cb.moveKnight (1);
                                else
                                    cb.moveKnight (8);

                                cb.reset ();
                             } 网管下载dl.bitscn.com
                         });
      pnl.add (c);

      //把Panel加入到小程序的面板容器

      add (pnl);

      //创建一个按钮对象用来开始骑士巡游.

      final Button btn = new Button ("Take the Tour");

      //创建按钮的Action listener(动作监听器),用来确定骑士巡游的位置.
//按照规则将骑士从一个位置移动到另一个位置.

      ActionListener al;

      al = new ActionListener ()
           {
               public void actionPerformed (ActionEvent e)
               { 中国网管论坛bbs.bitsCN.com
                  Runnable r;
                  r = new Runnable ()
                      {
                          int [] boardPositions1 =
                          {
                              1, 18, 33, 50, 60, 54, 64, 47,
                             32, 15,  5, 11, 17, 34, 49, 59, 中国网管论坛bbs.bitsCN.com
                             53, 63, 48, 31, 16,  6, 12,  2,
                             19, 25, 42, 57, 51, 61, 55, 40,
                             23,  8, 14,  4, 10, 27, 44, 38,
                             21, 36, 46, 29, 35, 41, 58, 52,
                             62, 56, 39, 24,  7, 13,  3,  9, 网管联盟bitsCN_com
                             26, 43, 37, 22, 28, 45, 30, 20
                          };

                          int [] boardPositions2 =
                          {
                              8, 23, 40, 55, 61, 51, 57, 42,
                             25, 10,  4, 14, 24, 39, 56, 62, 网管下载dl.bitscn.com
                             52, 58, 41, 26,  9,  3, 13,  7,
                             22, 32, 47, 64, 54, 60, 50, 33,
                             18,  1, 11,  5, 15, 30, 45, 35,
                             20, 37, 43, 28, 38, 48, 63, 53,
                             59, 49, 34, 17,  2, 12,  6, 16, 网管联盟bitsCN@com
                             31, 46, 36, 19, 29, 44, 27, 21
                          };

                          public void run ()
                          {
                             cb.reset ();

                             // thd用来检查用户离开小程序网页 网管论坛bbs_bitsCN_com
                 // 以便停止小程序的运行.

                             for (int i = 0; i < boardPositions1.length &&
                                  thd != null; i++)
                             {
                                  if (c.getSelectedIndex () == 0)
                                      cb.moveKnight (boardPositions1 [i]); 网管联盟bitsCN@com
                                  else
                                      cb.moveKnight (boardPositions2 [i]);

                                  try
                                  {
                                      Thread.sleep (DELAY);
网管联盟bitsCN@com

                                  }
                                  catch (InterruptedException e2)
                                  {
                                  }
                             }

中国网管论坛bbs.bitsCN.com



                             c.setEnabled (true);
                             btn.setEnabled (true);
                          }
                      };

                  c.setEnabled (false);
                  btn.setEnabled (false);

                  thd = new Thread (r); 网管联盟bitsCN@com
                  thd.start ();
               }
           };
      btn.addActionListener (al);

      //添加按钮到小程序面板容器

      add (btn);
   }

   //停止小程序

   public void stop ()
   {
      //用户离开网页时必须停止”骑士巡游”线程

      thd = null;
   }
}

final class ChessBoard extends Canvas
{
   //非白色方格的颜色

   private final static Color SQUARECOLOR = new Color (195, 214, 242);

   //棋盘方格的尺寸

   private final static int SQUAREDIM = 40;

   //棋盘方格的尺寸(包括黑边框) 网管网www.bitscn.com

   private final static int BOARDDIM = 8 * SQUAREDIM + 2;

   //棋盘左上角的左坐标(X坐标)

   private int boardx;

   //棋盘左上角的顶坐标(Y坐标)

   private int boardy;

   //棋盘长度

   private int width;

   // 棋盘宽度

   private int height;

   // 图像缓冲

   private Image imBuffer;

   // Graphics context associated with image buffer.

   private Graphics imG;

   // 骑士图像

   private Image imKnight;

   // 骑士图像的长度

   private int knightWidth;

   // 骑士图像的宽度

   private int knightHeight;

   //骑士轨迹的坐标

   private ArrayList trail;

   // Left coordinate of knight rectangle origin (upper-left corner).

   private int ox;

网管网www.bitscn.com



   // Top coordinate of knight rectangle origin (upper-left corner).

   private int oy;

   // 创建ChessBoard的Applet--调用它的getImage()和getDocumentBase()方法,
// 并且我们将使用它作为drawImage()方法的image observer

   Applet a;

   // 构造棋盘

   ChessBoard (Applet a)
   {
      // 确定部件的大小

      width = BOARDDIM+1;
      height = BOARDDIM+1;

      // 初始化棋盘, 使它处于中央

      boardx = (width - BOARDDIM) / 2 + 1;
      boardy = (height - BOARDDIM) / 2 + 1;

      //使用MediaTracker来确保骑士图像在我们获取它的长和宽之前被完全导入

      MediaTracker mt = new MediaTracker (this);

      // 导入骑士图像
中国网管联盟bitsCN.com


      imKnight = a.getImage (a.getDocumentBase (), "knight.gif");
      mt.addImage (imKnight, 0);

      try
      {
         mt.waitForID (0);
      }
      catch (InterruptedException e) {}

      //获得骑士的长度和宽度, 帮助骑士定位于方格中央

      knightWidth = imKnight.getWidth (a);
      knightHeight = imKnight.getHeight (a);

      //初始化骑士图像, 使骑士定位于左上角方格的中央

      ox = boardx + (SQUAREDIM - knightWidth) / 2 + 1;
      oy = boardy + (SQUAREDIM - knightHeight) / 2 + 1;

      //创建一个数据结构, 用来保存骑士巡游时的轨迹 网管u家u.bitsCN.com

      trail = new ArrayList ();

      //保存applet引用以便后面调用drawImage()时使用.

      this.a = a;
   }

   // This method is called when the ChessBoard component's peer is created.
   // The code in this method cannot be called in the ChessBoard constructor
   // because the createImage() method returns null at that point. It doesn't
   // return a meaningful value until the ChessBoard component has been added
   // to its container. The addNotify() method is not called until the first
   // time ChessBoard is added to a container.

   public void addNotify ()
   {
      // Given this object's Canvas "layer" a chance to create a Canvas peer.

      super.addNotify ();

      //创建图像缓冲 网管论坛bbs_bitsCN_com

      imBuffer = createImage (width, height);

      //得到图像缓冲的内容。

      imG = imBuffer.getGraphics ();
   }

   //当小程序的布局管理器布置它的组件时,会调用这个方法。
//如果可能,组件会显示为首选大小。

   public Dimension getPreferredSize ()
   {
      return new Dimension (width, height);
   }

   //移动骑士到指定的棋盘位置。如果位置小于1或大于64则抛出一个异常

   public void moveKnight (int boardPosition)
   {
      if (boardPosition < 1 || boardPosition > 64)
          throw new IllegalArgumentException ("invalid board position: " +
                                              boardPosition); 网管bitscn_com

      int rebasedBoardPosition = boardPosition-1;

      int col = rebasedBoardPosition % 8;
      int row = rebasedBoardPosition / 8;

      ox = boardx + col * SQUAREDIM + (SQUAREDIM - knightWidth) / 2 + 1;
      oy = boardy + row * SQUAREDIM + (SQUAREDIM - knightHeight) / 2 + 1;

      trail.add (new Point (ox + knightWidth / 2, oy + knightHeight / 2));

      repaint ();
   }

   //画出所有部件——先棋盘然后是骑士

   public void paint (Graphics g)
   {
      //画出棋盘

      paintChessBoard (imG, boardx, boardy);

      //画出骑士

      paintKnight (imG, ox, oy);
网管联盟bitsCN_com
      //画出骑士的轨迹

      paintTrail (imG);

      //画出图像缓冲的内容

      g.drawImage (imBuffer, 0, 0, this);
   }

   //画出棋盘——(x, y)是左上角坐标

   void paintChessBoard (Graphics g, int x, int y)
   {
      // 画出棋盘的边框

      g.setColor (Color.black);
      g.drawRect (x, y, 8 * SQUAREDIM + 1, 8 * SQUAREDIM + 1);

      //画出棋盘

      for (int row = 0; row < 8; row++)
      {
           g.setColor (((row & 1) != 0) ? SQUARECOLOR : Color.white);

           for (int col = 0; col < 8; col++) 网管论坛bbs_bitsCN_com
           {
                g.fillRect (x + 1 + col * SQUAREDIM, y + 1 + row * SQUAREDIM,
                            SQUAREDIM, SQUAREDIM);

                g.setColor ((g.getColor () == SQUARECOLOR) ? Color.white :
                            SQUARECOLOR);
           }
      }
   }

   //画出骑士——(x, y)是图片左上角坐标

   void paintKnight (Graphics g, int x, int y)
   {
网管联盟bitsCN_com

      g.drawImage (imKnight, x, y, a);
   }

   //画出骑士的轨迹

   void paintTrail (Graphics g)
   {
      g.setColor (Color.black);

      int len = trail.size ();

      if (len == 0)
          return;

      Point pt = (Point) trail.get (0);
      int ox = pt.x;
      int oy = pt.y;

      for (int i = 1; i < len; i++)
      {
           pt = (Point) trail.get (i);
           g.drawLine (ox, oy, pt.x, pt.y);
           ox = pt.x;
           oy = pt.y;
中国网管联盟bitsCN.com

      }
   }

   //清空ArrayList来重设棋盘

   public void reset ()
   {
      trail.clear ();
   }

   // The AWT invokes the update() method in response to the repaint() method
   // call that is made as a knight is moved. The default implementation of
   // this method, which is inherited from the Container class, clears the
   // applet's drawing area to the background color prior to calling paint().
   // This clearing followed by drawing causes flicker. KT overrides
   // update() to prevent the background from being cleared, which eliminates
   // the flicker.

   public void update (Graphics g)
   {
      paint (g);
   }
}
网管bitscn_com


  清单1展示了基于抽象窗口工具集(AWT)的KT小程序。当然我并没有半点反对Swing的意思——我只是希望让KT集中于AWT。小程序的public void init()方法负责创建程序界面,包含一个ChessBoard类的实例用来描述一个棋盘。

  ChessBoard继承自java.awt.Canvas,从定制的AWT组件中实例化ChessBoard。构造器负责决定各个部件的大小,导入骑士图片,安置左上角为骑士的初始位置和创建一个数据结构来保存骑士巡游的轨迹。程序覆盖了public Dimension getPreferredSize()方法,用来返回组件的大小,通知布局管理器ChessBoard希望在布局操作时维持这些尺寸为首选的大小。

  我同样也覆盖了public void addNotify()方法,以便createImage()方法被调用时不返回null值。当Canvas(或许我应该说ChessBoard)创建完成时这个方法才返回null。在addNotify() 中调用super.addNotify()后,null不会被返回。

  public void moveKnight(int boardPosition)方法移动骑士到boardPosition指定的位置上。这个参数必须在1-64的范围内,否则将抛出异常。除了将骑士的图像画在指定位置的中央外,这个方法还将图像的位置保存在前面创建的数据结构中(参看构造器)。public void reset()方法清除数据结构中的内容。如果没有这个方法,连续的巡游将会同时显示以前和新的巡游轨迹。此外,数据结构应该控制大小的增长,避免浪费内存和最终导致内存不足的错误。剩下的方法负责画出棋盘,画出骑士的图像和轨迹。同样,update()方法被覆盖来防止闪烁。

中国网管联盟bitsCN.com



  编译清单1以后,你应该想运行这个小程序。但是,在你能运行它之前,你必须通过HTML来向小程序查看器(appletviewer)描述我们编写的小程序。清单2提供了所需的HTML

  清单2. KT.html
  网管u家u.bitsCN.com 


  在我们学习清单1的时候,你可能已经注意到了一个遗漏:你可以从左上角或右上角来开始一次巡游,但是却不能从左下角或右下角开始。我故意漏掉这两个角落来让你去实现它。因为每个巡游过程都反映了另一个,为剩下的巡游指出开始位置应该不会太难。
我还有另外一个练习留给你:在选择组件的item监听器中:为什么指定cb.reset();跟在moveKnight()后面呢(参看init()方法)?

  回顾

  国际象棋提供了许多有趣的娱乐,比如说骑士巡游。它要求骑士从任何方格开始,游历剩余的63个方格而不能重复同一个位置。KT小程序选择了一个简单的实现,限制开始位置为左上角和右上角的方格。在你完成这个小程序之后,请研究一个通用的解决办法使骑士可以从任何方格开始巡游。

  关于作者

  Jeff  Friesen是一个自由作家、软件开发者和教育者。特别擅长于C,C++和Java技术。 网管u家u.bitsCN.com

  版权声明:任何获得Matrix授权的网站,转载时请务必保留以下作者信息和链接
  原文:http://www.javaworld.com/
  译文:
http://www.matrix.org.cn/

网管u家u.bitscn@com


TAGs问题   骑士   案例   源代码   程序   巡游   开始   方法    
 上一篇:ALDSP中动态数据表访问的解决方案   下一篇:用Java实现IRC文本到语音转换
相关文章列表
Java源代码案例--骑士巡游问题 评论:
loading.. 评论加载中…
评论:请自觉遵守互联网相关政策法规,评论不得超过250字。

验证码: 注册用户
本类热门排行:
1.用java编写的小游戏源代码分析
2.在.Net程序中使用log4net记录日志
3.Java与sqlserver2000的连接
4.Java源代码案例--骑士巡游问题
5.Java游戏编程分析--棋子的拖动
6.JAVA 正则表达式4种常用的功能
7.Log4j的使用总结!(用于收藏救急用)
8.JFreeChart中柱状图的详细设置
9.学习教程:Java Annotation 高
10.JAVA对象转为Java String的几种常用方法
最新推荐文章:
1.该如何实践State模式
2.JFreeChart中柱状图的详细设置
3.Java Swing APIs可插拔式外观风格特性应
4.用信号量Semaphore实现互斥锁Mutex
5.使用Weblogic Integration的应用程序架构
6.设计Java应用程序
7.对Java嵌套类的讨论(上)
8.提高Java代码可重用性的三个措施
9.JAVA上加密算法的实现用例
10.总结篇:将内部类进行到底
网管论坛交流:
·不疯魔不成活
·令你大开眼界的真正标准化机房,已整理重
·为赈灾,女孩舍身拍“裸照”
·Windows Server 2003服务器群集创建和配
·exchange2k3全套官方资料
·双儿一周岁了。。。特殊的礼物来啦。。
·存储备份技术版块守则
·无盘技术交流区守则
·DOS命令基础大全之命令详解<作者吐血
·Windows XP 操作系统默认设置需要注意的