Skip to content

Commit

Permalink
use doubles for precise and less confusing computations
Browse files Browse the repository at this point in the history
  • Loading branch information
Konrad Jamrozik committed Jun 29, 2024
1 parent 2807f09 commit 3161567
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 65 deletions.
15 changes: 12 additions & 3 deletions src/game-lib/Lib/IRandomGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,25 @@ public interface IRandomGen
int Roll(int min, int max);
int Roll((int min, int max) range);
int Roll(int @base, int min, int max);
float RollFloat(float min, float max);
public double RollDouble(double min, double max);
int Roll(Range range);
T Pick<T>(List<T> items);
List<T> Pick<T>(List<T> items, int count);
TValue Pick<TKey, TValue>(IDictionary<TKey, TValue> dict);
int RandomizeMissionSiteCountdown();
bool FlipCoin();

(int result, float variationRoll) RollVariation(int baseValue, (int min, int max) range, int precision);
public (int result, double variationRoll) RollVariationAndRound(double baseValue, (double min, double max) range);

(int result, float variationRoll) RollVariation(int baseValue, int min, int max, int precision);
public (double result, double variationRoll) RollVariation(double baseValue, (double min, double max) range);

public (double result, double variationRoll) RollVariation(double baseValue, double min, double max);




(int result, float variationRoll) RollVariationInt(int baseValue, (int min, int max) range, int precision);

(int result, float variationRoll) RollVariationInt(int baseValue, int min, int max, int precision);

}
27 changes: 22 additions & 5 deletions src/game-lib/Lib/RandomGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ public int Roll(int @base, int min, int max)
return @base + Roll(min, max);
}

public float RollFloat(float min, float max)
public double RollDouble(double min, double max)
{
Contract.Assert(min <= max);
int intMin = (int)(min * 10000);
int intMax = (int)(max * 10000);
int intRoll = Roll(intMin, intMax);
return intRoll / 10000f;
return intRoll / 10000d;
}

public int Roll(Range range)
Expand All @@ -77,10 +77,10 @@ public virtual int RandomizeMissionSiteCountdown()

public bool FlipCoin() => _random.Next(2) == 1;

public (int result, float variationRoll) RollVariation(int baseValue, (int min, int max) range, int precision)
=> RollVariation(baseValue, range.min, range.max, precision);
public (int result, float variationRoll) RollVariationInt(int baseValue, (int min, int max) range, int precision)
=> RollVariationInt(baseValue, range.min, range.max, precision);

public (int result, float variationRoll) RollVariation(int baseValue, int min, int max, int precision)
public (int result, float variationRoll) RollVariationInt(int baseValue, int min, int max, int precision)
{
// e.g. -15 = Roll(-30, 30)
int variationRoll = Roll(min, max);
Expand All @@ -90,4 +90,21 @@ public virtual int RandomizeMissionSiteCountdown()
int result = baseValue * modifier / precision;
return (result, variationRoll: variationRoll / (float)precision);
}

public (int result, double variationRoll) RollVariationAndRound(double baseValue, (double min, double max) range)
{
(double result, double variationRoll) = RollVariation(baseValue, range);
return (result: (int)Math.Round(result), variationRoll);
}

public (double result, double variationRoll) RollVariation(double baseValue, (double min, double max) range)
=> RollVariation(baseValue, range.min, range.max);

