First we try, then we trust

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

十一、 Layout的设计

Layout在我的程序中充当"布局",也就是说记录当前棋盘的状态。棋盘状态其实无外乎就是10个棋子的位置以及2个空格的位置而已。所以在Layout类中包含了两个成员:

public class Layout
{
  
private Chessman[] _chessmen = new Chessman[10];
  
private BlankPosition _blankPosition;
  
  …………
}

同时,Layout应当具有将自己转换为4字节整数的功能。所以提供了ToInt函数。关于转换的方式,可以参考《华容道与数据结构 (1)》中的内容。我这里需要说的是如何将Layout转换为整数。

由于Layout中的每个棋子都有其坐标值,例如如下布局:

 

为了正确对棋子编号,我们需要一个排序操作,而这个排序的依据就是先按纵坐标排,纵坐标相同的按横坐标排。排序后就可以根据每个棋子的类型生成对应的整数。就上面这个例子来说,我们可以得到如下表:

棋子
类型
Y
X
Y×10+X
顺序号
赵云 VChessman 0 0 0 1
曹操 General 0 1 1 A
张飞 VChessman 0 3 3 2
马超 VChessman 2 0 20 3
黄忠 VChessman 2 3 23 6
关羽 HChessman 4 1 41 10
卒1 Solider 2 1 21 4
卒2 Solider 2 2 22 5
卒3 Solider 3 1 31 7
卒4 Solider 3 2 32 8
空格1 Blank 4 0 40 9
空格2 Blank 4 3 43 11

按照Y×10+X公式排序后得到的顺序号正好是我们棋子的编号。剩下的事情就是将这些棋子串起来,形成一个整数值。在这里我们使用左移运算符。前面的设计中用二进制的00表示空格,01表示卒,10表示竖放,11表示横放,这与ChessmanTypeEnum中各棋子的整数值正好相对应。所以我们可以如此完成串接过程(仅仅以8位二进制位来演示):

int result = 0;       //二进制的00000000
= 2;                //二进制的10,竖放棋子
= 3;                //二进制的11,横放棋子
= 1;                //二进制的01,卒
result = a;           //二进制的00000010
result = result << 2//左移两位00001000
result += b;          //得到结果00001011
result = result << 2//左移两位00101100
result += c;          //得到结果00101101

其中左移运算给新棋子的加入腾出位置。具体Layout中的实现可以参考代码中的ToInt方法,这里就不再多说。

Layout中另外一处需要注意的问题就是在类的定义中有这样的两个数组:

private Chessman[] _chessmen = new Chessman[10];
private Chessman[] sortedChessmen  = new Chessman[12];

其中的sortedChessmen起到什么作用?我们知道数组是引用型的变量,为了兼顾排序求Int以及Layout的复制操作,我们需要两个数组,一个是不排序的,而另外一个是排序的,但它们都指向同一内存中的对象。这样在求整数以及追踪第几个棋子被移动的过程中,我们使用sortedChessmen,在复制Layout时我们使用_chessmen,但最终引用的对象确是同一个对象。如下图所示:

 

sortedChessmen在Layout的InitLayoutMap方法中被初始化,使用快速排序法进行排序操作(有关快速排序法将在下部分内容中加以介绍)。

Layout中还有一个只读属性IsFinished属性,用来判断当前棋局是否可以结束,其实就是判断曹操是否移动到了指定位置而已。该属性在ToInt方法中被初始化。

posted on 2005-02-06 10:04  吕震宇  阅读(3188)  评论(0编辑  收藏  举报