From 712056ee9f50f66d2dd4a2f4c4fa4b88573fb523 Mon Sep 17 00:00:00 2001 From: Ematerasu Date: Sat, 17 May 2025 11:41:29 +0200 Subject: [PATCH 1/2] Minor fixes --- Bots/Bots.sln | 24 ++++++++++++++ Bots/src/CardTierList.cs | 15 ++++++++- Engine/TalesOfTribute.csproj | 7 ++-- Engine/src/Board/BoardManager.cs | 2 +- .../Board/CardAction/ComplexEffectExecutor.cs | 26 +++++++++------ Engine/src/Board/Cards/UniqueCard.cs | 2 +- Engine/src/Board/Cards/UniqueEffect.cs | 5 +-- Engine/src/utils/CardDatabase.cs | 4 +-- Engine/src/utils/GlobalCardDatabase.cs | 16 +++++++-- Tests/Board/SaintAlessiaTests.cs | 33 ++++++++++++++++++- gRPC/Services/AIService.cs | 2 +- 11 files changed, 112 insertions(+), 24 deletions(-) create mode 100644 Bots/Bots.sln diff --git a/Bots/Bots.sln b/Bots/Bots.sln new file mode 100644 index 0000000..ab2081f --- /dev/null +++ b/Bots/Bots.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bots", "Bots.csproj", "{9C6335F4-1556-E642-9A79-3B54D949DB24}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9C6335F4-1556-E642-9A79-3B54D949DB24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C6335F4-1556-E642-9A79-3B54D949DB24}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C6335F4-1556-E642-9A79-3B54D949DB24}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C6335F4-1556-E642-9A79-3B54D949DB24}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {95450D74-CA50-448F-8833-1CF1FE223870} + EndGlobalSection +EndGlobal diff --git a/Bots/src/CardTierList.cs b/Bots/src/CardTierList.cs index 607ae14..614f2ae 100644 --- a/Bots/src/CardTierList.cs +++ b/Bots/src/CardTierList.cs @@ -148,7 +148,20 @@ public class CardTierList new CardTier("Ragpicker", PatronId.TREASURY, TierEnum.C), new CardTier("Tithe", PatronId.TREASURY, TierEnum.C), new CardTier("Writ of Coin", PatronId.TREASURY, TierEnum.D), - new CardTier("Unknown", PatronId.TREASURY, TierEnum.UNKNOWN) + new CardTier("Unknown", PatronId.TREASURY, TierEnum.UNKNOWN), + // Added Saint Alessia + new CardTier("Alessian Rebel", PatronId.SAINT_ALESSIA, TierEnum.C), + new CardTier("Ayleid Defector", PatronId.SAINT_ALESSIA, TierEnum.B), + new CardTier("Ayleid Quartermaster", PatronId.SAINT_ALESSIA, TierEnum.B), + new CardTier("Chainbreaker Captain", PatronId.SAINT_ALESSIA, TierEnum.A), + new CardTier("Chainbreaker Sergeant", PatronId.SAINT_ALESSIA, TierEnum.B), + new CardTier("Morihuas Sacred Bull", PatronId.SAINT_ALESSIA, TierEnum.S), + new CardTier("Morihuas the Archer", PatronId.SAINT_ALESSIA, TierEnum.A), + new CardTier("Pelinal Whitestrake", PatronId.SAINT_ALESSIA, TierEnum.S), + new CardTier("Priestess of the Eight", PatronId.SAINT_ALESSIA, TierEnum.B), + new CardTier("Saints Wrath", PatronId.SAINT_ALESSIA, TierEnum.B), + new CardTier("Soldier of the Empire", PatronId.SAINT_ALESSIA, TierEnum.C), + new CardTier("Whitestrake Ascendant", PatronId.SAINT_ALESSIA, TierEnum.S), }; public static TierEnum GetCardTier(string cardName) diff --git a/Engine/TalesOfTribute.csproj b/Engine/TalesOfTribute.csproj index 2bab50c..c90abab 100644 --- a/Engine/TalesOfTribute.csproj +++ b/Engine/TalesOfTribute.csproj @@ -18,9 +18,10 @@ - - Always - + + + Never + diff --git a/Engine/src/Board/BoardManager.cs b/Engine/src/Board/BoardManager.cs index b795400..ec662c4 100644 --- a/Engine/src/Board/BoardManager.cs +++ b/Engine/src/Board/BoardManager.cs @@ -23,7 +23,7 @@ public BoardManager(PatronId[] patrons, ulong seed) _rng = new SeededRandom(seed); Patrons = GetPatrons(patrons); var patronStarterCardsIds = Patrons.SelectMany(patron => patron.GetStarterCards()).ToArray(); - Tavern = new Tavern(GlobalCardDatabase.Instance.GetCardsByPatron(patrons, patronStarterCardsIds), _rng); + Tavern = new Tavern(GlobalCardDatabase.Instance.GetCardsByPatron(patrons, patronStarterCardsIds, CardId.WRIT_OF_COIN), _rng); _playerContext = new PlayerContext(new Player(PlayerEnum.PLAYER1, _rng), new Player(PlayerEnum.PLAYER2, _rng)); CardActionManager = new CardActionManager(_playerContext, Tavern); } diff --git a/Engine/src/Board/CardAction/ComplexEffectExecutor.cs b/Engine/src/Board/CardAction/ComplexEffectExecutor.cs index c64de05..74dbf87 100644 --- a/Engine/src/Board/CardAction/ComplexEffectExecutor.cs +++ b/Engine/src/Board/CardAction/ComplexEffectExecutor.cs @@ -226,17 +226,23 @@ public PlayResult Donate(Choice choice, List choices) } private void handleSaintAlessiaTriggers(List knockoutedCards) { - if (!_enemyPlayer.AgentCards.Any(c => c.CommonId == CardId.MORIHAUS_SACRED_BULL || c.CommonId == CardId.MORIHAUS_THE_ARCHER)) - return; - - foreach (var triggerCard in _enemyPlayer.AgentCards.Where(c => c.CommonId == CardId.MORIHAUS_SACRED_BULL || c.CommonId == CardId.MORIHAUS_THE_ARCHER).ToList()) + void HandleForPlayer(IPlayer player) { - if (knockoutedCards.Any(c => c.UniqueId == triggerCard.UniqueId)) - continue; - _enemyPlayer.CoinsAmount++; - _parent.AddToCompletedActionsList(new CompletedAction(_enemyPlayer.ID, CompletedActionType.GAIN_COIN, triggerCard, 1)); + var morihausAgents = player.AgentCards + .Where(c => c.CommonId == CardId.MORIHAUS_SACRED_BULL || c.CommonId == CardId.MORIHAUS_THE_ARCHER) + .ToList(); + + foreach (var triggerCard in morihausAgents) + { + if (knockoutedCards.Any(c => c.UniqueId == triggerCard.UniqueId)) + continue; + + player.CoinsAmount++; + _parent.AddToCompletedActionsList(new CompletedAction(player.ID, CompletedActionType.GAIN_COIN, triggerCard, 1)); + } } - - } + HandleForPlayer(_currentPlayer); + HandleForPlayer(_enemyPlayer); + } } diff --git a/Engine/src/Board/Cards/UniqueCard.cs b/Engine/src/Board/Cards/UniqueCard.cs index ba8d73b..76abdc4 100644 --- a/Engine/src/Board/Cards/UniqueCard.cs +++ b/Engine/src/Board/Cards/UniqueCard.cs @@ -18,7 +18,7 @@ public UniqueCard(string name, PatronId deck, CardId commonId, int cost, CardTyp public override string ToString() { return string.Format($"Card: {this.Name}, " + - $"Deck: {this.Deck}, Cost: {this.Cost}, Type: {this.Type}, UniqueId: {UniqueId.Value}"); + $"Deck: {this.Deck}, Cost: {this.Cost}, Type: {this.Type}, UniqueId: {UniqueId.Value}"); } public override bool Equals(object? obj) diff --git a/Engine/src/Board/Cards/UniqueEffect.cs b/Engine/src/Board/Cards/UniqueEffect.cs index 3bddeb3..29d1ce6 100644 --- a/Engine/src/Board/Cards/UniqueEffect.cs +++ b/Engine/src/Board/Cards/UniqueEffect.cs @@ -143,7 +143,7 @@ public UniqueEffect(EffectType type, int amount, int combo, UniqueCard parentCar ChoiceFollowUp.REFRESH_CARDS, context, amount, - amount + 0 ), new List()); } case EffectType.TOSS: @@ -213,7 +213,7 @@ public UniqueEffect(EffectType type, int amount, int combo, UniqueCard parentCar ChoiceFollowUp.REFRESH_CARDS, context, agentsCount, - agentsCount + 0 ), new List()); case EffectType.DONATE: { @@ -250,6 +250,7 @@ public override string ToString() EffectType.CREATE_SUMMERSET_SACKING => $"Create {Amount} Summerset Sacking cards and place it in CD pile", EffectType.HEAL => $"Heal this agent by {Amount}", EffectType.DONATE => $"Discard up to {Amount} cards from hand, draw {Amount} cards", + EffectType.KNOCKOUT_ALL => $"Knockout all agents", _ => "" }; } diff --git a/Engine/src/utils/CardDatabase.cs b/Engine/src/utils/CardDatabase.cs index d38546a..0cbf786 100644 --- a/Engine/src/utils/CardDatabase.cs +++ b/Engine/src/utils/CardDatabase.cs @@ -19,10 +19,10 @@ public UniqueCard GetCard(CardId cardId) return card; } - public List GetCardsByPatron(PatronId[] patrons, CardId[] starterCardsId) + public List GetCardsByPatron(PatronId[] patrons, CardId[] starterCardsId, params CardId[] excludeCards) { var allCardsGrouped = from card in _allCards - where patrons.Contains(card.Deck) && !starterCardsId.Contains(card.CommonId) && card.Type != CardType.CURSE + where patrons.Contains(card.Deck) && !starterCardsId.Contains(card.CommonId) && !excludeCards.Contains(card.CommonId) && card.Type != CardType.CURSE select Enumerable.Range(0, card.Copies).Select(_ => card.CreateUniqueCopy()).ToList(); return allCardsGrouped.SelectMany(c => c).ToList(); diff --git a/Engine/src/utils/GlobalCardDatabase.cs b/Engine/src/utils/GlobalCardDatabase.cs index 2be969a..e316ebd 100644 --- a/Engine/src/utils/GlobalCardDatabase.cs +++ b/Engine/src/utils/GlobalCardDatabase.cs @@ -1,4 +1,6 @@ -namespace ScriptsOfTribute; +using System.Reflection; + +namespace ScriptsOfTribute; public class GlobalCardDatabase { @@ -10,7 +12,17 @@ public static CardDatabase Instance { if (_instance != null) return _instance.Value; - var data = File.ReadAllText("cards.json"); + var assembly = Assembly.GetExecutingAssembly(); + const string resourceName = "TalesOfTribute.cards.json"; + using Stream? stream = assembly.GetManifestResourceStream(resourceName); + + if (stream == null) + { + throw new InvalidOperationException($"Cant find resource'{resourceName}' in DLL. Available resources: {string.Join(", ", assembly.GetManifestResourceNames())}"); + } + + using StreamReader reader = new StreamReader(stream); + string data = reader.ReadToEnd(); var parser = new Parser(data); _instance = new ThreadLocal(() => new CardDatabase(parser.CreateAllCards())); diff --git a/Tests/Board/SaintAlessiaTests.cs b/Tests/Board/SaintAlessiaTests.cs index 98bf213..2d3b927 100644 --- a/Tests/Board/SaintAlessiaTests.cs +++ b/Tests/Board/SaintAlessiaTests.cs @@ -67,7 +67,7 @@ void TestDonateEffect() br.CurrentPlayer.Hand.Add(priestess); br.CurrentPlayer.Hand.Add(sote); br.CurrentPlayer.Hand.Add(armory); - + var chainbreaker = GlobalCardDatabase.Instance.GetCard(CardId.CHAINBREAKER_CAPTAIN); var morihaus = GlobalCardDatabase.Instance.GetCard(CardId.MORIHAUS_SACRED_BULL); br.CurrentPlayer.DrawPile.Add(chainbreaker); @@ -200,4 +200,35 @@ void TestRefreshTopAgents() Assert.Equal(BoardState.NORMAL, br.CardActionManager.State); Assert.Contains(chainbreaker_sergeant, br.CurrentPlayer.DrawPile); } + + [Fact] + void TestMorihausTriggersBothPlayers() + { + var br = new BoardManager([PatronId.TREASURY, PatronId.PELIN, PatronId.RED_EAGLE, PatronId.ANSEI, PatronId.SAINT_ALESSIA], 123); + + var morihaus1 = Agent.FromCard(GlobalCardDatabase.Instance.GetCard(CardId.MORIHAUS_SACRED_BULL)); + var morihaus2 = Agent.FromCard(GlobalCardDatabase.Instance.GetCard(CardId.MORIHAUS_THE_ARCHER)); + var soldier1 = Agent.FromCard(GlobalCardDatabase.Instance.GetCard(CardId.SOLDIER_OF_THE_EMPIRE)); + var soldier2 = Agent.FromCard(GlobalCardDatabase.Instance.GetCard(CardId.SOLDIER_OF_THE_EMPIRE)); + + br.CurrentPlayer.Agents.Add(morihaus1); + br.CurrentPlayer.Agents.Add(soldier1); + + br.EnemyPlayer.Agents.Add(morihaus2); + br.EnemyPlayer.Agents.Add(soldier2); + + var black_sacrament = GlobalCardDatabase.Instance.GetCard(CardId.BLACK_SACRAMENT); + br.CurrentPlayer.Hand.Add(black_sacrament); + + br.PlayCard(black_sacrament); + + var knockout = br.CardActionManager.PendingChoice!.PossibleCards + .Where(card => card.CommonId == CardId.SOLDIER_OF_THE_EMPIRE && br.EnemyPlayer.Agents.Any(a => a.RepresentingCard.UniqueId == card.UniqueId)) + .ToList(); + + br.CardActionManager.MakeChoice(knockout); + + Assert.Equal(1, br.EnemyPlayer.CoinsAmount); + Assert.Equal(1, br.CurrentPlayer.CoinsAmount); + } } diff --git a/gRPC/Services/AIService.cs b/gRPC/Services/AIService.cs index 2148f38..6659409 100644 --- a/gRPC/Services/AIService.cs +++ b/gRPC/Services/AIService.cs @@ -44,7 +44,7 @@ public PatronId SelectPatron(List availablePatrons, int round) }; request.AvailablePatrons.AddRange(availablePatrons.Select(patronId => (PatronIdProto)(patronId)).ToList()); - + var response = _client.SelectPatron(request); return (PatronId)(response.PatronId); } From a3458381c2baa8f9b5b35defd5dd75e3c6ba7ab8 Mon Sep 17 00:00:00 2001 From: Ematerasu Date: Sat, 17 May 2025 11:42:57 +0200 Subject: [PATCH 2/2] Bots dont need solution --- Bots/Bots.sln | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 Bots/Bots.sln diff --git a/Bots/Bots.sln b/Bots/Bots.sln deleted file mode 100644 index ab2081f..0000000 --- a/Bots/Bots.sln +++ /dev/null @@ -1,24 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.2.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bots", "Bots.csproj", "{9C6335F4-1556-E642-9A79-3B54D949DB24}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9C6335F4-1556-E642-9A79-3B54D949DB24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9C6335F4-1556-E642-9A79-3B54D949DB24}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9C6335F4-1556-E642-9A79-3B54D949DB24}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9C6335F4-1556-E642-9A79-3B54D949DB24}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {95450D74-CA50-448F-8833-1CF1FE223870} - EndGlobalSection -EndGlobal