Fast Risk Battle Simulator I have written some code in python to determine the outcome(including the remaining troops of both sides) of a Risk battle fought until one side is defeated. When using the Numba's njit decorator the code can simulate one battle's outcome in ~ 0.2ms.
This is not fast enough for the application I have in mind. Ideally it would be one to two orders of magnitude faster. Any advice on how to improve performance would be greatly appreciated.
Function Code:
@njit
def _attack_(n_atk,n_def):
    n_atk_dies = 0
    n_def_dies = 0
    while n_atk &gt;0 and n_def&gt;0:
        if n_atk&gt;=3:
            n_atk_dies = 3
        if n_atk==2:
            n_atk_dies = 2
        if n_atk ==1:
            n_atk_dies = 1
        if n_def &gt;=2:
            n_def_dies = 2
        if n_def ==1:
            n_def_dies = 1   
        atk_rolls = -np.sort(-np.random.randint(1,7,n_atk_dies))
        def_rolls = -np.sort(-np.random.randint(1,7,n_def_dies))
        comp_ind = min(n_def_dies,n_atk_dies)
        comp = atk_rolls[:comp_ind]-def_rolls[:comp_ind]
        atk_losses =len([1 for i in comp if i&lt;=0])
        def_losses = len([1 for i in comp if i&gt;0])
        n_atk -= atk_losses
        n_def -= def_losses
    return [n_atk,n_def]

Benchmarking Code:
atk_list = list(np.random.randint(1,200,10000))
def_list = list(np.random.randint(1,200,10000))
cProfile.run('list(map(_attack_,atk_list,def_list))',sort='tottime')
You play with up to 199 dice per player. So most of the time, the attacker will attack with 3 dice and the defender will attack with 2 dice. As long as that's the case, you could simply pick one of the three possible outcomes (with precalculated probabilities) and apply it. Outline:
def _attack_(n_atk,n_def):
    while n_atk &gt;= 3 and n_def &gt;= 2:
        # pick random outcome and update the dice numbers
    while n_atk &gt;0 and n_def&gt;0:
        # same as before
    return [n_atk,n_def]

You could even precompute the result distributions for all \$199^2\$ possible inputs and randomly pick the result according to the given input's distribution. Outline:
def _attack_(n_atk,n_def):
    return random.choices(results[n_atk, n_def], cum_weights=cum_weights[n_atk, n_def])