Просмотр исходного кода

refactor:重构移动盘子生成逻辑。feat:关卡结果json每个盘子增加移动字段move。

wangfengdev 8 месяцев назад
Родитель
Сommit
8fa0433f66

+ 366 - 29
auto_fill_jewel_v3/FillGlobalConfig.cpp

@@ -15,31 +15,31 @@ FillGlobalConfig* FillGlobalConfig::_s_instance = nullptr ;
 
 bool FillGlobalConfig::init(string filename)
 {
-    _plateContours.clear();
-    _jewelContours.clear() ;
-    ifstream ifs( filename.c_str() );
-    if( ifs.good()==false ) return false ;
-    DynamicJsonBuffer buffer ;
-    JsonObject& root = buffer.parse(ifs) ;
-    string platedir = root["plate_contour_dir"].as<char*>() ;
-    string jeweldir = root["jewel_contour_dir"].as<char*>() ;
-    JsonArray& parr =root["plate_names"].as<JsonArray>() ;
-    JsonArray& jarr =root["jewel_names"].as<JsonArray>() ;
-    _plateContours.resize(parr.size());
-    _jewelContours.resize(jarr.size());
-    
-    for(int i=0;i<parr.size();++i ) {
-        string name1 = parr[i].as<char*>() ;
-        string fullname1 =  platedir + name1 ;
-        _plateContours[i]._name = name1 ;
-        _plateContours[i]._contour.readFromJsonFile(fullname1) ;
-    }
-    for(int i=0;i<jarr.size();++i ) {
-        string name1 = jarr[i].as<char*>() ;
-        string fullname1 =  jeweldir + name1 ;
-        _jewelContours[i]._name = name1 ;
-        _jewelContours[i]._contour.readFromJsonFile(fullname1) ;
-    }
+//    _plateContours.clear();
+//    _jewelContours.clear() ;
+//    ifstream ifs( filename.c_str() );
+//    if( ifs.good()==false ) return false ;
+//    DynamicJsonBuffer buffer ;
+//    JsonObject& root = buffer.parse(ifs) ;
+//    string platedir = root["plate_contour_dir"].as<char*>() ;
+//    string jeweldir = root["jewel_contour_dir"].as<char*>() ;
+//    JsonArray& parr =root["plate_names"].as<JsonArray>() ;
+//    JsonArray& jarr =root["jewel_names"].as<JsonArray>() ;
+//    _plateContours.resize(parr.size());
+//    _jewelContours.resize(jarr.size());
+//    
+//    for(int i=0;i<parr.size();++i ) {
+//        string name1 = parr[i].as<char*>() ;
+//        string fullname1 =  platedir + name1 ;
+//        _plateContours[i]._name = name1 ;
+//        _plateContours[i]._contour.readFromJsonFile(fullname1) ;
+//    }
+//    for(int i=0;i<jarr.size();++i ) {
+//        string name1 = jarr[i].as<char*>() ;
+//        string fullname1 =  jeweldir + name1 ;
+//        _jewelContours[i]._name = name1 ;
+//        _jewelContours[i]._contour.readFromJsonFile(fullname1) ;
+//    }
     return true;
 }
 
@@ -47,10 +47,12 @@ FillGlobalConfig* FillGlobalConfig::getInstance()
 {
     if( _s_instance == nullptr ) {
         _s_instance = new FillGlobalConfig ;
-        _s_instance->init("config.json") ;
+        //_s_instance->init("config.json") ;
         _s_instance->initJewelItems("sg_jewel_items.csv") ;
         _s_instance->initLevelDatas("sg_level_data.csv") ;
         _s_instance->initPlateItems("sg_plate_items.csv") ;
+        _s_instance->initMoveConfigDatas("sg_move_config_data.csv") ;
+        _s_instance->initGridBoxDatas("sg_gridbox_positions.csv") ;
     }
     return _s_instance ;
 }
@@ -183,8 +185,8 @@ bool FillGlobalConfig::initPlateItems( string filename )
                 pi._name = tokens[1] ;
                 int sz = 0 ;
                 string szStr = tokens[2] ;
-                if( szStr.compare("SM")==0 ) sz = FILLGLOBALCONFIG_PLATESIZE_SM ;
-                else if( szStr.compare("MD")==0 ) sz = FILLGLOBALCONFIG_PLATESIZE_MD;
+                //if( szStr.compare("SM")==0 ) sz = FILLGLOBALCONFIG_PLATESIZE_SM ;
+                if( szStr.compare("MD")==0 ) sz = FILLGLOBALCONFIG_PLATESIZE_MD;
                 else if( szStr.compare("LG")==0 ) sz = FILLGLOBALCONFIG_PLATESIZE_LG;
                 else continue; //无效盘子跳过
                 pi._size = sz ;
@@ -197,7 +199,7 @@ bool FillGlobalConfig::initPlateItems( string filename )
                 pi._bigJewCap = atof( tokens[9].c_str() ) ;
                 pi._bbwid = atof( tokens[10].c_str()) ;
                 pi._bbhei = atof( tokens[11].c_str()) ;
-                pi._etc = tokens[12] ;
+                pi._heng  = atof( tokens[12].c_str()) ;
                 _mapPlateItems[pi._id] = pi ;
                 _mapPlateIdArray[sz].push_back(pi._id) ;
             }
@@ -205,3 +207,338 @@ bool FillGlobalConfig::initPlateItems( string filename )
     }
     return true ;
 }
+
+
+bool FillGlobalConfig::initMoveConfigDatas( string filename )
+{
+    _mapMoveConfigDatas.clear() ;
+    ifstream ifs( filename.c_str() ) ;
+    if( ifs.good()==false ) return false ;
+    string line ;
+    //跳过第一行
+    std::getline(ifs, line) ;
+    vector<string> tokens ;
+    while( std::getline(ifs, line) ) {
+        if( line.length() > 2 ) {
+            tokens.clear() ;
+            boost::split(tokens, line, boost::is_any_of(","));
+            if( tokens.size()>=9 ) {
+                MoveConfig mhc ;
+                mhc._id = atof( tokens[0].c_str() ) ;
+                mhc._type = tokens[1] ;
+                mhc._lowestEquvSmlJewelCnt = atof( tokens[2].c_str() ) ;
+                mhc._nLayer = atof( tokens[3].c_str() ) ;
+                mhc._midPlateCntH = atof( tokens[4].c_str() ) ;
+                mhc._midPlateCntV = atof( tokens[5].c_str() ) ;
+                mhc._bigPlateCntH = atof( tokens[6].c_str() ) ;
+                mhc._bigPlateCntV = atof( tokens[7].c_str() ) ;
+                mhc._hard = atof( tokens[8].c_str() ) ;
+                _mapMoveConfigDatas[mhc._id] = mhc ;
+            }
+        }
+    }
+    return true ;
+}
+
+bool FillGlobalConfig::initGridBoxDatas(string filename)
+{
+    _mapGridBoxDatas.clear() ;
+    ifstream ifs( filename.c_str() ) ;
+    if( ifs.good()==false ) return false ;
+    string line ;
+    //跳过第一行
+    std::getline(ifs, line) ;
+    vector<string> tokens ;
+    while( std::getline(ifs, line) ) {
+        if( line.length() > 2 ) {
+            tokens.clear() ;
+            boost::split(tokens, line, boost::is_any_of(","));
+            if( tokens.size()>=11 ) {
+                GridBoxCell gbc ;
+                gbc._name = tokens[0] ;
+                gbc._move = atof( tokens[1].c_str() ) ;
+                gbc._bigIndex = atof( tokens[2].c_str() ) ;
+                gbc._mid1Index = atof( tokens[3].c_str() ) ;
+                gbc._mid2Index = atof( tokens[4].c_str() ) ;
+                gbc._bigllx = atof( tokens[5].c_str() ) ;
+                gbc._biglly = atof( tokens[6].c_str() ) ;
+                gbc._mid1llx = atof( tokens[7].c_str() ) ;
+                gbc._mid1lly = atof( tokens[8].c_str() ) ;
+                gbc._mid2llx = atof( tokens[9].c_str() ) ;
+                gbc._mid2lly = atof( tokens[10].c_str() ) ;
+                gbc._bigFilled = false;
+                gbc._mid1Filled = false;
+                gbc._mid2Filled = false ;
+                
+                _mapGridBoxDatas[gbc._name].push_back(gbc);
+            }
+        }
+    }
+    return true ;
+}
+
+
+vector<FillGlobalConfig::MoveConfig*> FillGlobalConfig::getSecondRoundMoveConfigDatas(int equvSmCnt)
+{
+    vector<FillGlobalConfig::MoveConfig*> res ;
+    FillGlobalConfig* fgc = FillGlobalConfig::getInstance() ;
+    if( 96 <= equvSmCnt && equvSmCnt < 144 ) {
+        vector<MoveConfig*> tarr = getMoveConfigDatasByTypeAndNLayers("A", 2) ;
+        for(auto m:tarr) res.push_back(m) ;
+    }else if( 144<= equvSmCnt && equvSmCnt < 192 ) {
+        vector<MoveConfig*> tarr = getMoveConfigDatasByTypeAndNLayers("A", 2) ;
+        for(auto m:tarr) res.push_back(m) ;
+        vector<MoveConfig*> tarr1 = getMoveConfigDatasByTypeAndNLayers("A", 3) ;
+        for(auto m:tarr1) res.push_back(m) ;
+        vector<MoveConfig*> tarr2 = getMoveConfigDatasByTypeAndNLayers("B", 2) ;
+        for(auto m:tarr2) res.push_back(m) ;
+    }else if( 192<=equvSmCnt && equvSmCnt < 216 ) {
+        vector<MoveConfig*> tarr = getMoveConfigDatasByTypeAndNLayers("A", 2) ;
+        for(auto m:tarr) res.push_back(m) ;
+        vector<MoveConfig*> tarr1 = getMoveConfigDatasByTypeAndNLayers("A", 3) ;
+        for(auto m:tarr1) res.push_back(m) ;
+        vector<MoveConfig*> tarr2 = getMoveConfigDatasByTypeAndNLayers("B", 2) ;
+        for(auto m:tarr2) res.push_back(m) ;
+        vector<MoveConfig*> tarr3 = getMoveConfigDatasByTypeAndNLayers("C", 2) ;
+        for(auto m:tarr3) res.push_back(m) ;
+    }else if( 216<= equvSmCnt && equvSmCnt < 288 ) {
+        vector<MoveConfig*> tarr = getMoveConfigDatasByTypeAndNLayers("A", 2) ;
+        for(auto m:tarr) res.push_back(m) ;
+        vector<MoveConfig*> tarr1 = getMoveConfigDatasByTypeAndNLayers("A", 3) ;
+        for(auto m:tarr1) res.push_back(m) ;
+        vector<MoveConfig*> tarr2 = getMoveConfigDatasByTypeAndNLayers("B", 2) ;
+        for(auto m:tarr2) res.push_back(m) ;
+        vector<MoveConfig*> tarr3 = getMoveConfigDatasByTypeAndNLayers("B", 3) ;
+        for(auto m:tarr3) res.push_back(m) ;
+        vector<MoveConfig*> tarr4 = getMoveConfigDatasByTypeAndNLayers("C", 2) ;
+        for(auto m:tarr4) res.push_back(m) ;
+    }else if( 288 <= equvSmCnt ) {
+        vector<MoveConfig*> tarr = getMoveConfigDatasByTypeAndNLayers("A", 2) ;
+        for(auto m:tarr) res.push_back(m) ;
+        vector<MoveConfig*> tarr1 = getMoveConfigDatasByTypeAndNLayers("A", 3) ;
+        for(auto m:tarr1) res.push_back(m) ;
+        vector<MoveConfig*> tarr2 = getMoveConfigDatasByTypeAndNLayers("B", 2) ;
+        for(auto m:tarr2) res.push_back(m) ;
+        vector<MoveConfig*> tarr3 = getMoveConfigDatasByTypeAndNLayers("B", 3) ;
+        for(auto m:tarr3) res.push_back(m) ;
+        vector<MoveConfig*> tarr4 = getMoveConfigDatasByTypeAndNLayers("C", 2) ;
+        for(auto m:tarr4) res.push_back(m) ;
+        vector<MoveConfig*> tarr5 = getMoveConfigDatasByTypeAndNLayers("C", 3) ;
+        for(auto m:tarr5) res.push_back(m) ;
+    }
+    
+    return res ;
+}
+
+vector<FillGlobalConfig::MoveConfig*> FillGlobalConfig::getFirstRoundMoveConfigDatas(int equvSmCnt)
+{
+    vector<FillGlobalConfig::MoveConfig*> res ;
+    FillGlobalConfig* fgc = FillGlobalConfig::getInstance() ;
+    if( equvSmCnt < 48 ) {
+        return res ;//empty
+    }else if( 48<=equvSmCnt && equvSmCnt<96 ) {
+        res = getMoveConfigDatasByTypeAndNLayers("A", 1) ;
+    }else if( 96<=equvSmCnt && equvSmCnt<144 ) {
+        res = getMoveConfigDatasByTypeAndNLayers("A", 2) ;
+    }else if( 144<= equvSmCnt && equvSmCnt < 192 ) {
+        vector<FillGlobalConfig::MoveConfig*> tarr1 = getMoveConfigDatasByTypeAndNLayers("A", 2) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr2 = getMoveConfigDatasByTypeAndNLayers("A", 3) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr3 = getMoveConfigDatasByTypeAndNLayers("B", 2) ;
+        for(auto mc:tarr1) res.push_back(mc) ;
+        for(auto mc:tarr2) res.push_back(mc) ;
+        for(auto mc:tarr3) res.push_back(mc) ;
+    }else if( 192<=equvSmCnt && equvSmCnt<216 ) {
+        vector<FillGlobalConfig::MoveConfig*> tarr1 = getMoveConfigDatasByTypeAndNLayers("A", 2) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr2 = getMoveConfigDatasByTypeAndNLayers("A", 3) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr3 = getMoveConfigDatasByTypeAndNLayers("B", 2) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr4 = getMoveConfigDatasByTypeAndNLayers("C", 2) ;
+        for(auto mc:tarr1) res.push_back(mc) ;
+        for(auto mc:tarr2) res.push_back(mc) ;
+        for(auto mc:tarr3) res.push_back(mc) ;
+        for(auto mc:tarr4) res.push_back(mc) ;
+    }else if( 216<=equvSmCnt && equvSmCnt<288 ) {
+        vector<FillGlobalConfig::MoveConfig*> tarr1 = getMoveConfigDatasByTypeAndNLayers("A", 2) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr2 = getMoveConfigDatasByTypeAndNLayers("A", 3) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr3 = getMoveConfigDatasByTypeAndNLayers("B", 2) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr4 = getMoveConfigDatasByTypeAndNLayers("C", 2) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr5 = getMoveConfigDatasByTypeAndNLayers("B", 3) ;
+        for(auto mc:tarr1) res.push_back(mc) ;
+        for(auto mc:tarr2) res.push_back(mc) ;
+        for(auto mc:tarr3) res.push_back(mc) ;
+        for(auto mc:tarr4) res.push_back(mc) ;
+        for(auto mc:tarr5) res.push_back(mc) ;
+    }else if( 288 <= equvSmCnt ) {
+        vector<FillGlobalConfig::MoveConfig*> tarr1 = getMoveConfigDatasByTypeAndNLayers("A", 2) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr2 = getMoveConfigDatasByTypeAndNLayers("A", 3) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr3 = getMoveConfigDatasByTypeAndNLayers("B", 2) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr4 = getMoveConfigDatasByTypeAndNLayers("C", 2) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr5 = getMoveConfigDatasByTypeAndNLayers("B", 3) ;
+        vector<FillGlobalConfig::MoveConfig*> tarr6 = getMoveConfigDatasByTypeAndNLayers("C", 3) ;
+        for(auto mc:tarr1) res.push_back(mc) ;
+        for(auto mc:tarr2) res.push_back(mc) ;
+        for(auto mc:tarr3) res.push_back(mc) ;
+        for(auto mc:tarr4) res.push_back(mc) ;
+        for(auto mc:tarr5) res.push_back(mc) ;
+        for(auto mc:tarr6) res.push_back(mc) ;
+    }
+    return res ;
+}
+
+
+vector<FillGlobalConfig::MoveConfig*> FillGlobalConfig::getMoveConfigDatasByTypeAndNLayers(string type,int nlayer)
+{
+    vector<FillGlobalConfig::MoveConfig*> res ;
+    FillGlobalConfig* fgc = FillGlobalConfig::getInstance() ;
+    int cnt = fgc->getMoveConfigCount() ;
+    for(int i = 0 ; i<cnt;++ i ) {
+        MoveConfig* mhc = fgc->getMoveConfigDataByIndex(i) ;
+        if( mhc->_type.find(type) != string::npos ) {
+            if(mhc->_nLayer == nlayer ) {
+                res.push_back( mhc ) ;
+            }
+        }
+    }
+    return res ;
+}
+
+
+vector<FillGlobalConfig::MultiMoveConfig> FillGlobalConfig::getAllMoveConfigByJewels(LevelData& levelData)
+{
+    vector<FillGlobalConfig::MultiMoveConfig> res ;
+    
+    int equvSmJewelCnt = 0 ;
+    for(int ij = 0 ; ij < levelData._jewelIds.size() ; ++ ij ) {
+        int jid = levelData._jewelIds[ij] ;
+        int cnt = levelData._cnts[ij] ;
+        FillGlobalConfig::JewelItem* jewel = this->getJewelItemById(jid);
+        if( jewel->_size == FILLGLOBALCONFIG_JEWELSIZE_LG ) {
+            cnt=cnt*4 ;
+        }else if(jewel->_size == FILLGLOBALCONFIG_JEWELSIZE_MD ) {
+            cnt=cnt*2 ;
+        }
+        equvSmJewelCnt += cnt ;
+    }
+    if( equvSmJewelCnt == 0  ) return res ;//empty.
+    
+    //第一个可移动行
+    vector<int> residueSmJewCnts ;//第一行剩余宝石
+    vector<FillGlobalConfig::MoveConfig*> firstMoveConfigs = getFirstRoundMoveConfigDatas(equvSmJewelCnt) ;
+    
+    int num1 = firstMoveConfigs.size() ;//第一个移动行的可能方案
+    for(int i1 = 0 ; i1 < num1 ; ++ i1 ) {
+        FillGlobalConfig::MoveConfig* mc = firstMoveConfigs[i1] ;
+        int res1 = equvSmJewelCnt - mc->_lowestEquvSmlJewelCnt ;
+        vector<FillGlobalConfig::MoveConfig*> secondConfigs = getSecondRoundMoveConfigDatas(res1) ;
+        bool secondAtLeasetHaveOneSolution = false ;
+        if( secondConfigs.size()>0 ) {
+            //存在第二移动行的情况
+            for(int i2 = 0 ; i2 < secondConfigs.size(); ++ i2 ) {
+                //第二行组合必须是 BA , CB , CA 这个顺序,也就是说第二行的字母ASCII值要小于等第一行的字母值
+                if( secondConfigs[i2]->_type[0] <= mc->_type[0] ) {
+                    FillGlobalConfig::MultiMoveConfig mmc ;
+                    mmc._first = mc ;
+                    mmc._second = secondConfigs[i2] ;
+                    res.push_back(mmc) ;
+                    secondAtLeasetHaveOneSolution = true ;
+                }
+            }
+        }
+        if( secondAtLeasetHaveOneSolution==false )
+        {
+            //无二移动行的情况
+            FillGlobalConfig::MultiMoveConfig mmc ;
+            mmc._first = mc ;
+            mmc._second = nullptr ;
+            res.push_back(mmc) ;
+        }
+    }
+    return res ;
+}
+
+FillGlobalConfig::PlateItem* FillGlobalConfig::getPlateItemBySzNDirection(int plateSzId,int heng)
+{
+    if( _mapPlateIdArray.find(plateSzId) != _mapPlateIdArray.end() ) {
+        vector<int>& idarr = _mapPlateIdArray[plateSzId] ;
+        for(int ii = 0 ; ii < idarr.size();++ii ) {
+            PlateItem* pi = this->getPlateItemById( idarr[ii] ) ;
+            if( pi!=nullptr ) {
+                if( pi->_heng == heng ) {
+                    return pi ;
+                }
+            }
+        }
+        return nullptr ;
+    }else{
+        return nullptr;
+    }
+}
+
+void FillGlobalConfig::clearGridBoxFilledStatus()
+{
+    for(auto it = _mapGridBoxDatas.begin(); it!=_mapGridBoxDatas.end();++it ) {
+        vector<GridBoxCell>& arr = it->second ;
+        for(auto itg = arr.begin(); itg != arr.end(); ++ itg ) {
+            itg->_bigFilled = false ;
+            itg->_mid1Filled = false;
+            itg->_mid2Filled = false ;
+        }
+    }
+}
+FillGlobalConfig::GridBoxCell* FillGlobalConfig::getLowestUnfilledGridBox(string name,const int moveid,const int iPlateSiz,ContourData::Point& retPositionLL)
+{
+    if( name.compare("")==0 ) name = "NN" ;
+    int index = 9999 ;
+    GridBoxCell* res = nullptr ;
+    
+    if( _mapGridBoxDatas.find(name) == _mapGridBoxDatas.end() ) {
+        //如果没找到组合,那么把字母反过来组合
+        string name2 ;
+        for(int ic = 0 ;ic<name.length();++ic ) {
+            name2 += name[name.length()-1-ic] ;
+        }
+        name = name2 ;
+    }
+    
+    if( _mapGridBoxDatas.find(name) != _mapGridBoxDatas.end() ) {
+        vector<GridBoxCell>& arr = _mapGridBoxDatas[name] ;
+        for(int ig = 0 ; ig < arr.size(); ++ ig ) {
+            GridBoxCell& gbc = arr[ig] ;
+            if( gbc._move == moveid ) {
+                if( iPlateSiz == FILLGLOBALCONFIG_PLATESIZE_LG ) {
+                    if( gbc._bigIndex!=-1 && (gbc._bigFilled==false && gbc._mid1Filled == false&& gbc._mid2Filled == false ) && gbc._bigIndex < index ) {
+                        index = gbc._bigIndex ;
+                        res = &gbc ;
+                        retPositionLL.x = gbc._bigllx ;
+                        retPositionLL.y = gbc._biglly ;
+                    }
+                }else { // FILLGLOBALCONFIG_PLATESIZE_MD
+                    if( gbc._mid1Index != -1 &&(gbc._bigFilled==false && gbc._mid1Filled == false ) && gbc._mid1Index < index ) {
+                        index = gbc._mid1Index ;
+                        res = &gbc ;
+                        retPositionLL.x = gbc._mid1llx ;
+                        retPositionLL.y = gbc._mid1lly ;
+                    }
+                    if( gbc._mid2Index != -1 &&(gbc._bigFilled==false && gbc._mid2Filled == false )  && gbc._mid2Index < index ) {
+                        index = gbc._mid2Index ;
+                        res = &gbc ;
+                        retPositionLL.x = gbc._mid2llx ;
+                        retPositionLL.y = gbc._mid2lly ;
+                    }
+                }
+            }
+        }
+    }
+    return res ;
+}
+void FillGlobalConfig::setFilled( GridBoxCell* gbc , const int iPlateSiz )
+{
+    if( iPlateSiz == FILLGLOBALCONFIG_PLATESIZE_LG ) {
+        gbc->_bigFilled = true ;
+    }else {
+        if( gbc->_mid1Index != -1 && gbc->_mid1Filled == false ) {
+            gbc->_mid1Filled = true ;
+        }else if( gbc->_mid2Index!=-1 && gbc->_mid2Filled==false ) {
+            gbc->_mid2Filled = true ;
+        }
+    }
+}

+ 59 - 3
auto_fill_jewel_v3/FillGlobalConfig.hpp

@@ -26,7 +26,7 @@ using std::vector;
 #define FILLGLOBALCONFIG_DIFFCULTY_HARD1 1
 #define FILLGLOBALCONFIG_DIFFCULTY_HARD2 2
 
-#define FILLGLOBALCONFIG_PLATESIZE_SM 1
+//#define FILLGLOBALCONFIG_PLATESIZE_SM 1
 #define FILLGLOBALCONFIG_PLATESIZE_MD 2
 #define FILLGLOBALCONFIG_PLATESIZE_LG 3
 
@@ -42,6 +42,8 @@ struct FillGlobalConfig {
     bool initJewelItems( string filename ) ;
     bool initLevelDatas( string filename ) ;
     bool initPlateItems( string filename ) ;
+    bool initMoveConfigDatas( string filename ) ;
+    bool initGridBoxDatas(string filename) ;
     
     //静态全局单例
     static FillGlobalConfig* getInstance() ;
@@ -94,11 +96,63 @@ struct FillGlobalConfig {
         float _blx,_bly,_trx,_try ;// bl for bottom-left, tr for top-right.
         double    _bigJewCap ;//大宝石容量
         float _bbwid,_bbhei ;//精灵图片尺寸
-        string _etc ;
+        int  _heng ; // 1-横放。0-竖放。
     } ;
     PlateItem* getPlateItemById(int pid){ return  _mapPlateItems.find(pid)!=_mapPlateItems.end()?(&_mapPlateItems[pid]):nullptr; }
     int getPlateItemCount( int plateSzId ) { return (_mapPlateIdArray.find(plateSzId)!=_mapPlateIdArray.end())?(_mapPlateIdArray[plateSzId].size()):0; }
-    PlateItem* getPlateItemBySzNIndex(int plateSzId,int index){ return (_mapPlateIdArray.find(plateSzId)!=_mapPlateIdArray.end())?( getPlateItemById(_mapPlateIdArray[plateSzId][index])):nullptr; }
+    PlateItem* getPlateItemBySzNDirection(int plateSzId,int heng) ;
+    
+    //移动盘子 配置参数
+    struct MoveConfig {
+        MoveConfig():_id(-1),_lowestEquvSmlJewelCnt(0),_nLayer(0),_midPlateCntH(0),_midPlateCntV(0),_bigPlateCntH(0),_bigPlateCntV(0),_hard(0) {}
+        int    _id ;//1,2,3,4,5... 序号
+        string _type ; //A,B,C 类型
+        int    _lowestEquvSmlJewelCnt ;//最少等效小宝石数量
+        int    _nLayer ;       // 层数
+        int    _midPlateCntH;   // 水平中盘子数量
+        int    _midPlateCntV;   // 垂直中盘子数量
+        int    _bigPlateCntH;   // 水平大盘子数量
+        int    _bigPlateCntV;   // 垂直大盘子数量
+        int    _hard ;//3,4,5,6,7.... 难度系数
+    } ;
+    //目前需求可移动盘子只有两行
+    struct MultiMoveConfig {
+        MultiMoveConfig():_first(nullptr),_second(nullptr){}
+        MoveConfig* _first ;
+        MoveConfig* _second ;
+    };
+    int getMoveConfigCount() { return _mapMoveConfigDatas.size() ; }
+    MoveConfig* getMoveConfigDataByIndex(int index) { auto it=_mapMoveConfigDatas.begin();std::advance(it, index);return &(it->second); }
+    
+    /// 计算第一个移动行可选移动方案 https://t7le908iko.feishu.cn/docx/Cn4tdr0S7o76Ffx4lrVcJtManBg   Section-6
+    /// @param equvSmCnt 全部等效小宝石数量
+    vector<MoveConfig*> getFirstRoundMoveConfigDatas(int equvSmCnt) ;
+    
+    /// 计算第二个移动行可选方案 https://t7le908iko.feishu.cn/docx/Cn4tdr0S7o76Ffx4lrVcJtManBg   Section-6
+    ///  @param equvSmCnt 是第一移动行剩下的等效小宝石数量
+    vector<MoveConfig*> getSecondRoundMoveConfigDatas(int equvSmCnt) ;
+    vector<MoveConfig*> getMoveConfigDatasByTypeAndNLayers(string type,int nlayer) ;
+    //计算给定宝石组合对应的全部 可移动盘子的可能方案
+    vector<MultiMoveConfig> getAllMoveConfigByJewels(LevelData& levelData) ;
+    
+    
+    // 大盘子中盘子的摆放位置信息
+    struct GridBoxCell {
+        //Cell():_move(0),_bigIndex(0),_mid1Index(0),_mid2Index(0),_bigFilled(false),_mid1Filled(false),_mid2Filled(false){}
+        string _name ;
+        int _move ;//0-none, 1-1stmove, 2-2ndmove.
+        int _bigIndex ; //-1 for none
+        int _mid1Index ;//-1 for none
+        int _mid2Index ;//-1 for none
+        bool _bigFilled ;
+        bool _mid1Filled ;
+        bool _mid2Filled ;
+        float _bigllx,_biglly,_mid1llx,_mid1lly,_mid2llx,_mid2lly ;
+    } ;
+    void clearGridBoxFilledStatus() ;
+    GridBoxCell* getLowestUnfilledGridBox(string name,const int moveid,const int iPlateSize,ContourData::Point& retPositionLL) ;
+    void setFilled( GridBoxCell* gbc , const int iPlateSize ) ;
+    
     
 private:
     static FillGlobalConfig* _s_instance ;
@@ -107,6 +161,8 @@ private:
     unordered_map<int, vector<LevelData> >    _mapLevelDatas ;// key 为主关卡编号, vector<LevelData> 为子关卡数组
     unordered_map<int, PlateItem>          _mapPlateItems ;
     unordered_map<int, vector<int> >       _mapPlateIdArray;  // key为小、中、大的整数编码值, value为该类型下面的盘子ID数组
+    unordered_map<int, MoveConfig>     _mapMoveConfigDatas ;
+    unordered_map<string, vector<GridBoxCell> > _mapGridBoxDatas ;
     
 } ;
 

+ 12 - 0
auto_fill_jewel_v3/FillResult.hpp

@@ -9,7 +9,11 @@
 #define FillResult_hpp
 
 #include <stdio.h>
+#include <vector>
 
+using std::vector;
+
+//一个宝石的填充信息
 struct FillResult {
     float _x,_y ;// 宝石位置相对盘子左下角(原点)的坐标,盘子内部坐标,注意盘子有效填充区域和盘子精灵尺寸的区别。
                  // 这里盘子的坐标原点指的是盘子精灵图片尺寸的左下角坐标。
@@ -17,7 +21,15 @@ struct FillResult {
     int _jewelTypeId ;//宝石类型ID
     float _width; //宝石外接宽度
     float _height;//宝石外接高度
+} ;
+
+//一个盘子的填充信息,包括它下面的层和宝石
+struct PlateFillResult {
+    PlateFillResult():_plateTypeId(-1),_moveId(0){}
     
+    vector< vector<FillResult> > _layersFillResults ;//层与层的宝石填充信息
+    int _plateTypeId ;//盘子类型ID
+    int _moveId ;//0-不可移动。 1-第一移动行。   2-第二移动行.
 } ;
 
 

+ 93 - 94
auto_fill_jewel_v3/GridPositionTool.cpp

@@ -9,6 +9,10 @@
 #include <utility>
 #include <iostream>
 using std::pair ;
+#include <cassert>
+
+using std::cout;
+using std::endl;
 
 #define GRIDPOSITIONTOOL_BIG_GRID_NCOLS 3
 #define GRIDPOSITIONTOOL_BIG_GRID_NROWS 3
@@ -22,113 +26,108 @@ using std::pair ;
 #define GRIDPOSITIONTOOL_SM_H 135
 
 
-
-void GridPositionTool::solve(const bool movable,const vector<int>& plateTypeIdArr , vector<vector<int>>& resultPositions )
+void GridPositionTool::solve(const bool movable,
+                                      const string firstMoveTypeCode,
+                                      const string secondMoveTypeCode,
+                                      const vector<int>& plateTypeIdArr ,
+                                      const vector<int>& moveIdArr ,
+                                      vector<vector<int>>& resultPositions )
 {
-    //定义格网左上角为0,0
-    //pair坐标是先列后行, pair<col,row>
-    vector<vector<int>> bigIndices = {
-        {6,4,7},
-        {1,2,3},
-        {8,5,9}
-    } ;
-    
-    vector<vector<int>> midIndices = {
-        {11, 7,13},
-        {12, 8,14},
-        { 1, 3, 5},
-        { 2, 4, 6},
-        {15, 9,17},
-        {16,10,18}
-    } ;
-    
-    vector<vector<int>> smlIndices = {
-        { 21,22,13,14,25,26},
-        { 23,24,15,16,27,28},
-        {  1, 2, 5, 6, 9,10},
-        {  3, 4, 7, 8,11,12},
-        { 29,30,17,18,33,34},
-        { 31,32,19,20,35,36}
-    } ;
+    resultPositions.resize(plateTypeIdArr.size()) ;
+    string moveTypeCode = "" ;//no movement.
+    if( movable ==true ) {
+        moveTypeCode = firstMoveTypeCode + secondMoveTypeCode ;// A B C  BA  CB  CA
+    }
+    auto fgc = FillGlobalConfig::getInstance() ;
+    //先安排第一行移动大盘子
+    for(int iplate = 0 ; iplate < plateTypeIdArr.size();++ iplate ) {
+        int plateTypeId = plateTypeIdArr[iplate] ;
+        FillGlobalConfig::PlateItem* platePtr = fgc->getPlateItemById(plateTypeId) ;
+        int moveid = moveIdArr[iplate] ;// 0-nomove, 1-firstmove , 2-secondmove
+        int plateSize = platePtr->_size ;
+        if( moveid==1 && plateSize == FILLGLOBALCONFIG_PLATESIZE_LG) {
+            ContourData::Point lowerLeftPosition ;
+            FillGlobalConfig::GridBoxCell* gbcPtr = fgc->getLowestUnfilledGridBox(moveTypeCode, moveid, plateSize, lowerLeftPosition);
+            assert( gbcPtr != nullptr ) ;
+            fgc->setFilled(gbcPtr, plateSize) ;
+            resultPositions[iplate] = {(int)lowerLeftPosition.x, (int)lowerLeftPosition.y}   ;
+        }
+    }
     
-    if( movable ) {
-        vector<vector<int>> bigIndices2 = {
-            {7,5, 8,-1},
-            {2,3, 4, 1},
-            {9,6,10,-1}
-        } ;
-        
-        vector<vector<int>> midIndices2 = {
-            {13, 9,15,-1},
-            {14,10,16,-1},
-            { 3, 5, 7, 1},
-            { 4, 6, 8, 2},
-            {17,11,19,-1},
-            {18,12,20,-1}
-        } ;
-        
-        vector<vector<int>> smlIndices2 = {
-            {25,26,17,18,29,30,-1,-1},
-            {27,28,19,20,31,32,-1,-1},
-            { 5, 6, 9,10,13,14, 1, 2},
-            { 7, 8,11,12,15,16, 3, 4},
-            {33,34,21,22,37,38,-1,-1},
-            {35,36,23,24,39,40,-1,-1}
-        } ;
-        bigIndices = bigIndices2 ;
-        midIndices = midIndices2;
-        smlIndices = smlIndices2;
+    //安排第一行移动中盘子
+    for(int iplate = 0 ; iplate < plateTypeIdArr.size();++ iplate ) {
+        int plateTypeId = plateTypeIdArr[iplate] ;
+        FillGlobalConfig::PlateItem* platePtr = fgc->getPlateItemById(plateTypeId) ;
+        int moveid = moveIdArr[iplate] ;// 0-nomove, 1-firstmove , 2-secondmove
+        int plateSize = platePtr->_size ;
+        if( moveid==1 && plateSize == FILLGLOBALCONFIG_PLATESIZE_MD) {
+            ContourData::Point lowerLeftPosition ;
+            FillGlobalConfig::GridBoxCell* gbcPtr = fgc->getLowestUnfilledGridBox(moveTypeCode, moveid, plateSize, lowerLeftPosition);
+            assert( gbcPtr != nullptr ) ;
+            fgc->setFilled(gbcPtr, plateSize) ;
+            resultPositions[iplate] = {(int)lowerLeftPosition.x, (int)lowerLeftPosition.y}   ;
+        }
     }
     
-    int currindex = 1 ;
-    auto fgc = FillGlobalConfig::getInstance() ;
-    //先安排大盘子
-    for(int ijew = 0 ; ijew < plateTypeIdArr.size(); ++ ijew ) {
-        int typeId = plateTypeIdArr[ijew] ;
-        FillGlobalConfig::PlateItem* plate = fgc->getPlateItemById(typeId) ;
-        if( plate->_size == FILLGLOBALCONFIG_PLATESIZE_LG ) {
-            int col=0;
-            int row=0;
-            if( getIndexPositionInGrid(bigIndices, currindex, col, row) ) {
-                vector<int> posi = computePositionByGridColRow(FILLGLOBALCONFIG_PLATESIZE_LG, col, row) ;
-                resultPositions.push_back(posi) ;
-                currindex++ ;
-            }
+    //先安排第二行移动大盘子
+    for(int iplate = 0 ; iplate < plateTypeIdArr.size();++ iplate ) {
+        int plateTypeId = plateTypeIdArr[iplate] ;
+        FillGlobalConfig::PlateItem* platePtr = fgc->getPlateItemById(plateTypeId) ;
+        int moveid = moveIdArr[iplate] ;// 0-nomove, 1-firstmove , 2-secondmove
+        int plateSize = platePtr->_size ;
+        if( moveid==2 && plateSize == FILLGLOBALCONFIG_PLATESIZE_LG) {
+            ContourData::Point lowerLeftPosition ;
+            FillGlobalConfig::GridBoxCell* gbcPtr = fgc->getLowestUnfilledGridBox(moveTypeCode, moveid, plateSize, lowerLeftPosition);
+            assert( gbcPtr != nullptr ) ;
+            fgc->setFilled(gbcPtr, plateSize) ;
+            resultPositions[iplate] = {(int)lowerLeftPosition.x, (int)lowerLeftPosition.y}   ;
         }
     }
     
-    //安排中盘子
-    currindex = currindex*2 - 1 ;
-    for(int ijew = 0 ; ijew < plateTypeIdArr.size(); ++ ijew ) {
-        int typeId = plateTypeIdArr[ijew] ;
-        FillGlobalConfig::PlateItem* plate = fgc->getPlateItemById(typeId) ;
-        if( plate->_size == FILLGLOBALCONFIG_PLATESIZE_MD ) {
-            int col=0;
-            int row=0;
-            if( getIndexPositionInGrid(midIndices, currindex, col, row) ) {
-                vector<int> posi = computePositionByGridColRow(FILLGLOBALCONFIG_PLATESIZE_MD, col, row) ;
-                resultPositions.push_back(posi) ;
-                currindex++ ;
-            }
+    //安排第二行移动中盘子
+    for(int iplate = 0 ; iplate < plateTypeIdArr.size();++ iplate ) {
+        int plateTypeId = plateTypeIdArr[iplate] ;
+        FillGlobalConfig::PlateItem* platePtr = fgc->getPlateItemById(plateTypeId) ;
+        int moveid = moveIdArr[iplate] ;// 0-nomove, 1-firstmove , 2-secondmove
+        int plateSize = platePtr->_size ;
+        if( moveid==2 && plateSize == FILLGLOBALCONFIG_PLATESIZE_MD) {
+            ContourData::Point lowerLeftPosition ;
+            FillGlobalConfig::GridBoxCell* gbcPtr = fgc->getLowestUnfilledGridBox(moveTypeCode, moveid, plateSize, lowerLeftPosition);
+            assert( gbcPtr != nullptr ) ;
+            fgc->setFilled(gbcPtr, plateSize) ;
+            resultPositions[iplate] = {(int)lowerLeftPosition.x, (int)lowerLeftPosition.y}   ;
         }
     }
     
-    //安排小盘子
-    currindex = currindex*2 - 1 ;
-    for(int ijew = 0 ; ijew < plateTypeIdArr.size(); ++ ijew ) {
-        int typeId = plateTypeIdArr[ijew] ;
-        FillGlobalConfig::PlateItem* plate = fgc->getPlateItemById(typeId) ;
-        if( plate->_size == FILLGLOBALCONFIG_PLATESIZE_SM ) {
-            int col=0;
-            int row=0;
-            if( getIndexPositionInGrid(smlIndices, currindex, col, row) ) {
-                vector<int> posi = computePositionByGridColRow(FILLGLOBALCONFIG_PLATESIZE_SM, col, row) ;
-                resultPositions.push_back(posi) ;
-                currindex++ ;
-            }
+    //安排不移动大盘子
+    for(int iplate = 0 ; iplate < plateTypeIdArr.size();++ iplate ) {
+        int plateTypeId = plateTypeIdArr[iplate] ;
+        FillGlobalConfig::PlateItem* platePtr = fgc->getPlateItemById(plateTypeId) ;
+        int moveid = moveIdArr[iplate] ;// 0-nomove, 1-firstmove , 2-secondmove
+        int plateSize = platePtr->_size ;
+        if( moveid==0 && plateSize == FILLGLOBALCONFIG_PLATESIZE_LG) {
+            ContourData::Point lowerLeftPosition ;
+            FillGlobalConfig::GridBoxCell* gbcPtr = fgc->getLowestUnfilledGridBox(moveTypeCode, moveid, plateSize, lowerLeftPosition);
+            assert( gbcPtr != nullptr ) ;
+            fgc->setFilled(gbcPtr, plateSize) ;
+            resultPositions[iplate] = {(int)lowerLeftPosition.x, (int)lowerLeftPosition.y}   ;
         }
     }
     
+    //安排不移动中盘子
+    for(int iplate = 0 ; iplate < plateTypeIdArr.size();++ iplate ) {
+        int plateTypeId = plateTypeIdArr[iplate] ;
+        FillGlobalConfig::PlateItem* platePtr = fgc->getPlateItemById(plateTypeId) ;
+        int moveid = moveIdArr[iplate] ;// 0-nomove, 1-firstmove , 2-secondmove
+        int plateSize = platePtr->_size ;
+        if( moveid==0 && plateSize == FILLGLOBALCONFIG_PLATESIZE_MD) {
+            ContourData::Point lowerLeftPosition ;
+            FillGlobalConfig::GridBoxCell* gbcPtr = fgc->getLowestUnfilledGridBox(moveTypeCode, moveid, plateSize, lowerLeftPosition);
+            assert( gbcPtr != nullptr ) ;
+            fgc->setFilled(gbcPtr, plateSize) ;
+            resultPositions[iplate] = {(int)lowerLeftPosition.x, (int)lowerLeftPosition.y}   ;
+        }
+    }
 }
 
 

+ 10 - 1
auto_fill_jewel_v3/GridPositionTool.hpp

@@ -18,11 +18,20 @@ struct GridPositionTool {
     /// 大盘子 210 210 210。      3x3=9个网格
     /// 中盘子 210 210 210。       3x6=18 个网格
     /// 小盘子 110 110 110 110 110 110  6x6=36 个网格
+
     
     /// 算法 https://t7le908iko.feishu.cn/docx/Cn4tdr0S7o76Ffx4lrVcJtManBg 第6节的顺序进行填充
+    /// @param movable 是否移动
+    /// @param firstMoveTypeCode 第一行移动代码,A、B、C,如果没有移动使用空字符串
+    /// @param secondMoveTypeCode 第二行移动代码,A、B、C,如果没有移动使用空字符串
     /// @param plateTypeIdArr   盘子类型ID数组,通过盘子类型ID可以获取盘子大中小尺寸信息
     /// @param resultPositions 是输出结果,对应输入矩形顺序,每个数据是一个box中心在整个可视化区域(640,720)的坐标。可视化区域坐标原点在左下角。
-    void solve(const bool movable, const vector<int>& plateTypeIdArr , vector<vector<int>>& resultPositions ) ;
+    void solve(const bool movable,
+               const string firstMoveTypeCode,
+               const string secondMoveTypeCode,
+               const vector<int>& plateTypeIdArr ,
+               const vector<int>& moveIdArr ,
+               vector<vector<int>>& resultPositions ) ;
     
 private:
     //给定顺序值index,从网格Grid中找到对应的网格列行坐标。列行坐标从0开始计。

+ 392 - 319
auto_fill_jewel_v3/LevelGenerate.cpp

@@ -13,12 +13,60 @@
 #include <tuple>
 #include "BoxPositionTool.hpp"
 #include "GridPositionTool.hpp"
+//#include "LevelThumbTool.hpp"
+#include "LevelOutputWriter.hpp"
+
+#define MOVEID_NO_MOVE 0
+#define MOVEID_1ST_LINE 1
+#define MOVEID_2ND_LINE 2
 
 using namespace std;
 
+void LevelGenerate::generateAll(FillGlobalConfig::LevelData levelData, string outname)
+{
+    cout<<"debug level "<<levelData._id<<" subid "<<levelData._subId<<endl;
+    auto fgc = FillGlobalConfig::getInstance() ;
+    fgc->clearGridBoxFilledStatus() ;//每次填充盘子前必须清空盘子占位情况
+    vector<PlateFillResult> resultPlateFRs ;
+    vector<ContourData::Point> resultPlateCenterPosiArr ;
+    generate(levelData, false, nullptr , resultPlateFRs, resultPlateCenterPosiArr) ;
+    //输出结果
+//    string pngName0 = outname + "_000000.png" ;
+//    LevelThumbTool ltt ;
+//    ltt.makeThumb3(pngName0, resultPlateFRs, resultPlateCenterPosiArr) ;
+    
+    string jsonName = outname + "_000000.json" ;
+    LevelOutputWriter out ;
+    out.writeLevelJson(resultPlateFRs, resultPlateCenterPosiArr, jsonName) ;
+    
+    //对于所有移动的配置
+    vector<FillGlobalConfig::MultiMoveConfig> mmcArr = fgc->getAllMoveConfigByJewels(levelData) ;
+    for(int im=0;im < mmcArr.size();++ im ) {
+        string moveName =  mmcArr[im]._first->_type ;
+        if( mmcArr[im]._second ) moveName += mmcArr[im]._second->_type ;
+        else moveName += "00" ;
+        cout<<"debug moveName "<<moveName<<endl;
+        fgc->clearGridBoxFilledStatus() ;//每次填充盘子前必须清空盘子占位情况
+        vector<PlateFillResult> resultPlateFRs1 ;
+        vector<ContourData::Point> resultPlateCenterPosiArr1 ;
+        generate(levelData, true, &mmcArr[im]  , resultPlateFRs1, resultPlateCenterPosiArr1) ;
+        //输出结果
+//        string pngName1 = outname + "_" + moveName;
+//        pngName1 += ".png" ;
+//        ltt.makeThumb3(pngName1, resultPlateFRs1, resultPlateCenterPosiArr1) ;
+        
+        string jsonName1 = outname + "_" + moveName + ".json" ;
+        LevelOutputWriter out1 ;
+        out1.writeLevelJson(resultPlateFRs1, resultPlateCenterPosiArr1, jsonName1) ;
+    }
+    
+    
+}
+
 bool LevelGenerate::generate( FillGlobalConfig::LevelData levelData,
                              const bool isMovable,
-                             vector<tuple<int,vector<vector<FillResult>>>>& resultPlateFillResults,
+                             const FillGlobalConfig::MultiMoveConfig* mulMoveConfig , //移动盘子配置,如果isMovable为false,这个参数忽略,可以传入nullptr
+                             vector<PlateFillResult>& resultPlateFillResults,
                              vector<ContourData::Point>& resultPlateCenterPointArr
                              )
 {
@@ -29,7 +77,7 @@ bool LevelGenerate::generate( FillGlobalConfig::LevelData levelData,
     uint64_t ms00 = std::chrono::duration_cast<chrono::milliseconds>(ms0).count() ;
     
     srand(_seed);
-    unordered_map<int,tuple<int,int> > jewIdSzCntStorageMap ;
+    unordered_map<int,tuple<int,int> > jewIdSzCntStorageMap ;// unordered_map<宝石ID,tuple<尺寸,库存数量> >
     FillGlobalConfig* fgc = FillGlobalConfig::getInstance() ;
     int bigJewelCnt = 0 ;
     int midJewelCnt = 0;
@@ -48,11 +96,14 @@ bool LevelGenerate::generate( FillGlobalConfig::LevelData levelData,
         }
     }
     //每个盘子类型小号宝石承载量
-    //按顺序分别为大横,大竖,小。这里不考虑迷你。
-    FillGlobalConfig::PlateItem* bigPlate = fgc->getPlateItemBySzNIndex(FILLGLOBALCONFIG_PLATESIZE_LG, 0) ;
-    FillGlobalConfig::PlateItem* midPlate = fgc->getPlateItemBySzNIndex(FILLGLOBALCONFIG_PLATESIZE_MD, 0) ;
-    FillGlobalConfig::PlateItem* smlPlate = fgc->getPlateItemBySzNIndex(FILLGLOBALCONFIG_PLATESIZE_SM, 0) ;
-    vector<FillGlobalConfig::PlateItem*> usedPlates = {bigPlate,midPlate,smlPlate} ;
+    //按顺序分别为大和中
+    FillGlobalConfig::PlateItem* bigPlateH = fgc->getPlateItemBySzNDirection(FILLGLOBALCONFIG_PLATESIZE_LG, 1) ;
+    FillGlobalConfig::PlateItem* midPlateH = fgc->getPlateItemBySzNDirection(FILLGLOBALCONFIG_PLATESIZE_MD, 1) ;
+    FillGlobalConfig::PlateItem* bigPlateV = fgc->getPlateItemBySzNDirection(FILLGLOBALCONFIG_PLATESIZE_LG, 0) ;
+    FillGlobalConfig::PlateItem* midPlateV = fgc->getPlateItemBySzNDirection(FILLGLOBALCONFIG_PLATESIZE_MD, 0) ;
+    
+    //FillGlobalConfig::PlateItem* smlPlate = fgc->getPlateItemBySzNIndex(FILLGLOBALCONFIG_PLATESIZE_SM, 0) ;
+    vector<FillGlobalConfig::PlateItem*> usedPlates = {bigPlateH,midPlateH,bigPlateV,midPlateV} ;//{bigPlate,midPlate,smlPlate} ;
     int maxSmlJewCapPerPlate = 0;
     int minSmlJewCapPerPlate = 99999 ;
     for(int ip=0;ip<usedPlates.size();++ip ) {
@@ -66,8 +117,8 @@ bool LevelGenerate::generate( FillGlobalConfig::LevelData levelData,
     //cout<<"smlJewelCnt "<<smlJewelCnt<<endl;
     //cout<<"effSmallJewelsCount "<<effSmallJewelsCount<<endl;
     
-    //构建符合难度条件的盘子和层数组合
-    vector<PlateIdAndLayerCnt> resPlateNLyrCnt = generatePlateTypeAndLayerCnts2(isMovable, levelData._difficulty,effSmallJewelsCount);
+    //构建符合难度条件的盘子和层数组合(包括可移动的盘子)
+    vector<PlateIdAndLayerCnt> resPlateNLyrCnt = generatePlateTypeAndLayerCnts2(isMovable, mulMoveConfig, levelData._difficulty,effSmallJewelsCount);
     if( resPlateNLyrCnt.size()==0 ) {
         //cout<<"failed at generatePlateTypeAndLayerCnts"<<endl;
         return false ;
@@ -83,7 +134,7 @@ bool LevelGenerate::generate( FillGlobalConfig::LevelData levelData,
     srand(filler._seed);
     
     //构建每个盘子每个层的填充结果容器
-    vector< tuple<int,int,vector<FillResult>> > plateIdLyrFillResultsArray ;// tuple<plateId, layerIndex, fillresults_array >
+    vector< tuple<int,int,vector<FillResult>,int> > plateIdLyrFillResultsMoveIdArray ;// tuple<plateId, layerIndex, fillresults_array ,moveId>
     
     
     //从顶到底部,逐层进行填充
@@ -92,8 +143,15 @@ bool LevelGenerate::generate( FillGlobalConfig::LevelData levelData,
         //cout<<"for layer "<<nlayer<<endl;
         
         //满足该层数的所有盘子ID
-        vector<int> plateIdArr ;
-        for(auto it = resPlateNLyrCnt.begin();it!=resPlateNLyrCnt.end();++it ) if(it->_layerCnt>=nlayer) plateIdArr.push_back(it->_plateId) ;
+        vector<int> plateIdArr ;//临时符合当前层次的盘子类型ID
+        vector<int> moveIdArr ;//临时保存对应盘子是否是可移动的,0-不可移动,1-第一移动行,2-第二移动行
+        for(auto it = resPlateNLyrCnt.begin();it!=resPlateNLyrCnt.end();++it )
+        {
+            if(it->_layerCnt>=nlayer) {
+                plateIdArr.push_back(it->_plateId) ;
+                moveIdArr.push_back( it->_moveId ) ;
+            }
+        }
         
         //该层总计需要填充多少小宝石
         int currLyrNeedEquvSmlCnt = 0 ;
@@ -112,10 +170,12 @@ bool LevelGenerate::generate( FillGlobalConfig::LevelData levelData,
         //cout<<"layer choose eqsmCnt "<<choseEqSmCnt<<endl;
         
         //逐个盘子填充
-        for(auto itpid = plateIdArr.begin();itpid!=plateIdArr.end();++itpid) {
-            vector<FillResult> fr = fillPlateOneLayer(filler, *itpid, chosenJewIdNCnt) ;
-            tuple<int,int,vector<FillResult>> plateLyrFillResult={*itpid,nlayer,fr} ;
-            plateIdLyrFillResultsArray.push_back(plateLyrFillResult);
+        for(int ipid = 0 ; ipid !=plateIdArr.size();++ipid) {
+            int plateId1 = plateIdArr[ipid] ;
+            int moveId1 = moveIdArr[ipid] ;
+            vector<FillResult> fr = fillPlateOneLayer(filler, plateId1 , chosenJewIdNCnt) ;
+            tuple<int,int,vector<FillResult>,int> plateLyrFillResult={plateId1 , nlayer , fr , moveId1 } ;
+            plateIdLyrFillResultsMoveIdArray.push_back(plateLyrFillResult);
         }
         
         //检查为该层挑选的宝石是不是全部填完了,如果没有填完还要还给库存,给下一轮填充用
@@ -135,13 +195,13 @@ bool LevelGenerate::generate( FillGlobalConfig::LevelData levelData,
         //剩了,添加一个小盘子和层,仍然剩了再继续加盘子加层
         unordered_map<int, int> residueJewIdAndCnts = findUnfilledInStorages(jewIdSzCntStorageMap) ;
         while( residueJewIdAndCnts.size()>0 ) {
-            //新建一个盘子
+            //新建一个盘子
             extraPlateCnt++;
             //cout<<"add extra plate "<< extraPlateCnt <<endl;
-            int plateId = smlPlate->_id ;
+            int plateId = midPlateH->_id ;
             for(int ilyr = 1 ; ilyr <= maxLyrCnt; ++ ilyr ) {
                 vector<FillResult> frs = fillPlateOneLayer(filler, plateId, residueJewIdAndCnts) ;
-                plateIdLyrFillResultsArray.push_back( {plateId,ilyr,frs} );
+                plateIdLyrFillResultsMoveIdArray.push_back( {plateId,ilyr,frs, MOVEID_NO_MOVE } );
                 //cout<<"add extra lyr "<<endl;
                 if( residueJewIdAndCnts.size()==0 ) break ;
             }
@@ -150,13 +210,11 @@ bool LevelGenerate::generate( FillGlobalConfig::LevelData levelData,
     }
     
     //整理数据
-    regroupPlateLyrFillResults(plateIdLyrFillResultsArray, resultPlateFillResults);
+    regroupPlateLyrFillResults(plateIdLyrFillResultsMoveIdArray, resultPlateFillResults);
     
     //摆放盘子
     {
-        vector<int> plateIdArr1;
-        for(auto itp = resultPlateFillResults.begin();itp!=resultPlateFillResults.end();++itp) plateIdArr1.push_back( std::get<0>(*itp) );
-        placePlates(isMovable, plateIdArr1, resultPlateCenterPointArr) ;
+        placePlates(isMovable,mulMoveConfig, resultPlateFillResults, resultPlateCenterPointArr) ;
     }
     
     
@@ -170,343 +228,217 @@ bool LevelGenerate::generate( FillGlobalConfig::LevelData levelData,
 [[deprecated]]
 vector<LevelGenerate::PlateIdAndLayerCnt> LevelGenerate::generatePlateTypeAndLayerCnts(const int difficulty,const int totEquvSmallJewelCnt)
 {
-    assert(difficulty>=0&&difficulty<=2) ;
-    vector<vector<LevelGenerate::PlateIdAndLayerCnt>> possibleResults ;
     vector<LevelGenerate::PlateIdAndLayerCnt> results ;
-    assert(totEquvSmallJewelCnt>0) ;
-    
-    FillGlobalConfig* fgc = FillGlobalConfig::getInstance() ;
-    FillGlobalConfig::PlateItem* bigPlate = fgc->getPlateItemBySzNIndex(FILLGLOBALCONFIG_PLATESIZE_LG, 0) ;
-    FillGlobalConfig::PlateItem* midPlate = fgc->getPlateItemBySzNIndex(FILLGLOBALCONFIG_PLATESIZE_MD, 0) ;
-    FillGlobalConfig::PlateItem* smlPlate = fgc->getPlateItemBySzNIndex(FILLGLOBALCONFIG_PLATESIZE_SM, 0) ;
-    vector<FillGlobalConfig::PlateItem*> usedPlates = {bigPlate,midPlate,smlPlate} ;
-    
-    //一个关卡最多出现大、中、小盘子个数
-    const int PLATE_BG_MAX_CNT = 9  ;// 1个大盘子=2两个中盘子=6个小盘子
-    const int PLATE_MD_MAX_CNT = 18 ;
-    const int PLATE_SM_MAX_CNT = 54 ;
-    //每个关卡最大容纳等效小盘子的个数 就是54个
-    //                                                 普通               难              极难
-    vector< vector<float> > diffPlateCntPercent0 = { {0.3,0.3,0.0}, {0.2,0.4,0.0}, {0.1,0.5,0.0} } ;
-    vector< vector<float> > diffPlateCntPercent1 = { {0.7,0.7,0.0}, {0.6,0.8,0.1}, {0.5,0.9,0.1} } ;
-    bool isFirstGood = false;//由于盘子百分比计算不一定能满足盘子和宝石要求,为了保证至少有一个盘子组合可以用这里保留第一个可以装下宝石的盘子组合,如果有更优的方案替换之。
-    vector<LevelGenerate::PlateIdAndLayerCnt> firstAvailablePlateComb ;
-    
-    for(int ipbig = 1; ipbig <= PLATE_BG_MAX_CNT; ++ ipbig ) {
-        for(int ipmid = 0 ; ipmid <= PLATE_MD_MAX_CNT; ++ ipmid ) {
-            int effsmPlateCnt = ipbig*6 + ipmid*3 ;
-            if( effsmPlateCnt>PLATE_SM_MAX_CNT ) continue ;
-            for(int ipsml = 0 ; ipsml <= PLATE_SM_MAX_CNT; ++ ipsml ) {
-                //等效小盘子个数
-                effsmPlateCnt += ipsml  ;
-                if( effsmPlateCnt>PLATE_SM_MAX_CNT ) continue ;
-                
-                float bigPlateCntPercent = ipbig*6.0 / effsmPlateCnt ;
-                float midPlateCntPercent = ipmid*3.0 / effsmPlateCnt ;
-                float smlPlateCntPercent = ipsml*1.0 / effsmPlateCnt ;
-                vector<float> percentArr = {bigPlateCntPercent,midPlateCntPercent,smlPlateCntPercent} ;
-                
-                //判断每个盘子百分比是否满足难度要求
-                bool percentOk = true ;
-                for(int iplatetype=0;iplatetype<percentArr.size();++iplatetype) {
-                    if( diffPlateCntPercent0[difficulty][iplatetype]>percentArr[iplatetype] || diffPlateCntPercent1[difficulty][iplatetype]<percentArr[iplatetype]  )
-                        percentOk=false;
-                }
-                if( isFirstGood == true ){
-                    //至少有一个装下宝石的方案了
-                    if( !percentOk ) continue ;//盘子百分比不符合要求,跳过
-                }
-                
-                if( isFirstGood==false ) {
-                    //保证至少有一个可以满足宝石的盘子组合
-                    vector<int> pidArr ;
-                    vector<int> capArr ;
-                    vector<int> lyrArr ;
-                    for(int it=0;it<ipbig;++it) { pidArr.push_back(bigPlate->_id);capArr.push_back(bigPlate->_bigJewCap*4); lyrArr.push_back(1); }
-                    for(int it=0;it<ipmid;++it) { pidArr.push_back(midPlate->_id);capArr.push_back(midPlate->_bigJewCap*4); lyrArr.push_back(1); }
-                    for(int it=0;it<ipsml;++it) { pidArr.push_back(smlPlate->_id);capArr.push_back(smlPlate->_bigJewCap*4); lyrArr.push_back(1); }
-                    bool lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt) ;
-                    if( lyrOk ){
-                        isFirstGood = true ;
-                        //保存结果
-                        firstAvailablePlateComb.resize(capArr.size());
-                        for(int it = 0 ; it < capArr.size(); ++ it ) {
-                            firstAvailablePlateComb[it]._plateId = pidArr[it] ;
-                            firstAvailablePlateComb[it]._layerCnt = lyrArr[it] ;
-                        }
-                    }
-                }
-                //计算每个类型的层数
-                if( difficulty== FILLGLOBALCONFIG_DIFFCULTY_NORM ) {
-                    //全部两层
-                    vector<int> pidArr ;
-                    vector<int> capArr ;
-                    vector<int> lyrArr ;
-                    for(int it=0;it<ipbig;++it) { pidArr.push_back(bigPlate->_id);capArr.push_back(bigPlate->_bigJewCap*4); lyrArr.push_back(2); }
-                    for(int it=0;it<ipmid;++it) { pidArr.push_back(midPlate->_id);capArr.push_back(midPlate->_bigJewCap*4); lyrArr.push_back(2); }
-                    for(int it=0;it<ipsml;++it) { pidArr.push_back(smlPlate->_id);capArr.push_back(smlPlate->_bigJewCap*4); lyrArr.push_back(2); }
-                    bool lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt) ;
-                    if(!lyrOk) continue;
-                    
-                    //保存结果
-                    vector<LevelGenerate::PlateIdAndLayerCnt> tres(capArr.size()) ;
-                    for(int it = 0 ; it < capArr.size(); ++ it ) {
-                        tres[it]._plateId = pidArr[it] ;
-                        tres[it]._layerCnt = lyrArr[it] ;
-                    }
-                    possibleResults.push_back(tres) ;
-                    
-                }else if( difficulty==FILLGLOBALCONFIG_DIFFCULTY_HARD2 ) {
-                    //全部3层
-                    vector<int> pidArr ;
-                    vector<int> capArr ;
-                    vector<int> lyrArr ;
-                    for(int it=0;it<ipbig;++it) { pidArr.push_back(bigPlate->_id);capArr.push_back(bigPlate->_bigJewCap*4); lyrArr.push_back(3); }
-                    for(int it=0;it<ipmid;++it) { pidArr.push_back(midPlate->_id);capArr.push_back(midPlate->_bigJewCap*4); lyrArr.push_back(3); }
-                    for(int it=0;it<ipsml;++it) { pidArr.push_back(smlPlate->_id);capArr.push_back(smlPlate->_bigJewCap*4); lyrArr.push_back(3); }
-                    bool lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt) ;
-                    if(!lyrOk) continue;
-                    //保存结果
-                    vector<LevelGenerate::PlateIdAndLayerCnt> tres(capArr.size()) ;
-                    for(int it = 0 ; it < capArr.size(); ++ it ) {
-                        tres[it]._plateId = pidArr[it] ;
-                        tres[it]._layerCnt = lyrArr[it] ;
-                    }
-                    possibleResults.push_back(tres) ;
-                }else{
-                    //50% 2层, 50% 3层
-                    vector<int> pidArr ;
-                    vector<int> capArr ;
-                    vector<int> lyrArr ;
-                    for(int it=0;it<ipbig;++it) { pidArr.push_back(bigPlate->_id);capArr.push_back(bigPlate->_bigJewCap*4); lyrArr.push_back((rand()%2==0)?2:3); }
-                    for(int it=0;it<ipmid;++it) { pidArr.push_back(midPlate->_id);capArr.push_back(midPlate->_bigJewCap*4); lyrArr.push_back((rand()%2==0)?2:3); }
-                    for(int it=0;it<ipsml;++it) { pidArr.push_back(smlPlate->_id);capArr.push_back(smlPlate->_bigJewCap*4); lyrArr.push_back((rand()%2==0)?2:3); }
-                    bool lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt) ;
-                    if(!lyrOk) continue;
-                    //保存结果
-                    vector<LevelGenerate::PlateIdAndLayerCnt> tres(capArr.size()) ;
-                    for(int it = 0 ; it < capArr.size(); ++ it ) {
-                        tres[it]._plateId = pidArr[it] ;
-                        tres[it]._layerCnt = lyrArr[it] ;
-                    }
-                    possibleResults.push_back(tres) ;
-                }
-            }
-        }
-    }
-    //随机挑选一个结果返回
-    if( possibleResults.size()> 0 ) {
-        int randindex = rand() % possibleResults.size();
-        results = possibleResults[randindex] ;
-    }else {
-        results = firstAvailablePlateComb ;
-    }
+    //...
+    //...
     return results ;
 }
 
 //考虑可移动关卡的条件
-vector<LevelGenerate::PlateIdAndLayerCnt> LevelGenerate::generatePlateTypeAndLayerCnts2(const bool isMovable,const int difficulty,const int totEquvSmallJewelCnt)
+vector<LevelGenerate::PlateIdAndLayerCnt> LevelGenerate::generatePlateTypeAndLayerCnts2(
+                                                                                        const bool isMovable,
+                                                                                        const FillGlobalConfig::MultiMoveConfig* mulMoveConfig,
+                                                                                        const int difficulty,
+                                                                                        const int totEquvSmallJewelCnt
+                                                                                        )
 {
-    //移动关卡相当于多了一个 大盘子,整个关卡最多可以达到10个大盘子
     //如果是移动关卡,那么至少需要保证如下等价小盘子数量,如果减少层数仍然不能满足那么就有几个盘子输出几个
-    const int MAX_LG_PLATE_CNT = 9 + (isMovable?1:0) ;
-    const int MAX_SM_PLATE_CNT = MAX_LG_PLATE_CNT*4 ;
-    const int MAX_SM_JEWEL_CNT_PER_LAYER = MAX_LG_PLATE_CNT * 24 ;
+    //const int MAX_LG_PLATE_CNT = 9 ;// + (isMovable?1:0) ;
+    // const int MAX_SM_PLATE_CNT = MAX_LG_PLATE_CNT*4 ;
+    //const int MAX_SM_JEWEL_CNT_PER_LAYER = MAX_LG_PLATE_CNT * 24 ;
+    const int MID_PLATE_CAP = 12 ;//中盘子对小宝石的容量
     
-    const int minEquaSmPlateCntForMovable = 16 ; //
-    const int smPlateEquaSmJewelCnt = 6 ;// 小盘子装1.5个大,3个中,6个小
+    //const int minEquaSmPlateCntForMovable = 16 ; //
+    //const int smPlateEquaSmJewelCnt = 6 ;// 小盘子装1.5个大,3个中,6个小
     
     //一个关卡最多出现大、中、小盘子个数
-    int bigPlateMaxCnt = 9  ;// 1个大盘子=2两个中盘子=4个小盘子
-    if( isMovable ) {
-        bigPlateMaxCnt = 10 ;
-    }
-    int midPlateMaxCnt = bigPlateMaxCnt * 2 ;
-    int smlPlateMaxCnt = bigPlateMaxCnt * 2 ;
+    int tempBigPlateCnt1 = 9  ;// 1个大盘子=2两个中盘子
+    int tempMidPlateCnt1 = 0 ;
+    computePlateCount(mulMoveConfig, tempBigPlateCnt1, tempMidPlateCnt1) ;
+    int midPlateMaxCnt = tempBigPlateCnt1 * 2 + tempMidPlateCnt1 ;//大盘子数量换算成中盘子
     
     
-    vector<LevelGenerate::PlateIdAndLayerCnt> results ;
-    
     FillGlobalConfig* fgc = FillGlobalConfig::getInstance() ;
-    FillGlobalConfig::PlateItem* bigPlate = fgc->getPlateItemBySzNIndex(FILLGLOBALCONFIG_PLATESIZE_LG, 0) ;
-    FillGlobalConfig::PlateItem* midPlate = fgc->getPlateItemBySzNIndex(FILLGLOBALCONFIG_PLATESIZE_MD, 0) ;
-    FillGlobalConfig::PlateItem* smlPlate = fgc->getPlateItemBySzNIndex(FILLGLOBALCONFIG_PLATESIZE_SM, 0) ;
-    vector<FillGlobalConfig::PlateItem*> usedPlates = {bigPlate,midPlate,smlPlate} ;
+    FillGlobalConfig::PlateItem* bigPlateH = fgc->getPlateItemBySzNDirection(FILLGLOBALCONFIG_PLATESIZE_LG, 1) ;
+    FillGlobalConfig::PlateItem* midPlateH = fgc->getPlateItemBySzNDirection(FILLGLOBALCONFIG_PLATESIZE_MD, 1) ;
+    FillGlobalConfig::PlateItem* bigPlateV = fgc->getPlateItemBySzNDirection(FILLGLOBALCONFIG_PLATESIZE_LG, 0) ;
+    FillGlobalConfig::PlateItem* midPlateV = fgc->getPlateItemBySzNDirection(FILLGLOBALCONFIG_PLATESIZE_MD, 0) ;
     
-    int equvSmPlateCnt = ceil( totEquvSmallJewelCnt * 1.0 / 6) ; // 本款需要多少个一层小盘子的总数量
+    //如果是移动关卡,先构建移动盘子
+    int totEquvSmallJewelCnt2 = totEquvSmallJewelCnt ;
+    vector<LevelGenerate::PlateIdAndLayerCnt> results ;
+    //剩余可以用的中盘子个数
+    if( isMovable ) {
+        int residueEquvSmlJewelCnt = 0 ;
+        results = generatePlateTypeAndLayerCntsForMovablePlates(mulMoveConfig, totEquvSmallJewelCnt2, residueEquvSmlJewelCnt) ;
+        totEquvSmallJewelCnt2 = residueEquvSmlJewelCnt ;
+        //减去移动的盘子
+        for(int ir = 0 ; ir < results.size(); ++ ir ){
+            if( fgc->getPlateItemById(results[ir]._plateId)->_size == FILLGLOBALCONFIG_PLATESIZE_LG ) {
+                midPlateMaxCnt-=2 ;//减去移动的盘子
+            }else if(fgc->getPlateItemById(results[ir]._plateId)->_size == FILLGLOBALCONFIG_PLATESIZE_MD) {
+                midPlateMaxCnt-=1 ;//减去移动的盘子
+            }
+        }
+    }
+    
+    int equvMidPlateCnt = ceil( totEquvSmallJewelCnt2 * 1.0 / MID_PLATE_CAP) ; // 本关需要多少个一层中盘子的总数量
     int layerCnt0 = 1 ;//盘子层数下限
     int layerCnt1 = 1 ;//盘子层数上限
     
     //计算可能的层取值范围
-    bool forceToFitMovable = false ;
-    if( isMovable ) {
-        if( equvSmPlateCnt <= minEquaSmPlateCntForMovable ) {
-            //不足移动关卡所需最小的等效小盘子个数,那么直接组合盘子即可。
-            layerCnt0 = 1 ;
-            layerCnt1 = 1 ;
-            forceToFitMovable = false ;
-        }else
-        {
-            //10个大盘子,6个大宝石,等效于每层最多放置 10*6*4=240个小宝石
-            layerCnt0 = fmax(1 , totEquvSmallJewelCnt / MAX_SM_JEWEL_CNT_PER_LAYER ) ;
-            if( layerCnt0<3 ) layerCnt1 = 3 ;
-            else layerCnt1 = layerCnt0 + 1 ;
-            forceToFitMovable = true ;
-        }
-    }else {
-        layerCnt0 = fmax(1 , totEquvSmallJewelCnt / MAX_SM_JEWEL_CNT_PER_LAYER ) ;
+    {
+        layerCnt0 = fmax(1 , totEquvSmallJewelCnt2 / (midPlateMaxCnt*MID_PLATE_CAP) ) ;
         if( layerCnt0<3 ) layerCnt1 = 3 ;
         else layerCnt1 = layerCnt0 + 1 ;
-        forceToFitMovable = false ;
     }
     
-    int bigPlateCnt0 = totEquvSmallJewelCnt * 1.0 / layerCnt1 / 24 ; //大盘子最少需要数量
-    int bigPlateCnt1 = totEquvSmallJewelCnt * 1.0 / layerCnt0 / 24 + 1; //大盘子最多需要数量
+    int bigPlateCnt0 = totEquvSmallJewelCnt2 * 1.0 / layerCnt1 / 24 ; //大盘子最少需要数量
+    int bigPlateCnt1 = totEquvSmallJewelCnt2 * 1.0 / layerCnt0 / 24 + 1; //大盘子最多需要数量
     int midPlateCnt0 = bigPlateCnt0*2 ;
     int midPlateCnt1 = bigPlateCnt1*2 ;
-    int smlPlateCnt0 = bigPlateCnt0*4 ;
-    int smlPlateCnt1 = bigPlateCnt1*4 ;
     
-    bigPlateCnt1 = fmin( bigPlateMaxCnt ,bigPlateCnt1 ) ;
+    bigPlateCnt1 = fmin( midPlateMaxCnt/2 ,bigPlateCnt1 ) ;
     midPlateCnt1 = fmin( midPlateMaxCnt ,midPlateCnt1 ) ;
-    smlPlateCnt1 = fmin( smlPlateMaxCnt ,smlPlateCnt1 ) ;
     
     bool findFirstAvailableComb = false ;//第一个完全放入宝石的组合,用于当不满足任何条件时,至少返回一个可装入全部宝石的默认方案
     vector<vector<LevelGenerate::PlateIdAndLayerCnt>> possibleResults ;//满足全部百分比要求的组合
     vector<LevelGenerate::PlateIdAndLayerCnt> firstAvailablePlateComb ;//第一个满足全部宝石填充的组合,如果需要移动还要考虑最小移动盘子个数的条件
     int firstAvailableLyrCnt = 0 ;
     
-    //难度对盘子数量的限制                                 普通                             极难
-    vector< vector<float> > hardPlateCntPercent0 = { {0.3,0.3,0.0}, {0.2,0.4,0.0}, {0.1,0.5,0.0} } ;
-    vector< vector<float> > hardPlateCntPercent1 = { {0.7,0.7,0.0}, {0.6,0.8,0.1}, {0.5,0.9,0.1} } ;
+    //难度对盘子数量的限制                                 普通         难         极难
+    vector< vector<float> > hardPlateCntPercent0 = { {0.3,0.3}, {0.2,0.4}, {0.1,0.5} } ;
+    vector< vector<float> > hardPlateCntPercent1 = { {0.7,0.7}, {0.6,0.8}, {0.5,0.9} } ;
     
     //穷举全部盘子和层的组合找到符合条件的组合
     for(int ibig = 0; ibig <= bigPlateCnt1; ++ ibig ) {
         for(int imid = 0; imid <= midPlateCnt1 ; ++ imid ) {
-            for(int isml = 0 ; isml <= smlPlateCnt1; ++ isml ) {
-                int equvSmPlateCnt1 = isml+imid*2+ibig*4 ;
-                if( equvSmPlateCnt1==0 || equvSmPlateCnt1 > MAX_SM_PLATE_CNT ) continue ;
-                if( equvSmPlateCnt1 < smlPlateCnt0 ) continue ;
-                if( isMovable && forceToFitMovable ) if( equvSmPlateCnt1<minEquaSmPlateCntForMovable ) continue ;
-                
-                float bigPlateCntPercent = ibig*4.0 / equvSmPlateCnt1 ;
-                float midPlateCntPercent = imid*2.0 / equvSmPlateCnt1 ;
-                float smlPlateCntPercent = isml*1.0 / equvSmPlateCnt1 ;
-                vector<float> percentArr = {bigPlateCntPercent,midPlateCntPercent,smlPlateCntPercent} ;
-                //判断每个盘子百分比是否满足难度要求
-                bool percentOk = true ;
-                for(int iplatetype=0;iplatetype<percentArr.size();++iplatetype) {
-                    if( hardPlateCntPercent0[difficulty][iplatetype]>percentArr[iplatetype] || hardPlateCntPercent1[difficulty][iplatetype]<percentArr[iplatetype]  )
-                        percentOk=false;
-                }
-                if( findFirstAvailableComb == true ){
-                    //至少有一个装下宝石的方案了
-                    if( !percentOk ) continue ;//盘子百分比不符合要求,跳过
-                }
-                
-                if( findFirstAvailableComb==false || (findFirstAvailableComb && firstAvailableLyrCnt>3 )  ) {
-                    //保证至少有一个可以满足宝石的盘子组合,优先保存总层数大于3的情况
-                    //当前盘子组合下最小满足的层数
-                    int upperLyrCnt = ceil( totEquvSmallJewelCnt * 1.0 / (equvSmPlateCnt1*6) ) ;
-                    bool upperLyrCntOk = true ;
-                    if( findFirstAvailableComb ) {
-                        if( upperLyrCnt >= firstAvailableLyrCnt ) {
-                            upperLyrCntOk = false ;
-                        }
-                    }
-                    vector<int> pidArr ;
-                    vector<int> capArr ;
-                    vector<int> lyrArr ;
-                    for(int it=0;it<ibig;++it) { pidArr.push_back(bigPlate->_id);capArr.push_back(bigPlate->_bigJewCap*4); lyrArr.push_back(upperLyrCnt); }
-                    for(int it=0;it<imid;++it) { pidArr.push_back(midPlate->_id);capArr.push_back(midPlate->_bigJewCap*4); lyrArr.push_back(upperLyrCnt); }
-                    for(int it=0;it<isml;++it) { pidArr.push_back(smlPlate->_id);capArr.push_back(smlPlate->_bigJewCap*4); lyrArr.push_back(upperLyrCnt); }
-                    bool lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt) ;
-                    if( lyrOk==false && upperLyrCnt>1 ) {
-                        for(int it=0;it<lyrArr.size();++it ) {
-                            lyrArr[it] -= 1 ;
-                            lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt) ;
-                            if( lyrOk ) break ;
-                        }
-                    }
-                    if( lyrOk && upperLyrCntOk ){
-                        findFirstAvailableComb = true ;
-                        firstAvailableLyrCnt = upperLyrCnt ;
-                        //保存结果
-                        firstAvailablePlateComb.resize(capArr.size());
-                        for(int it = 0 ; it < capArr.size(); ++ it ) {
-                            firstAvailablePlateComb[it]._plateId = pidArr[it] ;
-                            firstAvailablePlateComb[it]._layerCnt = lyrArr[it] ;
-                        }
+            int currEquvMidPlateCnt = imid+ibig*2 ;
+            if( currEquvMidPlateCnt==0 || currEquvMidPlateCnt > midPlateMaxCnt ) continue ;
+            
+            float bigPlateCntPercent = ibig*2.0 / currEquvMidPlateCnt ;
+            float midPlateCntPercent = imid*1.0 / currEquvMidPlateCnt ;
+            vector<float> percentArr = {bigPlateCntPercent,midPlateCntPercent} ;
+            //判断每个盘子百分比是否满足难度要求
+            bool percentOk = true ;
+            for(int iplatetype=0;iplatetype<percentArr.size();++iplatetype) {
+                if( hardPlateCntPercent0[difficulty][iplatetype]>percentArr[iplatetype] || hardPlateCntPercent1[difficulty][iplatetype]<percentArr[iplatetype]  )
+                    percentOk=false;
+            }
+            if( findFirstAvailableComb == true ){
+                //至少有一个装下宝石的方案了
+                if( !percentOk ) continue ;//盘子百分比不符合要求,跳过
+            }
+            
+            if( findFirstAvailableComb==false || (findFirstAvailableComb && firstAvailableLyrCnt>3 )  ) {
+                //保证至少有一个可以满足宝石的盘子组合,优先保存总层数大于3的情况
+                //当前盘子组合下最小满足的层数
+                int upperLyrCnt = ceil( totEquvSmallJewelCnt2 * 1.0 / (currEquvMidPlateCnt*MID_PLATE_CAP) ) ;
+                bool upperLyrCntOk = true ;
+                if( findFirstAvailableComb ) {
+                    if( upperLyrCnt >= firstAvailableLyrCnt ) {
+                        upperLyrCntOk = false ;
                     }
                 }
-                //计算每个类型的层数
-                if( difficulty== FILLGLOBALCONFIG_DIFFCULTY_NORM ) {
-                    //全部两层
-                    vector<int> pidArr ;
-                    vector<int> capArr ;
-                    vector<int> lyrArr ;
-                    for(int it=0;it<ibig;++it) { pidArr.push_back(bigPlate->_id);capArr.push_back(bigPlate->_bigJewCap*4); lyrArr.push_back(2); }
-                    for(int it=0;it<imid;++it) { pidArr.push_back(midPlate->_id);capArr.push_back(midPlate->_bigJewCap*4); lyrArr.push_back(2); }
-                    for(int it=0;it<isml;++it) { pidArr.push_back(smlPlate->_id);capArr.push_back(smlPlate->_bigJewCap*4); lyrArr.push_back(2); }
-                    bool lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt) ;
-                    if(!lyrOk) continue;
-                    
-                    //保存结果
-                    vector<LevelGenerate::PlateIdAndLayerCnt> tres(capArr.size()) ;
-                    for(int it = 0 ; it < capArr.size(); ++ it ) {
-                        tres[it]._plateId = pidArr[it] ;
-                        tres[it]._layerCnt = lyrArr[it] ;
+                vector<int> pidArr ;
+                vector<int> capArr ;
+                vector<int> lyrArr ;
+                //根据需求文档,非移动盘子,大盘子只用竖的,中盘子只用横的。
+                for(int it=0;it<ibig;++it) { pidArr.push_back(bigPlateV->_id);capArr.push_back(bigPlateV->_bigJewCap*4); lyrArr.push_back(upperLyrCnt); }
+                for(int it=0;it<imid;++it) { pidArr.push_back(midPlateH->_id);capArr.push_back(midPlateH->_bigJewCap*4); lyrArr.push_back(upperLyrCnt); }
+                bool lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt2) ;
+                if( lyrOk==false && upperLyrCnt>1 ) {
+                    for(int it=0;it<lyrArr.size();++it ) {
+                        lyrArr[it] -= 1 ;
+                        lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt2) ;
+                        if( lyrOk ) break ;
                     }
-                    possibleResults.push_back(tres) ;
-                    
-                }else if( difficulty==FILLGLOBALCONFIG_DIFFCULTY_HARD2 ) {
-                    //全部3层
-                    vector<int> pidArr ;
-                    vector<int> capArr ;
-                    vector<int> lyrArr ;
-                    for(int it=0;it<ibig;++it) { pidArr.push_back(bigPlate->_id);capArr.push_back(bigPlate->_bigJewCap*4); lyrArr.push_back(3); }
-                    for(int it=0;it<imid;++it) { pidArr.push_back(midPlate->_id);capArr.push_back(midPlate->_bigJewCap*4); lyrArr.push_back(3); }
-                    for(int it=0;it<isml;++it) { pidArr.push_back(smlPlate->_id);capArr.push_back(smlPlate->_bigJewCap*4); lyrArr.push_back(3); }
-                    //cout<<"debug big "<<ibig<<" mid "<<imid<<" sml "<<isml<<endl;
-                    bool lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt) ;
-                    if(!lyrOk) continue;
-                    //保存结果
-                    vector<LevelGenerate::PlateIdAndLayerCnt> tres(capArr.size()) ;
-                    for(int it = 0 ; it < capArr.size(); ++ it ) {
-                        tres[it]._plateId = pidArr[it] ;
-                        tres[it]._layerCnt = lyrArr[it] ;
-                    }
-                    possibleResults.push_back(tres) ;
-                }else{
-                    //50% 2层, 50% 3层
-                    vector<int> pidArr ;
-                    vector<int> capArr ;
-                    vector<int> lyrArr ;
-                    for(int it=0;it<ibig;++it) { pidArr.push_back(bigPlate->_id);capArr.push_back(bigPlate->_bigJewCap*4); lyrArr.push_back((rand()%2==0)?2:3); }
-                    for(int it=0;it<imid;++it) { pidArr.push_back(midPlate->_id);capArr.push_back(midPlate->_bigJewCap*4); lyrArr.push_back((rand()%2==0)?2:3); }
-                    for(int it=0;it<isml;++it) { pidArr.push_back(smlPlate->_id);capArr.push_back(smlPlate->_bigJewCap*4); lyrArr.push_back((rand()%2==0)?2:3); }
-                    bool lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt) ;
-                    if(!lyrOk) continue;
+                }
+                if( lyrOk && upperLyrCntOk ){
+                    findFirstAvailableComb = true ;
+                    firstAvailableLyrCnt = upperLyrCnt ;
                     //保存结果
-                    vector<LevelGenerate::PlateIdAndLayerCnt> tres(capArr.size()) ;
+                    firstAvailablePlateComb.resize(capArr.size());
                     for(int it = 0 ; it < capArr.size(); ++ it ) {
-                        tres[it]._plateId = pidArr[it] ;
-                        tres[it]._layerCnt = lyrArr[it] ;
+                        firstAvailablePlateComb[it]._plateId = pidArr[it] ;
+                        firstAvailablePlateComb[it]._layerCnt = lyrArr[it] ;
+                        firstAvailablePlateComb[it]._moveId = MOVEID_NO_MOVE ;
                     }
-                    possibleResults.push_back(tres) ;
                 }
+            }
+            //计算每个类型的层数
+            if( difficulty== FILLGLOBALCONFIG_DIFFCULTY_NORM ) {
+                //全部两层
+                vector<int> pidArr ;
+                vector<int> capArr ;
+                vector<int> lyrArr ;
+                //根据需求文档,非移动盘子,大盘子只用竖的,中盘子只用横的。
+                for(int it=0;it<ibig;++it) { pidArr.push_back(bigPlateV->_id);capArr.push_back(bigPlateV->_bigJewCap*4); lyrArr.push_back(2); }
+                for(int it=0;it<imid;++it) { pidArr.push_back(midPlateH->_id);capArr.push_back(midPlateH->_bigJewCap*4); lyrArr.push_back(2); }
+                bool lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt2) ;
+                if(!lyrOk) continue;
                 
+                //保存结果
+                vector<LevelGenerate::PlateIdAndLayerCnt> tres(capArr.size()) ;
+                for(int it = 0 ; it < capArr.size(); ++ it ) {
+                    tres[it]._plateId = pidArr[it] ;
+                    tres[it]._layerCnt = lyrArr[it] ;
+                    tres[it]._moveId = MOVEID_NO_MOVE ;//不会移动的盘子
+                }
+                possibleResults.push_back(tres) ;
+                
+            }else if( difficulty==FILLGLOBALCONFIG_DIFFCULTY_HARD2 ) {
+                //全部3层
+                vector<int> pidArr ;
+                vector<int> capArr ;
+                vector<int> lyrArr ;
+                //根据需求文档,非移动盘子,大盘子只用竖的,中盘子只用横的。
+                for(int it=0;it<ibig;++it) { pidArr.push_back(bigPlateV->_id);capArr.push_back(bigPlateV->_bigJewCap*4); lyrArr.push_back(3); }
+                for(int it=0;it<imid;++it) { pidArr.push_back(midPlateH->_id);capArr.push_back(midPlateH->_bigJewCap*4); lyrArr.push_back(3); }
+                //cout<<"debug big "<<ibig<<" mid "<<imid<<" sml "<<isml<<endl;
+                bool lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt2) ;
+                if(!lyrOk) continue;
+                //保存结果
+                vector<LevelGenerate::PlateIdAndLayerCnt> tres(capArr.size()) ;
+                for(int it = 0 ; it < capArr.size(); ++ it ) {
+                    tres[it]._plateId = pidArr[it] ;
+                    tres[it]._layerCnt = lyrArr[it] ;
+                    tres[it]._moveId = MOVEID_NO_MOVE ;//不会移动的盘子
+                }
+                possibleResults.push_back(tres) ;
+            }else{
+                //50% 2层, 50% 3层
+                vector<int> pidArr ;
+                vector<int> capArr ;
+                vector<int> lyrArr ;
+                //根据需求文档,非移动盘子,大盘子只用竖的,中盘子只用横的。
+                for(int it=0;it<ibig;++it) { pidArr.push_back(bigPlateV->_id);capArr.push_back(bigPlateV->_bigJewCap*4); lyrArr.push_back((rand()%2==0)?2:3); }
+                for(int it=0;it<imid;++it) { pidArr.push_back(midPlateH->_id);capArr.push_back(midPlateH->_bigJewCap*4); lyrArr.push_back((rand()%2==0)?2:3); }
+                bool lyrOk = isJustFilledAll2(capArr, lyrArr, totEquvSmallJewelCnt2) ;
+                if(!lyrOk) continue;
+                //保存结果
+                vector<LevelGenerate::PlateIdAndLayerCnt> tres(capArr.size()) ;
+                for(int it = 0 ; it < capArr.size(); ++ it ) {
+                    tres[it]._plateId = pidArr[it] ;
+                    tres[it]._layerCnt = lyrArr[it] ;
+                    tres[it]._moveId = MOVEID_NO_MOVE ;//不会移动的盘子
+                }
+                possibleResults.push_back(tres) ;
             }
         }
     }
     //随机挑选一个结果返回
     if( possibleResults.size()> 0 ) {
         int randindex = rand() % possibleResults.size();
-        results = possibleResults[randindex] ;
+        for(int ip = 0 ; ip < possibleResults[randindex].size();++ ip ) {
+            results.push_back(possibleResults[randindex][ip]) ;
+        }
     }else {
-        results = firstAvailablePlateComb ;
+        for(int ip = 0 ; ip < firstAvailablePlateComb.size();++ ip ) {
+            results.push_back(firstAvailablePlateComb[ip]) ;
+        }
     }
-    return results ;
+    return results ;//
     
 }
 
@@ -687,29 +619,38 @@ unordered_map<int, int> LevelGenerate::findUnfilledInStorages( unordered_map<int
 }
 
 
-void LevelGenerate::regroupPlateLyrFillResults( vector<tuple<int,int,vector<FillResult>>>& pidlyrFillArray, vector<tuple<int,vector<vector<FillResult>>>>& results )
+void LevelGenerate::regroupPlateLyrFillResults(
+                                               vector<tuple<int,int,vector<FillResult>,int>>& pidlyrFillMvidArray,
+                                               vector<PlateFillResult>& results)
 {
     int currLayer = 1 ;//从底层到顶层构建结果数据结构
-    int num = pidlyrFillArray.size() ;
+    int num = pidlyrFillMvidArray.size() ;
     int nloop = num ;
     while( nloop > 0 ) {
         //cout<<"regroup for layer "<<currLayer<<endl;
-        for( auto itlyr = pidlyrFillArray.begin(); itlyr!=pidlyrFillArray.end(); ++ itlyr ) {
+        for( auto itlyr = pidlyrFillMvidArray.begin(); itlyr!=pidlyrFillMvidArray.end(); ++ itlyr ) {
             int pid = std::get<0>( *itlyr ) ;
             int lyr = std::get<1>( *itlyr ) ;
             vector<FillResult>& frs = std::get<2>(*itlyr) ;
+            int moveId = std::get<3>(*itlyr) ;
+            
             if(pid>=0) {
                 if( lyr==currLayer ) {
                     if( lyr==1 ) {
                         vector<vector<FillResult>> frArr = {frs} ;
-                        results.push_back( {pid, frArr } ) ;
+                        PlateFillResult pfr ;
+                        pfr._layersFillResults = frArr ;
+                        pfr._moveId = moveId ;
+                        pfr._plateTypeId = pid ;
+                        results.push_back( pfr ) ;
                         num-- ;
                         std::get<0>( *itlyr ) = -1 ;
                     }else {
-                        for(auto itr = results.begin();itr!=results.end();++itr ) {
-                            int pidr = std::get<0>(*itr) ;
-                            vector<vector<FillResult>>& frArr = std::get<1>(*itr) ;
-                            if( pidr == pid && frArr.size()==currLayer-1 ) {
+                        for(auto itPfr = results.begin();itPfr!=results.end();++itPfr ) {
+                            int pidr = itPfr->_plateTypeId ;
+                            int mvid = itPfr->_moveId ;
+                            vector<vector<FillResult>>& frArr = itPfr->_layersFillResults ;
+                            if( pidr == pid && mvid == moveId && frArr.size()==currLayer-1 ) {
                                 frArr.push_back(frs);
                                 num-- ;
                                 std::get<0>( *itlyr ) = -1 ;
@@ -729,12 +670,144 @@ void LevelGenerate::regroupPlateLyrFillResults( vector<tuple<int,int,vector<Fill
 
 
 
-void LevelGenerate::placePlates(const bool movable, const vector<int>& plateIdArr, vector<ContourData::Point>& resultPlateCenterPointArr )
+void LevelGenerate::placePlates(const bool movable,
+                                const FillGlobalConfig::MultiMoveConfig* mulMoveConfig,
+                                const vector<PlateFillResult>& plateFRArr,
+                                vector<ContourData::Point>& resultPlateCenterPointArr )
 {
     GridPositionTool gpt ;
+    vector<int> plateIdArr ;
+    vector<int> moveIdArr ;
+    for(int ip = 0 ; ip < plateFRArr.size(); ++ ip ) {
+        plateIdArr.push_back( plateFRArr[ip]._plateTypeId ) ;
+        moveIdArr.push_back( plateFRArr[ip]._moveId ) ;
+    }
+    
     vector<vector<int>> resPosis ;
-    gpt.solve(movable, plateIdArr, resPosis) ;
+    
+    string firstMoveTypeCode = "" ;
+    string secondMoveTypeCode = "" ;
+    if( movable ) {
+        firstMoveTypeCode = mulMoveConfig->_first->_type[0] ;
+        if( mulMoveConfig->_second ) secondMoveTypeCode = mulMoveConfig->_second->_type[0] ;
+    }
+    gpt.solve(movable,
+              firstMoveTypeCode ,
+              secondMoveTypeCode,
+              plateIdArr,
+              moveIdArr,
+              resPosis   //这里结果是盘子左下角坐标,下面需要转换成中心坐标。
+              ) ;
+    
     for(int ip=0;ip<resPosis.size();++ip ) {
-        resultPlateCenterPointArr.push_back( { (float)resPosis[ip][0] , (float)resPosis[ip][1]} ) ;
+        FillGlobalConfig::PlateItem* platePtr = FillGlobalConfig::getInstance()->getPlateItemById(plateIdArr[ip]) ;
+        resultPlateCenterPointArr.push_back( { (float)resPosis[ip][0]+platePtr->_bbwid/2 , (float)resPosis[ip][1]+platePtr->_bbhei/2} ) ;
     }
 }
+
+
+
+vector<LevelGenerate::PlateIdAndLayerCnt>
+    LevelGenerate::generatePlateTypeAndLayerCntsForMovablePlates(
+                                                                 const FillGlobalConfig::MultiMoveConfig* mulMoveConfig,
+                                                                 const int totEquvSmallJewelCnt,
+                                                                 int& retResidueEquvSmlJewelCnt )
+{
+        vector<LevelGenerate::PlateIdAndLayerCnt> results ;
+        retResidueEquvSmlJewelCnt = totEquvSmallJewelCnt ;
+        FillGlobalConfig* fgc = FillGlobalConfig::getInstance() ;
+        FillGlobalConfig::PlateItem* bigPlateH = fgc->getPlateItemBySzNDirection(FILLGLOBALCONFIG_PLATESIZE_LG, 1) ;
+        FillGlobalConfig::PlateItem* midPlateH = fgc->getPlateItemBySzNDirection(FILLGLOBALCONFIG_PLATESIZE_MD, 1) ;
+        FillGlobalConfig::PlateItem* bigPlateV = fgc->getPlateItemBySzNDirection(FILLGLOBALCONFIG_PLATESIZE_LG, 0) ;
+        FillGlobalConfig::PlateItem* midPlateV = fgc->getPlateItemBySzNDirection(FILLGLOBALCONFIG_PLATESIZE_MD, 0) ;
+        
+        const int MAX_MOVE_CONFIG_CNT = 2;
+        FillGlobalConfig::MoveConfig* arr[MAX_MOVE_CONFIG_CNT] = { mulMoveConfig->_first , mulMoveConfig->_second } ;
+        for(int ii = 0 ; ii < MAX_MOVE_CONFIG_CNT ; ++ ii ) {
+            FillGlobalConfig::MoveConfig* moveConfig = arr[ii] ;
+            if( moveConfig==nullptr ) continue ;
+            //中盘子横
+            for(int imid = 0 ; imid < moveConfig->_midPlateCntH ; ++ imid ) {
+                PlateIdAndLayerCnt palc ;
+                palc._layerCnt = moveConfig->_nLayer ;
+                palc._plateId = midPlateH->_id ;
+                palc._moveId = ii+1 ;//第一行为1,第二行为2
+                results.push_back(palc) ;
+                retResidueEquvSmlJewelCnt -= midPlateH->_bigJewCap*4*palc._layerCnt ;
+            }
+            //中盘子竖
+            for(int imid = 0 ; imid < moveConfig->_midPlateCntV ; ++ imid ) {
+                PlateIdAndLayerCnt palc ;
+                palc._layerCnt = moveConfig->_nLayer ;
+                palc._plateId = midPlateV->_id ;
+                palc._moveId = ii+1 ;
+                results.push_back(palc) ;
+                retResidueEquvSmlJewelCnt -= midPlateV->_bigJewCap*4*palc._layerCnt ;
+            }
+            //大盘子横
+            for(int ibig = 0 ; ibig < moveConfig->_bigPlateCntH ; ++ ibig ) {
+                PlateIdAndLayerCnt palc ;
+                palc._layerCnt = moveConfig->_nLayer ;
+                palc._plateId = bigPlateH->_id ;
+                palc._moveId = ii+1 ;
+                results.push_back(palc) ;
+                retResidueEquvSmlJewelCnt -= bigPlateH->_bigJewCap*4*palc._layerCnt ;
+            }
+            //大盘子竖
+            for(int ibig = 0 ; ibig < moveConfig->_bigPlateCntV ; ++ ibig ) {
+                PlateIdAndLayerCnt palc ;
+                palc._layerCnt = moveConfig->_nLayer ;
+                palc._plateId = bigPlateV->_id ;
+                palc._moveId = ii+1 ;
+                results.push_back(palc) ;
+                retResidueEquvSmlJewelCnt -= bigPlateV->_bigJewCap*4*palc._layerCnt ;
+            }
+        }
+        return results ;
+}
+
+
+void LevelGenerate::computePlateCount(const FillGlobalConfig::MultiMoveConfig* mmc ,int& retBigPlateCnt, int& retMidPlateCnt )
+{
+    if( mmc==nullptr ) {
+        retBigPlateCnt = 9 ;
+        retMidPlateCnt = 0 ;
+    }else
+    if( mmc->_first && mmc->_second ) {
+        if( mmc->_first->_type.find("A") != string::npos &&  mmc->_second->_type.compare("A") != string::npos ) {
+            retBigPlateCnt = 6 ;
+            retMidPlateCnt = 8 ;
+        }else if( mmc->_first->_type.compare("B") != string::npos &&  mmc->_second->_type.compare("B") != string::npos ) {
+            retBigPlateCnt = 9 ;
+            retMidPlateCnt = 0 ;
+        }else if( mmc->_first->_type.compare("C") != string::npos &&  mmc->_second->_type.compare("C") != string::npos ) {
+            retBigPlateCnt = 11 ;
+            retMidPlateCnt = 0 ;
+        }else if( mmc->_first->_type.compare("B") != string::npos &&  mmc->_second->_type.compare("A") != string::npos) {
+            retBigPlateCnt = 6 ;
+            retMidPlateCnt = 7 ;
+        }else if( mmc->_first->_type.compare("C") != string::npos &&  mmc->_second->_type.compare("B") != string::npos ) {
+            retBigPlateCnt = 10 ;
+            retMidPlateCnt = 0 ;
+        }else if( mmc->_first->_type.compare("C") != string::npos &&  mmc->_second->_type.compare("A") != string::npos ) {
+            retBigPlateCnt = 7 ;
+            retMidPlateCnt = 7 ;
+        }
+        
+    }else if( mmc->_first && mmc->_second==nullptr ) {
+        if( mmc->_first->_type.compare("A") != string::npos  ) {
+            retBigPlateCnt = 6 ;
+            retMidPlateCnt = 7 ;
+        }else if( mmc->_first->_type.compare("B") != string::npos ) {
+            retBigPlateCnt = 9 ;
+            retMidPlateCnt = 0 ;
+        }else if( mmc->_first->_type.compare("C") != string::npos ) {
+            retBigPlateCnt = 10 ;
+            retMidPlateCnt = 0 ;
+        }
+    }else {
+        retBigPlateCnt = 9 ;
+        retMidPlateCnt = 0 ;
+    }
+    
+}

+ 20 - 5
auto_fill_jewel_v3/LevelGenerate.hpp

@@ -22,9 +22,10 @@ struct LevelGenerate {
 public:
     //盘子ID和层数
     struct PlateIdAndLayerCnt {
-        PlateIdAndLayerCnt():_plateId(-1),_layerCnt(0){}
+        PlateIdAndLayerCnt():_plateId(-1),_layerCnt(0),_moveId(0){}
         int _plateId ;
         int _layerCnt ;
+        int _moveId ;//0-静态盘子  1-第一行移动盘子  2-第二行移动盘子
     } ;
     
 public:
@@ -33,13 +34,18 @@ public:
     void setSeed(int s) { _seed = s  ;}
     int getSeed() { return _seed ; }
     
+    void generateAll(FillGlobalConfig::LevelData levelData, string outname) ;
+    
     //盘子和宝石的组合有解返回true,反之返回false
     bool generate( FillGlobalConfig::LevelData levelData,
                   const bool isMovable,
-                  vector<tuple<int,vector<vector<FillResult>>>>& resultPlateFillResults,
+                  const FillGlobalConfig::MultiMoveConfig* mulMoveConfig , //移动盘子配置,如果isMovable为false,这个参数忽略,可以传入nullptr
+                  vector<PlateFillResult>& resultPlateFillResults,
                   vector<ContourData::Point>& resultPlateCenterPointArr
                   ) ;
     
+    
+    
 private:
     int _seed ;
     
@@ -49,8 +55,13 @@ private:
     /// @return 返回盘子和层数的组合
     vector<PlateIdAndLayerCnt> generatePlateTypeAndLayerCnts(const int difficulty,const int totEquvSmallJewelCnt);
     
+    //根据难度数据和宝石总数计算需要使用的每个盘子的个数
     //考虑可移动关卡的条件
-    vector<PlateIdAndLayerCnt> generatePlateTypeAndLayerCnts2(const bool isMovable,const int difficulty,const int totEquvSmallJewelCnt);
+    vector<PlateIdAndLayerCnt> generatePlateTypeAndLayerCnts2(const bool isMovable,const FillGlobalConfig::MultiMoveConfig* mulMoveConfig,const int difficulty,const int totEquvSmallJewelCnt);
+    
+    //生成针对可移动盘子的组合
+    //残余等效小宝石数量返回到 residueEquvSmlJewelCnt 里面
+    vector<PlateIdAndLayerCnt> generatePlateTypeAndLayerCntsForMovablePlates(const FillGlobalConfig::MultiMoveConfig* mulMoveConfig,const int totEquvSmallJewelCnt, int& retResidueEquvSmlJewelCnt );
     
    
     
@@ -80,10 +91,14 @@ private:
     unordered_map<int, int> findUnfilledInStorages(unordered_map<int,tuple<int,int>>& jewStorages);
     
     /// 整理结果数据为 关卡-盘子-层-宝石的层次结构
-    void regroupPlateLyrFillResults( vector<tuple<int,int,vector<FillResult>>>& pidlyrFillArray, vector<tuple<int,vector<vector<FillResult>>>>& results ) ;
+    void regroupPlateLyrFillResults( vector<tuple<int,int,vector<FillResult>,int>>& pidlyrFillMvidArray, vector<PlateFillResult>& results ) ;
     
     /// 摆放盘子
-    void placePlates(const bool movable, const vector<int>& plateIdArr, vector<ContourData::Point>& resultPlateCenterPointArr ) ;
+    void placePlates(const bool movable, const FillGlobalConfig::MultiMoveConfig* mulMoveConfig, const vector<PlateFillResult>& plateFRArr, vector<ContourData::Point>& resultPlateCenterPointArr ) ;
+    
+    
+    // 根据移动盘子情况计算关卡大盘子数量和中盘子数量,该数字包含移动盘子和非移动盘子。
+    void computePlateCount(const FillGlobalConfig::MultiMoveConfig* mmc ,int& retBigPlateCnt, int& retMidPlateCnt ) ;
     
 } ;
 

+ 165 - 0
auto_fill_jewel_v3/LevelOutputWriter.cpp

@@ -0,0 +1,165 @@
+//
+//  LevelOutputWriter.cpp
+//  auto_fill_jewel_v3
+//
+//  Created by Red on 2024/12/10.
+//
+
+#include "LevelOutputWriter.hpp"
+#include <fstream>
+using std::ofstream;
+
+using namespace ArduinoJson;
+
+int LevelOutputWriter::getGidByFileName(JsonArray &jTileSets, FillGlobalConfig::PlateItem* plate) {
+    static int gid = 0;
+    for (int i = 0; i < jTileSets.size(); i++){
+        string jsonImgName = jTileSets[i]["image"].as<char*>()  ;
+        if ( jsonImgName.compare( plate->_pngName ) == 0 ){
+            return static_cast<int>(jTileSets[i]["firstgid"]);
+        }
+    }
+    gid ++;
+    JsonObject& obj = jTileSets.createNestedObject();
+    obj["firstgid"] = gid;
+    obj["image"] = plate->_pngName;
+    obj["imageheight"] = plate->_bbhei;
+    obj["imagewidth"] = plate->_bbwid;
+    obj["margin"] = 0;
+    obj["name"] = plate->_name;
+    obj.createNestedObject("properties");
+    obj["spacing"] = 0;
+    obj["tileheight"] = plate->_bbhei;
+    obj["tilewidth"] = plate->_bbwid;
+    return gid;
+}
+
+bool LevelOutputWriter::writeLevelJson(vector<PlateFillResult>& plateFillResults,
+                                       vector<ContourData::Point>& plateCenterPointArr,
+                                          string outfilename)
+{
+    auto fgc = FillGlobalConfig::getInstance() ;
+    int totalJewelCnt = 0 ;
+    for(auto itp = plateFillResults.begin();itp!=plateFillResults.end();++itp) {
+        int plateId = itp->_plateTypeId ;
+        vector<vector<FillResult>>& frArr = itp->_layersFillResults ;
+        for(auto itfr = frArr.begin(); itfr!=frArr.end();++itfr ) {
+            totalJewelCnt+=itfr->size() ;
+        }
+    }
+    DynamicJsonBuffer jsonBufferTiled;
+    DynamicJsonBuffer jsonBuffer;
+    
+    JsonObject& tiledRoot = jsonBufferTiled.createObject();
+    JsonObject& root = jsonBuffer.createObject();
+    
+    tiledRoot["height"] = 18;
+    
+    JsonArray& jLayerArr = tiledRoot.createNestedArray("layers");
+    JsonObject& lyrObj = jLayerArr.createNestedObject();
+    lyrObj["draworder"] = "topdown";
+    lyrObj["height"] = 18;
+    lyrObj["name"] = "Layer";
+    JsonArray& jObjArr = lyrObj.createNestedArray("objects"); //盘子信息
+    lyrObj["opacity"] = 1;
+    lyrObj["type"] = "objectgroup";
+    lyrObj["visible"] = true;
+    lyrObj["width"] = 16;
+    lyrObj["x"] = 0;
+    lyrObj["y"] = 0;
+    tiledRoot["nextobjectid"] = 4;
+    tiledRoot["orientation"] = "orthogonal";
+    tiledRoot.createNestedArray("properties");
+    tiledRoot["renderorder"] = "right-down";
+    tiledRoot["tileheight"] = 40;
+    JsonArray& jTileSets = tiledRoot.createNestedArray("tilesets"); //盘子纹理图
+    tiledRoot["tilewidth"] = 40;
+    tiledRoot["version"] = 1;
+    tiledRoot["width"] = 16;
+    
+    root.createNestedObject("target");//not used.
+    root["bgtem"] = "no"; //背景redream文件
+    root["jewel_count"] = totalJewelCnt ;
+    JsonArray& jplatesArr = root.createNestedArray("plates");
+    int ids = 1;
+    for (int i = 0; i < plateFillResults.size(); i ++){
+        auto itp = plateFillResults.begin() ;
+        std::advance(itp, i) ;
+        int plateId = itp->_plateTypeId ;
+        
+        JsonObject& tiledPlateObj = jObjArr.createNestedObject();
+        tiledPlateObj["gid"] = getGidByFileName(jTileSets, fgc->getPlateItemById(plateId));
+        tiledPlateObj["height"] = 0;
+        tiledPlateObj["id"] = ids;
+        tiledPlateObj["name"] = "plate";
+        tiledPlateObj.createNestedObject("properties");
+        tiledPlateObj["rotation"] = 0;
+        tiledPlateObj["type"] = "";
+        tiledPlateObj["visible"] = true;
+        tiledPlateObj["width"] = 0;
+        float platex = plateCenterPointArr[i].x;
+        float platey = plateCenterPointArr[i].y;
+        tiledPlateObj["x"] = platex - fgc->getPlateItemById(plateId)->_bbwid / 2;
+        tiledPlateObj["y"] = platey - fgc->getPlateItemById(plateId)->_bbhei / 2;
+        
+        vector<vector<FillResult>>& frArr =  itp->_layersFillResults ;
+        
+        JsonObject& plateObj = jplatesArr.createNestedObject();
+        plateObj["typeId"] = plateId ;
+        plateObj["plateId"] = ids++;
+        plateObj["x"] = platex;
+        plateObj["y"] = platey;
+        plateObj["sprite_frame_name"] = fgc->getPlateItemById(plateId)->_pngName;
+        plateObj["sizeX"] = fgc->getPlateItemById(plateId)->_bbwid;
+        plateObj["sizeY"] = fgc->getPlateItemById(plateId)->_bbhei;
+        plateObj["move"] = itp->_moveId ;
+        JsonArray& layerArr = plateObj.createNestedArray("layers");
+        for(int il = 0 ; il < frArr.size() ; ++il ) {
+            vector<FillResult>& frs = frArr[il];
+            JsonObject& lyrObj = layerArr.createNestedObject();
+            lyrObj["rotate"] = 0.0;
+            lyrObj["scale_x"] = 1.0;
+            lyrObj["scale_y"] = 1.0;
+            lyrObj["scale"] = 1.0;
+            lyrObj["sprite_frame_name"] = fgc->getPlateItemById(plateId)->_pngName;
+            lyrObj["lyrId"] = ids++;
+            lyrObj["zorder"] = il+1;
+            
+            JsonArray& posArr = lyrObj.createNestedArray("position");
+            posArr.add(platex);
+            posArr.add(platey);
+            JsonArray& screwArr = lyrObj.createNestedArray("screws");
+            for(int ij = 0 ;ij < frs.size();++ ij ) {
+                FillResult& fr = frs[ij];
+                FillGlobalConfig::JewelItem* jewPtr = fgc->getJewelItemById(fr._jewelTypeId);
+                if( fr._jewelTypeId>=0 ) {  // -1 is removed.
+                    JsonObject& jobj = screwArr.createNestedObject();
+                    jobj["rotate"] = fr._rotdeg;
+                    jobj["scale_x"] = jewPtr->_scale;
+                    jobj["scale_y"] = jewPtr->_scale;
+                    jobj["csx"] = 0;
+                    jobj["csy"] = 0;
+                    jobj["scale"] = jewPtr->_scale;
+                    jobj["sprite_frame_name"] = jewPtr->_pngName;
+                    jobj["screwId"] = ids++;
+                    jobj["typeId"] = fr._jewelTypeId ;
+                    JsonArray& posArr2 = jobj.createNestedArray("position");
+                    posArr2.add( fr._x ); // 钉子中心在游戏区域的坐标(原点左下角)
+                    posArr2.add( fr._y );
+                }
+            }
+        }
+    }
+    string jsonText ;
+    root.printTo(jsonText);
+    ofstream ofs( outfilename.c_str() );
+    if( ofs.good()==false ) return false;
+    ofs<<jsonText;
+    jsonText.clear();
+    tiledRoot.printTo(jsonText);
+    outfilename.insert(outfilename.size() - 5, "Tiled");
+    ofstream ofsTiled( outfilename.c_str() );
+    if( ofsTiled.good()==false ) return false;
+    ofsTiled<<jsonText;
+    return true;
+}

+ 37 - 0
auto_fill_jewel_v3/LevelOutputWriter.hpp

@@ -0,0 +1,37 @@
+//
+//  LevelOutputWriter.hpp
+//  auto_fill_jewel_v3
+//
+//  Created by Red on 2024/12/10.
+//
+
+#ifndef LevelOutputWriter_hpp
+#define LevelOutputWriter_hpp
+
+#include <stdio.h>
+#include <tuple>
+#include <vector>
+#include <string>
+#include "ajson5.hpp"
+
+using std::tuple;
+using std::vector;
+using std::string;
+
+#include "FillResult.hpp"
+#include "contourdata.h"
+#include "FillGlobalConfig.hpp"
+
+struct LevelOutputWriter {
+    
+    bool writeLevelJson(vector<PlateFillResult>& plateFillResults,
+                     vector<ContourData::Point>& plateCenterPointArr,
+                        string outfilename) ;
+    
+private:
+    
+    int getGidByFileName(ArduinoJson::JsonArray &jTileSets, FillGlobalConfig::PlateItem* plate) ;
+    
+} ;
+
+#endif /* LevelOutputWriter_hpp */

+ 0 - 100
auto_fill_jewel_v3/LevelThumbTool.cpp

@@ -1,100 +0,0 @@
-//
-//  LevelThumbTool.cpp
-//  auto_fill_jewel_v3
-//
-//  Created by Red on 2024/12/4.
-//
-
-#include "LevelThumbTool.hpp"
-#include <iostream>
-#include <sstream>
-using std::stringstream;
-using std::cout;
-using std::endl;
-
-void LevelThumbTool::makeThumb(string outpngname, vector<int>& plateIdArr,vector<ContourData::Point>& platePosiArr )
-{
-    int visWid = 640 ;
-    int visHei = 720 ;
-    int canvasWid = visWid+270 ;
-    int canvasHei = visHei+100 ;
-    
-    int x0 = 20 ;
-    int y0 = 50 ;
-    
-    cv::Mat levelimage = cv::Mat::ones( canvasHei, canvasWid , CV_8UC3 );
-    cv::Point p0(x0 ,y0 ) ;
-    cv::Point p1(x0+visWid,y0+visHei) ;
-    cv::rectangle(levelimage, p0, p1, cv::Scalar(191,191,191),2,cv::LINE_8) ;
-    for(int ip = 0 ; ip < plateIdArr.size(); ++ ip ) {
-        FillGlobalConfig::PlateItem* plate = FillGlobalConfig::getInstance()->getPlateItemById(plateIdArr[ip]) ;
-        string pngName = string("./轮廓/sg_盘子轮廓/")+ plate->_pngName ;
-        cv::Mat png = cv::imread(pngName) ;
-        
-        int x = platePosiArr[ip].x ;
-        int y = platePosiArr[ip].y ;
-        y = 720 - y ; //左下坐标转左上坐标
-        x+=x0;
-        y+=y0;
-        int wid = plate->_bbwid ;
-        int hei = plate->_bbhei ;
-        x = x-wid/2 ;
-        y = y-hei/2 ;
-        //cout<<x<<","<<y<<","<<wid<<","<<hei <<" canvas " << levelimage.rows <<"," << levelimage.cols <<endl;
-        png.copyTo( levelimage(cv::Rect(x,y,wid,hei)) ) ;
-    }
-    cv::imwrite(outpngname, levelimage) ;
-}
-
-void LevelThumbTool::drawthumb(
-                               string outname ,
-                               vector<tuple<int,vector<vector<FillResult>>>>& fillResultsArr,
-                               vector<ContourData::Point>& posiArr ) {
-    vector<int> plateIdArr ;
-    for(int ip = 0 ; ip < fillResultsArr.size();++ip ) {
-        plateIdArr.push_back( std::get<0>(fillResultsArr[ip]) ) ;
-    }
-    makeThumb(outname, plateIdArr, posiArr) ;
-}
-
-
-void LevelThumbTool::makeThumb2(string outpngname, vector<tuple<int,vector<vector<FillResult>>>>& fillResultsArr,vector<ContourData::Point>& platePosiArr )
-{
-    int visWid = 640 ;
-    int visHei = 720 ;
-    int canvasWid = visWid+270 ;
-    int canvasHei = visHei+100 ;
-    
-    int x0 = 20 ;
-    int y0 = 50 ;
-    
-    cv::Mat levelimage = cv::Mat::ones( canvasHei, canvasWid , CV_8UC3 );
-    cv::Point p0(x0 ,y0 ) ;
-    cv::Point p1(x0+visWid,y0+visHei) ;
-    cv::rectangle(levelimage, p0, p1, cv::Scalar(191,191,191),2,cv::LINE_8) ;
-    for(int ip = 0 ; ip < fillResultsArr.size(); ++ ip ) {
-        int plateTypeId = std::get<0>( fillResultsArr[ip] ) ;
-        int nlyr = (int) std::get<1>( fillResultsArr[ip] ).size() ;
-        
-        FillGlobalConfig::PlateItem* plate = FillGlobalConfig::getInstance()->getPlateItemById(plateTypeId) ;
-        string pngName = string("./轮廓/sg_盘子轮廓/")+ plate->_pngName ;
-        cv::Mat png = cv::imread(pngName) ;
-        
-        int x = platePosiArr[ip].x ;
-        int y = platePosiArr[ip].y ;
-        y = 720 - y ; //左下坐标转左上坐标
-        x+=x0;
-        y+=y0;
-        int wid = plate->_bbwid ;
-        int hei = plate->_bbhei ;
-        x = x-wid/2 ;
-        y = y-hei/2 ;
-        //cout<<x<<","<<y<<","<<wid<<","<<hei <<" canvas " << levelimage.rows <<"," << levelimage.cols <<endl;
-        png.copyTo( levelimage(cv::Rect(x,y,wid,hei)) ) ;
-        //写入层数
-        stringstream ss ;
-        ss<<" x"<<nlyr ;
-        cv::putText(levelimage, ss.str(), cv::Point(x+5,y+25), cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(103,101,238)) ;
-    }
-    cv::imwrite(outpngname, levelimage) ;
-}

+ 0 - 36
auto_fill_jewel_v3/LevelThumbTool.hpp

@@ -1,36 +0,0 @@
-//
-//  LevelThumbTool.hpp
-//  auto_fill_jewel_v3
-//
-//  Created by Red on 2024/12/4.
-// 生成关卡盘子摆放的缩略图
-
-#ifndef LevelThumbTool_hpp
-#define LevelThumbTool_hpp
-
-#include <stdio.h>
-#include <vector>
-using std::vector;
-#include "contourdata.h"
-#include "FillGlobalConfig.hpp"
-#include <opencv2/core.hpp>
-#include <opencv2/imgproc.hpp>
-#include <opencv2/highgui.hpp>
-#include "FillResult.hpp"
-#include <tuple>
-using std::tuple;
-
-struct LevelThumbTool {
-    
-    void drawthumb(
-                   string outname , // 输出png文件名
-                   vector<tuple<int,vector<vector<FillResult>>>>& fillResultsArr, // tuple<盘子类型ID, <层数组<一层填充结果>>>
-                   vector<ContourData::Point>& posiArr  //盘子中心坐标位置数组,坐标系原点在游戏区域左下角
-                   ) ;
-    //deprecated, use makeThumb2
-    void makeThumb(string outpngname, vector<int>& plateIdArr,vector<ContourData::Point>& platePosiArr ) ;
-    void makeThumb2(string outpngname, vector<tuple<int,vector<vector<FillResult>>>>& fillResultsArr,vector<ContourData::Point>& platePosiArr ) ;
-} ;
-
-
-#endif /* LevelThumbTool_hpp */

+ 7 - 172
auto_fill_jewel_v3/main.cpp

@@ -38,196 +38,31 @@
 #include <chrono>
 #include <tuple>
 #include <sstream>
-#include "LevelThumbTool.hpp"
+
 using namespace ArduinoJson;
 using namespace std;
 
 
-bool writeLevelJson(vector<tuple<int,vector<vector<FillResult>>>>& resultPlateFillResults,
-                    vector<ContourData::Point>& resultPlateCenterPointArr,
-                    string outfilename);
 
-int getGidByFileName(JsonArray *jTileSets, FillGlobalConfig::PlateItem* plate);
 
 int main(int argc, const char * argv[]) {
     cout<<"A program to generate level. 2024-11-23"<<endl;
     cout<<"usage:auto_fill_jewel_v3"<<endl;
     cout<<"version v3.0"<<endl ;
-//    if( argc!=2 ) {
-//        cout<<"缺少参数"<<endl;
-//        return 11 ;
-//    }
 
-    string outname = "测试关卡100.json" ;
+    
 
     FillGlobalConfig::getInstance() ; //inited
+    
+    FillGlobalConfig::LevelData* level100_1 = FillGlobalConfig::getInstance()->getLevelData(100, 0) ;
+
 
-    vector<tuple<int,vector<vector<FillResult>>>> resultPlateFillResults;
-    vector<ContourData::Point> resultPlateCenterPointArr;
 
     LevelGenerate genv3 ;
-    genv3.generate( *FillGlobalConfig::getInstance()->getLevelData(100, 0),
-                   false,
-                   resultPlateFillResults,
-                   resultPlateCenterPointArr
-                   ) ;
+    genv3.generateAll(*level100_1, "sg_level_out_100-1.json") ;
+
 
-    cout<<"write result to "<<outname<<endl;
-    writeLevelJson(resultPlateFillResults, resultPlateCenterPointArr, outname);
-    LevelThumbTool ltt ;
-    ltt.makeThumb2(outname+"_thumb.png", resultPlateFillResults, resultPlateCenterPointArr) ;
- 
     
     return 0;
 }
 
-int getGidByFileName(JsonArray &jTileSets, FillGlobalConfig::PlateItem* plate) {
-    static int gid = 0;
-    for (int i = 0; i < jTileSets.size(); i++){
-        if (jTileSets[i]["image"] == plate->_pngName){
-            return static_cast<int>(jTileSets[i]["firstgid"]);
-        }
-    }
-    gid ++;
-    JsonObject& obj = jTileSets.createNestedObject();
-    obj["firstgid"] = gid;
-    obj["image"] = plate->_pngName;
-    obj["imageheight"] = plate->_bbhei;
-    obj["imagewidth"] = plate->_bbwid;
-    obj["margin"] = 0;
-    obj["name"] = plate->_name;
-    obj.createNestedObject("properties");
-    obj["spacing"] = 0;
-    obj["tileheight"] = plate->_bbhei;
-    obj["tilewidth"] = plate->_bbwid;
-    return gid;
-}
-
-bool writeLevelJson(vector<tuple<int,vector<vector<FillResult>>>>& resultPlateFillResults,
-                 vector<ContourData::Point>& resultPlateCenterPointArr,
-                 string outfilename)
-{
-    auto fgc = FillGlobalConfig::getInstance() ;
-    int totalJewelCnt = 0 ;
-    for(auto itp = resultPlateFillResults.begin();itp!=resultPlateFillResults.end();++itp) {
-        int plateId = std::get<0>( *itp ) ;
-        vector<vector<FillResult>>& frArr = std::get<1>( *itp );
-        for(auto itfr = frArr.begin(); itfr!=frArr.end();++itfr ) {
-            totalJewelCnt+=itfr->size() ;
-        }
-    }
-    DynamicJsonBuffer jsonBufferTiled;
-    DynamicJsonBuffer jsonBuffer;
-    
-    JsonObject& tiledRoot = jsonBufferTiled.createObject();
-    JsonObject& root = jsonBuffer.createObject();
-    
-    tiledRoot["height"] = 18;
-    
-    JsonArray& jLayerArr = tiledRoot.createNestedArray("layers");
-    JsonObject& lyrObj = jLayerArr.createNestedObject();
-    lyrObj["draworder"] = "topdown";
-    lyrObj["height"] = 18;
-    lyrObj["name"] = "Layer";
-    JsonArray& jObjArr = lyrObj.createNestedArray("objects"); //盘子信息
-    lyrObj["opacity"] = 1;
-    lyrObj["type"] = "objectgroup";
-    lyrObj["visible"] = true;
-    lyrObj["width"] = 16;
-    lyrObj["x"] = 0;
-    lyrObj["y"] = 0;
-    tiledRoot["nextobjectid"] = 4;
-    tiledRoot["orientation"] = "orthogonal";
-    tiledRoot.createNestedArray("properties");
-    tiledRoot["renderorder"] = "right-down";
-    tiledRoot["tileheight"] = 40;
-    JsonArray& jTileSets = tiledRoot.createNestedArray("tilesets"); //盘子纹理图
-    tiledRoot["tilewidth"] = 40;
-    tiledRoot["version"] = 1;
-    tiledRoot["width"] = 16;
-    
-    root.createNestedObject("target");//not used.
-    root["bgtem"] = "no"; //背景redream文件
-    root["jewel_count"] = totalJewelCnt ;
-    JsonArray& jplatesArr = root.createNestedArray("plates");
-    int ids = 1;
-    for (int i = 0; i < resultPlateFillResults.size(); i ++){
-        auto itp = resultPlateFillResults.begin() ;
-        std::advance(itp, i) ;
-        int plateId = std::get<0>( *itp ) ;
-        
-        JsonObject& tiledPlateObj = jObjArr.createNestedObject();
-        tiledPlateObj["gid"] = getGidByFileName(jTileSets, fgc->getPlateItemById(plateId));
-        tiledPlateObj["height"] = 0;
-        tiledPlateObj["id"] = ids;
-        tiledPlateObj["name"] = "plate";
-        tiledPlateObj.createNestedObject("properties");
-        tiledPlateObj["rotation"] = 0;
-        tiledPlateObj["type"] = "";
-        tiledPlateObj["visible"] = true;
-        tiledPlateObj["width"] = 0;
-        float platex = resultPlateCenterPointArr[i].x;
-        float platey = resultPlateCenterPointArr[i].y;
-        tiledPlateObj["x"] = platex - fgc->getPlateItemById(plateId)->_bbwid / 2;
-        tiledPlateObj["y"] = platey - fgc->getPlateItemById(plateId)->_bbhei / 2;
-        
-        vector<vector<FillResult>>& frArr = std::get<1>( *itp );
-        
-        JsonObject& plateObj = jplatesArr.createNestedObject();
-        plateObj["typeId"] = plateId ;
-        plateObj["plateId"] = ids++;
-        plateObj["x"] = platex;
-        plateObj["y"] = platey;
-        plateObj["sprite_frame_name"] = fgc->getPlateItemById(plateId)->_pngName;
-        plateObj["sizeX"] = fgc->getPlateItemById(plateId)->_bbwid;
-        plateObj["sizeY"] = fgc->getPlateItemById(plateId)->_bbhei;
-        JsonArray& layerArr = plateObj.createNestedArray("layers");
-        for(int il = 0 ; il < frArr.size() ; ++il ) {
-            vector<FillResult>& frs = frArr[il];
-            JsonObject& lyrObj = layerArr.createNestedObject();
-            lyrObj["rotate"] = 0.0;
-            lyrObj["scale_x"] = 1.0;
-            lyrObj["scale_y"] = 1.0;
-            lyrObj["scale"] = 1.0;
-            lyrObj["sprite_frame_name"] = fgc->getPlateItemById(plateId)->_pngName;
-            lyrObj["lyrId"] = ids++;
-            lyrObj["zorder"] = il+1;
-            
-            JsonArray& posArr = lyrObj.createNestedArray("position");
-            posArr.add(platex);
-            posArr.add(platey);
-            JsonArray& screwArr = lyrObj.createNestedArray("screws");
-            for(int ij = 0 ;ij < frs.size();++ ij ) {
-                FillResult& fr = frs[ij];
-                FillGlobalConfig::JewelItem* jewPtr = fgc->getJewelItemById(fr._jewelTypeId);
-                if( fr._jewelTypeId>=0 ) {  // -1 is removed.
-                    JsonObject& jobj = screwArr.createNestedObject();
-                    jobj["rotate"] = fr._rotdeg;
-                    jobj["scale_x"] = jewPtr->_scale;
-                    jobj["scale_y"] = jewPtr->_scale;
-                    jobj["csx"] = 0;
-                    jobj["csy"] = 0;
-                    jobj["scale"] = jewPtr->_scale;
-                    jobj["sprite_frame_name"] = jewPtr->_pngName;
-                    jobj["screwId"] = ids++;
-                    jobj["typeId"] = fr._jewelTypeId ;
-                    JsonArray& posArr2 = jobj.createNestedArray("position");
-                    posArr2.add( fr._x ); // 钉子中心在游戏区域的坐标(原点左下角)
-                    posArr2.add( fr._y );
-                }
-            }
-        }
-    }
-    string jsonText ;
-    root.printTo(jsonText);
-    ofstream ofs( outfilename.c_str() );
-    if( ofs.good()==false ) return false;
-    ofs<<jsonText;
-    jsonText.clear();
-    tiledRoot.printTo(jsonText);
-    outfilename.insert(outfilename.size() - 5, "Tiled");
-    ofstream ofsTiled( outfilename.c_str() );
-    if( ofsTiled.good()==false ) return false;
-    ofsTiled<<jsonText;
-    return true;
-}

+ 113 - 0
测试数据/sg_gridbox_positions.csv

@@ -0,0 +1,113 @@
+类型名,移动码,大盘子序号,中盘子1序号,中盘子2序号,大盘子llx,大盘子lly,中盘子1llx,中盘子1lly,中盘子2llx,中盘子2lly
+A,1,-1,1,-1,0,0,644,219,0,0
+A,1,-1,2,-1,0,0,0,219,0,0
+A,1,-1,3,-1,0,0,215,219,0,0
+A,1,-1,4,-1,0,0,430,219,0,0
+A,0,-1,5,-1,0,0,0,357,0,0
+A,0,-1,6,-1,0,0,215,357,0,0
+A,0,-1,7,-1,0,0,430,357,0,0
+A,0,1,8,9,0,500,0,635,0,496
+A,0,2,10,11,215,500,215,635,215,496
+A,0,3,12,13,430,500,430,635,430,496
+A,0,4,14,15,0,-54,0,81,0,-58
+A,0,5,16,17,215,-54,215,81,215,-58
+A,0,6,18,19,430,-54,430,81,430,-58
+B,1,1,1,2,0,250,0,250,140,250
+B,1,2,3,4,370,250,370,250,510,250
+B,1,3,5,6,740,250,740,250,880,250
+B,0,4,7,8,0,500,0,635,0,496
+B,0,5,9,10,215,500,215,635,215,496
+B,0,6,11,12,430,500,430,635,430,496
+B,0,7,13,14,0,-54,0,81,0,-58
+B,0,8,15,16,215,-54,215,81,215,-58
+B,0,9,17,18,430,-54,430,81,430,-58
+C,1,1,1,2,0,223,0,220,0,358
+C,1,2,3,4,215,223,215,220,215,358
+C,1,3,5,6,430,223,430,220,430,358
+C,1,4,7,8,645,223,645,220,645,358
+C,0,5,9,10,0,500,0,635,0,496
+C,0,6,11,12,215,500,215,635,215,496
+C,0,7,13,14,430,500,430,635,430,496
+C,0,8,15,16,0,-54,0,81,0,-58
+C,0,9,17,18,215,-54,215,81,215,-58
+C,0,10,19,20,430,-54,430,81,430,-58
+AA,1,-1,1,-1,0,0,644,219,0,0
+AA,1,-1,2,-1,0,0,0,219,0,0
+AA,1,-1,3,-1,0,0,215,219,0,0
+AA,1,-1,4,-1,0,0,430,219,0,0
+AA,2,-1,5,-1,0,0,644,358,0,0
+AA,2,-1,6,-1,0,0,0,358,0,0
+AA,2,-1,7,-1,0,0,215,358,0,0
+AA,2,-1,8,-1,0,0,430,358,0,0
+AA,0,1,9,10,0,500,0,635,0,496
+AA,0,2,11,12,215,500,215,635,215,496
+AA,0,3,13,14,430,500,430,635,430,496
+AA,0,4,15,16,0,-54,0,81,0,-58
+AA,0,5,17,18,215,-54,215,81,215,-58
+AA,0,6,19,20,430,-54,430,81,430,-58
+BB,1,1,1,2,0,250,0,250,140,250
+BB,1,2,3,4,370,250,370,250,510,250
+BB,1,3,5,6,740,250,740,250,880,250
+BB,2,4,7,8,0,0,0,0,140,0
+BB,2,5,9,10,370,0,370,0,510,0
+BB,2,6,11,12,740,0,740,0,880,0
+BB,0,7,13,14,0,500,0,635,0,496
+BB,0,8,15,16,215,500,215,635,215,496
+BB,0,9,17,18,430,500,430,635,430,496
+CC,1,1,1,2,0,223,0,220,0,358
+CC,1,2,3,4,215,223,215,220,215,358
+CC,1,3,5,6,430,223,430,220,430,358
+CC,1,4,7,8,645,223,645,220,645,358
+CC,2,5,9,10,0,-54,0,81,0,-58
+CC,2,6,11,12,215,-54,215,81,215,-58
+CC,2,7,13,14,430,-54,430,81,430,-58
+CC,2,8,15,16,645,-54,645,81,645,-58
+CC,0,9,17,18,0,500,0,635,0,496
+CC,0,10,19,20,215,500,215,635,215,496
+CC,0,11,21,22,430,500,430,635,430,496
+BA,1,1,1,2,0,250,0,250,140,250
+BA,1,2,3,4,370,250,370,250,510,250
+BA,1,3,5,6,740,250,740,250,880,250
+BA,2,-1,7,-1,0,0,644,81,0,0
+BA,2,-1,8,-1,0,0,0,81,0,0
+BA,2,-1,9,-1,0,0,215,81,0,0
+BA,2,-1,10,-1,0,0,430,81,0,0
+BA,0,4,11,12,0,500,0,635,0,496
+BA,0,5,13,14,215,500,215,635,215,496
+BA,0,6,15,16,430,500,430,635,430,496
+BA,0,-1,17,-1,0,0,0,-61,0,0
+BA,0,-1,18,-1,0,0,215,-61,0,0
+BA,0,-1,19,-1,0,0,430,-61,0,0
+CB,1,1,1,2,0,223,0,220,0,358
+CB,1,2,3,4,215,223,215,220,215,358
+CB,1,3,5,6,430,223,430,220,430,358
+CB,1,4,7,8,645,223,645,220,645,358
+CB,2,5,9,10,0,0,0,0,140,0
+CB,2,6,11,12,370,0,370,0,510,0
+CB,2,7,13,14,740,0,740,0,880,0
+CB,0,8,15,16,0,500,0,635,0,496
+CB,0,9,17,18,215,500,215,635,215,496
+CB,0,10,19,20,430,500,430,635,430,496
+CA,1,1,1,2,0,223,0,220,0,358
+CA,1,2,3,4,215,223,215,220,215,358
+CA,1,3,5,6,430,223,430,220,430,358
+CA,1,4,7,8,645,223,645,220,645,358
+CA,2,-1,9,-1,0,0,644,81,0,0
+CA,2,-1,10,-1,0,0,0,81,0,0
+CA,2,-1,11,-1,0,0,215,81,0,0
+CA,2,-1,12,-1,0,0,430,81,0,0
+CA,0,5,13,14,0,500,0,635,0,496
+CA,0,6,15,16,215,500,215,635,215,496
+CA,0,7,17,18,430,500,430,635,430,496
+CA,0,-1,19,-1,0,0,0,-61,0,0
+CA,0,-1,20,-1,0,0,215,-61,0,0
+CA,0,-1,21,-1,0,0,430,-61,0,0
+NN,0,1,1,2,0,223,0,220,0,358
+NN,0,2,3,4,215,223,215,220,215,358
+NN,0,3,5,6,430,223,430,220,430,358
+NN,0,4,7,8,0,500,0,635,0,496
+NN,0,5,9,10,215,500,215,635,215,496
+NN,0,6,11,12,430,500,430,635,430,496
+NN,0,7,13,14,0,-54,0,81,0,-58
+NN,0,8,15,16,215,-54,215,81,215,-58
+NN,0,9,17,18,430,-54,430,81,430,-58

+ 1 - 1
测试数据/sg_level_data.csv

@@ -8,7 +8,7 @@
 100,1,2,5,MD,6
 100,1,2,8,MD,6
 100,1,2,11,MD,6
-100,1,2,14,MD,6
+100,1,2,15,BG,6
 
 1,1,0,2,MD,1
 1,1,0,4,SM,1

+ 22 - 0
测试数据/sg_move_config_data.csv

@@ -0,0 +1,22 @@
+唯一编号,类型,所需最少等效小宝石数量,层数,水平中盘子个数,垂直中盘子个数,水平大盘子个数,垂直大盘子个数,难度系数
+1,A11,48,1,4,0,0,0,3
+2,A12,96,2,4,0,0,0,6
+3,A13,144,3,4,0,0,0,9
+4,B12,144,2,0,6,0,0,12
+5,B13,216,3,0,6,0,0,18
+6,B22,144,2,0,4,1,0,10
+7,B23,216,3,0,4,1,0,15
+8,B32,144,2,0,2,2,0,8
+9,B33,216,3,0,2,2,0,12
+10,B42,144,2,0,0,3,0,6
+11,B43,216,3,0,0,3,0,9
+12,C12,192,2,8,0,0,0,16
+13,C13,288,3,8,0,0,0,24
+14,C22,192,2,6,0,0,1,14
+15,C23,288,3,6,0,0,1,21
+16,C32,192,2,4,0,0,2,12
+17,C33,288,3,4,0,0,2,18
+18,C42,192,2,2,0,0,3,10
+19,C43,288,3,2,0,0,3,15
+20,C52,192,2,0,0,0,4,8
+21,C53,288,3,0,0,0,4,12

+ 5 - 5
测试数据/sg_plate_items.csv

@@ -1,5 +1,5 @@
-盘子唯一编号,名称,尺寸,精灵图片名称,轮廓名称,有效区域左下x,有效区域左下y,有效区域右上x,有效区域右上y,大宝石容量,图片宽度,图片高度,备注
-1,大横,LG,pan-large_heng.png,pan-large_heng.png.json,5,14,264,205,6,270,210,no
-2,大竖,LG,pan-large_shu.png,pan-large_shu.png.json,5,14,204,265,6,210,270,no
-3,中,MD,pan-small.png,pan-small.png.json,5,8,204,129,3,210,135,no
-4,小,SM,pan-only1.png,pan-only1.png.json,5,14,104,113,1,110,118,no
+盘子唯一编号,名称,尺寸,精灵图片名称,轮廓名称,有效区域左下x,有效区域左下y,有效区域右上x,有效区域右上y,大宝石容量,图片宽度,图片高度,横放
+1,大横,LG,pan-large_heng.png,pan-large_heng.png.json,5,14,264,205,6,270,210,1
+2,大竖,LG,pan-large_shu.png,pan-large_shu.png.json,5,5,204,264,6,210,270,0
+3,中,MD,pan-small.png,pan-small.png.json,5,8,204,129,3,210,135,1
+4,中竖,MD,pan-small_shu.png,pan-small_shu.png.json,5,5,129,204,3,135,210,0

BIN
测试数据/轮廓/sg_盘子轮廓/.DS_Store


BIN
测试数据/轮廓/sg_盘子轮廓/pan-large_heng-inner.png


BIN
测试数据/轮廓/sg_盘子轮廓/pan-large_shu-inner.png


BIN
测试数据/轮廓/sg_盘子轮廓/pan-only1-inner.png


BIN
测试数据/轮廓/sg_盘子轮廓/pan-small-inner.png


BIN
测试数据/轮廓/sg_盘子轮廓/pan-small_shu-inner.png


BIN
测试数据/轮廓/sg_盘子轮廓/pan-small_shu.png


+ 1 - 0
测试数据/轮廓/sg_盘子轮廓/pan-small_shu.png.json

@@ -0,0 +1 @@
+[  [-59,99], [-59,-98], [61,-98], [61,99], [-59,99]  ]