private static int TestSimplePuzzle()
        {
            SokobanMap map = new SokobanMap();
            map.setFromStrings(new string[]
                                   {
            "~~~###~~~~~",
            "~~##.#~####",
            "~##..###..#",
            "##.X......#",
            "#...PX.#..#",
            "###.X###..#",
            "~~#..#OO..#",
            "~##.##O#.##",
            "~#......##~",
            "~#.....##~~",
            "~#######~~~"
                                   });

            PuzzleMap pMap = new PuzzleMap(null);
            pMap.Map = map;

            SolverAPI api = new SolverAPI();
            List<INode<SolverNode>> results = api.Solve(pMap);

            if (results == null || results.Count== 0)
            {
                Console.WriteLine("No Solutions found.");
                return -1;
            }

            return 0;
        }
        public void TestRoom()
        {
            string[] puzzle = new string[]
                {
                    "~~###########",
                    "~##.....#..P#",
                    "###.X.XX#...#",
                    "#.##X....XX.#",
                    "#..#..X.#...#",
                    "######.######",
                    "#OO.OOX.#$##~",
                    "#.OO....###~~",
                    "#..OO#####~~~",
                    "#########~~~~"
                };

            SokobanMap map = new SokobanMap();
            map.SetFromStrings(puzzle);

            PuzzleMap pMap = new PuzzleMap((Puzzle)null);
            pMap.Map = map;

            SolverController solver = new SolverController(pMap);
            solver.Init();
        }
Beispiel #3
0
        public void FloodCrateMoveMap()
        {
            SokobanMap map = new SokobanMap();
            map.setFromStrings(new string[]
                                   {
            "~~~###~~~~~",
            "~~##.#~####",
            "~##..###..#",
            "##.X......#",
            "#...PX.#..#",
            "###.X###..#",
            "~~#..#OO..#",
            "~##.##O#.##",
            "~#......##~",
            "~#.....##~~",
            "~#######~~~"
                                   });

            Bitmap result = CrateAnalysis.BuildCrateMoveMap(map, new VectorInt(3,3));
            Assert.IsNotNull(result);

            Debug.WriteLine(map.ToString());
            Debug.WriteLine(result.ToString());
            Debug.WriteLine("done.");
        }
Beispiel #4
0
 /// <summary>
 /// Copy Constructor
 /// </summary>
 /// <param name="copy"></param>
 public SokobanMap(SokobanMap copy)
 {
     map = new CellStates[copy.Size.X, copy.Size.Y];
     for (int px = 0; px < Size.X; px++)
         for (int py = 0; py < Size.Y; py++)
             this[new VectorInt(px, py)] = copy[new VectorInt(px, py)];
 }
Beispiel #5
0
 public PuzzleMap(Puzzle owner)
 {
     puzzle = owner;
     map = new SokobanMap();
     solutions = new List<Solution>();
     isMasterMap = null;
 }
        /// <summary>
        /// Strong Contruction
        /// </summary>
        /// <param name="map">Map to solve</param>
        public SolverController(SokobanMap map)
        {
            if (map == null) throw new ArgumentNullException("map");
            this.settings = new Settings();

            this.state = States.NotStarted;
            this.map = map;
            debugReport = new SolverReport();

            debugReport.AppendHeading(1, "SokoSolve | Solver Debug Report v{0}", ProgramVersion.VersionString);

            debugReport.Append("Creating Statistics");
            stats = new SolverStats(this);
            debugReport.Append("Creating Exit Conditions");
            exitConditions = new ItteratorExitConditions();

            // TODO: This should be configured, perhaps via a factory pattern
            debugReport.Append("Creating Forward Strategy");
            strategy = new SolverStrategy(this);
            debugReport.Append("Creating Forward Evaluator");
            evaluator = new Evaluator<SolverNode>(true);

            debugReport.Append("Creating Reverse Strategy");
            reverseStrategy = new ReverseStrategy(this);
            debugReport.Append("Creating Reverse Evaluator");
            reverseEvaluator = new Evaluator<SolverNode>(true);
        }
Beispiel #7
0
 public PuzzleMap(PuzzleMap clone)
 {
     puzzle = clone.puzzle;
     map = new SokobanMap(clone.map);
     solutions = new List<Solution>(clone.solutions);
     details = new GenericDescription(clone.details);
     rating = clone.rating;
     mapID = clone.mapID;
     isMasterMap = clone.IsMasterMap;
 }
 public static IBitmap GenerateFloorMap(SokobanMap Map)
 {
     Bitmap boundry = Map.ToBitmap(CellStates.Floor);
     boundry = boundry.BitwiseOR(Map.ToBitmap(CellStates.FloorCrate));
     boundry = boundry.BitwiseOR(Map.ToBitmap(CellStates.FloorGoal));
     boundry = boundry.BitwiseOR(Map.ToBitmap(CellStates.FloorGoalCrate));
     boundry = boundry.BitwiseOR(Map.ToBitmap(CellStates.FloorGoalPlayer));
     boundry = boundry.BitwiseOR(Map.ToBitmap(CellStates.FloorPlayer));
     return boundry;
 }
        /// <summary>
        /// Render the current map
        /// </summary>
        /// <param name="Map"></param>
        /// <returns></returns>
        public Image Draw(SokobanMap Map)
        {
            if (Map == null) return null;

            Bitmap canvas = new Bitmap(Map.Size.X * tileSize.X, Map.Size.Y * tileSize.Y);
            Graphics gg = Graphics.FromImage(canvas);

            DrawWithGraphics(Map, gg);

            return canvas;
        }
Beispiel #10
0
        /// <summary>
        /// Generate a static move map, ie. all positions for which a player could move to in the course of a puzzle
        /// </summary>
        /// <param name="Map"></param>
        /// <returns></returns>
        public static Bitmap GenerateStaticMoveMap(SokobanMap Map)
        {
            Bitmap boundry = GenerateBoundryMap(Map);

            // Flood fill from player position to remove any unreachable positions
            FloodFillStrategy floodFill = new FloodFillStrategy(boundry, Map.Player);
            Evaluator<LocationNode> eval = new Evaluator<LocationNode>();
            eval.Evaluate(floodFill);

            return floodFill.Result;
        }
        /// <summary>
        /// Find the shortest path from the player position to a destination position
        /// </summary>
        /// <param name="Map">Map</param>
        /// <param name="Destination">Destination goal position</param>
        /// <returns>NULL if no path is found</returns>
        public static List<VectorInt> FindPlayerPath(SokobanMap Map, VectorInt Destination)
        {
            Bitmap boundry = MapAnalysis.GenerateBoundryMap(Map);
            boundry = boundry.BitwiseOR(MapAnalysis.GenerateCrateMap(Map));

            // Find all positble moves for the player
            FloodFillStrategy floodFill = new FloodFillStrategy(boundry, Map.Player);
            Evaluator<LocationNode> eval = new Evaluator<LocationNode>();
            eval.Evaluate(floodFill);

            List<LocationNode> result = floodFill.GetShortestPath(Destination);
            if (result == null) return null;

            // Path found, convert to VectoInt
            return result.ConvertAll<VectorInt>(delegate(LocationNode item) { return item.Location; });
        }
Beispiel #12
0
        /// <summary>
        /// Strong Construction
        /// </summary>
        /// <param name="aMap"></param>
        public Game(Puzzle aPuzzle, SokobanMap aMap)
        {
            if (aMap == null) throw new ArgumentNullException("aPuzzle");
            StringCollection sc = null;
            if (!aMap.isValid(out sc)) throw new Exception(sc[0]);

            puzzle = aPuzzle;
            startPuzzle = aMap;
            current = new SokobanMap(StartPuzzle);
            moves = new Stack<Move>();

            stats = new Stats();
            stats.Start = DateTime.MinValue;
            stats.End = DateTime.MinValue;

            bookmarks = new List<Bookmark>();
        }
        protected SolverResult Solve(SokobanMap puzzle)
        {
            using (CodeTimer timer = new CodeTimer("TestSolver.Solve(...)"))
            {
                SolverController controller = new SolverController(puzzle);
                try
                {
                    System.Console.WriteLine(puzzle.ToString());

                    SolverResult results = controller.Solve();
                    if (results.Exception != null)
                    {
                        // Bubble up
                        throw new Exception("Solver Failed Iternally", results.Exception);
                    }

                    if (results.Status == SolverResult.CalculationResult.SolutionFound)
                    {
                        // Check that
                        Assert.IsTrue(results.HasSolution, "State says a solution was found, but none are listed in solutions list");
                    }

                    if (results.HasSolution)
                    {
                        int cc = 0;
                        foreach (Solution solution in results.Solutions)
                        {
                            string testRes = "Error";
                            Assert.IsTrue(solution.Test(puzzle, out testRes), testRes);
                            Console.WriteLine("Testing solution: {0} - {1}", cc, testRes);
                            cc++;
                        }
                    }

                    return results;
                }
                finally
                {
                    timer.Stop();
                    Console.WriteLine(controller.DebugReport.ToString(new DebugReportFormatter()));
                    System.Console.WriteLine("Total Time: " + timer.Duration(1));
                    System.Console.WriteLine("---");
                }
            }
        }
        public void TestReverseStrategyCoreSimple()
        {
            CodeTimer timer = new CodeTimer("");
            timer.Start();

            try
            {
                SokobanMap map = new SokobanMap();
                map.SetFromStrings(new string[]
                                   {
             "~##~#####",
              "##.##.O.#",
              "#.##.XO.#",
              "~##.X...#",
              "##.XP.###",
              "#.X..##~~",
              "#OO.##.##",
              "#...#~##~",
              "#####~#~~"
                                   });

                PuzzleMap pMap = new PuzzleMap((Puzzle)null);
                pMap.Map = map;

                SolverController controller = new SolverController(pMap);
                controller.Init();
                controller.State = SolverController.States.Running; // Manually set state, as we are not using the controller; but the strategy uses the controller to check if it should exit

                ReverseStrategy rev = new ReverseStrategy(controller);

                Evaluator<SolverNode> eval = new Evaluator<SolverNode>();
                EvalStatus result =  eval.Evaluate(rev);

                Assert.AreEqual(EvalStatus.CompleteSolution, result, "Should find a solution");
            }
            finally
            {
                timer.Stop();
                System.Console.WriteLine("Total Time: " + timer.Duration(1));
            }
        }
Beispiel #15
0
        /// <summary>
        /// Is the player adjacent to a puzzle position
        /// </summary>
        /// <param name="PuzzlePos"></param>
        /// <returns></returns>
        private static Direction IsAdjacentPlayer(SokobanMap Map, VectorInt PuzzlePos)
        {
            VectorInt playerPos = Map.Player;
            if (playerPos.Offset(Direction.Up) == PuzzlePos) return Direction.Up;
            if (playerPos.Offset(Direction.Down) == PuzzlePos) return Direction.Down;
            if (playerPos.Offset(Direction.Left) == PuzzlePos) return Direction.Left;
            if (playerPos.Offset(Direction.Right) == PuzzlePos) return Direction.Right;

            return Direction.None;
        }
Beispiel #16
0
 /// <summary>
 /// Construct from a previous move
 /// </summary>
 /// <param name="aBefore"></param>
 /// <param name="aMoveDirection"></param>
 public Move(SokobanMap aBefore, Direction aMoveDirection, bool aIsPush)
 {
     Before = aBefore;
     MoveDirection = aMoveDirection;
     isPush = aIsPush;
 }
        private SokobanMap BuildCurrentMap(SolverNode node)
        {
            SokobanMap result = new SokobanMap();
            result.Init(node.CrateMap.Size);

            for (int cx = 0; cx < result.Size.Width; cx++)
                for (int cy = 0; cy < result.Size.Height; cy++)
                {
                    if (controller.StaticAnalysis.WallMap[cx, cy]) result[cx, cy] = CellStates.Wall;
                    if (controller.StaticAnalysis.FloorMap[cx, cy]) result[cx, cy] = CellStates.Floor;
                    if (controller.StaticAnalysis.GoalMap[cx, cy])
                        result.SetState(new VectorInt(cx, cy), Cell.Goal);
                    if (node.CrateMap[cx, cy]) result.SetState(new VectorInt(cx, cy), Cell.Crate);
                    result.SetState(node.PlayerPosition, Cell.Player);
                }

            return result;
        }
