[Refactor] Replace hardcoded categories with data-driven SO system and abstract dice
- Add abstract dice system (IDie interface, DieDefinitionSO, StandardDieSO, DieInstance) to support future custom dice types while keeping backward compat via int[] DiceValues - Replace YachtCategory enum and CategoryScorer switch with CategoryDefinitionSO hierarchy: SumOfValueCategorySO, NOfAKindCategorySO, FullHouseCategorySO, StraightCategorySO, SumAllCategorySO - Add CategoryCatalogSO for ordered category collections and DiceCheckUtility for shared logic - Refactor ScoringSystem, Views, GameManager, GameController to use SO references - Update CategoryCondition modifier to use SO reference instead of enum - Update all editor tests to use SO-based categories and DieInstance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using YachtDice.Categories;
|
||||
using YachtDice.Modifiers.Core;
|
||||
using YachtDice.Modifiers.Definition;
|
||||
using YachtDice.Modifiers.Effects;
|
||||
@@ -10,19 +11,33 @@ namespace YachtDice.Tests
|
||||
{
|
||||
public class ModifierEffectTests
|
||||
{
|
||||
private CategoryDefinitionSO testCategory;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
testCategory = SumAllCategorySO.CreateForTest("chance", "Шанс");
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Object.DestroyImmediate(testCategory);
|
||||
}
|
||||
|
||||
private ModifierInstance CreateInstance(string id = "test")
|
||||
{
|
||||
var def = ModifierDefinitionSO.CreateForTest(id, null);
|
||||
return new ModifierInstance(def);
|
||||
}
|
||||
|
||||
private ModifierContext CreateContext(int baseScore, int[] dice, YachtCategory category)
|
||||
private ModifierContext CreateContext(int baseScore, int[] dice)
|
||||
{
|
||||
return new ModifierContext
|
||||
{
|
||||
BaseScore = baseScore,
|
||||
DiceValues = dice,
|
||||
Category = category,
|
||||
Category = testCategory,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -32,7 +47,7 @@ namespace YachtDice.Tests
|
||||
public void AddPerDieEffect_CountsMatchingDice()
|
||||
{
|
||||
var effect = AddPerDieEffect.CreateForTest(10, targetDieValue: 1);
|
||||
var ctx = CreateContext(5, new[] { 1, 1, 3, 4, 1 }, YachtCategory.Ones);
|
||||
var ctx = CreateContext(5, new[] { 1, 1, 3, 4, 1 });
|
||||
var inst = CreateInstance();
|
||||
|
||||
effect.Apply(ctx, inst).GetAwaiter().GetResult();
|
||||
@@ -44,7 +59,7 @@ namespace YachtDice.Tests
|
||||
public void AddPerDieEffect_ZeroTarget_CountsAllDice()
|
||||
{
|
||||
var effect = AddPerDieEffect.CreateForTest(2, targetDieValue: 0);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 });
|
||||
var inst = CreateInstance();
|
||||
|
||||
effect.Apply(ctx, inst).GetAwaiter().GetResult();
|
||||
@@ -56,7 +71,7 @@ namespace YachtDice.Tests
|
||||
public void AddPerDieEffect_NoMatches_ZeroBonus()
|
||||
{
|
||||
var effect = AddPerDieEffect.CreateForTest(10, targetDieValue: 6);
|
||||
var ctx = CreateContext(5, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateContext(5, new[] { 1, 2, 3, 4, 5 });
|
||||
var inst = CreateInstance();
|
||||
|
||||
effect.Apply(ctx, inst).GetAwaiter().GetResult();
|
||||
@@ -68,7 +83,7 @@ namespace YachtDice.Tests
|
||||
public void AddPerDieEffect_ScalesWithStacks()
|
||||
{
|
||||
var effect = AddPerDieEffect.CreateForTest(10, targetDieValue: 1);
|
||||
var ctx = CreateContext(5, new[] { 1, 1, 3, 4, 1 }, YachtCategory.Ones);
|
||||
var ctx = CreateContext(5, new[] { 1, 1, 3, 4, 1 });
|
||||
var inst = CreateInstance();
|
||||
inst.Stacks = 2;
|
||||
|
||||
@@ -83,7 +98,7 @@ namespace YachtDice.Tests
|
||||
public void AddFlatScoreEffect_AddsFlat()
|
||||
{
|
||||
var effect = AddFlatScoreEffect.CreateForTest(15);
|
||||
var ctx = CreateContext(25, new[] { 3, 3, 2, 2, 2 }, YachtCategory.FullHouse);
|
||||
var ctx = CreateContext(25, new[] { 3, 3, 2, 2, 2 });
|
||||
var inst = CreateInstance();
|
||||
|
||||
effect.Apply(ctx, inst).GetAwaiter().GetResult();
|
||||
@@ -95,7 +110,7 @@ namespace YachtDice.Tests
|
||||
public void AddFlatScoreEffect_ScalesWithStacks()
|
||||
{
|
||||
var effect = AddFlatScoreEffect.CreateForTest(15);
|
||||
var ctx = CreateContext(25, new[] { 3, 3, 2, 2, 2 }, YachtCategory.FullHouse);
|
||||
var ctx = CreateContext(25, new[] { 3, 3, 2, 2, 2 });
|
||||
var inst = CreateInstance();
|
||||
inst.Stacks = 3;
|
||||
|
||||
@@ -110,7 +125,7 @@ namespace YachtDice.Tests
|
||||
public void MultiplyPerDieEffect_MultipliesPerMatch()
|
||||
{
|
||||
var effect = MultiplyPerDieEffect.CreateForTest(2f, targetDieValue: 6);
|
||||
var ctx = CreateContext(18, new[] { 6, 6, 6, 1, 2 }, YachtCategory.Sixes);
|
||||
var ctx = CreateContext(18, new[] { 6, 6, 6, 1, 2 });
|
||||
var inst = CreateInstance();
|
||||
|
||||
effect.Apply(ctx, inst).GetAwaiter().GetResult();
|
||||
@@ -122,7 +137,7 @@ namespace YachtDice.Tests
|
||||
public void MultiplyPerDieEffect_NoMatches_MultiplierUnchanged()
|
||||
{
|
||||
var effect = MultiplyPerDieEffect.CreateForTest(3f, targetDieValue: 6);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 });
|
||||
var inst = CreateInstance();
|
||||
|
||||
effect.Apply(ctx, inst).GetAwaiter().GetResult();
|
||||
@@ -136,7 +151,7 @@ namespace YachtDice.Tests
|
||||
public void MultiplyScoreEffect_MultipliesOnce()
|
||||
{
|
||||
var effect = MultiplyScoreEffect.CreateForTest(1.5f);
|
||||
var ctx = CreateContext(50, new[] { 6, 6, 6, 6, 6 }, YachtCategory.Yacht);
|
||||
var ctx = CreateContext(50, new[] { 6, 6, 6, 6, 6 });
|
||||
var inst = CreateInstance();
|
||||
|
||||
effect.Apply(ctx, inst).GetAwaiter().GetResult();
|
||||
@@ -148,7 +163,7 @@ namespace YachtDice.Tests
|
||||
public void MultiplyScoreEffect_ScalesWithStacks()
|
||||
{
|
||||
var effect = MultiplyScoreEffect.CreateForTest(2f);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 });
|
||||
var inst = CreateInstance();
|
||||
inst.Stacks = 3;
|
||||
|
||||
@@ -164,7 +179,7 @@ namespace YachtDice.Tests
|
||||
public void PostMultiplyEffect_MultipliesPostMultiplier()
|
||||
{
|
||||
var effect = PostMultiplyEffect.CreateForTest(2f);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 });
|
||||
var inst = CreateInstance();
|
||||
|
||||
effect.Apply(ctx, inst).GetAwaiter().GetResult();
|
||||
@@ -178,7 +193,7 @@ namespace YachtDice.Tests
|
||||
public void AddCurrencyEffect_AddsToCurrencyDelta()
|
||||
{
|
||||
var effect = AddCurrencyEffect.CreateForTest(25);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 });
|
||||
var inst = CreateInstance();
|
||||
|
||||
effect.Apply(ctx, inst).GetAwaiter().GetResult();
|
||||
@@ -190,7 +205,7 @@ namespace YachtDice.Tests
|
||||
public void AddCurrencyEffect_ScalesWithStacks()
|
||||
{
|
||||
var effect = AddCurrencyEffect.CreateForTest(25);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 });
|
||||
var inst = CreateInstance();
|
||||
inst.Stacks = 2;
|
||||
|
||||
@@ -205,7 +220,7 @@ namespace YachtDice.Tests
|
||||
public void ConsumeChargeEffect_DecrementsRemainingUses()
|
||||
{
|
||||
var effect = ConsumeChargeEffect.CreateForTest(1);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 });
|
||||
var def = ModifierDefinitionSO.CreateForTest("limited", null,
|
||||
hasLimitedUses: true, maxUses: 3);
|
||||
var inst = new ModifierInstance(def);
|
||||
@@ -219,7 +234,7 @@ namespace YachtDice.Tests
|
||||
public void ConsumeChargeEffect_IgnoresPermanent()
|
||||
{
|
||||
var effect = ConsumeChargeEffect.CreateForTest(1);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 });
|
||||
var inst = CreateInstance(); // permanent (no limited uses)
|
||||
|
||||
effect.Apply(ctx, inst).GetAwaiter().GetResult();
|
||||
@@ -232,7 +247,7 @@ namespace YachtDice.Tests
|
||||
[Test]
|
||||
public void FinalScore_CombinesBaseAndFlatAndMultiplier()
|
||||
{
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 });
|
||||
ctx.FlatBonus = 5;
|
||||
ctx.Multiplier = 2f;
|
||||
ctx.PostMultiplier = 1.5f;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using YachtDice.Categories;
|
||||
using YachtDice.Modifiers.Conditions;
|
||||
using YachtDice.Modifiers.Core;
|
||||
using YachtDice.Modifiers.Definition;
|
||||
@@ -16,12 +17,38 @@ namespace YachtDice.Tests
|
||||
private ModifierRegistry registry;
|
||||
private ModifierPipeline pipeline;
|
||||
|
||||
// Тестовые категории
|
||||
private CategoryDefinitionSO chanceCategory;
|
||||
private CategoryDefinitionSO fullHouseCategory;
|
||||
private CategoryDefinitionSO onesCategory;
|
||||
private CategoryDefinitionSO threesCategory;
|
||||
private CategoryDefinitionSO foursCategory;
|
||||
private CategoryDefinitionSO sixesCategory;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
registry = new ModifierRegistry(10);
|
||||
pipeline = new ModifierPipeline(registry);
|
||||
pipeline.TracingEnabled = false; // disable debug logs during tests
|
||||
pipeline.TracingEnabled = false;
|
||||
|
||||
chanceCategory = SumAllCategorySO.CreateForTest("chance", "Шанс");
|
||||
fullHouseCategory = FullHouseCategorySO.CreateForTest("full_house", "Фулл-хаус");
|
||||
onesCategory = SumOfValueCategorySO.CreateForTest("ones", "Единицы", 1);
|
||||
threesCategory = SumOfValueCategorySO.CreateForTest("threes", "Тройки", 3);
|
||||
foursCategory = SumOfValueCategorySO.CreateForTest("fours", "Четвёрки", 4);
|
||||
sixesCategory = SumOfValueCategorySO.CreateForTest("sixes", "Шестёрки", 6);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Object.DestroyImmediate(chanceCategory);
|
||||
Object.DestroyImmediate(fullHouseCategory);
|
||||
Object.DestroyImmediate(onesCategory);
|
||||
Object.DestroyImmediate(threesCategory);
|
||||
Object.DestroyImmediate(foursCategory);
|
||||
Object.DestroyImmediate(sixesCategory);
|
||||
}
|
||||
|
||||
private ModifierDefinitionSO CreateDef(string id,
|
||||
@@ -40,7 +67,7 @@ namespace YachtDice.Tests
|
||||
registry.TryActivate(inst);
|
||||
}
|
||||
|
||||
private ModifierContext CreateScoringContext(int baseScore, int[] dice, YachtCategory category)
|
||||
private ModifierContext CreateScoringContext(int baseScore, int[] dice, CategoryDefinitionSO category)
|
||||
{
|
||||
return new ModifierContext
|
||||
{
|
||||
@@ -64,10 +91,10 @@ namespace YachtDice.Tests
|
||||
var mulDef = CreateDef("mul", TriggerType.OnCategoryScored, null,
|
||||
new List<EffectSO> { mulEffect });
|
||||
|
||||
RegisterAndActivate(mulDef); // registered first, but multiplicative phase
|
||||
RegisterAndActivate(addDef); // registered second, but additive phase
|
||||
RegisterAndActivate(mulDef);
|
||||
RegisterAndActivate(addDef);
|
||||
|
||||
var ctx = CreateScoringContext(20, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateScoringContext(20, new[] { 1, 2, 3, 4, 5 }, chanceCategory);
|
||||
var result = pipeline.Execute(TriggerType.OnCategoryScored, ctx).GetAwaiter().GetResult();
|
||||
|
||||
// (20 + 10) * 2 = 60
|
||||
@@ -88,7 +115,7 @@ namespace YachtDice.Tests
|
||||
RegisterAndActivate(postDef);
|
||||
RegisterAndActivate(mulDef);
|
||||
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, chanceCategory);
|
||||
var result = pipeline.Execute(TriggerType.OnCategoryScored, ctx).GetAwaiter().GetResult();
|
||||
|
||||
// (10 + 0) * 2 * 3 = 60
|
||||
@@ -100,7 +127,7 @@ namespace YachtDice.Tests
|
||||
[Test]
|
||||
public void Execute_ConditionFails_SkipsEffect()
|
||||
{
|
||||
var condition = CategoryCondition.CreateForTest(YachtCategory.FullHouse);
|
||||
var condition = CategoryCondition.CreateForTest(fullHouseCategory);
|
||||
var effect = AddFlatScoreEffect.CreateForTest(100);
|
||||
|
||||
var def = CreateDef("fh-bonus", TriggerType.OnCategoryScored,
|
||||
@@ -110,7 +137,7 @@ namespace YachtDice.Tests
|
||||
RegisterAndActivate(def);
|
||||
|
||||
// Scoring Ones, not FullHouse — condition should fail
|
||||
var ctx = CreateScoringContext(5, new[] { 1, 1, 1, 1, 1 }, YachtCategory.Ones);
|
||||
var ctx = CreateScoringContext(5, new[] { 1, 1, 1, 1, 1 }, onesCategory);
|
||||
var result = pipeline.Execute(TriggerType.OnCategoryScored, ctx).GetAwaiter().GetResult();
|
||||
|
||||
Assert.AreEqual(0, result.FlatBonus);
|
||||
@@ -120,7 +147,7 @@ namespace YachtDice.Tests
|
||||
[Test]
|
||||
public void Execute_ConditionPasses_AppliesEffect()
|
||||
{
|
||||
var condition = CategoryCondition.CreateForTest(YachtCategory.FullHouse);
|
||||
var condition = CategoryCondition.CreateForTest(fullHouseCategory);
|
||||
var effect = AddFlatScoreEffect.CreateForTest(15);
|
||||
|
||||
var def = CreateDef("fh-bonus", TriggerType.OnCategoryScored,
|
||||
@@ -129,7 +156,7 @@ namespace YachtDice.Tests
|
||||
|
||||
RegisterAndActivate(def);
|
||||
|
||||
var ctx = CreateScoringContext(25, new[] { 3, 3, 3, 2, 2 }, YachtCategory.FullHouse);
|
||||
var ctx = CreateScoringContext(25, new[] { 3, 3, 3, 2, 2 }, fullHouseCategory);
|
||||
var result = pipeline.Execute(TriggerType.OnCategoryScored, ctx).GetAwaiter().GetResult();
|
||||
|
||||
Assert.AreEqual(15, result.FlatBonus);
|
||||
@@ -147,8 +174,7 @@ namespace YachtDice.Tests
|
||||
|
||||
RegisterAndActivate(def);
|
||||
|
||||
// Fire OnCategoryScored, not OnTurnStart
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, chanceCategory);
|
||||
var result = pipeline.Execute(TriggerType.OnCategoryScored, ctx).GetAwaiter().GetResult();
|
||||
|
||||
Assert.AreEqual(0, result.FlatBonus);
|
||||
@@ -180,12 +206,9 @@ namespace YachtDice.Tests
|
||||
RegisterAndActivate(def1);
|
||||
|
||||
// dice: [3, 3, 3, 1, 2] — 3 threes
|
||||
var ctx = CreateScoringContext(9, new[] { 3, 3, 3, 1, 2 }, YachtCategory.Threes);
|
||||
var ctx = CreateScoringContext(9, new[] { 3, 3, 3, 1, 2 }, threesCategory);
|
||||
var result = pipeline.Execute(TriggerType.OnCategoryScored, ctx).GetAwaiter().GetResult();
|
||||
|
||||
// Additive phase: perDieAdd (+2*3=6) + flatAdd (+10) → FlatBonus = 16
|
||||
// Multiplicative phase: perDieMul (1.5^3=3.375) then finalMul (*2) → Multiplier = 6.75
|
||||
// FinalScore = floor((9 + 16) * 6.75) = floor(168.75) = 168
|
||||
Assert.AreEqual(16, result.FlatBonus);
|
||||
Assert.AreEqual(168, result.FinalScore);
|
||||
}
|
||||
@@ -195,7 +218,7 @@ namespace YachtDice.Tests
|
||||
[Test]
|
||||
public void Execute_NoActiveModifiers_NoChange()
|
||||
{
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, chanceCategory);
|
||||
var result = pipeline.Execute(TriggerType.OnCategoryScored, ctx).GetAwaiter().GetResult();
|
||||
|
||||
Assert.AreEqual(10, result.FinalScore);
|
||||
@@ -210,10 +233,9 @@ namespace YachtDice.Tests
|
||||
var def = CreateDef("inactive", TriggerType.OnCategoryScored, null,
|
||||
new List<EffectSO> { effect });
|
||||
|
||||
// Add but don't activate
|
||||
registry.Add(def);
|
||||
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, chanceCategory);
|
||||
var result = pipeline.Execute(TriggerType.OnCategoryScored, ctx).GetAwaiter().GetResult();
|
||||
|
||||
Assert.AreEqual(0, result.FlatBonus);
|
||||
@@ -233,7 +255,7 @@ namespace YachtDice.Tests
|
||||
|
||||
RegisterAndActivate(def);
|
||||
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, chanceCategory);
|
||||
var result = pipeline.Execute(TriggerType.OnCategoryScored, ctx).GetAwaiter().GetResult();
|
||||
|
||||
Assert.AreEqual(10, result.FlatBonus);
|
||||
@@ -254,7 +276,7 @@ namespace YachtDice.Tests
|
||||
|
||||
RegisterAndActivate(def);
|
||||
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, chanceCategory);
|
||||
var result = pipeline.Execute(TriggerType.OnCategoryScored, ctx).GetAwaiter().GetResult();
|
||||
|
||||
Assert.IsNotNull(result.DebugLog);
|
||||
@@ -276,13 +298,13 @@ namespace YachtDice.Tests
|
||||
RegisterAndActivate(def);
|
||||
|
||||
// Only 2 sixes — condition requires 3
|
||||
var ctx = CreateScoringContext(12, new[] { 6, 6, 1, 2, 3 }, YachtCategory.Sixes);
|
||||
var ctx = CreateScoringContext(12, new[] { 6, 6, 1, 2, 3 }, sixesCategory);
|
||||
var result = pipeline.Execute(TriggerType.OnCategoryScored, ctx).GetAwaiter().GetResult();
|
||||
|
||||
Assert.AreEqual(0, result.FlatBonus);
|
||||
|
||||
// 3 sixes — condition passes
|
||||
var ctx2 = CreateScoringContext(18, new[] { 6, 6, 6, 1, 2 }, YachtCategory.Sixes);
|
||||
var ctx2 = CreateScoringContext(18, new[] { 6, 6, 6, 1, 2 }, sixesCategory);
|
||||
var result2 = pipeline.Execute(TriggerType.OnCategoryScored, ctx2).GetAwaiter().GetResult();
|
||||
|
||||
Assert.AreEqual(100, result2.FlatBonus);
|
||||
@@ -303,13 +325,13 @@ namespace YachtDice.Tests
|
||||
RegisterAndActivate(def);
|
||||
|
||||
// Below threshold
|
||||
var ctx = CreateScoringContext(15, new[] { 3, 3, 3, 3, 3 }, YachtCategory.Threes);
|
||||
var ctx = CreateScoringContext(15, new[] { 3, 3, 3, 3, 3 }, threesCategory);
|
||||
var result = pipeline.Execute(TriggerType.OnCategoryScored, ctx).GetAwaiter().GetResult();
|
||||
|
||||
Assert.AreEqual(1f, result.Multiplier);
|
||||
|
||||
// At threshold
|
||||
var ctx2 = CreateScoringContext(20, new[] { 4, 4, 4, 4, 4 }, YachtCategory.Fours);
|
||||
var ctx2 = CreateScoringContext(20, new[] { 4, 4, 4, 4, 4 }, foursCategory);
|
||||
var result2 = pipeline.Execute(TriggerType.OnCategoryScored, ctx2).GetAwaiter().GetResult();
|
||||
|
||||
Assert.AreEqual(2f, result2.Multiplier);
|
||||
@@ -320,7 +342,7 @@ namespace YachtDice.Tests
|
||||
[Test]
|
||||
public void ToScoreResult_ConvertsCorrectly()
|
||||
{
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var ctx = CreateScoringContext(10, new[] { 1, 2, 3, 4, 5 }, chanceCategory);
|
||||
ctx.FlatBonus = 5;
|
||||
ctx.Multiplier = 2f;
|
||||
ctx.PostMultiplier = 1.5f;
|
||||
@@ -330,7 +352,7 @@ namespace YachtDice.Tests
|
||||
Assert.AreEqual(10, sr.BaseScore);
|
||||
Assert.AreEqual(5, sr.FlatBonus);
|
||||
Assert.AreEqual(3f, sr.Multiplier, 0.001f); // 2 * 1.5
|
||||
Assert.AreEqual(YachtCategory.Chance, sr.Category);
|
||||
Assert.AreEqual(chanceCategory, sr.Category);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using YachtDice.Categories;
|
||||
using YachtDice.Dice;
|
||||
using YachtDice.Scoring;
|
||||
|
||||
namespace YachtDice.Tests
|
||||
{
|
||||
public class ScoringSystemTests
|
||||
{
|
||||
private ScoringSystem CreateScoringSystem()
|
||||
private CategoryDefinitionSO yachtCategory;
|
||||
private CategoryDefinitionSO onesCategory;
|
||||
private CategoryDefinitionSO twosCategory;
|
||||
private CategoryDefinitionSO chanceCategory;
|
||||
private CategoryCatalogSO catalog;
|
||||
private DieDefinitionSO standardDie;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
var go = new GameObject("ScoringSystem");
|
||||
return go.AddComponent<ScoringSystem>();
|
||||
standardDie = DieDefinitionSO.CreateForTest<StandardDieSO>("d6", "d6");
|
||||
|
||||
yachtCategory = NOfAKindCategorySO.CreateForTest("yacht", "Яхта", 5, fixedScoreMode: true, score: 50);
|
||||
onesCategory = SumOfValueCategorySO.CreateForTest("ones", "Единицы", 1);
|
||||
twosCategory = SumOfValueCategorySO.CreateForTest("twos", "Двойки", 2);
|
||||
chanceCategory = SumAllCategorySO.CreateForTest("chance", "Шанс");
|
||||
|
||||
catalog = CategoryCatalogSO.CreateForTest(new List<CategoryDefinitionSO>
|
||||
{
|
||||
onesCategory, twosCategory, yachtCategory, chanceCategory
|
||||
});
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
@@ -17,13 +37,35 @@ namespace YachtDice.Tests
|
||||
{
|
||||
foreach (var go in Object.FindObjectsByType<ScoringSystem>(FindObjectsSortMode.None))
|
||||
Object.DestroyImmediate(go.gameObject);
|
||||
|
||||
Object.DestroyImmediate(yachtCategory);
|
||||
Object.DestroyImmediate(onesCategory);
|
||||
Object.DestroyImmediate(twosCategory);
|
||||
Object.DestroyImmediate(chanceCategory);
|
||||
Object.DestroyImmediate(catalog);
|
||||
Object.DestroyImmediate(standardDie);
|
||||
}
|
||||
|
||||
private ScoringSystem CreateScoringSystem()
|
||||
{
|
||||
var go = new GameObject("ScoringSystem");
|
||||
return go.AddComponent<ScoringSystem>();
|
||||
}
|
||||
|
||||
private IReadOnlyList<IDie> CreateDice(params int[] values)
|
||||
{
|
||||
var dice = new DieInstance[values.Length];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
dice[i] = new DieInstance(standardDie, values[i]);
|
||||
return dice;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ScoreCategory_WithNoModifiers_CalculatesBaseOnly()
|
||||
{
|
||||
var system = CreateScoringSystem();
|
||||
var result = system.ScoreCategory(new[] { 6, 6, 6, 6, 6 }, YachtCategory.Yacht);
|
||||
var dice = CreateDice(6, 6, 6, 6, 6);
|
||||
var result = system.ScoreCategory(dice, yachtCategory);
|
||||
|
||||
Assert.AreEqual(50, result.BaseScore);
|
||||
Assert.AreEqual(0, result.FlatBonus);
|
||||
@@ -35,7 +77,7 @@ namespace YachtDice.Tests
|
||||
public void ScoreCategory_FiresOnCategoryConfirmed()
|
||||
{
|
||||
var system = CreateScoringSystem();
|
||||
YachtCategory firedCategory = (YachtCategory)(-1);
|
||||
CategoryDefinitionSO firedCategory = null;
|
||||
ScoreResult firedResult = default;
|
||||
|
||||
system.OnCategoryConfirmed += (cat, res) =>
|
||||
@@ -44,9 +86,10 @@ namespace YachtDice.Tests
|
||||
firedResult = res;
|
||||
};
|
||||
|
||||
system.ScoreCategory(new[] { 1, 1, 1, 1, 1 }, YachtCategory.Ones);
|
||||
var dice = CreateDice(1, 1, 1, 1, 1);
|
||||
system.ScoreCategory(dice, onesCategory);
|
||||
|
||||
Assert.AreEqual(YachtCategory.Ones, firedCategory);
|
||||
Assert.AreEqual(onesCategory, firedCategory);
|
||||
Assert.AreEqual(5, firedResult.BaseScore);
|
||||
}
|
||||
|
||||
@@ -54,17 +97,19 @@ namespace YachtDice.Tests
|
||||
public void ScoreCategory_PreventsDuplicateCategory()
|
||||
{
|
||||
var system = CreateScoringSystem();
|
||||
system.ScoreCategory(new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var dice = CreateDice(1, 2, 3, 4, 5);
|
||||
system.ScoreCategory(dice, chanceCategory);
|
||||
|
||||
Assert.Throws<System.InvalidOperationException>(() =>
|
||||
system.ScoreCategory(new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance));
|
||||
system.ScoreCategory(dice, chanceCategory));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PreviewScore_WithNoModifiers_CalculatesBaseOnly()
|
||||
{
|
||||
var system = CreateScoringSystem();
|
||||
var result = system.PreviewScore(new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance);
|
||||
var dice = CreateDice(1, 2, 3, 4, 5);
|
||||
var result = system.PreviewScore(dice, chanceCategory);
|
||||
|
||||
Assert.AreEqual(15, result.BaseScore);
|
||||
Assert.AreEqual(0, result.FlatBonus);
|
||||
@@ -75,8 +120,8 @@ namespace YachtDice.Tests
|
||||
public void TotalScore_SumsAllScoredCategories()
|
||||
{
|
||||
var system = CreateScoringSystem();
|
||||
system.ScoreCategory(new[] { 1, 1, 1, 1, 1 }, YachtCategory.Ones);
|
||||
system.ScoreCategory(new[] { 2, 2, 2, 2, 2 }, YachtCategory.Twos);
|
||||
system.ScoreCategory(CreateDice(1, 1, 1, 1, 1), onesCategory);
|
||||
system.ScoreCategory(CreateDice(2, 2, 2, 2, 2), twosCategory);
|
||||
|
||||
Assert.AreEqual(15, system.TotalScore); // 5 + 10
|
||||
}
|
||||
@@ -85,12 +130,84 @@ namespace YachtDice.Tests
|
||||
public void ResetScorecard_ClearsAll()
|
||||
{
|
||||
var system = CreateScoringSystem();
|
||||
system.ScoreCategory(new[] { 1, 1, 1, 1, 1 }, YachtCategory.Ones);
|
||||
system.ScoreCategory(CreateDice(1, 1, 1, 1, 1), onesCategory);
|
||||
|
||||
system.ResetScorecard();
|
||||
|
||||
Assert.AreEqual(0, system.TotalScore);
|
||||
Assert.IsFalse(system.IsCategoryUsed(YachtCategory.Ones));
|
||||
Assert.IsFalse(system.IsCategoryUsed(onesCategory));
|
||||
}
|
||||
|
||||
// ── Category SO Unit Tests ──────────────────────────────────
|
||||
|
||||
[Test]
|
||||
public void SumOfValueCategory_SumsCorrectly()
|
||||
{
|
||||
var dice = CreateDice(3, 3, 3, 1, 2);
|
||||
var cat = SumOfValueCategorySO.CreateForTest("threes", "Тройки", 3);
|
||||
|
||||
Assert.AreEqual(9, cat.Calculate(dice));
|
||||
|
||||
Object.DestroyImmediate(cat);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NOfAKindCategory_ThreeOfAKind_ReturnsSumOrZero()
|
||||
{
|
||||
var cat = NOfAKindCategorySO.CreateForTest("three_of_a_kind", "Тройка", 3);
|
||||
|
||||
Assert.AreEqual(17, cat.Calculate(CreateDice(4, 4, 4, 3, 2))); // sum = 17
|
||||
Assert.AreEqual(0, cat.Calculate(CreateDice(1, 2, 3, 4, 5))); // no 3-of-a-kind
|
||||
|
||||
Object.DestroyImmediate(cat);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NOfAKindCategory_Yacht_ReturnsFixedScore()
|
||||
{
|
||||
Assert.AreEqual(50, yachtCategory.Calculate(CreateDice(6, 6, 6, 6, 6)));
|
||||
Assert.AreEqual(0, yachtCategory.Calculate(CreateDice(6, 6, 6, 6, 1)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FullHouseCategory_CalculatesCorrectly()
|
||||
{
|
||||
var cat = FullHouseCategorySO.CreateForTest("fh", "Фулл-хаус", 25);
|
||||
|
||||
Assert.AreEqual(25, cat.Calculate(CreateDice(3, 3, 3, 2, 2)));
|
||||
Assert.AreEqual(0, cat.Calculate(CreateDice(3, 3, 3, 3, 2)));
|
||||
|
||||
Object.DestroyImmediate(cat);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StraightCategory_SmallStraight()
|
||||
{
|
||||
var cat = StraightCategorySO.CreateForTest("ss", "Малый стрит", 4, 30);
|
||||
|
||||
Assert.AreEqual(30, cat.Calculate(CreateDice(1, 2, 3, 4, 6)));
|
||||
Assert.AreEqual(0, cat.Calculate(CreateDice(1, 2, 3, 5, 6)));
|
||||
|
||||
Object.DestroyImmediate(cat);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StraightCategory_LargeStraight()
|
||||
{
|
||||
var cat = StraightCategorySO.CreateForTest("ls", "Большой стрит", 5, 40);
|
||||
|
||||
Assert.AreEqual(40, cat.Calculate(CreateDice(1, 2, 3, 4, 5)));
|
||||
Assert.AreEqual(40, cat.Calculate(CreateDice(2, 3, 4, 5, 6)));
|
||||
Assert.AreEqual(0, cat.Calculate(CreateDice(1, 2, 3, 4, 6)));
|
||||
|
||||
Object.DestroyImmediate(cat);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SumAllCategory_SumsEverything()
|
||||
{
|
||||
Assert.AreEqual(15, chanceCategory.Calculate(CreateDice(1, 2, 3, 4, 5)));
|
||||
Assert.AreEqual(30, chanceCategory.Calculate(CreateDice(6, 6, 6, 6, 6)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user