Example #1
0
        /// <summary>
        /// Initialize all squares in the mazeExtension (everything but trajectoryDistance).
        /// </summary>
        /// <param name="maze"></param>
        private void InitializeMazeExtension(Maze maze)
        {
            for (int i = 0; i < maze.XSize; i++)
            {
                for (int j = 0; j < maze.YSize; j++)
                {
                    MazeSquare          sq  = maze[i, j];
                    MazeSquareExtension sqe = mazeExtension[i, j];

                    // extendedSquare:
                    sqe.extendedSquare = sq;

                    if (sq.isReserved)
                    {
                        // isDeadEnd:
                        sqe.isDeadEnd          = true;
                        sqe.trajectoryDistance = -1;
                    }
                    else
                    {
                        // neighbors:
                        sqe.neighbors = new List <MazeSquareExtension>(4);

                        for (WallPosition wp = WallPosition.WP_MIN; wp <= WallPosition.WP_MAX; wp++)
                        {
                            MazeSquare sq2 = sq.NeighborSquare(wp);

                            if (sq2 != null && !sq2.isReserved)
                            {
                                sqe.neighbors.Add(mazeExtension[sq2.XPos, sq2.YPos]);
                            }
                        }

                        // Negative values mean: not initialized
                        sqe.trajectoryDistance = int.MinValue;
                    }
                }
            }
        }
Example #2
0
            /// <summary>
            /// Returns the center square of the given sqaure's partition.
            /// </summary>
            /// <param name="sq"></param>
            /// <returns></returns>
            private MazeSquare ReferenceSquare(MazeSquare sq)
            {
                // x and y index of the current square's partition
                int px = sq.XPos * xPartitions / xSize, py = sq.YPos * yPartitions / ySize;

                // x and y coordinates of the selected partition's center square
                int xCenter = (2 * px + 1) * xSize / (2 * xPartitions);
                int yCenter = (2 * py + 1) * ySize / (2 * yPartitions);

                // Apply this partition's offset.
                if (xOffCenter != null && yOffCenter != null)
                {
                    xCenter += xOffCenter[px, py];
                    yCenter += yOffCenter[px, py];
                }

                // If defined: Find the vertex closest to the given square.
                if (vertices != null)
                {
                    MazeSquare closestVertex   = vertices[0];
                    double     closestDistance = Maze.Distance(sq, closestVertex);

                    for (int i = 1; i < vertices.Length; i++)
                    {
                        double d = Maze.Distance(sq, vertices[i]);
                        if (d < closestDistance)
                        {
                            closestVertex   = vertices[i];
                            closestDistance = d;
                        }
                    }

                    xCenter = closestVertex.XPos;
                    yCenter = closestVertex.YPos;
                }

                return(new MazeSquare(xCenter, yCenter));
            }
Example #3
0
 /// <summary>
 /// Setup method: Define the adjoining sqare on the other side of a wall.
 /// </summary>
 /// <param name="side"></param>
 /// <param name="neighbor"></param>
 internal void SetNeighbor(WallPosition side, MazeSquare neighbor)
 {
     this.neighbors[(int)side] = neighbor;
 }
