回头看了看推箱子的源代码,里面有不少嵌套的for循环用于查找满足某个条件的位置,或者是对其进行处理。过多的嵌套for循环似乎与STL的精神相悖,于是想是否可以用迭代器和STL算法来替换这些嵌套的循环。
办法是:增加一个迭代器类SSiterator,保存某个SokoState对象中map_成员的位置,并提供operator++(前缀及后缀)、operator--前缀及后缀)、operator*、operator==、operator!=等操作符。为了方便SokoState的成员函数使用,还增加了四个成员函数up()、down()、left()、right()。
有了这个SSiterator,就可以对SokoState类进行修改了。修改包括:把MapFlag、MapChar、isXXX等移出SokoState类,以便于SSiterator使用;增加begin()和end()成员,返回表示起点和终点的迭代器对象;最后是用迭代器和STL算法来替换原有的for循环。最后,除了operator<<和operator>>的输入输出操作符之外,其它的for循环全部消失了。至于输入输出操作符,由于要处理换行符,所以还没有想以好办法来消除for循环。
用到的STL算法包括有:copy、transform、lexicographical_compare;还有compose2、ptr_fun等函数对象适配器。
不过,到完成后,与v1版本的运行效率比较了一下,有较大程序的性能损失,慢了约2-3倍。其实它们的算法是一样,不过v1.1中有很多迭代器对象的构造、复制、析构,导致了较多的开销。
以下贴出新的源代码,只有两个文件,soko主程序没有修改(除了#include),不再重复。
// SokoState_v1.1.h -- SokoState类的头文件,第一版
#ifndef _SOKOSTATE_H
#define _SOKOSTATE_H
#include <iostream>
#include <vector>
#include <stack>
using std::vector;
using std::stack;
using std::ostream;
using std::istream;
const int MIN_ROWS = 4;
const int MAX_ROWS = 16;
const int MIN_COLS = 4;
const int MAX_COLS = 16;
class SokoState;
struct SSiterator :
public std::iterator<std::bidirectional_iterator_tag, unsigned char>
{
SokoState* s_;
int x_, y_;
SSiterator(SokoState& s, int x, int y) : s_(&s), x_(x), y_(y) {}
value_type& operator*() const;
SSiterator& operator++();
SSiterator& operator--();
SSiterator operator++(int) {
SSiterator tmp = *this;
++*this;
return tmp;
}
SSiterator operator--(int) {
SSiterator tmp = *this;
--*this;
return tmp;
}
bool operator==(const SSiterator& other) {
return s_ == other.s_ && x_ == other.x_ && y_ == other.y_;
}
bool operator!=(const SSiterator& other) {
return !operator==(other);
}
SSiterator up() {
SSiterator tmp = *this;
--(tmp.x_);
return tmp;
}
SSiterator down() {
SSiterator tmp = *this;
++(tmp.x_);
return tmp;
}
SSiterator left() {
SSiterator tmp = *this;
--(tmp.y_);
return tmp;
}
SSiterator right() {
SSiterator tmp = *this;
++(tmp.y_);
return tmp;
}
SSiterator& changeState(SokoState& s) {
s_ = &s;
return *this;
}
};
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);
void printAnswer(ostream& os) const;
private:
typedef SSiterator iterator;
friend struct SSiterator;
iterator begin() const
{ return iterator(const_cast<SokoState&>(*this), 0, 0); }
iterator end() const
{ return iterator(const_cast<SokoState&>(*this), rows_, 0); }
struct SokoStep //用于记录每一步箱子的移动
{
int x_;
int y_;
char d_; // direction: R,L,U,D
SokoStep(int x, int y , char d) : x_(x), y_(y), d_(d) {}
friend ostream& operator<< (ostream& os, const SokoStep& s)
{
os << "[" << s.x_ << ", " << s.y_ << ", " << s.d_ << "]";
return os;
}
};
void moveBox(vector<SokoState>& vs, iterator iBox, iterator iSoko,
iterator iNBox, char d) const;
void checkSoko(stack<iterator>& ps, iterator it);
void stateEq();
int rows_, cols_;
unsigned char map_[MAX_ROWS][MAX_COLS];
vector<SokoStep> steps_;
};
#endif
// SokoState_v1.1.cpp -- SokoState类的实现文件,第一版
#include <iterator>
#include <string>
#include <memory>
#include <ext/functional>
#include <stdexcept>
#include "SokoState_v1.1.h"
using namespace std;
using namespace __gnu_cxx;
enum MapChar {
CharWall = 'W', // Wall障碍物
CharBox = 'B', // Box箱子,不在目的地
CharSpace = 'S', // Space空地,没有任何东西
CharDest = 'D', // Dest目的地
CharInDest = 'I', // 已放在目的地上的箱子
CharSoko = 'K', // Soko推箱子的人,不在目的地
CharSokoInDest = 'O', // Soko推箱子的人,恰好在目的地
CharError = 'E' // 错误状态
};
enum MapFlag {
FlagWall = 0x01, // 是否障碍物
FlagBox = 0x02, // 是否有箱子
FlagDest = 0x04, // 是否目的地
FlagSoko = 0x08 // 是否有人
};
inline bool isWall(unsigned char c)
{
return (c & FlagWall) != 0;
}
inline bool isBox(unsigned char c)
{
return (c & FlagBox) != 0;
}
inline bool isDest(unsigned char c)
{
return (c & FlagDest) != 0;
}
inline bool isSoko(unsigned char c)
{
return (c & FlagSoko) != 0;
}
inline bool isSpace(unsigned char c)
{
return !isWall(c) && !isBox(c);
}
inline bool isPureSpace(unsigned char c)
{
return isSpace(c) && !isSoko (c);
}
inline void setFlag(SSiterator it, MapFlag f)
{
*it |= f;
}
inline void clearFlag(SSiterator it, MapFlag f)
{
*it &= ~f;
}
inline unsigned char clearSokoFlag(unsigned char c)
{
return c & ~FlagSoko;
}
inline char mapChar(unsigned char c)
{
if (isWall(c))
return CharWall;
else if (isBox(c))
return isDest(c) ? CharInDest : CharBox;
else if (isSoko(c))
return isDest(c) ? CharSokoInDest : CharSoko;
else if (isDest(c))
return CharDest;
else
return CharSpace;
}
SSiterator::value_type& SSiterator::operator*() const
{
return s_->map_[x_][y_];
}
SSiterator& SSiterator::operator++()
{
if (++y_ == s_->cols_)
{
y_ = 0;
++x_;
}
return *this;
}
SSiterator& SSiterator::operator--()
{
if (--y_ < 0)
{
y_ = s_->cols_-1;
--x_;
}
return *this;
}
void SokoState::checkSoko(stack<iterator>& ps, iterator it)
{
if (isPureSpace(*it))
{
setFlag(it, FlagSoko);
ps.push(it);
}
}
void SokoState::stateEq()
{
SSiterator it = find_if(begin(), end(), isSoko);
stack<iterator> points;
points.push(it);
while (!points.empty())
{
it = points.top();
points.pop();
checkSoko(points, it.up());
checkSoko(points, it.down());
checkSoko(points, it.left());
checkSoko(points, it.right());
}
}
void SokoState::moveBox(vector<SokoState>& vs, SSiterator iBox, SSiterator iSoko,
SSiterator iNBox, char d) const
{
if (!isSoko(*iSoko) || !isSpace(*iNBox))
return;
SokoState r = *this;
transform(r.begin(), r.end(), r.begin(), clearSokoFlag);
clearFlag(iBox.changeState(r), FlagBox); //移动箱子
setFlag(iBox, FlagSoko);
setFlag(iNBox.changeState(r), FlagBox);
r.stateEq();
r.steps_.push_back(SokoStep(iBox.x_, iBox.y_, d));
vs.push_back(r);
}
void SokoState::nextStep(vector<SokoState>& vs) const
{
iterator it = begin();
while (it != end())
{
it = find_if(it, end(), isBox);
if (it == end())
break;
moveBox(vs, it, it.down(), it.up(), 'U');
moveBox(vs, it, it.up(), it.down(), 'D');
moveBox(vs, it, it.right(), it.left(), 'L');
moveBox(vs, it, it.left(), it.right(), 'R');
++it;
}
}
bool SokoState::isTarget() const
{
iterator it = find_if(begin(), end(),
compose2(not_equal_to<bool>(), ptr_fun(isDest), ptr_fun(isBox)));
return it == end();
}
void SokoState::printAnswer(ostream& os) const
{
copy(steps_.begin(), steps_.end(), ostream_iterator<SokoStep>(os, " "));
}
bool SokoState::operator< (const SokoState& other) const
{
if (rows_ != other.rows_)
return rows_ < other.rows_;
if (cols_ != other.cols_)
return cols_ < other.cols_;
return(lexicographical_compare(begin(), end(),
other.begin(), other.end()));
}
ostream& operator<< (ostream& os, const SokoState& s)
{
SSiterator it = s.begin();
for (int i = 0; i < s.rows_; ++i)
{
transform(it, it.down(), ostream_iterator<char>(os, ""), mapChar);
os << "/n";
it = it.down();
}
os << "------------------------" << endl;
return os;
}
istream& operator>> (istream& is, SokoState& s)
{
is >> s.rows_;
if (s.rows_ < MIN_ROWS || s.rows_ > MAX_ROWS)
{
throw invalid_argument("rows of soko game error.");
}
is >> s.cols_;
if (s.cols_ < MIN_COLS || s.cols_ > MAX_COLS)
{
throw invalid_argument("cols of soko game error.");
}
string str;
SSiterator it = s.begin();
getline(is, str); // 滤过第一行的换行
for (int i = 0; i < s.rows_; i++)
{
getline(is, str);
for (int j = 0; j < s.cols_; j++, ++it)
{
*it = 0;
switch (str[j]) {
case CharBox :
setFlag(it, FlagBox);
break;
case CharWall :
setFlag(it, FlagWall);
break;
case CharSpace :
break;
case CharDest :
setFlag(it, FlagDest);
break;
case CharInDest :
setFlag(it, FlagDest);
setFlag(it, FlagBox);
break;
case CharSoko :
setFlag(it, FlagSoko);
break;
case CharSokoInDest :
setFlag(it, FlagSoko);
setFlag(it, FlagDest);
break;
default :
throw invalid_argument(
string("char of soko game error: ") + str );
}
}
}
s.stateEq();
return is;
}