推箱子游戏自动求解和关卡生成

        推箱子游戏自动求解部分使用A*寻路 ,否则状态数太多直到内存耗尽也无法求解成功,优化后20x20大小加10个以内box的地图,求解耗时基本可以控制在1秒以内。

一,A*寻路
     对于状态数有限的A*算法:比如迷宫搜索,比较慢的部分是在开表中寻找F值最低的节点,可以使用二叉堆来优化。

     对于状态数接近无限的A*算法:比如推箱子游戏, 三个要素决定了在内存和时间耗尽前能否搜索成功。
    1状态编码压缩决定了内存占用的大小。

    2高效剪枝是速度优化的重点。

    3评估函数的准确度决定能否更快更准的搜索到路径
        估值h等比偏小时,趋近于BFS,启发效率低下。因为前面g占比较小的最短路径h偏小的更多,f也会偏小,到了后面排序会被提前(导致最小路径树太粗)。
        估值h等比偏大时,趋近于DFS,结果不是真实的最短路径。
        估值h非等比于真实值发生混乱时,启发失效。
    使用std::set代替二叉堆,set的比较函数设计时要避免逻辑混乱(对于等价状态b==c,不要存在b>a&&c<a的矛盾,否则可能发生未知行为,比如insert b成功后find c失败)。
 
    优化:预计算每个节点到其它所有节点的路程F,在评估函数内使用这些值代替曼哈顿距离可以避免搜索多余节点直接向最短路径上搜索。在路径不通时可以即时返回而不是遍历整个图(比广度搜索还慢,可以加最大开表限制)。

A*寻路在推箱子游戏中的应用

《翻滚方块进行时》小程序码  微信扫一扫可运行

算法在3D版推箱子小游戏中的应用:3D版有高度限制,不能从低向高移动,且有触发开关会改变地形高度,算法上需要做一些调整。


      1状态编码压缩
         每个游戏状态包含各box的位置和man所在的区域ID。 由于每个box是无差别的,所以要对box进行归一化(根据位置进行排序)。 由于man在连通区域内移动且未推动box时视为状态不变,所以保存man所在的区域ID而不是保存其位置。
      2高效剪枝
         标记坏区域,墙角通常是box不应该到达的坏区域,另外墙边也有可能是坏区域,这里使用反向拉箱子的方法计算,如果箱子不能从某个格子被拉到任意一个目标点,则标记该格子为坏区域(参见上图中阴影部分)。 判断坏的移动,如果移动后导致某些箱子无法再被继续推动则判断为坏的移动。
      3评估函数
         由于每个box是无差别的,可能需要排列组合计算box到达不同目标点的情况,选择其中最短距离作为预估值。当有多个box时全排列计算量太大,复杂的为n!,可以改用类似TSP计算最短路径的方法,尝试若干次交换交叉路径。 可以适当调整预估比例。



//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/BoxMan/BoxManFinder.h
//  @Brief:     MiniGameBoxMan
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================

#include "General/Pch.h"
#include "General/List.h"
#include "General/Pool.h"
#include "Math/MathLibAdvance.h"
#include "BoxMan/MiniGameBoxMan.h"
#include <set>

class BoxManFinder
{
public:
    //异步搜索状态  
    enum FindStatus
    {
        Find_NotBegin = 2,
        Find_Busy     = 0,
        Find_Ok       = 1,
        Find_Fail_NoPath  = -1,
        Find_Fail_OutMem  = -2,
        Find_Fail_Unknow  = -3,
    };
    typedef MiniGameBoxMan::BoxManState GameState;
    BoxManFinder();    
    virtual ~BoxManFinder();    

    virtual int  InitMap(MiniGameBoxMan* game);
    virtual void Render();
    virtual void Free();
    //开始搜索
    virtual FindStatus BeginFind(const GameState& start,BoxManPath* outPath);
    virtual void StopFind();
    //一次时间片搜索
    virtual FindStatus UpdateCycle(BoxManPath* outPath)=0;

    FindStatus  m_findStatus;
    MiniGameBoxMan* m_game;
};

//A*寻路算法
class BoxManFinderAstar:public BoxManFinder
{
public:
    struct AStarNode
    {
        AStarNode()
        {
            g = 0;
            h = 0;
            f = 0;
            parent = NULL;
            next   = NULL;
        }
        float g     ;// 起点到此点的已知距离
        float h     ;// heuristic 启发函数预测到终点的距离
        float f     ;// full f = g + h
        AStarNode*   parent;// 父节点
        AStarNode*   next  ;// 路径指向
        GameState    state ;
        bool         isOk  ;//ok 但f不一定最小路径 还未变放松
    };

    struct lessN
    {
        //设计set的比较函数时要注意:错误情况, a、b、c,  bc连通(b==c), ab不连通, abc box位置相同,man位置不同, 则可能存在错误判断:b==c而b>a且c<a
        //set使用比较函数判定已有元素a、新元素b是否相等:a<b==false && b<a==false则认为a、b是相等的,则b不会被插入set中;
        //a<b==true && b<a==true,可能发生未知行为,因此比较函数必须对相同元素返回false。
        //b==c而b>a且c<a,可能发生未知行为,比如insert b成功后find c失败。
        bool operator()(const AStarNode* left, const AStarNode* right) const
        {
            PROSTATISTIC_BoxMan("lessN::operator()",0);  
            const vec2I_* lbox = left->state.box;
            const vec2I_* rbox = right->state.box;
            const int BoxNum = G_BoxManGame->m_boxNum;
            //根据box位置排序
            for (int i=0;i<BoxNum;++i,++lbox,++rbox)
            {
                if (lbox->x < rbox->x)
                {
                    return true;
                }
                if (lbox->x > rbox->x)
                {
                    return false;
                }
                if (lbox->y < rbox->y)
                {
                    return true;
                }
                if (lbox->y > rbox->y)
                {
                    return false;
                }
            }

            //box位置都相同
            const vec2I_* lman = &left->state.man[0];
            const vec2I_* rman = &right->state.man[0];
            if (lman->x == rman->x
                &&lman->y == rman->y)
            {
                //man位置都相同
                return false;
            }

            //man的位置不同,可以走通时是相同状态
            if(G_BoxManGame->CanWalk(left->state,lman->x,lman->y,rman->x,rman->y))
            {
                return false; //这里注释掉也能找到路径,但是状态太多,速度会慢3倍。
            }

            //不能根据man位置排序,因为可能发生逻辑错误 b==c而b>a且c<a
            //标记man所在的区域块,根据区域ID排序
            return left->state.zone[lman->x][lman->y] < left->state.zone[rman->x][rman->y];
        }
    };
    struct lessF
    {
        bool operator()(const AStarNode* left, const AStarNode* right) const
        {
            PROSTATISTIC_BoxMan("lessF::operator()",0);
            if (left->f < right->f)
            {
                return true;
            }
            else if (left->f == right->f)
            {
                if (left < right)
                {
                    return true;
                }
            }
            return false;
        }
    };

    //typedef std::list<AStarNode>        NodeList;
    typedef List<AStarNode>             NodeList;    //todo优化内存碎片加速
    typedef std::set<AStarNode*,lessN>  NodePtrSet;  //todo使用hash_set加速
    typedef std::set<AStarNode*,lessF>  NodePtrFSet;

    BoxManFinderAstar();
    ~BoxManFinderAstar();

    virtual void Render();
    virtual void Free();
    virtual int  InitMap(MiniGameBoxMan* game);
    //开始搜索
    virtual FindStatus BeginFind(const GameState& start,BoxManPath* outPath);
    virtual void StopFind();
    //一次时间片搜索
    virtual FindStatus UpdateCycle(BoxManPath* outPath);

    float CalH(const GameState& state);

    //插入open表
    bool  InsertToOpenTable(bool inOpen,AStarNode* node, AStarNode* currNode, float w );

    //邻居点插入open表
    int   InsertNeighbors(/*const*/ AStarNode* currNode );

    static const int MaxSearchNodes   = 200000;
    double  m_timeBegin;
    double  m_timeCost;

    GameState   m_start;
    //GameState   m_end;            //ignore man pos

    //NodeList         m_nodeData;    //节点数组
    Pool<AStarNode> m_nodeData;
    //因为节点数很大,且需要快速查找,所以最好用hashset
    NodePtrSet  m_openTable;   //open表  待检测节点集(前锋边)//todo hashmap
    NodePtrFSet m_openTableF;  //open表  和上表包含的状态相同,只是排序不同 //todo最小二叉堆,因为中途cost变更时要从中间调整二叉堆需要查找节点(需要在状态中保存节点堆下标)
    NodePtrSet  m_closeTable;  //close表 最小路径树节点集     //todo hashmap

    int  openTableMax;
    int  closeTableMax;

    AStarNode* startNode;
    AStarNode* okNode;

    int  BoxNum;
};
//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/BoxMan/BoxManFinder.cpp
//  @Brief:     MiniGameBoxMan
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================

#include "General/Pch.h"
#include "Math/MathLibAdvance.h"
#include "General/Timer.h"
#include "Render/RendDriver.h"
#include "Render/FontMgr.h"
#include "BoxMan/BoxManFinder.h"
#include "BoxMan/MiniGameBoxMan.h"
#include "General/List.cpp.h"
#include "General/Pool.cpp.h"
#include <set>
#include "General/Pce.h"

BoxManFinder::BoxManFinder()
:m_findStatus(Find_NotBegin)
, m_game(NULL)
{
}
BoxManFinder::~BoxManFinder()
{
}
int BoxManFinder::InitMap(MiniGameBoxMan* game)
{
    Free();
    m_game = game;
    return 0;
}
void BoxManFinder::Free()
{
}
void BoxManFinder::Render()
{
}
void BoxManFinder::StopFind()
{
}
BoxManFinder::FindStatus BoxManFinder::BeginFind(const GameState& start,BoxManPath* outPath)
{
    return Find_NotBegin;
}
BoxManFinder::FindStatus BoxManFinder::UpdateCycle(BoxManPath* outPath)
{
    return Find_NotBegin;
}

//==================^_^
BoxManFinderAstar::BoxManFinderAstar()
:m_nodeData(1000)
{
}

BoxManFinderAstar::~BoxManFinderAstar()
{
    Free();
}

void BoxManFinderAstar::Free()
{
    m_findStatus = Find_NotBegin;
    m_openTable.clear();
    m_openTableF.clear();
    m_closeTable.clear();
    m_nodeData.FreeChunks();
}

int  BoxManFinderAstar::InitMap(MiniGameBoxMan* game)
{
    Free();
    m_game = game;
    m_findStatus = Find_Busy;
    BoxNum = m_game->m_boxNum;
    return 0;
}

void BoxManFinderAstar::Render()
{
    if (m_findStatus==Find_NotBegin)
    {
        return;
    }
    int Space = 18/DpiScale;
    float x = m_game->m_gameRect.GetRight()-300/DpiScale-2;
    float y = m_game->m_gameRect.GetBottom()-Space*5-2;
    vec2 pos(550,y);

    Color color(1,0,0,1);
    switch(m_findStatus)
    {
    case Find_Busy: color = Color(0.7f,0.7f,1,1); break;
    case Find_Ok  : color = Color(0.2f,1,0.2f,1); break;
    case Find_Fail_NoPath: 
    case Find_Fail_OutMem:
    case Find_Fail_Unknow:
        color = Color(1,0.2f,0.2f,1); break;
    }

    //back
    G_RendDriver->BlendFunc(Blend_Filter);
    G_RendDriver->Color4f(0,0,0,0.6f);
    G_RendDriver->DrawRect(RectF(pos.x,pos.y,300/DpiScale,Space*5));

    //
    G_FontMgr->GetFontDesc().fontFace = FF_Direct;
    G_FontMgr->GetFontDesc().fontSize = 16/DpiScale;
    G_FontMgr->SetColor(color);
    switch(m_findStatus)
    {
    case Find_Busy: 
        G_FontMgr->TextAtPos(vec2(pos.x,pos.y), "path find busy"); 
        break;
    case Find_Ok  :  
        G_FontMgr->TextAtPos(vec2(pos.x,pos.y), "path find ok"); 
        break;
    case Find_Fail_NoPath:  
        G_FontMgr->TextAtPos(vec2(pos.x,pos.y), "path find fail"); 
        break;
    }

    G_FontMgr->SetColor( Color(1,1,1,1));
    pos.y += Space;
    G_FontMgr->TextAtPos(vec2(pos.x,pos.y), StrFormat("open/close(%d / %d)", (int)m_openTable.size(), (int)m_closeTable.size())); 
    pos.y += Space;
    G_FontMgr->TextAtPos(vec2(pos.x,pos.y), StrFormat("open/close max(%d / %d)", openTableMax, closeTableMax)); 
    pos.y += Space;
    G_FontMgr->TextAtPos(vec2(pos.x,pos.y), StrFormat("time(%.3f)", (float)m_timeCost)); 

    pos.y += Space;
    G_RendDriver->Color4f(0,0,0,1);
    G_RendDriver->DrawRect(RectF(pos.x,pos.y,200,10));
    //int searchNode = m_nodeData.size();
    int searchNode = m_openTable.size() + m_closeTable.size();
    G_RendDriver->Color4f(0.5,0,0,1);
    G_RendDriver->DrawRect(RectF(pos.x,pos.y,200.0f*searchNode/MaxSearchNodes,10));

    G_FontMgr->SetColor( Color(0.2f,1,0.2f,1));
    G_FontMgr->TextAtPos(vec2(pos.x,pos.y), StrFormat("%d", searchNode)); 
}