Example #4
0
            public override bool[] PreferredDirections(MazeSquare sq)
            {
                // A measure of how many template squares' wall are already open.
                // When positive, this square's wall should also be open.
                double[] openTemplates = new double[4];

                for (int x = sq.XPos % gridWidth; x < maze.XSize; x += gridWidth)
                {
                    for (int y = sq.YPos % gridHeight; x < maze.YSize; x += gridHeight)
                    {
                        #region Skip the square that is being evaluated.
                        if (x == sq.XPos && y == sq.YPos)
                        {
                            continue;
                        }
                        #endregion

                        for (WallPosition p = WallPosition.WP_MIN; p <= WallPosition.WP_MAX; p++)
                        {
                            #region Skip this wall if it is the maze border or a reserved area border.

                            bool skip = false;

                            switch (p)
                            {
                            case WallPosition.WP_W:
                                skip = (x == 0 || maze[x - 1, y].isReserved);
                                break;

                            case WallPosition.WP_E:
                                skip = (x + 1 == maze.XSize || maze[x + 1, y].isReserved);
                                break;

                            case WallPosition.WP_N:
                                skip = (y == 0 || maze[x, y - 1].isReserved);
                                break;

                            case WallPosition.WP_S:
                                skip = (y + 1 == maze.YSize || maze[x, y + 1].isReserved);
                                break;
                            }

                            if (skip)
                            {
                                continue;
                            }

                            #endregion

                            double dx = x - sq.XPos, dy = y - sq.YPos, d = Math.Sqrt(dx * dx + dy * dy);
                            double increment = 0;

                            // Let the influence of farther regions decrease rather slowly.
                            d = Math.Log(d, Math.E);

                            switch (maze[x, y][p])
                            {
                            case WallState.WS_OPEN:
                                increment = +1.0 / d;
                                break;

                            case WallState.WS_CLOSED:
                            case WallState.WS_OUTLINE:
                                increment = -1.0 / d;
                                break;

                            default:
                            case WallState.WS_MAYBE:
                                increment = +0.1 / d;
                                break;
                            }
                            openTemplates[(int)p] += increment;
                        }
                    }
                }

                bool[] result = new bool[4];
                for (int p = 0; p < 4; p++)
                {
                    result[p] = (openTemplates[p] >= 0);
                }
                return(result);
            }
Example #5
0
 /// <summary>
 /// Returns an array four boolean values, indexed by the WallPosition constants.
 /// The value is true when that wall is a preferred direction for paths from the given square.
 /// </summary>
 /// <param name="sq"></param>
 /// <returns></returns>
 public abstract bool[] PreferredDirections(MazeSquare sq);
Example #6
0
            public override bool[] PreferredDirections(MazeSquare sq)
            {
                bool[] result = new bool[4] {
                    false, false, false, false
                };

                #region Collect the distances of all neighbor squares (including diagonals) from the reference square.

                // Actually, this is the (absolute) difference between the distances of a neighbor square and sq itself.
                double[,] distances = new double[3, 3];

                // The distance of the current square to the reference square should be approximated.
                MazeSquare sq0 = this.ReferenceSquare(sq);
                double     d0  = Maze.Distance(sq, sq0);
                // Rounding to the next integer will approximate circles in 1 unit steps.
                if (approximateCircles)
                {
                    d0 = Math.Round(d0);
                }

                for (int i = -1; i <= +1; i++)
                {
                    for (int j = -1; j <= +1; j++)
                    {
                        // Note: this[x,y] is not defined outside the maze size.
                        MazeSquare sq1 = new MazeSquare(sq.XPos + i, sq.YPos + j);
                        distances[i + 1, j + 1] = (minimizeDistance ? +1 : -1) * Math.Abs(Maze.Distance(sq1, sq0) - d0);
                    }
                }

                #endregion

                #region Find a range of distances including the current square and two of the neighbors.

                double d1 = double.MaxValue, d2 = double.MaxValue;
                for (int i = 0; i < 3; i++)
                {
                    for (int j = 0; j < 3; j++)
                    {
                        if (i == 1 && j == 1)       // Discard the current square.
                        {
                            continue;
                        }
                        if (distances[i, j] >= d2)  // Discard distances greater than the current second best.
                        {
                            continue;
                        }
                        if (distances[i, j] >= d1)  // This is a new second best distance.
                        {
                            d2 = distances[i, j];
                        }
                        else                        // This is a new best distance.
                        {
                            d2 = d1;
                            d1 = distances[i, j];
                        }
                    }
                }

                #endregion

                #region Add the directions to the two best neighbors to the result.

                if (distances[1, 0] <= d2) // straight north
                {
                    result[(int)WallPosition.WP_N] = true;
                }
                if (distances[0, 1] <= d2) // straight west
                {
                    result[(int)WallPosition.WP_W] = true;
                }
                if (distances[1, 2] <= d2) // straight south
                {
                    result[(int)WallPosition.WP_S] = true;
                }
                if (distances[2, 1] <= d2) // straight east
                {
                    result[(int)WallPosition.WP_E] = true;
                }

                // For approximating a diagonal connection, we follow the closer path (unless this logic is inverted).
                if (distances[0, 0] <= d2) // north-west
                {
                    result[(int)((distances[1, 0] < distances[0, 1] == followDiagonals) ? WallPosition.WP_N : WallPosition.WP_W)] = true;
                }
                if (distances[2, 0] <= d2) // north-east
                {
                    result[(int)((distances[1, 0] < distances[2, 1] == followDiagonals) ? WallPosition.WP_N : WallPosition.WP_E)] = true;
                }
                if (distances[0, 2] <= d2) // south-west
                {
                    result[(int)((distances[1, 2] < distances[0, 1] == followDiagonals) ? WallPosition.WP_S : WallPosition.WP_W)] = true;
                }
                if (distances[2, 2] <= d2) // south-east
                {
                    result[(int)((distances[1, 2] < distances[2, 1] == followDiagonals) ? WallPosition.WP_S : WallPosition.WP_E)] = true;
                }

                #endregion
                return(result);
            }
