public List<Step[]> GetSolution(BoardState state, BoardGoal goal, CancellationToken cancellationToken)
        {
            if (state == null)
            {
                throw new ArgumentNullException(nameof(state));
            }

            if (goal == null)
            {
                throw new ArgumentNullException(nameof(goal));
            }

            if (state.Width != goal.Width || state.Height != goal.Height)
            {
                throw new ArgumentException($"The state ({state.Width}x{state.Height}) and the goal ({goal.Width}x{goal.Height}) has different sizes.");
            }

            PriorityQueueV1 openSet = new PriorityQueueV1();
            openSet.Enqueue(NodeV1.CreateInitialNode(state, goal));
            while (true)
            {
                NodeV1 current = openSet.Dequeue();
                if (current.State.Satisfies(goal))
                {
                    return new List<Step[]> { GetPathFrom(current).Reverse().ToArray() };
                }

                foreach (NodeV1 neighbor in current.GetNeighbors(goal))
                {
                    openSet.Enqueue(neighbor);
                }

                cancellationToken.ThrowIfCancellationRequested();
            }
        }
 private NodeV1(NodeV1 parentNode, Step previousStep, BoardState state, int distanceFromInitialNode, int estimatedDistanceToGoal)
 {
     this.ParentNode = parentNode;
     this.PreviousStep = previousStep;
     this.State = state;
     this.distanceFromInitialNode = distanceFromInitialNode;
     this.Cost = distanceFromInitialNode + estimatedDistanceToGoal;
 }
        public List<Step[]> GetSolution(BoardState state, BoardGoal goal, CancellationToken cancellationToken)
        {
            if (state == null)
            {
                throw new ArgumentNullException(nameof(state));
            }

            if (goal == null)
            {
                throw new ArgumentNullException(nameof(goal));
            }

            if (state.Width != goal.Width || state.Height != goal.Height)
            {
                throw new ArgumentException($"The state ({state.Width}x{state.Height}) and the goal ({goal.Width}x{goal.Height}) has different sizes.");
            }

            List<Step[]> solutions = new List<Step[]>();
            int targetCost = int.MaxValue;

            PriorityQueueV5 openSet = new PriorityQueueV5();
            openSet.Enqueue(NodeV5.CreateInitialNode(state, goal));
            while (true)
            {
                NodeV5 current = openSet.Dequeue();
                if (current.Cost > targetCost)
                {
                    break;
                }

                if (current.EstimatedDistanceToGoal == 0)
                {
                    Step[] solution = GetPathFrom(current).Reverse().ToArray();
                    solutions.Add(solution);
                    if (solution.Length < targetCost)
                    {
                        targetCost = solution.Length;
                    }

                    continue;
                }

                foreach (NodeV5 neighbor in current.GetNeighbors(goal))
                {
                    if (!openSet.Contains(neighbor))
                    {
                        openSet.Enqueue(neighbor);
                    }
                }

                cancellationToken.ThrowIfCancellationRequested();
            }

            return solutions;
        }
        public void Test1()
        {
            // Arrange
            BoardState state = new BoardState(3, 3, new[] { 1, 2, 0, 4, 5, 3, 7, 8, 6 });
            BoardGoal goal = BoardGoal.CreateCompleted(3, 3);
            IBoardSolverService solver = new BoardSolverService();

            // Act
            Step[] steps = solver.GetSolution(state, goal, CancellationToken.None)[0];

            // Assert
            CollectionAssert.AreEqual(new[] { Step.Up, Step.Up }, steps);
        }
        public GameService([NotNull] IMessageBus messageBus, [NotNull] IBoardGeneratorService boardGeneratorService)
        {
            if (messageBus == null)
            {
                throw new ArgumentNullException(nameof(messageBus));
            }

            if (boardGeneratorService == null)
            {
                throw new ArgumentNullException(nameof(boardGeneratorService));
            }

            this.messageBus = messageBus;
            this.boardGeneratorService = boardGeneratorService;

            this.drill = Drill.CreateNew("Default", BoardTemplate.CreateEmpty(4, 4), BoardGoal.CreateCompleted(4, 4));
            this.InitialState = this.boardGeneratorService.Generate(this.drill.Template, this.drill.Goal);
            this.boardState = this.InitialState;
        }
        public void Scramble()
        {
            this.InitialState = this.boardGeneratorService.Generate(this.drill.Template, this.drill.Goal);
            this.BoardState = this.InitialState;

            this.messageBus.Publish(new BoardScrambled());
        }
        private static int GetManhattanDistance(BoardState state, BoardGoal goal)
        {
            int width = state.Width;
            int height = state.Height;

            int sum = 0;
            for (int i = 0; i < goal.TileCount; i++)
            {
                if (goal[i] == 0)
                {
                    continue;
                }

                for (int j = 0; j < state.TileCount; j++)
                {
                    if (state[j] == goal[i])
                    {
                        int x1 = i % width;
                        int y1 = i / height;
                        int x2 = j % width;
                        int y2 = j / height;

                        sum += Math.Abs(x1 - x2) + Math.Abs(y1 - y2);
                        break;
                    }
                }
            }

            return sum;
        }
 public static NodeV1 CreateInitialNode(BoardState state, BoardGoal goal)
 {
     return new NodeV1(state, GetManhattanDistance(state, goal));
 }
 private NodeV1(BoardState state, int estimatedDistanceToGoal)
 {
     this.State = state;
     this.Cost = estimatedDistanceToGoal;
 }
 private NodeV1 CreateNeighbor(Step previousStep, BoardState newState, BoardGoal goal)
 {
     return new NodeV1(this, previousStep, newState, this.distanceFromInitialNode + 1, GetManhattanDistance(newState, goal));
 }
        private void OnSolved(BackgroundJob job, Step[][] solutions)
        {
            if (job.CancellationTokenSource.IsCancellationRequested)
            {
                return;
            }

            this.Status = SolverServiceStatus.Solved;
            this.Solutions = solutions
                .OrderBy(solution => string.Join(",", solution))
                .Select(solution => solution.Select(step => new SolutionStep(step, SolutionStepStatus.NotSteppedYet)).ToList())
                .ToList();

            this.SolutionLength = solutions[0].Length;
            this.nextStepIndex = 0;
            this.solvedBoardState = job.StateToSolve;

            this.messageBus.Publish(new SolutionsFound(job.InitialState, job.StateToSolve, solutions));
        }
        public static SimplifiedBoardState Create(BoardState state, BoardGoal goal)
        {
            int[] values = new int[state.TileCount];
            int eye = 0;

            for (int i = 0; i < state.TileCount; i++)
            {
                if (state[i] == 0)
                {
                    eye = i;
                    values[i] = -1;
                    continue;
                }

                for (int j = 0; j < goal.TileCount; j++)
                {
                    if (state[i] == goal[j])
                    {
                        values[i] = state[i];
                        break;
                    }
                }
            }

            return new SimplifiedBoardState(state.Width, state.Height, values, eye);
        }