Beispiel #18
0
        public void TestFindCratePath()
        {
            SokobanMap map = new SokobanMap();
            map.setFromStrings(new string[]
                                   {
            "~~~###~~~~~",
            "~~##.#~####",
            "~##..###..#",
            "##.X......#",
            "#...PX.#..#",
            "###.X###..#",
            "~~#..#OO..#",
            "~##.##O#.##",
            "~#......##~",
            "~#.....##~~",
            "~#######~~~"
                                   });

            CrateAnalysis.ShortestCratePath result = CrateAnalysis.FindCratePath(map, new VectorInt(3, 3), new VectorInt(9, 5));
            Assert.IsNotNull(result);

            Debug.WriteLine(map.ToString());
            Debug.WriteLine(result.CratePath.ToString());
            Debug.WriteLine(result.PlayerPath.ToString());
            Debug.WriteLine("done.");
        }
        protected SolverResult SolveFromString(string[] puzzle)
        {
            SokobanMap map = new SokobanMap();
            map.SetFromStrings(puzzle);

            return Solve(map);
        }
Beispiel #20
0
        public void TestFindCratePath_DeeperTest()
        {
            string mapString =
            @"~~~~~~~~~~~#####
            ~~~~~~~~~~##...#
            ~~~~~~~~~~#....#
            ~~~~####~~#.X.##
            ~~~~#..####X.X#~
            ~~~~#.....X.X.#~
            ~~~##.##.X.X.X#~
            ~~~#..O#..X.X.#~
            ~~~#..O#......#~
            #####.#########~
            #OOOO.P..#~~~~~~
            #OOOO....#~~~~~~
            ##..######~~~~~~
            ~####~~~~~~~~~~~";

            SokobanMap map = new SokobanMap();
            map.setFromString(mapString);

            CrateAnalysis.ShortestCratePath result = CrateAnalysis.FindCratePath(map, new VectorInt(10, 7), new VectorInt(7, 5));
            Assert.IsNull(result, "There is not cratemovepath to from position 10,7 to 7,5");
        }
Beispiel #21
0
        /// <summary>
        /// Perform a player move. This is the function that encapsulated the game moves.
        /// </summary>
        /// <param name="moveDir">Direction of move/push</param>
        /// <returns>Result of the move</returns>
        /// <remarks>
        ///     Basic puzzle rules:
        ///     <list type="">
        ///         <item>Player can push, never pull, a crate into a space</item>
        ///         <item>There are no diagonal moves, or pushes</item>
        ///         <item>When all crates are on goal positions the puzzle is complete</item>
        ///         <item>Two crates cannot be pushed in a line</item>
        ///     </list>
        /// </remarks>
        public virtual MoveResult Move(Direction moveDir)
        {
            // First Move?
            if (Stats.Start == DateTime.MinValue)
            {
                Stats.Start = DateTime.Now;
            }

            Stats.Moves++;
            Stats.MovesTotal++;

            VectorInt curr = Current.Player;
            VectorInt currstep = Current.Player.Offset(moveDir);
            VectorInt currstepstep = currstep.Offset(moveDir);
            CellStates p_curr = Current[curr];
            CellStates p_currstep  = Current[currstep];

            CellStates p_currstepstep = CellStates.Void;
            if (Current.Rectangle.Contains(currstepstep))
                p_currstepstep = Current[currstepstep];

            // Is the move valid
            if (p_currstep == CellStates.Wall || p_currstep == CellStates.Void) return MoveResult.Invalid;

            // Next map
            SokobanMap next = new SokobanMap(Current);

            // Step?
            if (p_currstep == CellStates.Floor || p_currstep == CellStates.FloorGoal)
            {
                // Step, no push

                // New player pos
                if (p_currstep == CellStates.Floor) next[currstep] = CellStates.FloorPlayer;
                    else next[currstep] = CellStates.FloorGoalPlayer;

                // Old pos
                if (p_curr == CellStates.FloorGoalPlayer) next[curr] = CellStates.FloorGoal;
                if (p_curr == CellStates.FloorPlayer) next[curr] = CellStates.Floor;

                Moves.Push(new Move(Current, moveDir, false));
                Current = next;
                return MoveResult.ValidMove;
            }

            // Push?
            if (p_currstep == CellStates.FloorCrate || p_currstep == CellStates.FloorGoalCrate)
            {
                Stats.Pushes++;
                Stats.PushesTotal++;
                // Push a crate

                // Valid push?
                if (p_currstepstep == CellStates.Floor || p_currstepstep == CellStates.FloorGoal)
                {
                    // Valid.

                    // Old pos
                    if (p_curr == CellStates.FloorGoalPlayer) next[curr] = CellStates.FloorGoal;
                    if (p_curr == CellStates.FloorPlayer) next[curr] = CellStates.Floor;

                    // Player Pos
                    if (p_currstep == CellStates.FloorCrate) next[currstep] = CellStates.FloorPlayer;
                    if (p_currstep == CellStates.FloorGoalCrate) next[currstep] = CellStates.FloorGoalPlayer;

                    // Crate Pos
                    if (p_currstepstep == CellStates.Floor) next[currstepstep] = CellStates.FloorCrate;
                    if (p_currstepstep == CellStates.FloorGoal) next[currstepstep] = CellStates.FloorGoalCrate;

                    Moves.Push(new Move(Current, moveDir, true));
                    Current = next;

                    // Has the map been solved
                    if (Current.isPuzzleComplete())
                    {
                        Stats.End = DateTime.Now;
                        return MoveResult.ValidPushWin;
                    }

                    // Normal push, or crate onto goal
                    if (next[currstepstep] == CellStates.FloorGoalCrate)
                    {
                        return MoveResult.ValidPushGoal;
                    }

                    return MoveResult.ValidPush;
                }
                else
                {
                    // Cannot push
                    return MoveResult.Invalid;
                }
            }
            return MoveResult.Invalid;
        }
