diff --git a/standard.go b/standard.go index 7773d78..4fd444e 100644 --- a/standard.go +++ b/standard.go @@ -6,15 +6,16 @@ import ( "sort" ) -type StandardRuleset struct{} +type StandardRuleset struct { + FoodSpawnChance int32 // [0, 100] + MinimumFood int32 +} const ( BoardSizeSmall = 7 BoardSizeMedium = 11 BoardSizeLarge = 19 - FoodSpawnChance = 0.15 - SnakeMaxHealth = 100 SnakeStartSize = 3 @@ -173,7 +174,7 @@ func (r *StandardRuleset) placeFoodFixed(b *BoardState) error { } func (r *StandardRuleset) placeFoodRandomly(b *BoardState) error { - return r.spawnFood(b, len(b.Snakes)) + return r.spawnFood(b, int32(len(b.Snakes))) } func (r *StandardRuleset) isKnownBoardSize(b *BoardState) bool { @@ -518,14 +519,17 @@ func (r *StandardRuleset) growSnake(snake *Snake) { } func (r *StandardRuleset) maybeSpawnFood(b *BoardState) error { - if len(b.Food) == 0 || rand.Float32() <= FoodSpawnChance { + numCurrentFood := int32(len(b.Food)) + if numCurrentFood < r.MinimumFood { + return r.spawnFood(b, r.MinimumFood-numCurrentFood) + } else if r.FoodSpawnChance > 0 && int32(rand.Intn(100)) < r.FoodSpawnChance { return r.spawnFood(b, 1) } return nil } -func (r *StandardRuleset) spawnFood(b *BoardState, n int) error { - for i := 0; i < n; i++ { +func (r *StandardRuleset) spawnFood(b *BoardState, n int32) error { + for i := int32(0); i < n; i++ { unoccupiedPoints := r.getUnoccupiedPoints(b, false) if len(unoccupiedPoints) > 0 { newFood := unoccupiedPoints[rand.Intn(len(unoccupiedPoints))] diff --git a/standard_test.go b/standard_test.go index b4aa5d8..37bf9ac 100644 --- a/standard_test.go +++ b/standard_test.go @@ -1828,22 +1828,89 @@ func TestGetEvenUnoccupiedPoints(t *testing.T) { } } -func TestMaybeSpawnFood(t *testing.T) { +func TestMaybeSpawnFoodMinimum(t *testing.T) { + tests := []struct { + MinimumFood int32 + Food []Point + ExpectedFood int + }{ + // Use pre-tested seeds and results + {0, []Point{}, 0}, + {1, []Point{}, 1}, + {9, []Point{}, 9}, + {7, []Point{{4, 5}, {4, 4}, {4, 1}}, 7}, + } + + for _, test := range tests { + r := StandardRuleset{MinimumFood: test.MinimumFood} + b := &BoardState{ + Height: 11, + Width: 11, + Snakes: []Snake{ + {Body: []Point{{1, 0}, {1, 1}}}, + {Body: []Point{{0, 1}, {0, 2}, {0, 3}}}, + }, + Food: test.Food, + } + + err := r.maybeSpawnFood(b) + require.NoError(t, err) + require.Equal(t, test.ExpectedFood, len(b.Food)) + } +} + +func TestMaybeSpawnFoodZeroChance(t *testing.T) { + r := StandardRuleset{FoodSpawnChance: 0} + b := &BoardState{ + Height: 11, + Width: 11, + Snakes: []Snake{ + {Body: []Point{{1, 0}, {1, 1}}}, + {Body: []Point{{0, 1}, {0, 2}, {0, 3}}}, + }, + Food: []Point{}, + } + for i := 0; i < 1000; i++ { + err := r.maybeSpawnFood(b) + require.NoError(t, err) + require.Equal(t, len(b.Food), 0) + } +} + +func TestMaybeSpawnFoodHundredChance(t *testing.T) { + r := StandardRuleset{FoodSpawnChance: 100} + b := &BoardState{ + Height: 11, + Width: 11, + Snakes: []Snake{ + {Body: []Point{{1, 0}, {1, 1}}}, + {Body: []Point{{0, 1}, {0, 2}, {0, 3}}}, + }, + Food: []Point{}, + } + for i := 1; i <= 22; i++ { + err := r.maybeSpawnFood(b) + require.NoError(t, err) + require.Equal(t, i, len(b.Food)) + } +} + +func TestMaybeSpawnFoodHalfChance(t *testing.T) { tests := []struct { Seed int64 Food []Point - ExpectedFood []Point + ExpectedFood int32 }{ // Use pre-tested seeds and results - {123, []Point{}, []Point{{2, 3}}}, - {456, []Point{{4, 4}}, []Point{{4, 4}}}, - {789, []Point{{4, 4}}, []Point{{4, 4}}}, - {1024, []Point{}, []Point{{1, 2}}}, - {511, []Point{{4, 4}}, []Point{{4, 4}, {4, 1}}}, - {165, []Point{{4, 4}}, []Point{{4, 4}, {4, 2}}}, + {123, []Point{}, 1}, + {12345, []Point{}, 0}, + {456, []Point{{4, 4}}, 1}, + {789, []Point{{4, 4}}, 2}, + {511, []Point{{4, 4}}, 1}, + {165, []Point{{4, 4}}, 2}, } - r := StandardRuleset{} + r := StandardRuleset{FoodSpawnChance: 50} for _, test := range tests { b := &BoardState{ Height: 4, @@ -1858,10 +1925,7 @@ func TestMaybeSpawnFood(t *testing.T) { rand.Seed(test.Seed) err := r.maybeSpawnFood(b) require.NoError(t, err) - require.Equal(t, len(test.ExpectedFood), len(b.Food), "Seed %d", test.Seed) - for i, e := range test.ExpectedFood { - require.Equal(t, e, b.Food[i], "Seed %d", test.Seed) - } + require.Equal(t, test.ExpectedFood, int32(len(b.Food)), "Seed %d", test.Seed) } }