DEV 1364: Allow solo games with all rulesets through the CLI (#80)
* add support for automatic solo through RulesetBuilder * allow solo games with all modes in CLI
This commit is contained in:
parent
426da8ac5e
commit
25dc404493
3 changed files with 115 additions and 7 deletions
|
|
@ -133,7 +133,11 @@ func (gameState *GameState) initialize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build ruleset from settings
|
// Build ruleset from settings
|
||||||
ruleset := rules.NewRulesetBuilder().WithSeed(gameState.Seed).WithParams(gameState.settings).Ruleset()
|
ruleset := rules.NewRulesetBuilder().
|
||||||
|
WithSeed(gameState.Seed).
|
||||||
|
WithParams(gameState.settings).
|
||||||
|
WithSolo(len(gameState.URLs) < 2).
|
||||||
|
Ruleset()
|
||||||
gameState.ruleset = ruleset
|
gameState.ruleset = ruleset
|
||||||
|
|
||||||
// Initialize snake states as empty until we can ping the snake URLs
|
// Initialize snake states as empty until we can ping the snake URLs
|
||||||
|
|
|
||||||
27
ruleset.go
27
ruleset.go
|
|
@ -79,6 +79,7 @@ type rulesetBuilder struct {
|
||||||
params map[string]string // game customisation parameters
|
params map[string]string // game customisation parameters
|
||||||
seed int64 // used for random events in games
|
seed int64 // used for random events in games
|
||||||
rand Rand // used for random number generation
|
rand Rand // used for random number generation
|
||||||
|
solo bool // if true, only 1 alive snake is required to keep the game from ending
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRulesetBuilder returns an instance of a builder for the Ruleset types.
|
// NewRulesetBuilder returns an instance of a builder for the Ruleset types.
|
||||||
|
|
@ -118,6 +119,12 @@ func (rb *rulesetBuilder) WithRand(rand Rand) *rulesetBuilder {
|
||||||
return rb
|
return rb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithSolo sets whether the ruleset is a solo game.
|
||||||
|
func (rb *rulesetBuilder) WithSolo(value bool) *rulesetBuilder {
|
||||||
|
rb.solo = value
|
||||||
|
return rb
|
||||||
|
}
|
||||||
|
|
||||||
// Ruleset constructs a customised ruleset using the parameters passed to the builder.
|
// Ruleset constructs a customised ruleset using the parameters passed to the builder.
|
||||||
func (rb rulesetBuilder) Ruleset() PipelineRuleset {
|
func (rb rulesetBuilder) Ruleset() PipelineRuleset {
|
||||||
name, ok := rb.params[ParamGameType]
|
name, ok := rb.params[ParamGameType]
|
||||||
|
|
@ -125,20 +132,28 @@ func (rb rulesetBuilder) Ruleset() PipelineRuleset {
|
||||||
name = GameTypeStandard
|
name = GameTypeStandard
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var stages []string
|
||||||
|
if rb.solo {
|
||||||
|
stages = append(stages, StageGameOverSoloSnake)
|
||||||
|
} else {
|
||||||
|
stages = append(stages, StageGameOverStandard)
|
||||||
|
}
|
||||||
|
|
||||||
switch name {
|
switch name {
|
||||||
case GameTypeStandard:
|
case GameTypeStandard:
|
||||||
return rb.PipelineRuleset(name, NewPipeline(standardRulesetStages...))
|
stages = append(stages, standardRulesetStages[1:]...)
|
||||||
case GameTypeConstrictor:
|
case GameTypeConstrictor:
|
||||||
return rb.PipelineRuleset(name, NewPipeline(constrictorRulesetStages...))
|
stages = append(stages, constrictorRulesetStages[1:]...)
|
||||||
case GameTypeRoyale:
|
case GameTypeRoyale:
|
||||||
return rb.PipelineRuleset(name, NewPipeline(royaleRulesetStages...))
|
stages = append(stages, royaleRulesetStages[1:]...)
|
||||||
case GameTypeSolo:
|
case GameTypeSolo:
|
||||||
return rb.PipelineRuleset(name, NewPipeline(soloRulesetStages...))
|
stages = soloRulesetStages
|
||||||
case GameTypeWrapped:
|
case GameTypeWrapped:
|
||||||
return rb.PipelineRuleset(name, NewPipeline(wrappedRulesetStages...))
|
stages = append(stages, wrappedRulesetStages[1:]...)
|
||||||
default:
|
default:
|
||||||
return rb.PipelineRuleset(name, NewPipeline(standardRulesetStages...))
|
stages = append(stages, standardRulesetStages[1:]...)
|
||||||
}
|
}
|
||||||
|
return rb.PipelineRuleset(name, NewPipeline(stages...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// PipelineRuleset provides an implementation of the Ruleset using a pipeline with a name.
|
// PipelineRuleset provides an implementation of the Ruleset using a pipeline with a name.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package rules_test
|
package rules_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/BattlesnakeOfficial/rules"
|
"github.com/BattlesnakeOfficial/rules"
|
||||||
|
|
@ -138,6 +139,94 @@ func TestRulesetBuilder(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRulesetBuilderGameOver(t *testing.T) {
|
||||||
|
settings := rules.Settings{
|
||||||
|
RoyaleSettings: rules.RoyaleSettings{
|
||||||
|
ShrinkEveryNTurns: 12,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
moves := []rules.SnakeMove{
|
||||||
|
{ID: "1", Move: "up"},
|
||||||
|
}
|
||||||
|
boardState := rules.NewBoardState(7, 7)
|
||||||
|
boardState.Snakes = append(boardState.Snakes, rules.Snake{
|
||||||
|
ID: "1",
|
||||||
|
Body: []rules.Point{
|
||||||
|
{X: 3, Y: 3},
|
||||||
|
{X: 3, Y: 3},
|
||||||
|
{X: 3, Y: 3},
|
||||||
|
},
|
||||||
|
Health: 100,
|
||||||
|
})
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
gameType string
|
||||||
|
solo bool
|
||||||
|
gameOver bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
gameType: rules.GameTypeStandard,
|
||||||
|
solo: false,
|
||||||
|
gameOver: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameType: rules.GameTypeConstrictor,
|
||||||
|
solo: false,
|
||||||
|
gameOver: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameType: rules.GameTypeRoyale,
|
||||||
|
solo: false,
|
||||||
|
gameOver: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameType: rules.GameTypeWrapped,
|
||||||
|
solo: false,
|
||||||
|
gameOver: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameType: rules.GameTypeSolo,
|
||||||
|
solo: false,
|
||||||
|
gameOver: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameType: rules.GameTypeStandard,
|
||||||
|
solo: true,
|
||||||
|
gameOver: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameType: rules.GameTypeConstrictor,
|
||||||
|
solo: true,
|
||||||
|
gameOver: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameType: rules.GameTypeRoyale,
|
||||||
|
solo: true,
|
||||||
|
gameOver: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameType: rules.GameTypeWrapped,
|
||||||
|
solo: true,
|
||||||
|
gameOver: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("%v_%v", test.gameType, test.solo), func(t *testing.T) {
|
||||||
|
rsb := rules.NewRulesetBuilder().WithParams(map[string]string{
|
||||||
|
rules.ParamGameType: test.gameType,
|
||||||
|
}).WithSolo(test.solo)
|
||||||
|
|
||||||
|
ruleset := rsb.Ruleset()
|
||||||
|
|
||||||
|
gameOver, _, err := ruleset.Execute(boardState, settings, moves)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, test.gameOver, gameOver)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStageFuncContract(t *testing.T) {
|
func TestStageFuncContract(t *testing.T) {
|
||||||
//nolint:gosimple
|
//nolint:gosimple
|
||||||
var stage rules.StageFunc
|
var stage rules.StageFunc
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue