First we try, then we trust

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

十、 Chessman的设计

整个程序中一个非常关键的环节就是Chessman(棋子)的设计以及Layout(棋盘布局)的设计。这次先说说Chessman的设计。

由于我的程序只针对10子布局,所以一个Layout应当包括10个棋子,分别归属于General、HChessman、VChessman与Soldier。下面是抽象Chessman类的部分定义:

public abstract class Chessman
{
  
protected Position _position;

  
protected Position _newPosition = new Position(-1,-1);
  
protected BlankPosition _newBlankPosition = new BlankPosition();

  
// 构造函数
  public Chessman(Position position)
  
{
    
this._position = position;
  }


  
// ………… 此处省略一些属性的定义

  
public virtual ChessmanType chessmanType
  
{
    
get 
    
{
      
return ChessmanType.Blank;
    }

  }


  
public abstract void CheckAvailableSteps(BlankPosition _blankPosition, CallBackDelegate _callback);
}

在这个定义里面我们可以看到以下几项:_position,记录了当前棋子在棋盘中的位置。_newPosition、_newBlankPosition,当移动棋子后,棋子的新位置以及空格的新位置。关于这点在后面有进一步的说明。只读chessmanType属性,标识当前棋子类型。

还有一个抽象方法:CheckAvailableSteps是Chessman的核心。在方法中包含了一个CallBackDelegate类型参数,是一个回调函数的委派。程序的运行机制是:CircularLinkedList调用Layout的CheckAvailableSteps方法,Layout再依次调用其每个包含棋子的CheckAvailableSteps方法,并提供一个回调函数(就是Layout的EncapsulateChessStep方法)。一旦棋子有可行的走法,就回调该函数,传入新的棋子位置以及新的空格位置,并以此产生一新Layout。然后的步骤就是将此新Layout纳入CircularLinkedList并检验AVLTree是否有重复值等等,大家可以参考(《华容道与数据结构 (4) 》)中的内容。

这理有一点需要明白的就是棋子的移动问题。其实某个棋子能否移动以及如何移动仅与当前空格位置有关。如图:

 

所以,我们仅需知道空格在什么位置便可以计算出下一步的移动方法。另外,棋子移动后,我们只需要回传该棋子的新位置以及空格的新位置就可以在原有布局的基础上产生出一新的可行布局。当然此布局还要经过AVLTree的检验才可以被加入CircularLinkedList以及TreeLinkedList当中。

举例来说,在HChessman类中,我们会看到若干私有方法如下:

private bool CanMoveUp(BlankPosition _blankPosition)
{
  
if(_blankPosition.IsBlank(_position.x , _position.y - 1&& 
     _blankPosition.IsBlank(_position.x 
+ 1, _position.y - 1))
  
{
    
// 设置棋子新位置
    _newPosition.x = _position.x;
    _newPosition.y 
= _position.y - 1;

    
// 设置新的空白位置
    _newBlankPosition.Pos1 = _position;
    _newBlankPosition.Pos2.x 
= _position.x + 1;
    _newBlankPosition.Pos2.y 
= _position.y;
        
    
return true;
  }

  
return false;
}

该段代码判断当前棋子是否可以向上移动,唯一的判别依据就是空格的位置,也就是传入的_blankPosition参数。如果可以移动的化,则给_newPosition与_newBlankPosition赋值移动后棋子的位置与空格的位置。在HChessman的CheckAvailableSteps方法中,我们可以看到如下代码:

public override void CheckAvailableSteps
(BlankPosition _blankPosition, CallBackDelegate _callback)
{
  
if(CanMoveUp(_blankPosition))
    _callback(_newPosition, _newBlankPosition, MoveMethod.Up);

  …………

}

上面代码的意思是说,如果棋子可以向上移动的化,则回调_callback(也就是Layout的EncapsulateChessStep方法),传入三个参数:棋子新位置、新空格位置以及当前移动方法。在EncapsulateChessStep方法中将根据这些信息生成新的棋盘布局Layout。Layout的EncapsulateChessStep方法定义如下:(经部分简化)

private void EncapsulateChessStep
  (Position newPosition, BlankPosition newBlankPosition, MoveMethod mm)
{
  Layout l 
= _mediator.AllocateLayout();
  
  
// 生成新布局
  for(int i = 0; i<10; i++)
  
{
    
if(this.chessmen[i] == this.sortedChessmen[_current])
    
{
      l.chessmen[i].position 
= newPosition;
      l.blankPosition 
= newBlankPosition;
    }

    
else
    
{
      l.chessmen[i].position 
= this.chessmen[i].position;
    }

  }


  
// 初始化新布局
  l.InitLayoutMap();

  
// 初始化当前移动"步法"
  ChessStep cs = new ChessStep();
  cs.layout 
= l.ToInt();
  cs.moveMethod 
= mm;

  
if(sortedChessmen[_current].chessmanType == ChessmanType.General)
    cs.chessmanNum 
= 99;
  
else
    cs.chessmanNum 
= (short)chessmanNum;

  
// 将封装好的ChessStep发送给mediator作进一步处理
  if(l.IsFinished)
  
{
    _gotTheAnswer 
= true;
    _mediator.Finished(cs);
  }

  
else
    _mediator.CheckStep(cs);
}

在这段程序中,首先通过中介者_mediator从CircularLinkedList当中分配一可用Layout。

  Layout l = _mediator.AllocateLayout();  

然后将当前布局的10个棋子复制过去,在这里,注意被移动的棋子要复制其新位置,并用新的空格位置初始化新布局。

  // 生成新布局
  for(int i = 0; i<10; i++)
  
{
    
if(this.chessmen[i] == this.sortedChessmen[_current])
    
{
      l.chessmen[i].position 
= newPosition;
      l.blankPosition 
= newBlankPosition;
    }

    
else
    
{
      l.chessmen[i].position 
= this.chessmen[i].position;
    }

  }

剩下的工作就是对布局初始化,生成移动"步法"并判断是否曹操移动到了目的位置,然后将"步法"交给中介者进一步处理。

Chessman的代码就介绍到这里,下部分内容将介绍Layout。

posted on 2005-02-05 16:42  吕震宇  阅读(3012)  评论(0编辑  收藏  举报