Gen 1 Endgame analyses

Hipmonlee

Have a nice day
is a Community Contributoris a Senior Staff Member Alumnusis a Smogon Discord Contributor Alumnusis a Tiering Contributor Alumnusis a Top Contributor Alumnusis a Battle Simulator Moderator Alumnusis a Four-Time Past WCoP Champion
This is partly inspired by something I have seen at RBY2k10. But I am hoping this will develop into something much more.

Ok, RBY is a pretty simple game and all, and when near the end of the battle and are down to just one or two pokemon, there are some scenarios that are probably a little more likely than some others.

I thought, RBY is so thoroughly known to players, but, only at the anecdotal level really. But there really is some room for some cold hard mathematics here. To really get down to the nitty gritty.

So anyway I decided to start with Tauros vs Tauros. When you have a 100% Tauros against another 100% Tauros to win the game, I am confident enough in saying that the first move you should pick should definitely be Body Slam.

So lets say you bodyslam and one of you gets a critical hit, and the other one gets a paralysis.

What is each players chance of victory, and what is their best move?

Well to define the situation more clearly:
Tauros A (who got hit with a critical hit) has between 167 and 134 hp remaining.
Tauros B (who is paralysed) has between 258 and 241 hp remaining.

Hyperbeam does at a minimum 168 damage (convenient!) and as a maximum (without CHing) 198.

A critical hit Bodyslam does at a maximum 219 damage. Which is not enough for Tauros A to KO Tauros B.

So Tauros B should definitely use Hyperbeam because he will definitely KO if he hits with it.

Tauros A can KO with a critical hit Hyperbeam or can bodyslam and hope for the FP.

However, the maths suddenly gets very difficult, because either side can perpetually miss even after they have run out of pp. Since I am not a mathematician, I have to look for a different way of approaching these sorts of problems. One of the lovely things about probabilities is that you can get results several orders of magnitude more accurate than you need if you can just run 10,000,000 or so tests..

So, having quickly performed ten-million tests, I can tell you that by bodyslamming Tauros A won 32.8% of them. By Hyperbeaming it won 26.7%.

This is more or less as expected, aside from the strength of the advantage to the Tauros with greater health..


Here is a similar scenario.

Lets say TaurosA got bodyslammed by a Snorlax earlier in the battle, and TaurosB got paralysed by a Chansey.

So TaurosA at 242 health (pretty sure damage range doesnt affect anything here) vs TaurosB at full health and paralysed. Each one bodyslamming until they reach KO range of hyperbeam (note there is a bias here, they wont hyperbeam until they get to ko range, even if they are in ko range and a ch bodyslam wont do enough to ko).

Tauros A, the damaged unparalysed Tauros wins 45.5% of the time.


These scenarios were just damage calculation. But from this, and other things like this, we should be able to build detailed analyses for much more complicated scenarios. Like, build up decision trees for more and more complicated scenarios.


To build these I have a stupid java programme that I hardcode the decisions into. I used it to run some wrap scenarios a while back.. Like, Starmie switching into a weakened dragonite as it uses agility (Nite wins >50% of the time). I would be curious to see how far we could pursue this. Perhaps it would be good to have a few close logs we could look at the closing stages of.. I have some, but I kinda want to finish the tournament they are from first..

Also to do this (though in the end it proved rather useless), I wrote a python damage calculating programme. It demonstrates my utter lack of python knowledge I am sure, and is actually pretty shoddily put together even by my standards..

But it tells you all the possible damage outputs, rather than just min max and average.

Code:
import argparse