BoxManFinder::FindStatus  BoxManFinderAstar::BeginFind(const GameState& start,BoxManPath* outPath)
{
    PROSTATISTIC_BoxMan("BoxManFinder::BeginFind()",0);

    if(G_Timer)
        m_timeBegin = G_Timer->GetAccumTimeReal();
    m_timeCost  = 0;
    m_findStatus   = Find_Busy;
    openTableMax  = 0;
    closeTableMax = 0;

    okNode = NULL;
    outPath->clear();

    m_openTable.clear();
    m_openTableF.clear();
    m_closeTable.clear();
    m_nodeData.FreeChunks();

    //起始点 == 结束点
    if ( m_game->IsOk(start) == true)
    {
        m_findStatus = Find_Ok;
        if(G_Timer)
            m_timeCost = G_Timer->GetAccumTimeReal() - m_timeBegin;
        return Find_Ok;
    }

    m_start = start;
    //m_nodeData.push_back(AStarNode());
    //startNode = &m_nodeData.front();        // 起始点
    startNode = m_nodeData.AllocObject();        // 起始点
    startNode->state = start;
    startNode->g = 0;
    startNode->h = CalH(start);
    startNode->f = startNode->g+startNode->h;
    startNode->parent = NULL;
    startNode->isOk = m_game->IsOk(startNode->state);
    m_openTable.insert(startNode);    //加入open表
    m_openTableF.insert(startNode);   //加入open表
    openTableMax  = 1;
    return UpdateCycle(outPath);
}
void BoxManFinderAstar::StopFind()
{
    m_findStatus = Find_NotBegin;
}

BoxManFinder::FindStatus BoxManFinderAstar::UpdateCycle(BoxManPath* outPath)
{
    PROSTATISTIC_BoxMan("BoxManFinder::UpdateCycle()",0);
    if (m_findStatus!=Find_Busy)
    {
        return m_findStatus;
    }
    AStarNode *curNode;       // 当前点
    AStarNode *endNode;       
    int insertNum = 0;
    double startTime = 0;
    if(G_Timer)
        startTime = G_Timer->GetAccumTimeReal();
    double cycleTime = 0;
    while(1)
    {
        int size = m_openTable.size();
        if (size<=0)
            break;
        curNode = *m_openTableF.begin();                    // open表的第一个点f值最小(堆排序)
        m_openTable.erase(m_openTable.find(curNode));      
        m_openTableF.erase(m_openTableF.begin());

        m_closeTable.insert(curNode);  //加入close表
        
        closeTableMax = m_closeTable.size();

        if (curNode->isOk)     //找到最短路径
        {
            m_findStatus = Find_Ok;
            endNode = curNode;
            break;
        }

        bool outMem = (m_openTable.size()+m_closeTable.size()) >= MaxSearchNodes;

        if (outMem==false)//未超出内存,加入邻居
        {
            insertNum += InsertNeighbors( curNode );  
        }

        if (openTableMax < m_openTable.size())
        {
            openTableMax = m_openTable.size();
        }

        if ( m_openTable.empty() )   //寻路失败
        {
            if (outMem==true)
            {
                m_findStatus = Find_Fail_OutMem;//超出内存导致
            }
            else
            {
                m_findStatus = Find_Fail_NoPath;//没有通路导致
            }
            break;
        }

        if (m_openTable.size()!=m_openTableF.size())
        {
            m_findStatus = Find_Fail_Unknow; //set operator < 错误导致
            break;
        }

        ////加入邻居时可能找到非最短路径(未经排序和边放松)
        //if (okNode)     //找到非最短路径立即退出,(避免找到了路径,但估值太大,一直排序在末尾导致长时间搜索)
        //{
        //    m_findStatus = Find_Ok;
        //    endNode = okNode;
        //    break;
        //}
        if(G_Timer)
        {
            cycleTime = G_Timer->GetAccumTimeReal() - startTime;
            if(cycleTime > 0.1)
            {
                m_findStatus = Find_Busy; //超出时间片
                break;
            }
        }
        else
        {
            m_findStatus = Find_Busy; //超出时间片
            break;
        }

    }

    if (m_findStatus == Find_Ok)
    {
        //倒推路径
        AStarNode * it = endNode;
        it->next = NULL;
        while( it->parent )
        {
            it->parent->next = it;
            it = it->parent;
        }

        //微调路径,前后几个state可以微调顺序,防止来回跑动
        {
            bool canWalk;
            vec2I_ preA; 
            vec2I_ preB; 
            vec2I_ preC; 
            BoxManPath pathSegA;
            BoxManPath pathSegB;
            BoxManPath pathSegC;
            const int MaxMapSize = m_game->MaxMapSize;
            static int costTable[MaxMapSize][MaxMapSize];
            it = startNode;// 起始点
            while(it)
            {
                GameState* stateArr[4];
                AStarNode* p = it;
                int i = 0;
                for (;i<4;i++)
                {
                    if(p==NULL)
                        break;
                    stateArr[i] = &p->state;
                    p = p->next;
                }
                if(i<4)
                    break;
                //    a      b      c
                // 0----->1----->2----->3
                //状态0~3,操作a~c
                //如果可以把c调整到b前面,仍然能够走通且步数更少,则用新的状态2'替换旧的状态2 (典型情况:a c移动的是同一个box,b移动另外一个较远的box)
                //    a      c      b
                // 0----->1----->2'---->3
                //
                GameState* state;
                vec2I_ fromBoxA;
                vec2I_ fromBoxB;
                vec2I_ fromBoxC;
                vec2I_ toBoxA  ;  
                vec2I_ toBoxB  ; 
                vec2I_ toBoxC  ;  
                m_game->GetMove(*stateArr[0],*stateArr[1],fromBoxA,toBoxA);
                m_game->GetMove(*stateArr[1],*stateArr[2],fromBoxB,toBoxB);
                m_game->GetMove(*stateArr[2],*stateArr[3],fromBoxC,toBoxC);
                preA = fromBoxA*2 - toBoxA;
                preB = fromBoxB*2 - toBoxB;
                preC = fromBoxC*2 - toBoxC;

                vec2I_ manPos = stateArr[1]->man[0];
                m_game->GenCosts(*stateArr[1],manPos.x,manPos.y,costTable);  
                if (costTable[preC.x][preC.y]>=0  //man 可以走到
                    && toBoxB!=fromBoxC//b c不是移动的同一个box 
                    && fromBoxB!=toBoxC//b移动前的box不挡着c
                    && toBoxC!=preB    //c移动后的box不挡着b
                    )
                {
                    GameState state2New = *stateArr[1];
                    state2New.man[0] = fromBoxC;
                    state2New.movebox(fromBoxC.x,fromBoxC.y,toBoxC.x,toBoxC.y); //走c

                    manPos = state2New.man[0];
                    m_game->GenCosts(state2New,manPos.x,manPos.y,costTable);  
                    if (costTable[preB.x][preB.y]>=0  //man 可以走到 走b
                        )
                    {
                        //拐弯推箱子
                        state = stateArr[0];  canWalk = m_game->GenWalkPath(*state,state->man[0].x,state->man[0].y,preA.x,preA.y,&pathSegA);
                        state = stateArr[1];  canWalk = m_game->GenWalkPath(*state,state->man[0].x,state->man[0].y,preB.x,preB.y,&pathSegB);
                        state = stateArr[2];  canWalk = m_game->GenWalkPath(*state,state->man[0].x,state->man[0].y,preC.x,preC.y,&pathSegC);
                        int oldStepNum = pathSegA.size()+pathSegB.size()+pathSegC.size(); 

                        state = stateArr[0];  canWalk = m_game->GenWalkPath(*state,state->man[0].x,state->man[0].y,preA.x,preA.y,&pathSegA);
                        state = stateArr[1];  canWalk = m_game->GenWalkPath(*state,state->man[0].x,state->man[0].y,preC.x,preC.y,&pathSegB);
                        state = &state2New ;  canWalk = m_game->GenWalkPath(*state,state->man[0].x,state->man[0].y,preB.x,preB.y,&pathSegC);
                        int newStepNum = pathSegA.size()+pathSegB.size()+pathSegC.size();
                        if (newStepNum<oldStepNum)
                        {
                            *stateArr[2] = state2New;
                        }
                    }
                }

                it = it->next;
            }
        }

        //输出路径
        {
            bool canWalk;
            BoxManPath pathSeg;
            it = startNode;// 起始点
            while(it && it->next)
            {
                //拼接各个分段
                GameState * fromState = &it->state;
                GameState * nextState = &it->next->state;

                vec2I_ fromBox;
                vec2I_ toBox  ;   
                m_game->GetMove(*fromState,*nextState,fromBox,toBox);

                {
                    //拐弯推箱子
                    vec2I_ pre = fromBox*2 - toBox;
                    canWalk = m_game->GenWalkPath(*fromState,fromState->man[0].x,fromState->man[0].y,pre.x,pre.y,&pathSeg);
                    pathSeg.push_back(fromBox);
                }

                if(canWalk)
                {
                    outPath->insert(outPath->end(),pathSeg.begin(),pathSeg.end());
                }
                else
                {
                    int a = 0;
                }

                it = it->next;
            }
        }

        if(G_Timer)
            m_timeCost = G_Timer->GetAccumTimeReal() - m_timeBegin;
        OutputDebugText(StrFormat("//path find ok (open%d,close%d,path%d,time%.3f)\n//", openTableMax, closeTableMax,int(outPath->size()),float(m_timeCost)));
        for (BoxManPath::iterator it=outPath->begin();it!=outPath->end();)
        {
            OutputDebugText(StrFormat("(%d,%d)", it->x, it->y));
            ++it;
            if (it!=outPath->end())
            {
                OutputDebugText("-->");
            }
        }
        OutputDebugText("\n");

    }
    else if (m_findStatus<0)
    {
        if(G_Timer)
            m_timeCost = G_Timer->GetAccumTimeReal() - m_timeBegin;
        OutputDebugText(StrFormat("//path find fail %d (open%d,close%d),time%.3f\n", m_findStatus, openTableMax, closeTableMax,float(m_timeCost)));
    }
    else// if (m_findStatus==Find_Busy)
    {
        m_timeCost += cycleTime;
    }
    return m_findStatus;
}

int  BoxManFinderAstar::InsertNeighbors(/*const*/ AStarNode* curNode)
{
    PROSTATISTIC_BoxMan("BoxManFinder::InsertNeighbors()",0);
    // 邻居
    const int MaxMapSize = m_game->MaxMapSize;
    MiniGameBoxMan::Block (&mapdata)[MaxMapSize][MaxMapSize] = m_game->m_map;
    AStarNode* pNode;
    AStarNode  cNode;
    const vec2I_& manPos = curNode->state.man[0];
    //BoxManPath path;
    int insertNum = 0;
    NodePtrSet::iterator CloseEnd = m_closeTable.end();

    static int costTable[MaxMapSize][MaxMapSize];
    m_game->GenCosts(curNode->state,manPos.x,manPos.y,costTable);   //man 可以走到[px,py]

    //PROSTATISTIC_BoxMan("BoxManFinder::InsertNeighbors()",0);
    for(int i=0;i<BoxNum;i++)
    {
        const vec2I_& curBox = curNode->state.box[i];
        vec2I_* dir = MiniGameBoxMan::dirOff;
        for(int d = 0; d < 4; ++d,++dir)
        {
            bool valid = false;//剔除剪枝
            cNode = *curNode;

            int tx = curBox.x + dir->x;
            int ty = curBox.y + dir->y;
            int px = curBox.x - dir->x;
            int py = curBox.y - dir->y;
            if (   tx>=0 && tx<MaxMapSize
                && ty>=0 && ty<MaxMapSize
                && px>=0 && px<MaxMapSize
                && py>=0 && py<MaxMapSize
                //&& curNode->state.obj(px,py)!=MiniGameBoxMan::Obj_Box
                && costTable[px][py] >=0  //canWalk to pre
                && m_game->IsBadMove(curNode->state,curBox.x,curBox.y,tx,ty)==false
                )
            {
                // //man 可以走到[px,py]
                {
                    cNode.state.moveman(manPos.x,manPos.y,px,py);
                    if (m_game->CanPush(cNode.state,(MiniGameBoxMan::Dir)d)>0)
                    {
                        valid = true;
                        cNode.state.man[0] = curBox;
                        cNode.state.movebox(curBox.x,curBox.y,tx,ty);
                    }
                }
            }

            if (valid)
            {
                bool inClose = (m_closeTable.find(&cNode)!=CloseEnd/*m_closeTable.end()*/);
                if (inClose==false)//不在闭表中
                {
                    NodePtrSet::iterator it = m_openTable.find(&cNode); 
                    bool inOpen = (it!=m_openTable.end());
                    if (inOpen==false)
                    {
                        //不在开表中
                        //nodeData = open + close
                        //m_nodeData.push_back(cNode); //这里分配内存是大部分的耗时
                        //pNode = &m_nodeData.back();
                        pNode = m_nodeData.AllocObject();
                        *pNode = cNode;
                        insertNum++;
                    }
                    else
                    {
                        //已在开表中
                        pNode = *it;

                        //m_nodeData.push_back(cNode); 
                        //pNode = &m_nodeData.back();
                    }

                    //int w = path.size()+1;     //最小步数
                    int w = costTable[px][py]+1; //最小步数
                    //int w = 1;                 //最小推动箱子数
                    bool relax = InsertToOpenTable(inOpen,pNode,curNode,w);

                    //边放松了
                    if (relax)
                    {
                        //manpos 可能改变了 修正等效状态
                        pNode->state.man[0] = cNode.state.man[0];
                    }

                    pNode->isOk = m_game->IsOk(pNode->state); //找到非最短路径 方便某些算法即时结束
                    if (pNode->isOk)
                    {
                        if(okNode==NULL || okNode->f>pNode->f)     //记录目前为止的最短路径
                        {
                            okNode = pNode;
                        }
                    }
                }
            }
        }
    }
    return insertNum;
}

bool BoxManFinderAstar::InsertToOpenTable(bool inOpen,AStarNode* node, AStarNode* curNode, float w)
{
    PROSTATISTIC_BoxMan("BoxManFinder::InsertToOpenTable()",0);
    //if ( node.type != Barrier )    // 有效节点
    {
        //NodePtrSet::iterator it;
        //bool inOpen = (m_openTable.find(node)!=m_openTable.end()); //避免重复查找
        if (inOpen )
        {
            // 在open表中
            // 判断是否更短路径(相比已存在的)
            if ( node->g > curNode->g + w )    // 如果更优化
            {
                //sort f cost
                m_openTableF.erase(m_openTableF.find(node));
                node->g = curNode->g + w;
                node->f = node->g + node->h;
                node->parent = curNode;
                m_openTableF.insert(node);
                return true;
            }
        }
        else
        {
            // 不在open表中
            //不是更短路径,无需调整
            node->g = curNode->g + w;
            node->h = CalH(node->state);
            node->f = node->g + node->h;
            node->parent = curNode;
            //int size = m_openTable.size();
            m_openTable.insert(node);
            m_openTableF.insert(node);
        }
    }
    return false;
}

float BoxManFinderAstar::CalH(const GameState& state)
{
    //找到的不一定是最短路径,而是估计的最短路径(比如从起点有一条绕大弯的滑道)
    //评估函数要尽量准确(可以更快速更准确的搜索到路径)

    //当地图很大,且某个box需要反向拉到开阔地掉头时,可能找不到路径!!
    PROSTATISTIC_BoxMan("BoxManFinder::CalH()",0);
    int minH = MaxInt;
    int h;
    //评估
    const vec2I_* start,*end;
    const vec2I_* Box = state.box;
    const vec2I_* Dst = m_game->Dst;

//#define DIS(start,end)  ( ((start->x>=end->x)?(start->x - end->x):(end->x - start->x)) \
//                           + ((start->y>=end->y)?(start->y - end->y):(end->y - start->y)) )
    
#define DIS2(start,dst)  ( m_game->DstCost[dst][start->x][start->y] )


    //类似TSP
#define DstNum BoxNum
    int map[MiniGameBoxMan::MaxBox];
    int flag[MiniGameBoxMan::MaxBox];
    for (int r=0;r<BoxNum;r+=3) //移位 更多尝试 贪心算法
    {
        h = 0;
        for (int d=0;d<DstNum;++d)
            flag[d] = 1;
        
        for (int b=0;b<BoxNum;++b)
        {
            //int index = b;
            int boxIndex = (b+r)%BoxNum;

            start = &Box[boxIndex];
            int minDis = MaxInt;
            int minDstIndex = 0;

            for (int d=0;d<DstNum;++d)
            {
                if (flag[d])
                {
                    end = &Dst[d];
                    //int dis = DIS(start,end);
                    int dis = DIS2(start,d);
                    if (dis<minDis)
                    {
                        minDis = dis;
                        minDstIndex = d;
                        if (dis==0) //最小
                            break;
                    }
                }
            }

            flag[minDstIndex] = 0;
            map[boxIndex] = minDstIndex;

            h += minDis;
        }

        //交换交叉的路径
        int cnt = 0;
        while(cnt++<5)
        {
            const vec2I_* startI,*endI;
            const vec2I_* startJ,*endJ;
            bool find = false;
            for (int i=0;i<BoxNum-1;++i)
            {
                startI = &Box[i];
                endI   = &Dst[map[i]];
                for (int j=i+1;j<BoxNum;++j)
                {
                    startJ = &Box[j];
                    endJ   = &Dst[map[j]];

                    //int dis  = DIS(startI,endI) + DIS(startJ,endJ);
                    //int dis2 = DIS(startI,endJ) + DIS(startJ,endI);
                    int dis  = DIS2(startI,map[i]) + DIS2(startJ,map[j]);
                    int dis2 = DIS2(startI,map[j]) + DIS2(startJ,map[i]);

                    if (dis2<dis)
                    {
                        //交换start
                        h -= (dis-dis2);
                        int temp = map[i];
                        map[i] = map[j];
                        map[j] = temp;
                        endI   = &Dst[map[i]];
                        find = true;
                    }
                }
            }
            if (find==false)
            {
                break;
            }
        }

        if (h<minH)
        {
            minH = h;
        }
    }


    //float score[MiniGameBoxMan::MaxBox] = {0.9f,0.85f,0.80f,0.75f,0.7f,0.5f,0.5f,0.5f,0.5f,0.5f};
    int good = 0;
    for (int b=0;b<BoxNum;++b)
    {
        start = &Box[b];
        for (int d=0;d<BoxNum;++d)
        {
            end = &Dst[d];
            if (start->x==end->x && start->y==end->y)
            {
                good++;
                break;
            }
        }
    }


    //估值h等比偏小,趋近于BFS,启发效率低下。因为前面g占比较小的最短路径h偏小的更多,f也会偏小,到了后面排序会被提前(导致最小路径树太粗)
    //估值h等比偏大,趋近于DFS,更大概率偏离真实的最短路径
    //估值h非等比(近似)于实际值,估值混乱,启发失效,
    //minH *= 2;
    minH *= 20;
   
    //minH -= good*10;    //minH<0时无法区分好坏
    //minH *= (1-good*0.09f);

    return minH;
}

//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/BoxMan/MiniGameBoxMan.h
//  @Brief:     MiniGameBoxMan
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================

#ifndef  __MiniGameBoxMan__H__
#define  __MiniGameBoxMan__H__

#include "Math/MathLib.h"
#include "Render/Texture.h"
#include "Rpg/MiniGame.h"
#include "Render/MC_Misc.h"

#include <vector>
#include <list>

//3d版:可以从上往下推箱子,不能从下往上推  人可以向上跳  
//      升降机关控制电梯可以抬起别处方块  
//      漏空地板人和箱子会掉下去,不能推踩  
//      两个箱子上下可以堆叠   
//      加了单向限制搜索的状态数更少

//#define PROSTATISTIC_BoxMan
#define PROSTATISTIC_BoxMan PROSTATISTIC //对速度有影响

#define BM_ERROR_POINT_WALL 3

#define BM_Move_Bad    -1
#define BM_Cannot_Move -2
#define BM_MOVED_OK  5
#define BM_BOX_MOVED 6
#define BM_MAN_MOVED 7

//typedef vec2I vec2I_;
typedef vec2C vec2I_; //省了内存, tagzone等函数使用stl时速度会变慢
typedef std::list<vec2I_>   BoxManPath;

enum MiniBoxManCmd
{
    CMD_ManMove ,      //棋子走动
    CMD_GameOver,
    CMD_Restart ,
};
const char* BoxManCmdToString(int enumeration);

class Maze;
class BoxManFinder;
class BoxManPlayerRole;
class MiniGameBoxMan:public MiniGame
{
public:
    enum BlockType
    {
        BT_Wall     = '#',  //wall
        BT_Out      = 'X',  //outside
        BT_Empty    = ' ',  //road    /SPACE/  1 2 3 4带高度?
        BT_EmptyBad = '!',  //road but bad point
        BT_Dst      = '.',  //destination

        BT_BoxAtDst = 'B',     
        BT_ManAtDst = 'M',     
    };
    enum ObjType
    {
        Obj_Null  = 0  ,
        Obj_Box   = 'b',
        Obj_Man   = 'm',
    };
    enum Dir
    {
        Dir_NULL =-1,
        UP       =0,
        DOWN     ,
        LEFT     ,
        RIGHT    ,
    };
    static vec2I_ dirOff[4];

    static const int MaxMapSize =  20;
    static const int MaxMan = 1;
    static const int MaxBox = 10;

    struct BoxManState
    {
        //todo 压缩内存占用,决定了可以搜索的最大状态数  //488byte*50W state => 250M 当内存超出时使用虚拟内存 或保存到文件
        signed char zone[MaxMapSize][MaxMapSize];
        vec2I_ man[MaxMan];
        vec2I_ box[MaxBox];
        //char   movingBox; //只能记位置,不能记索引,因为box做了normalize()
        //vec2I_ fromBox,toBox;     
        void operator=(const BoxManState& rhs)
        {
            PROSTATISTIC_BoxMan("BoxManState::operator=()",0);
            memcpy(this,&rhs,sizeof(BoxManState));    //提速,避免过多的函数调用(没有内联成功?)
        }
        ObjType obj(int x,int y) const;
        void moveman(int x,int y,int tx,int ty);
        void movebox(int x,int y,int tx,int ty);
        void normalize();
    };
    struct Block
    {
        BlockType blockType;
        float     phase;
    };
    MiniGameBoxMan();
    virtual ~MiniGameBoxMan();

    virtual bool KeepResource(bool once,int& circle,String& nextTip);
    virtual bool Start();
    virtual bool Restart();
    virtual bool Stop();
    virtual bool Render();
    virtual void RenderUI();
    virtual bool Update();
    virtual bool Free();
    virtual bool IsEnd();

    //三种类型结构
    virtual MiniPlayer*  CreatePlayer();
    virtual MiniPlayer*  CreateRobot ();
    virtual MiniPlayer*  CreateRole  ();

    //处理游戏网络命令包
    virtual int  ProcessPacketCmd(PacketBase* packet);
    //virtual const char* CmdToString(const char* stream,int len);

    void  UpdateGameRect();


    //自动求解
    void ThinkTheWay();
    void ClearPath();

    //鼠标点选寻路走动
    enum 
    {
        zRoad = 0 ,//>=0 zoneID
        zWall = -1,
        zBox  = -2,
    };
    void TagZone(BoxManState& state);
    bool CanWalk(const BoxManState& state,int x, int y,int dx, int dy);
    bool GenWalkPath(const BoxManState& state,int x, int y,int dx, int dy,BoxManPath*  path,bool ignoreDstBox=false);
    bool GetMove(const BoxManState& fromState,const BoxManState& toState,vec2I_& fromBox,vec2I_& toBox);

    void GenCosts(const BoxManState& state,int manX, int manY,int table[MaxMapSize][MaxMapSize]);

    int  CanPush(BoxManState& state,Dir dir);
    int  PushMan(BoxManState& state,Dir dir);

    bool IsBadMove(const BoxManState& state,int bx, int by, int tx, int ty);
    bool IsBad3X3 (const BoxManState& state,int bx, int by, int tx, int ty);
    bool IsBad3X3N(const BoxManState& state,int cx,int cy,int bx, int by, int tx, int ty);

    bool ReadMap(int index);
    bool RandMap(int step=-1);
    bool SaveMap(const char* fileName);//editor
    bool Convert3DMap(int lv,std::string& buf);
    bool LoadedProcess();
    bool IsOk(const BoxManState& state);


protected:
    void OnLButtonUp(const vec2I& point);
    void OnKeyDown();

protected:
public:
    BoxManPlayerRole* m_myRolePlayer;

    RectF       m_gameRect;
    RectF       m_rectLevel;
    TexturePtr  m_texBack;
    TexturePtr  m_texMap;

    int   m_level;
    vec2I m_mapSize;
    int   m_manNum;
    int   m_boxNum;

    Block m_map [MaxMapSize][MaxMapSize];
    static const int MaxNarrowZone = 16;
    signed char  m_narrowZone [MaxMapSize][MaxMapSize];//标记单边区域
    vec2I_ Dst[MaxBox];
    int DstCost[MaxBox][MaxMapSize][MaxMapSize];

    Maze* _Maze;
    char Sight[MaxMapSize*MaxMapSize];

    BoxManState m_curState;

    BoxManFinder* m_BoxManFinder;

    BoxManPath* m_movePath;
    float       m_pathTime;
    float       m_keyTime;

    int   m_randGenStatus;

    int  CellWidth;
};
extern MiniGameBoxMan* G_BoxManGame;
#endif 
//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/BoxMan/MiniGameBoxMan.cpp
//  @Brief:     MiniGameBoxMan
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================

#include "General/Pch.h"
#include "General/Window.h"
#include "General/Timer.h"
#include "General/String.h"
#include "Math/MathLibAdvance.h"
#include "Gui/GuiMgr.h"
#include "Gui/RpgGuis.h"
#include "Gui/GuiControlMisc.h"
#include "Input/InputMgr.h"
#include "Render/RendDriver.h"
#include "Render/Shader.h"
#include "Render/Camera.h"
#include "Rpg/SyncGameInfo.h"
#include "Packet/PacketMiniGame.h"
#include "Net/PacketList.h"
//#include "BoxMan/BoxManPlayer.h"
#include "BoxMan/BoxManFinder.h"
#include "BoxMan/MiniGameBoxMan.h"
#include "BoxMan/MiBoxMan_PlayGui.h"
#include "General/List.cpp.h"
#include <set>
#include "General/Pce.h"
#include "Render/FontMgr.h"
#include "Render/Maze.h"

struct BMNode
{
    inline BMNode()
        :x(0)
        ,y(0)
        ,from(NULL)
    {
    }
    inline BMNode(char _x,char _y,BMNode* _from)
        :x(_x)
        ,y(_y)
        ,from(_from)
    {
    }
    char x,y;
    BMNode* from;
};

//template class Std::List<BMNode>;
template class Std::List<vec2I_>;

//typedef List<BMNode> BMNodeList;
typedef std::list<BMNode> BMNodeList;

const char* BoxManCmdToString(int enumeration)
{
    switch(enumeration)
    {
    case CMD_ManMove :return "CMD_ManMove ";
    case CMD_GameOver:return "CMD_GameOver";
    case CMD_Restart :return "CMD_Restart ";
    default          :return "CMD_unknow";
    }
    return "CMD_unknow";
}

MiniGameBoxMan *G_BoxManGame;
vec2I_ MiniGameBoxMan::dirOff[4] = 
{ 
    vec2I_( 0,-1), 
    vec2I_( 0, 1), 
    vec2I_(-1, 0), 
    vec2I_( 1, 0) 
};
MiniGameBoxMan::MiniGameBoxMan()
:m_movePath(NULL)
,m_BoxManFinder(NULL)
{
    CmdEnumToString = BoxManCmdToString;
    G_BoxManGame = this;
    m_level = 1;
    m_3dMode = false;
    for(int y = 0; y < MaxMapSize; y++)
    {
        for(int x = 0; x < MaxMapSize; x++)
        {
            Block* block   = &m_map[x][y];
            block->phase = RandRange(0.0f,1.0f);
        }
    }
    _Maze = new Maze;
}

MiniGameBoxMan::~MiniGameBoxMan()
{
    G_BoxManGame = NULL;
    SafeDelete(m_movePath);
    SafeDelete(_Maze);
    SafeDelete(m_BoxManFinder);
}