Example #7
0
        /// <summary>
        /// Registers the given square as visited.
        /// </summary>
        /// <param name="sq"></param>
        /// <returns>a list of dead end squares</returns>
        public List <MazeSquare> Visit(MazeSquare sq)
        {
            List <MazeSquare>   result = new List <MazeSquare>();
            MazeSquareExtension sqe    = mazeExtension[sq.XPos, sq.YPos];

            // Don't process squares that have been visited before.
            if (sqe.isDeadEnd)
            {
                return(result);
            }

            // The visited square is marked as dead.
            sqe.isDeadEnd = true;
            int d = sqe.trajectoryDistance;

            if (d > 0)
            {
                sqe.trajectoryDistance *= -1;
            }
            else
            {
                d *= -1;
            }

            if (d == 0)
            {
                // This is the end square.  No need to kill any more squares...
                return(result);
            }

            // Add neighbors to the list of uncertainSquares if their trajectory passed through the visited square.
            // That is the case if their distance is greater by one than the visited square's distance.
            for (int i = 0; i < sqe.neighbors.Count; i++)
            {
                MazeSquareExtension sqe2 = sqe.neighbors[i];

                if (sqe2.trajectoryDistance == d + 1)
                {
                    AddUncertainSquare(sqe2, -1);
                }
            }

            if (HarmlessConstellation(sqe))
            {
                return(result);
            }

            // Add all squares whose trajectory depends on the ones already inserted.
            CollectUncertainSquares();

            // The areas next to all confirmedSquares will receive an adjusted trajectoryDistance.
            ReviveConfirmedSquaresNeighbors();

            // The remaining uncertainSquares with negative (invalid) trajectoryDistance are dead.
            for (int j = 0; j < uncertainSquares.Count; j++)
            {
                MazeSquareExtension sqe2 = uncertainSquares[j];
                if (sqe2.trajectoryDistance < 0 && !sqe2.isDeadEnd)
                {
                    sqe2.isDeadEnd = true;
                    if (sqe2.extendedSquare.MazeId == sq.MazeId)
                    {
                        result.Add(sqe2.extendedSquare);
                    }
                }
            }

            // Empty the internal lists.
            uncertainSquares.Clear();
            confirmedSquares.Clear();

            return(result);
        }
Example #8
0
 /// <summary>
 /// Returns true if the given square is marked as dead.
 /// A square is dead if
 ///  a) it is a reserved area in the maze
 ///  b) it has already been visited
 ///  c) there is no trajectory leading to the maze's end square
 /// </summary>
 /// <param name="sq"></param>
 /// <returns></returns>
 public bool IsDead(MazeSquare sq)
 {
     return(mazeExtension[sq.XPos, sq.YPos].isDeadEnd);
 }