public (double result, double variationRoll) RollVariation(double baseValue, double min, double max)
{
double variationRoll = RollDouble(min, max);
double modifier = 1 + variationRoll;
double result = baseValue * modifier;
return (result, variationRoll);
}
}
49 changes: 23 additions & 26 deletions src/game-lib/Model/Faction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@ public class Faction : IIdentifiable
{
public int Id { get; }
public readonly string Name;
public int Power;
public double Power;
/// <summary>
/// The number of times the time must be advanced for a new mission site to be generated.
/// When it is 1 and the time is advanced, a new mission site is generated and the countdown is reset.
/// </summary>
public int MissionSiteCountdown;
public int PowerClimb;
public int PowerAcceleration; // More derivatives: https://en.wikipedia.org/wiki/Fourth,_fifth,_and_sixth_derivatives_of_position
public int AccumulatedPowerAcceleration;
public double PowerClimb;
public double PowerAcceleration; // More derivatives: https://en.wikipedia.org/wiki/Fourth,_fifth,_and_sixth_derivatives_of_position
// kja implement: the more intel invested, the more faction damage missions are doing
// kja-refactor introduce "IntelRuleset", "MoneyRuleset" etc. It will make things easier to balance.
public int IntelInvested;
Expand All @@ -28,11 +27,10 @@ public class Faction : IIdentifiable
public Faction(
int id,
string name,
int power,
double power,
int missionSiteCountdown,
int powerClimb,
int powerAcceleration,
int accumulatedPowerAcceleration,
double powerClimb,
double powerAcceleration,
int intelInvested)
{
Id = id;
Expand All @@ -41,27 +39,26 @@ public Faction(
MissionSiteCountdown = missionSiteCountdown;
PowerClimb = powerClimb;
PowerAcceleration = powerAcceleration;
AccumulatedPowerAcceleration = accumulatedPowerAcceleration;
IntelInvested = intelInvested;
}

[JsonIgnore]
public int NormalizedPower => Power / Ruleset.FactionPowerResolution;
public int PowerAsInt => (int)Math.Floor(Power);

public static Faction Init(
IRandomGen randomGen,
int id,
string name,
int power,
int? powerClimb = null,
int? powerAcceleration = null)
double power,
double? powerClimb = null,
double? powerAcceleration = null)
=> new(
id,
name,
power,
randomGen.RandomizeMissionSiteCountdown(),
powerClimb ?? 0,
powerAcceleration ?? 0,
accumulatedPowerAcceleration: 0,
intelInvested: 0);

public Faction DeepClone()
Expand All @@ -72,7 +69,6 @@ public Faction DeepClone()
MissionSiteCountdown,
PowerClimb,
PowerAcceleration,
AccumulatedPowerAcceleration,
IntelInvested);

