private static void SamegameIDAStarTest(string levelPath, int maxCost, int tableSize)//TODO Need a good heuristic for samegame
        {
            string[]       levels       = ReadSamegameLevels(levelPath);
            IPuzzleState[] states       = new IPuzzleState[levels.Length];
            int            solvedLevels = 0;
            double         totalScore   = 0;

            for (int i = 0; i < states.Length; i++)
            {
                states[i] = new SamegameGameState(levels[i], null, null);
                IDAStarSearch idaStar = new IDAStarSearch();
                Log("Level" + (i + 1) + ":\n" + states[i].PrettyPrint());
                List <IPuzzleMove> solution = null;
                string             moves    = "";
                while (!states[i].isTerminal())
                {
                    solution = idaStar.Solve(states[i], maxCost, tableSize, 100);
                    if (solution.Count > 0)
                    {
                        moves += solution[0];
                        states[i].DoMove(solution[0]);
                    }
                    else
                    {
                        break;
                    }
                }
                if (states[i].EndState())
                {
                    solvedLevels++;
                }
                totalScore += states[i].GetResult();
                Log("Level " + (i + 1) + " solved: " + (states[i].EndState()) + " solution length:" + moves.Count() + " Score: " + states[i].GetResult());
                Log("Moves: " + moves);
                Log("Solved " + solvedLevels + "/" + (i + 1));
                Console.Write("\rSolved " + solvedLevels + "/" + (i + 1));
            }
            Log("Total score: " + totalScore);
        }
        private static void SamegameTest(double const_C, double const_D, int iterations, int restarts, string[] levels, uint seed, bool ucb1Tuned, bool rave, bool nodeRecycling, int memoryBudget)
        {
            uint threadIndex = GetThreadIndex();

            Console.WriteLine("Thread " + threadIndex + " started");
            MersenneTwister rnd = new MersenneTwister(seed + threadIndex);
            int             currentLevelIndex = GetTaskIndex(threadIndex);
            Stopwatch       stopwatch         = new Stopwatch();
            long            stateInitTime;
            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;

            while (currentLevelIndex >= 0)
            {
                ISPSimulationStrategy simulationStrategy = new SamegameTabuColorRandomStrategy(levels[currentLevelIndex], rnd);
                //Console.Write("\rRun " + (restartN + 1) + " of " + restarts + "  ");
                stopwatch.Restart();
                SamegameGameState s = new SamegameGameState(levels[currentLevelIndex], rnd, simulationStrategy);
                stopwatch.Stop();
                stateInitTime = stopwatch.ElapsedMilliseconds;
                IPuzzleMove          move;
                SamegameMCTSStrategy player   = new SamegameMCTSStrategy(rnd, ucb1Tuned, rave, raveThreshold, nodeRecycling, memoryBudget, useNodeElimination, iterations, null, const_C, const_D);
                string             moveString = string.Empty;
                List <IPuzzleMove> moveList   = new List <IPuzzleMove>();
                stopwatch.Restart();
                while (!s.isTerminal())
                {
                    move = player.selectMove(s);
                    moveList.Add(move);
                    s.DoMove(move);
                    totalRollouts         += player.Mcts.IterationsExecuted;
                    totalNodes            += player.Mcts.NodeCount;
                    totalNodesEliminated  += player.Mcts.nodesEliminated;
                    totalNodesNotExpanded += player.Mcts.nodesNotExpanded;
                    visitsList.AddRange(player.Mcts.visits);
                    raveVisitsList.AddRange(player.Mcts.raveVisits);
                    totalDepth += player.Mcts.maxDepth;
                }
                stopwatch.Stop();

                solvingTime = stopwatch.ElapsedMilliseconds;
                lock (taskLock)
                {
                    if (s.GetScore() > scores[currentLevelIndex])
                    {
                        scores[currentLevelIndex]    = s.GetScore();
                        bestMoves[currentLevelIndex] = moveList;
                        Log("Completed run " + taskTaken[currentLevelIndex] + "/" + restarts + " of level " + (currentLevelIndex + 1) + ". New top score found: " + scores[currentLevelIndex] + " - Init Time: " + TimeFormat(stateInitTime) + " - Solving Time: " + TimeFormat(solvingTime));
                        //PrintMoveList(currentLevelIndex, moveList);
                        //PrintBestScore();
                    }
                    else
                    {
                        Log("Completed run " + taskTaken[currentLevelIndex] + "/" + restarts + " of level " + (currentLevelIndex + 1) + " with score: " + s.GetScore() + " - Init Time: " + TimeFormat(stateInitTime) + " - Solving Time: " + TimeFormat(solvingTime));
                    }
                }
                currentLevelIndex = GetTaskIndex(threadIndex);
            }
            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) / levels.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 / levels.Length);
        }