Example #9
0
        /// <summary>
        /// Convert all undecided walls to either closed or open.
        /// In the resulting maze, there must be a path from every square to every other square.
        /// There must not be any circles, i.e. the maze must have a tree-like structure.
        /// </summary>
        private void BuildMaze()
        {
            // We hold a number of active squares in a stack.
            // Make the initial capacity sufficient to hold all squares.
            //
            Stack <MazeSquare> stack = new Stack <MazeSquare>(xSize * ySize);

            List <MazeSquare>   outlineSquares = new List <MazeSquare>();
            List <WallPosition> outlineWalls   = new List <WallPosition>();

            #region Start with a single random cell in the stack.

            while (true)
            {
                int        x  = random.Next(xSize);
                int        y  = random.Next(ySize);
                MazeSquare sq = this[x, y];
                if (sq.MazeId == this.MazeId)
                {
                    sq.isConnected = true;
                    stack.Push(sq);
                    break;
                }
            }

            #endregion

            #region Extend the maze by visiting the cells next to those in the stack.

            while (stack.Count > 0)
            {
                List <WallPosition> unresolvedWalls = new List <WallPosition>((int)WallPosition.WP_NUM);
                MazeSquare          sq0             = stack.Pop();

                // Collect the unfixed walls of sq0.
                //
                for (WallPosition wp = WallPosition.WP_MIN; wp <= WallPosition.WP_MAX; wp++)
                {
                    switch (sq0[wp])
                    {
                    case WallState.WS_MAYBE:
                        MazeSquare sq = sq0.NeighborSquare(wp);

                        if (sq.isConnected || sq.MazeId != sq0.MazeId)
                        {
                            sq0[wp] = sq[MazeSquare.OppositeWall(wp)] = WallState.WS_CLOSED;
                        }
                        else
                        {
                            unresolvedWalls.Add(wp);
                        }
                        break;     // WS_MAYBE

                    case WallState.WS_OUTLINE:
                        outlineSquares.Add(sq0);
                        outlineWalls.Add(wp);
                        break;     // WS_OUTLINE
                    }
                } // foreach wp

                // Discard this square if it has no unresolved walls.
                if (unresolvedWalls.Count == 0)
                {
                    // Note: This is the only place that may end the loop.
                    // If the stack is empty: Open one outline wall.
                    if (stack.Count == 0)
                    {
                        while (outlineSquares.Count > 0)
                        {
                            // Select a random square with an outline wall.
                            int          p  = random.Next(outlineSquares.Count);
                            MazeSquare   sq = outlineSquares[p];
                            WallPosition wp = outlineWalls[p];
                            outlineSquares.RemoveAt(p);
                            outlineWalls.RemoveAt(p);

                            if (sq[wp] == WallState.WS_OUTLINE)
                            {
                                sq[wp] = WallState.WS_MAYBE;
                                stack.Push(sq);
                                // This square will be used in the next iteration.
                                break; // from while(outlineSquares)
                            }
                        }
                    }

                    continue; // no walls to choose from
                }

                // Add the current cell to the stack.
                // Note: Do this before replacing unresolvedWalls with preferredWalls.
                if (unresolvedWalls.Count > 1)
                {
                    stack.Push(sq0);
                }

                // Use only preferred wall positions.
                if (unresolvedWalls.Count > 1 && irregularMazeShape != null &&
                    (random.Next(100) < irregularMazeShape.ApplicationPercentage(this.irregularity)))
                {
                    bool[] preferredPositions          = irregularMazeShape.PreferredDirections(sq0);
                    List <WallPosition> preferredWalls = new List <WallPosition>(unresolvedWalls.Count);
                    foreach (WallPosition p in unresolvedWalls)
                    {
                        if (preferredPositions[(int)p])
                        {
                            preferredWalls.Add(p);
                        }
                    }
                    if (preferredWalls.Count > 0)
                    {
                        unresolvedWalls = preferredWalls;
                    }
                }

                // Choose one wall.
                WallPosition wp0 = unresolvedWalls[random.Next(unresolvedWalls.Count)];
                MazeSquare   sq1 = sq0.NeighborSquare(wp0);

                // Open the wall.
                sq0[wp0] = sq1[MazeSquare.OppositeWall(wp0)] = WallState.WS_OPEN;

                // Add the new cell to the stack.
                sq1.isConnected = true;
                stack.Push(sq1);
            } // while stack is not empty

            #endregion
        }
Example #10
0
        /// <summary>
        /// Returns the square diametrically opposed to the given square.
        /// </summary>
        public MazeSquare GetOpposedSquare(MazeSquare sq)
        {
            Rectangle bbox = this.GetBoundingBox();

            return(this[bbox.Right - 1 - sq.XPos, bbox.Bottom - 1 - sq.YPos]);
        }