public List<MissionSite> CreateMissionSites(
Expand Down Expand Up @@ -108,7 +104,7 @@ public List<MissionSite> CreateMissionSites(
// make AI player send less experienced soldiers on it.
List<MissionSite> sites = [];
int siteId = missionSiteIdGen.Generate;
(int difficulty, int baseDifficulty, float variationRoll) =
(int difficulty, double baseDifficulty, double variationRoll) =
Ruleset.RollMissionSiteDifficulty(randomGen, this);

var site = new MissionSite(
Expand All @@ -123,11 +119,18 @@ public List<MissionSite> CreateMissionSites(
// returning a list.
sites.Add(site);

Console.WriteLine(
"kja temp debug " +
$"Add {site.LogString} : " +
$"Faction: {Name,20}, " +
$"difficulty: {difficulty,3}, " +
$"baseDifficulty: {baseDifficulty,5:F2}, " +
$"variationRoll: {variationRoll,5:F2}.");
log.Info(
$"Add {site.LogString} : " +
$"Faction: {Name,20}, " +
$"difficulty: {difficulty,3}, " +
$"baseDifficulty: {baseDifficulty,3}, " +
$"baseDifficulty: {baseDifficulty,5:F2}, " +
$"variationRoll: {variationRoll,5:F2}.");

return sites;
Expand All @@ -136,9 +139,11 @@ public List<MissionSite> CreateMissionSites(
public void AdvanceTime(List<Mission> successfulMissions)
{
Power = Math.Max(0, Power - successfulMissions.Sum(mission => mission.Site.Modifiers.PowerDamageReward));

PowerClimb = Math.Max(
0,
PowerClimb - successfulMissions.Sum(mission => mission.Site.Modifiers.PowerClimbDamageReward));

PowerAcceleration = Math.Max(
0,
PowerAcceleration -
Expand All @@ -147,15 +152,7 @@ public void AdvanceTime(List<Mission> successfulMissions)
if (Power == 0)
return;

int threshold = Ruleset.FactionPowerClimbAccumulationThreshold;
Power += PowerClimb;
AccumulatedPowerAcceleration += PowerAcceleration;
while (AccumulatedPowerAcceleration >= threshold)
{
AccumulatedPowerAcceleration -= threshold;
PowerClimb += 1;
}


PowerClimb += PowerAcceleration;
}
}
28 changes: 14 additions & 14 deletions src/game-lib/Model/MissionSiteModifiers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,26 +76,26 @@ public static MissionSiteModifiers Compute(IRandomGen randomGen, Faction faction
// - much less funding
// - and support rewards and penalties are amplified

int baseMoneyReward = faction.NormalizedPower / 10;
(int moneyReward, _) = randomGen.RollVariation(baseMoneyReward, -50, 50, 100);
double baseMoneyReward = faction.Power / 10;
(int moneyReward, _) = randomGen.RollVariationAndRound(baseMoneyReward, (min: -0.5, max: 0.5));

int baseIntelReward = faction.NormalizedPower / 10;
(int intelReward, _) = randomGen.RollVariation(baseIntelReward, -50, 50, 100);
double baseIntelReward = faction.Power / 10;
(int intelReward, _) = randomGen.RollVariationAndRound(baseIntelReward, (min: -0.5, max: 0.5));

int baseFundingReward = 5 + faction.NormalizedPower / 10;
(int fundingReward, _) = randomGen.RollVariation(baseFundingReward, -50, 50, 100);
double baseFundingReward = 5 + faction.Power / 10;
(int fundingReward, _) = randomGen.RollVariationAndRound(baseFundingReward, (min: -0.5, max: 0.5));

int baseFundingPenalty = 1 + faction.NormalizedPower / 10;
(int fundingPenalty, _) = randomGen.RollVariation(baseFundingPenalty, -50, 50, 100);
double baseFundingPenalty = 1 + faction.Power / 10;
(int fundingPenalty, _) = randomGen.RollVariationAndRound(baseFundingPenalty, (min: -0.5, max: 0.5));

int baseSupportReward = 20 + faction.NormalizedPower / 10;
(int supportReward, _) = randomGen.RollVariation(baseSupportReward, -50, 50, 100);
double baseSupportReward = 20 + faction.Power / 10;
(int supportReward, _) = randomGen.RollVariationAndRound(baseSupportReward, (min: -0.5, max: 0.5));

int baseSupportPenalty = 20 + faction.NormalizedPower / 10;
(int supportPenalty, _) = randomGen.RollVariation(baseSupportPenalty, -50, 50, 100);
double baseSupportPenalty = 20 + faction.Power / 10;
(int supportPenalty, _) = randomGen.RollVariationAndRound(baseSupportPenalty, (min: -0.5, max: 0.5));

int basePowerDamageReward = 20 + faction.Power / 10;
(int powerDamageReward, _) = randomGen.RollVariation(basePowerDamageReward, -20, 20, 100);
double basePowerDamageReward = 20 + faction.Power / 10;
(int powerDamageReward, _) = randomGen.RollVariationAndRound(basePowerDamageReward, (min: -0.2, max: 0.2));

return new MissionSiteModifiers(
moneyReward: moneyReward,
Expand Down
26 changes: 9 additions & 17 deletions src/game-lib/Model/Ruleset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,16 @@ public static class Ruleset
public const int InitialSupport = 30;
public const int InitialMaxTransportCapacity = 4;

public const int FactionPowerResolution = 10;
public const int FactionPowerClimbAccumulationThreshold = 100;

// For more example factions data, see:
// https://github.com/konrad-jamrozik/game/blob/eccb44a1d5f074e95b07aebca2c6bc5bbfdfdda8/src/ufo-game/Model/Data/FactionsData.cs#L34
public static Factions InitFactions(IRandomGen randomGen) => new(
[
// Note: need to ensure here that IDs are consecutive, and from zero.
// kja-refact: Power should be "actual" value, not with the precision. So 40 instead of 400. Then powerClimb (to be renamed to powerClimb)
// would be 0.4 and powerAcceleration would be 0.008. This would make it easier to reason about the values.
// Just bite the bullet and use doubles.
// kja also, frontend diagram chart shows the increased precision (e.g. 100) while datagrid shows normalized (e.g. 10).
Faction.Init(randomGen, id: 0, "Black Lotus cult" , power: 200, powerClimb: 4, powerAcceleration: 8 ),
Faction.Init(randomGen, id: 1, "Red Dawn remnants" , power: 300, powerClimb: 5, powerAcceleration: 5 ),
Faction.Init(randomGen, id: 2, "EXALT" , power: 400, powerClimb: 6, powerAcceleration: 4 ),
Faction.Init(randomGen, id: 3, "Zombies" , power: 100, powerClimb: 1, powerAcceleration: 20 )
Faction.Init(randomGen, id: 0, "Black Lotus cult" , power: 20, powerClimb: 0.4, powerAcceleration: 0.008 ),
Faction.Init(randomGen, id: 1, "Red Dawn remnants" , power: 30, powerClimb: 0.5, powerAcceleration: 0.005 ),
Faction.Init(randomGen, id: 2, "EXALT" , power: 40, powerClimb: 0.6, powerAcceleration: 0.004 ),
Faction.Init(randomGen, id: 3, "Zombies" , power: 10, powerClimb: 0.1, powerAcceleration: 0.02 )
]);

public const int IntelToWin = 3000;
Expand All @@ -39,8 +33,7 @@ public static class Ruleset

public const int MissionSiteSurvivalBaseDifficultyRequirement = 30;
public static readonly Range FactionMissionSiteCountdownRange = new Range(3, 10);
public const int MissionSiteDifficultyRollResolution = 100;
public static readonly (int min, int max) MissionSiteDifficultyVariationRange = (min: -30, max: 30);
public static readonly (double min, double max) MissionSiteDifficultyVariationRange = (min: -0.3, max: 0.3);
public const int MissionSiteTurnsUntilExpiration = 3;

public static (bool survived, int? recoversIn) RollForAgentSurvival(
Expand Down Expand Up @@ -78,7 +71,7 @@ private static int SkillFromMissions(Agent agent)
return skillFromFirstMissions + missionsBeyondFirstMissions * SkillFromEachMissionBeyondFirstMissions;
}

public static (int difficulty, int baseDifficulty, float variationRoll) RollMissionSiteDifficulty(
public static (int difficulty, double baseDifficulty, double variationRoll) RollMissionSiteDifficulty(
IRandomGen randomGen,
Faction faction)
{
Expand All @@ -88,11 +81,10 @@ public static (int difficulty, int baseDifficulty, float variationRoll) RollMiss
// As such, if difficulty per turn would grow at least as fast as Ruleset.AgentTrainingCoefficient,
// then at some point missions would become impossible, as eventually even the most experienced
// agents would die, and any new agents would never be able to catch up with mission difficulty.
int baseDifficulty = faction.NormalizedPower;
(int difficulty, float variationRoll) = randomGen.RollVariation(
double baseDifficulty = faction.Power;
(int difficulty, double variationRoll) = randomGen.RollVariationAndRound(
baseValue: baseDifficulty,
range: MissionSiteDifficultyVariationRange,
precision: MissionSiteDifficultyRollResolution);
MissionSiteDifficultyVariationRange);
return (difficulty, baseDifficulty, variationRoll);
}

Expand Down

0 comments on commit 3161567

Please sign in to comment.