eff = {
 "SESE": 40,
 "SE": 20,
 "neutral": 10,
 "NVE": 5,
 "NVENVE": 2.5
}
stb = {
 "STAB": 1.5,
 "noSTAB": 1
}
parser = argparse.ArgumentParser()
parser.add_argument("Atk", type=int)
parser.add_argument("Def", type=int)
parser.add_argument("Power", type=int)
parser.add_argument("STAB")
parser.add_argument("Effectiveness")
args = parser.parse_args()
S = stb[args.STAB]
T = eff[args.Effectiveness]
print("possible damages without CH: ");
output = ""
for R in range(217,256):
  damage = int( 42 * args.Atk * args.Power )
  damage = int( damage / max(1, args.Def) )
  damage = int( damage / 50 )
  damage = int( damage + 2 )
  damage = int( damage * S )
  damage = int( damage * T )
  damage = int( damage / 10 )
  damage = int( damage * R )
  damage = int( damage / 255 )
  output += str(damage)
  if R == 237: output += " (median)"
  if R != 255: output +=(", ")
print(output)
print("possible damages with CH: ")
output = ""
for R in range(217,256):
  damage = int( 82 * args.Atk * args.Power )
  damage = int( damage / max(1, args.Def) )
  damage = int( damage / 50 )
  damage = int( damage + 2 )
  damage = int( damage * S )
  damage = int( damage * T )
  damage = int( damage / 10 )
  damage = int( damage * R )
  damage = int( damage / 255 )
  output += str(damage)
  if R == 237: output += " (median)"
  if R != 255: output += ", "
print(output)
So to run it you just run it with the pokemons attack followed by its defense followed by base powered followed by either STAB or noSTAB followed by one of SESE SE neutral NVE NVENVE.

If that makes any sense to you.. I just thought this might be useful to someone. Probably most of you who can use it could make a better one yourself..
 
I really don't know what the whole discussion is but I thought I'd just point out there is no advantage to having it bring out the entire range of possible damages vs just the min/max. In the one rare case it does make a difference, it'd require a 6th argument, which is opponent/your current HP.

In this case, you get a more accurate interpretation of your "true" odds of dealing "enough" damage. But of course, this is still minute differences. I feel like I'm getting the numbers wrong but whatever.

Code:
import argparse
from decimal import *

eff = {
 "SESE": 40,
 "SE": 20,
 "neutral": 10,
 "NVE": 5,
 "NVENVE": 2.5
}
stb = {
 "STAB": 1.5,
 "noSTAB": 1
}
parser = argparse.ArgumentParser()
parser.add_argument("Atk", type=int)
parser.add_argument("Def", type=int)
parser.add_argument("Power", type=int)
parser.add_argument("STAB")
parser.add_argument("Effectiveness")
parser.add_argument("HP", type=int)
args = parser.parse_args()
S = stb[args.STAB]
T = eff[args.Effectiveness]
print("possible damages without CH: ");
output = ""
counter = 0
for R in range(217,256):
  damage = int( 42 * args.Atk * args.Power )
  damage = int( damage / max(1, args.Def) )
  damage = int( damage / 50 )
  damage = int( damage + 2 )
  damage = int( damage * S )
  damage = int( damage * T )
  damage = int( damage / 10 )
  damage = int( damage * R )
  damage = int( damage / 255 )
  
  if R == 217: min = damage
  if R == 255: max = damage
  
  if damage > args.HP: counter += 1 
  
  output += str(damage)
  
  
  if R == 237: output += " (median)"
  if R != 255: output +=(", ")
  
denom = max - min
numer = args.HP - min
ans = 1-Decimal(numer)/Decimal(denom)
if ans < 0: ans = 0
if ans > 1: ans = 1
print(output)
print "true odds of doing enough damage: ", (Decimal(counter)/Decimal(39))
print "min/max way odds: ", ans
Code:
python asdf.py 298 288 85 STAB neutral 106
possible damages without CH: 
95, 95, 96, 96, 97, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 101, 102, 102, 103, 103, 104 (median), 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 108, 109, 109, 110, 110, 111, 111, 112
true odds of doing enough damage:  0.3076923076923076923076923077
min/max way odds:  0.3529411764705882352941176471
 

Users Who Are Viewing This Thread (Users: 1, Guests: 0)

Top