January 20, 2010 by James Tauber / Technical

Typewar: Points and Levels

Part of what makes typewar addictive is the use of points and levels. This post explores how points are awarded and what the level boundaries are.

I've played role-playing games for many years, starting with first edition AD&D pen-and-paper, computer games like Ultima and most recently MMORPGs like Lord of the Rings Online. There is no doubt that, for me at least, earning points to get to the next level is a very powerful motivating force to "keep going" in a game.

So when I started conceptualizing typewar, I knew the fundamental game mechanism would be:

  • gain (or lose) points for questions answered
  • gain another level when a certain number of points is reached
  • clearly indicate progress to the next level

How Many Points Are Given

First, to discourage guessing, points are lost for wrong answers (although you can never lose a level). The amount you win or lose depends on what percentage of the time other players get the question correct.

The points adjustment uses the following code:

if correct:
    if percentage is not None:
        points = 11 - int(percentage / 10)
    else:
        points = 1
else:
    if percentage is not None:
        points = -int(percentage / 10)
    else:
        points = -1

In other words, if the question is answered correctly 100% of the time, you get 1 point for a correct answer; if 80% of the time, you get 3 points; if 60% of the time, 5 points. If you get the question wrong however, then you'll lose 10 points, 8 points and 6 points respectively.

Note that if the question hasn't been asked before, you gain/lose 1 point. When the site was in beta, you gained or lost 5 points but this meant that the first person to hit an easy question was unduly rewarded.

It is possible that an even better solution would be to use the bayesian average rather than raw percentage. We'll talk about the use of bayesian averages elsewhere on the site in another blog post.

What the Level Boundaries Are

Much experimentation was done during the beta as to what level boundaries felt right in terms of quick leveling early on and increase in effort needed to level as you progressed. In the end, the cubic equation

points_required = ((level + 1) ** 3) / 4

seemed to work particularly nicely.

This means that it only takes 54 points to get to level 5; 332 points to get to level 10; 2,315 points to get to level 20 and a whopping 33,162 points to get to level 50.

When the site launched it had a level cap of 30. Every time we increased the level cap by 10, someone hit the new cap that same day, even when the cap was raised to level 60 (requiring 56,745 points)

Distribution of Players

If you graph the log distribution of points (in buckets of 1,000) on the current site it looks like this:

whereas if you graph the log distribution of levels, it looks like this:

One thing we haven't done yet, which may be worthwhile, is experimenting with what the distribution of levels would be using different level boundary functions. Although the current distribution does look fairly good.

Showing Progress

To provide a visual indication of how close a player is to leveling, we draw a progress bar:

To generate this, we first of all have a function that calculates the progress:

def calculate_progress(points, level):
    """
    given current points and current level, calculate the low and high points
    of your progress to next level
    """
    l = calculate_level(points)
    
    if level > l:
        # player may have lost points to take them below level requirement
        low_points = points
    else:
        low_points = POINTS_REQUIREMENTS[l - 1]
    
    high_points = POINTS_REQUIREMENTS[min(LEVEL_CAP, level + 1) - 1]
    
    return min(low_points, high_points), high_points

We then have a template tag that converts this to a width between 0 and 100:

@register.inclusion_tag("stats/_level_progress.html")
def level_progress(points, level):
    
    low, high = calculate_progress(points, level)
    
    if high == low:
        w = 100
    else:
        w = 100 * (points - low) / (high - low)
    
    return {
        "STATIC_URL": settings.STATIC_URL,
        "on_width": w,
        "off_width": 100 - w,
    }

which is then used to draw the two images that make up the progress bar:

<div class="progress_bar">
    <img src="{{ STATIC_URL }}img/progress_on.png" 
        height="10" width="{{ on_width }}" />
    <img src="{{ STATIC_URL }}img/progress_off.png"
        height="10" width="{{ off_width }}" />
</div>

Incidentally, the progress_on.png and progress_off.png images were generated using the Python library described in How to Generate Gradients and Textures using the follow code:

write_png("progress_on.png", 1, 10, gradient(LINEAR_Y, NO_NOISE, [
    (0.4, (0x77, 0x99, 0x99), (0x22, 0x33, 0x33)),
    (1, (0x22, 0x33, 0x33), (0x11, 0x22, 0x22)),
]))

write_png("progress_off.png", 1, 10, gradient(LINEAR_Y, NO_NOISE, [
    (1, (0x44, 0x55, 0x55), (0x77, 0x99, 0x99)),
]))

Since the beginning, the site showed the player their overall position on the site (another post will cover the challenges in calculating this). After the site had launched, we thought it could be made even more addictive by showing not only the player progress to the next level but also how close they were to changing position. So the site now tells you how many points you are away from the person immediately in front of you as well as how far away the person behind you is.

Conclusion

And so, in addition to the drive to get more points, we keep the player motivated by progression in level (which also unlocks new typefaces and letters) and position on the site.

We're not done yet, though. Soon we'll be adding badges as well, to give the player even more motivation to keep on going.

And, of course, we have our iPhone app coming any day now to take the addiction mobile!

Eldarion CEO Interviewed in Python Magazine Eldarion and Massively Overrated Launch Typewar iPhone App

Want to talk with us about something more custom or have questions?

Get in touch with us