Beispiel #22
0
        /// <summary>
        /// Convert a pixel location to a map cell location
        /// </summary>
        /// <returns></returns>
        public RectangleInt GetPixelPosition(SokobanMap Map, VectorInt cell)
        {
            VectorInt pos = cell.Divide(tileSize);

            return new RectangleInt(pos, pos.Add(Map.Size));
        }
Beispiel #23
0
 /// <summary>
 /// Reset to the start position
 /// </summary>
 public virtual void Reset()
 {
     Stats.Restarts++;
     Stats.Moves = 0;
     Stats.Pushes = 0;
     Current = new SokobanMap(StartPuzzle);
     Moves = new Stack<Move>();
 }
Beispiel #24
0
 /// <summary>
 /// Draw a puzzle
 /// </summary>
 /// <param name="Map"></param>
 /// <param name="DrawSource"></param>
 public void DrawWithGraphicsColours(SokobanMap Map, Graphics DrawSource)
 {
     for (int cx = 0; cx < Map.Size.X; cx++)
         for (int cy = 0; cy < Map.Size.Y; cy++)
         {
             DrawSource.FillRectangle(tileColours[(int)Map[new VectorInt(cx, cy)]],
                 cx * tileSize.X, cy * tileSize.Y, tileSize.X, tileSize.Y);
         }
 }
Beispiel #25
0
        /// <summary>
        /// Convert a pixel location to a map cell location
        /// </summary>
        /// <param name="Map"></param>
        /// <param name="pixel"></param>
        /// <returns></returns>
        public VectorInt GetCellPosition(SokobanMap Map, VectorInt pixel)
        {
            VectorInt pos = pixel.Divide(tileSize);

            if (pos.X >= Map.Size.X || pos.Y >= Map.Size.Y) return VectorInt.MinValue;

            return pos;
        }
