void CheckObjectif(ref SokobanGameState gs)
 {
     foreach (var item in gs.Grid)
     {
         if (item.state == State.Objective)
         {
             foreach (var bloc in gs.caisses)
             {
                 if (bloc.position == item.position)
                 {
                     item.state = State.ObjectiveAccomplish;
                     break;
                 }
             }
         }
         if (item.state == State.ObjectiveAccomplish)
         {
             var test = false;
             foreach (var bloc in gs.caisses)
             {
                 if (item.position == bloc.position)
                 {
                     test = true;
                     break;
                 }
             }
             if (!test)
             {
                 item.state = State.Objective;
             }
         }
     }
 }
        public bool IsAvailable(SokobanGameState gameState)
        {
            var pos = gameState.playerPosition + direction;
            var t   = gameState.Grid[pos.x, pos.y];

            switch (t.state)
            {
            case State.Walkable:
                return(true);

            case State.Objective:
                return(true);

            case State.Unwalkable:
                return(false);

            case State.ObjectiveAccomplish:
                return(false);

            case State.Caisse:
                var nextPos  = pos + direction;
                var nextTile = gameState.Grid[nextPos.x, nextPos.y];
                if (nextTile.state == State.Objective || nextTile.state == State.Walkable)
                {
                    return(true);
                }

                return(false);

            default:
                return(false);
            }
        }
        public bool Perform(ref SokobanGameState gameState)
        {
            // Move
            var nextPos  = gameState.playerPosition + this.direction;
            var nextTile = gameState.Grid[nextPos.x, nextPos.y];
            var res      = false;

            switch (nextTile.state)
            {
            case State.Caisse:
                if (IsWalkableState(TestNextTileAfterBloc(nextPos, ref gameState)))
                {
                    for (int i = 0; i < gameState.caisses.Count; i++)
                    {
                        if (gameState.caisses[i].position == nextPos)
                        {
                            gameState.Grid[gameState.playerPosition.x, gameState.playerPosition.y].state = State.Walkable;
                            gameState.caisses[i].Move(direction);
                            var caisse = gameState.caisses[i];
                            if (gameState.Grid[caisse.position.x, caisse.position.y].state == State.Objective)
                            {
                                gameState.Grid[caisse.position.x, caisse.position.y].state = State.ObjectiveAccomplish;
                                res = true;
                            }
                            if (gameState.Grid[caisse.position.x, caisse.position.y].state == State.Walkable)
                            {
                                gameState.Grid[gameState.caisses[i].position.x, gameState.caisses[i].position.y].state = State.Caisse;
                            }
                            gameState.playerPosition = nextPos;
                            gameState.Grid[nextPos.x, nextPos.y].state = State.Player;
                            var blocPos = gameState.caisses[i].position;

                            if (gameState.CheckFinish())
                            {
                                Debug.Log("FINISH");
                            }
                        }
                    }
                }
                break;

            case State.Walkable:
                var currentState = gameState.Grid[gameState.playerPosition.x, gameState.playerPosition.y].state;
                gameState.Grid[gameState.playerPosition.x, gameState.playerPosition.y].state = currentState == State.Objective ? State.Objective : State.Walkable;
                gameState.playerPosition += direction;
                gameState.Grid[gameState.playerPosition.x, gameState.playerPosition.y].state = State.Player;
                break;

            case State.Unwalkable:
                break;

            case State.Objective:
                var state = gameState.Grid[gameState.playerPosition.x, gameState.playerPosition.y].state;
                gameState.Grid[gameState.playerPosition.x, gameState.playerPosition.y].state = state == State.Objective ? State.Objective : State.Walkable;
                gameState.playerPosition += direction;
                break;

            case State.ObjectiveAccomplish:
                if (IsWalkableState(TestNextTileAfterBloc(nextPos, ref gameState)))
                {
                    for (int i = 0; i < gameState.caisses.Count; i++)
                    {
                        if (gameState.caisses[i].position == nextPos)
                        {
                            gameState.Grid[gameState.playerPosition.x, gameState.playerPosition.y].state = State.Walkable;
                            gameState.caisses[i].Move(direction);
                            var caisse = gameState.caisses[i];
                            if (gameState.Grid[caisse.position.x, caisse.position.y].state == State.Objective)
                            {
                                gameState.Grid[caisse.position.x, caisse.position.y].state = State.ObjectiveAccomplish;
                                res = true;
                            }
                            if (gameState.Grid[caisse.position.x, caisse.position.y].state == State.Walkable)
                            {
                                gameState.Grid[gameState.caisses[i].position.x, gameState.caisses[i].position.y].state = State.Caisse;
                            }
                            gameState.playerPosition = nextPos;
                            gameState.Grid[nextPos.x, nextPos.y].state = State.Player;
                            var blocPos = gameState.caisses[i].position;

                            if (gameState.CheckFinish())
                            {
                                Debug.Log("FINISH");
                            }
                        }
                    }
                }
                break;
            }
            return(res);
        }
 State TestNextTileAfterBloc(Vector2Int pos, ref SokobanGameState gameState)
 {
     return(gameState.Grid[pos.x + this.direction.x, pos.y + this.direction.y].state);
 }
        private static void ManualSokoban()
        {
            string level = " #####\n #   ####\n #   #  #\n ##    .#\n### ###.#\n# $ # #.#\n# $$# ###\n#@  #\n#####";

            //string level = "####\n# .#\n#  ###\n#*@  #\n#  $ #\n#  ###\n####";
            Log("Level:\n" + level);
            MersenneTwister       rng = new MersenneTwister(1 + threadIndex);
            ISPSimulationStrategy simulationStrategy = new SokobanRandomStrategy();
            SokobanGameState      s           = new SokobanGameState(level, RewardType.NegativeBM, simulationStrategy);
            SokobanGameState      backupState = (SokobanGameState)s.Clone();
            bool        quit = false;
            IPuzzleMove move = null;

            Console.WriteLine(s.PrettyPrint());
            while (!quit)
            {
                ConsoleKeyInfo     input = Console.ReadKey();
                List <IPuzzleMove> moves = s.GetMoves();
                switch (input.Key)
                {
                case ConsoleKey.UpArrow:
                    if (moves.Contains(new SokobanGameMove("u")))
                    {
                        move = new SokobanGameMove("u");
                    }
                    else
                    {
                        move = new SokobanGameMove("U");
                    }
                    break;

                case ConsoleKey.DownArrow:
                    if (moves.Contains(new SokobanGameMove("d")))
                    {
                        move = new SokobanGameMove("d");
                    }
                    else
                    {
                        move = new SokobanGameMove("D");
                    }
                    break;

                case ConsoleKey.LeftArrow:
                    if (moves.Contains(new SokobanGameMove("l")))
                    {
                        move = new SokobanGameMove("l");
                    }
                    else
                    {
                        move = new SokobanGameMove("L");
                    }
                    break;

                case ConsoleKey.RightArrow:
                    if (moves.Contains(new SokobanGameMove("r")))
                    {
                        move = new SokobanGameMove("r");
                    }
                    else
                    {
                        move = new SokobanGameMove("R");
                    }
                    break;

                case ConsoleKey.R:
                    s    = (SokobanGameState)backupState.Clone();
                    move = null;
                    break;

                case ConsoleKey.Q:
                    move = null;
                    quit = true;
                    break;
                }
                if (move != null)
                {
                    Console.WriteLine("Move: " + move);
                    s.DoMove(move);
                }
                Console.WriteLine(s.PrettyPrint());
                Console.WriteLine("Score: " + s.GetScore() + "  |  isTerminal: " + s.isTerminal());
            }
        }
        private static int[] SokobanTest(double const_C, double const_D, int iterations, int restarts, string[] levels, uint seed, bool abstractSokoban, RewardType rewardType, bool stopOnResult, double epsilonValue, bool log)
        {
            uint threadIndex = GetThreadIndex();

            RNG.Seed(seed + threadIndex);
            MersenneTwister       rng = new MersenneTwister(seed + threadIndex);
            int                   currentLevelIndex = GetTaskIndex(threadIndex);
            ISPSimulationStrategy simulationStrategy;

            simulationStrategy = new SokobanEGreedyStrategy(epsilonValue, rng);
            IPuzzleState[] states = new IPuzzleState[levels.Length];

            //SokobanMCTSStrategy player;

            //Debug.WriteLine("Solved random: "+randomSolved+"/"+levels.Length);

            //    Debug.WriteLine(restart);


            int solvedLevels = 0;

            int[]      rolloutsCount = new int[states.Length];
            Stopwatch  stopwatch     = new Stopwatch();
            long       stateInitializationTime;
            long       solvingTime;
            int        totalRollouts         = 0;
            int        totalNodes            = 0;
            List <int> visitsList            = new List <int>();
            List <int> raveVisitsList        = new List <int>();
            double     totalDepth            = 0;
            int        totalNodesEliminated  = 0;
            int        totalNodesNotExpanded = 0;

            for (int i = 0; i < states.Length; i++)
            {
                RNG.Seed(seed + threadIndex);
                rng = new MersenneTwister(seed + threadIndex);
                if (simulationType == SimulationType.EpsilonGreedy)
                {
                    simulationStrategy = new SokobanEGreedyStrategy(epsilonValue, rng);
                }
                else
                {
                    simulationStrategy = new SokobanIDAstarStrategy(maxNodes, tableSize, 200, 0.2);
                }
                if (i % SinglePlayerMCTSMain.threadIndex != threadIndex)
                {
                    continue;
                }
                stopwatch.Restart();
                if (abstractSokoban)
                {
                    states[i] = new AbstractSokobanState(levels[i], rewardType, useNormalizedPosition, useGoalMacro, useTunnelMacro, useGoalCut, simulationStrategy, rng);
                }
                else
                {
                    states[i] = new SokobanGameState(levels[i], rewardType, simulationStrategy);
                }
                stopwatch.Stop();
                stateInitializationTime = stopwatch.ElapsedMilliseconds;
                List <IPuzzleMove> moveList = new List <IPuzzleMove>();
                //player = new SokobanMCTSStrategy(rng, iterations, 600, null, const_C, const_D, stopOnResult);

                //SP_MCTSAlgorithm mcts = new SP_MCTSAlgorithm(new SP_UCTTreeNodeCreator(const_C, const_D, rng), stopOnResult);

                OptMCTSAlgorithm mcts  = new OptMCTSAlgorithm(new Opt_SP_UCTTreeNodeCreator(const_C, const_D, rng, ucb1Tuned, rave, raveThreshold, nodeRecycling), iterations, memoryBudget, stopOnResult, avoidCycles, useNodeElimination);
                string           moves = "";
                stopwatch.Restart();
                moveList = mcts.Solve(states[i], iterations);
                stopwatch.Stop();
                solvingTime = stopwatch.ElapsedMilliseconds;

                //moveList = player.GetSolution(states[i]);
                int pushCount = 0;

                foreach (IPuzzleMove m in moveList)
                {
                    if (abstractSokoban)
                    {
                        //Debug.WriteLine("Move: " + m);
                        //Debug.WriteLine(states[i]);
                        SokobanPushMove push = (SokobanPushMove)m;
                        foreach (IPuzzleMove basicMove in push.MoveList)
                        {
                            moves += basicMove;
                            if (basicMove.move > 3)//the move is a push move
                            {
                                pushCount++;
                            }
                        }
                    }
                    else
                    {
                        moves += m;
                        if (m.move > 3)//the move is a push move
                        {
                            pushCount++;
                        }
                    }
                    states[i].DoMove(m);
                }
                if (states[i].EndState())
                {
                    solvedLevels++;
                    totalRollouts += mcts.IterationsForFirstSolution;
                }
                else
                {
                    totalRollouts += mcts.IterationsExecuted;
                }
                totalDepth            += mcts.maxDepth;
                totalNodesEliminated  += mcts.nodesEliminated;
                totalNodesNotExpanded += mcts.nodesNotExpanded;
                rolloutsCount[i]       = mcts.IterationsExecuted;
                scores[i]              = rolloutsCount[i];
                solved[i]              = states[i].EndState();
                if (log)
                {
                    Log("Level " + (i + 1) + "\titerations: " + mcts.IterationsExecuted + "\titerations for first solution: " + mcts.IterationsForFirstSolution + "\ttotal solutions: " + mcts.SolutionCount + "\tbest solution length (moves/pushes): " + moves.Count() + "/" + pushCount + "\tInit Time: " + TimeFormat(stateInitializationTime) + " - Solving Time: " + TimeFormat(solvingTime) + "\tTree depth: " + mcts.maxDepth + "\tNodes: " + mcts.NodeCount + "\tNodes Eliminated: " + mcts.nodesEliminated + "\tNodes Not Expanded: " + mcts.nodesNotExpanded + "\tBest solution: " + moves);
                }
                totalNodes += mcts.NodeCount;
                visitsList.AddRange(mcts.visits);
                raveVisitsList.AddRange(mcts.raveVisits);
                Console.Write("\r                              ");
                Console.Write("\rSolved " + solvedLevels + "/" + (i + 1));
            }
            visitsList.Sort((x, y) => (x.CompareTo(y)));
            raveVisitsList.Sort((x, y) => (x.CompareTo(y)));
            double avgVisits = 0;

            foreach (int v in visitsList)
            {
                avgVisits += v;
            }
            double avgRaveVisits = 0;

            foreach (int v in raveVisitsList)
            {
                avgRaveVisits += v;
            }
            avgVisits     /= visitsList.Count;
            avgRaveVisits /= raveVisitsList.Count;

            Log("Solved " + solvedLevels + "/" + levels.Length);
            Log("Total iterations: " + totalRollouts);
            Log("Total nodes: " + totalNodes);
            Log("Nodes eliminated: " + totalNodesEliminated);
            Log("Nodes Not Expanded: " + totalNodesNotExpanded);
            Log("avg nodes:" + ((double)totalNodes) / states.Length);
            Log("avg visits: " + avgVisits);
            Log("avg raveVisits: " + avgRaveVisits);
            Log("median visits: " + (visitsList.Count % 2 == 0 ? visitsList[visitsList.Count / 2] : (visitsList[visitsList.Count / 2] + visitsList[1 + visitsList.Count / 2]) / 2));
            Log("median raveVisits: " + (raveVisitsList.Count % 2 == 0 ? raveVisitsList[raveVisitsList.Count / 2] : (raveVisitsList[raveVisitsList.Count / 2] + raveVisitsList[1 + raveVisitsList.Count / 2]) / 2));
            Log("avg depth: " + totalDepth / states.Length);
            return(rolloutsCount);
        }