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
{
}
?>
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.
Okay, so i don't really know how I go about this.
I'm currently working on a lottery system for a game.
I have a table with virtual items which I want to randomly select by a likely chance.
Table examples:
ID = 1, item_name = Sword, likely_chance = 75
ID = 2, Item_name = 10,000, likely_chance = 20
For id 2, 10,000 represents 10,000 coins.
I want to come up with an algorithm which will select a item with a higher chance of selecting a higher likely chance but also still be able to win a item with a lower likely chance rarely.
If you have items with "likely chances" of C1, C2, C3...Cn, then you can calculate the sum Ctotal.
Then, you can get a random value between 0 and Ctotal, and walk through your array (order is irrelevant) until the sum of "skipped" items exceeds this random value.
For example, in your case, Ctotal = 75 + 20 = 95. Get a random number between 0 and 95, and if it is less than 75 - give a sword; else - give 10000 coins. It will provide a fair winnings distribution according to your likely chances - 78.95% and 21.05%, respectively.
$items = ['Sword', '10000 coins'];
$chances = [70, 25];
$ctotal = array_sum($chances); echo "Total is $ctotal\n";
$rand = rand(0, $ctotal); echo "Rand is $rand\n";
$i = 0;
$currentSum = 0;
while (true)
{
$currentSum += $chances[$i];
if ($currentSum >= $rand)
{
echo "You win: ".$items[$i];
break;
}
$i++;
}
Here is the working Demo. Note that IDEOne remembers the last output and doesn't run this program again every time. The output will appear to be the same, but it is not.
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
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
) ;
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))