The TestSpawnPriority_LowerIDBreaksTie test declared core1 but never referenced it, causing a compile error. Added an assertion that core1.LastSpawnedTurn remains 0 (confirming it didn't spawn). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
611 lines
16 KiB
Go
611 lines
16 KiB
Go
package engine
|
|
|
|
import (
|
|
"math/rand"
|
|
"testing"
|
|
)
|
|
|
|
func newTestGameState() *GameState {
|
|
config := DefaultConfig()
|
|
config.Rows = 20
|
|
config.Cols = 20
|
|
rng := rand.New(rand.NewSource(42))
|
|
return NewGameState(config, rng)
|
|
}
|
|
|
|
func TestExecuteMoves(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
p1 := gs.AddPlayer()
|
|
|
|
bot0 := gs.SpawnBot(p0.ID, Position{10, 10})
|
|
bot1 := gs.SpawnBot(p1.ID, Position{5, 5})
|
|
|
|
// Submit moves
|
|
gs.SubmitMove(bot0.Position, DirN) // 10,10 -> 9,10
|
|
gs.SubmitMove(bot1.Position, DirE) // 5,5 -> 5,6
|
|
|
|
gs.executeMoves()
|
|
|
|
// Verify positions
|
|
if bot0.Position != (Position{9, 10}) {
|
|
t.Errorf("bot0 position = %v, want {9,10}", bot0.Position)
|
|
}
|
|
if bot1.Position != (Position{5, 6}) {
|
|
t.Errorf("bot1 position = %v, want {5,6}", bot1.Position)
|
|
}
|
|
}
|
|
|
|
func TestExecuteMovesIntoWall(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
|
|
// Place a wall
|
|
gs.Grid.Set(9, 10, TileWall)
|
|
|
|
bot := gs.SpawnBot(p0.ID, Position{10, 10})
|
|
gs.SubmitMove(bot.Position, DirN) // Would go to 9,10 which is a wall
|
|
|
|
gs.executeMoves()
|
|
|
|
// Bot should stay in place
|
|
if bot.Position != (Position{10, 10}) {
|
|
t.Errorf("bot position = %v, want {10,10} (blocked by wall)", bot.Position)
|
|
}
|
|
}
|
|
|
|
func TestExecuteMovesWrap(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
|
|
bot := gs.SpawnBot(p0.ID, Position{0, 0})
|
|
gs.SubmitMove(bot.Position, DirN) // Should wrap to 19,0
|
|
|
|
gs.executeMoves()
|
|
|
|
if bot.Position != (Position{19, 0}) {
|
|
t.Errorf("bot position = %v, want {19,0} (wrapped)", bot.Position)
|
|
}
|
|
}
|
|
|
|
func TestExecuteMovesSelfCollision(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
|
|
// Two bots from same player trying to move to same position
|
|
bot0 := gs.SpawnBot(p0.ID, Position{10, 10})
|
|
bot1 := gs.SpawnBot(p0.ID, Position{10, 12})
|
|
|
|
gs.SubmitMove(bot0.Position, DirE) // 10,10 -> 10,11
|
|
gs.SubmitMove(bot1.Position, DirW) // 10,12 -> 10,11
|
|
|
|
gs.executeMoves()
|
|
|
|
// Both should be dead
|
|
if bot0.Alive {
|
|
t.Error("bot0 should be dead from self-collision")
|
|
}
|
|
if bot1.Alive {
|
|
t.Error("bot1 should be dead from self-collision")
|
|
}
|
|
}
|
|
|
|
func TestExecuteCombat1v1(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
p1 := gs.AddPlayer()
|
|
|
|
// Two bots adjacent - both should die (1v1 = mutual destruction)
|
|
bot0 := gs.SpawnBot(p0.ID, Position{10, 10})
|
|
bot1 := gs.SpawnBot(p1.ID, Position{10, 11})
|
|
|
|
gs.executeCombat()
|
|
|
|
// Both should be dead (1 enemy each, equal counts)
|
|
if bot0.Alive {
|
|
t.Error("bot0 should be dead in 1v1")
|
|
}
|
|
if bot1.Alive {
|
|
t.Error("bot1 should be dead in 1v1")
|
|
}
|
|
}
|
|
|
|
func TestExecuteCombat2v1(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
p1 := gs.AddPlayer()
|
|
|
|
// Two bots vs one - the lone bot should die
|
|
bot0 := gs.SpawnBot(p0.ID, Position{10, 10})
|
|
bot0b := gs.SpawnBot(p0.ID, Position{10, 11}) // Adjacent to bot1
|
|
bot1 := gs.SpawnBot(p1.ID, Position{10, 12})
|
|
|
|
gs.executeCombat()
|
|
|
|
// bot1 should die (1 enemy vs 2 enemies)
|
|
if bot1.Alive {
|
|
t.Error("bot1 should be dead in 2v1")
|
|
}
|
|
// bot0 and bot0b should survive (2 enemies vs 1 enemy)
|
|
if !bot0.Alive || !bot0b.Alive {
|
|
t.Error("bot0 and bot0b should survive 2v1")
|
|
}
|
|
}
|
|
|
|
func TestExecuteCombatFormation(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
p1 := gs.AddPlayer()
|
|
|
|
// Tight formation (3 bots) vs scattered (3 bots)
|
|
// Formation: 3 bots in a line
|
|
formation := []*Bot{
|
|
gs.SpawnBot(p0.ID, Position{10, 10}),
|
|
gs.SpawnBot(p0.ID, Position{10, 11}),
|
|
gs.SpawnBot(p0.ID, Position{10, 12}),
|
|
}
|
|
|
|
// Scattered: 3 bots spread out (only one in attack range of formation)
|
|
scattered := []*Bot{
|
|
gs.SpawnBot(p1.ID, Position{10, 13}), // In range of formation
|
|
gs.SpawnBot(p1.ID, Position{5, 5}), // Far away
|
|
gs.SpawnBot(p1.ID, Position{15, 15}), // Far away
|
|
}
|
|
|
|
gs.executeCombat()
|
|
|
|
// The scattered bot in range (10,13) faces 3 enemies
|
|
// Each formation bot faces 1 enemy
|
|
// Formation bots: 1 enemy each
|
|
// Scattered bot: 3 enemies
|
|
// Scattered bot dies (3 >= 1)
|
|
// Formation bots survive (1 < 3)
|
|
|
|
if scattered[0].Alive {
|
|
t.Error("scattered bot in range should die")
|
|
}
|
|
for i, b := range formation {
|
|
if !b.Alive {
|
|
t.Errorf("formation bot %d should survive", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestExecuteCapture(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
p1 := gs.AddPlayer()
|
|
|
|
// Player 0 has a core
|
|
core := gs.AddCore(p0.ID, Position{10, 10})
|
|
|
|
// Player 1's bot moves onto the core
|
|
bot1 := gs.SpawnBot(p1.ID, Position{9, 10})
|
|
gs.SubmitMove(bot1.Position, DirS) // Move to 10,10 (the core)
|
|
gs.executeMoves()
|
|
|
|
// Core is undefended (no p0 bot on it)
|
|
gs.executeCaptures()
|
|
|
|
// Core should be razed
|
|
if core.Active {
|
|
t.Error("core should be razed after capture")
|
|
}
|
|
// Scoring: p1 +2 (p1 didn't start with a core, so score was 0)
|
|
// p0: started with 1 point (from core), loses 1 point = 0
|
|
if gs.Players[p1.ID].Score != 2 { // 0 (starting) + 2 (capture)
|
|
t.Errorf("p1 score = %d, want 2", gs.Players[p1.ID].Score)
|
|
}
|
|
if gs.Players[p0.ID].Score != 0 { // 1 (starting) - 1 (capture)
|
|
t.Errorf("p0 score = %d, want 0", gs.Players[p0.ID].Score)
|
|
}
|
|
}
|
|
|
|
func TestExecuteCaptureDefended(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
p1 := gs.AddPlayer()
|
|
|
|
// Player 0 has a core with a defending bot
|
|
core := gs.AddCore(p0.ID, Position{10, 10})
|
|
defender := gs.SpawnBot(p0.ID, Position{10, 10}) // Defending
|
|
|
|
// Player 1's bot moves onto the core
|
|
attacker := gs.SpawnBot(p1.ID, Position{9, 10})
|
|
gs.SubmitMove(attacker.Position, DirS)
|
|
gs.executeMoves()
|
|
|
|
// Combat resolves first - both bots on same tile
|
|
// Actually, in our implementation, two enemy bots on same tile is handled in combat
|
|
// Let me reconsider: if both bots end up on the same tile, combat handles it
|
|
// For this test, let's have the attacker adjacent but not on the core
|
|
|
|
gs.ClearTurnState()
|
|
attacker.Position = Position{10, 11} // Adjacent to core
|
|
|
|
gs.executeCaptures()
|
|
|
|
// Core should still be active (defended)
|
|
if !core.Active {
|
|
t.Error("core should not be captured when defended")
|
|
}
|
|
if !defender.Alive {
|
|
t.Error("defender should still be alive")
|
|
}
|
|
}
|
|
|
|
func TestExecuteCollection(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
|
|
// Place energy
|
|
en := gs.AddEnergyNode(Position{10, 10})
|
|
en.HasEnergy = true
|
|
|
|
// Bot adjacent to energy
|
|
_ = gs.SpawnBot(p0.ID, Position{10, 11})
|
|
|
|
gs.executeCollection()
|
|
|
|
// Player should have collected energy
|
|
if gs.Players[p0.ID].Energy != 1 {
|
|
t.Errorf("player energy = %d, want 1", gs.Players[p0.ID].Energy)
|
|
}
|
|
if en.HasEnergy {
|
|
t.Error("energy should be collected")
|
|
}
|
|
}
|
|
|
|
func TestExecuteCollectionContested(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
p1 := gs.AddPlayer()
|
|
|
|
// Place energy
|
|
en := gs.AddEnergyNode(Position{10, 10})
|
|
en.HasEnergy = true
|
|
|
|
// Bots from both players adjacent
|
|
gs.SpawnBot(p0.ID, Position{10, 11})
|
|
gs.SpawnBot(p1.ID, Position{10, 9})
|
|
|
|
gs.executeCollection()
|
|
|
|
// Energy should be destroyed (contested)
|
|
if gs.Players[p0.ID].Energy != 0 || gs.Players[p1.ID].Energy != 0 {
|
|
t.Error("no player should collect contested energy")
|
|
}
|
|
if en.HasEnergy {
|
|
t.Error("contested energy should be destroyed")
|
|
}
|
|
}
|
|
|
|
func TestExecuteSpawn(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
|
|
// Player has a core
|
|
gs.AddCore(p0.ID, Position{10, 10})
|
|
|
|
// Give player enough energy
|
|
gs.Players[p0.ID].Energy = 3
|
|
|
|
gs.executeSpawns()
|
|
|
|
// Player should have spawned a bot at the core
|
|
bots := gs.GetPlayerBots(p0.ID)
|
|
if len(bots) != 1 {
|
|
t.Errorf("player should have 1 bot, got %d", len(bots))
|
|
}
|
|
if bots[0].Position != (Position{10, 10}) {
|
|
t.Errorf("spawned bot position = %v, want {10,10}", bots[0].Position)
|
|
}
|
|
if gs.Players[p0.ID].Energy != 0 {
|
|
t.Errorf("player energy = %d, want 0", gs.Players[p0.ID].Energy)
|
|
}
|
|
}
|
|
|
|
func TestExecuteSpawnOccupiedCore(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
|
|
// Player has a core with a bot already on it
|
|
gs.AddCore(p0.ID, Position{10, 10})
|
|
gs.SpawnBot(p0.ID, Position{10, 10})
|
|
|
|
// Give player enough energy
|
|
gs.Players[p0.ID].Energy = 3
|
|
|
|
gs.executeSpawns()
|
|
|
|
// No spawn should happen (core occupied)
|
|
bots := gs.GetPlayerBots(p0.ID)
|
|
if len(bots) != 1 {
|
|
t.Errorf("player should still have 1 bot, got %d", len(bots))
|
|
}
|
|
if gs.Players[p0.ID].Energy != 3 {
|
|
t.Error("energy should not be spent on occupied core")
|
|
}
|
|
}
|
|
|
|
func TestExecuteEnergyTick(t *testing.T) {
|
|
gs := newTestGameState()
|
|
gs.Config.EnergyInterval = 3
|
|
|
|
// Energy node with tick = 2 (one more turn until spawn)
|
|
en := gs.AddEnergyNode(Position{10, 10})
|
|
en.Tick = 2
|
|
|
|
gs.executeEnergyTick()
|
|
|
|
if !en.HasEnergy {
|
|
t.Error("energy should have spawned")
|
|
}
|
|
if en.Tick != 0 {
|
|
t.Errorf("tick should be 0, got %d", en.Tick)
|
|
}
|
|
}
|
|
|
|
func TestCheckWinConditionsElimination(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
_ = gs.AddPlayer() // p1 - opponent with no bots
|
|
|
|
// Player 0 has bots, player 1 doesn't
|
|
gs.SpawnBot(p0.ID, Position{10, 10})
|
|
|
|
result := gs.checkWinConditions()
|
|
|
|
if result == nil {
|
|
t.Fatal("expected win result")
|
|
}
|
|
if result.Winner != p0.ID {
|
|
t.Errorf("winner = %d, want %d", result.Winner, p0.ID)
|
|
}
|
|
if result.Reason != "elimination" {
|
|
t.Errorf("reason = %s, want elimination", result.Reason)
|
|
}
|
|
}
|
|
|
|
func TestCheckWinConditionsDraw(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
p1 := gs.AddPlayer()
|
|
|
|
// No bots alive for anyone
|
|
bot0 := gs.SpawnBot(p0.ID, Position{10, 10})
|
|
bot1 := gs.SpawnBot(p1.ID, Position{10, 11})
|
|
bot0.Alive = false
|
|
bot1.Alive = false
|
|
|
|
result := gs.checkWinConditions()
|
|
|
|
if result == nil {
|
|
t.Fatal("expected win result")
|
|
}
|
|
if result.Winner != -1 {
|
|
t.Errorf("winner = %d, want -1 (draw)", result.Winner)
|
|
}
|
|
if result.Reason != "draw" {
|
|
t.Errorf("reason = %s, want draw", result.Reason)
|
|
}
|
|
}
|
|
|
|
func TestCheckWinConditionsDominance(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
p1 := gs.AddPlayer()
|
|
|
|
// Player 0 has 9 bots, player 1 has 1 bot = 90% dominance
|
|
for i := 0; i < 9; i++ {
|
|
gs.SpawnBot(p0.ID, Position{Row: i, Col: 0})
|
|
}
|
|
gs.SpawnBot(p1.ID, Position{Row: 15, Col: 15})
|
|
|
|
// Dominance requires 100 consecutive turns at >= 80%
|
|
// First 99 turns should not trigger
|
|
for i := 0; i < 99; i++ {
|
|
result := gs.checkWinConditions()
|
|
if result != nil && result.Reason == "dominance" {
|
|
t.Fatalf("dominance should not trigger at turn %d (only %d consecutive)", i, i+1)
|
|
}
|
|
}
|
|
|
|
// 100th check should trigger dominance
|
|
result := gs.checkWinConditions()
|
|
if result == nil {
|
|
t.Fatal("expected dominance win after 100 consecutive turns")
|
|
}
|
|
if result.Winner != p0.ID {
|
|
t.Errorf("winner = %d, want %d", result.Winner, p0.ID)
|
|
}
|
|
if result.Reason != "dominance" {
|
|
t.Errorf("reason = %s, want dominance", result.Reason)
|
|
}
|
|
}
|
|
|
|
func TestCheckWinConditionsDominanceReset(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
p1 := gs.AddPlayer()
|
|
|
|
// Player 0 has 9 bots, player 1 has 1 = 90% dominance
|
|
bots0 := make([]*Bot, 9)
|
|
for i := 0; i < 9; i++ {
|
|
bots0[i] = gs.SpawnBot(p0.ID, Position{Row: i, Col: 0})
|
|
}
|
|
gs.SpawnBot(p1.ID, Position{Row: 15, Col: 15})
|
|
|
|
// Run 50 turns of dominance
|
|
for i := 0; i < 50; i++ {
|
|
result := gs.checkWinConditions()
|
|
if result != nil && result.Reason == "dominance" {
|
|
t.Fatalf("dominance should not trigger at %d turns", i+1)
|
|
}
|
|
}
|
|
|
|
// Break dominance by killing some p0 bots
|
|
for i := 0; i < 6; i++ {
|
|
gs.KillBot(bots0[i], "test")
|
|
}
|
|
// Now p0 has 3 bots, p1 has 1 = 75% (< 80%)
|
|
|
|
result := gs.checkWinConditions()
|
|
// Should not trigger dominance and counter should reset
|
|
if result != nil && result.Reason == "dominance" {
|
|
t.Error("dominance should not trigger when below 80%")
|
|
}
|
|
if gs.Dominance[p0.ID] != 0 {
|
|
t.Errorf("dominance counter should reset to 0, got %d", gs.Dominance[p0.ID])
|
|
}
|
|
}
|
|
|
|
func TestCheckWinConditionsTurns(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
p1 := gs.AddPlayer()
|
|
|
|
// Both have bots
|
|
gs.SpawnBot(p0.ID, Position{10, 10})
|
|
gs.SpawnBot(p1.ID, Position{5, 5})
|
|
|
|
// Set turn to max
|
|
gs.Turn = gs.Config.MaxTurns
|
|
|
|
// Player 0 has higher score
|
|
gs.Players[p0.ID].Score = 5
|
|
gs.Players[p1.ID].Score = 3
|
|
|
|
result := gs.checkWinConditions()
|
|
|
|
if result == nil {
|
|
t.Fatal("expected win result")
|
|
}
|
|
if result.Winner != p0.ID {
|
|
t.Errorf("winner = %d, want %d (higher score)", result.Winner, p0.ID)
|
|
}
|
|
if result.Reason != "turns" {
|
|
t.Errorf("reason = %s, want turns", result.Reason)
|
|
}
|
|
}
|
|
|
|
func TestSpawnPriority_IdleLongestSpawnsFirst(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
|
|
// Two cores for same player
|
|
core0 := gs.AddCore(p0.ID, Position{5, 5})
|
|
core1 := gs.AddCore(p0.ID, Position{15, 15})
|
|
|
|
// Give core0 a higher lastSpawnedTurn (spawned more recently)
|
|
core0.LastSpawnedTurn = 10
|
|
core1.LastSpawnedTurn = 3
|
|
|
|
// Only enough energy for one spawn
|
|
gs.Players[p0.ID].Energy = 3
|
|
|
|
gs.executeSpawns()
|
|
|
|
// core1 should spawn (idle longer: lastSpawnedTurn=3 < 10)
|
|
bots := gs.GetPlayerBots(p0.ID)
|
|
if len(bots) != 1 {
|
|
t.Fatalf("expected 1 spawned bot, got %d", len(bots))
|
|
}
|
|
if bots[0].Position != core1.Position {
|
|
t.Errorf("spawned at %v, want %v (idle-longest core)", bots[0].Position, core1.Position)
|
|
}
|
|
if core1.LastSpawnedTurn != gs.Turn {
|
|
t.Errorf("core1 LastSpawnedTurn = %d, want %d", core1.LastSpawnedTurn, gs.Turn)
|
|
}
|
|
}
|
|
|
|
func TestSpawnPriority_LowerIDBreaksTie(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
|
|
// Two cores, both idle since turn 0 (equal lastSpawnedTurn)
|
|
core0 := gs.AddCore(p0.ID, Position{5, 5})
|
|
core1 := gs.AddCore(p0.ID, Position{15, 15})
|
|
|
|
// core0 has lower ID (added first)
|
|
|
|
// Only enough energy for one spawn
|
|
gs.Players[p0.ID].Energy = 3
|
|
|
|
gs.executeSpawns()
|
|
|
|
// core0 (lower ID) should spawn first
|
|
bots := gs.GetPlayerBots(p0.ID)
|
|
if len(bots) != 1 {
|
|
t.Fatalf("expected 1 spawned bot, got %d", len(bots))
|
|
}
|
|
if bots[0].Position != core0.Position {
|
|
t.Errorf("spawned at %v, want %v (lower ID tiebreak)", bots[0].Position, core0.Position)
|
|
}
|
|
if core1.LastSpawnedTurn != 0 {
|
|
t.Errorf("core1 LastSpawnedTurn = %d, want 0 (no spawn)", core1.LastSpawnedTurn)
|
|
}
|
|
}
|
|
|
|
func TestSpawnPriority_MultipleEligibleEnoughEnergy(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
|
|
core0 := gs.AddCore(p0.ID, Position{5, 5})
|
|
core1 := gs.AddCore(p0.ID, Position{15, 15})
|
|
|
|
// core1 idle longer
|
|
core0.LastSpawnedTurn = 5
|
|
core1.LastSpawnedTurn = 1
|
|
|
|
// Enough energy for two spawns
|
|
gs.Players[p0.ID].Energy = 6
|
|
|
|
gs.executeSpawns()
|
|
|
|
// Both should spawn (order: core1 first, then core0)
|
|
bots := gs.GetPlayerBots(p0.ID)
|
|
if len(bots) != 2 {
|
|
t.Fatalf("expected 2 spawned bots, got %d", len(bots))
|
|
}
|
|
// First bot spawned is at core1 (idle-longest)
|
|
if bots[0].Position != core1.Position {
|
|
t.Errorf("first spawn at %v, want %v", bots[0].Position, core1.Position)
|
|
}
|
|
if bots[1].Position != core0.Position {
|
|
t.Errorf("second spawn at %v, want %v", bots[1].Position, core0.Position)
|
|
}
|
|
if gs.Players[p0.ID].Energy != 0 {
|
|
t.Errorf("remaining energy = %d, want 0", gs.Players[p0.ID].Energy)
|
|
}
|
|
}
|
|
|
|
func TestSpawnPriority_LastSpawnedTurnUpdatesOnSpawn(t *testing.T) {
|
|
gs := newTestGameState()
|
|
p0 := gs.AddPlayer()
|
|
|
|
core0 := gs.AddCore(p0.ID, Position{5, 5})
|
|
core1 := gs.AddCore(p0.ID, Position{15, 15})
|
|
|
|
// Both start idle
|
|
gs.Players[p0.ID].Energy = 3
|
|
gs.Turn = 5
|
|
gs.executeSpawns()
|
|
|
|
// core0 (lower ID) should have spawned
|
|
if core0.LastSpawnedTurn != 5 {
|
|
t.Errorf("core0 LastSpawnedTurn = %d, want 5", core0.LastSpawnedTurn)
|
|
}
|
|
if core1.LastSpawnedTurn != 0 {
|
|
t.Errorf("core1 LastSpawnedTurn = %d, want 0 (no spawn)", core1.LastSpawnedTurn)
|
|
}
|
|
|
|
// Now give energy again — core1 should spawn (idle since turn 0 < 5)
|
|
gs.Players[p0.ID].Energy = 3
|
|
gs.Turn = 10
|
|
gs.executeSpawns()
|
|
|
|
if core1.LastSpawnedTurn != 10 {
|
|
t.Errorf("core1 LastSpawnedTurn = %d, want 10", core1.LastSpawnedTurn)
|
|
}
|
|
}
|