osrs image

If you were a teenager using the internet nine years ago, you had the chance to play Runescape in its prime. I really hope you did. For those who didn’t make it past Tutorial Island, Runescape is an MMORPG with a player versus player (PvP) aspect that has been lauded as timeless. Out of the flurry of similar MMO’s I played all those years ago, Runescape was the only one in which the outcome of PvP encounters depended on both a character’s levels as well as a human player’s ability in executing the fight. Fun to watch; fun to execute. Combined with different PvP environments (The Wilderness, The Duel Arena, PvP Worlds, Bounty Hunter) and a combat-level formula that allows for competitive fights at every stage of one’s training, Runescape was a favorite amongst casual and hardcore gamers alike. It should come as no surprise that Jagex, the developers of the game, maintain an entirely separate branch, called Old School Runescape (aka 2007scape or OSRS), that has just as many active players as the most up-to-date version of the game.

PvP in 2007scape is extremely competitive because all of the 12 and 13 year olds from a decade ago are now much smarter and in their mid-twenties. Not only does skill play a major part in being successful, but an account’s build (its combat levels) has to be carefully planned and achieved. There is no low-hanging fruit. I thought it would be neat to develop an end-to-end combat simulator to determine an optimal character build, taking into account game ticks, combat scenarios, and all possible stat and gear combinations. This would have been an extensive work, and wouldn’t have been of much use. People already know what the best builds are and use them. Further, in the wilderness, skill is the real determining factor. If I wrote a program to evaluate all those factors, at the end of the day I could say, hey, risking item x is marginally more costly than risking item y. I could say with authority that 2 levels in skill b are preferable to 2 levels in skill c at combat level d in scenarios 1, 3, 4 & 5, but not 2 or 6, but there’s really no basis of application as it would be too stratified. I had to find a PvP environment in which my model could ignore skill & human error, operate using simplified game mechanics, and produce results leading to an indisputable, repeatable edge over the competition.

In the Duel Arena, all of that criteria is met. Players are free to choose their own rules and the most popular rule combination turns off all armor, weapons, food, potions, and prayer. Players fight in a predictively simple manner that requires no skill. You just click once and watch. This is known as “boxing”. While most people who box have maximum combat stats, I’ve selected a different, lower leveled build that may be able to compete with maxed-out accounts if I can determine optimal stats and odds I’m willing to take.

The combat stats in Runescape are Attack, Strength, Defense, Ranged, Magic, Prayer and Hitpoints and can be leveled between 1 and 99. We’ll be ignoring Ranged, Magic, and Prayer for the simulation since those styles are almost always turned off in the duel arena. The game’s combat formula, resulting in combat levels 3 and 126, is calculated in a way that allows for some pretty wacky builds. One is the ranged tank. While 1-prayer accounts might be staking with 99 attack, strength, defense, and hp at 113 combat, I can have 99 defense and hp while having 149 “points” to distribute between my attack and strength at only 97 combat. My idea is that if I can find a winning combination of attack/strength levels at 97 combat, I will not only win virtually every fight against players around my level (because 99 hp and defense with 1 prayer is overpowered), but can maintain an edge versus opponents of higher levels if they are willing to stake (wager) a magnified amount. I just need to find out what those odds are and if they would make sense.

For example, If my model shows that I win 26% of fights with (atk/str/def/hp) 80/69/99/99 (80+69 = 149) versus opponents who are 99/99/99/99, I should be willing to make a one million gp (gold pieces, in-game currency) stake versus an opponent’s three million gp wager. In the long run, I will come out ahead. My expected value would be positive: (.26 * 3) – (.74 * 1). Against accounts that aren’t maxed, say, 97/99/95/98, I’ll stand to make even more. Why is this worth pursuing? Above, I stated that the most competitive builds are commonly known- hasn’t somebody already figured out what I am proposing? They have not. The calculations for comparing builds in the constrained combat environment of the duel arena are so particular that it would be impossible for anyone to manually test the optimal combination without programming their own combat simulator.

In my simulation, I will not only be finding the optimal allocation of the 149 atk/str levels, but, for every distribution of those 149 levels, I will produce the maximum odds at which a player should be staking 99/99/99/99 accounts if they aim to make money in the long run. Although trading in-game currency for real-world money is against the rules, to put this into perspective, a hypothetical 10% edge on staking for one-billion gp per hour will net you a profit of $250 USD per hour. (Disclosure: a player will have no problem finding enough opponents to sustain that volume, however, finding a consistent stream of players willing to stake with the odds in your favor may not be easy). Though the arena is wildly popular, those duking it out with maxed stats are gambling on 50-50 odds, only slightly better when an opponent isn’t maxed. There is no possible way to sustain a winning streak, which is why the lion’s share of Runescape PvP footage on YouTube features the wilderness, not the duel arena. By creating a combat simulator, I hope to calculate the optimal skill allocation for an account that will allow me to arbitrate between odds people are willing to give and what the odds actually are.

Let’s start building the simulator. I’ll be using Java. First, the assumptions and constraints. I will assume two players are boxing with no armor on, no weapons equipped, no prayer allowed, no food/potions allowed, and no other stat bonuses. One assumption that will be considered controversial is that I will assume no attack style bonuses. I consider this necessary for obtaining normalized results. Since there are no weapons, both players will be attacking at the same speed, which is speed 6. This is 2.4 seconds or 4 game ticks, but this doesn’t need to be implemented since there is no difference between player attack speeds and no external actions that could depend on game ticks. A turn-by-turn will fit the simulation just fine. Further, I’ll grant each player “first hit” equally. First hit refers to who, despite both players clicking at the exact same time, attacks first in a fight (by one game tick). Jagex randomly give a player priority over another in a fight. In the duel arena, and especially with restrictive rules, this is important so that both players don’t both deal the final blow at the same time, thus ending the match in a draw. A duel can never end in a draw.

For the simulation I will generate two characters. One will be maxed. The other will start with 99 hitpoints and defense with 50 attack and 99 strength. I will simulate combat between the two builds 1000 times and then will move on to an account with 51 attack and 98 strength versus a maxed account. And then a 52 atk/97 str, and so on, until I test a 99/50/99/99 vs a 99/99/99/99. From there, I’ll calculate odds for each build. Maybe you only need to get 1.35x, or maybe I’m completely off and you need 40x. We’ll have a clear idea of the best build. My guess is 82/67/99/99.

I coded this up with just a main method and a Player class. The player is constructed with certain stats at which point the defense roll, attack roll, and max hit are calculated.

public Player(String alias, int atk, int str, int def, int hp){
  this.alias = alias;
  this.attack = atk;
  this.strength = str;
  this.defense = def;
  this.hitpoints = hp;
  this.hitpointsRemaining = hp;

  //precalc this stuff
  this.attackRoll = 10 * atk * (1 + atk / 64);
  this.defenseRoll = 10 * def * (1 + def / 64);
  this.maxHit = Math.round((str + 8 + (str + 0) / 64) / 10);
}

Then, in the main, after the two players are constructed, I precalc each player’s accuracy percentage, which is predetermined since we know the attack and defense rolls are constant.

Max.precalculateAccuracyPercentage(Tim);
Tim.precalculateAccuracyPercentage(Max);

The runtime difference would be negligible if I only run a couple thousand tests, but if I want to nail down the statistics, the optimization helps out a lot. Here’s what that looks like in the Player class.

//Assume nothing is changing during the fight, save some time by doing this first.

public void precalculateAccuracyPercentage(Player opponent){
  if (this.attackRoll < opponent.defenseRoll){
    this.accuracyPercentage = (double)(this.attackRoll - 1) / (2 * opponent.defenseRoll); 
    System.out.println("attackroll: " + this.attackRoll + "\nDefense roll: " + opponent.defenseRoll + "\nAP is: " +     this.accuracyPercentage + "\n"); 
  } 
  else{ 
    this.accuracyPercentage = 1 - (double)(opponent.defenseRoll + 1) / (2 * this.attackRoll); 
  }
}

I fired up a simple loop pitting two maxed accounts against each other over 10000 fights, just to confirm there were no (glaring) mistakes in the code. Everything seems fine.

...
Max attacks... and doesn't hit
Tim attacks... and hits a 3 Max's HP is now 8
Max attacks... and doesn't hit
Tim attacks... and doesn't hit
Max attacks... and hits a 4 Tim's HP is now 2
Tim attacks... and doesn't hit
Max attacks... and hits a 9 Tim's HP is now -7
Tim died!

Tim won: 5002, Max won: 4998

Next, I tested every build from 50/99/99/99 to 99/50/99/99 against a 99/99/99/99 a million times. Here are the results:

AttackStrengthWinsWin Percentage Against Maxed AccountDeviation from Mean
5099210.00210.000336
5198280.0028-0.000364
5297250.0025-0.000064
5396260.0026-0.000164
5495250.0025-0.000064
5594210.00210.000336
5693180.00180.000636
5792270.0027-0.000264
5891230.00230.000136
5990290.0029-0.000464
6089320.0032-0.000764
6188300.003-0.000564
6287160.00160.000836
6386290.0029-0.000464
6485280.0028-0.000364
6584190.00190.000536
6683200.0020.000436
6782210.00210.000336
6881240.00240.000036
6980200.0020.000436
7079300.003-0.000564
7178240.00240.000036
7277200.0020.000436
7376330.0033-0.000864
7475180.00180.000636
7574180.00180.000636
7673220.00220.000236
7772330.0033-0.000864
7871190.00190.000536
7970180.00180.000636
8069270.0027-0.000264
8168240.00240.000036
8267330.0033-0.000864
8366190.00190.000536
8465320.0032-0.000764
8564300.003-0.000564
8663210.00210.000336
8762300.003-0.000564
8861220.00220.000236
8960280.0028-0.000364
9059250.0025-0.000064
9158200.0020.000436
9257290.0029-0.000464
9356170.00170.000736
9455210.00210.000336
9554280.0028-0.000364
9653230.00230.000136
9752310.0031-0.000664
9851160.00160.000836
9950250.0025-0.000064
mean:0.002436

 

As you can see, the odds are stacked against this account fighting maxed players. The best odds, at 73/76, 77/72, or 82/67, have our underdog winning just .0033% of fights. For every 1,000 GP you stake, your maxed opponent would have to put up 30000x your wager for you to break even: 30,000,000 GP. Nobody in their right mind would do this, meaning that our attempt at gaming the odds falls short. However, the combat simulator that I have created opens up a lot of possibilities for cool projects. Stay tuned.

 


Questions & Criticisms:

Do you seriously think you’re the first person to write a program that simulates combat?
– No, but people who have done it have been rather secretive.

Not accounting for attack style is flawed.
– Choosing one attack style, assuming perfect “switching”, or even cycling through attack styles would have produced similar (if not identical) results and wouldn’t have been worth the effort.

Staking does require skill in that you should be switching attack styles.
– I agree, however it doesn’t take much skill to do this and boxing is still the most bare-bones way to run a simulation of combat.

Nobody is going to stake you magnified amounts, they will figure it is a scam.
– After a certain point this is probably true. However, 2-3x wagers still happen with some frequency.

Your accuracy formula is imprecise.
– I believe all formulas used were extremely accurate and best represented game mechanics

Will you release all of your code?
– Yes. I’m planning on making it more robust first. Ideally I’ll make a GUI if I find the time.

Your base build point (99 defense and hitpoints) is arbitrary.
– Sure, but finding an optimal distribution of atk/str points is more clean cut than figuring how defense and hitpoints factor in.

Accounts with level 1 prayer are going to be seen as suspicious staking-focused accounts.
– True.

Your best builds here are only “optimal” vs maxed accounts.
– I know. I’d have to run a more thorough simulation to figure out the best build at each combat level or the best build relative to odds you can get.

What about whip/dds, another popular variant of staking rules?
– I would like to simulate this in the future.

An Optimal Runescape Staking Build
  • Ryan

    This is quite an interesting study.
    Is it ironic we see peaks at 60 and 70 attack? (When you obtain a new potential in game item)
    Obvious you arent using d scims in boxing, but it does seem odd that those seem to be sweet spots.

    I would love to see a study incorperating defense, as defense increases combat levels more signifcantly than attack and str.
    I would also want to look at even combat level duels, not trying to beat a maxed account necessarily.

    This is very cool and awesome that you made your code public so we all can try.

    • Thanks for your comment. Several months ago I did develop a more advanced simulation that tested every possible combination of stats at each combat level. This required a lot of computing power and, even while renting Amazon servers, there was some bug in my code that I couldn’t figure out. If I ever get it to work, the results will be very interesting and I look forward to sharing them.

      • Ryan

        feel free to share the code on github and comment back the link.
        Me and a buddy of mine can take a look at it and maybe find the issue.

  • Drew

    Tim, I’m interesting in getting a better understanding of your simulation with Java. I’m not very experienced with coding in Java, but I would like to learn for my own purposes. Could you refer me to a source that would serve as a crash course in understanding this type of Java simulation so that I could try this myself? Additionally, I have an idea about using this type of simulation along with the ‘Kelly criterion’ to possibly maximize profits at osrs staking. I am interested in discussing further. Please get back to me

    • This is an example of a brute force/exhaustive simulation. Fast runtime was sacrificed in the interest of getting all data points rather than finding one optimal build. The good news is that this is just a massive nested ‘for’ loop where I get permutation of player_1’s atk+stk == 149 and run it against the max build 1,000,000 times. It gets really ugly when you remove the artificial constraints and try to test everything, as I mentioned below.

      The Kelly Criterion is something I have thought about as well. Fortune’s Formula is a really great book that gave me a lot of ideas for cool side projects. Once you get the win percentage, it’s easy to use the formula. If I ever generalize this and create a GUI, I will probably add that calculation.

  • Drew

    Any chance you could post the github link?

  • Rieno Dota

    Cool study but the math that is the fundation for your research is wrong. If u stake them 1 mill for 2 mill, u need to win 1 out of 3 times, not 1 out of 4.

    • The ‘30000x stake’ logic is correct as well as the ‘150USD per hour with 10% edge’. But I erroneously figured that 26% win percent had edge when staking double, when in actually it has edge when staking triple. Not only would applying an expected value formula have made my argument more clear, but it would have caught any errors in my mental math. Thanks for the correction, I’ll update the article later today.