Ejemplo n.º 1
0
        protected void CalculateLowerBoundMaps()
        {
            // Create the pre-calculated lower bound data structures.

            // Create work arrays for matching lower bound function.
            boxTarget = new int[boxes];
            targetBox = new int[targets];
            distance = new int[boxes];

            // Create an empty level with path finder for measuring distances.
            Level emptyLevel = LevelUtils.GetEmptyLevel(originalLevel);
            PathFinder emptyPathFinder = PathFinder.CreateInstance(emptyLevel, true);

            // Create the target distance maps and target index map.
            targetDistanceMaps = new Array2D<int>[targets];
            targetIndexMap = new Array2D<int>(level.Height, level.Width);
            targetIndexMap.SetAll(-1);
            for (int i = 0; i < targets; i++)
            {
                targetDistanceMaps[i] = new Array2D<int>(level.Height, level.Width);
                targetIndexMap[targetCoordinates[i]] = i;
            }

            // For each inside square calculate the sorted list of nearest targets and
            // the distance to the nearest target.
            nearestTargetsMap = new Array2D<int[]>(level.Height, level.Width);
            nearestTargetDistanceMap = new Array2D<int>(level.Height, level.Width);
            foreach (Coordinate2D boxCoord in level.InsideCoordinates)
            {
                List<int> nearestTargets = new List<int>();
                for (int j = 0; j < targets; j++)
                {
                    Coordinate2D targetCoord = targetCoordinates[j];
                    targetDistanceMaps[j][boxCoord] = emptyPathFinder.FindAndGetDistance(boxCoord, targetCoord);
                    nearestTargets.Add(j);
                }
                nearestTargets.Sort(delegate(int index1, int index2)
                    {
                        int distance1 = targetDistanceMaps[index1][boxCoord];
                        int distance2 = targetDistanceMaps[index2][boxCoord];
                        return distance1.CompareTo(distance2);
                    });
                nearestTargetDistanceMap[boxCoord] = targetDistanceMaps[nearestTargets[0]][boxCoord];
                nearestTargetsMap[boxCoord] = nearestTargets.ToArray();
            }
        }
Ejemplo n.º 2
0
        private Level GenerateLevelBlob(State state, int size, Array2D<Cell> designData, Array2D<bool> designFixed, out int designRow, out int designColumn)
        {
            // Inflate size for extra cells to be subtracted.
            int blobSize = size * (density + 100) / 100;
            // Create larger work array and corresponding fixed map.
            Array2D<Cell> array = new Array2D<Cell>(2 * size, 2 * size);
            array.SetAll(Cell.Outside);
            Array2D<bool> arrayFixed = new Array2D<bool>(array.Height, array.Width);

            // Adjust design data so that outside or undefined cells are empty.
            Array2D<Cell> designDataCopy = new Array2D<Cell>(designData);
            designDataCopy.Replace(Cell.Outside, Cell.Empty);
            designDataCopy.Replace(Cell.Undefined, Cell.Empty);

            // Copy design into middle of work array.
            designRow = size;
            designColumn = size;
            designDataCopy.CopyTo(array, designRow + 1, designColumn + 1, 1, 1, designData.Height - 2, designData.Width - 2);
            designFixed.CopyTo(arrayFixed, designRow + 1, designColumn + 1, 1, 1, designData.Height - 2, designData.Width - 2);

            // Set intial boundaries.
            int rowMin = array.Height;
            int colMin = array.Width;
            int rowMax = -1;
            int colMax = -1;
            int count = 0;
            foreach (Coordinate2D coord in array.Coordinates)
            {
                if (!Level.IsOutside(array[coord]))
                {
                    rowMin = Math.Min(rowMin, coord.Row);
                    colMin = Math.Min(colMin, coord.Column);
                    rowMax = Math.Max(rowMax, coord.Row);
                    colMax = Math.Max(colMax, coord.Column);
                    count++;
                }
            }

            while (count < blobSize)
            {
                // Choose an edge at random.
                int edge = state.Random.Next(4);
                int row = 0;
                int column = 0;
                int limit = 0;
                int hIncr = 0;
                int vIncr = 0;
                switch (edge)
                {
                    case 0:
                        row = rowMin - 1;
                        column = colMin + state.Random.Next(colMax - colMin + 1);
                        limit = rowMax - rowMin + 1;
                        vIncr = 1;
                        hIncr = 0;
                        break;
                    case 1:
                        row = rowMax + 1;
                        column = colMin + state.Random.Next(colMax - colMin + 1);
                        limit = rowMax - rowMin + 1;
                        vIncr = -1;
                        hIncr = 0;
                        break;
                    case 2:
                        row = rowMin + state.Random.Next(rowMax - rowMin + 1);
                        column = colMin - 1;
                        limit = colMax - colMin + 1;
                        vIncr = 0;
                        hIncr = 1;
                        break;
                    case 3:
                        row = rowMin + state.Random.Next(rowMax - rowMin + 1);
                        column = colMax + 1;
                        limit = colMax - colMin + 1;
                        vIncr = 0;
                        hIncr = -1;
                        break;
                }

                // Search along a line until we hit a empty or fixed cell.
                bool found = false;
                for (int i = 0; i < limit; i++)
                {
                    if (array[row + vIncr, column + hIncr] != Cell.Outside || arrayFixed[row + vIncr, column + hIncr])
                    {
                        if (!arrayFixed[row, column])
                        {
                            found = true;
                        }
                        break;
                    }
                    row += vIncr;
                    column += hIncr;
                }

                // If we didn't find anything, try again.
                if (!found)
                {
                    continue;
                }

                // Don't allow the level to grow outside the array.
                if (row < 1 || row >= array.Height - 1 || column < 1 || column >= array.Width - 1)
                {
                    continue;
                }

                // Add the new square and update the boundaries.
                array[row, column] = Cell.Empty;
                rowMin = Math.Min(rowMin, row);
                colMin = Math.Min(colMin, column);
                rowMax = Math.Max(rowMax, row);
                colMax = Math.Max(colMax, column);
                count++;
            }

            int attemptsLeft = 2 * (count - size);
            while (count > size && attemptsLeft > 0)
            {
                // Choose a new square at random.
                int row = rowMin + state.Random.Next(rowMax - rowMin + 1);
                int column = colMin + state.Random.Next(colMax - colMin + 1);
                Coordinate2D coord = new Coordinate2D(row, column);
                if (!array.IsValid(coord))
                {
                    continue;
                }

                // Avoid existing walls and outside areas.
                if (!Level.IsInside(array[coord]))
                {
                    continue;
                }

                // We might get into an infinite loop.
                attemptsLeft--;

                // Avoid fixed cells.
                if (arrayFixed[coord])
                {
                    continue;
                }

                // Avoid cells on the perimeter.
                bool isAdjacent = false;
                foreach (Coordinate2D neighbor in coord.EightNeighbors)
                {
                    if (array[neighbor] == Cell.Outside)
                    {
                        isAdjacent = true;
                        break;
                    }
                }
                if (isAdjacent)
                {
                    continue;
                }

                // Remove the cell.
                array[coord] = Cell.Wall;
                count--;

            }

            // Extract the constructed level.
            Array2D<Cell> subarray = array.GetSubarray(rowMin - 1, colMin - 1, rowMax - rowMin + 3, colMax - colMin + 3);
            subarray.Replace(Cell.Outside, Cell.Wall);
            Level level = new Level(subarray);

            // Adjust design coordinate.
            designRow -= rowMin - 1;
            designColumn -= colMin - 1;

            return level;
        }
