Numba-optimized version of `Player.best_response` compilied in
    nopython mode, specialized for 2-player games (where there is only
    one opponent).

    Return the best response action (with the smallest index if more
    than one) to `opponent_mixed_action` under `payoff_matrix`.

    Parameters
    ----------
    payoff_matrix : ndarray(float, ndim=2)
        Payoff matrix.

    opponent_mixed_action : ndarray(float, ndim=1)
        Opponent's mixed action. Its length must be equal to
        `payoff_matrix.shape[1]`.

    tol : scalar(float), optional(default=None)
        Tolerance level used in determining best responses.

    Returns
    -------
    scalar(int)
        Best response action.

def best_response_2p(payoff_matrix, opponent_mixed_action, tol=1e-8):
    """
    Numba-optimized version of `Player.best_response` compilied in
    nopython mode, specialized for 2-player games (where there is only
    one opponent).

    Return the best response action (with the smallest index if more
    than one) to `opponent_mixed_action` under `payoff_matrix`.

    Parameters
    ----------
    payoff_matrix : ndarray(float, ndim=2)
        Payoff matrix.

    opponent_mixed_action : ndarray(float, ndim=1)
        Opponent's mixed action. Its length must be equal to
        `payoff_matrix.shape[1]`.

    tol : scalar(float), optional(default=None)
        Tolerance level used in determining best responses.

    Returns
    -------
    scalar(int)
        Best response action.

    """
    n, m = payoff_matrix.shape

    payoff_max = -np.inf
    payoff_vector = np.zeros(n)

    for a in range(n):
        for b in range(m):
            payoff_vector[a] += payoff_matrix[a, b] * opponent_mixed_action[b]
        if payoff_vector[a] > payoff_max:
            payoff_max = payoff_vector[a]

    for a in range(n):
        if payoff_vector[a] >= payoff_max - tol:
            return a