Author Topic: Skill check (with varied degree of success)  (Read 1004 times)

Offline Chris

  • Game Owner
  • Level 35
  • *
  • Posts: 2,217
  • Reputation: +28/-1
    • View Profile
Skill check (with varied degree of success)
« on: July 08, 2011, 04:00:58 PM »
Players have skills. It's rather linear (each player earn +1 or +2 per day, so there are big differences over long time but not astronomical).

Now I want to make "test a skill". Depending on the test result (highly random) the skill would give from +0 to +5 to encounter outcome.

Example: you have RescuePrincess skill at 10, you encountered a princess in distress. The skill check gave you +1 so you saved the princess and got some minor reward. Next player have skill 1,000, the skill check gave him +0 (bad luck) and he failed. Next one have the skill at 10,000 (I think 10,000 is insanely high and higher won't appear even it the game run for many years) and he managed to get +5 and princess married him.

So, if you have below 20 you can get max +1 (unless very lucky in which case +2). If above 50 then around +2 and +1 easily. Skill 1000 means +3 or +4 or under extreme luck +5. At 5000 you can get +5 if reasonably lucky.

I'm looking for an algorithm that would do something like that.

Offline Zeggy

  • Global Moderator
  • Level 35
  • *****
  • Posts: 1,187
  • Reputation: +13/-4
    • View Profile
Re: Skill check (with varied degree of success)
« Reply #1 on: July 08, 2011, 05:27:51 PM »
Okay, here's my attempt:

First, we need a function to normalize the skill level.
Your mapping of skill level (1-5000) to skill gains (1-5) isn't uniform and is difficult to work with, so I found that log base 3 of the skill level works quite well. This gives a resulting range for normalized(skill level) of:
x -> log(x)/log(3)
1 -> 0.91
20 -> 2.72
50 -> 3.56
1000 -> 6.28
5000 -> 7.75

Second, we calculate the result of +0, +1, etc.
I used a sigmoid function for this (http://en.wikipedia.org/wiki/Sigmoid_function/), but any function will work as long as the y range is between 0 and 5, and the x axis is enough for the normalized skill level.

Here is the full function I used: (1/(1+e^(-x+4)*2))*5
It's adjusted a bit:
The +4 moves the graph right, so a player with skill level 1 starts off with 0 chance of getting +1 (I'm assuming here, maybe you don't want this in your game, I'll explain how to change this below).
The *2 makes the graph a bit less steep, so the gains with higher skill level are more gradual.
The *5 makes sure the y axis ranges from 0-5 (the return value).

You can adjust the +4 and *2 to change the chances of the results.
Increasing the +4 will reduce the chance of higher results.
Increasing the *2 will reduce the increase in chance as skill level goes higher.

Here's what the function looks like: http://i.imgur.com/Gnsd5.png
X axis being the normalized skill level, and y axis the skill gain of 0 to 5.

Putting these two parts together to generate a +0..5 value:
Calculate a random number between 1 and the user's current skill level.
Calculate normalized(random number) using the function in part 1.
Calculate the result of the function in part 2 to get a value between 0 and 5 (the value is a float).

After this, it's up to you how to interpret the results. If it's possible for a skill level 1 user to always get +0, then round to the nearest number. Otherwise, you could round up, then a player with skill level 1 will always get +1.


Example code (not tested):
Code: [Select]
function testSkill($skill_level) {
  $random = rand(1, $skill_level); //generate random number up to skill level
  $x = log($random, 3); //normalize the random value
  return (1/(1+exp(-$x+4)*2))*5; //calculate skill gain
}

$result = testSkill($x);

$gain = ceil($result);
//Or $gain = round($result);


Some data:
http://i.imgur.com/xn6T2.png

Then depending on how you round the result, that gives the possible range of stat gains for a player at that skill level.
« Last Edit: July 08, 2011, 05:55:54 PM by Zeggy »

Offline Chris

  • Game Owner
  • Level 35
  • *
  • Posts: 2,217
  • Reputation: +28/-1
    • View Profile
Re: Skill check (with varied degree of success)
« Reply #2 on: July 09, 2011, 04:03:53 AM »
Nice read, very educational :)

I don't like the tiny gains of higher skill levels. With these numbers it would make the most sense to develop all skills equally to around level 50 and then the progress stops. I guess I need it more linear (since skill gains are linear too).

Also, this is not a formula but an algorithm, more complexity is OK. It does not need to have a clean math representation. Also I would represent it as a matrix not a function.

Like:
skill 1: 30% for +0, 60% +1, 10% +2
skill 20: 20% for +0, 40% +1, 40% +2
skill 100: 20% for +0, 10% +1, 40% +2, 30% +3
skill 1000: 20% for +0, 10% +2, 30% +3, 35% +4, 5% +5

I mean, there could be different formulas for each bonus range (0-5). First you check if you got +5 (the most steep one, require very high skill and have low odds) if not then check if +4 and so on.

Offline Zeggy

  • Global Moderator
  • Level 35
  • *****
  • Posts: 1,187
  • Reputation: +13/-4
    • View Profile
Re: Skill check (with varied degree of success)
« Reply #3 on: July 09, 2011, 04:41:51 AM »
Well then, attempt number 2 :P

Create a table with the following columns:
skill (primary), chance0, chance1.. to chance5 (all ints with range 0 to 100)

The chance0..chance5 columns are the percentage chance out of 100 of getting that skill gain at that skill level.

Example data (from your post):
1, 30, 60, 10, 0, 0, 0
20, 20, 40, 40, 0, 0, 0
100, 20, 10, 40, 30, 0, 0
1000, 20, 0, 10, 30, 35, 5

Code:
//Select the first entry with skill below the player's skill (so 200 skill would select the row with 100 skill)
$chances mysql_fetch_row(mysql_query('SELECT * FROM skill_chances WHERE skill < ' $player_skill_level ' ORDER BY skill DESC LIMIT 1'));

$random rand(0100); //generate random number
$statgain 0;
$current_chance 0;

for (
$i 1$i 6$i++) {
  
$current_chance += $chances[$i];

  
//test random number against the chance of each skill gain
  
if ($random $current_chance) {
    
$statgain $i 1;
    break;
  }
}


Basically it just uses the data in your post above from a database table (but you could put it in a 2d array of course) and calculates the skill gain with a random number.
« Last Edit: July 09, 2011, 04:45:07 AM by Zeggy »

Offline Chris

  • Game Owner
  • Level 35
  • *
  • Posts: 2,217
  • Reputation: +28/-1
    • View Profile
Re: Skill check (with varied degree of success)
« Reply #4 on: July 09, 2011, 06:27:07 AM »
You are barbarian to even think of using MySQL for this :) But it gave me an idea; since there are predefined ranges, why not try linear interpolation?
Code: [Select]
$chances1=array(30, 60, 10, 0, 0, 0);
$chances10=array(20, 40, 40, 0, 0, 0);
$chances100=array(20, 10, 40, 30, 0, 0);
$chances1000=array(20, 0, 10, 30, 35, 5);
$chances10000=array(20, 0, 5, 30, 35, 10);

if($level<10) {$src_chances=$chances1; $dst_chances=$chances10; $start=1;$end=10;}
elseif($level<100) {$src_chances=$chances10; $dst_chances=$chances100; $start=10;$end=100;}
elseif($level<1000) {$src_chances=$chances100; $dst_chances=$chances1000; $start=100;$end=1000;}
else {$src_chances=$chances1000; $dst_chances=$chances10000; $start=1000;$end=10000;}

if($level>10000) $level=10000; // dirty cap.

$x1=$start;
$y1=$src_chances[0];
$x2=$end;
$y2=$dst_chances[0];
$targetx=$level;
$targety0=$y1+($y2-$y1)*($targetx-$x1)/($x2-$x1);
$y1=$src_chances[1];
$y2=$dst_chances[1];
$targety1=$y1+($y2-$y1)*($targetx-$x1)/($x2-$x1);

$chances=array($targety0, $targety1, ...);
Of course this would break percentages, but that's not a problem since we can use them as weights instead.


Note: comment also all the solutions so far from the gameplay point of view.

 


SimplePortal 2.3.3 © 2008-2010, SimplePortal