Increase precision for cp/size calc

still isn't close enough, but removes the usage of decimals for cp calc.
redd.it/ahac9i

CP calc doesn't flag anything I have that is legal, but the calc is
still off by 2 bits at most... whyyyy? Even the same code run in cpp
results in the same inaccuracy? Maybe this is some arm64 difference?
This commit is contained in:
Kurt 2019-01-22 21:08:48 -08:00
parent 13c13d5f49
commit c033fe2741
2 changed files with 72 additions and 20 deletions

View File

@ -280,9 +280,11 @@ public void VerifyVersionEvolution(LegalityAnalysis data)
private static void VerifyBelugaStats(LegalityAnalysis data, PB7 pb7)
{
if (Math.Abs(pb7.HeightAbsolute - pb7.CalcHeightAbsolute) > 0.001)
// ReSharper disable once CompareOfFloatsByEqualityOperator -- THESE MUST MATCH EXACTLY
if (!IsCloseEnough(pb7.HeightAbsolute, pb7.CalcHeightAbsolute))
data.AddLine(GetInvalid(LStatIncorrectHeight, CheckIdentifier.Encounter));
if (Math.Abs(pb7.WeightAbsolute - pb7.CalcWeightAbsolute) > 0.001)
// ReSharper disable once CompareOfFloatsByEqualityOperator -- THESE MUST MATCH EXACTLY
if (!IsCloseEnough(pb7.WeightAbsolute, pb7.CalcWeightAbsolute))
data.AddLine(GetInvalid(LStatIncorrectWeight, CheckIdentifier.Encounter));
if (pb7.Stat_CP != pb7.CalcCP && !IsStarter(pb7))
data.AddLine(GetInvalid(LStatIncorrectCP, CheckIdentifier.Encounter));
@ -295,6 +297,13 @@ private static void VerifyBelugaStats(LegalityAnalysis data, PB7 pb7)
}
}
private static bool IsCloseEnough(float a, float b)
{
var ia = BitConverter.ToInt32(BitConverter.GetBytes(a), 0);
var ib = BitConverter.ToInt32(BitConverter.GetBytes(b), 0);
return Math.Abs(ia - ib) <= 2;
}
private static bool IsTradeEvoRequired7b(IEncounterable enc, PKM pb7)
{
// There's no everstone! All Trade evolutions must evolve.

View File

@ -1,4 +1,5 @@
using System;
using System.Runtime.CompilerServices;
namespace PKHeX.Core
{
@ -452,8 +453,7 @@ public int BaseCP
var p = PersonalInfo;
int level = CurrentLevel;
int nature = Nature;
int friend = CurrentFriendship; // stats +10% depending on friendship!
int scalar = (int)(((friend / 255.0f / 10.0f) + 1.0f) * 100.0f);
int scalar = CPScalar;
// Calculate stats for all, then sum together.
// HP is not overriden to 1 like a regular stat calc for Shedinja.
@ -465,7 +465,23 @@ public int BaseCP
+ (ushort)(GetStat(p.SPA, HT_SPA ? 31 : IV_SPA, level, nature, 2) * scalar / 100)
+ (ushort)(GetStat(p.SPD, HT_SPD ? 31 : IV_SPD, level, nature, 3) * scalar / 100);
return (int)((statSum * 6f * level) / 100f);
float result = statSum * 6f;
result *= level;
result /= 100f;
return (int)result;
}
}
public int CPScalar
{
get
{
int friend = CurrentFriendship; // stats +10% depending on friendship!
float scalar = friend / 255f;
scalar /= 10f;
scalar++;
scalar *= 100f;
return (int) scalar;
}
}
@ -477,9 +493,11 @@ public int AwakeCP
if (sum == 0)
return 0;
var lvl = CurrentLevel;
// var scalar = ((lvl * 4.0f) / 100.0f) + 2.0f;
var scalar = ((lvl * 4.0m) / 100.0m) + 2.0m; // they don't use decimal but c# rounding mode
return (int)(sum * scalar);
float scalar = lvl * 4f;
scalar /= 100f;
scalar += 2f;
float result = sum * scalar;
return (int)result;
}
}
@ -492,7 +510,6 @@ public void ResetCalculatedValues()
ResetWeight();
}
// ReSharper disable RedundantCast
// Casts are as per the game code; they may seem redundant but every bit of precision matters?
// This still doesn't precisely match :( -- just use a tolerance check when updating.
// If anyone can figure out how to get all precision to match, feel free to update :)
@ -531,46 +548,72 @@ public static int GetSizeRating(int scalar)
return 4; // 1/16 = XL
}
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
private static float GetHeightRatio(int heightScalar)
{
return (float)((float)((float)(byte)heightScalar / 255.0f) * 0.8f) + 0.6f;
// +/- 40%
float result = (byte) heightScalar / 255f;
result *= 0.8f;
result += 0.6f;
return result;
}
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
private static float GetWeightRatio(int weightScalar)
{
return (float)((float)((float)((float)((float)(byte)weightScalar / 255.0f) * 0.4f) + 0.8f));
// +/- 20%
float result = (byte) weightScalar / 255f;
result *= 0.4f;
result += 0.8f;
return result;
}
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
public static float GetHeightAbsolute(PersonalInfo p, int heightScalar)
{
float HeightRatio = GetHeightRatio(heightScalar);
return HeightRatio * (float)p.Height;
return HeightRatio * p.Height;
}
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
public static float GetWeightAbsolute(PersonalInfo p, int heightScalar, int weightScalar)
{
float HeightRatio = GetHeightRatio(heightScalar);
float WeightRatio = GetWeightRatio(weightScalar);
return HeightRatio * (float)(WeightRatio * (float)p.Weight);
float weight = WeightRatio * p.Weight;
return HeightRatio * weight;
}
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
public static byte GetHeightScalar(float height, int avgHeight)
{
// height is already *100
int v11 = (int)(float)((float)((float)(height + (float)((float)avgHeight * -0.6f)) / (float)((float)avgHeight * 0.8f)) * 255.0f);
int v12 = v11 & ~(v11 >> 31);
return (byte)Math.Min(255, v12);
float biasH = avgHeight * -0.6f;
float biasL = avgHeight * 0.8f;
float numerator = biasH + height;
float result = numerator / biasL;
result *= 255f;
int value = (int) result;
int unsigned = value & ~(value >> 31);
return (byte)Math.Min(255, unsigned);
}
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
public static byte GetWeightScalar(float height, float weight, int avgHeight, int avgWeight)
{
// height is already *100
// weight is already *10
float weightComponent = (height / avgHeight) * weight;
int v14 = (int)(float)((float)((float)(weightComponent + (float)((float)avgWeight * -0.8f)) / (float)((float)avgWeight * 0.4)) * 255.0f);
int v15 = v14 & ~(v14 >> 31);
return (byte)Math.Min(255, v15);
float heightRatio = height / avgHeight;
float weightComponent = heightRatio * weight;
float top = avgWeight * -0.8f;
top += weightComponent;
float bot = avgWeight * 0.4f;
float result = top / bot;
result *= 255f;
int value = (int)result;
int unsigned = value & ~(value >> 31);
return (byte)Math.Min(255, unsigned);
}
}
}