Skip to content

Commit

Permalink
WIP: new mission site creation logic, dependend on factions. WIP beca…
Browse files Browse the repository at this point in the history
…use need to iron out some known bugs.
  • Loading branch information
Konrad Jamrozik committed Jun 13, 2024
1 parent f96fec9 commit d421673
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 34 deletions.
1 change: 1 addition & 0 deletions src/game-lib-tests/GameSessionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public void BasicHappyPathGameSessionWorks()
turnController.HireAgents(count: 3);
controller.AdvanceTime();
controller.AdvanceTime();
// kja BUG ROOT CAUSE: getting no elems here because now site generation is randomized. Need to override it for testing.
MissionSite site = controller.CurrentGameStatePlayerView.MissionSites.First();
turnController.LaunchMission(site, agentCount: 3);
controller.AdvanceTime();
Expand Down
20 changes: 3 additions & 17 deletions src/game-lib/Controller/TimeAdvancementController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public TimeAdvancementController(ILog log, RandomGen randomGen, EventIdGen event

int expiredMissionSites = UpdateActiveMissionSites(state);

// kja funding / support change must be expanded to take into account variable mission rewards:
// kja-wishlist funding / support change must be expanded to take into account variable mission rewards:
// Each mission may return different amount of funding, support, money and intel.
int fundingChange = Ruleset.ComputeFundingChange(successfulMissions, failedMissions, expiredMissionSites);
int supportChange = Ruleset.ComputeSupportChange(successfulMissions, failedMissions, expiredMissionSites);
Expand Down Expand Up @@ -209,23 +209,9 @@ private int UpdateActiveMissionSites(GameState state)
);
return expiredMissions;
}

// kja2-simul-feat to make simulation more interesting: create easier missions from time to time and
// make AI player send less experienced soldiers on it.
private void CreateMissionSites(GameState state)
{
if (state.Timeline.CurrentTurn % 3 == 0)
{
int siteId = state.NextMissionSiteId;
(int difficulty, int difficultyFromTurn, int roll) =
Ruleset.RollMissionSiteDifficulty(state.Timeline.CurrentTurn, _randomGen);
// kja inject appropriate faction instead of state.Faction[0]
var site = new MissionSite(siteId, state.Factions[0], difficulty, turnAppeared: state.Timeline.CurrentTurn, expiresIn: 3);
state.MissionSites.Add(site);
_log.Info($"Add {site.LogString} : " +
$"difficulty: {difficulty,3}, " +
$"difficultyFromTurn: {difficultyFromTurn,3}, " +
$"difficultyRoll: {roll,2}.");
}
List<MissionSite> missionSites = state.Factions.CreateMissionSites(_log, _randomGen, state);
state.MissionSites.AddRange(missionSites);
}
}
2 changes: 1 addition & 1 deletion src/game-lib/Model/Agent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace UfoGameLib.Model;

// kja I think that yes, it should be IIdentifiable. lib\Json\JsonConverterSupportingReferences.cs:86
// kja-wishlist I think that yes, it should be IIdentifiable. lib\Json\JsonConverterSupportingReferences.cs:86
// should this implement IIdentifiable the same as MissionSite?
// I think currently it doesn't because Agents are never serialized by reference
// by GameStateJsonConverter, so it doesn't matter if they implement IIdentifiable
Expand Down
75 changes: 72 additions & 3 deletions src/game-lib/Model/Faction.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,100 @@
using System.Text.Json.Serialization;
using Lib.Contracts;
using Lib.Json;
using UfoGameLib.Lib;
using UfoGameLib.State;

namespace UfoGameLib.Model;