bool MiniGameBoxMan::Start()
{
    if(!MiniGame::Start())
        return false;

    m_gameState = MS_Gamming;
    m_pathTime = 0;
    m_keyTime  = 0;

    //    char buf[256];
    G_TextureMgr->AddTexture(m_texMap ,  "data/minigame/boxman/map.png");
    G_TextureMgr->AddTexture(m_texBack,  "data/minigame/boxman/backframe.png");
    //m_level = 1;
    m_level = 3;
    //m_level = 6;
    ReadMap(m_level);

    if(m_movePath==NULL)
        m_movePath = new BoxManPath;
    if(m_BoxManFinder==NULL)
    {
        //m_BoxManFinder = new BoxManFinderAstar;
        m_BoxManFinder = new BoxManFinderNet;
    }
    //
    if (m_movieScene == NULL)
    {
        LoadOption loader(LoadOption::GenDonotReShrinkBound, true, true);
        m_movieScene = new RendSys::MovieClip;
        m_movieScene->LoadFromFile("data/minigame/boxman/board.movie", &loader);

        Frame frame;
        frame.SetPos(m_startPos);
        m_movieScene->SetProgramFrame(&frame);
        m_movieScene->Advance();
    }

    if (m_movieScene->IsLoadComplete() == false)
    {
        m_gameState = MS_End;
        return false;
    }
    UpdateGameRect();
    //进入miniplaygui,(选人、选关卡都已在房间里进行完毕)。
    G_GuiMgr->PushGui(GetStyle()->playGUI.c_str(), GL_DIALOG);

    //设置摄像机
    CameraCtrlerTarget* ctrler = new CameraCtrlerTarget;
    ctrler->SetDistToTar(60);
    ctrler->SetTarPos(m_startPos);
    G_Camera->PushCtrler(ctrler);
    G_Camera->SetEuler(0, -60, 0);
    //片头摄像机
    PushIntroCamera();

    return true;
}

bool MiniGameBoxMan::Restart()
{
    G_GuiMgr->GetGui<MiBoxMan_PlayGui>()->m_textButtonOption[1]->SetState(GCS_NORMAL);
    m_movePath->clear();
    m_BoxManFinder->StopFind();
    if (m_level==-1)
    {
        m_randGenStatus = 0;
        RandMap();
    }
    else
    {
        if(ReadMap(m_level)==false)
        {
            m_level = 1;
            ReadMap(m_level);
        }
    }

    return true;
}

MiniPlayer* MiniGameBoxMan::CreatePlayer()
{
    return NULL  ;
}

MiniPlayer* MiniGameBoxMan::CreateRobot()
{
    return NULL  ;
}

MiniPlayer* MiniGameBoxMan::CreateRole()
{
    return NULL;
}
bool MiniGameBoxMan::Stop()
{
    SafeDelete(m_movePath);
    SafeDelete(m_BoxManFinder);

    {
        G_GuiMgr->GetGui<Rpg_ResultDlg>()->ShowResult(true);
        G_GuiMgr->PushGui("Rpg_ResultDlg", GL_DIALOGBOTTOM);
    }
    return MiniGame::Stop();
}

bool MiniGameBoxMan::KeepResource(bool once, int &circle, String &nextTip)
{
    //G_TextureMgr->AddTexture(m_textureBoard,"data/minigame/BoxMan/board.png");
    //G_TextureMgr->AddTexture(m_textureCantgo,"data/minigame/BoxMan/cantgo.png");
    return true;
}

bool MiniGameBoxMan::Render()
{
    vec2 _mousePos = G_BoxManGame->GetMousePos();

    if (m_3dMode)
    {
        G_RendDriver->EndUI();
        if(m_movieScene==NULL
            ||m_movieScene->IsLoadComplete()==false)
            return false;

        m_movieScene->RendClip();
        G_RendDriver->PushMatrix();
        G_RendDriver->MultMatrix(mat2Dto3D);

    }
    else
    {
        G_RendDriver->BeginUI();
        G_RendDriver->Color4f(1,1,1,1);
        G_RendDriver->SetRenderStateEnable(RS_BLEND,true);
        G_RendDriver->BlendFunc(Blend_Filter);
        G_ShaderMgr->PushShader();

        G_RendDriver->PushMatrix();
        G_RendDriver->MultMatrix(mat2DDpi);

        m_texBack->Bind();
        G_RendDriver->DrawTextureRect(BoardRect2D);
    }

    G_RendDriver->DepthMask(false);

    G_ShaderMgr->MapChangeParm();

    //画等级
    m_rectLevel = RectF(BoardRect2D.x + 633, BoardRect2D.y + 34, 39, 23);
    if(m_rectLevel.IsPointIn(_mousePos))
    {
        G_RendDriver->Color4f(1, 1, 0, 1);
    }
    else
    {
        G_RendDriver->Color4f(1, 0, 0, 1);
    }

    m_texLcdNumber->Bind();
    DrawLcd(3, m_level, m_rectLevel);

    //画时间
    m_texLcdNumber->Bind();
    G_RendDriver->Color4f(1, 0, 0, 1);
    DrawLcd(3, m_gameTime, RectF(BoardRect2D.x + 640, BoardRect2D.y + 177, 39, 23));

    G_RendDriver->Color4f(1, 1, 1, 1);
    G_RendDriver->SetRenderStateEnable(RS_BLEND, true);
    G_RendDriver->BlendFunc(RS_SRC_ALPHA, RS_ONE_MINUS_SRC_ALPHA);
    G_RendDriver->SetRenderStateEnable(RS_ALPHA_TEST, true);
    G_RendDriver->AlphaFunc(RS_GREATER, 0.0f);
    G_RendDriver->EnableRendState(RS_TEXTURE_2D);
    //for(int i = 0; i < m_allPlayerNum; i++)
    //{
    //    dynamic_cast<BoxManPlayer*>(m_miniPlayer[i])->Render();
    //}
    for (int i=0;i<1;i++)
    {
        //G_RendDriver->SetSamplerState(i, SS_MINFILTER, TF_LINEAR);
        //G_RendDriver->SetSamplerState(i, SS_MAGFILTER, TF_LINEAR);
        G_RendDriver->SetSamplerState(i, SS_MINFILTER, TF_POINT);    //缩放的字体还是有问题  虽然清晰了  但是笔画可能变细 导致单行像素消失
        G_RendDriver->SetSamplerState(i, SS_MAGFILTER, TF_POINT);
    }
    m_texMap->Bind();

    //
    //_Maze->MakeSightBlock(vec2I(m_curState.man[0].x,m_curState.man[0].y),100,Sight);


    //画地图
    for(int y = 0; y < m_mapSize.y; y++)
    {
        for(int x = 0; x < m_mapSize.x; x++)
        {
            char index = -1;
            Block* block = &m_map[x][y];
            switch(block->blockType)
            {
            case BT_Wall:
                index = 0;
                break;
            case BT_Out:
                index = 2;
                break;
            case BT_Empty:
                index = 1;
                break;
            case BT_EmptyBad:
                index = 8;
                break;
            case BT_Dst:
                //index = 5;
                index = 1;
                break;
            }
            if(index!=-1)
                G_RendDriver->DrawTextureRect(RectF(m_gameRect.x + x * CellWidth, m_gameRect.y + y * CellWidth, CellWidth, CellWidth), RectF(index / 9.0f, 0, 1 / 9.0f, 1));
        }
    }

    //画箱子
    m_texMap->Bind();
    BoxManState* state = &m_curState;

    if (m_BoxManFinder->m_findStatus == BoxManFinder::Find_Busy)
    {
        BoxManFinderAstar* astar = dynamic_cast<BoxManFinderAstar*>(m_BoxManFinder);
        if(astar && astar->m_openTableF.size()>0)
        {
            BoxManFinderAstar::AStarNode* curNode = *astar->m_openTableF.begin();
            if(curNode)
            {
                state = &curNode->state;
                G_RendDriver->Color4f(1, 1, 1, 0.5f);
            }
        }
    }

    for(int y = 0; y < m_mapSize.y; y++)
    {
        for(int x = 0; x < m_mapSize.x; x++)
        {
            Block* block = &m_map[x][y];
            char index = -1;
            switch(state->obj(x,y))
            {
            case Obj_Man:
            //    if (block->blockType!=BT_Dst)
            //        index = 6;
            //    else
            //        index = 7;        
                index = 6;
                break;
            case Obj_Box:
                if (block->blockType!=BT_Dst)
                    index = 3;
                else
                    index = 4;
                break;
            default:
                if (block->blockType==BT_Dst)
                    index = 5;
                break;
            }
            if(index!=-1)
            {
                G_RendDriver->Color4f(1, 1, 1, 1);
                G_RendDriver->BlendFunc(Blend_Filter); 
                G_RendDriver->DrawTextureRect(RectF(m_gameRect.x + x * CellWidth, m_gameRect.y + y * CellWidth, CellWidth, CellWidth), RectF(index / 9.0f, 0, 1 / 9.0f, 1));
            }
            if (block->blockType==BT_Dst)
            {               
                index = 7;
                G_RendDriver->BlendFunc(Blend_Additive);
                float size = (2 + 0.2f*sin(m_gameTime*16+block->phase*TWOPI))*CellWidth;
                G_RendDriver->Color4f(1, 1, 1, size/CellWidth/2-0.1f);
                G_RendDriver->DrawTextureRect(RectF(m_gameRect.x + (x+0.5)*CellWidth - size/2, m_gameRect.y + (y+0.5f)*CellWidth - size/2, size, size), RectF(index / 9.0f, 0, 1 / 9.0f, 1));
            }
        }
    }

    m_BoxManFinder->Render();

    G_RendDriver->DepthMask(true);

    if (m_3dMode)
    {
        G_RendDriver->PopMatrix();
    }
    else
    {
        G_RendDriver->PopMatrix();
        G_RendDriver->EndUI();
        G_ShaderMgr->PopShader();
    }
    return true;
}

void MiniGameBoxMan::RenderUI()
{
    //Render();
}
bool MiniGameBoxMan::ReadMap(int index)
{
    m_randGenStatus = -1;
    char buf[256];
    for(int y = 0; y < MaxMapSize; y++)
    {
        for(int x = 0; x < MaxMapSize; x++)
        {
            Block* block   = &m_map[x][y];
            block->blockType = BT_Out;
        }
    }

    sprintf(buf,"data/minigame/boxman/level%03d.txt",index);
    File file;
    if(file.Fopen(buf, "rt")==false)
    {
        return false;
    }

    file.ReadString();
    m_mapSize.x = file.ReadInt();
    m_mapSize.y = file.ReadInt();
    if(m_mapSize.x > MaxMapSize || m_mapSize.y > MaxMapSize)
        return false;

    file.NextLine();
    m_manNum = 0;
    m_boxNum = 0;
    for (int i=0;i<MaxMan;++i)
    {
        m_curState.man[i] = vec2I_();
    }
    for (int i=0;i<MaxBox;++i)
    {
        m_curState.box[i] = vec2I_();
    }

    int dstNum = 0;
    unsigned char c;
    for(int y = 0; y < m_mapSize.y; y++)
    {
        for(int x = 0; x < m_mapSize.x; x++)
        {
            file.Fread(&c,1,1);
            Block* block   = &m_map[x][y];
            switch(c)
            {
            case BT_BoxAtDst:
                if (m_boxNum>=MaxBox)
                    return false;
                block->blockType = BT_Dst;
                m_curState.box[m_boxNum] = vec2I_(x,y);
                Dst[dstNum] = vec2I_(x,y);
                m_boxNum++;
                dstNum++;
                break;
            case BT_ManAtDst:
                if (m_manNum>=MaxMan)
                    return false;
                block->blockType = BT_Dst;
                m_curState.man[m_manNum] = vec2I_(x,y);
                Dst[dstNum] = vec2I_(x,y);
                m_manNum++;
                dstNum++;
                break;
            case Obj_Man:
                if (m_manNum>=MaxMan)
                    return false;
                block->blockType = BT_Empty;
                m_curState.man[m_manNum] = vec2I_(x,y);
                m_manNum++;
                break;
            case Obj_Box:
                if (m_boxNum>=MaxBox)
                    return false;
                block->blockType = BT_Empty;
                m_curState.box[m_boxNum] = vec2I_(x,y);
                m_boxNum++;
                break;
            case BT_Dst:
                if (dstNum>=MaxBox)
                    return false;
                block->blockType = BT_Dst;
                Dst[dstNum] = vec2I_(x,y);
                dstNum++;
                break;
            default:
                block->blockType = (BlockType)c;
            }
        }

        file.NextLine();
    }

    LoadedProcess();

    return true;
}



bool MiniGameBoxMan::LoadedProcess()
{

    if (m_boxNum>MaxBox)
    {
        m_boxNum = MaxBox;
    }

    //排序 使得IsOK可以加速
    //normalize dst
    {
        int boxNum = G_BoxManGame->m_boxNum;
        vec2I_ temp;
        vec2I_* l = Dst;
        for(int i=0;i<boxNum-1;++i,++l)
        {
            vec2I_* r = l+1;
            for(int j=i+1;j<boxNum;++j,++r)
            {
                bool greate = false;
                if (l->x > r->x)
                {
                    greate = true;
                }
                else if (l->x == r->x)
                {
                    if (l->y > r->y)
                    {
                        greate = true;
                    }
                }
                if (greate)
                {
                    //swap
                    temp.x = l->x;
                    temp.y = l->y;
                    l->x = r->x;
                    l->y = r->y;
                    r->x = temp.x;
                    r->y = temp.y;
                }
            }
        }
    }

    m_curState.normalize();
    TagZone(m_curState);

    //标记bad point(假设box在dst上,反向推拉不到的地方标记为bad(比如贴墙的边缘)
    {
        char   flag[MaxMapSize][MaxMapSize];
        memset(flag,0,sizeof(flag));
        vec2I_ travNodes[MaxMapSize*MaxMapSize];//List<vec2I_> travNodes;
        vec2I_ nowNode;
        int CUR,END;
        Block* block,*block2;
        vec2I_ pos,pos2;

        for(int b = 0; b < m_boxNum; b++)
        {
            //广度优先搜索,标记可行连通区域
            CUR = END = 0;                  //travNodes.clear();
            nowNode = Dst[b];
            travNodes[END++] = nowNode;     //travNodes.push_back(start);
            flag[nowNode.x][nowNode.y] = 1;
            while(CUR<END)                  //while(travNodes.empty()==false)
            {
                nowNode = travNodes[CUR++]; //nowNode = travNodes.front();travNodes.pop_front();
                for(int d = 0; d < 4; ++d)
                {
                    pos    = nowNode + dirOff[d];            
                    pos2   = pos + dirOff[d];  
                    if (   pos.x>=0 && pos.x<m_mapSize.x
                        && pos.y>=0 && pos.y<m_mapSize.y
                        && pos2.x>=0 && pos2.x<m_mapSize.x
                        && pos2.y>=0 && pos2.y<m_mapSize.y
                        && flag[pos.x][pos.y]==0
                        )
                    {
                        block  = &m_map[pos.x][pos.y];
                        block2 = &m_map[pos2.x][pos2.y];
                        if (  (block->blockType==BT_Empty || block->blockType==BT_Dst)
                            &&(block2->blockType==BT_Empty || block2->blockType==BT_Dst)
                            //&&costTable[pos2.x][pos2.y]>=0  //man 可以走到
                            )
                        {
                            flag[pos.x][pos.y] = 1;
                            travNodes[END++] = pos; //travNodes.push_back(pos);
                        }
                    }

                }
            }
        }
        for(int y = 0; y < m_mapSize.y; y++)
        {
            for(int x = 0; x < m_mapSize.x; x++)
            {
                Block* block   = &m_map[x][y];
                if(flag[x][y]== 0 && block->blockType==BT_Empty)
                    block->blockType = BT_EmptyBad;
            }
        }
    }

    //预计算距离 用作估分
    {
        memset(DstCost,0xFF,sizeof(DstCost));
        vec2I_ travNodes[MaxMapSize*MaxMapSize];//List<vec2I_> travNodes;
        vec2I_ nowNode;
        int CUR,END;
        Block* block;
        vec2I_ pos;
        int cost = 0;

        for(int b = 0; b < m_boxNum; b++)
        {
            //广度优先搜索
            CUR = END = 0;                  //travNodes.clear();
            nowNode = Dst[b];
            travNodes[END++] = nowNode;     //travNodes.push_back(start);
            DstCost[b][nowNode.x][nowNode.y] = cost;
            while(CUR<END)                  //while(travNodes.empty()==false)
            {
                nowNode = travNodes[CUR++]; //nowNode = travNodes.front();travNodes.pop_front();
                cost = DstCost[b][nowNode.x][nowNode.y];
                for(int d = 0; d < 4; ++d)
                {
                    pos = nowNode + dirOff[d];  
                    if (   pos.x>=0 && pos.x<m_mapSize.x
                        && pos.y>=0 && pos.y<m_mapSize.y
                        && DstCost[b][pos.x][pos.y]==-1
                        )
                    {
                        block  = &m_map[pos.x][pos.y];
                        if (  (block->blockType==BT_Empty || block->blockType==BT_Dst))
                        {
                            DstCost[b][pos.x][pos.y] = cost+1;
                            travNodes[END++] = pos; //travNodes.push_back(pos);
                        }
                    }

                }
            }
        }
    }

    //标记单边区域ID(不可以掉头 不含dst),同一单边区域内不可以存在两个以上box(且man在区域外,只能往里推)
    //#################
    //# 11111111111   #  
    //#  #########    #
    //#  #########    #
    //# 22222222222   # 
    //#################
    {
        for(int y = 0; y < m_mapSize.y; y++)
        {
            for(int x = 0; x < m_mapSize.x; x++)
            {
                Block* pblock = &m_map[x][y];
                if (pblock->blockType==BT_Wall || pblock->blockType==BT_Out)
                {
                    m_narrowZone[x][y] = -1;
                }
                else
                {
                    m_narrowZone[x][y] = 127;
                }
            }
        }


        char  neighbors [MaxMapSize][MaxMapSize];//
        char  corners   [MaxMapSize][MaxMapSize];//
        for(int y = 0; y < m_mapSize.y; y++)
        {
            for(int x = 0; x < m_mapSize.x; x++)
            {
                bool l = (x>0 && m_map[x-1][y].blockType!=BT_Wall && m_map[x-1][y].blockType!=BT_Out);
                bool r = (x<m_mapSize.x-1 && m_map[x+1][y].blockType!=BT_Wall && m_map[x+1][y].blockType!=BT_Out);
                bool u = (y>0 && m_map[x][y-1].blockType!=BT_Wall && m_map[x][y-1].blockType!=BT_Out);
                bool d = (y<m_mapSize.y-1 && m_map[x][y+1].blockType!=BT_Wall && m_map[x][y+1].blockType!=BT_Out);
                int neighbor = 0;
                if (l) neighbor++;
                if (r) neighbor++;
                if (u) neighbor++;
                if (d) neighbor++;
                neighbors[x][y] = neighbor;
                if (neighbor==2 
                    && l!=r)
                {
                    corners[x][y] = true;
                }
                else
                {
                    corners[x][y] = false;
                }
            }
        }

        //
        vec2I_ temp;
        vec2I_ nowNode;
        List<vec2I_> travNodes;
        int zoneID = 0;
        //int zoneID = '1';
        for(int y = 0; y < m_mapSize.y; y++)
        {
            for(int x = 0; x < m_mapSize.x; x++)
            {
                if(m_narrowZone[x][y] == 127 
                    && neighbors[x][y] <= 2
                    && corners[x][y] == false
                    && m_map[x][y].blockType!=BT_Dst
                    )
                {
                    m_narrowZone[x][y] = zoneID;
                    travNodes.clear();
                    nowNode.x = x;
                    nowNode.y = y;
                    travNodes.push_back(nowNode);
                    while(travNodes.empty()==false)
                    {
                        nowNode = travNodes.front();
                        travNodes.pop_front();

                        if(neighbors[nowNode.x][nowNode.y] <= 2)
                        {
                            const vec2I_* dir = dirOff;
                            for(int d = 0; d < 4; ++d,++dir)
                            {
                                int tx = nowNode.x + dir->x;  
                                int ty = nowNode.y + dir->y;
                                if(   tx<0 || tx>=m_mapSize.x
                                    ||ty<0 || ty>=m_mapSize.y)
                                {
                                    continue;
                                }


                                signed char&  tzone  = m_narrowZone[tx][ty];
                                if(    tzone==127
                                    && m_map[tx][ty].blockType!=BT_Dst
                                    //&& neighbors[tx][ty] <= 2//单边区域使用内封口(外封口可能被移动)
                                    && neighbors[tx][ty] <= 3//单边区域可以使用非全开的外封口(外封口不能被移动)
                                    )
                                {
                                    tzone = zoneID;
                                    temp.x = tx;
                                    temp.y = ty;
                                    travNodes.push_back(temp);
                                }
                            }
                        }
                    }

                    zoneID++;
                    if (zoneID>=127)
                    {
                        int a = 0;
                    }
                }
            }
        }
        for(int y = 0; y < m_mapSize.y; y++)
        {
            for(int x = 0; x < m_mapSize.x; x++)
            {
                if(m_narrowZone[x][y] == 127)
                {
                    m_narrowZone[x][y] = -1;
                }
            }
        }
    }

    return true;
}

bool MiniGameBoxMan::Update()
{
    MiniGame::Update();
    UpdateGameRect();

    //一次时间片搜索
    if (m_BoxManFinder->m_findStatus==BoxManFinderAstar::Find_Busy)
    {
        BoxManFinder::FindStatus res = m_BoxManFinder->UpdateCycle(m_movePath);
        if (res!=BoxManFinderAstar::Find_Busy)
        {
            PROSTATISTIC_OUTPUT();
        }
    }

    //float frameTime = G_Timer->GetStepTimeLimited();
    //for(int i = 0; i < m_allPlayerNum; i++)
    //{
    //    //    dynamic_cast<BoxManPlayer*>(m_miniPlayer[i])->Update();
    //}

    if(G_Mouse->IsButtonUping(MOUSE_LEFT))
    {
        vec2I pos_ = vec2I(G_BoxManGame->GetMousePos());
        OnLButtonUp(pos_);
    }

    if (  m_randGenStatus==-1 //生成中不能移动,请等待地图生成
        &&m_BoxManFinder->m_findStatus!=BoxManFinderAstar::Find_Busy    //搜索中不能移动,请先取消搜索
        )
    {
        OnKeyDown();
    }

    IsEnd();

    if(m_movePath->size()>0)
    {
        m_pathTime+= G_Timer->GetStepTimeLimited();
        if(m_pathTime>0.1f || G_Keyboard->IsKeyPressed(DIK_LCONTROL))
        {
            m_pathTime = 0;

            vec2I_& manPos = m_curState.man[0];
            vec2I_ *p = &m_movePath->front();
            char tx = p->x - manPos.x;
            char ty = p->y - manPos.y;
            Dir dir = Dir_NULL;
            if (tx==-1) dir = LEFT;
            else if (tx==1 ) dir = RIGHT;
            else if (ty==-1) dir = UP;
            else if (ty==1 ) dir = DOWN;
            if(CanPush(m_curState,dir) <0)
            {
                m_movePath->clear();
            }
            else
            {
                PushMan(m_curState,dir);
                m_movePath->pop_front();
            }
        }
    }
    return true;
}

bool MiniGameBoxMan::Free()
{
    MiniGame::Free();
    return true;
}

bool MiniGameBoxMan::IsEnd()
{
    if (m_randGenStatus==-1
        &&IsOk(m_curState) == true)
    {
        //int a = 0;
        m_level++;
        Restart();
    }

    return m_gameState == MS_End;
}

int  MiniGameBoxMan::ProcessPacketCmd(PacketBase *packet)
{
    int cmd;
    packet->ReadValue(cmd);
    switch(cmd)
    {
    case CMD_ManMove:
        {
            int man = 0;
            vec2I targetPos;
            packet->ReadValue(man);
            packet->ReadValue(targetPos);
            //TryGo(man,targetPos);
        }
        break;
    case CMD_GameOver:
        //    {
        //        int turn = 0;
        //        packet->ReadValue(turn);
        //        m_winnerTurn = turn;
        //        m_turnTime = 0;
        //        m_gameStatus = Resulting;
        //        char sound[256];
        //        if (m_winnerTurn == m_lordTurn)
        //        {
        //            sprintf(sound,"data/sound/poker/play_lord_win");
        //        }
        //        else
        //        {
        //            sprintf(sound,"data/sound/poker/play_farmer_win");
        //        }
        //        if (m_winnerTurn%2) //
        //            strcat(sound,"_femail.wav");
        //        else
        //            strcat(sound,".wav");
        //        m_players[m_winnerTurn]->m_sound->PlaySound__(sound);
        //    }
        break;
    case CMD_Restart:
        //    Free();
        //    Start();
        break;
    }
    return 0;
}

void MiniGameBoxMan::OnLButtonUp(const vec2I& _point)
{
    //_point是3d 和 dpi转换后的坐标
    if(m_rectLevel.IsPointIn(vec2(_point)))
    {
        m_level++;
        Restart();
    }

    if (  m_randGenStatus==-1 //生成中不能移动,请等待地图生成
        &&m_BoxManFinder->m_findStatus!=BoxManFinderAstar::Find_Busy    //搜索中不能移动,请先取消搜索
        )
    {
        vec2I point = _point - vec2I(m_gameRect.x, m_gameRect.y);
        if((point.x > m_mapSize.x * CellWidth) || (point.x < 0)
            || (point.y > m_mapSize.y * CellWidth) || (point.y < 0))
        {
            //墙外
            return;
        }
        int tox = point.x / CellWidth;
        int toy = point.y / CellWidth;
        Block* block = &m_map[tox][toy];
        //墙
        if(block->blockType == BT_Wall)
        {
            return;
        }
        //墙外
        if(block->blockType == BT_Out)
        {
            return;
        }
        vec2I_& manPos = m_curState.man[0];
        bool canWalk = GenWalkPath(m_curState,manPos.x,manPos.y,tox, toy,m_movePath,true);
        m_pathTime = 0;
        if(canWalk=false)
        {
            m_movePath->clear();
            MsgBox(NULL, "这个地方不能去!", "ERROR", MB_OK);
        }
    }

}

void MiniGameBoxMan::OnKeyDown()
{
    int  nChar = 0;
    int  result = -1;
    Dir  dir = Dir_NULL;

    m_keyTime+= G_Timer->GetStepTimeLimited();
    if(m_keyTime>0.2f)
    {
        m_keyTime = 0;

        if (G_Keyboard->IsKeyPressed(DIK_UP   )) dir = UP   ;
        if (G_Keyboard->IsKeyPressed(DIK_DOWN )) dir = DOWN ;
        if (G_Keyboard->IsKeyPressed(DIK_LEFT )) dir = LEFT ;
        if (G_Keyboard->IsKeyPressed(DIK_RIGHT)) dir = RIGHT;

        if(dir!=Dir_NULL)
        {
            m_movePath->clear();
            result = CanPush(m_curState,dir);
            switch(result)
            {
            case BM_Move_Bad:
                MsgBox(NULL, "这个地方不能推进去,\n进去就出不来了!", "PROMPT", MB_OK);
                break;
            case BM_BOX_MOVED:
            case BM_MAN_MOVED:
                PushMan(m_curState,dir);
                break;
            }
        }
    }
}

void MiniGameBoxMan::UpdateGameRect()
{
    float width__  = m_texBack->GetWidth();
    float height_  = m_texBack->GetHeight();
    SetBoardRect(RectF((G_Window->m_iWidth - width__) / 2, (G_Window->m_iHeight - height_) / 2, width__, height_),
        RectF(m_startPos.x - 50, m_startPos.z - 33, 100, 66),
        m_startPos.y+10.1f);

    m_gameRect = RectF(BoardRect2D.x+28,BoardRect2D.y+25,530,455);
    CellWidth = Min(m_gameRect.width/m_mapSize.x,m_gameRect.height/m_mapSize.y);

    m_gameRect.x += (m_gameRect.width - m_mapSize.x*CellWidth)*0.5f;
    m_gameRect.width = m_mapSize.x*CellWidth;

    m_gameRect.y += (m_gameRect.height - m_mapSize.y*CellWidth)*0.5f;
    m_gameRect.height = m_mapSize.y*CellWidth;
}



