Quellcode durchsuchen

refactor:每个关卡输入数据使用两个json。 feat:生成结果增加time字段、移动行方向字段。

wangfengdev vor 8 Monaten
Ursprung
Commit
d8f98d8512

+ 12 - 0
auto_fill_jewel_v3.xcodeproj/project.pbxproj

@@ -15,6 +15,8 @@
 		3BF911C52CFF032000E11762 /* LevelGenerate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BF911B92CFF032000E11762 /* LevelGenerate.cpp */; };
 		3BF911C62CFF032000E11762 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BF911BB2CFF032000E11762 /* main.cpp */; };
 		3BF911C72CFF032000E11762 /* RandomGridFiller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BF911BC2CFF032000E11762 /* RandomGridFiller.cpp */; };
+		9D2C4AF12D0BE5D8003FA290 /* levelinputdata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9D2C4AF02D0BE5D8003FA290 /* levelinputdata.cpp */; };
+		9D2C4AF22D0BE5D8003FA290 /* levelextinputdata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9D2C4AEE2D0BE5D8003FA290 /* levelextinputdata.cpp */; };
 		9D6F6AA12D08489E00ED1D61 /* LevelOutputWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9D6F6AA02D08489E00ED1D61 /* LevelOutputWriter.cpp */; };
 		9D6F6AA72D084C8200ED1D61 /* GridPositionTool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9D6F6AA42D084C8200ED1D61 /* GridPositionTool.cpp */; };
 /* End PBXBuildFile section */
