public static void Main(string[] args)
        {
            string     game;
            int        iterations;
            int        maxCost;
            RewardType rewardType;
            double     const_C;
            double     epsilon = 1;
            double     const_D;
            int        restarts;
            uint       seed;
            bool       stopOnResult;
            string     level;

            if (args.Length != 3)
            {
                throw new ArgumentException("Need three arguments: game method level");
            }
            //textWriter = File.WriteAllText("Log.txt");
            //File.Delete("Log.txt");
            textWriter = File.AppendText("Log.txt");


            string[] gameSettings = args[0].Split(':');
            string[] commands     = args[1].Split(':');

            level = args[2];
            string configurationString = "\n\n\n********************************\n********************************\nBEGIN TASK:\nGame: " + gameSettings[0];

            switch (gameSettings[0])
            {
            case "sokoban":
                if (!bool.TryParse(gameSettings[1], out useNormalizedPosition))
                {
                    PrintInputError("useNormalizedPosition requires a bool value");
                    return;
                }
                if (!bool.TryParse(gameSettings[2], out useTunnelMacro))
                {
                    PrintInputError("useTunnelMacro requires a bool value");
                    return;
                }
                if (!bool.TryParse(gameSettings[3], out useGoalMacro))
                {
                    PrintInputError("useGoalMacro requires a bool value");
                    return;
                }
                if (!bool.TryParse(gameSettings[4], out useGoalCut))
                {
                    PrintInputError("useGoalCut requires a bool value");
                    return;
                }
                configurationString += "\nUse Normalized Position: " + useNormalizedPosition + "\nUse Tunnel Macro: " + useTunnelMacro + "\nUse Goal Macro: " + useGoalMacro + (useGoalMacro ? "\nUse Goal Cut: " + useGoalCut : "\nGoal Cut ignored with Goal Macro disabled") + "\n";
                switch (commands[0])
                {
                case "mcts":
                    if (!int.TryParse(commands[1], out iterations))
                    {
                        PrintInputError("iterations requires an integer value");
                        return;
                    }
                    if (!double.TryParse(commands[2], out const_C))
                    {
                        PrintInputError("Const_C requires a double value");
                        return;
                    }
                    if (!double.TryParse(commands[3], out const_D))
                    {
                        PrintInputError("Const_D requires a double value");
                        return;
                    }
                    if (!RewardType.TryParse(commands[4], out rewardType))
                    {
                        PrintInputError("reward type requires an valid RewardType");
                        return;
                    }
                    if (!bool.TryParse(commands[5], out stopOnResult))
                    {
                        PrintInputError("stop on result requires a boolean value");
                        return;
                    }
                    if (!uint.TryParse(commands[6], out seed))
                    {
                        PrintInputError("seed requires an unsigned integer value");
                        return;
                    }
                    if (!bool.TryParse(commands[7], out ucb1Tuned))
                    {
                        PrintInputError("UCB1 tuned requires a boolean value");
                        return;
                    }
                    if (!bool.TryParse(commands[8], out rave))
                    {
                        PrintInputError("rave requires a boolean value");
                        return;
                    }
                    if (!int.TryParse(commands[9], out raveThreshold))
                    {
                        PrintInputError("Rave threshold requires an integer value");
                        return;
                    }
                    if (!bool.TryParse(commands[10], out nodeRecycling))
                    {
                        PrintInputError("node recycling requires a boolean value");
                        return;
                    }
                    if (!int.TryParse(commands[11], out memoryBudget))
                    {
                        PrintInputError("Memory budget requires an integer value");
                        return;
                    }
                    if (!bool.TryParse(commands[12], out avoidCycles))
                    {
                        PrintInputError("Avoid Cycles requires a boolean value");
                        return;
                    }
                    if (!bool.TryParse(commands[13], out useNodeElimination))
                    {
                        PrintInputError("Use Node Elimination requires a boolean value");
                        return;
                    }
                    if (!SimulationType.TryParse(commands[14], out simulationType))
                    {
                        PrintInputError("Use Node Elimination requires a boolean value");
                        return;
                    }
                    string simulationString = "";
                    switch (simulationType)
                    {
                    case SimulationType.EpsilonGreedy:
                        if (!double.TryParse(commands[15], out epsilon))
                        {
                            PrintInputError("epsilon requires a double value");
                            return;
                        }
                        simulationString += "\nSimulation:  EpsilonGreedy - epsilon: " + epsilon;
                        break;

                    case SimulationType.IDAstar:
                        if (!int.TryParse(commands[15], out maxNodes))
                        {
                            PrintInputError("maxCost requires an integer value");
                            return;
                        }
                        if (!int.TryParse(commands[16], out tableSize))
                        {
                            PrintInputError("maxTableSize requires an integer value");
                            return;
                        }
                        simulationString += "\nSimulation: IDA* - maxNodes: " + maxNodes + "; tableSize: " + tableSize;
                        break;
                    }

                    if (nodeRecycling && (memoryBudget <= 0 || memoryBudget >= iterations))
                    {
                        PrintInputError("Memory budget value not compatible with node recycling");
                        return;
                    }
                    configurationString += "\nSP-MCTS \nMethod: MCTS \niterations: " + iterations + "\nUCT constant: " + const_C + "\nSP_UCT constant: " + const_D + "\nReward Type: " + rewardType +
                                           "\nUCB1Tuned: " + ucb1Tuned + "\nRAVE: " + rave + "\nRAVE Threshold: " + raveThreshold + "\nNode Recycling: " + nodeRecycling + "\nMemory Budget: " + (nodeRecycling?"" + memoryBudget:"Ignored with node recycling disabled") + "\nAvoid Cycles: " + avoidCycles + "\nNode Elimination:" + useNodeElimination + "\nStop on Result: " + stopOnResult + "\nlevel path: " + level + "\nseed: " + seed +
                                           simulationString + "\n********************************\n********************************\n";
                    Log(configurationString);
                    MultiThreadSokobanTest(const_C, const_D, iterations, 1, level, seed, true, rewardType, stopOnResult, epsilon, true, 1);
                    break;

                case "ida":
                    if (!int.TryParse(commands[1], out maxCost))
                    {
                        PrintInputError("maxCost requires an integer value");
                        return;
                    }
                    if (!RewardType.TryParse(commands[2], out rewardType))
                    {
                        PrintInputError("reward type requires an valid RewardType");
                        return;
                    }
                    if (!int.TryParse(commands[3], out int maxTableSize))
                    {
                        PrintInputError("maxTableSize requires an integer value");
                        return;
                    }
                    configurationString += "\nMethod: IDA* \nMaximum cost: " + maxCost + "\nReward Type: " + rewardType + "\nlevel: " + level + "\n********************************\n********************************\n";
                    Log(configurationString);
                    SokobanIDAStarTest(level, maxCost, rewardType, maxTableSize, useNormalizedPosition, useTunnelMacro, useGoalMacro, useGoalCut);
                    break;

                case "random":
                    if (!int.TryParse(commands[1], out iterations))
                    {
                        PrintInputError("seed requires an unsigned integer value");
                        return;
                    }
                    if (!int.TryParse(commands[2], out int maxDepth))
                    {
                        PrintInputError("seed requires an unsigned integer value");
                        return;
                    }
                    if (!uint.TryParse(commands[3], out seed))
                    {
                        PrintInputError("seed requires an unsigned integer value");
                        return;
                    }
                    configurationString += "\nMethod: random \niterations: " + iterations + "\nMax Iteration Depth: " + maxDepth + "\nseed: " + seed + "\nlevel: " + level + "\n********************************\n********************************\n";
                    Log(configurationString);
                    SokobanRandom(level, maxDepth, iterations, seed);
                    break;
                }
                break;

            case "samegame":
                switch (commands[0])
                {
                case "mcts":
                    if (!int.TryParse(commands[1], out iterations))
                    {
                        PrintInputError("iterations requires an integer value");
                        return;
                    }
                    if (!double.TryParse(commands[2], out const_C))
                    {
                        PrintInputError("const_C requires a double value");
                        return;
                    }
                    if (!double.TryParse(commands[3], out const_D))
                    {
                        PrintInputError("const_d requires adouble value");
                        return;
                    }
                    if (!int.TryParse(commands[4], out restarts))
                    {
                        PrintInputError("restarts requires an integer value");
                        return;
                    }
                    if (!uint.TryParse(commands[5], out seed))
                    {
                        PrintInputError("seed requires an unsigned integer value");
                        return;
                    }
                    if (!bool.TryParse(commands[6], out bool ucb1Tuned))
                    {
                        PrintInputError("seed requires an unsigned integer value");
                        return;
                    }
                    if (!bool.TryParse(commands[7], out bool rave))
                    {
                        PrintInputError("seed requires an unsigned integer value");
                        return;
                    }
                    if (!int.TryParse(commands[8], out raveThreshold))
                    {
                        PrintInputError("Rave threshold requires an integer value");
                        return;
                    }
                    if (!bool.TryParse(commands[9], out bool nodeRecycling))
                    {
                        PrintInputError("seed requires an unsigned integer value");
                        return;
                    }
                    if (!int.TryParse(commands[10], out int memoryBudget))
                    {
                        PrintInputError("restarts requires an integer value");
                        return;
                    }
                    if (!bool.TryParse(commands[11], out useNodeElimination))
                    {
                        PrintInputError("Use Node Elimination requires a boolean value");
                        return;
                    }
                    if (nodeRecycling && (memoryBudget <= 0 || memoryBudget >= iterations))
                    {
                        PrintInputError("Memory budget value not compatible with node recycling");
                        return;
                    }
                    Log("\n********************\nBEGIN TASK:\nGame:" + gameSettings[0] + "\nMethod: MCTS \niterations: " + iterations + "\nUCT constant: " + const_C + "\nSP_UCT constant: " + const_D + "\nrestarts: " + restarts +
                        "\nSeed: " + seed + "\nUCB1Tuned: " + ucb1Tuned + "\nRAVE: " + rave + "\nRAVE threshold: " + raveThreshold + "\nNode Recycling: " + nodeRecycling + "\nMemory Budget: " + (nodeRecycling ? "" + memoryBudget : "Ignored with node recycling disabled") + "\nNode Elimination: " + useNodeElimination + "\nlevel: " + level + "\n********************");
                    MultiThreadSamegameTest(const_C, const_D, iterations, restarts, level, 1, seed, ucb1Tuned, rave, nodeRecycling, memoryBudget);
                    break;

                case "ida":
                    if (!int.TryParse(commands[1], out maxCost))
                    {
                        PrintInputError("maxCost requires an integer value");
                        return;
                    }
                    if (!int.TryParse(commands[2], out int tableSize))
                    {
                        PrintInputError("maxTableSize requires an integer value");
                        return;
                    }
                    SamegameIDAStarTest(level, maxCost, tableSize);
                    break;
                }
                break;

            default:
                PrintInputError("Wrong game name: Accepted names are 'sokoban' and 'samegame'");
                break;
            }
        }