void MiniGameBoxMan::ThinkTheWay()
{
    //定义好状态识别及转换,A*搜索即可。
    m_pathTime = 0;
    m_movePath->clear();
    m_BoxManFinder->InitMap(this);
    BoxManFinder::FindStatus res = m_BoxManFinder->BeginFind(m_curState,m_movePath);
    if (res!=BoxManFinderAstar::Find_Busy)
    {
        PROSTATISTIC_OUTPUT();
    }

    //path find ok (open296,close2507,path151,time0.187)
    //    (2,3)-->(3,3)-->(4,3)-->(3,3)-->(4,3)-->(5,3)-->(5,4)-->(5,5)-->(6,5)-->(7,5)-->(7,4)-->(6,4)-->(7,4)-->(7,3)-->(7,4)-->(7,3)-->(6,3)-->(6,2)-->(6,3)-->(5,3)-->(6,3)-->(5,3)-->(4,3)-->(5,3)-->(6,3)-->(6,2)-->(6,1)-->(5,1)-->(4,1)-->(3,1)-->(2,1)-->(2,2)-->(2,1)-->(2,2)-->(2,3)-->(2,2)-->(2,3)-->(2,4)-->(2,3)-->(2,2)-->(2,1)-->(3,1)-->(4,1)-->(5,1)-->(6,1)-->(6,2)-->(6,3)-->(5,3)-->(4,3)-->(3,3)-->(4,3)-->(5,3)-->(6,3)-->(6,2)-->(6,1)-->(5,1)-->(4,1)-->(3,1)-->(2,1)-->(2,2)-->(2,3)-->(1,3)-->(1,4)-->(2,4)-->(2,3)-->(3,3)-->(4,3)-->(5,3)-->(6,3)-->(7,3)-->(8,3)-->(8,2)-->(7,2)-->(7,3)-->(6,3)-->(5,3)-->(4,3)-->(3,3)-->(2,3)-->(2,2)-->(2,1)-->(3,1)-->(4,1)-->(5,1)-->(6,1)-->(6,2)-->(7,2)-->(7,3)-->(6,3)-->(6,2)-->(6,3)-->(5,3)-->(6,3)-->(5,3)-->(4,3)-->(5,3)-->(4,3)-->(3,3)-->(4,3)-->(3,3)-->(3,4)-->(3,3)-->(4,3)-->(5,3)-->(6,3)-->(6,4)-->(6,5)-->(5,5)-->(5,4)-->(6,4)-->(6,3)-->(5,3)-->(5,4)-->(5,3)-->(4,3)-->(5,3)-->(6,3)-->(6,2)-->(6,1)-->(5,1)-->(4,1)-->(3,1)-->(2,1)-->(2,2)-->(2,3)-->(1,3)-->(1,4)-->(2,4)-->(2,3)-->(2,2)-->(2,1)-->(3,1)-->(4,1)-->(5,1)-->(6,1)-->(6,2)-->(6,3)-->(5,3)-->(4,3)-->(3,3)-->(4,3)-->(5,3)-->(6,3)-->(6,2)-->(6,1)-->(5,1)-->(4,1)-->(3,1)-->(2,1)-->(2,2)-->(2,3)
        //==================^_^
        //BoxManFinder::InsertNeighbors()          cnt=2506    time=0.1764  avg=0.000070
        //BoxManFinder::BeginFind()                cnt=1       time=0.1001  avg=0.100076
        //BoxManFinder::InsertToOpenTable()        cnt=4983    time=0.0436  avg=0.000009
        //BoxManState::movebox()                   cnt=7674    time=0.0196  avg=0.000003
        //MiniGameBoxMan::TagZone()                cnt=7675    time=0.0182  avg=0.000002
        //lessN::operator()                        cnt=205857  time=0.0076  avg=0.000000
        //MiniGameBoxMan::GenCosts()               cnt=2506    time=0.0033  avg=0.000001
        //BoxManState::obj()                       cnt=78533   time=0.0031  avg=0.000000
        //BoxManFinder::CalH()                     cnt=2507    time=0.0025  avg=0.000001
        //MiniGameBoxMan::CanPush()                cnt=13855   time=0.0021  avg=0.000000
        //lessF::operator()                        cnt=36633   time=0.0008  avg=0.000000
        //BoxManState::normalize()                 cnt=7675    time=0.0004  avg=0.000000
        //MiniGameBoxMan::GenWalkPath()            cnt=28      time=0.0004  avg=0.000014
        //MiniGameBoxMan::IsBadMove()              cnt=30645   time=0.0003  avg=0.000000
        //MiniGameBoxMan::CanWalk()                cnt=11416   time=0.0002  avg=0.000000
        //BoxManState::moveman()                   cnt=13855   time=0.0002  avg=0.000000
        //MiniGameBoxMan::IsOk()                   cnt=5252    time=0.0001  avg=0.000000
        //MiniGameBoxMan::travNodes()              cnt=214     time=0.0001  avg=0.000001
        //MiniGameBoxMan::PushMan()                cnt=2       time=0.0000  avg=0.000001
}

void MiniGameBoxMan::ClearPath()
{
    m_movePath->clear();
    m_pathTime = 0;
}
int  MiniGameBoxMan::CanPush(BoxManState& state,Dir dir)
{
    PROSTATISTIC_BoxMan("MiniGameBoxMan::CanPush()",0);
    int result = BM_Cannot_Move;
    if(dir == Dir_NULL) 
        return BM_Cannot_Move;

    vec2I_& manPos = state.man[0];

    int  tx  = manPos.x + dirOff[dir].x;
    int  ty  = manPos.y + dirOff[dir].y;
    int  ttx = tx + dirOff[dir].x;
    int  tty = ty + dirOff[dir].y;
    Block* block   = &m_map[manPos.x][manPos.y];
    Block* blockT  = &m_map[tx][ty];
    Block* blockTT = &m_map[ttx][tty];
    if(       tx>=0 && tx<MaxMapSize
        && ty>=0 && ty<MaxMapSize
        && blockT->blockType != BT_Wall && blockT->blockType != BT_Out
        )
    {
        switch(state.obj(tx,ty))
        {
        case Obj_Null:
            result = BM_MAN_MOVED;
            break;
        case Obj_Box:
            if(       ttx>=0 && ttx<MaxMapSize
                && tty>=0 && tty<MaxMapSize
                //&& blockTT->blockType != BT_Wall && blockTT->blockType != BT_Out
                //&& state.obj(ttx,tty) != Obj_Box
                && state.zone[ttx][tty] >=0
                )
            {
                if(blockTT->blockType == BT_EmptyBad)
                    result = BM_Move_Bad;
                else 
                    result = BM_BOX_MOVED;
            }
            break;
        }
    }
    return result;
}

int  MiniGameBoxMan::PushMan(BoxManState& state,Dir dir)
{
    PROSTATISTIC_BoxMan("MiniGameBoxMan::PushMan()",0);
    int result = BM_Cannot_Move;
    if(dir == Dir_NULL) 
        return BM_Cannot_Move;

    vec2I_& manPos = state.man[0];

    int  tx  = manPos.x + dirOff[dir].x;
    int  ty  = manPos.y + dirOff[dir].y;
    int  ttx = tx + dirOff[dir].x;
    int  tty = ty + dirOff[dir].y;
    Block* block   = &m_map[manPos.x][manPos.y];
    Block* blockT  = &m_map[tx][ty];
    Block* blockTT = &m_map[ttx][tty];
    if(    tx>=0 && tx<MaxMapSize
        && ty>=0 && ty<MaxMapSize
        && blockT->blockType != BT_Wall && blockT->blockType != BT_Out
        )
    {
        switch(state.obj(tx,ty))
        {
        case Obj_Null:
            result = BM_MAN_MOVED;
            break;
        case Obj_Box:
            if(       ttx>=0 && ttx<MaxMapSize
                && tty>=0 && tty<MaxMapSize
                && blockTT->blockType != BT_Wall && blockTT->blockType != BT_Out
                && state.obj(ttx,tty) != Obj_Box
                )
            {
                if(blockTT->blockType == BT_EmptyBad)
                    result = BM_Move_Bad;
                else 
                    result = BM_BOX_MOVED;
            }
            break;
        }
    }

    if(result == BM_MAN_MOVED
        ||result == BM_BOX_MOVED
        )
    {
        state.moveman(manPos.x,manPos.y,tx,ty);
    }

    if(result == BM_BOX_MOVED)
    {
        state.movebox(tx,ty,ttx,tty);
    }

    if (IsOk(state)==false)
    {
        return result;
    }

    return BM_MOVED_OK;
}

//3X3中心的box是否bad
bool MiniGameBoxMan::IsBad3X3(const BoxManState& state,int bx, int by, int tx, int ty)
{
    PROSTATISTIC_BoxMan("MiniGameBoxMan::IsBad3X3()",0);
    int cx = tx;
    int cy = ty;
    MiniGameBoxMan::Block (&mapdata)[MaxMapSize][MaxMapSize] = G_BoxManGame->m_map;
    char blockT = mapdata[cx][cy].blockType;
    if(  blockT==MiniGameBoxMan::BT_Wall
       ||blockT==MiniGameBoxMan::BT_Out
       ||blockT==MiniGameBoxMan::BT_EmptyBad //(速度显著提升)
       )
        return true;

    char blockTs[9] = 
    {
        0,0,0,
        0,blockT ,0,
        0,0,0,
    };
    blockTs[0] = mapdata[cx-1][cy+1].blockType;
    blockTs[3] = mapdata[cx-1][cy].blockType;
    blockTs[6] = mapdata[cx-1][cy-1].blockType;

    blockTs[2] = mapdata[cx+1][cy+1].blockType;
    blockTs[5] = mapdata[cx+1][cy].blockType;
    blockTs[8] = mapdata[cx+1][cy-1].blockType;

    blockTs[1] = mapdata[cx][cy+1].blockType;
    blockTs[7] = mapdata[cx][cy-1].blockType;


    const int mx = tx - bx;
    const int my = ty - by;
    // 0 1 2
    // 3 4 5
    // 6 7 8
    signed char lzone[9] = 
    {
        zWall,zWall,zWall,
        zWall,zBox ,zWall,
        zWall,zWall,zWall,
    };
    {
        lzone[0] = state.zone[cx-1][cy+1];
        lzone[3] = state.zone[cx-1][cy];
        lzone[6] = state.zone[cx-1][cy-1];

        lzone[2] = state.zone[cx+1][cy+1];
        lzone[5] = state.zone[cx+1][cy];
        lzone[8] = state.zone[cx+1][cy-1];
        
        lzone[1] = state.zone[cx][cy+1];
        lzone[7] = state.zone[cx][cy-1];


        if (mx>0)         lzone[3] = zRoad;
        else if (mx<0)    lzone[5] = zRoad;
        else if (my>0)    lzone[7] = zRoad;
        else /*if (my<0)*/lzone[1] = zRoad;
    }


    //BT_Wall = '#' 
    //(cx,cy)陷入bad3X3
    // 0 1 2
    // 3 4 5
    // 6 7 8
    //*##  *#*
    //*bb  *bb
    //***  **#
    if(lzone[5]<0)//zWall or zBox
    {
        if(   (lzone[1]==zWall || lzone[7]==zWall)  //4竖直方向无法推动,需要先把5移走
            &&(lzone[2]==zWall || lzone[8]==zWall)  //5竖直方向无法推动 4 5叠到了一起
            )
        {       
            if( blockT!=MiniGameBoxMan::BT_Dst
                ||(lzone[5]==zBox && blockTs[5]!=MiniGameBoxMan::BT_Dst)
                )
            {       
                return true;
            }
        }
    }

    if(lzone[1]<0)//zWall or zBox
    {
        if(   (lzone[3]==zWall || lzone[5]==zWall)  
            &&(lzone[0]==zWall || lzone[2]==zWall) 
            )
        {       
            if( blockT!=MiniGameBoxMan::BT_Dst
                ||(lzone[1]==zBox && blockTs[1]!=MiniGameBoxMan::BT_Dst)
                )
            {       
                return true;
            }
        }
    }

    if(lzone[3]<0)//zWall or zBox
    {
        if(   (lzone[1]==zWall || lzone[7]==zWall)  
            &&(lzone[0]==zWall || lzone[6]==zWall)
            )
        {       
            if( blockT!=MiniGameBoxMan::BT_Dst
                ||(lzone[3]==zBox && blockTs[3]!=MiniGameBoxMan::BT_Dst)
                )
            {       
                return true;
            }
        }
    }

    if(lzone[7]<0)//zWall or zBox
    {
        if(   (lzone[3]==zWall || lzone[5]==zWall)  
            &&(lzone[6]==zWall || lzone[8]==zWall) 
            )
        {       
            if( blockT!=MiniGameBoxMan::BT_Dst
                ||(lzone[7]==zBox && blockTs[7]!=MiniGameBoxMan::BT_Dst)
                )
            {       
                return true;
            }
        }
    }

    if(blockT!=MiniGameBoxMan::BT_Dst)
    {
        // 0 1 2
        // 3 4 5
        // 6 7 8
        //*bb   #b* 
        //*bb   *bb
        //***   **#
        if(lzone[1]<0 && lzone[5]<0) //4无法推动,需要先把1或5移走
        {
            if(lzone[2]<0) //1 2 5 都无法移动了 
            {
                return true;
            }
            if(lzone[0]==zWall && lzone[8]==zWall) //4 1 5 都无法移动了 
            {
                return true;
            }
        }
        if(lzone[1]<0 && lzone[3]<0)
        {
            if(lzone[0]<0)
            {
                return true;
            }
            if(lzone[2]==zWall && lzone[6]==zWall)
            {                                                                                                                                                                                                                                                                                                                                                 
                return true;
            }
        }
        if(lzone[3]<0 && lzone[7]<0)
        {
            if(lzone[6]<0)
            {
                return true;
            }
            if(lzone[0]==zWall && lzone[8]==zWall)
            {
                return true;
            }
        }
        if(lzone[5]<0 && lzone[7]<0)
        {
            if(lzone[8]<0)
            {
                return true;
            }
            if(lzone[2]==zWall && lzone[6]==zWall)
            {
                return true;
            }
        }


        //todo
        //###############
        //#   b      #
        //反拉 找不到dst即失败
    }
    return false;
}
bool MiniGameBoxMan::IsBad3X3N(const BoxManState& state,int cx,int cy,int bx, int by, int tx, int ty)
{
    PROSTATISTIC_BoxMan("MiniGameBoxMan::IsBad3X3()",0);
    MiniGameBoxMan::Block (&mapdata)[MaxMapSize][MaxMapSize] = G_BoxManGame->m_map;
    char blockT = mapdata[cx][cy].blockType;
    //if(blockT==MiniGameBoxMan::BT_EmptyBad)
    //    return true;

    const int mx = tx - bx;
    const int my = ty - by;

    const int MapSizeX_1 = m_mapSize.x-1;
    const int MapSizeY_1 = m_mapSize.y-1;
    // 0 1 2
    // 3 4 5
    // 6 7 8
    signed char lzone[9] = 
    {
        zWall,zWall,zWall,
        zWall,zWall,zWall,
        zWall,zWall,zWall,
    };
    {
        lzone[4] = state.zone[cx][cy];
        if(cx>0)
        {
                               lzone[3] = state.zone[cx-1][cy];
            if (cy>0)          lzone[6] = state.zone[cx-1][cy-1];
            if (cy<MapSizeY_1) lzone[0] = state.zone[cx-1][cy+1];
        }
        if(cx<MapSizeX_1)
        {
                               lzone[5] = state.zone[cx+1][cy];
            if (cy>0)          lzone[8] = state.zone[cx+1][cy-1];
            if (cy<MapSizeY_1) lzone[2] = state.zone[cx+1][cy+1];
        }
        if(cy>0)             lzone[7] = state.zone[cx][cy-1];
        if(cy<MapSizeY_1)    lzone[1] = state.zone[cx][cy+1];


        if (tx==cx && ty==cy)
        {
        }
        else
        {
            if (cx>tx)         lzone[3] = zBox;
            else if (cx<tx)    lzone[5] = zBox;
            else if (cy>ty)    lzone[7] = zBox;
            else /*if (cy<ty)*/lzone[1] = zBox;

            if(cx>bx)
            {
                if(cy<by)      lzone[0] = zRoad;
                else if(cy>by) lzone[6] = zRoad;
            }
            else if(cx<bx)
            {
                if(cy<by)      lzone[2] = zRoad;
                else if(cy>by) lzone[8] = zRoad;
            }
        }
    }

    if(lzone[4]!=zBox) //非box
        return false;

    // 0 1 2
    // 3 4 5
    // 6 7 8

    //BT_Wall = '#' 
    //(cx,cy)陷入bad3X3
    if(blockT!=MiniGameBoxMan::BT_Dst)
    {
        //*##  *#*
        //*bb  *bb
        //***  **#
        if(lzone[1]<0)//zWall or zBox
        {
            if(lzone[0]==zWall || lzone[2]==zWall)
            {                                                                                                                                                                                                                                                                                                                                                 
                if(lzone[3]==zWall || lzone[5]==zWall)
                    return true;
            }
        }
        if(lzone[3]<0)//zWall or zBox
        {
            if(lzone[0]==zWall || lzone[6]==zWall)
            {                                                                                                                                                                                                                                                                                                                                                 
                if(lzone[1]==zWall || lzone[7]==zWall)
                    return true;
            }
        }
        if(lzone[7]<0)//zWall or zBox
        {
            if(lzone[3]==zWall || lzone[5]==zWall)
            {                                                                                                                                                                                                                                                                                                                                                 
                if(lzone[6]==zWall || lzone[8]==zWall)
                    return true;
            }
        }
        if(lzone[5]<0)//zWall or zBox
        {
            if(lzone[1]==zWall || lzone[7]==zWall)
            {                                                                                                                                                                                                                                                                                                                                                 
                if(lzone[2]==zWall || lzone[8]==zWall)
                    return true;
            }
        }

        //*bb   #b* 
        //*bb   *bb
        //***   **#
        if(lzone[1]<0 && lzone[3]<0)
        {
            if(lzone[0]<0)
            {
                return true;
            }
            if(lzone[2]==zWall && lzone[6]==zWall)
            {                                                                                                                                                                                                                                                                                                                                                 
                return true;
            }
        }
        if(lzone[1]<0 && lzone[5]<0)
        {
            if(lzone[2]<0)
            {
                return true;
            }
            if(lzone[0]==zWall && lzone[8]==zWall)
            {
                return true;
            }
        }
        if(lzone[3]<0 && lzone[7]<0)
        {
            if(lzone[6]<0)
            {
                return true;
            }
            if(lzone[0]==zWall && lzone[8]==zWall)
            {
                return true;
            }
        }
        if(lzone[5]<0 && lzone[7]<0)
        {
            if(lzone[8]<0)
            {
                return true;
            }
            if(lzone[2]==zWall && lzone[6]==zWall)
            {
                return true;
            }
        }
    }
    return false;
}
bool MiniGameBoxMan::IsBadMove(const BoxManState& state,int bx, int by, int tx, int ty)
{
    PROSTATISTIC_BoxMan("MiniGameBoxMan::IsBadMove()",0);
    //剪枝提速,减少内存占用,确保找到路径

    //(tx,ty)陷入Bad3X3
    if (IsBad3X3(state,bx,by,tx,ty))
    {
        return true;
    }


    //(tx,ty)的四个邻居陷入Bad3X3   (剪枝效率很低)? 
    if (0)
    {
        const int mx = tx - bx;
        const int my = ty - by;
        const int MapSizeX_1 = m_mapSize.x-1;
        const int MapSizeY_1 = m_mapSize.y-1;
        if (mx>=0 && tx<MapSizeX_1 && IsBad3X3N(state,tx+1,ty, bx,by,tx,ty))
        {
            return true;
        }
        if (mx<=0 && tx>0          && IsBad3X3N(state,tx-1,ty, bx,by,tx,ty))
        {
            return true;
        }
        if (my>=0 && ty<MapSizeY_1 && IsBad3X3N(state,tx,ty+1, bx,by,tx,ty))
        {
            return true;
        }
        if (my<=0 && ty>0          && IsBad3X3N(state,tx,ty-1, bx,by,tx,ty))
        {
            return true;
        }
    }


    //标记单边区域ID(不可以掉头 不含dst),同一单边区域内不可以存在两个以上box(且man在区域外,只能往里推)  (剪枝效率很低)
    if (0)
    {
        int mZone = m_narrowZone[state.man[0].x][state.man[0].y];
        char num[MaxNarrowZone];
        memset(num,0,MaxNarrowZone);
        const int boxNum = m_boxNum;
        const vec2I_* b = state.box;
        for (int i=0;i<boxNum;++i,++b)
        {
            int bZone = m_narrowZone[b->x][b->y];
            if( bZone>= 0)
            {
                num[bZone]++;
                if (num[bZone]>=2 && mZone!=bZone)
                {
                    return true;
                }
            }
        }
    }

    return false;
}

bool MiniGameBoxMan::IsOk(const BoxManState& state)
{
    PROSTATISTIC_BoxMan("MiniGameBoxMan::IsOk()",0);
    //for(int y = 0; y < m_mapSize.y; y++)
    //{
    //    for(int x = 0; x < m_mapSize.x; x++)
    //    {
    //        Block* block   = &m_map[x][y];
    //        if(block->blockType == BT_Dst
    //            && state.obj(x,y) != Obj_Box
    //            )
    //            return false;
    //    }
    //}
    const int boxNum = m_boxNum;
    const vec2I_* b = state.box;
    const vec2I_* d = Dst;
    for (int i=0;i<boxNum;++i,++b,++d)
    {
        if(       b->x != d->x 
            || b->y != d->y)
            return false;
    }
    return true;
}

void MiniGameBoxMan::TagZone(BoxManState& state)
{
    //划分标记可行连通区域 辅助路径查找(速度显著提升),设计地图时不要有孤岛,则zoneID最大不会超过MaxBoxNum+1
    PROSTATISTIC_BoxMan("MiniGameBoxMan::TagZone()",0);

    int num = MaxMapSize*MaxMapSize;
    signed char*  pzone  = &state.zone[0][0];
    Block* pblock = &m_map[0][0];

    for(int i = 0; i < num; ++i,++pzone,++pblock)
    {
        if (pblock->blockType==BT_Wall || pblock->blockType==BT_Out)
        {
            *pzone = zWall;
        }
        else
        {
            *pzone = 127;
        }
    }
    int boxNum = G_BoxManGame->m_boxNum;
    vec2I_* pbox = &state.box[0];
    for(int i = 0; i < boxNum; ++i,++pbox)
    {
        state.zone[pbox->x][pbox->y] = zBox;
    }

    //
    vec2I_ temp;
    vec2I_ nowNode;
    //List<vec2I_> travNodes;
    static vec2I_ travNodes[MaxMapSize*MaxMapSize];
    int CUR,END;
    int zoneID = 1;
    //int zoneID = '1';
    for(int y = 0; y < m_mapSize.y; y++)
    {
        for(int x = 0; x < m_mapSize.x; x++)
        {
            if(state.zone[x][y] == 127)
            {
                //划分标记可行连通区域
                state.zone[x][y] = zoneID;
                //travNodes.clear();
                CUR = END = 0;
                nowNode.x = x;
                nowNode.y = y;
                //travNodes.push_back(nowNode);
                //while(travNodes.empty()==false)
                travNodes[END++] = nowNode;
                while(CUR<END)
                {
                    //nowNode = travNodes.front();
                    nowNode = travNodes[CUR++];
                    //travNodes.pop_front();
                    const vec2I_* dir = dirOff;
                    for(int d = 0; d < 4; ++d,++dir)
                    {
                        int tx = nowNode.x + dir->x;  //使用char tx; 速度会变慢
                        int ty = nowNode.y + dir->y;
                        if(   tx<0 || tx>=m_mapSize.x
                            ||ty<0 || ty>=m_mapSize.y)
                        {
                            continue;
                        }

                        signed char&  tzone  = state.zone[tx][ty];
                        if(    tzone==127)
                        {
                            tzone = zoneID;
                            temp.x = tx;
                            temp.y = ty;
                            //travNodes.push_back(temp);
                            travNodes[END++] = temp;
                        }
                    }
                }

                zoneID++;
                if (zoneID>=127)
                {
                    int a = 0;
                }
            }
        }
    }

    if (0)
    {
        for(int y = 0; y < m_mapSize.y; y++)
        {
            for(int x = 0; x < m_mapSize.x; x++)
            {
                OutputDebugText(StrFormat("%2d ",(int)state.zone[x][y]));
            }
            OutputDebugText("\n");
        }
    }
}

bool MiniGameBoxMan::CanWalk(const BoxManState& state,int x, int y,int dx, int dy)
{
    PROSTATISTIC_BoxMan("MiniGameBoxMan::CanWalk()",0);
    if(   dx<0 || dx>=MaxMapSize
        ||dy<0 || dy>=MaxMapSize)
    {
        return false;
    }

    if (state.zone[dx][dy]<0 //zWall or zBox
        || state.zone[dx][dy]!=state.zone[x][y])
    {
        return false;
    }
    return true;
}

bool MiniGameBoxMan::GenWalkPath(const BoxManState& state,int x, int y,int dx, int dy, BoxManPath* outPath,bool ignoreDstBox)
{
    PROSTATISTIC_BoxMan("MiniGameBoxMan::GenWalkPath()",0);
    outPath->clear();
    if(   x<0 || x>=m_mapSize.x
        ||y<0 || y>=m_mapSize.y)
    {
        return false;
    }
    if(   dx<0 || dx>=m_mapSize.x
        ||dy<0 || dy>=m_mapSize.y)
    {
        return false;
    }
    int sZoneID = state.zone[x][y];
    int dZoneID = state.zone[dx][dy];

    if (ignoreDstBox==true)
    {
        if (dZoneID==zWall)
        {
            return false;
        }
        if ( dZoneID!=zBox  
            && dZoneID!=sZoneID//不通
            )
        {
            return false;
        }
    }
    else
    {
        if (dZoneID<0) //zWall or zBox
        {
            return false;
        }
        if (dZoneID!=sZoneID) //不通
        {
            return false;
        }
    }

    if(dx==x && dy==y)
        return true;

    char flags[MaxMapSize][MaxMapSize]; //这里static速度影响不大
    memset(flags,0,sizeof(flags));

    //广度优先搜索的另一种写法
    /*static */BMNodeList travNodes;
    BMNode temp(dx,dy,NULL);
    travNodes.push_back(temp);

    vec2I_ tempV;

    BMNodeList::iterator nowIt = travNodes.begin();
    while(nowIt != travNodes.end()) //empty()==false, 需要回推路径所以不能删除节点
    {
        BMNode& nowNode = *nowIt;
        const vec2I_* dir = dirOff;
        for(int d = 0; d < 4; ++d,++dir)
        {
            int tx = nowNode.x + dir->x;
            int ty = nowNode.y + dir->y;
            if(tx==x && ty==y)//find ok
            {
                //gen path
                BMNode* node = &nowNode;
                while(node)
                {
                    tempV.x = node->x;
                    tempV.y = node->y;
                    outPath->push_back(tempV);
                    node = node->from;
                }
                return true;
            }
            if(   tx<0 || tx>=m_mapSize.x
                ||ty<0 || ty>=m_mapSize.y)
            {
                continue;
            }

            if(state.zone[tx][ty] == sZoneID)
            {
                //node总数太大,不能预先开辟flag内存时
                //char flag = 0;
                //BMNodeList::iterator it  = travNodes.begin();
                //BMNodeList::iterator end = travNodes.end();
                //for(; it!=end; ++it)
                //{
                //    if(it->x==tx && it->y==ty)
                //    {
                //        flag = 1;
                //        break;
                //    }
                //}
                char& flag = flags[tx][ty]; //和上面速度差不多
                if(flag==0)
                {
                    flag = 1;
                    temp.x = tx;
                    temp.y = ty;
                    temp.from = &nowNode;
                    PROSTATISTIC_BoxMan("MiniGameBoxMan::travNodes()",0); 
                    travNodes.push_back(temp);
                }
            }
        }
        nowIt++;
    }
    return false;
}


bool MiniGameBoxMan::GetMove(const BoxManState& fromState,const BoxManState& toState,vec2I_& fromBox,vec2I_& toBox)
{
    //fromState toState必须是邻居
    //fromBox = nextState->fromBox;
    //toBox   = nextState->toBox;
    //查找fromBox
    const int BoxNum = m_boxNum;
    for(int i=0;i<BoxNum;i++)
    {
        fromBox = fromState.box[i];
        bool same = false;
        for(int j=0;j<BoxNum;j++)
        {
            if (fromBox==toState.box[j])
            {
                same = true;
                break;
            }
        }
        if (same==false)
        {
            //if (fromBox != nextState->fromBox)
            //{
            //    int a = 0;  //仅一个地图会断到?
            //}
            break;//找到nextState相同位置没有的box,即推动的fromBox
        }
    }
    //查找toBox
    for(int i=0;i<BoxNum;i++)
    {
        toBox = toState.box[i];
        bool same = false;
        for(int j=0;j<BoxNum;j++)
        {
            if (toBox==fromState.box[j])
            {
                same = true;
                break;
            }
        }
        if (same==false)
        {
            //if (toBox != nextState->toBox)
            //{
            //    int a = 0;
            //}
            break;
        }
    }
    return true;
}

void MiniGameBoxMan::GenCosts(const BoxManState& state,int manX, int manY,int table[MaxMapSize][MaxMapSize])
{
    PROSTATISTIC_BoxMan("MiniGameBoxMan::GenCosts()",0);
    for(int y = 0; y < m_mapSize.y; y++)
    {
        for(int x = 0; x < m_mapSize.x; x++)
        {
            table[x][y] = -1;
        }
    }

    if(   manX<0 || manX>=m_mapSize.x
        ||manY<0 || manY>=m_mapSize.y)
    {
        return;
    }

    int sZoneID = state.zone[manX][manY];

    //广度优先搜索
    //List<BMNode> travNodes;
    static BMNode travNodes[MaxMapSize*MaxMapSize];
    int CUR,END;
    CUR = END = 0;

    BMNode temp(manX,manY,NULL);
    //travNodes.push_back(temp);
    travNodes[END++] = temp;
    table[manX][manY] = 0;

    vec2I tempV;

    //while(travNodes.empty()==false)
    while(CUR<END)
    {
        //BMNode& nowNode = travNodes.front();
        BMNode& nowNode = travNodes[CUR];

        int nowCost = table[nowNode.x][nowNode.y];
        const vec2I_* dir = dirOff;
        for(int d = 0; d < 4; ++d,++dir)
        {
            int tx = nowNode.x + dir->x;
            int ty = nowNode.y + dir->y;
            if(   tx<0 || tx>=m_mapSize.x
                ||ty<0 || ty>=m_mapSize.y)
            {
                continue;
            }

            if(state.zone[tx][ty] == sZoneID)
            {
                int& flag = table[tx][ty]; 
                if(flag==-1)
                {
                    flag = nowCost+1;
                    temp.x = tx;
                    temp.y = ty;
                    //travNodes.push_back(temp);
                    travNodes[END++] = temp;
                }
            }
        }

        //travNodes.pop_front();
        CUR++;
    }
}

void MiniGameBoxMan::BoxManState::movebox(int x,int y,int tx,int ty)
{
    PROSTATISTIC_BoxMan("BoxManState::movebox()",0);
    int boxNum = G_BoxManGame->m_boxNum;
    vec2I_* it = box;
    for (int i=0;i<boxNum;++i,++it)
    {
        if (x==it->x && y==it->y)
        {
            it->x = tx;
            it->y = ty;
            //fromBox.x = x;
            //fromBox.y = y;
            //toBox.x = tx;
            //toBox.y = ty;
        }
    }
    normalize();
    G_BoxManGame->TagZone(*this);
}

void MiniGameBoxMan::BoxManState::normalize()
{
    PROSTATISTIC_BoxMan("BoxManState::normalize()",0);
    //排序 两个box互换位置是同一状态
    int boxNum = G_BoxManGame->m_boxNum;
    vec2I_ temp;
    vec2I_* l = box;
    for(int i=0;i<boxNum-1;++i,++l)
    {
        vec2I_* r = l+1;
        for(int j=i+1;j<boxNum;++j,++r)
        {
            bool greate = false;
            if (l->x > r->x)
            {
                greate = true;
            }
            else if (l->x == r->x)
            {
                if (l->y > r->y)
                {
                    greate = true;
                }
            }
            if (greate)
            {
                //swap
                temp.x = l->x;
                temp.y = l->y;
                l->x = r->x;
                l->y = r->y;
                r->x = temp.x;
                r->y = temp.y;
            }
        }
    }
}

void MiniGameBoxMan::BoxManState::moveman(int x,int y,int tx,int ty)
{
    PROSTATISTIC_BoxMan("BoxManState::moveman()",0);
    vec2I_* it = man;
    for (int i=0;i<G_BoxManGame->m_manNum;++i,++it)
    {
        if (x==it->x && y==it->y)
        {
            it->x = tx;
            it->y = ty;
        }
    }
}

MiniGameBoxMan::ObjType MiniGameBoxMan::BoxManState::obj(int x,int y) const
{
    PROSTATISTIC_BoxMan("BoxManState::obj()",0);
    const vec2I_* it = man;
    for (int i=0;i<G_BoxManGame->m_manNum;++i,++it)
    {
        if (x==it->x && y==it->y)
        {
            return Obj_Man;
        }
    } 
    it = box;
    for (int i=0;i<G_BoxManGame->m_boxNum;++i,++it)
    {
        if (x==it->x && y==it->y)
        {
            return Obj_Box;
        }
    }
    return Obj_Null;
}





  
二,随机生成关卡
        使用反向拉箱法,先将箱子全部放置在目标点,尝试若干次反向拉动箱子。生成好关卡后使用上面的自动求解算法计算通关步数,步数越多说明关卡的难度越大,相应的关卡质量越好,可以一次生成若干个取得分最高的。

bool MiniGameBoxMan::RandMap(int step)
{
    static int seed;
    seed = G_Timer->GetAccumTime();
    //seed = 95;11

    int cntLv = 0;
    //while (1)
    {
        //if(cntLv++>1)//100)
        //    return false;

        Srand(seed);
        for(int y = 0; y < MaxMapSize; y++)
        {
            for(int x = 0; x < MaxMapSize; x++)
            {
                Block* block   = &m_map[x][y];
                block->blockType = (BlockType)-1;
            }
        }

        m_mapSize.x = RandRange(10,MaxMapSize);
        m_mapSize.y = RandRange(10,MaxMapSize);

        m_manNum = 1;//RandRange(1,MaxMan);
        m_boxNum = RandRange(6,MaxBox);

        for (int i=0;i<MaxMan;++i)
        {
            m_curState.man[i] = vec2I_(-1,-1);
        }
        for (int i=0;i<MaxBox;++i)
        {
            m_curState.box[i] = vec2I_(-1,-1);
        }

        //
        int outNum = RandRange(3,5);
        for (int i=0;i<outNum;++i)
        {
            int X = RandRange(-m_mapSize.x/3,m_mapSize.x-2);
            int Y = RandRange(-m_mapSize.y/3,m_mapSize.y-2);
            int W = RandRange(0,m_mapSize.x/2);
            int H = RandRange(0,m_mapSize.y/2);
            for(int y=Y; y<m_mapSize.y&&y<Y+H; y++)
            {
                if(y<0)
                    continue;
                for(int x=X; x<m_mapSize.x&&x<X+W; x++)
                {
                    if(x<0)
                        continue;
                    Block* block   = &m_map[x][y];
                    block->blockType = BT_Out;
                }
            }
        }
        for(int y = 0; y < m_mapSize.y; y++)
        {
            for(int x = 0; x < m_mapSize.x; x++)
            {
                Block* block   = &m_map[x][y];

                if (block->blockType!=BT_Out)
                {
                    if (y==0||x==0||x==m_mapSize.x-1||y==m_mapSize.y-1
                        ||m_map[x-1][y].blockType==BT_Out
                        ||m_map[x+1][y].blockType==BT_Out
                        ||m_map[x][y-1].blockType==BT_Out
                        ||m_map[x][y+1].blockType==BT_Out
                        ||m_map[x-1][y-1].blockType==BT_Out
                        ||m_map[x-1][y+1].blockType==BT_Out
                        ||m_map[x+1][y-1].blockType==BT_Out
                        ||m_map[x+1][y+1].blockType==BT_Out
                        ||Rand()%6==0
                        )
                    {
                        block->blockType = BT_Wall;
                    }
                    else
                    {
                        block->blockType = BT_Empty;
                    }
                }
            }
        }

        //
        std::list<vec2I_> blocks;
        for(int y = 0; y < m_mapSize.y; y++)
        {
            for(int x = 0; x < m_mapSize.x; x++)
            {
                Block* block   = &m_map[x][y];
                if (block->blockType==BT_Empty)
                {
                    blocks.push_back(vec2I_(x,y));
                }
            }
        }

        if (blocks.size()<m_boxNum+m_manNum)
        {
            return false;
        }
        //man
        vec2I_ manPos;
        for(int i = 0; i < m_manNum; i++)
        {
            int index = RandRange(0,int(blocks.size()-1));
            std::list<vec2I_>::iterator it = blocks.begin();
            advance(it,index);
            manPos = (*it);
            m_curState.man[i] = manPos;
            blocks.erase(it);
        }

        //dst  todo如果dst仅因为墙的局限就只能拉一两步,将是不好的dst
        for(int i = 0; i < m_boxNum; i++)
        {

            int index = RandRange(0,int(blocks.size()-1));
            std::list<vec2I_>::iterator it = blocks.begin();
            advance(it,index);
            Dst[i] = (*it);
            Block* block   = &m_map[Dst[i].x][Dst[i].y];
            block->blockType = BT_Dst;
            blocks.erase(it);
        }

        //打通玩家和所有dst
        float dragData[MaxMapSize*MaxMapSize];
        for(int i = 0; i < m_boxNum; i++)
        {
            for(int y = 0; y < m_mapSize.y; y++)
            {
                for(int x = 0; x < m_mapSize.x; x++)
                {
                    Block* block   = &m_map[x][y];
                    dragData[m_mapSize.x*(y) + (x)] = (block->blockType==BT_Wall||block->blockType==BT_Out)?10:1;
                }
            }
            MazeSearchAStar finder;
            finder.InitMap(m_mapSize.x,m_mapSize.y,1,dragData,NULL,false);
            vec2I_ exit = Dst[i];
            bool find  = finder.FindPath(manPos.x,manPos.y,0,exit.x,exit.y,0);
            if ( find )
            {   
                //遍历路径
                MazeSearchAStar::AStarNode *startNode = &(finder.mazeBlock(manPos.x,manPos.y,0));
                MazeSearchAStar::AStarNode *endNode   = &(finder.mazeBlock(exit.x,exit.y,0));    
                MazeSearchAStar::AStarNode * it = startNode;
                while(it)
                {
                    if (it != endNode)
                    {
                        Block* block = &m_map[it->x][it->y];
                        if (block->blockType==BT_Wall||block->blockType==BT_Out)
                        {
                            block->blockType = BT_Empty;

                            if (it->x>0&&m_map[it->x-1][it->y].blockType==BT_Out)
                                m_map[it->x-1][it->y].blockType=BT_Wall;
                            if (it->x<m_mapSize.x-1&&m_map[it->x+1][it->y].blockType==BT_Out)
                                m_map[it->x+1][it->y].blockType=BT_Wall;

                            if (it->y>0&&m_map[it->x][it->y-1].blockType==BT_Out)
                                m_map[it->x][it->y-1].blockType=BT_Wall;
                            if (it->y<m_mapSize.y-1&&m_map[it->x][it->y+1].blockType==BT_Out)
                                m_map[it->x][it->y+1].blockType=BT_Wall;

                        }
                    }
                    it = it->next;
                }
            }
        }


        //反拉,一定有解
        for(int i = 0; i < m_boxNum; i++)
        {
            m_curState.box[i] = Dst[i];
        }
        int cntMove = 0;
        std::list<vec2I_> pullManPos;
        std::list<vec2I_> pullBoxPos;
        int costTable[MaxMapSize][MaxMapSize];
        while (1)
        {
            if(cntMove++>9000)
                break;

            manPos = m_curState.man[0];

            G_BoxManGame->TagZone(m_curState);  
            G_BoxManGame->GenCosts(m_curState,manPos.x,manPos.y,costTable);  

            int boxEnable[MaxBox*4];
            int dirEnable[MaxBox*4];
            int movEnableNum = 0;
            vec2I_ pos,pos2;
            for(int b=0;b<m_boxNum;b++)
            {
                vec2I_& box = m_curState.box[b];
                for(int i = 0; i < 4; i++)
                {
                    pos    = box+dirOff[i];            
                    pos2   = pos+dirOff[i];            
                    if (   pos.x>=0 && pos.x<m_mapSize.x
                        && pos.y>=0 && pos.y<m_mapSize.y
                        && pos2.x>=0 && pos2.x<m_mapSize.x
                        && pos2.y>=0 && pos2.y<m_mapSize.y
                        )
                    {
                        Block* block  = &m_map[pos.x][pos.y];
                        Block* block2 = &m_map[pos2.x][pos2.y];
                        if (  (block->blockType==BT_Empty || block->blockType==BT_Dst)
                            &&(block2->blockType==BT_Empty || block2->blockType==BT_Dst)
                            &&m_curState.obj(pos.x,pos.y)!=Obj_Box
                            &&m_curState.obj(pos2.x,pos2.y)!=Obj_Box
                            &&costTable[pos2.x][pos2.y]>=0  //man 可以走到
                            )
                        {
                            boxEnable[movEnableNum] = b;
                            dirEnable[movEnableNum] = i;
                            movEnableNum++;
                        }
                    }
                }
            }

            if (movEnableNum>0)
            {        
                int i = RandRange(0,movEnableNum-1);
                int dir = dirEnable[i];
                vec2I_& box = m_curState.box[boxEnable[i]];

                pos  = box+dirOff[dir];          
                pos2 = pos+dirOff[dir];   
                m_curState.man[0] = pos2;
                m_curState.movebox(box.x,box.y,pos.x,pos.y);
                pullManPos.push_back(pos2);
                pullBoxPos.push_back(pos);
                if(pullManPos.size()>8)//多回退几步
                {
                    pullManPos.pop_front();
                    pullBoxPos.pop_front();
                }
            }
            else //man陷入死胡同后多回退几步,只回退一步可能立刻又进死胡同了
            {
                vec2I_ boxPos;
                vec2I_ lastBox;
                while (pullManPos.size()>0)
                {
                    manPos  = pullManPos.back();
                    boxPos  = pullBoxPos.back();
                    lastBox = boxPos*2 - manPos;//m_curState.man[0]; //可能非连续拉同一个box
                    m_curState.man[0] = boxPos;
                    m_curState.movebox(boxPos.x,boxPos.y,lastBox.x,lastBox.y);
                    pullManPos.pop_back();
                }
            }
        }
    }

    LoadedProcess();

    if (IsOk(m_curState)) //Distance all >10
    { 
        return false;
    }
    m_randGenStatus = -1;
    return true;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值