DEV-765 pipeline refactor (#64)

Refactor rulesets into smaller composable operations

In order to mix up the functionality from different rulesets like Solo, Royale, etc. the code in these classes needs to be broken up into small functions that can be composed in a pipeline to make a custom game mode.
This commit is contained in:
Torben 2022-03-16 16:58:05 -07:00 committed by GitHub
parent 5e629e9e93
commit 397d925110
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 1475 additions and 222 deletions

View file

@ -16,9 +16,7 @@ type SquadRuleset struct {
SharedLength bool
}
const EliminatedBySquad = "squad-eliminated"
func (r *SquadRuleset) Name() string { return "squad" }
func (r *SquadRuleset) Name() string { return GameTypeSquad }
func (r *SquadRuleset) CreateNextBoardState(prevState *BoardState, moves []SnakeMove) (*BoardState, error) {
nextBoardState, err := r.StandardRuleset.CreateNextBoardState(prevState, moves)
@ -26,13 +24,11 @@ func (r *SquadRuleset) CreateNextBoardState(prevState *BoardState, moves []Snake
return nil, err
}
// TODO: LOG?
err = r.resurrectSquadBodyCollisions(nextBoardState)
if err != nil {
return nil, err
}
// TODO: LOG?
err = r.shareSquadAttributes(nextBoardState)
if err != nil {
return nil, err
@ -41,38 +37,50 @@ func (r *SquadRuleset) CreateNextBoardState(prevState *BoardState, moves []Snake
return nextBoardState, nil
}
func (r *SquadRuleset) areSnakesOnSameSquad(snake *Snake, other *Snake) bool {
return r.areSnakeIDsOnSameSquad(snake.ID, other.ID)
func areSnakesOnSameSquad(squadMap map[string]string, snake *Snake, other *Snake) bool {
return areSnakeIDsOnSameSquad(squadMap, snake.ID, other.ID)
}
func (r *SquadRuleset) areSnakeIDsOnSameSquad(snakeID string, otherID string) bool {
return r.SquadMap[snakeID] == r.SquadMap[otherID]
func areSnakeIDsOnSameSquad(squadMap map[string]string, snakeID string, otherID string) bool {
return squadMap[snakeID] == squadMap[otherID]
}
func (r *SquadRuleset) resurrectSquadBodyCollisions(b *BoardState) error {
if !r.AllowBodyCollisions {
return nil
_, err := r.callStageFunc(ResurrectSnakesSquad, b, []SnakeMove{})
return err
}
func ResurrectSnakesSquad(b *BoardState, settings Settings, moves []SnakeMove) (bool, error) {
if !settings.SquadSettings.AllowBodyCollisions {
return false, nil
}
for i := 0; i < len(b.Snakes); i++ {
snake := &b.Snakes[i]
if snake.EliminatedCause == EliminatedByCollision {
if snake.EliminatedBy == "" {
return errors.New("snake eliminated by collision and eliminatedby is not set")
return false, errors.New("snake eliminated by collision and eliminatedby is not set")
}
if snake.ID != snake.EliminatedBy && r.areSnakeIDsOnSameSquad(snake.ID, snake.EliminatedBy) {
if snake.ID != snake.EliminatedBy && areSnakeIDsOnSameSquad(settings.SquadSettings.squadMap, snake.ID, snake.EliminatedBy) {
snake.EliminatedCause = NotEliminated
snake.EliminatedBy = ""
}
}
}
return nil
return false, nil
}
func (r *SquadRuleset) shareSquadAttributes(b *BoardState) error {
if !(r.SharedElimination || r.SharedLength || r.SharedHealth) {
return nil
_, err := r.callStageFunc(ShareAttributesSquad, b, []SnakeMove{})
return err
}
func ShareAttributesSquad(b *BoardState, settings Settings, moves []SnakeMove) (bool, error) {
squadSettings := settings.SquadSettings
if !(squadSettings.SharedElimination || squadSettings.SharedLength || squadSettings.SharedHealth) {
return false, nil
}
for i := 0; i < len(b.Snakes); i++ {
@ -83,21 +91,21 @@ func (r *SquadRuleset) shareSquadAttributes(b *BoardState) error {
for j := 0; j < len(b.Snakes); j++ {
other := &b.Snakes[j]
if r.areSnakesOnSameSquad(snake, other) {
if r.SharedHealth {
if areSnakesOnSameSquad(squadSettings.squadMap, snake, other) {
if squadSettings.SharedHealth {
if snake.Health < other.Health {
snake.Health = other.Health
}
}
if r.SharedLength {
if squadSettings.SharedLength {
if len(snake.Body) == 0 || len(other.Body) == 0 {
return errors.New("found snake of zero length")
return false, errors.New("found snake of zero length")
}
for len(snake.Body) < len(other.Body) {
r.growSnake(snake)
growSnake(snake)
}
}
if r.SharedElimination {
if squadSettings.SharedElimination {
if snake.EliminatedCause == NotEliminated && other.EliminatedCause != NotEliminated {
snake.EliminatedCause = EliminatedBySquad
// We intentionally do not set snake.EliminatedBy because there might be multiple culprits.
@ -108,10 +116,14 @@ func (r *SquadRuleset) shareSquadAttributes(b *BoardState) error {
}
}
return nil
return false, nil
}
func (r *SquadRuleset) IsGameOver(b *BoardState) (bool, error) {
return r.callStageFunc(GameOverSquad, b, []SnakeMove{})
}
func GameOverSquad(b *BoardState, settings Settings, moves []SnakeMove) (bool, error) {
snakesRemaining := []*Snake{}
for i := 0; i < len(b.Snakes); i++ {
if b.Snakes[i].EliminatedCause == NotEliminated {
@ -120,7 +132,7 @@ func (r *SquadRuleset) IsGameOver(b *BoardState) (bool, error) {
}
for i := 0; i < len(snakesRemaining); i++ {
if !r.areSnakesOnSameSquad(snakesRemaining[i], snakesRemaining[0]) {
if !areSnakesOnSameSquad(settings.SquadSettings.squadMap, snakesRemaining[i], snakesRemaining[0]) {
// There are multiple squads remaining
return false, nil
}
@ -128,3 +140,20 @@ func (r *SquadRuleset) IsGameOver(b *BoardState) (bool, error) {
// no snakes or single squad remaining
return true, nil
}
func (r SquadRuleset) Settings() Settings {
s := r.StandardRuleset.Settings()
s.SquadSettings = SquadSettings{
squadMap: r.SquadMap,
AllowBodyCollisions: r.AllowBodyCollisions,
SharedElimination: r.SharedElimination,
SharedHealth: r.SharedHealth,
SharedLength: r.SharedLength,
}
return s
}
// Adaptor for integrating stages into SquadRuleset
func (r *SquadRuleset) callStageFunc(stage StageFunc, boardState *BoardState, moves []SnakeMove) (bool, error) {
return stage(boardState, r.Settings(), moves)
}