diff --git a/src/game-lib/Controller/GameSessionController.cs b/src/game-lib/Controller/GameSessionController.cs index 5dd17a9b..978938c3 100644 --- a/src/game-lib/Controller/GameSessionController.cs +++ b/src/game-lib/Controller/GameSessionController.cs @@ -55,11 +55,14 @@ public GameSessionController(Configuration config, ILog log, GameSession gameSes _log, GameSession.RandomGen, GameSession.EventIdGen, + GameSession.AgentIdGen, + GameSession.MissionIdGen, () => GameSession.CurrentGameState); _timeAdvancementController = new TimeAdvancementController( _log, GameSession.RandomGen, - GameSession.EventIdGen); + GameSession.EventIdGen, + GameSession.MissionSiteIdGen); } public GameStatePlayerView CurrentGameStatePlayerView diff --git a/src/game-lib/Controller/GameTurnController.cs b/src/game-lib/Controller/GameTurnController.cs index 4ff7f28d..9dc8f3c7 100644 --- a/src/game-lib/Controller/GameTurnController.cs +++ b/src/game-lib/Controller/GameTurnController.cs @@ -9,20 +9,30 @@ namespace UfoGameLib.Controller; public class GameTurnController { private readonly ILog _log; + public RandomGen RandomGen { get; } + private readonly EventIdGen _eventIdGen; + private readonly AgentIdGen _agentIdGen; + private readonly MissionIdGen _missionIdGen; private readonly Func _gameState; private GameState GameState => _gameState(); - private readonly EventIdGen _eventIdGen; - private readonly List _recordedPlayerActionEvents = new(); - - public GameTurnController(ILog log, RandomGen randomGen, EventIdGen eventIdGen, Func gameState) + private readonly List _recordedPlayerActionEvents = []; + + public GameTurnController( + ILog log, + RandomGen randomGen, + EventIdGen eventIdGen, + AgentIdGen agentIdGen, + MissionIdGen missionIdGen, + Func gameState) { _log = log; RandomGen = randomGen; - _gameState = gameState; _eventIdGen = eventIdGen; + _agentIdGen = agentIdGen; + _missionIdGen = missionIdGen; + _gameState = gameState; } - public RandomGen RandomGen { get; } public int CurrentTurn => GameState.Timeline.CurrentTurn; @@ -60,7 +70,7 @@ public void LaunchMission(MissionSite site, int agentCount) } public PlayerActionEvent HireAgents(int count) - => ExecuteAndRecordAction(new HireAgentsPlayerAction(_log, count)); + => ExecuteAndRecordAction(new HireAgentsPlayerAction(_log, _agentIdGen, count)); public PlayerActionEvent BuyTransportCapacity(int capacity) => ExecuteAndRecordAction(new BuyTransportCapacityPlayerAction(_log, capacity)); @@ -81,7 +91,7 @@ public PlayerActionEvent RecallAgents(Agents agents) => ExecuteAndRecordAction(new RecallAgentsPlayerAction(_log, agents)); public PlayerActionEvent LaunchMission(MissionSite site, Agents agents) - => ExecuteAndRecordAction(new LaunchMissionPlayerAction(_log, site, agents)); + => ExecuteAndRecordAction(new LaunchMissionPlayerAction(_log, _missionIdGen, site, agents)); public List GetAndDeleteRecordedPlayerActionEvents() { diff --git a/src/game-lib/Controller/HireAgentsPlayerAction.cs b/src/game-lib/Controller/HireAgentsPlayerAction.cs index 674e7e86..41d17760 100644 --- a/src/game-lib/Controller/HireAgentsPlayerAction.cs +++ b/src/game-lib/Controller/HireAgentsPlayerAction.cs @@ -1,4 +1,5 @@ using Lib.Contracts; +using UfoGameLib.Events; using UfoGameLib.Lib; using UfoGameLib.Model; using UfoGameLib.State; @@ -8,12 +9,14 @@ namespace UfoGameLib.Controller; public class HireAgentsPlayerAction : PlayerAction { private readonly ILog _log; + private readonly AgentIdGen _agentIdGen; private readonly int _count; - public HireAgentsPlayerAction(ILog log, int count) + public HireAgentsPlayerAction(ILog log, AgentIdGen agentIdGen, int count) { Contract.Assert(count >= 1); _log = log; + _agentIdGen = agentIdGen; _count = count; } @@ -26,7 +29,7 @@ protected override (List? ids, int? targetId) ApplyImpl(GameState state) state.Assets.Money -= totalHireCost; for (int i = 0; i < _count; i++) { - state.Assets.Agents.Add(new Agent(state.NextAgentId, state.Timeline.CurrentTurn)); + state.Assets.Agents.Add(new Agent(_agentIdGen.Generate, state.Timeline.CurrentTurn)); } return (ids: [state.Assets.Agents.Count], targetId: _count); diff --git a/src/game-lib/Controller/LaunchMissionPlayerAction.cs b/src/game-lib/Controller/LaunchMissionPlayerAction.cs index 0a01ac1e..fb8ef115 100644 --- a/src/game-lib/Controller/LaunchMissionPlayerAction.cs +++ b/src/game-lib/Controller/LaunchMissionPlayerAction.cs @@ -8,14 +8,16 @@ namespace UfoGameLib.Controller; public class LaunchMissionPlayerAction : PlayerAction { private readonly ILog _log; + private readonly MissionIdGen _missionIdGen; private readonly MissionSite _site; private readonly Agents _agents; - public LaunchMissionPlayerAction(ILog log, MissionSite site, Agents agents) + public LaunchMissionPlayerAction(ILog log, MissionIdGen missionIdGen, MissionSite site, Agents agents) { Contract.Assert(agents.Any()); agents.AssertCanBeSentOnMission(); _log = log; + _missionIdGen = missionIdGen; _site = site; _agents = agents; } @@ -33,7 +35,7 @@ protected override (List? ids, int? targetId) ApplyImpl(GameState state) $"_agents.Count: {_agents.Count} " + $">= _site.RequiredSurvivingAgentsForSuccess: {_site.RequiredSurvivingAgentsForSuccess}"); - int missionId = state.NextMissionId; + int missionId = _missionIdGen.Generate; var mission = new Mission(missionId, _site, state.Timeline.CurrentTurn, _agents.Count); Contract.Assert(_agents.All(agent => AgentSurvivalRoll.AgentCanSurvive(agent, mission))); diff --git a/src/game-lib/Controller/TimeAdvancementController.cs b/src/game-lib/Controller/TimeAdvancementController.cs index f6b21d41..e32f47e8 100644 --- a/src/game-lib/Controller/TimeAdvancementController.cs +++ b/src/game-lib/Controller/TimeAdvancementController.cs @@ -12,12 +12,18 @@ public class TimeAdvancementController private readonly RandomGen _randomGen; private readonly List _worldEvents; private readonly EventIdGen _eventIdGen; + private readonly MissionSiteIdGen _missionSiteIdGen; - public TimeAdvancementController(ILog log, RandomGen randomGen, EventIdGen eventIdGen) + public TimeAdvancementController( + ILog log, + RandomGen randomGen, + EventIdGen eventIdGen, + MissionSiteIdGen missionSiteIdGen) { _log = log; _randomGen = randomGen; _eventIdGen = eventIdGen; + _missionSiteIdGen = missionSiteIdGen; _worldEvents = new List(); } @@ -211,7 +217,7 @@ private int UpdateActiveMissionSites(GameState state) } private void CreateMissionSites(GameState state) { - List missionSites = state.Factions.CreateMissionSites(_log, _randomGen, state); + List missionSites = state.Factions.CreateMissionSites(_log, _randomGen, _missionSiteIdGen, state); state.MissionSites.AddRange(missionSites); } } \ No newline at end of file diff --git a/src/game-lib/Events/EventIdGen.cs b/src/game-lib/Events/EventIdGen.cs index 82b59740..044458a5 100644 --- a/src/game-lib/Events/EventIdGen.cs +++ b/src/game-lib/Events/EventIdGen.cs @@ -1,14 +1,14 @@ using Lib.Contracts; +using UfoGameLib.Lib; using UfoGameLib.State; namespace UfoGameLib.Events; -public class EventIdGen +public class EventIdGen : IdGen { - private int _nextEventId; - public EventIdGen(List turns) { + Contract.Assert(turns.Any()); // To compute starting next event ID we first need to determine if there are any events in the input // game session turns. If yes, we consider as the starting next event ID to be (last event ID + 1). // If not, we consider the last turn NextEventId value, if set. @@ -28,10 +28,6 @@ public EventIdGen(List turns) nextEventIdFromLastEvent is null || nextEventIdFromLastTurn is null || nextEventIdFromLastEvent == nextEventIdFromLastTurn, $"nextEventIdFromLastEvent: {nextEventIdFromLastEvent}, nextEventIdFromLastTurn: {nextEventIdFromLastTurn}"); - _nextEventId = nextEventIdFromLastEvent ?? nextEventIdFromLastTurn ?? 0; + NextId = nextEventIdFromLastEvent ?? nextEventIdFromLastTurn ?? 0; } - - public int Generate => _nextEventId++; - - public int Value => _nextEventId; } \ No newline at end of file diff --git a/src/game-lib/Lib/IdGen.cs b/src/game-lib/Lib/IdGen.cs new file mode 100644 index 00000000..15bd9b1e --- /dev/null +++ b/src/game-lib/Lib/IdGen.cs @@ -0,0 +1,10 @@ +namespace UfoGameLib.Lib; + +public class IdGen +{ + protected int NextId; + + public int Generate => NextId++; + + public int Value => NextId; +} \ No newline at end of file diff --git a/src/game-lib/Model/AgentIdGen.cs b/src/game-lib/Model/AgentIdGen.cs new file mode 100644 index 00000000..59e12a76 --- /dev/null +++ b/src/game-lib/Model/AgentIdGen.cs @@ -0,0 +1,12 @@ +using UfoGameLib.Lib; +using UfoGameLib.State; + +namespace UfoGameLib.Model; + +public class AgentIdGen : IdGen +{ + public AgentIdGen(List turns) + { + NextId = turns.Last().EndState.AllAgents.Count; + } +} diff --git a/src/game-lib/Model/Faction.cs b/src/game-lib/Model/Faction.cs index 95fc75e3..c76cd632 100644 --- a/src/game-lib/Model/Faction.cs +++ b/src/game-lib/Model/Faction.cs @@ -55,7 +55,11 @@ public static Faction Init( public Faction DeepClone() => new(Id, Name, Power, MissionSiteCountdown, PowerIncrease, PowerAcceleration, IntelInvested); - public List CreateMissionSites(ILog log, RandomGen randomGen, GameState state) + public List CreateMissionSites( + ILog log, + RandomGen randomGen, + MissionSiteIdGen missionSiteIdGen, + GameState state) { Contract.Assert(MissionSiteCountdown >= 1); @@ -69,8 +73,7 @@ public List CreateMissionSites(ILog log, RandomGen randomGen, GameS // 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 sites = []; - // kja BUG ROOT CAUSE: this must increase the ID, right now it does not. - int siteId = state.NextMissionSiteId; + int siteId = missionSiteIdGen.Generate; (int difficulty, int difficultyFromTurn, int roll) = Ruleset.RollMissionSiteDifficulty(state.Timeline.CurrentTurn, randomGen); diff --git a/src/game-lib/Model/Factions.cs b/src/game-lib/Model/Factions.cs index 54391ff5..d1900c8b 100644 --- a/src/game-lib/Model/Factions.cs +++ b/src/game-lib/Model/Factions.cs @@ -17,6 +17,10 @@ public Factions(IEnumerable? factions = null) public Factions DeepClone() => new Factions(this.Select(faction => faction.DeepClone())); - public List CreateMissionSites(ILog log, RandomGen randomGen, GameState state) - => this.SelectMany(faction => faction.CreateMissionSites(log, randomGen, state)).ToList(); + public List CreateMissionSites( + ILog log, + RandomGen randomGen, + MissionSiteIdGen missionSiteIdGen, + GameState state) + => this.SelectMany(faction => faction.CreateMissionSites(log, randomGen, missionSiteIdGen, state)).ToList(); } \ No newline at end of file diff --git a/src/game-lib/Model/MissionIdGen.cs b/src/game-lib/Model/MissionIdGen.cs new file mode 100644 index 00000000..f85e043f --- /dev/null +++ b/src/game-lib/Model/MissionIdGen.cs @@ -0,0 +1,14 @@ +using Lib.Contracts; +using UfoGameLib.Lib; +using UfoGameLib.State; + +namespace UfoGameLib.Model; + +public class MissionIdGen : IdGen +{ + public MissionIdGen(List turns) + { + Contract.Assert(turns.Any()); + NextId = turns.Last().EndState.Missions.Count; + } +} \ No newline at end of file diff --git a/src/game-lib/Model/MissionSiteIdGen.cs b/src/game-lib/Model/MissionSiteIdGen.cs new file mode 100644 index 00000000..56ff6d39 --- /dev/null +++ b/src/game-lib/Model/MissionSiteIdGen.cs @@ -0,0 +1,14 @@ +using Lib.Contracts; +using UfoGameLib.Lib; +using UfoGameLib.State; + +namespace UfoGameLib.Model; + +public class MissionSiteIdGen : IdGen +{ + public MissionSiteIdGen(List turns) + { + Contract.Assert(turns.Any()); + NextId = turns.Last().EndState.MissionSites.Count; + } +} \ No newline at end of file diff --git a/src/game-lib/Model/Ruleset.cs b/src/game-lib/Model/Ruleset.cs index 5e792b2e..ac70ae96 100644 --- a/src/game-lib/Model/Ruleset.cs +++ b/src/game-lib/Model/Ruleset.cs @@ -3,7 +3,7 @@ namespace UfoGameLib.Model; -// kja convert "Ruleset" class into a namespace. Maybe introduce IRuleset "marker" interface? +// kja2-refactor convert "Ruleset" class into a namespace. Maybe introduce IRuleset "marker" interface? public static class Ruleset { public const int InitialMoney = 500; diff --git a/src/game-lib/State/GameSession.cs b/src/game-lib/State/GameSession.cs index 1606877a..a1eb7769 100644 --- a/src/game-lib/State/GameSession.cs +++ b/src/game-lib/State/GameSession.cs @@ -1,6 +1,7 @@ using Lib.Contracts; using UfoGameLib.Events; using UfoGameLib.Lib; +using UfoGameLib.Model; namespace UfoGameLib.State; @@ -27,22 +28,28 @@ public class GameSession public readonly EventIdGen EventIdGen; + public readonly AgentIdGen AgentIdGen; + + public readonly MissionIdGen MissionIdGen; + + public readonly MissionSiteIdGen MissionSiteIdGen; + public GameSession(RandomGen randomGen, List? turns = null) { RandomGen = randomGen; Turns = turns ?? [new GameSessionTurn(startState: GameState.NewInitialGameState(randomGen))]; - Contract.Assert(Turns.Count >= 1); + Contract.Assert(Turns.Any()); Turns.ForEach(turn => turn.AssertInvariants()); + EventIdGen = new EventIdGen(Turns); + AgentIdGen = new AgentIdGen(Turns); + MissionSiteIdGen = new MissionSiteIdGen(Turns); + MissionIdGen = new MissionIdGen(Turns); + } public IReadOnlyList GameStates => Turns.SelectMany(turn => [turn.StartState, turn.EndState]) .ToList() .AsReadOnly(); - - public IReadOnlyList GameEvents - => Turns.SelectMany(turn => turn.GameEvents) - .ToList() - .AsReadOnly(); } \ No newline at end of file diff --git a/src/game-lib/State/GameState.cs b/src/game-lib/State/GameState.cs index e4ef2b7e..159d7cd3 100644 --- a/src/game-lib/State/GameState.cs +++ b/src/game-lib/State/GameState.cs @@ -76,10 +76,6 @@ public void ToJsonFile(File file) public bool IsGameWon => Assets.Intel >= Ruleset.IntelToWin; - public int NextAgentId => AllAgents.Count; - public int NextMissionId => Missions.Count; - public int NextMissionSiteId => MissionSites.Count; - public void Terminate(Agent agent, bool sack = false) { Assets.Agents.Remove(agent);