/// <summary>
        /// Using NegaMax with Alpha-Beta Pruning and Transposition Table to determine the best possible move.
        /// </summary>
        /// <param name="gameState"> Current GameState. </param>
        /// <param name="depth"> How many layers of branches to foresee. </param>
        /// <param name="alpha">  Maximum to prune out. </param>
        /// <param name="beta">  Minimum to prune out. </param>
        /// <param name="maximizingPlayer">  Current Player. True = caller. </param>
        /// <returns>  The evaluated score. </returns>
        private float NegaMax(GameStateReader gameState, int depth, float alpha = float.NegativeInfinity, float beta = float.PositiveInfinity, bool maximizingPlayer = true /*, bool allowNullMove = true*/)
        {
            #region Transposition Table retrieval

            var  origAlpha   = alpha;
            long zobristHash = gameState.zobristHash;
            if (transpositionTable.ContainsKey(zobristHash))
            {
                var ttEntry = transpositionTable[zobristHash];
                var value   = ttEntry.value;
                if (!maximizingPlayer)
                {
                    value *= -1;
                }
                if (ttEntry.depth >= depth)
                {
                    switch (ttEntry.flag)
                    {
                    case TranspositionTableEntry.Flag.EXACT:
                        return(value);

                    case TranspositionTableEntry.Flag.UPPERBOUND:
                        alpha = Mathf.Max(alpha, value);
                        break;

                    case TranspositionTableEntry.Flag.LOWERBOUND:
                        beta = Mathf.Min(beta, value);
                        break;
                    }
                    if (alpha >= beta)
                    {
                        return(value);
                    }
                }
            }

            #endregion

            #region End Node

            if (gameState.GameOver)
            {
                return(EvaluateBoardState(gameState) * (maximizingPlayer ? 1 : -1));
            }

            if (depth == 0)
            {
                if (gameState.Quiet())
                {
                    return(EvaluateBoardState(gameState) * (maximizingPlayer ? 1 : -1));
                }
                else
                {
                    return(Quiesce(gameState, quiesceDepth, !maximizingPlayer, alpha, beta));
                }
            }

            #endregion

            #region Search Tree

            var bestEval     = float.NegativeInfinity;
            var bestMove     = new Vector2Int[2];
            var orderedMoves = gameState.PossibleMovesQuick(maximizingPlayer);
            orderedMoves = OrderMoves(orderedMoves, gameState, depth);

            foreach (var child in orderedMoves)
            {
                gameState.MakeMove(child);
                var eval = -NegaMax(gameState, depth - 1, -beta, -alpha, !maximizingPlayer);
                gameState.UndoMove();
                if (eval > bestEval)
                {
                    bestEval = eval;
                    bestMove = child;
                    alpha    = Mathf.Max(alpha, bestEval);
                    if (alpha >= beta)
                    {
                        if (gameState.IsntCapture(child[1]))
                        {
                            int ply = this.depth - depth;
                            killerMoves[ply, 1] = killerMoves[ply, 0];
                            killerMoves[ply, 0] = child;
                            historyMoves[child[0].x, child[0].y, child[1].x, child[1].y] += depth * depth;
                        }
                        break;
                    }
                }
            }

            #endregion

            #region Add to Transposition Table.

            var newEntry = new TranspositionTableEntry {
                depth  = depth,
                value  = bestEval * (maximizingPlayer ? 1 : -1),
                pvMove = bestMove
            };
            switch (bestEval)
            {
            case float value when value <= origAlpha:
                newEntry.flag = TranspositionTableEntry.Flag.UPPERBOUND;
                break;

            case float value when value >= beta:
                newEntry.flag = TranspositionTableEntry.Flag.LOWERBOUND;
                break;

            default:
                newEntry.flag = TranspositionTableEntry.Flag.EXACT;
                break;
            }
            transpositionTable[zobristHash] = newEntry;

            #endregion

            #region Return result

            return(bestEval);

            #endregion
        }
        /// <summary>
        /// Using (Root) NegaMax with Alpha-Beta Pruning and Transposition Table to determine the best possible move.
        /// </summary>
        /// <param name="gameState"> Current GameState. </param>
        /// <param name="depth"> How many layers of branches to foresee. </param>
        /// <param name="alpha">  Maximum to prune out. </param>
        /// <param name="beta">  Minimum to prune out. </param>
        /// <param name="maximizingPlayer">  Current Player. True = caller. </param>
        /// <returns>  The best move. </returns>
        private Vector2Int[] RootNegaMax(GameStateReader gameState, int depth, float alpha = float.NegativeInfinity, float beta = float.PositiveInfinity, bool maximizingPlayer = true /*, bool allowNullMove = true*/)
        {
            #region Inititalize Variables

            var  origAlpha   = alpha;
            long zobristHash = gameState.zobristHash;

            var bestEval     = float.NegativeInfinity;
            var orderedMoves = gameState.PossibleMovesQuick(maximizingPlayer);
            orderedMoves = OrderMoves(orderedMoves, gameState, depth);
            var bestMove = orderedMoves[0];

            #endregion

            #region Search Tree

            foreach (var child in orderedMoves)
            {
                gameState.MakeMove(child);
                var eval = -NegaMax(gameState, depth - 1, -beta, -alpha, !maximizingPlayer);
                gameState.UndoMove();
                if (eval > bestEval)
                {
                    bestEval = eval;
                    bestMove = child;
                    alpha    = Mathf.Max(alpha, bestEval);
                    if (alpha >= beta)
                    {
                        if (gameState.IsntCapture(child[1]))
                        {
                            int ply = this.depth - depth;
                            killerMoves[ply, 1] = killerMoves[ply, 0];
                            killerMoves[ply, 0] = child;
                            historyMoves[child[0].x, child[0].y, child[1].x, child[1].y] += depth * depth;
                        }
                        break;
                    }
                }
            }

            #endregion

            #region Add to Transposition Table.

            var newEntry = new TranspositionTableEntry {
                depth  = depth,
                value  = bestEval,
                pvMove = bestMove
            };
            switch (bestEval)
            {
            case float value when value <= origAlpha:
                newEntry.flag = TranspositionTableEntry.Flag.UPPERBOUND;
                break;

            case float value when value >= beta:
                newEntry.flag = TranspositionTableEntry.Flag.LOWERBOUND;
                break;

            default:
                newEntry.flag = TranspositionTableEntry.Flag.EXACT;
                break;
            }
            transpositionTable[zobristHash] = newEntry;

            #endregion

            #region Return result

            return(bestMove);

            #endregion
        }