Skip to content

Commit

Permalink
add support for damaging factions as well as properly accounting pena…
Browse files Browse the repository at this point in the history
…lties for expired mission sites
  • Loading branch information
Konrad Jamrozik committed Jun 24, 2024
1 parent 067fe52 commit 8d62652
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 48 deletions.
2 changes: 1 addition & 1 deletion src/game-lib-tests/AIPlayerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void DoNothingAIPlayerIntellectPlaysGameUntilConclusion()

[Test]
public void BasicAIPlayerIntellectPlaysGameUntilConclusion()
=> AIPlayerPlaysGameUntilConclusion(AIPlayer.Intellect.Basic, turnLimit: 300);
=> AIPlayerPlaysGameUntilConclusion(AIPlayer.Intellect.Basic, turnLimit: 100);

[Test]
public void ExampleGameSessionForApi()
Expand Down
13 changes: 6 additions & 7 deletions src/game-lib/Controller/TimeAdvancementController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ public TimeAdvancementController(

(List<Mission> successfulMissions, List<Mission> failedMissions) = EvaluateMissions(state);

// kja need to take penalties from each mission instead of just counting downstream.
int expiredMissionSites = UpdateActiveMissionSites(state);
List<MissionSite> expiredMissionSites = UpdateActiveMissionSites(state);

int moneyChange = Ruleset.ComputeMoneyChange(state.Assets, successfulMissions, agentUpkeep);
int intelChange = Ruleset.ComputeIntelChange(state.Assets, successfulMissions);
Expand Down Expand Up @@ -76,7 +75,7 @@ public TimeAdvancementController(
var worldEvents = new List<WorldEvent>(_worldEvents);
_worldEvents.Clear();

state.Factions.AdvanceTime();
state.Factions.AdvanceTime(successfulMissions);

state.AssertInvariants();
return (advanceTimeEvent, worldEvents);
Expand Down Expand Up @@ -195,16 +194,16 @@ private void UpdateAgentStates(GameState state)
});
}