Beispiel #26
0
        /// <summary>
        /// Draw a puzzle
        /// </summary>
        /// <param name="Map"></param>
        /// <param name="DrawSource"></param>
        public void DrawWithGraphics(SokobanMap Map, Graphics DrawSource)
        {
            for (int cx = 0; cx < Map.Size.X; cx++)
                for (int cy = 0; cy < Map.Size.Y; cy++)
                {
                    Image tile = tiles[(int) Map[new VectorInt(cx, cy)]];
                    if (tile != null)
                    {
                        DrawSource.DrawImage(tile,cx * tileSize.X, cy * tileSize.Y, tileSize.X, tileSize.Y);
                    }

                }
        }
        /// <summary>
        /// Build the result from the completed solver
        /// </summary>
        /// <param name="controller"></param>
        public void Build(SolverController controller)
        {
            map = controller.Map;

            // Build type strong info class
            info = new SolverResultInfo();
            info.TotalNodes = (int) controller.Stats.Nodes.ValueTotal;
            info.TotalSeconds = controller.Stats.EvaluationTime.ValueTotal;
            info.Machine = DebugHelper.GetCPUDescription();
            info.RatingScore = PuzzleAnalysis.CalcRating(map);
            foreach (Statistic stat in controller.Stats.Stats)
            {
                SolverLabel lb = stat.GetDisplayData();

                info.InfoValues.Add(new NameValuePair() {Name = lb.Name, Value = lb.Value});
            }

            // Build the exit status and summary
            if (HasSolution)
            {
                // General Sucess
                status = CalculationResult.SolutionFound;
                StringBuilder sb = new StringBuilder();
                if (solutions.Count == 1)
                {
                    sb.Append("Solution");
                }
                else
                {
                    sb.AppendFormat("{0} solutions", solutions.Count);
                }

                sb.AppendFormat(" in {0} and {1} nodes at {2:0} nodes/s (puzzle score {3})",
                                StringHelper.ToString(TimeSpan.FromSeconds(info.TotalSeconds)),
                                info.TotalNodes,
                                info.TotalNodes/info.TotalSeconds,
                                info.RatingScore);
                summary = sb.ToString();
            }
            else
            {
                if (exception != null)
                {
                    status = CalculationResult.Error;
                    summary = string.Format("Error ({0})", exception.Message);
                }
                else
                {
                    if (controllerResult == SolverController.States.Cancelled)
                    {
                        status = CalculationResult.Cancelled;
                        summary =
                            string.Format(" Cancelled after {0} sec and {1} nodes at {2:0} nodes/s (puzzle score {3})",
                                          StringHelper.ToString(TimeSpan.FromSeconds(info.TotalSeconds)),
                                          info.TotalNodes,
                                          info.TotalNodes/info.TotalSeconds,
                                          info.RatingScore);
                    }
                    else if (controllerResult == SolverController.States.CompleteNoSolution)
                    {
                        status = CalculationResult.NoSolution;
                        summary = "The puzzle does not have a solution";
                    }
                    else
                    {
                        status = CalculationResult.GaveUp;
                        summary = string.Format(
                                "The solver could not find a within the given constraints. Tried for {0} sec and {1} nodes at {2:0} nodes/s (puzzle score {3})",
                                StringHelper.ToString(TimeSpan.FromSeconds(info.TotalSeconds)),
                                info.TotalNodes,
                                info.TotalNodes/info.TotalSeconds,
                                info.RatingScore);
                    }
                }
            }
        }
Beispiel #28
0
 public static Bitmap GenerateWallMap(SokobanMap map)
 {
     Bitmap result = map.ToBitmap(CellStates.Wall);
     return result;
 }
Beispiel #29
0
 /// <summary>
 /// Get a simple boundry map
 /// </summary>
 /// <param name="Map"></param>
 /// <returns></returns>
 public static Bitmap GenerateBoundryMap(SokobanMap Map)
 {
     Bitmap boundry = Map.ToBitmap(CellStates.Wall);
     boundry = boundry.BitwiseOR(Map.ToBitmap(CellStates.Void));
     return boundry;
 }
Beispiel #30
0
 /// <summary>
 /// Get map for the crates
 /// </summary>
 /// <param name="Map"></param>
 /// <returns></returns>
 public static Bitmap GenerateCrateMap(SokobanMap Map)
 {
     Bitmap result = Map.ToBitmap(CellStates.FloorCrate);
     result = result.BitwiseOR(Map.ToBitmap(CellStates.FloorGoalCrate));
     return result;
 }