Add new royale ruleset.
This commit is contained in:
parent
ccc2a27fd1
commit
dd5a2fd88f
2 changed files with 223 additions and 0 deletions
82
royale.go
Normal file
82
royale.go
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
package rules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RoyaleRuleset struct {
|
||||||
|
StandardRuleset
|
||||||
|
|
||||||
|
Turn int32
|
||||||
|
ShrinkEveryNTurns int32
|
||||||
|
|
||||||
|
// Output
|
||||||
|
OutOfBounds []Point
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RoyaleRuleset) CreateNextBoardState(prevState *BoardState, moves []SnakeMove) (*BoardState, error) {
|
||||||
|
if r.ShrinkEveryNTurns < 1 {
|
||||||
|
return nil, errors.New("royale game must shrink at least every 1 turn")
|
||||||
|
}
|
||||||
|
|
||||||
|
nextBoardState, err := r.StandardRuleset.CreateNextBoardState(prevState, moves)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: LOG?
|
||||||
|
err = r.populateOutOfBounds(nextBoardState)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: LOG?
|
||||||
|
err = r.eliminateOutOfBounds(nextBoardState)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextBoardState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RoyaleRuleset) populateOutOfBounds(b *BoardState) error {
|
||||||
|
r.OutOfBounds = []Point{}
|
||||||
|
|
||||||
|
if r.ShrinkEveryNTurns < 1 {
|
||||||
|
return errors.New("royale game must shrink at least every 1 turn")
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Turn < r.ShrinkEveryNTurns {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
numShrinks := r.Turn / r.ShrinkEveryNTurns
|
||||||
|
minX, maxX := numShrinks, b.Width-1-numShrinks
|
||||||
|
minY, maxY := numShrinks, b.Height-1-numShrinks
|
||||||
|
for x := int32(0); x < b.Width; x++ {
|
||||||
|
for y := int32(0); y < b.Height; y++ {
|
||||||
|
if x < minX || x > maxX || y < minY || y > maxY {
|
||||||
|
r.OutOfBounds = append(r.OutOfBounds, Point{x, y})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RoyaleRuleset) eliminateOutOfBounds(b *BoardState) error {
|
||||||
|
for i := 0; i < len(b.Snakes); i++ {
|
||||||
|
snake := &b.Snakes[i]
|
||||||
|
if snake.EliminatedCause == NotEliminated {
|
||||||
|
head := snake.Body[0]
|
||||||
|
for _, p := range r.OutOfBounds {
|
||||||
|
if head == p {
|
||||||
|
// Snake is now out of bounds, eliminate it
|
||||||
|
snake.EliminatedCause = EliminatedByOutOfBounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
141
royale_test.go
Normal file
141
royale_test.go
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
package rules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRoyaleRulesetInterface(t *testing.T) {
|
||||||
|
var _ Ruleset = (*RoyaleRuleset)(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoyaleDefaultSanity(t *testing.T) {
|
||||||
|
boardState := &BoardState{}
|
||||||
|
r := RoyaleRuleset{}
|
||||||
|
_, err := r.CreateNextBoardState(boardState, []SnakeMove{})
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Equal(t, err, errors.New("royale game must shrink at least every 1 turn"))
|
||||||
|
|
||||||
|
r = RoyaleRuleset{ShrinkEveryNTurns: 1}
|
||||||
|
_, err = r.CreateNextBoardState(boardState, []SnakeMove{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoyalePopulateObstacles(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Width int32
|
||||||
|
Height int32
|
||||||
|
Turn int32
|
||||||
|
ShrinkEveryNTurns int32
|
||||||
|
Error error
|
||||||
|
ExpectedOutOfBounds []Point
|
||||||
|
}{
|
||||||
|
{Error: errors.New("royale game must shrink at least every 1 turn")},
|
||||||
|
{ShrinkEveryNTurns: 1, ExpectedOutOfBounds: []Point{}},
|
||||||
|
{Turn: 1, ShrinkEveryNTurns: 1, ExpectedOutOfBounds: []Point{}},
|
||||||
|
{Width: 3, Height: 3, Turn: 1, ShrinkEveryNTurns: 10, ExpectedOutOfBounds: []Point{}},
|
||||||
|
{Width: 3, Height: 3, Turn: 9, ShrinkEveryNTurns: 10, ExpectedOutOfBounds: []Point{}},
|
||||||
|
{
|
||||||
|
Width: 3, Height: 3, Turn: 10, ShrinkEveryNTurns: 10,
|
||||||
|
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 2}, {2, 0}, {2, 1}, {2, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Width: 3, Height: 3, Turn: 11, ShrinkEveryNTurns: 10,
|
||||||
|
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 2}, {2, 0}, {2, 1}, {2, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Width: 3, Height: 3, Turn: 19, ShrinkEveryNTurns: 10,
|
||||||
|
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 2}, {2, 0}, {2, 1}, {2, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Width: 3, Height: 3, Turn: 20, ShrinkEveryNTurns: 10,
|
||||||
|
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 0}, {2, 1}, {2, 2}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
b := &BoardState{Width: test.Width, Height: test.Height}
|
||||||
|
r := RoyaleRuleset{
|
||||||
|
Turn: test.Turn,
|
||||||
|
ShrinkEveryNTurns: test.ShrinkEveryNTurns,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.populateOutOfBounds(b)
|
||||||
|
require.Equal(t, test.Error, err)
|
||||||
|
if err == nil {
|
||||||
|
// Obstacles should match
|
||||||
|
require.Equal(t, test.ExpectedOutOfBounds, r.OutOfBounds)
|
||||||
|
for _, expectedP := range test.ExpectedOutOfBounds {
|
||||||
|
wasFound := false
|
||||||
|
for _, actualP := range r.OutOfBounds {
|
||||||
|
if expectedP == actualP {
|
||||||
|
wasFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.True(t, wasFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoyaleEliminateOutOfBounds(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Snakes []Snake
|
||||||
|
OutOfBounds []Point
|
||||||
|
ExpectedEliminatedCauses []string
|
||||||
|
ExpectedEliminatedByIDs []string
|
||||||
|
}{
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
Snakes: []Snake{{Body: []Point{{0, 0}}}},
|
||||||
|
OutOfBounds: []Point{},
|
||||||
|
ExpectedEliminatedCauses: []string{NotEliminated},
|
||||||
|
ExpectedEliminatedByIDs: []string{""},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Snakes: []Snake{{Body: []Point{{0, 0}}}},
|
||||||
|
OutOfBounds: []Point{{0, 0}},
|
||||||
|
ExpectedEliminatedCauses: []string{EliminatedByOutOfBounds},
|
||||||
|
ExpectedEliminatedByIDs: []string{""},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Snakes: []Snake{{Body: []Point{{0, 0}, {1, 0}, {2, 0}}}},
|
||||||
|
OutOfBounds: []Point{{1, 0}, {2, 0}},
|
||||||
|
ExpectedEliminatedCauses: []string{NotEliminated},
|
||||||
|
ExpectedEliminatedByIDs: []string{""},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Snakes: []Snake{
|
||||||
|
{Body: []Point{{0, 0}, {1, 0}, {2, 0}}},
|
||||||
|
{Body: []Point{{3, 3}, {3, 4}, {3, 5}, {3, 6}}},
|
||||||
|
},
|
||||||
|
OutOfBounds: []Point{{1, 0}, {2, 0}, {3, 4}, {3, 5}, {3, 6}},
|
||||||
|
ExpectedEliminatedCauses: []string{NotEliminated, NotEliminated},
|
||||||
|
ExpectedEliminatedByIDs: []string{"", ""},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Snakes: []Snake{
|
||||||
|
{Body: []Point{{0, 0}, {1, 0}, {2, 0}}},
|
||||||
|
{Body: []Point{{3, 3}, {3, 4}, {3, 5}, {3, 6}}},
|
||||||
|
},
|
||||||
|
OutOfBounds: []Point{{3, 3}},
|
||||||
|
ExpectedEliminatedCauses: []string{NotEliminated, EliminatedByOutOfBounds},
|
||||||
|
ExpectedEliminatedByIDs: []string{"", ""},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
b := &BoardState{Snakes: test.Snakes}
|
||||||
|
r := RoyaleRuleset{OutOfBounds: test.OutOfBounds}
|
||||||
|
err := r.eliminateOutOfBounds(b)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for i, snake := range b.Snakes {
|
||||||
|
require.Equal(t, test.ExpectedEliminatedCauses[i], snake.EliminatedCause)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue