Im making a browser based PHP game and in my database for the players it has a record of that players total EXP or experience.
What i need is a formula to translate that exp into a level or rank, out of 100.
So they start off at level 1, and when they hit say, 50 exp, go to level 2, then when they hit maybe 125/150, level 2.
Basically a formula that steadily makes each level longer (more exp)
Can anyone help? I'm not very good at maths :P
Many formulas may suit your needs, depending on how fast you want the required exp to go up.
In fact, you really should make this configurable (or at least easily changed in one central location), so that you can balance the game later. In most games these (and other) formulas are determined only after playtesting and trying out several options.
Here's one formula: First level-up happens at 50 exp; second at 150exp; third at 300 exp; fourth at 500 exp; etc. In other words, first you have to gather 50 exp, then 100 exp, then 150exp, etc. It's an Arithmetic Progression.
For levelup X then you need 25*X*(1+X) exp.
Added: To get it the other way round you just use basic math. Like this:
y=25*X*(1+X)
0=25*X*X+25*X-y
That's a standard Quadratic equation, and you can solve for X with:
X = (-25±sqrt(625+100y))/50
Now, since we want both X and Y to be greater than 0, we can drop one of the answers and are left with:
X = (sqrt(625+100y)-25)/50
So, for example, if we have 300 exp, we see that:
(sqrt(625+100*300)-25)/50 = (sqrt(30625)-25)/50 = (175-25)/50 = 150/50 = 3
Now, this is the 3rd levelup, so that means level 4.
If you wanted the following:
Level 1 # 0 points
Level 2 # 50 points
Level 3 # 150 points
Level 4 # 300 points
Level 5 # 500 points etc.
An equation relating experience (X) with level (L) is:
X = 25 * L * L - 25 * L
To calculate the level for a given experience use the quadratic equation to get:
L = (25 + sqrt(25 * 25 - 4 * 25 * (-X) ))/ (2 * 25)
This simplifies to:
L = (25 + sqrt(625 + 100 * X)) / 50
Then round down using the floor function to get your final formula:
L = floor(25 + sqrt(625 + 100 * X)) / 50
Where L is the level, and X is the experience points
It really depends on how you want the exp to scale for each level.
Let's say
LvL1 : 50 Xp
Lvl2: LvL1*2=100Xp
LvL3: LvL2*2=200Xp
Lvl4: LvL3*2=400Xp
This means you have a geometric progression
The Xp required to complete level n would be
`XPn=base*Q^(n-1)`
In my example base is the inital 50 xp and Q is 2 (ratio).
Provided a player starts at lvl1 with no xp:
when he dings lvl2 he would have 50 total Xp
at lvl3 150xp
at lvl4 350xp
and so forth
The total xp a player has when he gets a new level up would be:
base*(Q^n-1)/(Q-1)
In your case you already know how much xp the player has. For a ratio of 2 the formula gets simpler:
base * (2^n-1)=total xp at level n
to find out the level for a given xp amount all you need to do is apply a simple formula
$playerLevel=floor(log($playerXp/50+1,2));
But with a geometric progression it will get harder and harder and harder for players to level.
To display the XP required for next level you can just calculate total XP for next level.
$totalXpNextLevel=50*(pow(2,$playerLevel+1)-1);
$reqXp=$totalXpNextLevel - $playerXp;
Check start of the post:
to get from lvl1 -> lvl2 you need 50 xp
lvl2 ->lvl3 100xp
to get from lvl x to lvl(x+1)
you would need
$totalXprequired=50*pow(2,$playerLevel-1);
Google gave me this:
function experience($L) {
$a=0;
for($x=1; $x<$L; $x++) {
$a += floor($x+300*pow(2, ($x/7)));
}
return floor($a/4);
}
for($L=1;$L<100;$L++) {
echo 'Level '.$L.': '.experience($L).'<br />';
}
It is supposed the be the formula that RuneScape uses, you might me able to modify it to your needs.
Example output:
Level 1: 0
Level 2: 55
Level 3: 116
Level 4: 184
Level 5: 259
Level 6: 343
Level 7: 435
Level 8: 536
Level 9: 649
Level 10: 773
Here is a fast solution I used for a similar problem. You will likely wanna change the math of course, but it will give you the level from a summed xp.
$n = -1;
$L = 0;
while($n < $xp){
$n += pow(($L+1),3)+30*pow(($L+1),2)+30*($L+1)-50;
$L++;
}
echo("Current XP: " .$xp);
echo("Current Level: ".$L);
echo("Next Level: " .$n);
I take it what you're looking for is the amount of experience to decide what level they are on? Such as:
Level 1: 50exp
Level 2: 100exp
Level 3: 150exp ?
if that's the case you could use a loop something like:
$currentExp = x;
$currentLevel;
$i; // initialLevel
for($i=1; $i < 100; $i *= 3)
{
if( ($i*50 > $currentExp) && ($i < ($i+1)*$currentExp)){
$currentLevel = $i/3;
break;
}
}
This is as simple as I can make an algorithm for levels, I haven't tested it so there could be errors.
Let me know if you do use this, cool to think an algorithm I wrote could be in a game!
The original was based upon a base of 50, thus the 25 scattered across the equation.
This is the answer as a real equation. Just supply your multiplier (base) and your in business.
$_level = floor( floor( ($_multipliter/2)
+ sqrt( ($_multipliter^2) + ( ($_multipliter*2) * $_score) )
)
/ $_multipliter
) ;
Related
I am trying to figure out if there is a way to find the percentage between 2 numbers.
It's a progress / ranking system.
I want to find the percentage the $current_exp is between the $current_min and the $current_max values, is there a way to achieve this in PHP? So far I've got to this, but it doesn't work as you progress in ranks, it doesn't treat the $current_min as 0 so when you rank up, it says you are like 75% into your next rank progression when you're in fact 0. Does this make sense?
$currentProg = ($current_exp * 100) / $current_max;
Say the current minimum is 18750 and the current maximum is 25100, the current exp is 22000... What percentage from the min to the max is the current exp? This will change each rank as the $current_min and $current_max variables get set depending on the exp of the user.
The next rank is Current min is 25100 Current max is 34230
Currently, when you are at 26000 exp, the output is saying 75.956763073327% which is not correct, it should be like 1 or 2%?
Thanks in advance 🙏
Not a good mathematician, but it looks like it should be:
(Difference of rank - minimum) / (Difference of maximum - minimum) * 100
<?php
$x = 25100;
$z = 34230;
$y = 26000;
echo ($y - $x + 1) / ($z - $x + 1) * 100; // outputs 9.8674843938232 %
Online Demo
Note: + 1 is added to both numerator and denominator to avoid divide by zero errors.
I am looking for some insight. Artistic creativity and data is more my strong point and not so much math.
Say I have an mmo style text based browser game that I'm messing around with. The level system is potentially unlimited but as people go up in level it will become harder and harder to reach the next level. What I want is for it to start out rather easy. I'll set a base target amount of experience points required to reach level 2. But here's where my problem comes in. I'd like to know what some viable options are for setting the levels going forward. My thoughts were to make a script that I run one time at the creation of the game. It would start with:
$base_xp = 100;
This would be the amount of experience points required to reach level two. Then for every level after that I just want each level to increase by a percentage of the level before it. Perhaps 3 or 5% more than the previous level. The script would run, with
i=1;
and then run a foreach look or while loop and insert into database a table that lists every single level and it's experience point requirement maybe to something like level 1,000 for now.
To me that seems really tedious. But so does manually typing in
$level2 = 100;
$level3 = 205
$level4 = 310;
etc etc.. for possibly thousands of levels.
The only other thing I can think of is a math formula that runs every script execution to see if they've met the requirements for the next level. But I'm a little fuzzy on how to write such a formula being kinda bad at math and all.
Any thoughts would be appreciated. If i had to make a definitive question it would be:
What is an efficient way to dynamically or statically create a list of levels and their xp requirements?
If I do make an SQL table for it and insert them all. i could just query it once and store those requirements with APC or something else, to make retrieval faster during game play and not query the database every time for a table with a thousand or more rows.
Here a two ideas. The first was my comment.
You have a stored number in your game that is always the number at which you increase the cap by. I would do something greater than 3% so it can be super hard later. But I would say maybe 6% being the cap. I'm not going to say that is much greater, but if others can provide a better number than that would be fine. This way when the value hits your CAP you do CAP = CAP * x + CAP and now you have your increased cap. No queries..
However, what if you want to tell someone you can get to level 50 at X. Or level 1000, 10000000000. You may want a table for those instances. In that case I would create a loop of that just stores into a database that has an auto incremented ID and store the XP expected. You will need to determine datatype for the XP but maybe int won't be good enough.
$cap = 100;
for($i = 0;$i < 10000000;$i++){
$cap = $cap * $i + $cap
// insert data
}
Then yea. When you query do their current level and then go up to the next 5 levels so you can keep a running number of XP possibilities. Or query them all you're call. At some point memory will run out.
I would maybe consider only doing up to level 100 until someone gets to level 50 then go to 200, 300 so on so forth until you are satisfied with your game level cap. Always stopping the person from reaching 201 if there is only a cap of 200.
Do you just want to generate the number of points needed for the next level, and each next level is just 4% (for example, as you mentioned) higher than the next? Don't store anything, just calculate it:
<?php
function check_level($lvl) {
$percent_increase = 0.04;
$ret = 100;
if ($lvl == 1)
return 0;
for ($i=3; $i <= $lvl; $i++)
$ret = $ret + ($ret * $percent_increase);
return $ret;
}
for( $x=1; $x <= 20; $x++)
echo "$x: ".check_level($x).PHP_EOL;
?>
Output:
1: 0
2: 100
3: 104
4: 108.16
5: 112.4864
6: 116.985856
7: 121.66529024
8: 126.5319018496
9: 131.59317792358
10: 136.85690504053
11: 142.33118124215
12: 148.02442849183
13: 153.94540563151
14: 160.10322185677
15: 166.50735073104
16: 173.16764476028
17: 180.09435055069
18: 187.29812457272
19: 194.79004955563
20: 202.58165153785
This increases slowly at 4% though. Might want to use something larger, or more likely, something exponential
In case it can ever help anyone in the future.. here is the conclusion I came to.
$level = $currentlevel;
$nextlevel = $level+1;
$cap = 50;
for($i = $level;$i < $nextlevel;$i++){
$percent = 8;
$percentage = $percent / 100;
$sum1 = $percentage * $cap;
$sum2 = $sum1 + $cap;
$sum3 = $sum2 * $i;
echo "level $i: $sum3<br>";
}
I did not actually wind up storing anything in a database. I'm just going to go with dynamic generation. The formula above only calculates the next level ahead. But I also made the following formula based on Cayce's and Mike's recommendations that can generate a list of any number of infinite amount of levels. The below example would show up to level 1,000 and the xp requirements.
$cap = 50;
for($i = 2;$i < 1001;$i++){
$percent = 8;
$percentage = $percent / 100;
$sum1 = $percentage * $cap;
$sum2 = $sum1 + $cap;
$sum3 = $sum2 * $i;
echo "level $i: $sum3<br>";
}
Example Output:
level 2: 108
level 3: 162
level 4: 216
level 5: 270
level 6: 324
level 7: 378
level 8: 432
level 9: 486
level 10: 540
level 11: 594
level 12: 648
level 13: 702
level 14: 756
level 15: 810
level 16: 864
level 17: 918
level 18: 972
level 19: 1026
level 20: 1080
Just so that it makes sense. The amount of xp required seems small between each level, but as you level up your xp is wiped and you have to attain the entire amount of xp listed for that level, not just the difference between say level 3 and level 2.
I need to create a business logic or php function to compute the following: given some input $rank (which is the alexa ranking) I need to compute some $points in such a way that $points will be high for the top ranking website and will decrease with increasing $rank value.
I imagine something like this:
function($rank)
{
$points = x*$rank;
return $points;
}
How do I get $points in such a way that
if the rank is 1 then the points returned is maximum (e.g. 10000).
if rank is 2 then $points returned will be 9500 or nearby.
if rank is 4 then $points returned will be 6000 or nearby.
if rank is 200 $points returned will be 2 or whatever the function will return.
Rule: if $rank is less then $points should be more. Maximal value of $points is 10000 which is for $rank=1.
Now as the $rank increases the $points value should decrease accordingly.
There are many formulas which might satisfy your requiremements.
Nested powers
One possibility:
$points = 10000 * pow(0.993575964272119, pow($rank, 3.16332422407427) - 1)
This gives you the following results:
f(1) = 10000
f(2) = 9500
f(4) = 6000
f(9) = 12.065
f(10) = 0.84341
f(200) = 0
So the three values you fixed (1, 2 and 4) are all satisfied, but the result for 200 indicates that this might not be exactly what you're looking for. The curve looks like this:
By the way, I found this using python and mpmath, by fixing the form of the formula and determining the numbers with the many digits numerically:
>>> import mpmath
>>> print(mpmath.findroot((lambda a,b: 10000*a**(2**b - 1) - 9500,
... lambda a,b: 10000*a**(4**b - 1) - 6000),
... (0.995, 2.7)))
[0.993575964272119]
[ 3.16332422407427]
If you decide on a different form of the function, this approach might be adapted.
Exp of a polynomial
A possible different form with the desired properties would be this:
$points = exp(9.14265175282929 + $rank*(0.127179575914116 - $rank*0.0594909567672230))
This does not decrease quite as quickly as the one above:
f( 1) = 10000
f( 2) = 9500
f( 4) = 6000
f( 13) = 2.1002
f( 14) = 0.47852
f(200) = 0
It was obtained by solving this system of equations:
a + b + c = log(10000)
a + 2b + 4c = log( 9500)
a + 4b + 16c = log( 6000)
to obtain the coefficients a through c for the polynomial. One can add another degree to match f(200)=2 as well, but in that case, the last coefficient will become positive, which means that points will start to increase with rank for very large ranks.
If you want to match that f(200)=2 as well, you can do so using
$points = exp(max(8.86291000469285 - $rank*0.0408488141206645,
9.14265175282929 + $rank*(0.127179575914116 - $rank*0.0594909567672230)))
although this will result in a bend in your curve.
To compare these alternatives to the above:
function getPoints($rank)
{
$returnValue = -0.005 * $rank * $rank - 0.035 * $rank + 100.040;
if ($returnValue < 0) $returnValue = 0;
return $returnValue;
}
This was my thinking.
Function is not forking for large values:
it should atleast give some small value for large ranks...
like if rank is 2000000 then points will be 2.
Thnx btw
So I have a members website written in PHP and MYSQL.
Say for a second I want to let my users level up as a reward system and they get XP from doing certain things on the site like posting and what not. Giving the XP is the easy bit, the bit I am kinda stuck on is how the levelling will actually work.
When someone joins it will auto be at lv 1. Now say it lv x 2 for XP to level up per time;
lv1 = 2 xp
lv2 = 4 xp
lv3 = 6xp
lv4 = 8xp
How do I factor this into a formula, so when the XP is reached it will for the certain LV it will level the user up and double the amount of XP needed?
If we start at level 0, and require 2*level xp to get from level-1 to level (ie: 2 xp gets you to level 1, 2+4 total gets you to level 2, 2+4+6 total for level 3, etc), then we have an arithmetic sequence, and the sum is equal to (level/2) * (2 + (2*level))
Simplifying further:
$total_xp_required = $level * (1 + $level);
Now, if we use the quadratic formula to solve level^2 + level + -xp = 0 for level, we get level = (-1 ± sqrt(1 - 4*(-xp))) / 2.
The positive root will always be the one we want, so of the +/-, we only care about the +. Also, non-integer levels don't make sense, so turn it into an int. The only catch is, floats are kinda flaky -- numbers could come up to like 1.99999999998 or something rather than 2.0. We can add a tiny fudge factor to the number before truncating it.
$level = int((sqrt(1 + ($xp*4)) - 1) / 2 + .000000005);
Now, if you want to double the xp required each time, it gets even easier. Say level 1 requires 2 xp, level 2 takes 4, level 3 takes 8, etc. Then your total xp required for a given level is 2 ^ level.
Powers of 2 being a special case in binary, 2^x can be represented by 1 << x.
$total_xp_required = 1 << $level;
And to calculate the level, there are a number of tricks. Mathematically, the level is the log2 of the score.
$level = intval(log($xp) / log(2) + .000000005);
Or stringwise, we can just count the number of digits in the number's base-2 representation. No fudge factor needed here, since floats never come into the picture.
$level = strlen(sprintf("%b", $xp)) - 1;
Either way, at this point, since we can calculate level from xp and vice versa, you don't really need to store the level at all -- just calculate it when you need it.
formulas:
your sample:
//level => required XP to reach it 1 => 0; 2=>2; 3=>6; 4=>8....
$xpForNextLevel = ($currentLevel)*2;
Double required xp after each level
//level => required XP to reach it 1 => 0; 2=>2; 3=>4; 4=>8; 5=>16; 6=>32....
$xpForNextLevel = pow(2,$currentLevel);
EDIT:
Where $xpForNextLevel is the total amount of xp required to reach the next level, NOT the difference betwean this level and the next.
$expRequired = 2^$level;
or
$expRequired = 2*$level;
I did my leveling and EXP system like this, but mine's more advanced with skill points..
<?php
$level_up = ($level + 1);
if ($exp >= $max_exp)
{
$sql = "UPDATE users SET level=(level + 1) , max_exp=(exp + 300) , skill_points=(skill_points + 3) WHERE id='".$id."' LIMIT 1";
$res = mysql_query($sql);
echo '<div class="Leveled">' . 'You sucessfully leveled up to ' . $level_up . '!' . ' As a reward you were given 3 skill points!' . '</div>';
}
else
{
}
?>
For an online game (MMORPG) I want to create characters (players) with random strength values. The stronger the characters are, the less should exist of this sort.
Example:
12,000 strength 1 players
10,500 strength 2 players
8,500 strength 3 players
6,000 strength 4 players
3,000 strength 5 players
Actually, I need floating, progressive strength values from 1.1 to 9.9 but for this example it was easier to explain it with integer strengths.
Do you have an idea how I could code this in PHP? Of course, I would need mt_rand() to generate random numbers. But how can I achieve this pyramid structure?
What function is it? Root function, exponential function, power function or logarithm function?
Thanks in advance!
It should look like this in a graph:
Pyramid graph http://img7.imageshack.us/img7/107/pyramidy.jpg
You can simulate a distribution such as the one you described using a logarithmic function. The following will return a random strength value between 1.1 and 9.9:
function getRandomStrength()
{
$rand = mt_rand() / mt_getrandmax();
return round(pow(M_E, ($rand - 1.033) / -0.45), 1);
}
Distribution over 1000 runs (where S is the strength value floored):
S | Count
--+------
1 - 290
2 - 174
3 - 141
4 - 101
5 - 84
6 - 67
7 - 55
8 - 50
9 - 38
Note:
This answer was updated to include a strength value of 1.1 (which wasn't included before because of the rounding) and to fix the name of the mt_getrandmax() function
The simplest way to do this would be to provide 'bands' for where a random number should go. In your example, you have 15 players so you could have:
rand < 1/15, highest strength
1/15 < rand < 3/15, second highest
3/15 < rand < 6/15, third highest
6/15 < rand < 10/15, fourth highest
10/15 < rand < 15/15, lowest strength
You could also parameterise such a function with a 'max' number of each band that you allow and when the band is filled, it is subsumed into the next lowest existing band (apart from the bottom band, which would be subsumed into the next highest) to ensure only a certain number of each with a random distribution.
Edit adding from my comments:
To get a floating range pyramid structure the best function would most likely be a logarithm. The formula:
11 - log10(rand)
would work (with log10 being a logarithm with base 10) as this would give ranges like:
1 < rand < 10 = 9 < strength < 10
10 < rand < 100 = 8 < strength < 9
100 < rand < 1000 = 7 < strength < 8
etc.
but rand would need to range from 1 to 10^10 which would require a lot of randomness (more than most random generators can manage). To get a random number in this sort of range you could multiply some together. 3 random numbers could manage it:
11 - log10(rand1 * rand2 * rand3)
with rand1 having range 1-10000 and rand2 and rand3 having range 1-1000. This would skew the distribution away from a proper pyramid slightly though (more likely to have numbers in the centre I believe) so it may not be suitable.
workmad3 has the start of it down, I think, but there's a catch - you need to track your bucket sizes and whether or not they're full. A random number generator won't guarantee that. You'll need to assign your bucket values (strenghs) and sizes (number of people), and let your random generator tell you which bucket to drop the player into - if that one is full, 'overflow' to the next lower.
As to assigning the bucket sizes for a given strength value, that's the tricky bit (and I think what you're really working at). The characteristics of your desired distribution are critical. If you want a linear drop (which the pyramid shape hints at), a line of the form
strength = max_strength - m(number_characters)
would work. Varying the value of m would change the speed at which the line drops off, and will basically limit your max number of total characters. If you're looking for a more sophisticated way for the strength values to drop off, you could use a parabolic or hyperbolic curve - these are a bit more complex, but give you very different characteristics.
something like this
<?php
$rand = rand(1,10);
switch ($rand) {
case 1:
echo "band 1";
break;
case 2:
case 3:
echo "band 2";
break;
case 4:
case 5:
case 6:
echo "band 3";
break;
default:
echo "band 4";
break;
}
?>
Band 1 being the strongest, band 4 being the weakest.
Ofcourse this is basic, you would want to refactor this to use loops instead of hardcoded switches, but you get the idea :)
It's probably easiest to use percentages in this case.
From your examples would approximately be (converted to an array for ease of use later):
$strength[1] = .3; // start with a key of 1
$strength[2] = .26;
$strength[3] = .21;
$strength[4] = .15;
$strength[5] = .08;
That way, you can generate a random number using mt_rand() and divide by the maximum possible value to get a number between 0 and 1:
$rand = mt_rand() / mt_getrandmax(); // rand is some random value between 0 and 1
Then you can use a foreach statement to isolate each case:
$comparisonPercentage = 1;
$selectedLevel = count($strength); // covers the case where mt_rand() returns 0
foreach($strength as $level => $currentPercentage)
{
$comparisonPercentage -= $currentPercentage;
if ($rand > $comparisonPercentage)
{
$selectedLevel = $level;
break;
}
}
// $selectedLevel contains the level you need...
If you do it this way, you only have to change the $strength array if you need to fiddle with the percentages.
generate a random number between 0 and 40000, if its between 0 and 12000, assign strength 1, between 12000 and 22500 assign 2 etc.
Edit: for progressive values between 0 and 10 use the square root of a random number between 0 and 100, then substract if from 10
rand -> strengh
0-1 -> 9.9 -> 9 (1%)
2-4 -> 9 -> 8 (2%)
...
81 - 100 -> 1 - 0 (19%)
For results between 1.1 and 9.9 the formula would be in pseudocode)
strength = 10 - sqrt(rand(1..79))