/// <summary> /// Constructor. /// Create a maze whose parameters are encoded in the given code (see property Code). /// </summary> /// <param name="code">a string of seven letters (case is ignored)</param> public Maze(string code) { int version = MazeCode.GetCodeVersion(code); this.dimensionsObj = MazeDimensions.Instance(version); this.codeObj = MazeCode.Instance(version); codeObj.Decode(code , out this.seed , out this.xSize, out this.ySize ); this.random = RandomFactory.CreateRandom(seed); }
/// <summary> /// Constructor. /// Create a maze with the given dimensions. /// </summary> /// <param name="xSize"></param> /// <param name="ySize"></param> /// <param name="version"></param> /// <param name="seed"></param> internal Maze(int xSize, int ySize, int version, int seed) { this.dimensionsObj = MazeDimensions.Instance(version); this.codeObj = MazeCode.Instance(version); this.xSize = Math.Max(dimensionsObj.MinSize, Math.Min(dimensionsObj.MaxXSize, xSize)); this.ySize = Math.Max(dimensionsObj.MinSize, Math.Min(dimensionsObj.MaxYSize, ySize)); // Get an initial random seed and use that to create the Random. if (seed < 0) { Random r = RandomFactory.CreateRandom(); this.seed = r.Next(codeObj.SeedLimit); } else { this.seed = seed; } this.random = RandomFactory.CreateRandom(this.seed); }
/// <summary> /// Extract the Maze parameters from an encoded string. /// </summary> /// <param name="code"></param> /// <param name="seed"></param> /// <param name="xSize"></param> /// <param name="ySize"></param> /// <exception cref="ArgumentOutOfRangeException">decoded parameters are invalid</exception> public void Decode(string code , out int seed , out int xSize, out int ySize ) { long nCode = 0; MazeDimensions dimensionsObj = MazeDimensions.Instance(codeVersion); #region Convert the character code (base 26) into a numeric code char[] a = code.Replace(this.Separator, "").ToCharArray(); if (!(a.Length == CodeLength)) { throw new ArgumentOutOfRangeException("code", code, "length = " + a.Length + " / Must be " + Description); } for (int p = 0; p < a.Length; p++) { int digit = 0; digit = CodeDigits.ToString().IndexOf(a[p]); if (digit < 0) { throw new ArgumentOutOfRangeException("code", code, "c = '" + a[p] + "' / Must be " + Description); } nCode *= CodeDigitRange; nCode += digit; } #endregion #region Decode items in the code in reverse order of encoding long nCodeOriginal = nCode; // for debugging long itemRange; itemRange = SeedLimit; seed = (int)(nCode % itemRange); nCode /= itemRange; itemRange = dimensionsObj.MaxXSize - dimensionsObj.MinSize + 1; xSize = (int)(nCode % itemRange) + dimensionsObj.MinSize; nCode /= itemRange; itemRange = dimensionsObj.MaxYSize - dimensionsObj.MinSize + 1; ySize = (int)(nCode % itemRange) + dimensionsObj.MinSize; nCode /= itemRange; if (codeVersion == 0) { #region Decoding of obsolete Version 0 parameters WallPosition direction; int xStart, yStart; int xEnd, yEnd; int d1, d2, c1, c2; itemRange = (int)WallPosition.WP_NUM; direction = (WallPosition)(nCode % itemRange); nCode /= itemRange; itemRange = dimensionsObj.MaxXSize + 1; c2 = (int)(nCode % itemRange); nCode /= itemRange; itemRange = dimensionsObj.MaxXSize + 1; c1 = (int)(nCode % itemRange); nCode /= itemRange; itemRange = dimensionsObj.MaxBorderDistance + 1; d2 = (int)(nCode % itemRange); nCode /= itemRange; itemRange = dimensionsObj.MaxBorderDistance + 1; d1 = (int)(nCode % itemRange); nCode /= itemRange; switch (direction) { case WallPosition.WP_E: xStart = d1; xEnd = xSize - 1 - d2; yStart = c1; yEnd = c2; break; case WallPosition.WP_W: xEnd = d1; xStart = xSize - 1 - d2; yEnd = c1; yStart = c2; break; case WallPosition.WP_S: yStart = d1; yEnd = ySize - 1 - d2; xStart = c1; xEnd = c2; break; case WallPosition.WP_N: yEnd = d1; yStart = ySize - 1 - d2; xEnd = c1; xStart = c2; break; default: xStart = yStart = xEnd = yEnd = -1; break; } #endregion } #endregion #region Verify the validity of the decoded items if (!(nCode == 0)) { throw new ArgumentOutOfRangeException("remainder(code)", nCode, "Must be a zero."); } ValidateCodeItemRange("seed", seed, 0, SeedLimit - 1); ValidateCodeItemRange("xSize", xSize, dimensionsObj.MinSize, dimensionsObj.MaxXSize); ValidateCodeItemRange("ySize", ySize, dimensionsObj.MinSize, dimensionsObj.MaxYSize); #endregion }
/// <summary> /// A string that encodes the maze parameters. /// This code can be used to construct an identical maze. /// </summary> public string Code(Maze maze) { long nCode = 0; MazeDimensions dimensionsObj = MazeDimensions.Instance(codeVersion); #region Encode the relevant parameters into a numeric code // Items are encoded in reverse order of decoding. // Some items must be encoded before others if decoding requires them. if (codeVersion == 0) { // Encode the start and end points. // Instead of the direct coordinates we will use the following information: // * travel direction // * distance from the border (instead of coordinate) // * other coordinate // The scaling factor is always MazeDimensions.MaxXSize, as it is greater than MazeDimensions.MaxYSize. int d1, d2, c1, c2; switch (maze.Direction) { case WallPosition.WP_E: d1 = maze.StartSquare.XPos; d2 = maze.XSize - 1 - maze.EndSquare.XPos; c1 = maze.StartSquare.YPos; c2 = maze.EndSquare.YPos; break; case WallPosition.WP_W: d1 = maze.EndSquare.XPos; d2 = maze.XSize - 1 - maze.StartSquare.XPos; c1 = maze.EndSquare.YPos; c2 = maze.StartSquare.YPos; break; case WallPosition.WP_S: d1 = maze.StartSquare.YPos; d2 = maze.YSize - 1 - maze.EndSquare.YPos; c1 = maze.StartSquare.XPos; c2 = maze.EndSquare.XPos; break; case WallPosition.WP_N: d1 = maze.EndSquare.YPos; d2 = maze.YSize - 1 - maze.StartSquare.YPos; c1 = maze.EndSquare.XPos; c2 = maze.StartSquare.XPos; break; default: d1 = d2 = c1 = c2 = -1; break; } nCode *= (dimensionsObj.MaxBorderDistance + 1); nCode += d1; nCode *= (dimensionsObj.MaxBorderDistance + 1); nCode += d2; nCode *= (dimensionsObj.MaxXSize + 1); nCode += c1; nCode *= (dimensionsObj.MaxXSize + 1); nCode += c2; nCode *= (int)WallPosition.WP_NUM; nCode += (int)maze.Direction; } // Encode maze dimension. nCode *= (dimensionsObj.MaxYSize - dimensionsObj.MinSize + 1); nCode += (maze.YSize - dimensionsObj.MinSize); nCode *= (dimensionsObj.MaxXSize - dimensionsObj.MinSize + 1); nCode += (maze.XSize - dimensionsObj.MinSize); // Encode initial seed. nCode *= SeedLimit; nCode += maze.Seed; #endregion // v0: The resulting nCode is less than 26^12. See SWA.Ariadne.Model.Tests unit tests. // v1: The resulting nCode is less than 36^6. See SWA.Ariadne.Model.Tests unit tests. #region Convert the numeric code into a character code (base 26) StringBuilder result = new StringBuilder(7); for (int p = CodeLength; p-- > 0;) { int digit = (int)(nCode % CodeDigitRange); nCode /= CodeDigitRange; char c = CodeDigits[digit]; result.Insert(0, c); } switch (codeVersion) { case 0: result.Insert(8, this.Separator); result.Insert(4, this.Separator); break; case 1: result.Insert(3, this.Separator); break; case 2: result.Insert(3, this.Separator); break; } #endregion return(result.ToString()); }