package logic import ( "testing" "wuziqi-server/characters" "wuziqi-server/core" ) // ============================================================ // 道具逻辑测试 (11个道具) // ============================================================ // 1. 医疗包:恢复1点血量,可以解除中毒效果 func TestItem_Medkit_Basic(t *testing.T) { RunScenario(t, GameScenario{ Name: "医疗包-基础恢复", Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 3}, // MaxHP 会自动设置为4 }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "medkit"}, }, Actions: []GameAction{ {Type: "damage", PlayerID: "p1", Value: 1}, // 先扣1血到2 {Type: "move", PlayerID: "p1", Value: 0}, // 使用医疗包恢复到3 }, Checks: []ScenarioCheck{ {PlayerID: "p1", Field: "hp", Expected: 3, Message: "医疗包应该恢复1点血量"}, }, }) } func TestItem_Medkit_CurePoison(t *testing.T) { RunScenario(t, GameScenario{ Name: "医疗包-解除中毒", Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4, Poisoned: true}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "medkit"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "p1", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "p1", Field: "poisoned", Expected: false, Message: "医疗包应该解除中毒"}, }, }) } func TestItem_Medkit_ElephantCannotUse(t *testing.T) { RunScenario(t, GameScenario{ Name: "医疗包-大象无法使用", Players: []PlayerSetup{ {ID: "elephant", Character: "elephant", HP: 4}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "medkit"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "elephant", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "elephant", Field: "hp", Expected: 4, Message: "大象无法使用医疗包"}, }, }) } // 2. 定时炸弹:踩到后3回合后爆炸,扣除2点血量 func TestItem_BombTimer_Activation(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4}, {ID: "p2", Character: "cat", HP: 3}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "bomb_timer"}, }, }) // p1 踩到定时炸弹 engine.HandleMove(state, "p1", 0) if state.Players["p1"].TimeBombTurns != 3 { t.Errorf("定时炸弹应该设置3回合倒计时,got %d", state.Players["p1"].TimeBombTurns) } } func TestItem_BombTimer_Explosion(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4, TimeBomb: 1}, // 1回合后爆炸 {ID: "p2", Character: "cat", HP: 3}, }, Grid: []CellSetup{}, }) // 推进回合,炸弹应该爆炸 state.CurrentTurnIndex = 1 // 设置为p2,下一个是p1 engine.AdvanceTurn(state) if state.Players["p1"].HP != 2 { t.Errorf("定时炸弹爆炸应该造成2点伤害,HP应该是2,got %d", state.Players["p1"].HP) } } func TestItem_BombTimer_SlothReduction(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "sloth", Character: "sloth", HP: 4, TimeBomb: 1}, {ID: "p2", Character: "cat", HP: 3}, }, Grid: []CellSetup{}, }) state.CurrentTurnIndex = 1 engine.AdvanceTurn(state) if state.Players["sloth"].HP != 3 { t.Errorf("树懒定时炸弹伤害应该减为1,HP应该是3,got %d", state.Players["sloth"].HP) } } // 3. 毒药瓶:随机让一个存活玩家中毒,每2回合扣1血 func TestItem_Poison_BasicEffect(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4}, {ID: "p2", Character: "monkey", HP: 4}, // 非树懒 }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "poison"}, }, }) engine.HandleMove(state, "p1", 0) // p2 应该中毒(只有一个非使用者目标) if !state.Players["p2"].Poisoned { t.Error("毒药应该使目标中毒") } } func TestItem_Poison_DamagePerTwoTurns(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4, Poisoned: true}, {ID: "p2", Character: "cat", HP: 3}, }, Grid: []CellSetup{}, }) // 第1回合:PoisonSteps=1,不扣血 state.CurrentTurnIndex = 1 engine.AdvanceTurn(state) if state.Players["p1"].HP != 4 { t.Errorf("第1回合不应该扣血,got HP=%d", state.Players["p1"].HP) } // 第2回合:PoisonSteps=2,扣1血 state.CurrentTurnIndex = 1 engine.AdvanceTurn(state) if state.Players["p1"].HP != 3 { t.Errorf("第2回合应该扣1血,got HP=%d", state.Players["p1"].HP) } } func TestItem_Poison_SlothImmune(t *testing.T) { RunScenario(t, GameScenario{ Name: "毒药-树懒免疫", Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4}, {ID: "sloth", Character: "sloth", HP: 4}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "poison"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "p1", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "sloth", Field: "poisoned", Expected: false, Message: "树懒应该免疫毒药"}, }, }) } // 4. 护盾:免疫1次伤害 func TestItem_Shield_BlockDamage(t *testing.T) { RunScenario(t, GameScenario{ Name: "护盾-抵挡伤害", Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4, Shield: true}, }, Grid: []CellSetup{ {Index: 0, Type: "bomb"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "p1", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "p1", Field: "hp", Expected: 4, Message: "护盾应该抵挡炸弹伤害"}, {PlayerID: "p1", Field: "shield", Expected: false, Message: "护盾应该被消耗"}, }, }) } // 5. 好人卡:跳过回合+护盾 func TestItem_Skip_Effect(t *testing.T) { RunScenario(t, GameScenario{ Name: "好人卡-跳过回合并获得护盾", Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4}, {ID: "p2", Character: "cat", HP: 4}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "skip"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "p1", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "p1", Field: "skip_turn", Expected: true, Message: "应该设置跳过回合"}, {PlayerID: "p1", Field: "shield", Expected: true, Message: "应该获得护盾"}, {PlayerID: "p2", Field: "current_turn", Expected: true, Message: "回合应该切换到p2"}, }, }) } func TestItem_Skip_ElephantCanUse(t *testing.T) { RunScenario(t, GameScenario{ Name: "好人卡-大象现在可以使用", Players: []PlayerSetup{ {ID: "elephant", Character: "elephant", HP: 5}, {ID: "p2", Character: "dog", HP: 4}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "skip"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "elephant", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "elephant", Field: "skip_turn", Expected: true, Message: "大象现在应该可以使用好人卡"}, {PlayerID: "elephant", Field: "shield", Expected: true, Message: "大象现在应该获得护盾"}, }, }) } // 6. 放大镜:透视一个随机格子 func TestItem_Magnifier_RevealCell(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "magnifier"}, {Index: 5, Type: "bomb"}, // 未揭示的炸弹 }, }) engine.HandleMove(state, "p1", 0) if len(state.Players["p1"].RevealedCells) == 0 { t.Error("放大镜应该揭示一个格子") } } // 7. 飞刀:对随机敌人造成1点伤害 func TestItem_Knife_BasicDamage(t *testing.T) { RunScenario(t, GameScenario{ Name: "飞刀-基础伤害", Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4}, {ID: "p2", Character: "monkey", HP: 4}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "knife"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "p1", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "p2", Field: "hp", Expected: 3, Message: "飞刀应该造成1点伤害"}, {PlayerID: "p1", Field: "hp", Expected: 4, Message: "使用者不受伤害"}, }, }) } func TestItem_Knife_TigerAOE(t *testing.T) { RunScenario(t, GameScenario{ Name: "飞刀-老虎在场变全体2点伤害", Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4}, {ID: "p2", Character: "monkey", HP: 4}, {ID: "tiger", Character: "tiger", HP: 4}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "knife"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "p1", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "p2", Field: "hp", Expected: 2, Message: "老虎在场,飞刀应该造成2点伤害"}, {PlayerID: "tiger", Field: "hp", Expected: 2, Message: "老虎自己也受2点伤害"}, {PlayerID: "p1", Field: "hp", Expected: 4, Message: "使用者不受伤害"}, }, }) } // 8. 复活甲:免疫一次死亡,保留1点血量 func TestItem_Revive_SaveFromDeath(t *testing.T) { RunScenario(t, GameScenario{ Name: "复活甲-免疫死亡", Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 1, Revive: true}, }, Grid: []CellSetup{ {Index: 0, Type: "bomb"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "p1", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "p1", Field: "hp", Expected: 1, Message: "复活甲应该保留1点血量"}, {PlayerID: "p1", Field: "alive", Expected: true, Message: "玩家应该存活"}, {PlayerID: "p1", Field: "revive", Expected: false, Message: "复活甲应该被消耗"}, }, }) } func TestItem_Revive_ElephantCannotUse(t *testing.T) { RunScenario(t, GameScenario{ Name: "复活甲-大象无法使用", Players: []PlayerSetup{ {ID: "elephant", Character: "elephant", HP: 5}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "revive"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "elephant", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "elephant", Field: "revive", Expected: false, Message: "大象无法使用复活甲"}, }, }) } // 9. 闪电:对所有玩家造成1点伤害 func TestItem_Lightning_AllDamage(t *testing.T) { RunScenario(t, GameScenario{ Name: "闪电-全体伤害", Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4}, {ID: "p2", Character: "monkey", HP: 4}, {ID: "p3", Character: "tiger", HP: 4}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "lightning"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "p1", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "p1", Field: "hp", Expected: 3, Message: "闪电对使用者也造成伤害"}, {PlayerID: "p2", Field: "hp", Expected: 3, Message: "闪电对p2造成伤害"}, {PlayerID: "p3", Field: "hp", Expected: 3, Message: "闪电对p3造成伤害"}, }, }) } // 10. 宝箱:游戏结束后获得奖励 func TestItem_Chest_Counter(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "chest"}, }, }) engine.HandleMove(state, "p1", 0) if state.Players["p1"].ChestCount != 1 { t.Errorf("宝箱计数应该是1,got %d", state.Players["p1"].ChestCount) } } // 11. 诅咒:下次受伤翻倍 func TestItem_Curse_DoubleDamage(t *testing.T) { RunScenario(t, GameScenario{ Name: "诅咒-伤害翻倍", Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4, Curse: true}, }, Grid: []CellSetup{ {Index: 0, Type: "bomb"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "p1", Value: 0}, // 炸弹2伤害 * 2 = 4 }, Checks: []ScenarioCheck{ {PlayerID: "p1", Field: "hp", Expected: 0, Message: "诅咒应该让伤害翻倍"}, {PlayerID: "p1", Field: "curse", Expected: false, Message: "诅咒应该被消耗"}, }, }) } func TestItem_Curse_CatIgnores(t *testing.T) { RunScenario(t, GameScenario{ Name: "诅咒-猫咪无视翻倍", Players: []PlayerSetup{ {ID: "cat", Character: "cat", HP: 3, Curse: true}, }, Grid: []CellSetup{ {Index: 0, Type: "bomb"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "cat", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "cat", Field: "hp", Expected: 2, Message: "猫咪诅咒也只受1点伤害"}, }, }) } // ============================================================ // 角色逻辑测试 (8个角色) // ============================================================ // 1. 大象:5点血,无法使用好人卡/医疗包/复活甲 func TestCharacter_Elephant_HP(t *testing.T) { charMgr := characters.NewCharacterManager(nil) hp := charMgr.GetInitialHP("elephant", 4) if hp != 5 { t.Errorf("大象初始血量应该是5,got %d", hp) } } func TestCharacter_Elephant_ItemRestrictions(t *testing.T) { // 已在上面的道具测试中覆盖 t.Log("大象道具限制已在道具测试中覆盖") } // 2. 猫咪:3点血,所有伤害强制为1(包括诅咒) func TestCharacter_Cat_HP(t *testing.T) { charMgr := characters.NewCharacterManager(nil) hp := charMgr.GetInitialHP("cat", 4) if hp != 3 { t.Errorf("猫咪初始血量应该是3,got %d", hp) } } func TestCharacter_Cat_DamageCap(t *testing.T) { RunScenario(t, GameScenario{ Name: "猫咪-伤害限制为1", Players: []PlayerSetup{ {ID: "cat", Character: "cat", HP: 3}, }, Grid: []CellSetup{ {Index: 0, Type: "bomb"}, // 2点伤害 }, Actions: []GameAction{ {Type: "move", PlayerID: "cat", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "cat", Field: "hp", Expected: 2, Message: "猫咪受到任何伤害都只扣1点"}, }, }) } // 3. 汪汪:4(6)人赛每6(9)回合触发放大镜 func TestCharacter_Dog_MagnifierAbility(t *testing.T) { // 由于狗狗技能依赖于 GlobalTurnCount % 6 == 0, // 且只有狗狗自己行动时才会触发,我们直接测试逻辑 engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "dog", Character: "dog", HP: 4}, }, Grid: []CellSetup{}, }) // 创建足够的格子 state.Grid = make([]*core.GridCell, 100) for i := range state.Grid { state.Grid[i] = &core.GridCell{Type: "empty", Revealed: false} } state.Grid[99].Type = "bomb" // 放一个未揭示的炸弹 // 狗狗独立步数设为 5,下一次操作将使其变为 6 state.Players["dog"].DogStepCount = 5 // 狗狗操作,DogStepCount 变为 6,6 % 6 == 0,应该触发 engine.HandleMove(state, "dog", 0) t.Logf("操作后 DogStepCount=%d, RevealedCells=%d", state.Players["dog"].DogStepCount, len(state.Players["dog"].RevealedCells)) // 检查是否触发了放大镜 if len(state.Players["dog"].RevealedCells) == 0 { t.Errorf("狗狗应该在 DogStepCount=6 时触发放大镜能力") } else { t.Logf("狗狗放大镜触发成功,揭示了 %d 个格子", len(state.Players["dog"].RevealedCells)) } } // 4. 吉吉国王(猴子):每回合15%概率获得香蕉恢复1血,最多2次 func TestCharacter_Monkey_BananaAbility(t *testing.T) { // 由于是概率性的,我们测试计数器限制 engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "monkey", Character: "monkey", HP: 2}, }, Grid: []CellSetup{}, }) // 手动设置已触发2次 state.Players["monkey"].MonkeyBananaCount = 2 // 模拟移动,不应该再触发 for i := 0; i < 10; i++ { state.Grid = append(state.Grid, &core.GridCell{Type: "empty", Revealed: false}) } initialHP := state.Players["monkey"].HP engine.HandleMove(state, "monkey", 0) // 由于已经触发2次,不应该再恢复 if state.Players["monkey"].MonkeyBananaCount > 2 { t.Error("猴子香蕉能力最多触发2次") } t.Logf("猴子HP变化: %d -> %d (计数: %d)", initialHP, state.Players["monkey"].HP, state.Players["monkey"].MonkeyBananaCount) } // 5. 坤坤(小鸡):受伤8%概率获得道具,最多2次 func TestCharacter_Chicken_ItemOnDamage(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "chicken", Character: "chicken", HP: 4}, }, Grid: []CellSetup{}, }) // 手动设置已触发2次 state.Players["chicken"].ChickenItemCount = 2 // 造成伤害,不应该再触发 engine.ApplyDamage(state, state.Players["chicken"], 1, false) if state.Players["chicken"].ChickenItemCount > 2 { t.Error("小鸡道具能力最多触发2次") } } // 6. 懒懒(树懒):免疫毒药,炸弹伤害减半 func TestCharacter_Sloth_PoisonImmune(t *testing.T) { // 已在毒药测试中覆盖 t.Log("树懒毒药免疫已在道具测试中覆盖") } func TestCharacter_Sloth_BombDamageReduction(t *testing.T) { RunScenario(t, GameScenario{ Name: "树懒-炸弹伤害减半", Players: []PlayerSetup{ {ID: "sloth", Character: "sloth", HP: 4}, }, Grid: []CellSetup{ {Index: 0, Type: "bomb"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "sloth", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "sloth", Field: "hp", Expected: 3, Message: "树懒踩炸弹只受1点伤害"}, }, }) } // 7. 河马:无法拾取道具,55%概率免疫死亡(最多1次) func TestCharacter_Hippo_CannotPickItems(t *testing.T) { RunScenario(t, GameScenario{ Name: "河马-无法拾取道具", Players: []PlayerSetup{ {ID: "hippo", Character: "hippo", HP: 4}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "shield"}, }, Actions: []GameAction{ {Type: "move", PlayerID: "hippo", Value: 0}, }, Checks: []ScenarioCheck{ {PlayerID: "hippo", Field: "shield", Expected: false, Message: "河马无法拾取道具"}, }, }) } func TestCharacter_Hippo_DeathResistOnce(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "hippo", Character: "hippo", HP: 1}, }, Grid: []CellSetup{}, }) // 模拟多次死亡尝试 deathResistTriggered := 0 for i := 0; i < 100; i++ { // 重置状态 state.Players["hippo"].HP = 1 state.Players["hippo"].HippoDeathImmune = false engine.ApplyDamage(state, state.Players["hippo"], 10, false) if state.Players["hippo"].HP > 0 { deathResistTriggered++ if state.Players["hippo"].HippoDeathImmune != true { t.Error("河马死亡抵抗标志应该被设置") } } } t.Logf("河马死亡抵抗触发次数: %d/100 (预期约55%%)", deathResistTriggered) // 测试已触发后不再生效 state.Players["hippo"].HP = 1 state.Players["hippo"].HippoDeathImmune = true engine.ApplyDamage(state, state.Players["hippo"], 10, false) if state.Players["hippo"].HP > 0 { t.Error("河马死亡抵抗应该只能触发一次") } } // 8. 老虎:飞刀变全体2点伤害 func TestCharacter_Tiger_KnifeEnhance(t *testing.T) { // 已在飞刀测试中覆盖 t.Log("老虎飞刀增强已在道具测试中覆盖") } // ============================================================ // 游戏流程测试 // ============================================================ func TestGameFlow_GameOver(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4}, {ID: "p2", Character: "cat", HP: 1}, }, Grid: []CellSetup{ {Index: 0, Type: "bomb"}, }, }) // p2 踩炸弹死亡 state.CurrentTurnIndex = 1 engine.HandleMove(state, "p2", 0) if state.WinnerID != "p1" { t.Errorf("p1应该获胜,got winner=%s", state.WinnerID) } if state.GameStarted { t.Error("游戏应该结束") } } func TestGameFlow_DrawLastManStanding(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 1}, {ID: "p2", Character: "cat", HP: 1}, }, Grid: []CellSetup{ {Index: 0, Type: "item", ItemID: "lightning"}, // 闪电对所有人造成1点伤害 }, }) // p1 使用闪电,所有人同时死亡 // 按照逻辑,闪电会依次调用 ApplyDamage 给 p1, p2 // p2 是最后一个被处理的(也是最后一个 HP 归零的),所以应该是 LastDeadPlayerID engine.HandleMove(state, "p1", 0) if state.WinnerID != "p2" { t.Errorf("平局时由于p2是处理列表最后一个死亡的,应该获胜。got winner=%s", state.WinnerID) } if state.GameStarted { t.Error("游戏应该结束") } } func TestGameFlow_SafeAreaExpansion(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4}, }, Grid: []CellSetup{}, }) // 创建3x3网格,右下角有炸弹 state.GridSize = 3 state.Grid = make([]*core.GridCell, 9) for i := range state.Grid { state.Grid[i] = &core.GridCell{Type: "empty", Revealed: false, NeighborBombs: 0} } state.Grid[8].Type = "bomb" state.Grid[4].NeighborBombs = 1 // 中间格子有1个邻居炸弹 state.Grid[5].NeighborBombs = 1 state.Grid[7].NeighborBombs = 1 // 点击左上角 engine.HandleMove(state, "p1", 0) // 统计揭示的格子 revealedCount := 0 for _, cell := range state.Grid { if cell.Revealed { revealedCount++ } } t.Logf("揭示的格子数: %d", revealedCount) if revealedCount < 5 { t.Errorf("安全区扩散应该揭示更多格子,只揭示了 %d 个", revealedCount) } if state.Grid[8].Revealed { t.Error("炸弹不应该被揭示") } } func TestGameFlow_TurnAdvance(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4}, {ID: "p2", Character: "cat", HP: 3}, {ID: "p3", Character: "tiger", HP: 4}, }, Grid: []CellSetup{ {Index: 0, Type: "empty"}, }, }) // p1 移动 engine.HandleMove(state, "p1", 0) // 回合应该推进到 p2 if state.TurnOrder[state.CurrentTurnIndex] != "p2" { t.Errorf("回合应该推进到p2,当前是 %s", state.TurnOrder[state.CurrentTurnIndex]) } } func TestGameFlow_SkipTurn(t *testing.T) { engine, state := createScenarioState(GameScenario{ Players: []PlayerSetup{ {ID: "p1", Character: "dog", HP: 4}, {ID: "p2", Character: "cat", HP: 3, SkipTurn: true}, {ID: "p3", Character: "tiger", HP: 4}, }, Grid: []CellSetup{}, }) // 从p1推进,应该跳过p2到p3 state.CurrentTurnIndex = 0 engine.AdvanceTurn(state) if state.TurnOrder[state.CurrentTurnIndex] != "p3" { t.Errorf("应该跳过p2到p3,当前是 %s", state.TurnOrder[state.CurrentTurnIndex]) } }