@@ -80,6 +82,10 @@
 		3BF911BC2CFF032000E11762 /* RandomGridFiller.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RandomGridFiller.cpp; sourceTree = "<group>"; };
 		3BF911BD2CFF032000E11762 /* RandomGridFiller.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RandomGridFiller.hpp; sourceTree = "<group>"; };
 		3BF911BE2CFF032000E11762 /* SpriteData.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SpriteData.hpp; sourceTree = "<group>"; };
+		9D2C4AED2D0BE5D8003FA290 /* levelextinputdata.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = levelextinputdata.h; sourceTree = "<group>"; };
+		9D2C4AEE2D0BE5D8003FA290 /* levelextinputdata.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = levelextinputdata.cpp; sourceTree = "<group>"; };
+		9D2C4AEF2D0BE5D8003FA290 /* levelinputdata.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = levelinputdata.h; sourceTree = "<group>"; };
+		9D2C4AF02D0BE5D8003FA290 /* levelinputdata.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = levelinputdata.cpp; sourceTree = "<group>"; };
 		9D6F6A9F2D08489E00ED1D61 /* LevelOutputWriter.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LevelOutputWriter.hpp; sourceTree = "<group>"; };
 		9D6F6AA02D08489E00ED1D61 /* LevelOutputWriter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LevelOutputWriter.cpp; sourceTree = "<group>"; };
 		9D6F6AA22D084C7700ED1D61 /* ajson5.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ajson5.hpp; sourceTree = "<group>"; };
@@ -178,6 +184,10 @@
 		3BF911BF2CFF032000E11762 /* auto_fill_jewel_v3 */ = {
 			isa = PBXGroup;
 			children = (
+				9D2C4AED2D0BE5D8003FA290 /* levelextinputdata.h */,
+				9D2C4AEE2D0BE5D8003FA290 /* levelextinputdata.cpp */,
+				9D2C4AEF2D0BE5D8003FA290 /* levelinputdata.h */,
+				9D2C4AF02D0BE5D8003FA290 /* levelinputdata.cpp */,
 				9D6F6AA32D084C8200ED1D61 /* GridPositionTool.hpp */,
 				9D6F6AA42D084C8200ED1D61 /* GridPositionTool.cpp */,
 				9D6F6AA22D084C7700ED1D61 /* ajson5.hpp */,
@@ -279,6 +289,8 @@
 				3BF911C52CFF032000E11762 /* LevelGenerate.cpp in Sources */,
 				3BF911C72CFF032000E11762 /* RandomGridFiller.cpp in Sources */,
 				3BF911C12CFF032000E11762 /* BoxPositionTool.cpp in Sources */,
+				9D2C4AF12D0BE5D8003FA290 /* levelinputdata.cpp in Sources */,
+				9D2C4AF22D0BE5D8003FA290 /* levelextinputdata.cpp in Sources */,
 				3BF911C42CFF032000E11762 /* FillGlobalConfig.cpp in Sources */,
 				3BF911C62CFF032000E11762 /* main.cpp in Sources */,
 				3BF911C32CFF032000E11762 /* contourdatatools.cpp in Sources */,

+ 388 - 4
auto_fill_jewel_v3/LevelGenerate.cpp

@@ -13,7 +13,7 @@
 #include <tuple>
 #include "BoxPositionTool.hpp"
 #include "GridPositionTool.hpp"
-#include "LevelThumbTool.hpp"
+//#include "LevelThumbTool.hpp"
 
 #define MOVEID_NO_MOVE 0
 #define MOVEID_1ST_LINE 1
@@ -31,8 +31,8 @@ void LevelGenerate::generateAll(FillGlobalConfig::LevelData levelData, string ou
     generate(levelData, false, nullptr , resultPlateFRs, resultPlateCenterPosiArr) ;
     //输出结果
     string pngName0 = outname + "_000000.png" ;
-    LevelThumbTool ltt ;
-    ltt.makeThumb3(pngName0, resultPlateFRs, resultPlateCenterPosiArr) ;
+    //LevelThumbTool ltt ;
+    //ltt.makeThumb3(pngName0, resultPlateFRs, resultPlateCenterPosiArr) ;
     
     //对于所有移动的配置
     vector<FillGlobalConfig::MultiMoveConfig> mmcArr = fgc->getAllMoveConfigByJewels(levelData) ;
@@ -48,7 +48,7 @@ void LevelGenerate::generateAll(FillGlobalConfig::LevelData levelData, string ou
         //输出结果
         string pngName1 = outname + "_" + moveName;
         pngName1 += ".png" ;
-        ltt.makeThumb3(pngName1, resultPlateFRs1, resultPlateCenterPosiArr1) ;
+        //ltt.makeThumb3(pngName1, resultPlateFRs1, resultPlateCenterPosiArr1) ;
     }
     
     
@@ -215,6 +215,194 @@ bool LevelGenerate::generate( FillGlobalConfig::LevelData levelData,
     return true ;
 }
 
+
+bool LevelGenerate::generate_v2(
+                                const LevelInputData& indata,
+                                const LevelExtInputData& extdata,
+                                vector<PlateFillResult>& resultPlateFillResults,
+                                vector<ContourData::Point>& resultPlateCenterPointArr,
+                                string& error )
+{
+    FillGlobalConfig::getInstance()->clearGridBoxFilledStatus() ;
+    resultPlateFillResults.clear() ;
+    resultPlateCenterPointArr.clear() ;
+    
+    auto ms0 = std::chrono::system_clock::now().time_since_epoch() ;
+    uint64_t ms00 = std::chrono::duration_cast<chrono::milliseconds>(ms0).count() ;
+    
+    srand( extdata._seed );
+    unordered_map<int,tuple<int,int> > jewIdSzCntStorageMap ;// unordered_map<宝石ID,tuple<尺寸,库存数量> >
+    FillGlobalConfig* fgc = FillGlobalConfig::getInstance() ;
+    int bigJewelCnt = 0 ;
+    int midJewelCnt = 0;
+    int smlJewelCnt = 0 ;
+    for(int ij = 0 ; ij < indata._jewels.size()  ;++ ij ) {
+        int jewId = indata._jewels[ij]._jewelId ;
+        int   cnt = indata._jewels[ij]._count   ;
+        FillGlobalConfig::JewelItem* jewItem = fgc->getJewelItemById(jewId) ;
+        jewIdSzCntStorageMap[jewId] = {jewItem->_size,cnt} ;
+        if( jewItem->_size == FILLGLOBALCONFIG_JEWELSIZE_SM ) {
+            smlJewelCnt+=cnt ;
+        }else if( jewItem->_size == FILLGLOBALCONFIG_JEWELSIZE_MD ) {
+            midJewelCnt += cnt ;
+        }else if( jewItem->_size == FILLGLOBALCONFIG_JEWELSIZE_LG ) {
+            bigJewelCnt += cnt ;
+        }
+    }
+    //每个盘子类型小号宝石承载量
+    //按顺序分别为大和中
+    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 ) {
+        maxSmlJewCapPerPlate = fmax( usedPlates[ip]->_bigJewCap*4 , maxSmlJewCapPerPlate) ;
+        minSmlJewCapPerPlate = fmin( usedPlates[ip]->_bigJewCap*4 , minSmlJewCapPerPlate) ;
+    }
+    //等效小宝石数量
+    int effSmallJewelsCount = bigJewelCnt*4 + midJewelCnt*2 + smlJewelCnt ;
+    if( effSmallJewelsCount==0 ) {
+        error = "equv small jewel count is zero." ;
+        return false ;
+    }
+    
+    //构建符合难度条件的盘子和层数组合(包括可移动的盘子)
+    bool isMovable = (extdata._hasMovement==1)?true:false ;
+    FillGlobalConfig::MultiMoveConfig* mulMoveConfigPtr = nullptr  ;
+    FillGlobalConfig::MultiMoveConfig mulMoveConfig;
+    if( isMovable ) {
+        vector<FillGlobalConfig::MoveConfig*> mc1arr = FillGlobalConfig::getInstance()->getMoveConfigDatasByTypeAndNLayers(extdata._firstMoveType, extdata._firstMoveLyrCnt) ;
+        if( mc1arr.size()==0 ) {
+            error = string("failed to get first move config data by move type '")+extdata._firstMoveType+"'." ;
+            return false ;
+        }
+        mulMoveConfig._first = mc1arr[0] ;
+        if( extdata._secondMoveType.compare("") != 0 ) {
+            vector<FillGlobalConfig::MoveConfig*> mc2arr = FillGlobalConfig::getInstance()->getMoveConfigDatasByTypeAndNLayers(extdata._secondMoveType, extdata._secondMoveLyrCnt) ;
+            if( mc2arr.size()==0 ) {
+                error = string("failed to get second move config data by move type '")+extdata._secondMoveType+"'." ;
+                return false ;
+            }
+            mulMoveConfig._second = mc2arr[0] ;
+        }else{
+            mulMoveConfig._second = nullptr ;
+        }
+        mulMoveConfigPtr = &mulMoveConfig;
+    }
+    
+    vector<PlateIdAndLayerCnt> resPlateNLyrCnt = generatePlateTypeAndLayerCnts3(
+                                                                                isMovable,
+                                                                                mulMoveConfigPtr,
+                                                                                indata._difficulty,
+                                                                                effSmallJewelsCount,
+                                                                                extdata
+                                                                                );
+    if( resPlateNLyrCnt.size()==0 ) {
+        error = string("failed on generatePlateTypeAndLayerCnts3.") ;
+        return false ;
+    }
+    
+    //计算盘子的最大层数
+    int maxLyrCnt = 0;
+    for(auto it = resPlateNLyrCnt.begin();it!=resPlateNLyrCnt.end();++it ) maxLyrCnt=fmax(maxLyrCnt,it->_layerCnt) ;
+    
+    //对每个盘子组合进行填充宝石
+    RandomGridFiller filler ;
+    filler._seed = extdata._seed  ;
+    srand(filler._seed);
+    
+    //构建每个盘子每个层的填充结果容器
+    vector< tuple<int,int,vector<FillResult>,int> > plateIdLyrFillResultsMoveIdArray ;// tuple<plateId, layerIndex, fillresults_array ,moveId>
+    
+    
+    //从顶到底部,逐层进行填充
+    int residues = 0 ;
+    for(int nlayer = maxLyrCnt; nlayer > 0 ; nlayer-- ) {
+        //cout<<"for layer "<<nlayer<<endl;
+        
+        //满足该层数的所有盘子ID
+        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 ;
+        for(int ip=0;ip<plateIdArr.size();++ip ) {
+            currLyrNeedEquvSmlCnt += fgc->getPlateItemById(plateIdArr[ip])->_bigJewCap*4 ;
+        }
+        //cout<<"layer need eqsmCnt "<<currLyrNeedEquvSmlCnt<<endl;
+        
+        //根据所需数量和所需求解百分比,计算从库存取出每类宝石多少个。
+        float solPercent = 0.7 ;//根据需求文档,每层可解百分比都是70%.
+        unordered_map<int, int> chosenJewIdNCnt = randPickJewels(currLyrNeedEquvSmlCnt, solPercent, jewIdSzCntStorageMap) ;
+        int choseEqSmCnt=0;
+        for(auto itp=chosenJewIdNCnt.begin();itp!=chosenJewIdNCnt.end();++itp) {
+            choseEqSmCnt += jewSz2SmlCnt( fgc->getJewelItemById(itp->first)->_size) * itp->second ;
+        }
+        //cout<<"layer choose eqsmCnt "<<choseEqSmCnt<<endl;
+        
+        //逐个盘子填充
+        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);
+        }
+        
+        //检查为该层挑选的宝石是不是全部填完了,如果没有填完还要还给库存,给下一轮填充用
+        residues = 0 ;
+        for(auto itjn = chosenJewIdNCnt.begin();itjn!=chosenJewIdNCnt.end();++itjn){
+            if( itjn->second>0 ) {
+                residues+=itjn->second;
+                std::get<1>(jewIdSzCntStorageMap[itjn->first]) += itjn->second ;//剩余的加回到库存里
+            }
+        }
+        //cout<<"layer chosen jewels residues "<<residues<<endl;
+    }
+    
+    //查看库存是否全部用掉了
+    if( residues>0 ) {
+        int extraPlateCnt = 0 ;
+        //剩了,添加一个小盘子和层,仍然剩了再继续加盘子加层
+        unordered_map<int, int> residueJewIdAndCnts = findUnfilledInStorages(jewIdSzCntStorageMap) ;
+        while( residueJewIdAndCnts.size()>0 ) {
+            //新建一个中盘子
+            extraPlateCnt++;
+            //cout<<"add extra plate "<< extraPlateCnt <<endl;
+            int plateId = midPlateH->_id ;
+            for(int ilyr = 1 ; ilyr <= maxLyrCnt; ++ ilyr ) {
+                vector<FillResult> frs = fillPlateOneLayer(filler, plateId, residueJewIdAndCnts) ;
+                plateIdLyrFillResultsMoveIdArray.push_back( {plateId,ilyr,frs, MOVEID_NO_MOVE } );
+                //cout<<"add extra lyr "<<endl;
+                if( residueJewIdAndCnts.size()==0 ) break ;
+            }
+            if( residueJewIdAndCnts.size()==0 ) break ;
+        }
+    }
+    //整理数据
+    regroupPlateLyrFillResults(plateIdLyrFillResultsMoveIdArray, resultPlateFillResults);
+    //摆放盘子
+    {
+        placePlates(isMovable,mulMoveConfigPtr, resultPlateFillResults, resultPlateCenterPointArr) ;
+    }
+    auto ms1 = std::chrono::system_clock::now().time_since_epoch() ;
+    uint64_t ms11 = std::chrono::duration_cast<chrono::milliseconds>(ms1).count() ;
+    cout<<"duration(ms): "<<ms11-ms00<<endl;
+    return true ;
+}
+
 // this method is deprecated, use generatePlateTypeAndLayerCnts2()
 [[deprecated]]
 vector<LevelGenerate::PlateIdAndLayerCnt> LevelGenerate::generatePlateTypeAndLayerCnts(const int difficulty,const int totEquvSmallJewelCnt)
@@ -434,6 +622,202 @@ vector<LevelGenerate::PlateIdAndLayerCnt> LevelGenerate::generatePlateTypeAndLay
 }
 
 
+
+vector<LevelGenerate::PlateIdAndLayerCnt> LevelGenerate::generatePlateTypeAndLayerCnts3(
+                                                                         const bool isMovable,
+                                                                         const FillGlobalConfig::MultiMoveConfig* mulMoveConfig,
+                                                                         const int difficulty,
+                                                                         const int totEquvSmallJewelCnt,
+                                                                         const LevelExtInputData& extdata )
+{
+    const int MID_PLATE_CAP = 12 ;//中盘子对小宝石的容量
+    //一个关卡最多出现大、中个数
+    int tempBigPlateCnt1 = 9  ;// 1个大盘子=2两个中盘子
+    int tempMidPlateCnt1 = 0 ;
+    computePlateCount(mulMoveConfig, tempBigPlateCnt1, tempMidPlateCnt1) ;
+    int equvMidPlateMaxCnt = tempBigPlateCnt1 * 2 + tempMidPlateCnt1 ;//大盘子数量换算成中盘子
+    
+    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) ;
+    
+    //如果是移动关卡,先构建移动盘子
+    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 ) {
+                equvMidPlateMaxCnt-=2 ;//减去移动的盘子
+            }else if(fgc->getPlateItemById(results[ir]._plateId)->_size == FILLGLOBALCONFIG_PLATESIZE_MD) {
+                equvMidPlateMaxCnt-=1 ;//减去移动的盘子
+            }
+        }
+    }
+    
+    // 本关需要多少个一层中盘子的总数量,
+    // 已经去掉了移动盘子所用的宝石(如果有移动盘子的话)
+    int equvMidPlateCntOneLyr = ceil( totEquvSmallJewelCnt2 * 1.0 / MID_PLATE_CAP) ;
+
+    int bigPlateCntL1 = 0 ;
+    int midPlateCntL1 = equvMidPlateCntOneLyr ;
+    if( extdata._bigPlatePercent > 0 ) {
+        float bigPercent = extdata._bigPlatePercent*1.0/(extdata._bigPlatePercent+extdata._midPlatePercent) ;
+        float midPercent = 1.0 - bigPercent ;
+        bigPlateCntL1 = ceil( equvMidPlateCntOneLyr * 1.0 / (2.0+midPercent/bigPercent) ) ;
+        midPlateCntL1 = fmax(0, equvMidPlateCntOneLyr - bigPlateCntL1*2 ) ;
+    }
+    
+    int layerCnt0 = 1 ;//盘子层数下限
+    int layerCnt1 = 3 ;//盘子层数上限
+    //计算可能的层取值范围
+    {
+        layerCnt0 = fmax(1 , totEquvSmallJewelCnt2 / (equvMidPlateCntOneLyr*MID_PLATE_CAP) ) ;
+        if( layerCnt0<3 ) layerCnt1 = 3 ;
+        else layerCnt1 = layerCnt0 + 1 ;
+    }
+    
+    int bigPlateCnt0 = bigPlateCntL1 * 1.0 / layerCnt1 ; //大盘子最少需要数量
+    int bigPlateCnt1 = bigPlateCntL1 * 1.0 / layerCnt0 + 1; //大盘子最多需要数量
+    int midPlateCnt0 = midPlateCntL1*1.0/layerCnt1 ;
+    int midPlateCnt1 = midPlateCntL1*1.0/layerCnt0 + 1 ;
+    
+    bool findFirstAvailableComb = false ;//第一个完全放入宝石的组合,用于当不满足任何条件时,至少返回一个可装入全部宝石的默认方案
+    vector<vector<LevelGenerate::PlateIdAndLayerCnt>> possibleResults ;//满足全部百分比要求的组合
+    vector<LevelGenerate::PlateIdAndLayerCnt> firstAvailablePlateComb ;//第一个满足全部宝石填充的组合,如果需要移动还要考虑最小移动盘子个数的条件
+    int firstAvailableLyrCnt = 0 ;
+    
+    //难度对盘子数量的限制                                 普通         难         极难
+    //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} } ;
+    //这个数据不需要了,通过extdata中直接获取这个数据。
+    
+    //穷举全部盘子和层的组合找到符合条件的组合
+    for(int ibig = bigPlateCnt0; ibig <= bigPlateCnt1; ++ ibig ) {
+        for(int imid = midPlateCnt0; imid <= midPlateCnt1 ; ++ imid ) {
+            int currEquvMidPlateCnt = imid+ibig*2 ;
+            if( currEquvMidPlateCnt==0 ) 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 ;
+                    }
+                }
+                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 ;
+                    }
+                }
+                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] ;
+                        firstAvailablePlateComb[it]._moveId = MOVEID_NO_MOVE ;
+                    }
+                }
+            }
+            //计算每个类型的层数
+            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();
+        for(int ip = 0 ; ip < possibleResults[randindex].size();++ ip ) {
+            results.push_back(possibleResults[randindex][ip]) ;
+        }
+    }else {
+        for(int ip = 0 ; ip < firstAvailablePlateComb.size();++ ip ) {
+            results.push_back(firstAvailablePlateComb[ip]) ;
+        }
+    }
+    return results ;//
+}
+
+
+
+
 //随机排序数组索引值
 vector<int> LevelGenerate::randIndices(const int count0 ) {
     int count = count0 ;

+ 12 - 0
auto_fill_jewel_v3/LevelGenerate.hpp

@@ -17,6 +17,8 @@ using std::string;
 #include "RandomGridFiller.hpp"
 #include <tuple>
 using std::tuple;
+#include "levelinputdata.h"
+#include "levelextinputdata.h"
 
 struct LevelGenerate {
 public:
@@ -44,6 +46,13 @@ public:
                   vector<ContourData::Point>& resultPlateCenterPointArr
                   ) ;
     
+    //将输入数据拆分成基本输入参数和扩展输入参数作为输入
+    bool generate_v2(
+                     const LevelInputData& indata,
+                     const LevelExtInputData& extdata,
+                     vector<PlateFillResult>& resultPlateFillResults,
+                     vector<ContourData::Point>& resultPlateCenterPointArr,
+                     string& error) ;
     
     
 private:
@@ -59,6 +68,9 @@ private:
     //考虑可移动关卡的条件
     vector<PlateIdAndLayerCnt> generatePlateTypeAndLayerCnts2(const bool isMovable,const FillGlobalConfig::MultiMoveConfig* mulMoveConfig,const int difficulty,const int totEquvSmallJewelCnt);
     
+    //相对于generatePlateTypeAndLayerCnts2通过随机数计算盘子数量, generatePlateTypeAndLayerCnts3使用输入盘子百分比计算盘子数量。
+    vector<PlateIdAndLayerCnt> generatePlateTypeAndLayerCnts3(const bool isMovable,const FillGlobalConfig::MultiMoveConfig* mulMoveConfig,const int difficulty,const int totEquvSmallJewelCnt,const LevelExtInputData& extdata );
+    
     //生成针对可移动盘子的组合
     //残余等效小宝石数量返回到 residueEquvSmlJewelCnt 里面
     vector<PlateIdAndLayerCnt> generatePlateTypeAndLayerCntsForMovablePlates(const FillGlobalConfig::MultiMoveConfig* mulMoveConfig,const int totEquvSmallJewelCnt, int& retResidueEquvSmlJewelCnt );

+ 8 - 2
auto_fill_jewel_v3/LevelOutputWriter.cpp

@@ -34,9 +34,12 @@ int LevelOutputWriter::getGidByFileName(JsonArray &jTileSets, FillGlobalConfig::
     return gid;
 }
 
-bool LevelOutputWriter::writeLevelJson(vector<PlateFillResult>& plateFillResults,
+bool LevelOutputWriter::writeLevelJson(
+                                       const LevelInputData& indata,
+                                       const LevelExtInputData& extdata,
+                                       vector<PlateFillResult>& plateFillResults,
                                        vector<ContourData::Point>& plateCenterPointArr,
-                                          string outfilename)
+                                       string outfilename)
 {
     auto fgc = FillGlobalConfig::getInstance() ;
     int totalJewelCnt = 0 ;
@@ -80,6 +83,9 @@ bool LevelOutputWriter::writeLevelJson(vector<PlateFillResult>& plateFillResults
     root.createNestedObject("target");//not used.
     root["bgtem"] = "no"; //背景redream文件
     root["jewel_count"] = totalJewelCnt ;
+    root["time"] = indata._time ;
+    root["first_move_direction"] = extdata._firstMoveDirection ;
+    root["second_move_direction"] = extdata._secondMoveDirection ;
     JsonArray& jplatesArr = root.createNestedArray("plates");
     int ids = 1;
     for (int i = 0; i < plateFillResults.size(); i ++){

+ 7 - 2
auto_fill_jewel_v3/LevelOutputWriter.hpp

@@ -21,11 +21,16 @@ using std::string;
 #include "FillResult.hpp"
 #include "contourdata.h"
 #include "FillGlobalConfig.hpp"
+#include "levelinputdata.h"
+#include "levelextinputdata.h"
 
 struct LevelOutputWriter {
     
-    bool writeLevelJson(vector<PlateFillResult>& plateFillResults,
-                     vector<ContourData::Point>& plateCenterPointArr,
+    bool writeLevelJson(
+                        const LevelInputData& indata,
+                        const LevelExtInputData& extdata,
+                        vector<PlateFillResult>& plateFillResults,
+                        vector<ContourData::Point>& plateCenterPointArr,
                         string outfilename) ;
     
 private:

+ 104 - 0
auto_fill_jewel_v3/levelextinputdata.cpp

@@ -0,0 +1,104 @@
+#include "levelextinputdata.h"
+using namespace ArduinoJson ;
+#include <fstream>
+using std::ifstream;
+using std::ofstream;
+
+LevelExtInputData::LevelExtInputData() {
+
+    _seed = 1234567 ;
+    _hasMovement = 0;
+
+    _firstMoveType = ""; // A1, B1,B2,B3, C1,C2...
+    _firstMoveLyrCnt = 0 ;
+    _firstMoveDirection = 0 ; //0-fromRight2Left  1-fromLeft2Right
+    _secondMoveType = "" ;
+    _secondMoveLyrCnt = 0 ;
+    _secondMoveDirection =0 ;
+
+    _bigPlatePercent = 50;   //不移动盘子中大盘子百分比
+    _midPlatePercent = 50;   //不移动盘子中中盘子百分比
+    _lyr1Percent = 0 ;         //不移动盘子中有一层的盘子百分比
+    _lyr2Percent = 100;        //不移动盘子中有两层的盘子百分比
+    _lyr3Percent = 0 ;        //不移动盘子中有三层的盘子百分比
+    _matchLyr1 = 70;       //第一层可配百分比,第一层是点击层、第二层是可见层、第三层为隐藏层
+    _matchLyr2 = 70;  //第二层可配百分比
+    _matchLyr3 = 70;    //第三层可配百分比
+
+}
+
+
+bool LevelExtInputData::readFromFile(string filename)
+{
+    *this = LevelExtInputData() ;
+
+    ifstream ifs( filename.c_str() ) ;
+    if( ifs.good() == false ) return false ;
+    DynamicJsonBuffer buffer ;
+    JsonObject& root = buffer.parse(ifs) ;
+
+    _seed = root["seed"].as<int>() ;
+    _hasMovement = root["HasMovement"].as<int>() ;
+    if( _hasMovement ) {
+        _firstMoveType = root["movement"]["firstType"].as<char*>() ; // A1, B1,B2,B3, C1,C2...
+        _firstMoveLyrCnt = root["movement"]["firstLyrCnt"].as<int>()  ;
+        _firstMoveDirection =root["movement"]["firstDirection"].as<int>() ; //0-fromRight2Left  1-fromLeft2Right
+        _secondMoveType = root["movement"]["secondType"].as<char*>()  ;
+        _secondMoveLyrCnt = root["movement"]["secondLyrCnt"].as<int>()  ;
+        _secondMoveDirection =root["movement"]["secondDirection"].as<int>()  ;
+    }
+
+
+    _bigPlatePercent = root["BigPlatePercent"].as<int>() ;   //不移动盘子中大盘子百分比
+    _midPlatePercent = root["MidPlatePercent"].as<int>() ;   //不移动盘子中中盘子百分比
+    _lyr1Percent = root["Lyr1Percent"].as<int>()  ;         //不移动盘子中有一层的盘子百分比
+    _lyr2Percent = root["Lyr2Percent"].as<int>() ;        //不移动盘子中有两层的盘子百分比
+    _lyr3Percent = root["Lyr3Percent"].as<int>()  ;        //不移动盘子中有三层的盘子百分比
+    _matchLyr1 = root["MatchLyr1"].as<int>() ;       //第一层可配百分比,第一层是点击层、第二层是可见层、第三层为隐藏层
+    _matchLyr2 = root["MatchLyr2"].as<int>() ;  //第二层可配百分比
+    _matchLyr3 = root["MatchLyr3"].as<int>() ;    //第三层可配百分比
+    ifs.close();
+    return true;
+}
+bool LevelExtInputData::writeToFile(string filename)
+{
+
+    DynamicJsonBuffer buffer ;
+    JsonObject& root = buffer.createObject() ;
+
+    root["seed"] = _seed ;
+    root["HasMovement"] = _hasMovement ;
+    JsonObject& movement = root.createNestedObject("movement") ;
+    movement["firstType"] = _firstMoveType ;
+    movement["firstLyrCnt"] = _firstMoveLyrCnt ;
+    movement["firstDirection"] = _firstMoveDirection ;
+    movement["secondType"] = _secondMoveType ;
+    movement["secondLyrCnt"] = _secondMoveLyrCnt ;
+    movement["secondDirection"] = _secondMoveDirection ;
+
+    root["BigPlatePercent"] = _bigPlatePercent   ;   //不移动盘子中大盘子百分比
+    root["MidPlatePercent"] = _midPlatePercent  ;   //不移动盘子中中盘子百分比
+    root["Lyr1Percent"] = _lyr1Percent   ;         //不移动盘子中有一层的盘子百分比
+    root["Lyr2Percent"] = _lyr2Percent;        //不移动盘子中有两层的盘子百分比
+    root["Lyr3Percent"] = _lyr3Percent;        //不移动盘子中有三层的盘子百分比
+    root["MatchLyr1"] = _matchLyr1   ;       //第一层可配百分比,第一层是点击层、第二层是可见层、第三层为隐藏层
+    root["MatchLyr2"] = _matchLyr2   ;  //第二层可配百分比
+    root["MatchLyr3"] = _matchLyr3 ;    //第三层可配百分比
+
+    string jsontext ;
+    root.printTo(jsontext) ;
+
+    ofstream ofs( filename.c_str() ) ;
+    if( ofs.good() == false ) return false ;
+    ofs<<jsontext ;
+    ofs.close() ;
+    return true ;
+}
+
+string LevelExtInputData::levelInputFilename2ExtFilename( string levelInputFilename )
+{
+    size_t dotpos = levelInputFilename.rfind('.') ;
+    if( dotpos==string::npos ) return "" ;
+    string res = levelInputFilename.substr(0, dotpos ) + "_ext.json";
+    return res ;
+}

+ 47 - 0
auto_fill_jewel_v3/levelextinputdata.h

@@ -0,0 +1,47 @@
+#ifndef LEVELEXTINPUTDATA_H
+#define LEVELEXTINPUTDATA_H
+
+//一个关卡扩展输入参数
+
+#include "ajson5.hpp"
+#include <string>
+#include <vector>
+
+using std::vector;
+using std::string;
+
+
+struct LevelExtInputData
+{
+public:
+    LevelExtInputData();
+    bool readFromFile(string filename) ;
+    bool writeToFile(string filename) ;
+
+
+    int _seed ;
+    int _hasMovement ;
+
+    string _firstMoveType ; // A1, B1,B2,B3, C1,C2...
+    int _firstMoveLyrCnt ;
+    int _firstMoveDirection; //0-fromRight2Left  1-fromLeft2Right
+    string _secondMoveType ;
+    int _secondMoveLyrCnt ;
+    int _secondMoveDirection ;
+
+    int _bigPlatePercent;   //不移动盘子中大盘子百分比
+    int _midPlatePercent;   //不移动盘子中中盘子百分比
+    int _lyr1Percent;         //不移动盘子中有一层的盘子百分比
+    int _lyr2Percent;        //不移动盘子中有两层的盘子百分比
+    int _lyr3Percent;        //不移动盘子中有三层的盘子百分比
+    int _matchLyr1;       //第一层可配百分比,第一层是点击层、第二层是可见层、第三层为隐藏层
+    int _matchLyr2;  //第二层可配百分比
+    int _matchLyr3;    //第三层可配百分比
+
+
+    //将关卡输入数据文件名转换为扩展参数文件名 Level9999_99.json  to Level9999_99_ext.json
+    string levelInputFilename2ExtFilename( string levelInputFilename ) ;
+
+};
+
+#endif // LEVELEXTINPUTDATA_H

+ 95 - 0
auto_fill_jewel_v3/levelinputdata.cpp

@@ -0,0 +1,95 @@
+#include "levelinputdata.h"
+
+using namespace ArduinoJson;
+#include <fstream>
+using std::ifstream;
+using std::ofstream;
+
+
+LevelInputData::LevelInputData() {
+    _id = -1 ;
+    _subid = -1 ;
+    _time = 0 ;
+    _difficulty = 0 ;//0-normal 1-hard 2-veryhard
+}
+
+
+bool LevelInputData::readFromFile(string filename)
+{
+    _jewels.clear() ;
+
+    ifstream ifs( filename.c_str() ) ;
+    if( ifs.good() == false ) return false ;
+    DynamicJsonBuffer buffer ;
+    JsonObject& root = buffer.parse(ifs) ;
+
+    _id = root["No"].as<int>() ;
+    _name =root["Name"].as<char*>() ;
+    if( _name.length()>8 ) {
+        _subid = atof( _name.substr(_name.length()-2).c_str() ) ;
+    }else{
+        _subid = 0 ;
+    }
+    if( root.containsKey("time") ) {
+        _time = root["time"].as<int>() ;
+    }else{
+        _time = root["Duration"].as<int>() ;
+    }
+     //如果没有time字段,那么读取Duration字段
+
+    _difficulty = root["Difficulty"].as<int>() ;//0-normal 1-hard 2-veryhard
+    JsonArray& goals = root["Goals"].as<JsonArray>() ;
+    JsonArray& board = root["Board"].as<JsonArray>() ;
+
+    for(int i = 0 ; i<goals.size();++ i ) {
+        JsonObject& obj1 = goals[i].as<JsonObject>() ;
+        JewelInputData jid ;
+        jid._jewelId = obj1["ItemType"].as<int>() ;
+        jid._count = obj1["Count"].as<int>() ;
+        _jewels.push_back(jid) ;
+    }
+    for(int i = 0 ; i<board.size();++ i ) {
+        JsonObject& obj1 = board[i].as<JsonObject>() ;
+        JewelInputData jid ;
+        jid._jewelId = obj1["ItemType"].as<int>() ;
+        jid._count = obj1["Count"].as<int>() ;
+        if( jid._jewelId > 0 ) { //随机宝石暂时不考虑,直接跳过
+            _jewels.push_back(jid) ;
+        }
+    }
+
+    return true ;
+}
+
+
+
+bool LevelInputData::writeToFile(string filename)
+{
+    DynamicJsonBuffer buffer ;
+    JsonObject& root = buffer.createObject() ;
+
+    root["No"] = _id  ;
+    root["Name"] = _name  ;
+    root["time"] = _time ;
+    root["Duration"] = _time ;
+    root["Difficulty"] = _difficulty ;
+
+    JsonArray& goals = root.createNestedArray("Goals");
+
+    JsonArray& board = root.createNestedArray("Board"); //just leave it empty.
+
+    for(int i = 0 ; i<_jewels.size();++ i ) {
+        JsonObject& obj1 = goals.createNestedObject();
+        obj1["ItemType"] = _jewels[i]._jewelId ;
+        obj1["Count"] = _jewels[i]._count ;
+    }
+
+    string jsontext ;
+    root.printTo(jsontext) ;
+
+    ofstream ofs( filename.c_str() ) ;
+    if( ofs.good() == false ) return false ;
+    ofs<<jsontext ;
+    ofs.close() ;
+    return true ;
+}

+ 37 - 0
auto_fill_jewel_v3/levelinputdata.h

@@ -0,0 +1,37 @@
+#ifndef LEVELINPUTDATA_H
+#define LEVELINPUTDATA_H
+
+//一个关卡的输入参数类
+
+#include "ajson5.hpp"
+#include <string>
+#include <vector>
+
+using std::vector;
+using std::string;
+
+
+struct LevelInputData
+{
+public:
+    LevelInputData();
+    bool readFromFile(string filename) ;
+    bool writeToFile(string filename) ;
+
+
+public:
+    struct JewelInputData {
+        int _jewelId ;
+        int _count ;//宝石个数
+    } ;
+
+public:
+    int _id ;
+    int _subid ;
+    string _name ;// 1997_A_01_01
+    int _time ;
+    int _difficulty ;//0-normal 1-hard 2-veryhard
+    vector<JewelInputData> _jewels ;
+};
+
+#endif // LEVELINPUTDATA_H

+ 195 - 12
auto_fill_jewel_v3/main.cpp

@@ -38,31 +38,214 @@
 #include <chrono>
 #include <tuple>
 #include <sstream>
-
+//#include "LevelThumbTool.hpp"
 using namespace ArduinoJson;
 using namespace std;
-
-
+#include "levelinputdata.h"
+#include "levelextinputdata.h"
+#include "LevelOutputWriter.hpp"
 
 
 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 ;
-
+    cout<<"usage:auto_fill_jewel_v3 level0001_01.json level0001_01_ext.json output.json"<<endl;
+    cout<<"version v3.3"<<endl ;
+    if( argc!=4 ) {
+        cout<<"缺少参数"<<endl;
+        return 11 ;
+    }
     
-
-    FillGlobalConfig::getInstance() ; //inited
+    string levelinFilename = argv[1] ;
+    string levelextFilename = argv[2] ;
+    string outputFilename = argv[3] ;
+    
+    LevelInputData levelInputData ;
+    LevelExtInputData levelExtData ;
     
-    FillGlobalConfig::LevelData* level100_1 = FillGlobalConfig::getInstance()->getLevelData(100, 0) ;
+    bool ok1 = levelInputData.readFromFile(levelinFilename);
+    bool ok2 = levelExtData.readFromFile(levelextFilename) ;
+    if( ok1==false ) {
+        cout<<"加载关卡输入参数失败 "<<levelinFilename <<endl;
+        return 12 ;
+    }
+    if( ok2==false ) {
+        cout<<"加载关卡扩展参数失败 "<<levelextFilename <<endl;
+        return 13 ;
+    }
+    FillGlobalConfig::getInstance() ; //初始化单例
+    LevelGenerate genv3 ;
+    vector<PlateFillResult>    resultPlateFillResults    ;
+    vector<ContourData::Point> resultPlateCenterPointArr ;
+    string errorStr ;
+    bool genok = genv3.generate_v2(levelInputData, levelExtData, resultPlateFillResults, resultPlateCenterPointArr,errorStr);
+    if( genok==false ) {
+        cout<<"Error: "<<errorStr<<endl;
+        return 14 ;
+    }
+    cout<<"write results"<<endl;
+    LevelOutputWriter writer ;
+    writer.writeLevelJson(levelInputData, levelExtData, resultPlateFillResults, resultPlateCenterPointArr, outputFilename) ;
+    cout<<"done"<<endl;
+    
+    return 0;
+}
+
 
 
 
-    LevelGenerate genv3 ;
-    genv3.generateAll(*level100_1, "sg_level_out_100-1.json") ;
 
 
+
+/*
+void plateInfo(vector<tuple<int,vector<vector<FillResult>>>>& result1,string& info ) {
+    auto fgc = FillGlobalConfig::getInstance() ;
+    int nbig=0;
+    int nmid = 0 ;
+    int nsml = 0 ;
+    stringstream ss ;
+    for(auto it = result1.begin(); it!= result1.end(); ++ it ) {
+        int plateId = std::get<0>( *it ) ;
+        vector<vector<FillResult>>& layers = std::get<1>(*it);
+        FillGlobalConfig::PlateItem* plate = fgc->getPlateItemById(plateId) ;
+        if( plate->_size == FILLGLOBALCONFIG_PLATESIZE_LG ){
+            nbig++;
+            ss<<"LG:"<<layers.size()<<";" ;
+        }
+        else if( plate->_size==FILLGLOBALCONFIG_PLATESIZE_MD ){
+            nmid++;
+            ss<<"MD:"<<layers.size()<<";" ;
+        }
+        else{
+            nsml++ ;
+            ss<<"SM:"<<layers.size()<<";" ;
+        }
+    }
+    stringstream ss2 ;
+    ss2<<"N-LG:"<<nbig<<"; N-MD:"<<nmid<<"; N-SM:"<<nsml<<"  "<<ss.str() ;
+    info = ss2.str() ;
+}
+*/
+
+/*
+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() ;
+        }
+    }
     
-    return 0;
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& root = jsonBuffer.createObject() ;
+    root.createNestedObject("target");//not used.
+    root["bgtem"] = "no"; //背景redream文件
+    root["jewel_count"] = totalJewelCnt ;
+    JsonArray& jplatesArr = root.createNestedArray("plates") ;
+    int ids = 1 ;
+    for(int ip = 0 ; ip < resultPlateFillResults.size() ; ++ ip ) {
+        JsonObject& plateObj = jplatesArr.createNestedObject() ;
+        auto itp = resultPlateFillResults.begin() ;
+        std::advance(itp, ip) ;
+        
+        int plateId = std::get<0>( *itp ) ;
+        vector<vector<FillResult>>& frArr = std::get<1>( *itp );
+        
+        float platex = resultPlateCenterPointArr[ip].x  ;
+        float platey = resultPlateCenterPointArr[ip].y  ;
+        
+        plateObj["typeId"] = plateId ;
+        plateObj["plateId"] = ids++ ;
+        // plateObj[""] = 
+        plateObj["x"] = platex ;  //这里是盘子中心在游戏区域的坐标(原点左下角)
+        plateObj["y"] = platey ;
+        plateObj["sprite_frame_name"] = fgc->getPlateItemById(plateId)->_pngName ;
+        
+        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;
+    return true ;
 }
+ */
+
+
+//测试输出结果到屏幕
+/*
+void testPrintPlateToScreenByOpenCV () {
+    if(false)
+    {//测试
+        
+        int iplate = 5 ;
+        int ilayer = 2 ;
+        
+        auto itPlate = resultPlateFillResults.begin() ;
+        std::advance(itPlate, iplate) ;
+        FillGlobalConfig::PlateItem* platePtr = FillGlobalConfig::getInstance()->getPlateItemById( std::get<0>(*itPlate) ) ;
+        vector<vector<FillResult>>& layersArr = std::get<1>(*itPlate);
+        cv::Mat plateimage = cv::Mat::zeros( platePtr->_bbwid, platePtr->_bbhei , CV_8UC3 );
+        for(int ibox = 0 ; ibox < layersArr[ilayer].size() ; ++ ibox ) {
+            FillResult& fr = layersArr[ilayer][ibox] ;
+            BoostGeometryTools::BoostPolygon poly1 = BoostGeometryTools::makeRotateNTranslateBox(-fr._width/2, -fr._height/2, fr._width, fr._height, fr._rotdeg, fr._x, fr._y) ;
+            vector<cv::Point> cvpoints ;
+            for(int ipt = 0 ;ipt < poly1.outer().size();++ ipt ) {
+                cv::Point p2 ;
+                p2.x = poly1.outer()[ipt].get<0>();
+                p2.y = poly1.outer()[ipt].get<1>() ;
+                cvpoints.push_back(p2) ;
+            }
+            cv::polylines(plateimage, cvpoints, true, cv::Scalar(rand()%255,rand()%255,rand()%255));
+        }
+        //
+        stringstream ss ;
+        ss<<"win"<<iplate<<"-"<<ilayer ;
+        cv::imshow( ss.str().c_str() , plateimage );
+        cv::waitKey();
+    }
+}
+ */
 

+ 1 - 0
测试数据/Level0003_01.json

@@ -0,0 +1 @@
+{"No":3,"Name":"3_A_01_01","time":240,"Duration":240,"Difficulty":0,"Goals":[{"ItemType":3,"Count":15},{"ItemType":6,"Count":12},{"ItemType":9,"Count":9},{"ItemType":12,"Count":9}],"Board":[]}

+ 1 - 0
测试数据/Level0003_01_ext.json

@@ -0,0 +1 @@
+{"seed":1784484492,"HasMovement":1,"movement":{"firstType":"A13","firstLyrCnt":3,"firstDirection":1,"secondType":"","secondLyrCnt":0,"secondDirection":0},"BigPlatePercent":24,"MidPlatePercent":76,"Lyr1Percent":0,"Lyr2Percent":0,"Lyr3Percent":100,"MatchLyr1":50,"MatchLyr2":70,"MatchLyr3":70}