ToME: the Tales of Maj'Eyal

Everything about ToME
It is currently Sun Nov 17, 2019 10:07 pm

All times are UTC




Post new topic Reply to topic  [ 5 posts ] 
Author Message
PostPosted: Tue Nov 20, 2012 10:03 pm 
Offline
Uruivellas

Joined: Thu Nov 18, 2010 6:42 pm
Posts: 744
Improved checkHit function

TOME currently uses two different functions to check for success in opposed roll situations, such as one skill countering another. These cases come up extensively in such things as melee attacks, spell saving throws, stealth and invisibility, detecting and disarming traps, etc. For most to-hit checks, the function checkHit compares offense (atk) verses defense (def) with the following formula:

HitChance = 50 + 2.5 * (atk - def)

This function is simple and linear, but needs hard limit checks to be robust, and is unforgiving of wide differences in offense versus defense. That is, the chance of success quickly vanishes or tops out once the difference between atk and def approaches +/-20 (subject to hard-coded limits). This is an important limitation because it allows certain combinations of npc (and player) talents to become nearly invincible. A random boss with several defense bonuses can become almost completely unhittable by a melee-based character, for example.

The second function, checkHitOld works in almost the same way, but uses an exponential function to compute success chance:

a = 1/(1 + exp(-(atk - def)/7))
b = atk/(atk + def)
HitChance = 50 * (a + b)

with limit checks to avoid singularities when atk = def. This function is smoother and more forgiving, but is computationally more expensive (not really an issue here) and starts to break down at low defense values -- it cannot tolerate atk and def <= 0. The function behaves differently in the early game than in the late game because the balance between atk and def is significantly affected by the magnitude of the values involved. Both functions require limit checks (default 5% and 95%) to keep success probabilities reasonable throughout the normal range of play.

As an alternative to these functions, I propose a new checkHit function (that I call checkHitSmooth) that combines the best features of both of the current formulas:

HitChance = 50 + 50 * (atk - def)/(abs(atk - def) + m)
m = constant > 0 (default 10)

This function is simple, both mathematically and computationally, is inherently bounded (it always gives a hit chance between 0% and 100% without limit checks needed, and scales well with all character levels. The tuning paramer, m, directly affects the slope of the line passing through (50%,0). Higher values flatten out the curve, while smaller ones steepen it near the middle. The following charts plot a comparison of the three functions (with the limit checks in place for checkHit and checkHitOld):
Attachment:
Charts1-4.jpg
Charts1-4.jpg [ 160.72 KiB | Viewed 3265 times ]

The stability of the new function is obvious. Since it depends only on the difference between atk and def, and not their absolute values, it maintains its integrity at any level, preserving game balance from character level 1 to level 50 (or beyond). It tolerates any conceivable range of values. (Since everyone really wants to know that -1,000,000 atk versus +10,000 def has a 0.000495% chance to succeed, when m = 10.) By tolerating negative numbers, it opens up the option of having negative offensive stats, particularly in the early game where debuffs are currently limited by small combat values.

Further, the new routine (below) is a completely compatible drop-in replacement for the current checkHit function, without any other changes required in the game:
Code:
-- I5 updated to smooth out hit probabilities and to be more forgiving of level differences
function _M:checkHit(atk, def, min, max, factor)
   local min = min or 0
   local max = max or 100
   if game.player:hasQuest("tutorial-combat-stats") then
      min = 0
      max = 100
   end --ensures predictable combat for the tutorial
   local hit = 50+50*(atk-def)/(math.abs(atk-def)+10) -- tunable parameter in denominator 10> ~ 16.7% hit at -20 atk-def
   hit = util.bound(hit, min, max)  -- Limit checks aren't needed with the new formula.  Left in for compatibility
   print("checkHit(smooth)",atk,def,"=> chance to hit", hit)
   return rng.percent(hit), hit
end

The "factor" argument is a holdover from a previous version of checkHit that is still included (though not used) in some of the game's older routines. (It would be desirable to use this as a dynamic tuning parameter for the new function, but all of the legacy function calls would need to be updated.)

By changing the constant, m, checkHitSmooth can be tuned to more closely mimic either of the existing checkHit and checkHitOld functions. With m=20, it gives nearly identical results to checkHitOld, but without the deterioration at low values:
Attachment:
Chart5.jpg
Chart5.jpg [ 42.44 KiB | Viewed 3265 times ]

I have tested this function extensively in my Infinite500 addon, which requires that a large range of values be tolerated, sometimes as much as +/-100 deep in the I.D. Replacing both existing functions with this one, I have used m=10 as the tuning parameter as a compromise between the more forgiving checkHitOld and maintaining consistency in game play with the more widely used existing (linear) checkHit function. I think this has worked out especially well, since tactical uncertainty is increased: improbable events do occur requiring you to stay on your toes and you always have a reasonable chance to land an attack.

_________________
Author of the Infinite 500 and PlenumTooltip addons, and the joys of Scaling in ToME.


Top
 Profile  
 
PostPosted: Tue Nov 20, 2012 10:42 pm 
Offline
Uruivellas

Joined: Tue May 15, 2012 11:44 pm
Posts: 800
Wow. Nice work. :)

_________________
<darkgod> all this fine balancing talk is boring
<darkgod> brb buffing boulder throwers


Top
 Profile  
 
PostPosted: Tue Nov 20, 2012 10:42 pm 
Offline
Sher'Tul

Joined: Mon Jun 13, 2011 4:10 pm
Posts: 1262
+1000

Also, could this be applied to power vs. save checks?

_________________
Sorry about all the parentheses (sometimes I like to clarify things).


Top
 Profile  
 
PostPosted: Tue Nov 20, 2012 10:45 pm 
Offline
Uruivellas

Joined: Tue May 15, 2012 11:44 pm
Posts: 800
bricks wrote:
+1000

Also, could this be applied to power vs. save checks?


That would be good.

_________________
<darkgod> all this fine balancing talk is boring
<darkgod> brb buffing boulder throwers


Top
 Profile  
 
PostPosted: Tue Nov 20, 2012 10:58 pm 
Offline
Uruivellas

Joined: Thu Nov 18, 2010 6:42 pm
Posts: 744
supermini wrote:
bricks wrote:
+1000

Also, could this be applied to power vs. save checks?


That would be good.

That's the default behavior.

_________________
Author of the Infinite 500 and PlenumTooltip addons, and the joys of Scaling in ToME.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC


Who is online

Users browsing this forum: Preternex and 0 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group