private int UpdateActiveMissionSites(GameState state)
private List<MissionSite> UpdateActiveMissionSites(GameState state)
{
int expiredMissions = 0;
var expiredMissionSites = new List<MissionSite>();
state.MissionSites.Active.ForEach(
missionSite =>
{
var expired = missionSite.TickExpiration(state.Timeline.CurrentTurn);
if (expired)
{
expiredMissions++;
expiredMissionSites.Add(missionSite);
_worldEvents.Add(
new WorldEvent(
_eventIdGen.Generate,
Expand All @@ -214,7 +213,7 @@ private int UpdateActiveMissionSites(GameState state)
}
}
);
return expiredMissions;
return expiredMissionSites;
}
private void CreateMissionSites(GameState state)
{
Expand Down
6 changes: 3 additions & 3 deletions src/game-lib/Lib/RandomGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ public int Roll(int @base, int min, int max)
public float RollFloat(float min, float max)
{
Contract.Assert(min <= max);
int intMin = (int)min * 1000;
int intMax = (int)max * 1000;
int intMin = (int)(min * 10000);
int intMax = (int)(max * 10000);
int intRoll = Roll(intMin, intMax);
return intRoll / 1000f;
return intRoll / 10000f;
}

public int Roll(Range range)
Expand Down
29 changes: 25 additions & 4 deletions src/game-lib/Model/Faction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ public class Faction : IIdentifiable
public int MissionSiteCountdown;
// kja rename PowerIncrease to PowerClimb
public int PowerIncrease;
public readonly int PowerAcceleration; // More derivatives: https://en.wikipedia.org/wiki/Fourth,_fifth,_and_sixth_derivatives_of_position
public int PowerAcceleration; // More derivatives: https://en.wikipedia.org/wiki/Fourth,_fifth,_and_sixth_derivatives_of_position
public int AccumulatedPowerAcceleration;
// kja implement: the more intel invested, the more faction damage missions are doing
// kja introduce "IntelRuleset", "MoneyRuleset" etc. It will make things easier to balance.
public readonly int IntelInvested;


Expand All @@ -44,6 +46,8 @@ public Faction(
IntelInvested = intelInvested;
}

[JsonIgnore]
public int NormalizedPower => Power / Ruleset.FactionPowerResolution;
public static Faction Init(
IRandomGen randomGen,
int id,
Expand Down Expand Up @@ -80,6 +84,9 @@ public List<MissionSite> CreateMissionSites(
{
Contract.Assert(MissionSiteCountdown >= 1);

if (Power == 0)
return [];

MissionSiteCountdown--;
if (MissionSiteCountdown >= 1)
return [];
Expand All @@ -103,12 +110,12 @@ public List<MissionSite> CreateMissionSites(
List<MissionSite> sites = [];
int siteId = missionSiteIdGen.Generate;
(int difficulty, int baseDifficulty, float variationRoll) =
Ruleset.RollMissionSiteDifficulty(randomGen, Power);
Ruleset.RollMissionSiteDifficulty(randomGen, this);

var site = new MissionSite(
siteId,
this,
MissionSiteModifiers.Compute(randomGen, this),
MissionSiteModifiers.Compute(randomGen, this, difficulty),
difficulty,
turnAppeared: state.Timeline.CurrentTurn,
expiresIn: Ruleset.MissionSiteTurnsUntilExpiration);
Expand All @@ -127,8 +134,20 @@ public List<MissionSite> CreateMissionSites(
return sites;
}

public void AdvanceTime()
public void AdvanceTime(List<Mission> successfulMissions)
{
Power = Math.Max(0, Power - successfulMissions.Sum(mission => mission.Site.Modifiers.PowerDamageReward));
PowerIncrease = Math.Max(
0,
PowerIncrease - successfulMissions.Sum(mission => mission.Site.Modifiers.PowerIncreaseDamageReward));
PowerAcceleration = Math.Max(
0,
PowerAcceleration -
successfulMissions.Sum(mission => mission.Site.Modifiers.PowerAccelerationDamageReward));

if (Power == 0)
return;

int threshold = Ruleset.FactionPowerIncreaseAccumulationThreshold;
Power += PowerIncrease;
AccumulatedPowerAcceleration += PowerAcceleration;
Expand All @@ -137,5 +156,7 @@ public void AdvanceTime()
AccumulatedPowerAcceleration -= threshold;
PowerIncrease += 1;
}


}
}
11 changes: 9 additions & 2 deletions src/game-lib/Model/Factions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ public List<MissionSite> CreateMissionSites(
GameState state)
=> this.SelectMany(faction => faction.CreateMissionSites(log, randomGen, missionSiteIdGen, state)).ToList();

public void AdvanceTime()
=> ForEach(faction => faction.AdvanceTime());
public void AdvanceTime(List<Mission> successfulMissions)
{
ForEach(
faction =>
{
List<Mission> factionSuccessfulMissions = successfulMissions.Where(mission => mission.Site.Faction.Id == faction.Id).ToList();
faction.AdvanceTime(factionSuccessfulMissions);
});
}
}
46 changes: 25 additions & 21 deletions src/game-lib/Model/MissionSiteModifiers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,33 @@ public record MissionSiteModifiers
public readonly int IntelReward;
public readonly int FundingReward;
public readonly int SupportReward;
public readonly int FundingPenalty;
public readonly int SupportPenalty;
public readonly int PowerDamageReward;
public readonly int PowerIncreaseDamageReward;
public readonly int PowerAccelerationDamageReward;
public readonly int FundingPenalty;
public readonly int SupportPenalty;

[JsonConstructor]
public MissionSiteModifiers(
int moneyReward,
int intelReward,
int fundingReward,
int supportReward,
int fundingPenalty,
int supportPenalty,
int powerDamageReward,
int powerIncreaseDamageReward,
int powerAccelerationDamageReward,
int fundingPenalty,
int supportPenalty)
int powerAccelerationDamageReward)
{
MoneyReward = moneyReward;
IntelReward = intelReward;
FundingReward = fundingReward;
SupportReward = supportReward;
FundingPenalty = fundingPenalty;
SupportPenalty = supportPenalty;
PowerDamageReward = powerDamageReward;
PowerIncreaseDamageReward = powerIncreaseDamageReward;
PowerAccelerationDamageReward = powerAccelerationDamageReward;
FundingPenalty = fundingPenalty;
SupportPenalty = supportPenalty;
}

public MissionSiteModifiers(
Expand All @@ -55,17 +55,16 @@ public MissionSiteModifiers(
intelReward ?? 0,
fundingReward ?? 0,
supportReward ?? 0,
fundingPenalty ?? 0,
supportPenalty ?? 0,
powerDamageReward ?? 0,
powerIncreaseDamageReward ?? 0,
powerAccelerationDamageReward ?? 0,
fundingPenalty ?? 0,
supportPenalty ?? 0
)
powerAccelerationDamageReward ?? 0)
{
}

// kja2-refact introduce FactionsRuleset for this and other Faction based rules
public static MissionSiteModifiers Compute(IRandomGen randomGen, Faction faction)
// kja-refact introduce FactionsRuleset for this and other Faction based rules
public static MissionSiteModifiers Compute(IRandomGen randomGen, Faction faction, int difficulty)
{
// kja2-feat these formulas should depend on factions.
// E.g.:
Expand All @@ -77,32 +76,37 @@ public static MissionSiteModifiers Compute(IRandomGen randomGen, Faction faction
// - much less funding
// - and support rewards and penalties are amplified

int baseMoneyReward = faction.Power / Ruleset.FactionPowerResolution;
int baseMoneyReward = faction.NormalizedPower / 10;
(int moneyReward, _) = randomGen.RollVariation(baseMoneyReward, -50, 50, 100);

int baseIntelReward = faction.Power / Ruleset.FactionPowerResolution;
int baseIntelReward = faction.NormalizedPower / 10;
(int intelReward, _) = randomGen.RollVariation(baseIntelReward, -50, 50, 100);

int baseFundingReward = 5 + faction.Power / 10 / Ruleset.FactionPowerResolution;
int baseFundingReward = 5 + faction.NormalizedPower / 10;
(int fundingReward, _) = randomGen.RollVariation(baseFundingReward, -50, 50, 100);

int baseFundingPenalty = 1 + faction.Power / 10 / Ruleset.FactionPowerResolution;
int baseFundingPenalty = 1 + faction.NormalizedPower / 10;
(int fundingPenalty, _) = randomGen.RollVariation(baseFundingPenalty, -50, 50, 100);

int baseSupportReward = 20 + faction.Power / 10 / Ruleset.FactionPowerResolution;
int baseSupportReward = 20 + faction.NormalizedPower / 10;
(int supportReward, _) = randomGen.RollVariation(baseSupportReward, -50, 50, 100);

int baseSupportPenalty = 20 + faction.Power / 10 / Ruleset.FactionPowerResolution;
int baseSupportPenalty = 20 + faction.NormalizedPower / 10;
(int supportPenalty, _) = randomGen.RollVariation(baseSupportPenalty, -50, 50, 100);

int basePowerDamageReward = 20 + faction.Power / 10;
(int powerDamageReward, _) = randomGen.RollVariation(basePowerDamageReward, -20, 20, 100);

return new MissionSiteModifiers(
moneyReward: moneyReward,
intelReward: intelReward,
fundingReward: fundingReward,
supportReward: supportReward,
fundingPenalty: fundingPenalty,
supportPenalty: supportPenalty
);
supportPenalty: supportPenalty,
powerDamageReward: powerDamageReward,
powerIncreaseDamageReward: 0,
powerAccelerationDamageReward: 0);
}

public MissionSiteModifiers DeepClone()
Expand Down
25 changes: 17 additions & 8 deletions src/game-lib/Model/Ruleset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ public static class Ruleset

// 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 InitialFactions(IRandomGen randomGen) => new(
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 powerIncrease (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.
Faction.Init(randomGen, id: 0, "Black Lotus cult", power: 200, powerIncrease: 4, powerAcceleration: 8),
Faction.Init(randomGen, id: 1, "Red Dawn remnants", power: 300, powerIncrease: 5, powerAcceleration: 5),
Faction.Init(randomGen, id: 2, "EXALT", power: 400, powerIncrease: 6, powerAcceleration: 4),
Expand Down Expand Up @@ -76,15 +79,15 @@ private static int SkillFromMissions(Agent agent)

public static (int difficulty, int baseDifficulty, float variationRoll) RollMissionSiteDifficulty(
IRandomGen randomGen,
int factionPower)
Faction faction)
{
// Note that currently the only ways of increasing agents survivability of difficulty is:
// - by surviving missions
// - via training
// 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 = factionPower / FactionPowerResolution;
int baseDifficulty = faction.NormalizedPower;
(int difficulty, float variationRoll) = randomGen.RollVariation(
baseValue: baseDifficulty,
range: MissionSiteDifficultyVariationRange,
Expand All @@ -99,15 +102,21 @@ public static int RequiredSurvivingAgentsForSuccess(MissionSite site)
return reqAgentsForSuccess;
}

public static int ComputeFundingChange(List<Mission> successfulMissions, List<Mission> failedMissions, int expiredMissionSites)
public static int ComputeFundingChange(
List<Mission> successfulMissions,
List<Mission> failedMissions,
List<MissionSite> expiredMissionSites)
=> successfulMissions.Sum(mission => mission.Site.Modifiers.FundingReward)
- failedMissions.Sum(mission => mission.Site.Modifiers.FundingPenalty)
- expiredMissionSites * 1; // kja dedup this const with MissionSiteModifiers into Ruleset
- failedMissions.Sum(mission => mission.Site.Modifiers.FundingPenalty)
- expiredMissionSites.Sum(site => site.Modifiers.FundingPenalty);

public static int ComputeSupportChange(List<Mission> successfulMissions, List<Mission> failedMissions, int expiredMissionSites)
public static int ComputeSupportChange(
List<Mission> successfulMissions,
List<Mission> failedMissions,
List<MissionSite> expiredMissionSites)
=> successfulMissions.Sum(mission => mission.Site.Modifiers.SupportReward)
- failedMissions.Sum(mission => mission.Site.Modifiers.SupportPenalty)
- expiredMissionSites * 1; // kja dedup this const with MissionSiteModifiers into Ruleset
- expiredMissionSites.Sum(site => site.Modifiers.SupportPenalty);

public static int ComputeMoneyChange(Assets assets, List<Mission> successfulMissions, int agentUpkeep)
{
Expand Down
2 changes: 1 addition & 1 deletion src/game-lib/State/GameState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public static GameState NewInitialGameState(IRandomGen? randomGen = null, Factio
new MissionSites(),
new Missions(),
terminatedAgents: new Agents(terminated: true),
factions: factions ?? Ruleset.InitialFactions(randomGen));
factions: factions ?? Ruleset.InitFactions(randomGen));
}

public void AssertInvariants()
Expand Down
2 changes: 1 addition & 1 deletion web/src/lib/rendering/renderGameEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,6 @@ function formatString(
}

function logIds(ids: number[]): string {
// The "[...id]: spread here is used to avoid mutating the "ids" array."
// The "[...id]" spread here is used to avoid mutating the "ids" array.
return str([...ids].sort((left, right) => left - right))
}

0 comments on commit 8d62652

Please sign in to comment.