用STL实现DFS/BFS算法——推箱子游戏(1)

用STL实现DFS/BFS算法
——推箱子游戏 (1)
推箱子的游戏想必很多朋友都有玩过,简单地说就是,在一个m*n的范围内有k个箱子和k个目的地,你只能使用推的方法来移动箱子,不能拖,也不能推动两个或以上的箱子,活动范围通常比较狭窄,还有一些不可移动的障碍物,所以有一定难度和可玩性。以下是一个中等难度的推箱子题目:
 
你可以看到,图的正中央是负责推箱子的“人”(Soko);四周围住的是不可移动的障碍物(Wall),中间也有一些;灰色的是空地(Space);在Soko的右边是一个箱子(Box),由于它不是在目的地上,所以用黄色来表示;它右下方的空地有一个紫红点,表示这是一个停放箱子的目的地(Dest);在Soko的上方、左上方、右上方共有三个蓝色的箱子,表示这三个箱子已经放在目的地上了(不过为了把所有箱子都移动到目的地,这些已经放好的箱子也可以被移开)。所以,这道推箱子题目共有四个箱子和四个目的地。
首先,我们要决定如何在程序中表示推箱子问题的某个状态。我想到最简单的方法就是用一个m*n的二维char数组来表示,数组中的每一个char表示图中的一个格,格子可能是障碍物、人、箱子、目的地或空格,这几种状态也可能重叠,如人可以恰好站在一个目的地上,或者一个箱子恰好放在目的地上。我们用以下几个char来表示可能出现的格子状态:
        CharWall = 'W',         // Wall 障碍物
        CharBox = 'B',          // Box 箱子,不在目的地
        CharSpace = 'S',        // Space 空地,没有任何东西
        CharDest = 'D',         // Dest 目的地
        CharInDest = 'I',       // 已放在目的地上的箱子
        CharSoko = 'K',         // Soko 推箱子的人,不在目的地
        CharSokoInDest = 'O',   // Soko 推箱子的人,恰好在目的地
        CharError = 'E'         // 错误状态
用这种方法表示上面的题目,可以得到上图右侧的二维数组,行数和列数均为9。你可以看到左图并不是整整齐齐的二维图,它的最外围有一些缺口,我们在右图中全部用’W’来表示。这是显而易见的,它们没有什么作用,只是为了把图形填充为整齐的二维数组。如果你想用’S’来表示也无所谓,不会影响我们的程序执行,不过我想’W’更合适。
以上是一个推箱子问题状态的文本表示法,或者说是输入输出的方法,便于我们查看问题的状态。但是对于计算机程序处理来说,它并不是理想的表示方法,计算机更善于处理二进制的数字。所以我们还应该用二进制的方式来表示问题的状态,以便于程序的处理。同样的,我们还是使用二维数组来表示,不过这次数组的元素不是char字符,而是二进制的unsigned char。经过简单的分析可以知道,一个格子可以用4个bit来表示所有状态:
        FlagWall = 0x01,    // 是否障碍物
        FlagBox = 0x02,     // 是否有箱子
        FlagDest = 0x04,    // 是否目的地
        FlagSoko = 0x08     // 是否有人
在输入和输出问题状态时,需要进行这两种表示法之间的转换。即在输入时,把文本表示法转换为二进制表示法;在输出时,则把二进制表示法转换为文本表示法。这些都不难,后面会列出相关的代码。现在,我们先来给出问题状态类的定义。
和我们前面见到过的数独问题和N皇后问题一样,要使用DFS/BFS算法,我们需要设计问题状态类。对于推箱子问题,我们把问题状态类命名为SokoState,它的定义应该象下面这样:
class SokoState
{
public:
    SokoState() : rows_(0), cols_(0) {}
    void nextStep(vector<SokoState>&) const;
    bool isTarget() const;
    bool operator< (const SokoState& other) const;
    friend ostream& operator<< (ostream& os, const SokoState& s);
    friend istream& operator>> (istream& is, SokoState& s);
 
private:
    struct SokoStep; // 用于记录每一步箱子的移动
    int rows_
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值