// kja integrate Faction class with the rest of codebase
public class Faction : IIdentifiable
{
public int Id { get; }
public readonly string Name;
public readonly int Power;
public int MissionSiteCountdown;
public readonly int PowerIncrease;
public readonly int PowerAcceleration;
public readonly int IntelInvested;


[JsonConstructor]
public Faction(int id, string name, int power, int powerIncrease, int powerAcceleration, int intelInvested)
public Faction(
int id,
string name,
int power,
int missionSiteCountdown,
int powerIncrease,
int powerAcceleration,
int intelInvested)
{
Id = id;
Name = name;
Power = power;
MissionSiteCountdown = missionSiteCountdown;
PowerIncrease = powerIncrease;
PowerAcceleration = powerAcceleration;
IntelInvested = intelInvested;
}

public static Faction Init(
RandomGen randomGen,
int id,
string name,
int power,
int? powerIncrease = null,
int? powerAcceleration = null)
=> new(
id,
name,
power,
RandomizeMissionSiteCountdown(randomGen),
powerIncrease ?? 0,
powerAcceleration ?? 0,
0);

public Faction DeepClone()
=> new(Id, Name, Power, MissionSiteCountdown, PowerIncrease, PowerAcceleration, IntelInvested);

public List<MissionSite> CreateMissionSites(ILog log, RandomGen randomGen, GameState state)
{
return new Faction(Id, Name, Power, PowerIncrease, PowerAcceleration, IntelInvested);
Contract.Assert(MissionSiteCountdown >= 1);

MissionSiteCountdown--;
if (MissionSiteCountdown > 0)
return [];

Contract.Assert(MissionSiteCountdown == 0);
MissionSiteCountdown = RandomizeMissionSiteCountdown(randomGen);

// kja2-simul-feat to make simulation more interesting: create easier missions from time to time and
// make AI player send less experienced soldiers on it.
List<MissionSite> sites = [];
// kja BUG ROOT CAUSE: this must increase the ID, right now it does not.
int siteId = state.NextMissionSiteId;
(int difficulty, int difficultyFromTurn, int roll) =
Ruleset.RollMissionSiteDifficulty(state.Timeline.CurrentTurn, randomGen);

var site = new MissionSite(
siteId,
this,
difficulty,
turnAppeared: state.Timeline.CurrentTurn,
expiresIn: Ruleset.MissionSiteTurnsUntilExpiration);

// Note: currently returning only one mission site even though this method supports
// returning a list.
sites.Add(site);

log.Info(
$"Add {site.LogString} : " +
$"Faction: {Name}, " +
$"difficulty: {difficulty,3}, " +
$"difficultyFromTurn: {difficultyFromTurn,3}, " +
$"difficultyRoll: {roll,2}.");

return sites;
}

private static int RandomizeMissionSiteCountdown(RandomGen randomGen)
=> randomGen.Roll(Ruleset.FactionMissionSiteCountdown);
}
5 changes: 5 additions & 0 deletions src/game-lib/Model/Factions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Text.Json.Serialization;
using UfoGameLib.Lib;
using UfoGameLib.State;

namespace UfoGameLib.Model;

Expand All @@ -14,4 +16,7 @@ public Factions(IEnumerable<Faction>? factions = null)

public Factions DeepClone()
=> new Factions(this.Select(faction => faction.DeepClone()));

public List<MissionSite> CreateMissionSites(ILog log, RandomGen randomGen, GameState state)
=> this.SelectMany(faction => faction.CreateMissionSites(log, randomGen, state)).ToList();
}
7 changes: 4 additions & 3 deletions src/game-lib/Model/MissionSite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace UfoGameLib.Model;

// kja add new usage of intel:
// kja-wishlist add new usage of intel:
// - Investing intel into faction will cause missions from this faction to be generated with better modifiers.

// kja add new win criterion:
// kja-wishlist add new win criterion:
// - reduce all factions power to 0.

public class MissionSite : IIdentifiable
Expand All @@ -22,7 +22,7 @@ public class MissionSite : IIdentifiable
public bool Expired { get; private set; }
public int? ExpiresIn { get; private set; }

// kja Changes to MissionSite to make:
// kja-wishlist Changes to MissionSite to make:
// - add a Faction reference, serialize it properly via ref
// - add props: DifficultyModifier, RewardAndPenaltyModifiers (money, intel, funding, support),
// FactionDamageModifier (power, power increase, power acceleration)
Expand Down Expand Up @@ -50,6 +50,7 @@ public MissionSite(
AssertStatusInvariant();
}

// kja shouldn't it be ExpiresIn >= 1 instead of ExpiresIn >= 0 ?
[JsonIgnore]
public bool IsActive => TurnDeactivated == null && !Expired && ExpiresIn >= 0;

