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.
Related
This will be a pretty strange question, but bear with me.
I'm coding a browser based game, and each player has an amount of guards, each with 100 health. Every time they are shot, the guards lose health. If all the guards are dead, the player takes the health instead.
The shot damage always overlaps as well, so if player has 3 guards, and the top guard has 60 health, a shot of 100 will kill guard 3 and leave guard 2 with 60 health.
i use a php array to sort this, and it works, except when it comes down to the players health. It doesn't calculate correctly, eg all a players guards are dead and the player has 60 health left. He is shot for 100, but instead of dying the health loops round so he has some other number health instead of -40.
$p_bg = array(); // players guard count
$p_bg[0] = $rs[bgs_hp2]; // only the top guard hp is saved (bgs_hp2)
$p_hp = $rs[hp2]; // players health
$dmg = 80 // shot damage
$x = 0;
while($x < $rs[BGs2]) { $p_bg[$x] = 100; $x++; }
// As long as there's still damage to take and bgs to take it:
while($dmg && !empty($p_bg)) {
$soak = min($p_bg[0], $dmg); // not more than the first bg can take
$p_bg[0] -= $soak; // remove hps from the first bg
$dmg -= $soak; // deduct from the amount of damage to tage
if ($p_bg[0] == 0) {
// bodyguard dead, remove him from the array
array_shift($p_bg);
}
}
// If there's any damage left over, it goes to hp
$p_hp = $p_hp - $dmg;
Without knowing the contents of $rs or knowing what the constants bgs_hp2, hp2, and BGs2 are, it's hard to say, but it seems like the problem lies around this combination of lines:
$p_bg = array(); // Create an empty array
$p_bg[0] = $rs[bgs_hp2]; // Populate the first index, possibly null?
$x = 0;
while($x < $rs[BGs2]) { $p_bg[$x] = 100; $x++; }
I'd suspect BGs2 is the bodyguard count for the player? It seems that you're setting the top bodyguard's health to 100 each time this code is hit. Maybe it would be more clear if it was rearranged as follows:
$p_bg = array($rs[bgs_hp2]);
for ($x = 0; $x < $rs[BGs2]; $x++) { $p_bg[] = 100; }
Other than that, log out your variables (using print_r($var) or var_dump($var) as necessary) to see the actual data you're executing on. Good luck!
My question is how could I replace those if's with math formula?
if ($l <= 3500)
{
$min = 100;
}
elseif ($l <= 4000)
{
$min = 120;
}
elseif ($l <= 4500)
{
$min = 140;
}
elseif ($l <= 5000)
{
$min = 160;
}
As you see this is raising 20 for every 500 levels.
As you see this is raising 20 for every 500 levels.
Well, that's your formula right there.
$min = 100 + ceil(($l-3500)/500) * 20;
We start with 100, our base value and add that to the rest of the calculation.
$l starts with 3500 less.
We ceil() the result since we only want to jump when we pass the whole value.
We multiply that by 20.
If we want to address the case where $l is less than 3500 and set 100 as the minimum value, we also need to asset that $l-3500 is more than zero. We can do this as such:
$min = 100 + ceil(max(0,$l-3500)/500) * 20;
How did I get there?
What we're actually doing is plotting a line. Like you said yourself we go a constant amount for every constant amount. We have something called linear progression here.
Great, so we recognized the problem we're facing. We have an imaginary line to plot and we want integer values. What next? Well, let's see where the line starts?
In your case the answer is pretty straightforward.
if ($l <= 3500) {
$min = 100;
}
That's our starting point. So we know the point (3500,100) is on our line. This means the result starts at 100 and the origin starts at 3500.
We know that our formula is in the form of 100+<something>. What is that something?
Like you said, for every 500 levels you're raising 20. So we know we move 20/500 for every 1 level (because well, if we multiply that by 500 we get our original rule). We also know (from before) that we start from 3500.
Now, we might be tempted to use $min = 100 + ($l-3500) * (20/500); and that's almost right. The only problem here is that you only want integer values. This is why we ceil the value of level/500 to only get whole steps.
I tried to keep this with as little math terminology as possible, you can check the wikipedia page if you want things more formal. If you'd like any clarification - let me know
Here is my approach about this problem. It's not better than a single-line formula, but for sake of being modifiable, I generally decide this kind of solutions:
$min = 100;
for($i=3500; $i<=5000; $i+=500)
{
if($l <= $i) break;
$min += 20;
}
//Now $min has got desired value.
You can express the function as follows:
f(x) := a * x + b
The inclination of the line is calculated as:
a := 20 / 500
To find b you need to extrapolate a value that's on the line; in this case, that could be 3500 (x) and 120 (f(x)). That works out to be -40.
So the function has become:
f(x) := (20 / 500) * x - 40
There are two special cases:
Left of 3500 the value of f(x) must remain 100, even though f(x) is less.
The inclination is not continuous but discrete.
Both cases applied:
$min = max(100, ceil($l / 500) * 20 - 40)
I would like advice on the best/most efficient way to get an adjusted score for a team based on a raw score. Let me explain.
The teams are contract bridge teams and the raw score for the winner is from 0 (tie) to any number, but greater than 100 would be rare. The raw score is called IMPS and the adjusted score is called VPs, but that is just for clarity.
The adjustment table is:
IMPs VPs
1 thru 2 16 to 14
3 thru 4 17 to 13
5 thru 7 18 to 12
8 thru 11 19 to 11
12 thru 15 20 to 10
16 thru 20 21 to 9
21 thru 26 22 to 8
27 thru 33 23 to 7
34 thru 41 24 to 6
42 thru 50 25 to 5
51 thru 60 26 to 4
61 thru 71 27 to 3
72 thru 83 8 to 2
84 thru 95 29 to 1
96+ 30 to 0
Here is my code. It works fine:
PHP
$teamArawScore = 20; //Actual result will come from form input
if ($teamArawScore >95 )
{
$teamAadjScore = 30;
$teamBadjScore = 0;
}
else
{
$adjustmentArray = array
(15,
16,16,
17,17,
18,18,18,
19,19,19,19,
20,20,20,20,
21,21,21,21,21,
22,22,22,22,22,22,
23,23,23,23,23,23,23,
24,24,24,24,24,24,24,24,
25,25,25,25,25,25,25,25,25,
26,26,26,26,26,26,26,26,26,26,
27,27,27,27,27,27,27,27,27,27,27,
28,28,29,29,28,28,28,28,28,28,28,28,
29,29,29,29,29,29,29,29,29,29,29,29);
$teamAadjScore = $adjustmentArray[$teamArawScore];
$teamBadjScore = 30 - $teamAadjScore;
}
echo "TeamA won by $teamArawScore so it won $teamAadjScore VPs and TeamB won $teamBadjScore VPs.";
My approach just seems inefficient. Since the array is small, I doubt there is any performance issues, but I would like to do the conversion as efficiently as possible.
What do you suggest?
Since there's no consistency in the table you described you will always be populating a lookup array containing the actual logic. You could also structure this differently, with a shorter array containing the sequential amount of points leading to a given score etc., but in the end those would all result in having to loop over the array to see where you end up - swapping memory for CPU cycles.
Effectively, you need a lookup table anyway because there appears to be no algorithm that can reliably map the contents of the lookup table, and your implementation is now O(1) in big-O notation. As such it can by definition not be made more efficient.
For reference on lookup tables (emphasis added in quote):
In computer science, a lookup table is an array that replaces runtime
computation with a simpler array indexing operation. The savings in
terms of processing time can be significant, since retrieving a value
from memory is often faster than undergoing an 'expensive' computation
or input/output operation. The tables may be precalculated and
stored in static program storage, calculated (or "pre-fetched") as
part of a program's initialization phase (memoization), or even stored
in hardware in application-specific platforms.
There's nothing 'bad practice' about using them. Back in the days when CPUs didn't have floating point units on board we'd have entire sine and sqrt tables embedded in programs to circumvent computationally expensive calculations at runtime.
I got a little something for you.Even though I'm not sure it would actualy optimize the execution speed.
Looping through an array, knowing the base value being 16 if we have a positive score (higher than one) from the A team.
This is not optimized at all (mainly because of the $a == 0 condition), but here it is:
<?php
$teamArawScore = 30;
$a = $teamArawScore;
$teamAfinalScore = 16;
$scoreArray = array(3,5,8,12,16,21,27,34,42,51,61,72,84,10000000000);
$count = 0;
foreach($scoreArray as $elem)
{
if($a < $elem)
{
$teamAfinalScore += $count;
break;
}
$count++;
}
if($a ==0)
{
$teamAfinalScore = 15;
$teamBfinalScore = 30 - $teamAfinalScore;
}
echo "Team A: ".$teamAfinalScore. "<br />Team B:".$teamBfinalScore;
?>
<?php
$teamArawScore = 1000; //Actual result will come from form input
if ($teamArawScore >95 )
{
$teamAadjScore = 30;
$teamBadjScore = 0;
}
else
{
$adjustmentArray = array('1'=>16,'3'=>17,'5'=>18,'8'=>19,'12'=>20,'16'=>21,'21'=>22,'27'=>23,'34'=>24,'42'=>50,'51'=>60,'61'=>27,'72'=>83,'84'=>95);
$base_score=array(1,3,5,8,12,16,21,27,34,42,51,61,72,84);
$count=count($base_score);
$adjustment_value=$adjustmentArray['1'];
for($i=1; $i<$count-1; $i++){
if($teamArawScore < $base_score[$i+1]){
$adjustment_value=$adjustmentArray[$base_score[$i]];
break;
}
else{
$adjustment_value=$adjustmentArray[$base_score[$i]]; // for values greater than 84
}
}
$teamAadjScore = $adjustment_value;
$teamBadjScore = 30 - $teamAadjScore;
}
echo "TeamA won by $teamArawScore so it won $teamAadjScore VPs and TeamB won $teamBadjScore VPs." ;
The PHP blackjack script is simple, I have an array of cards and I select a random one and add it and it's also pretty easy to keep the count the hard part comes in with the aces.
Is there any efficient method to counting them except bruteforcing? Theoretically it would be possible (although highly unlikely) to get 4 aces in a row, how would I make it count as 14 and not 44, 34, 24 etc? (closest to 21 without getting over it)
Something like this to handle the aces:
$total = 0;
// Sort in a way that the aces are last, handle other cards FIRST
foreach($cards as $card)
{
switch($card)
{
case "king":
case "queen":
case "jack":
case "10":
$total += 10;
break;
// Etc, other cards
case "ace":
if($total >= 11)
{
$total += 1;
}
else
{
$total += 11;
}
break;
}
}
Because of the rules for aces, a card in blackjack does not have a value by itself. You do not look at each card, determine a value, and add those.
You look at the hand, and determine the value of the hand.
Now when determining the value of a hand, its true for most cards the value is equal to the card number, but you need special logic for face cards and aces.
Therefore:
Don't draw "numbers" from your deck, draw "cards", and write a function that evaluates a "hand" (list) of "cards" into a value.
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
) ;