Ejemplo n.º 3
0
        private void UnsafeGenerateOneLevel(State state)
        {
            // Randomly select a supplied design.
            state.CurrentDesign = state.Random.Next(designCount);
            Array2D<Cell> designData = designInfo[state.CurrentDesign].Data;
            Array2D<bool> designFixed = designInfo[state.CurrentDesign].Fixed;

            // Choose a size at random between the minimum and maximum limits.
            int size = minSize + state.Random.Next(maxSize - minSize + 1);
            state.CurrentSize = size;

            int designRow;
            int designColumn;
            Level level = null;
            if (useEntireLevel)
            {
                designRow = 0;
                designColumn = 0;
                level = new Level(new Array2D<Cell>(designData));
            }
            else
            {
                level = GenerateLevel(state, size, designData, designFixed, out designRow, out designColumn);
            }

            // Make sure will have something to solve.
            if (designInfo[state.CurrentDesign].Boxes == 0 && boxes == 0)
            {
                state.Log.DebugPrint("level generated with no boxes");
                return;
            }

            // Add islands not included in design.
            for (int i = 0; i < islands; i++)
            {
                AddIsland(state, level);
            }

            // Add boxes not included in design.
            if (designInfo[state.CurrentDesign].Boxes < boxes)
            {
                int boxesToAdd = boxes - designInfo[state.CurrentDesign].Boxes;
                int displacedBoxes = boxesToAdd >= displaced ? displaced : boxesToAdd;
                int inplaceBoxes = boxesToAdd - displacedBoxes;
                for (int i = 0; i < displacedBoxes; i++)
                {
                    AddDisplacedBox(state, level);
                }
                for (int i = 0; i < inplaceBoxes; i++)
                {
                    AddInplaceBox(state, level);
                }
            }

            if (verbose >= 2)
            {
                state.Log.DebugPrint("design = {0}, size = {1}, algorithm = {2}", state.CurrentDesign, state.CurrentSize, state.CurrentAlgorithm);
                state.Log.DebugPrint(level.AsText);
            }

            // Create a map of squares that have already been tried
            // as the starting sokoban coordinate, initialized to false.
            Array2D<bool> tried = new Array2D<bool>(level.Height, level.Width);
            Coordinate2D sokobanCoord = designInfo[state.CurrentDesign].SokobanCoordinate;

            if (!sokobanCoord.IsUndefined)
            {
                // Only try specified sokoban coordinate.
                tried.SetAll(true);
                tried[sokobanCoord.Row + designRow, sokobanCoord.Column + designColumn] = false;
            }

            // For all accessible squares.
            PathFinder finder = PathFinder.CreateInstance(level);
            while (true)
            {
                // Find an untried starting square.
                bool foundSquare = false;
                foreach (Coordinate2D coord in level.InsideCoordinates)
                {
                    if (level.IsEmpty(coord) && !tried[coord])
                    {
                        sokobanCoord = coord;
                        foundSquare = true;
                        break;
                    }
                }
                if (!foundSquare)
                {
                    break;
                }

                // Put sokoban on untried empty square.
                level.AddSokoban(sokobanCoord);

                // Find all squares accessible from this one.
                finder.Find();

                // Mark all accessible squares as tried.
                foreach (Coordinate2D coord in finder.AccessibleCoordinates)
                {
                    tried[coord] = true;
                }

                MoveList moveList = FastSolve(state, level);
                if (moveList != null)
                {
                    int pushes = LevelUtils.SolutionPushes(moveList);
                    if (verbose >= 1)
                    {
                        state.Log.DebugPrint("raw: moves = {0}, pushes = {1}", moveList.Count, pushes);
                    }
                    if (pushes > pushLimit)
                    {
                        // First clean up the level which might have a large number
                        // of excess squares that would stress the final solver.
                        Level cleanLevel = LevelUtils.CleanLevel(level, moveList);

                        // Now get a clean solution using move optimization.
                        MoveList cleanMoveList = FinalSolve(state, cleanLevel);

                        // Although this does have a solution, the solver
                        // could fail for other reasons (out of memory, etc).
                        if (cleanMoveList != null)
                        {
                            // Finally check whether there are any squares we can remove.
                            AddSmallestLevel(state, cleanLevel, cleanMoveList);
                        }
                    }
                }
                else
                {
                    if (verbose >= 2)
                    {
                        state.Log.DebugPrint("no solution");
                    }
                }

                level.RemoveSokoban();
            }
        }
Ejemplo n.º 4
0
        private Level GenerateLevelBuckshot(State state, int size, Array2D<Cell> designData, Array2D<bool> designFixed, out int designRow, out int designColumn)
        {
            // Create larger work array and corresponding fixed map.
            Array2D<Cell> array = new Array2D<Cell>(2 * size + designData.Height, 2 * size + designData.Width);
            array.SetAll(Cell.Wall);
            Array2D<bool> arrayFixed = new Array2D<bool>(array.Height, array.Width);

            // Adjust design data so that undefined cells are walls.
            Array2D<Cell> designDataCopy = new Array2D<Cell>(designData);
            designDataCopy.Replace(Cell.Undefined, Cell.Wall);

            // Copy design into middle of work array.
            designRow = size;
            designColumn = size;
            designDataCopy.CopyTo(array, designRow, designColumn, 0, 0, designData.Height, designData.Width);
            designFixed.CopyTo(arrayFixed, designRow, designColumn, 0, 0, designData.Height, designData.Width);

            // Set intial boundaries.
            int rowMin = array.Height;
            int colMin = array.Width;
            int rowMax = -1;
            int colMax = -1;
            int count = 0;
            foreach (Coordinate2D coord in array.Coordinates)
            {
                if (!Level.IsWall(array[coord]))
                {
                    rowMin = Math.Min(rowMin, coord.Row);
                    colMin = Math.Min(colMin, coord.Column);
                    rowMax = Math.Max(rowMax, coord.Row);
                    colMax = Math.Max(colMax, coord.Column);
                    count++;
                }
            }

            while (count < size)
            {
                // Choose a new square at random.
                int row = rowMin - growth + state.Random.Next(rowMax - rowMin + 1 + 2 * growth);
                int column = colMin - growth + state.Random.Next(colMax - colMin + 1 + 2 * growth);
                Coordinate2D coord = new Coordinate2D(row, column);
                if (!array.IsValid(coord))
                {
                    continue;
                }

                // Avoid fixed cells.
                if (arrayFixed[coord])
                {
                    continue;
                }

                // Avoid existing squares.
                if (!Level.IsWall(array[coord]))
                {
                    continue;
                }

                // Ensure the new square is adjacent to an existing square.
                bool isAdjacent = false;
                foreach (Coordinate2D neighbor in coord.FourNeighbors)
                {
                    if (!Level.IsWall(array[neighbor]))
                    {
                        isAdjacent = true;
                        break;
                    }
                }
                if (!isAdjacent)
                {
                    continue;
                }

                // Add the new square and update the boundaries.
                array[coord] = Cell.Empty;
                rowMin = Math.Min(rowMin, row);
                colMin = Math.Min(colMin, column);
                rowMax = Math.Max(rowMax, row);
                colMax = Math.Max(colMax, column);
                count++;
            }

            // Extract the constructed level.
            Array2D<Cell> subarray = array.GetSubarray(rowMin - 1, colMin - 1, rowMax - rowMin + 3, colMax - colMin + 3);
            Level level = new Level(subarray);

            // Adjust design coordinate.
            designRow -= rowMin - 1;
            designColumn -= colMin - 1;

            return level;
        }
Ejemplo n.º 5
0
        private static Level ExpandAndReplace(Level level, int rowOffset, int columnOffset, Level replacement)
        {
            // Compute the overlap.
            int row0 = Math.Min(0, rowOffset - 1);
            int column0 = Math.Min(0, columnOffset - 1);
            int row1 = Math.Max(level.Height, replacement.Height - 2 + rowOffset);
            int column1 = Math.Max(level.Width, replacement.Width - 2 + columnOffset);
            int height = row1 - row0;
            int width = column1 - column0;

            // Expand the original level.
            Array2D<Cell> newData = new Array2D<Cell>(height, width);
            newData.SetAll(Cell.Wall);
            level.Data.CopyTo(newData, -row0, -column0, 0, 0, level.Height, level.Width);
            Level newLevel = new Level(newData);

            // Retry the replacement.
            return Replace(newLevel, rowOffset - row0, columnOffset - column0, replacement);
        }
Ejemplo n.º 6
0
        private static Level TryNormalizeTunnel(Level level, Coordinate2D tunnelCoord, Direction direction)
        {
            // Determine the offsets of the parallel direction.
            Coordinate2D parallel = direction;

            // If the upper half is closed off it's not a tunnel.
            if (level.IsWall(tunnelCoord - parallel))
            {
                return null;
            }

            // Empty out the level.
            Level newLevel = LevelUtils.GetEmptyLevel(level);

            // Add a wall to separate the level into two halves.
            newLevel[tunnelCoord] = Cell.Wall;

            // Find all cells accessible from the upper half.
            PathFinder finder = PathFinder.CreateInstance(newLevel);
            finder.Find(tunnelCoord - parallel);

            // If the lower half is accessible from the upper half we can't normalize.
            if (finder.IsAccessible(tunnelCoord + parallel))
            {
                return null;
            }

            // Create a map of the two regions, initialized to zero.
            Array2D<int> regionMap = new Array2D<int>(newLevel.Height, newLevel.Width);

            // Mark first region in the map.
            foreach (Coordinate2D coord in finder.AccessibleCoordinates)
            {
                regionMap[coord] = 1;
            }

            // Find all cells accessible from the lower half.
            finder.Find(tunnelCoord + parallel);

            // Mark second region in the map.
            foreach (Coordinate2D coord in finder.AccessibleCoordinates)
            {
                regionMap[coord] = 2;
            }

            // See if normalization would result in overlap.
            foreach (Coordinate2D coord in newLevel.Coordinates)
            {
                // For all coordinates in region one.
                if (regionMap[coord] != 1)
                {
                    continue;
                }

                if (coord == tunnelCoord - parallel)
                {
                    // We will have intentional contact right at the match site.
                    continue;
                }

                // Check whether there would be a square in region 1 adjacent to a square in region 2.
                Coordinate2D newCoord = coord + parallel;
                foreach (Coordinate2D neighbor in newCoord.FourNeighbors)
                {
                    if (newLevel.IsValid(neighbor) && regionMap[neighbor] == 2)
                    {
                        // Normalizing would alter the level.
                        return null;
                    }
                }
            }

            // Normalize the level using the region map as our guide.
            Array2D<Cell> data = new Array2D<Cell>(level.Height, level.Width);
            data.SetAll(Cell.Wall);
            foreach (Coordinate2D coord in level.Coordinates)
            {
                if (regionMap[coord] == 1)
                {
                    data[coord] = level[coord];
                }
                else if (regionMap[coord] == 2)
                {
                    data[coord - parallel] = level[coord];
                }
            }

            // Construct the level.
            return new Level(data);
        }