Expand Down
3 changes: 3 additions & 0 deletions src/game-lib/Model/Missions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public Missions DeepClone(MissionSites clonedMissionSites)
{
return new Missions(this.Select(mission =>
{
// kja getting "more than one elem" here on BasicAIPlayer plays game test.
// This denotes issue with busted site IDs with the new mission generation logic
// Root cause identified: see UfoGameLib.Model.Faction.CreateMissionSites
MissionSite clonedMissionSite = clonedMissionSites.Single(clonedSite => clonedSite.Id == mission.Site.Id);
return mission.DeepClone(clonedMissionSite);
}));
Expand Down
16 changes: 10 additions & 6 deletions src/game-lib/Model/Ruleset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

namespace UfoGameLib.Model;

// kja convert "Ruleset" class into a namespace. Maybe introduce IRuleset "marker" interface?
public static class Ruleset
{
public const int InitialMoney = 500;
Expand All @@ -13,12 +14,13 @@ 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 => new(
public static Factions InitialFactions(RandomGen randomGen) => new(
[
new Faction(0, "Black Lotus cult", 200, 5, 0, 0),
new Faction(1, "Red Dawn remnants", 300, 5, 0, 0),
new Faction(2, "EXALT", 400, 6, 0, 0),
new Faction(3, "Zombies", 1200, 1, 0, 0)
// Note: need to ensure here that IDs are consecutive, and from zero.
Faction.Init(randomGen, id: 0, "Black Lotus cult", power: 200, powerIncrease: 5),
Faction.Init(randomGen, id: 1, "Red Dawn remnants", power: 300, powerIncrease: 5),
Faction.Init(randomGen, id: 2, "EXALT", power: 400, powerIncrease: 6),
Faction.Init(randomGen, id: 3, "Zombies", power: 1200, powerIncrease: 1)
]);

public const int IntelToWin = 3000;
Expand All @@ -29,6 +31,8 @@ public static class Ruleset
public const int AgentBaseSurvivalSkill = 100;

public const int BaseMissionSiteDifficulty = 30;
public static readonly Range FactionMissionSiteCountdown = new Range(3, 10);
public const int MissionSiteTurnsUntilExpiration = 3;

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

// kja instead of RollMissionSiteDifficulty, now we will be rolling various MissionSite coefficients
// kja-wishlist instead of RollMissionSiteDifficulty, now we will be rolling various MissionSite coefficients
// based on faction data and possibly other factors. And faction will have power correlating with turn.
public static (int difficulty, int difficultyFromTurn, int roll) RollMissionSiteDifficulty(
int currentTurn,
Expand Down
2 changes: 1 addition & 1 deletion src/game-lib/State/GameSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class GameSession
public GameSession(RandomGen randomGen, List<GameSessionTurn>? turns = null)
{
RandomGen = randomGen;
Turns = turns ?? [new GameSessionTurn(startState: GameState.NewInitialGameState())];
Turns = turns ?? [new GameSessionTurn(startState: GameState.NewInitialGameState(randomGen))];
Contract.Assert(Turns.Count >= 1);
Turns.ForEach(turn => turn.AssertInvariants());
EventIdGen = new EventIdGen(Turns);
Expand Down
1 change: 1 addition & 0 deletions src/game-lib/State/GameSessionTurn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public void AssertInvariants()
EventsInTurn.Count == EndState.UpdateCount - StartState.UpdateCount,
"Number of events in turn must match the number of updates between the game states.");

// kja extract this into "ConsecutiveId" abstraction and invariant.
IReadOnlyList<GameEvent> events = GameEvents;
if (events.Any())
{
Expand Down
10 changes: 7 additions & 3 deletions src/game-lib/State/GameState.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Lib.Json;
using UfoGameLib.Lib;
using UfoGameLib.Model;
using File = Lib.OS.File;

Expand Down Expand Up @@ -39,8 +40,10 @@ public GameState(
Factions = factions;
}

public static GameState NewInitialGameState()
=> new GameState(
public static GameState NewInitialGameState(RandomGen? randomGen = null)
{
randomGen ??= new RandomGen(new Random());
return new GameState(
updateCount: 0,
new Timeline(currentTurn: Timeline.InitialTurn),
new Assets(
Expand All @@ -53,7 +56,8 @@ public static GameState NewInitialGameState()
new MissionSites(),
new Missions(),
terminatedAgents: new Agents(terminated: true),
factions: Ruleset.InitialFactions);
factions: Ruleset.InitialFactions(randomGen));
}

public static GameState FromJsonFile(File file)
=> file.ReadJsonInto<GameState>(StateJsonSerializerOptions);
Expand Down

0 comments on commit d421673

Please sign in to comment.