///<summary>Loader of txt levelset (filename, simplified loading for fast update)</summary> public FunctionResult LoadTxtLevelSet(string sFileName, bool bSimplified) { System.IO.StreamReader hReader;//For reading file try { hReader = new System.IO.StreamReader(sFileName);//Open file } catch { return FunctionResult.FailedToOpenFile;//Unable } Reset();//Reset levelset string sLine, sTrim; string sLastNotEmpty=""; char[] sChars; int i,j; int iPos, iEnd; int iCommentPhase;//Comment phase: 0 - before comments block found, 1 - inside, 2 - after SokoCell uNewCell; try { sTitle = System.IO.Path.GetFileNameWithoutExtension(sFileName);//Assing initial title (would be replaced by title from file, if specified) } catch { return FunctionResult.FileSystemError;//Something very wrong with filename } ArrayList uXSizeList = new ArrayList(20); ArrayList uYSizeList = new ArrayList(20); ArrayList uPosList = new ArrayList(20); ArrayList uFileLines = new ArrayList(100); //Stag 1. Estimate level number and their sizes int iPhase = 0; iLevelsNum = 0; iCommentPhase = 0; int iX = 0; int iY = 0; while (!hReader.EndOfStream) //Read till end of file { try { sLine = hReader.ReadLine(); //Read one line of file } catch { return FunctionResult.ErrorOnReadingFile;//Something wrong with file } uFileLines.Add(sLine);//Store line into array sTrim = sLine.Trim(); if (sTrim.StartsWith("#")) {//All lines, that starts with # are lines of level board if (iPhase != 2) {//Level was not yet started iLevelsNum++;//Start level uPosList.Add(uFileLines.Count-1);//Remeber line, where level starts iPhase = 2;//Now we loading level iX = 0;//Initial sizes iY = 0; } iY++;//New line for level if (iX < sLine.Length) iX = sLine.Length;//Update width of level, if needed } else {//All lines, that not starts with # are delimits levels from each other if (iPhase == 0) { //Header of levelset try { //Try to get values from comments (comparisons should be in increment-len order, otherwise exception may occurs earlier, than line will be found) if (sTrim.Substring(0, 6) == "Name: ") { sTitle = sTrim.Substring(6); sTrim = ""; } else if (sTrim.Substring(0, 7) == "Title: ") { sTitle = sTrim.Substring(7); sTrim = ""; } else if (sTrim.Substring(0, 8) == "Author: ") { sAuthor = sTrim.Substring(8); sTrim = ""; } else if (sTrim.Substring(0, 8) == "Comment:") { sComment = ""; if (sTrim.Length > 8) sComment = sTrim.Substring(8).Trim();//Line contain text after "Comment:", so it is single-line comment else iCommentPhase = 1;//Line do not contain other text, so it is multi-line comment sTrim = ""; } else if (sTrim.Substring(0, 11) == "Copyright: ") { sCopyright = sTrim.Substring(11); sTrim = ""; } else if (sTrim.Substring(0, 11) == "Comment-End") {//End of multi-line comment iCommentPhase = 2; sTrim = ""; } } catch (ArgumentOutOfRangeException) { //If line is very short, substring will throw exception. We bury such exceptions here :) } if (iCommentPhase == 1) { //Multiline comment continuing if (sComment.Length > 0) sComment += "\r\n";//Not first line, add line delimiter sComment += sTrim; sTrim = ""; } if (sTrim.Length > 0) { //Line is still not identified if (iCommentPhase == 0) { //All lines before comment block could be comment if (sComment.Length > 0) sComment += "\r\n";//Not first line, add line delimiter sComment += sTrim; } sLastNotEmpty = sTrim;//Store line, it could be title for first level } } if (iPhase == 2) { //Level was ended uXSizeList.Add(iX);//Add sizes of level into arrays uYSizeList.Add(iY); iPhase = 3;//Now we waiting next level } } } if (iPhase == 2) { //Last level was not fully ended, end it here uXSizeList.Add(iX); uYSizeList.Add(iY); } hReader.Close();//Close file if (iLevelsNum == 0) return FunctionResult.NoLevelsFound;//No levels found in file //Stage 2. Loading all detected levels iLevelsAllocated = iLevelsNum;//Now we know exact number of levels uLevels = new SokobanLevel[iLevelsAllocated];//Allocate array of levels for (i = 0; i < iLevelsNum; i++) //Iterate for all levels { //Create new level with sizes, calculated in stage 1 uLevels[i] = new SokobanLevel((int)uXSizeList[i], (int)uYSizeList[i]); if (bSimplified) continue;//Simplified loading - skip decoding levels (for fast updating list of levelsets, level contructor above required to load record file and calculate number of solved levels) //uLevels[i].sTitle = "unnamed level " + (i + 1).ToString();//Initial level title uLevels[i].sTitle = ""; //void level title iPos = (int)uPosList[i];//Restore line number, where level starts //Choose where we should end loading level if (i<(iLevelsNum-1)) iEnd = (int)uPosList[i + 1]; //Not last level - up to first line of previous level else iEnd = uFileLines.Count; //Last level - up to last line of level iPhase = 1;//Initial phase - comment before level iCommentPhase = 0; iY = 0; //uNewCell = SokoCell.Background; while (iPos < iEnd)//Iterate thru all lines of level board { sLine = (string)uFileLines[iPos]; iPos++;//Restore line from array sTrim = sLine.Trim();//Trim whitespaces if (sTrim.StartsWith("#")) { //All lines that starts with # are lines of level if (iPhase == 1) { //Get title from last unidentified line uLevels[i].sTitle = sLastNotEmpty; } if (iPhase == 3) { //Level board already was loaded, it cannot start second time break;//Exit from loading this level } iPhase = 2;//Now we loading level sChars = sLine.ToCharArray();//Get chars of string iX = 0;//Start new line for (j = 0; j < sChars.Length; j++)//Iterate thru all chars { switch (sChars[j])//Decore char into cell of level { case '#': uNewCell = SokoCell.Wall; break; case ' ': uNewCell = SokoCell.Empty; break; case '.': uNewCell = SokoCell.Target; break; case '$': uNewCell = SokoCell.Box; break; case '@': uNewCell = SokoCell.Player; break; case '*': uNewCell = SokoCell.BoxOnTarget; break; case '+': uNewCell = SokoCell.PlayerOnTarget; break; default: continue;//Unknown - skipped } uLevels[i].SetCell(iX, iY, uNewCell);//Put cell into level iX++;//Advance to next cell } iY++;//Advance to next line } else { //All lines, that not starts with # are delimits levels from each other if (iPhase == 2)//If level just ends sLastNotEmpty = "";//Forgot all previous unidentified lines iPhase = 3;//Now we are after level board try { //Try to get values from comments (comparisons should be in increment-len order, otherwise exception may occurs earlier, than line will be found) if (sTrim.Substring(0, 7) == "Title: ") { uLevels[i].sTitle = sTrim.Substring(7); sTrim = ""; } else if (sTrim.Substring(0, 8) == "Author: ") { uLevels[i].sAuthor = sTrim.Substring(8); sTrim = ""; } else if (sTrim.Substring(0, 8) == "Comment:") { if (sTrim.Length > 8) uLevels[i].sComment = sTrim.Substring(8).Trim();//Line contain text after "Comment:", so it is single-line comment else iCommentPhase = 1;//Line do not contain other text, so it is multi-line comment sTrim = ""; } else if (sTrim.Substring(0, 11) == "Comment-End") { //Multi-line comment ends iCommentPhase = 0; sTrim = ""; } } catch (ArgumentOutOfRangeException) { //If line is very short, substring will throw exception. We bury such exceptions here :) } if (iCommentPhase == 1) { //Multiline comment continuing if (uLevels[i].sComment.Length > 0) uLevels[i].sComment += "\r\n";//Not first line, add line delimiter uLevels[i].sComment += sTrim; sTrim = ""; } if (sTrim.Length > 0) { //Line is still not identified sLastNotEmpty = sTrim;//Store line, it could be title for next level } } } if (uLevels[i].sTitle.Length <= 0) { uLevels[i].sTitle = "level " + (i+1).ToString();//Initial level title } uLevels[i].CleanUp();//Distinguish backgrounds from empty cells (both specified as spaces in file) } return FunctionResult.OK;//Level set successfully loaded }
///<summary>Copy-constructor for creating temprorary levels (source level)</summary> public SokobanGame(SokobanLevel uLevelToCopy) { iMovesAlloc = 0; iReversAlloc = 0; CopyFrom(uLevelToCopy);//Copy all data from source //Flush player and box move trees iMoveTreeFromX = -1; iMoveTreeFromY = -1; iBoxMoveTreeFromX = -1; iBoxMoveTreeFromY = -1; }
///<summary>Generate null level set - 1 random level</summary> public FunctionResult GenNullLevelSet() { int i, j,k; int iXsize, iYsize; Random uRandom = new Random(Environment.TickCount); Reset();//Reset levelset sAuthor = "SokobanCompact"; sComment = "Randomly generated"; sCopyright = ""; sTitle = "null levelset"; sFileName = sNullName; sID = sNullName; sRecordFile = null; iLevelsNum = 1;//1 level iLevelsAllocated = iLevelsNum; uLevels = new SokobanLevel[iLevelsAllocated];//Allocate array of levels iXsize = 10; iYsize = 10; uLevels[0] = new SokobanLevel(iXsize, iYsize); uLevels[0].sTitle = "null level"; uLevels[0].sAuthor = "SokobanCompact"; uLevels[0].sComment = "Randomly generated"; for (i = 0; i < iXsize; i++) for (j = 0; j < iYsize; j++) { if (i==0 || j==0 || i==(iXsize-1) || j==(iYsize-1)) uLevels[0].SetCell(i, j, SokoCell.Wall); else uLevels[0].SetCell(i, j, SokoCell.Empty); } uLevels[0].SetCell(1, 1, SokoCell.Player); k = 0; do { i = uRandom.Next(2, iXsize - 2); j = uRandom.Next(2, iYsize - 2); if (uLevels[0].GetCell(i, j)!=SokoCell.Empty) continue; uLevels[0].SetCell(i, j, SokoCell.Box); k++; } while (k < 10); k = 0; do { i = uRandom.Next(2, iXsize - 2); j = uRandom.Next(2, iYsize - 2); if (uLevels[0].GetCell(i, j)!=SokoCell.Empty) continue; uLevels[0].SetCell(i, j, SokoCell.Target); k++; } while (k < 10); return FunctionResult.OK; }