Return a NormalFormGame instance of (the 2-player version of) the
    "ranking game" studied by Goldberg et al. (2013), where each player
    chooses an effort level associated with a score and a cost which are
    both increasing functions with randomly generated step sizes. The
    player with the higher score wins the first prize, whose value is 1,
    and the other player obtains the "second prize" of value 0; in the
    case of a tie, the first prize is split and each player receives a
    value of 0.5. The payoff of a player is given by the value of the
    prize minus the cost of the effort.

    Parameters
    ----------
    n : scalar(int)
        Number of actions, i.e, number of possible effort levels.
    steps : scalar(int), optional(default=10)
        Parameter determining the upper bound for the size of the random
        steps for the scores and costs for each player: The step sizes
        for the scores are drawn from `1`, ..., `steps`, while those for
        the costs are multiples of `1/(n*steps)`, where the cost of
        effort level `0` is 0, and the maximum possible cost of effort
        level `n-1` is less than or equal to 1.
    random_state : int or np.random.RandomState, optional
        Random seed (integer) or np.random.RandomState instance to set
        the initial state of the random number generator for
        reproducibility. If None, a randomly initialized RandomState is
        used.

    Returns
    -------
    g : NormalFormGame

    Examples
    --------
    >>> g = ranking_game(5, random_state=1234)
    >>> g.players[0]
    Player([[ 0.  ,  0.  ,  0.  ,  0.  ,  0.  ],
            [ 0.82, -0.18, -0.18, -0.18, -0.18],
            [ 0.8 ,  0.8 , -0.2 , -0.2 , -0.2 ],
            [ 0.68,  0.68,  0.68, -0.32, -0.32],
            [ 0.66,  0.66,  0.66,  0.66, -0.34]])
    >>> g.players[1]
    Player([[ 1.  ,  0.  ,  0.  ,  0.  ,  0.  ],
            [ 0.8 ,  0.8 , -0.2 , -0.2 , -0.2 ],
            [ 0.66,  0.66,  0.66, -0.34, -0.34],
            [ 0.6 ,  0.6 ,  0.6 ,  0.6 , -0.4 ],
            [ 0.58,  0.58,  0.58,  0.58,  0.58]])

def ranking_game(n, steps=10, random_state=None):
    """
    Return a NormalFormGame instance of (the 2-player version of) the
    "ranking game" studied by Goldberg et al. (2013), where each player
    chooses an effort level associated with a score and a cost which are
    both increasing functions with randomly generated step sizes. The
    player with the higher score wins the first prize, whose value is 1,
    and the other player obtains the "second prize" of value 0; in the
    case of a tie, the first prize is split and each player receives a
    value of 0.5. The payoff of a player is given by the value of the
    prize minus the cost of the effort.

    Parameters
    ----------
    n : scalar(int)
        Number of actions, i.e, number of possible effort levels.
    steps : scalar(int), optional(default=10)
        Parameter determining the upper bound for the size of the random
        steps for the scores and costs for each player: The step sizes
        for the scores are drawn from `1`, ..., `steps`, while those for
        the costs are multiples of `1/(n*steps)`, where the cost of
        effort level `0` is 0, and the maximum possible cost of effort
        level `n-1` is less than or equal to 1.
    random_state : int or np.random.RandomState, optional
        Random seed (integer) or np.random.RandomState instance to set
        the initial state of the random number generator for
        reproducibility. If None, a randomly initialized RandomState is
        used.

    Returns
    -------
    g : NormalFormGame

    Examples
    --------
    >>> g = ranking_game(5, random_state=1234)
    >>> g.players[0]
    Player([[ 0.  ,  0.  ,  0.  ,  0.  ,  0.  ],
            [ 0.82, -0.18, -0.18, -0.18, -0.18],
            [ 0.8 ,  0.8 , -0.2 , -0.2 , -0.2 ],
            [ 0.68,  0.68,  0.68, -0.32, -0.32],
            [ 0.66,  0.66,  0.66,  0.66, -0.34]])
    >>> g.players[1]
    Player([[ 1.  ,  0.  ,  0.  ,  0.  ,  0.  ],
            [ 0.8 ,  0.8 , -0.2 , -0.2 , -0.2 ],
            [ 0.66,  0.66,  0.66, -0.34, -0.34],
            [ 0.6 ,  0.6 ,  0.6 ,  0.6 , -0.4 ],
            [ 0.58,  0.58,  0.58,  0.58,  0.58]])

    """
    payoff_arrays = tuple(np.empty((n, n)) for i in range(2))
    random_state = check_random_state(random_state)

    scores = random_state.randint(1, steps+1, size=(2, n))
    scores.cumsum(axis=1, out=scores)

    costs = np.empty((2, n-1))
    costs[:] = random_state.randint(1, steps+1, size=(2, n-1))
    costs.cumsum(axis=1, out=costs)
    costs[:] /= (n * steps)

    _populate_ranking_payoff_arrays(payoff_arrays, scores, costs)
    g = NormalFormGame(
        [Player(payoff_array) for payoff_array in payoff_arrays]
    )
    return g