Royale: Shrink a random edge instead of the whole board
This commit is contained in:
parent
2ca57f0779
commit
64dfc6dd55
2 changed files with 119 additions and 16 deletions
42
royale.go
42
royale.go
|
|
@ -2,6 +2,8 @@ package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"hash/crc32"
|
||||||
|
"math/rand"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RoyaleRuleset struct {
|
type RoyaleRuleset struct {
|
||||||
|
|
@ -63,9 +65,27 @@ func (r *RoyaleRuleset) populateOutOfBounds(b *BoardState, turn int32) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
randGenerator, err := r.getRandGenerator(b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
numShrinks := turn / r.ShrinkEveryNTurns
|
numShrinks := turn / r.ShrinkEveryNTurns
|
||||||
minX, maxX := numShrinks, b.Width-1-numShrinks
|
minX, maxX := int32(0), b.Width-1
|
||||||
minY, maxY := numShrinks, b.Height-1-numShrinks
|
minY, maxY := int32(0), b.Height-1
|
||||||
|
for i := int32(0); i < numShrinks; i++ {
|
||||||
|
switch randGenerator.Intn(4) {
|
||||||
|
case 0:
|
||||||
|
minX += 1
|
||||||
|
case 1:
|
||||||
|
maxX -= 1
|
||||||
|
case 2:
|
||||||
|
minY += 1
|
||||||
|
case 3:
|
||||||
|
maxY -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for x := int32(0); x < b.Width; x++ {
|
for x := int32(0); x < b.Width; x++ {
|
||||||
for y := int32(0); y < b.Height; y++ {
|
for y := int32(0); y < b.Height; y++ {
|
||||||
if x < minX || x > maxX || y < minY || y > maxY {
|
if x < minX || x > maxX || y < minY || y > maxY {
|
||||||
|
|
@ -101,3 +121,21 @@ func (r *RoyaleRuleset) damageOutOfBounds(b *BoardState) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RoyaleRuleset) getRandGenerator(b *BoardState) (*rand.Rand, error) {
|
||||||
|
if len(b.Snakes) < 1 {
|
||||||
|
return nil, errors.New("royale mode requires at least one snake id")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the "lowest" Snake ID as a random seed
|
||||||
|
seedStr := b.Snakes[0].ID
|
||||||
|
for i := 1; i < len(b.Snakes); i++ {
|
||||||
|
if b.Snakes[i].ID < seedStr {
|
||||||
|
seedStr = b.Snakes[i].ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
seed := int64(crc32.ChecksumIEEE([]byte(seedStr)))
|
||||||
|
|
||||||
|
return rand.New(rand.NewSource(seed)), nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,17 @@ func TestRoyaleDefaultSanity(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRoyaleOutOfBoundsNoSnakes(t *testing.T) {
|
||||||
|
b := &BoardState{}
|
||||||
|
r := RoyaleRuleset{
|
||||||
|
ShrinkEveryNTurns: 10,
|
||||||
|
DamagePerTurn: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.populateOutOfBounds(b, 100)
|
||||||
|
require.Equal(t, errors.New("royale mode requires at least one snake id"), err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestRoyaleOutOfBounds(t *testing.T) {
|
func TestRoyaleOutOfBounds(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Width int32
|
Width int32
|
||||||
|
|
@ -39,24 +50,48 @@ func TestRoyaleOutOfBounds(t *testing.T) {
|
||||||
{Width: 3, Height: 3, Turn: 9, ShrinkEveryNTurns: 10, ExpectedOutOfBounds: []Point{}},
|
{Width: 3, Height: 3, Turn: 9, ShrinkEveryNTurns: 10, ExpectedOutOfBounds: []Point{}},
|
||||||
{
|
{
|
||||||
Width: 3, Height: 3, Turn: 10, ShrinkEveryNTurns: 10,
|
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}},
|
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Width: 3, Height: 3, Turn: 11, ShrinkEveryNTurns: 10,
|
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}},
|
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Width: 3, Height: 3, Turn: 19, ShrinkEveryNTurns: 10,
|
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}},
|
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Width: 3, Height: 3, Turn: 20, ShrinkEveryNTurns: 10,
|
Width: 3, Height: 3, Turn: 20, ShrinkEveryNTurns: 10,
|
||||||
|
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}, {1, 2}, {2, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Width: 3, Height: 3, Turn: 31, ShrinkEveryNTurns: 10,
|
||||||
|
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Width: 3, Height: 3, Turn: 42, ShrinkEveryNTurns: 10,
|
||||||
|
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 0}, {2, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Width: 3, Height: 3, Turn: 53, ShrinkEveryNTurns: 10,
|
||||||
|
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 0}, {2, 1}, {2, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Width: 3, Height: 3, Turn: 64, ShrinkEveryNTurns: 10,
|
||||||
|
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 0}, {2, 1}, {2, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Width: 3, Height: 3, Turn: 6987, ShrinkEveryNTurns: 10,
|
||||||
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 0}, {2, 1}, {2, 2}},
|
ExpectedOutOfBounds: []Point{{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 0}, {2, 1}, {2, 2}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
b := &BoardState{Width: test.Width, Height: test.Height}
|
b := &BoardState{
|
||||||
|
Width: test.Width,
|
||||||
|
Height: test.Height,
|
||||||
|
Snakes: []Snake{{ID: "test-snake"}},
|
||||||
|
}
|
||||||
r := RoyaleRuleset{
|
r := RoyaleRuleset{
|
||||||
Turn: test.Turn,
|
Turn: test.Turn,
|
||||||
ShrinkEveryNTurns: test.ShrinkEveryNTurns,
|
ShrinkEveryNTurns: test.ShrinkEveryNTurns,
|
||||||
|
|
@ -174,38 +209,68 @@ func TestRoyaleDamagePerTurn(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRoyalDamageNextTurn(t *testing.T) {
|
func TestRoyalDamageNextTurn(t *testing.T) {
|
||||||
b := &BoardState{Width: 10, Height: 10, Snakes: []Snake{{ID: "one", Health: 100, Body: []Point{{1, 1}}}}}
|
b := &BoardState{Width: 10, Height: 10, Snakes: []Snake{{ID: "one", Health: 100, Body: []Point{{1, 0}}}}}
|
||||||
r := RoyaleRuleset{Turn: 10, ShrinkEveryNTurns: 10, DamagePerTurn: 30}
|
r := RoyaleRuleset{ShrinkEveryNTurns: 10, DamagePerTurn: 30}
|
||||||
m := []SnakeMove{{ID: "one", Move: "right"}}
|
m := []SnakeMove{{ID: "one", Move: "right"}}
|
||||||
|
|
||||||
|
r.Turn = 10
|
||||||
n, err := r.CreateNextBoardState(b, m)
|
n, err := r.CreateNextBoardState(b, m)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, NotEliminated, n.Snakes[0].EliminatedCause)
|
require.Equal(t, NotEliminated, n.Snakes[0].EliminatedCause)
|
||||||
require.Equal(t, int32(99), n.Snakes[0].Health)
|
require.Equal(t, int32(99), n.Snakes[0].Health)
|
||||||
require.Equal(t, Point{2, 1}, n.Snakes[0].Body[0])
|
require.Equal(t, Point{2, 0}, n.Snakes[0].Body[0])
|
||||||
require.Equal(t, 36, len(r.OutOfBounds))
|
require.Equal(t, 10, len(r.OutOfBounds)) // X = 0
|
||||||
|
|
||||||
r.Turn = 20
|
r.Turn = 20
|
||||||
n, err = r.CreateNextBoardState(b, m)
|
n, err = r.CreateNextBoardState(b, m)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, NotEliminated, n.Snakes[0].EliminatedCause)
|
require.Equal(t, NotEliminated, n.Snakes[0].EliminatedCause)
|
||||||
require.Equal(t, int32(99), n.Snakes[0].Health)
|
require.Equal(t, int32(99), n.Snakes[0].Health)
|
||||||
require.Equal(t, Point{2, 1}, n.Snakes[0].Body[0])
|
require.Equal(t, Point{2, 0}, n.Snakes[0].Body[0])
|
||||||
require.Equal(t, 64, len(r.OutOfBounds))
|
require.Equal(t, 19, len(r.OutOfBounds)) // Y = 0
|
||||||
|
|
||||||
r.Turn = 21
|
r.Turn = 21
|
||||||
n, err = r.CreateNextBoardState(b, m)
|
n, err = r.CreateNextBoardState(b, m)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, NotEliminated, n.Snakes[0].EliminatedCause)
|
require.Equal(t, NotEliminated, n.Snakes[0].EliminatedCause)
|
||||||
require.Equal(t, int32(69), n.Snakes[0].Health)
|
require.Equal(t, int32(69), n.Snakes[0].Health)
|
||||||
require.Equal(t, Point{2, 1}, n.Snakes[0].Body[0])
|
require.Equal(t, Point{2, 0}, n.Snakes[0].Body[0])
|
||||||
require.Equal(t, 64, len(r.OutOfBounds))
|
require.Equal(t, 19, len(r.OutOfBounds))
|
||||||
|
|
||||||
b.Snakes[0].Health = 15
|
b.Snakes[0].Health = 15
|
||||||
n, err = r.CreateNextBoardState(b, m)
|
n, err = r.CreateNextBoardState(b, m)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, EliminatedByStarvation, n.Snakes[0].EliminatedCause)
|
require.Equal(t, EliminatedByStarvation, n.Snakes[0].EliminatedCause)
|
||||||
require.Equal(t, int32(0), n.Snakes[0].Health)
|
require.Equal(t, int32(0), n.Snakes[0].Health)
|
||||||
require.Equal(t, Point{2, 1}, n.Snakes[0].Body[0])
|
require.Equal(t, Point{2, 0}, n.Snakes[0].Body[0])
|
||||||
require.Equal(t, 64, len(r.OutOfBounds))
|
require.Equal(t, 19, len(r.OutOfBounds))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoyalGetRandGenerator(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
SnakeIDs []string
|
||||||
|
Error error
|
||||||
|
firstInt int
|
||||||
|
}{
|
||||||
|
{[]string{}, errors.New("royale mode requires at least one snake id"), 0},
|
||||||
|
{[]string{"1"}, nil, 1400170195406563237},
|
||||||
|
{[]string{"1", "2", "3", "4", "5"}, nil, 1400170195406563237},
|
||||||
|
{[]string{"5", "4", "3", "2", "1"}, nil, 1400170195406563237},
|
||||||
|
{[]string{"3", "4", "1", "5", "2"}, nil, 1400170195406563237},
|
||||||
|
{[]string{"3", "4", "5", "2"}, nil, 5139088052943840554},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
b := &BoardState{}
|
||||||
|
for _, id := range test.SnakeIDs {
|
||||||
|
b.Snakes = append(b.Snakes, Snake{ID: id})
|
||||||
|
}
|
||||||
|
|
||||||
|
r := RoyaleRuleset{}
|
||||||
|
generator, err := r.getRandGenerator(b)
|
||||||
|
require.Equal(t, test.Error, err)
|
||||||
|
if err == nil {
|
||||||
|
require.Equal(t, test.firstInt, generator.Int())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue