Post

The Math of COD Zombies

The Math of COD Zombies
Scope:

Call of Duty Zombies is a round-based survival game that I have poured too much of my life into, especially World at War, Black Ops I, II, and III. These games share similar core mechanics on how the health and number of zombies increases with each round.

Interestingly, the round numbers give a misleading indicator of progress. For example, it takes much longer to get from round 50 to round 100 than it does to get from round 1 to round 50. This isn’t just a trend in casual play; the best players face this problem. For example, consider the on-disk map for Black Ops III, Shadows of Evil. The world record time to reach rounds $50$ and $100$ are 53.09 and 2:44:50 respectively, both by FadedZ. The time to reach round $100$ is almost $3$ times as long! What is going on?

Well, as many of the maps in these games have weapons (or traps) that deal infinite damage to zombies, health scaling isn’t the contributor. It must be the number of zombies a round.

How many Zombies are there in a round?

Let $r \in [1, \dots, 255]$ be the round number, $n \in [1, 2, 3, 4]$ be the number of players, and $z_{n, r}$ be the number for zombies for $n$ players at round $r$. A table for the first $10$ rounds, obtained using Zombacus.com, is below.

$z_{n, r}$123456789
1 Player$6$$8$$13$$18$$24$$27$$28$$28$$29$
2 Players$7$$9$$15$$21$$27$$31$$32$$33$$34$
3 Players$9$$10$$18$$25$$32$$38$$40$$43$$45$
4 Players$10$$12$$21$$29$$37$$45$$49$$52$$56$

After round $9$, we use formulas from the Zombies wiki to write the following identities. For $r \geq 10$, we have

\[z_{1,r} = 24 + \lfloor 0.5*0.18 r^2 \rfloor\]

and

\[z_{n,r} = 24 + \lfloor (n-1)*0.18 r^2 \rfloor\]

for $n \in [2, 3, 4]$. Regardless of the number of players, the number of zombies per round grows as the square of the round number! This leads to higher rounds taking significantly longer than earlier rounds.

We can summarize this information in the Python function below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from math import floor

# get the number of zombies per round
def num_zombies_per_round(round_number: int, num_players: int):
    low_round_zombies_array = [
        [ 6,  8, 13, 18, 24, 27, 28, 28, 29], 
        [ 7,  9, 15, 21, 27, 31, 32, 33, 34], 
        [ 9, 10, 18, 25, 32, 38, 40, 43, 45], 
        [10, 12, 21, 29, 37, 45, 49, 52, 56], 
    ]
    if round_number < 10:
        return low_round_zombies_array[num_players - 1][round_number - 1]
    if num_players == 1:
        return 24 + floor(0.5*0.18*round_number**2)
    elif num_players in [2,3,4]:
        return 24 + floor((num_players - 1)*0.18*round_number**2)

How is this useful?

This data is important for getting to a high round. Often high round runs are performed with a fixed strategy that is known to kill some number of zombies per time interval. If you know how many zombies you can expect to face in your run, you can estimate the time to reach some goal round $R$.

So if you have a goal round $R$ and are currently on round $r$, how close are you to your goal? Phrased another way, what round is $50\%$, $75\%$, $90\%$ to my goal round? This leads us to define the zombies percentile function $f_n(p,R)$, which is defined as the smallest integer $r$ such that

\[p \geq \frac{\sum_{k = 1}^r z_{n,k}}{\sum_{k = 1}^R z_{n,k}}.\]

Here $p$ is a real number in $[0,1]$ which represents the fraction of the way you are to your goal round $R$. The above equation can be easily computed. For instance, for round $100$, we have

$f_n(p, 100)$$p=$50%$p=$75%$p=$90%
1 Player$79$$91$$97$
2 Players$79$$91$$97$
3 Players$79$$91$$97$
4 Players$80$$91$$97$

In Python, the zombies percentile function is as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# get the total number of zombies up to and including round_number
def total_zombies_up_to_round(round_number: int, num_players: int):
    total = 0
    for k in range(1,round_number+1):
        total += num_zombies_per_round(round_number=k, num_players=num_players)
    return total

# how many rounds before I am percentile% to my (and my friends?) goal_round?
def zombies_percentile_function(num_players: int, percentile: float, goal_round: int):
    denominator = total_zombies_up_to_round(round_number=goal_round, num_players=num_players)
    numerator = 0
    k = 0
    while (numerator/denominator) < percentile:
        k += 1
        numerator += num_zombies_per_round(round_number=k, num_players=num_players)

    return k

Surprisingly, the value

\[\frac{f_n(p, R)}{R}\]

for $R \in [20,255]$ is roughly constant, regardless of the number of players $n$. This allows for the following easy-to-remember heuristics that are approximately correct for any number of players.

The 50% Rule You are 50% to your goal round $R$ when you are at about round $0.8R$.

The 75% Rule You are 75% to your goal round $R$ when you are at about round $0.9R$.

These two rules are really helpful when determining how long you have to go in your game. For example, if I wanted to get to round $115$, using the 50% Rule I know that I’m about half way there when I’m on round $0.8 * 115 = 92$.

Thank you for reading!

This post is licensed under CC BY 4.0 by the author.