Royale mode does damage instead of eliminating.
This commit is contained in:
parent
dd5a2fd88f
commit
2ca57f0779
2 changed files with 111 additions and 20 deletions
41
royale.go
41
royale.go
|
|
@ -9,6 +9,7 @@ type RoyaleRuleset struct {
|
||||||
|
|
||||||
Turn int32
|
Turn int32
|
||||||
ShrinkEveryNTurns int32
|
ShrinkEveryNTurns int32
|
||||||
|
DamagePerTurn int32
|
||||||
|
|
||||||
// Output
|
// Output
|
||||||
OutOfBounds []Point
|
OutOfBounds []Point
|
||||||
|
|
@ -16,7 +17,7 @@ type RoyaleRuleset struct {
|
||||||
|
|
||||||
func (r *RoyaleRuleset) CreateNextBoardState(prevState *BoardState, moves []SnakeMove) (*BoardState, error) {
|
func (r *RoyaleRuleset) CreateNextBoardState(prevState *BoardState, moves []SnakeMove) (*BoardState, error) {
|
||||||
if r.ShrinkEveryNTurns < 1 {
|
if r.ShrinkEveryNTurns < 1 {
|
||||||
return nil, errors.New("royale game must shrink at least every 1 turn")
|
return nil, errors.New("royale game must shrink at least every turn")
|
||||||
}
|
}
|
||||||
|
|
||||||
nextBoardState, err := r.StandardRuleset.CreateNextBoardState(prevState, moves)
|
nextBoardState, err := r.StandardRuleset.CreateNextBoardState(prevState, moves)
|
||||||
|
|
@ -24,14 +25,26 @@ func (r *RoyaleRuleset) CreateNextBoardState(prevState *BoardState, moves []Snak
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Algorithm:
|
||||||
|
// - Populate OOB for last turn
|
||||||
|
// - Apply damage to snake heads that are OOB
|
||||||
|
// - Re-populate OOB for this turn
|
||||||
|
// ---> This means damage on board shrinks doesn't hit until the following turn.
|
||||||
|
|
||||||
// TODO: LOG?
|
// TODO: LOG?
|
||||||
err = r.populateOutOfBounds(nextBoardState)
|
err = r.populateOutOfBounds(nextBoardState, r.Turn-1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: LOG?
|
// TODO: LOG?
|
||||||
err = r.eliminateOutOfBounds(nextBoardState)
|
err = r.damageOutOfBounds(nextBoardState)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: LOG?
|
||||||
|
err = r.populateOutOfBounds(nextBoardState, r.Turn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -39,18 +52,18 @@ func (r *RoyaleRuleset) CreateNextBoardState(prevState *BoardState, moves []Snak
|
||||||
return nextBoardState, nil
|
return nextBoardState, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RoyaleRuleset) populateOutOfBounds(b *BoardState) error {
|
func (r *RoyaleRuleset) populateOutOfBounds(b *BoardState, turn int32) error {
|
||||||
r.OutOfBounds = []Point{}
|
r.OutOfBounds = []Point{}
|
||||||
|
|
||||||
if r.ShrinkEveryNTurns < 1 {
|
if r.ShrinkEveryNTurns < 1 {
|
||||||
return errors.New("royale game must shrink at least every 1 turn")
|
return errors.New("royale game must shrink at least every turn")
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Turn < r.ShrinkEveryNTurns {
|
if turn < r.ShrinkEveryNTurns {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
numShrinks := r.Turn / r.ShrinkEveryNTurns
|
numShrinks := turn / r.ShrinkEveryNTurns
|
||||||
minX, maxX := numShrinks, b.Width-1-numShrinks
|
minX, maxX := numShrinks, b.Width-1-numShrinks
|
||||||
minY, maxY := numShrinks, b.Height-1-numShrinks
|
minY, maxY := numShrinks, b.Height-1-numShrinks
|
||||||
for x := int32(0); x < b.Width; x++ {
|
for x := int32(0); x < b.Width; x++ {
|
||||||
|
|
@ -64,15 +77,23 @@ func (r *RoyaleRuleset) populateOutOfBounds(b *BoardState) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RoyaleRuleset) eliminateOutOfBounds(b *BoardState) error {
|
func (r *RoyaleRuleset) damageOutOfBounds(b *BoardState) error {
|
||||||
|
if r.DamagePerTurn < 1 {
|
||||||
|
return errors.New("royale damage per turn must be greater than zero")
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < len(b.Snakes); i++ {
|
for i := 0; i < len(b.Snakes); i++ {
|
||||||
snake := &b.Snakes[i]
|
snake := &b.Snakes[i]
|
||||||
if snake.EliminatedCause == NotEliminated {
|
if snake.EliminatedCause == NotEliminated {
|
||||||
head := snake.Body[0]
|
head := snake.Body[0]
|
||||||
for _, p := range r.OutOfBounds {
|
for _, p := range r.OutOfBounds {
|
||||||
if head == p {
|
if head == p {
|
||||||
// Snake is now out of bounds, eliminate it
|
// Snake is now out of bounds, reduce health
|
||||||
snake.EliminatedCause = EliminatedByOutOfBounds
|
snake.Health = snake.Health - r.DamagePerTurn
|
||||||
|
if snake.Health <= 0 {
|
||||||
|
snake.Health = 0
|
||||||
|
snake.EliminatedCause = EliminatedByStarvation
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,14 @@ func TestRoyaleDefaultSanity(t *testing.T) {
|
||||||
r := RoyaleRuleset{}
|
r := RoyaleRuleset{}
|
||||||
_, err := r.CreateNextBoardState(boardState, []SnakeMove{})
|
_, err := r.CreateNextBoardState(boardState, []SnakeMove{})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, err, errors.New("royale game must shrink at least every 1 turn"))
|
require.Equal(t, errors.New("royale game must shrink at least every turn"), err)
|
||||||
|
|
||||||
r = RoyaleRuleset{ShrinkEveryNTurns: 1}
|
r = RoyaleRuleset{ShrinkEveryNTurns: 1, DamagePerTurn: 1}
|
||||||
_, err = r.CreateNextBoardState(boardState, []SnakeMove{})
|
_, err = r.CreateNextBoardState(boardState, []SnakeMove{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRoyalePopulateObstacles(t *testing.T) {
|
func TestRoyaleOutOfBounds(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Width int32
|
Width int32
|
||||||
Height int32
|
Height int32
|
||||||
|
|
@ -32,7 +32,7 @@ func TestRoyalePopulateObstacles(t *testing.T) {
|
||||||
Error error
|
Error error
|
||||||
ExpectedOutOfBounds []Point
|
ExpectedOutOfBounds []Point
|
||||||
}{
|
}{
|
||||||
{Error: errors.New("royale game must shrink at least every 1 turn")},
|
{Error: errors.New("royale game must shrink at least every turn")},
|
||||||
{ShrinkEveryNTurns: 1, ExpectedOutOfBounds: []Point{}},
|
{ShrinkEveryNTurns: 1, ExpectedOutOfBounds: []Point{}},
|
||||||
{Turn: 1, 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: 1, ShrinkEveryNTurns: 10, ExpectedOutOfBounds: []Point{}},
|
||||||
|
|
@ -62,7 +62,7 @@ func TestRoyalePopulateObstacles(t *testing.T) {
|
||||||
ShrinkEveryNTurns: test.ShrinkEveryNTurns,
|
ShrinkEveryNTurns: test.ShrinkEveryNTurns,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := r.populateOutOfBounds(b)
|
err := r.populateOutOfBounds(b, test.Turn)
|
||||||
require.Equal(t, test.Error, err)
|
require.Equal(t, test.Error, err)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Obstacles should match
|
// Obstacles should match
|
||||||
|
|
@ -81,7 +81,7 @@ func TestRoyalePopulateObstacles(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRoyaleEliminateOutOfBounds(t *testing.T) {
|
func TestRoyaleDamageOutOfBounds(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Snakes []Snake
|
Snakes []Snake
|
||||||
OutOfBounds []Point
|
OutOfBounds []Point
|
||||||
|
|
@ -98,7 +98,7 @@ func TestRoyaleEliminateOutOfBounds(t *testing.T) {
|
||||||
{
|
{
|
||||||
Snakes: []Snake{{Body: []Point{{0, 0}}}},
|
Snakes: []Snake{{Body: []Point{{0, 0}}}},
|
||||||
OutOfBounds: []Point{{0, 0}},
|
OutOfBounds: []Point{{0, 0}},
|
||||||
ExpectedEliminatedCauses: []string{EliminatedByOutOfBounds},
|
ExpectedEliminatedCauses: []string{EliminatedByStarvation},
|
||||||
ExpectedEliminatedByIDs: []string{""},
|
ExpectedEliminatedByIDs: []string{""},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -122,15 +122,15 @@ func TestRoyaleEliminateOutOfBounds(t *testing.T) {
|
||||||
{Body: []Point{{3, 3}, {3, 4}, {3, 5}, {3, 6}}},
|
{Body: []Point{{3, 3}, {3, 4}, {3, 5}, {3, 6}}},
|
||||||
},
|
},
|
||||||
OutOfBounds: []Point{{3, 3}},
|
OutOfBounds: []Point{{3, 3}},
|
||||||
ExpectedEliminatedCauses: []string{NotEliminated, EliminatedByOutOfBounds},
|
ExpectedEliminatedCauses: []string{NotEliminated, EliminatedByStarvation},
|
||||||
ExpectedEliminatedByIDs: []string{"", ""},
|
ExpectedEliminatedByIDs: []string{"", ""},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
b := &BoardState{Snakes: test.Snakes}
|
b := &BoardState{Snakes: test.Snakes}
|
||||||
r := RoyaleRuleset{OutOfBounds: test.OutOfBounds}
|
r := RoyaleRuleset{OutOfBounds: test.OutOfBounds, DamagePerTurn: 100}
|
||||||
err := r.eliminateOutOfBounds(b)
|
err := r.damageOutOfBounds(b)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for i, snake := range b.Snakes {
|
for i, snake := range b.Snakes {
|
||||||
|
|
@ -139,3 +139,73 @@ func TestRoyaleEliminateOutOfBounds(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRoyaleDamagePerTurn(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Health int32
|
||||||
|
DamagePerTurn int32
|
||||||
|
ExpectedHealth int32
|
||||||
|
ExpectedEliminationCause string
|
||||||
|
Error error
|
||||||
|
}{
|
||||||
|
{100, 0, 100, NotEliminated, errors.New("royale damage per turn must be greater than zero")},
|
||||||
|
{100, -100, 100, NotEliminated, errors.New("royale damage per turn must be greater than zero")},
|
||||||
|
{100, 1, 99, NotEliminated, nil},
|
||||||
|
{100, 99, 1, NotEliminated, nil},
|
||||||
|
{100, 100, 0, EliminatedByStarvation, nil},
|
||||||
|
{100, 101, 0, EliminatedByStarvation, nil},
|
||||||
|
{100, 999, 0, EliminatedByStarvation, nil},
|
||||||
|
{2, 1, 1, NotEliminated, nil},
|
||||||
|
{1, 1, 0, EliminatedByStarvation, nil},
|
||||||
|
{1, 999, 0, EliminatedByStarvation, nil},
|
||||||
|
{0, 1, 0, EliminatedByStarvation, nil},
|
||||||
|
{0, 999, 0, EliminatedByStarvation, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
b := &BoardState{Snakes: []Snake{{Health: test.Health, Body: []Point{{0, 0}}}}}
|
||||||
|
r := RoyaleRuleset{OutOfBounds: []Point{{0, 0}}, DamagePerTurn: test.DamagePerTurn}
|
||||||
|
|
||||||
|
err := r.damageOutOfBounds(b)
|
||||||
|
require.Equal(t, test.Error, err)
|
||||||
|
require.Equal(t, test.ExpectedHealth, b.Snakes[0].Health)
|
||||||
|
require.Equal(t, test.ExpectedEliminationCause, b.Snakes[0].EliminatedCause)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoyalDamageNextTurn(t *testing.T) {
|
||||||
|
b := &BoardState{Width: 10, Height: 10, Snakes: []Snake{{ID: "one", Health: 100, Body: []Point{{1, 1}}}}}
|
||||||
|
r := RoyaleRuleset{Turn: 10, ShrinkEveryNTurns: 10, DamagePerTurn: 30}
|
||||||
|
m := []SnakeMove{{ID: "one", Move: "right"}}
|
||||||
|
|
||||||
|
n, err := r.CreateNextBoardState(b, m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, NotEliminated, n.Snakes[0].EliminatedCause)
|
||||||
|
require.Equal(t, int32(99), n.Snakes[0].Health)
|
||||||
|
require.Equal(t, Point{2, 1}, n.Snakes[0].Body[0])
|
||||||
|
require.Equal(t, 36, len(r.OutOfBounds))
|
||||||
|
|
||||||
|
r.Turn = 20
|
||||||
|
n, err = r.CreateNextBoardState(b, m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, NotEliminated, n.Snakes[0].EliminatedCause)
|
||||||
|
require.Equal(t, int32(99), n.Snakes[0].Health)
|
||||||
|
require.Equal(t, Point{2, 1}, n.Snakes[0].Body[0])
|
||||||
|
require.Equal(t, 64, len(r.OutOfBounds))
|
||||||
|
|
||||||
|
r.Turn = 21
|
||||||
|
n, err = r.CreateNextBoardState(b, m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, NotEliminated, n.Snakes[0].EliminatedCause)
|
||||||
|
require.Equal(t, int32(69), n.Snakes[0].Health)
|
||||||
|
require.Equal(t, Point{2, 1}, n.Snakes[0].Body[0])
|
||||||
|
require.Equal(t, 64, len(r.OutOfBounds))
|
||||||
|
|
||||||
|
b.Snakes[0].Health = 15
|
||||||
|
n, err = r.CreateNextBoardState(b, m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, EliminatedByStarvation, n.Snakes[0].EliminatedCause)
|
||||||
|
require.Equal(t, int32(0), n.Snakes[0].Health)
|
||||||
|
require.Equal(t, Point{2, 1}, n.Snakes[0].Body[0])
|
||||||
|
require.Equal(t, 64, len(r.OutOfBounds))
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue