-
Notifications
You must be signed in to change notification settings - Fork 51
/
ruleset.go
143 lines (124 loc) · 4.32 KB
/
ruleset.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package rules
type Ruleset interface {
// Returns the name of the ruleset, if applicable.
Name() string
// Returns the settings used by the ruleset.
Settings() Settings
// Processes the next turn of the ruleset, returning whether the game has ended, the next BoardState, or an error.
// For turn zero (initialization), moves will be left empty.
Execute(prevState *BoardState, moves []SnakeMove) (gameOver bool, nextState *BoardState, err error)
}
type SnakeMove struct {
ID string
Move string
}
type rulesetBuilder struct {
params map[string]string // game customisation parameters
seed int64 // used for random events in games
rand Rand // used for random number generation
solo bool // if true, only 1 alive snake is required to keep the game from ending
settings *Settings // used to set settings directly instead of via string params
}
// NewRulesetBuilder returns an instance of a builder for the Ruleset types.
func NewRulesetBuilder() *rulesetBuilder {
return &rulesetBuilder{
params: map[string]string{},
}
}
// WithParams accepts a map of string parameters for customizing games.
//
// Parameters are copied. If called multiple times, parameters are merged such that:
// - existing keys in both maps get overwritten by the new ones
// - existing keys not present in the new map will be retained
// - non-existing keys only in the new map will be added
//
// Unrecognised parameters will be ignored and default values will be used.
// Invalid parameters (i.e. a non-numerical value where one is expected), will be ignored
// and default values will be used.
func (rb *rulesetBuilder) WithParams(params map[string]string) *rulesetBuilder {
for k, v := range params {
rb.params[k] = v
}
return rb
}
// WithSeed sets the seed used for randomisation by certain game modes.
func (rb *rulesetBuilder) WithSeed(seed int64) *rulesetBuilder {
rb.seed = seed
return rb
}
// WithRandom overrides the random number generator with a specific instance
// instead of a Rand initialized from the seed.
func (rb *rulesetBuilder) WithRand(rand Rand) *rulesetBuilder {
rb.rand = rand
return rb
}
// WithSolo sets whether the ruleset is a solo game.
func (rb *rulesetBuilder) WithSolo(value bool) *rulesetBuilder {
rb.solo = value
return rb
}
// WithSettings sets the settings object for the ruleset directly.
func (rb *rulesetBuilder) WithSettings(settings Settings) *rulesetBuilder {
rb.settings = &settings
return rb
}
// NamedRuleset constructs a known ruleset by using name to look up a standard pipeline.
func (rb rulesetBuilder) NamedRuleset(name string) Ruleset {
var stages []string
if rb.solo {
stages = append(stages, StageGameOverSoloSnake)
} else {
stages = append(stages, StageGameOverStandard)
}
switch name {
case GameTypeStandard:
stages = append(stages, standardRulesetStages[1:]...)
case GameTypeConstrictor:
stages = append(stages, constrictorRulesetStages[1:]...)
case GameTypeWrappedConstrictor:
stages = append(stages, wrappedConstrictorRulesetStages[1:]...)
case GameTypeRoyale:
stages = append(stages, royaleRulesetStages[1:]...)
case GameTypeSolo:
stages = soloRulesetStages
case GameTypeWrapped:
stages = append(stages, wrappedRulesetStages[1:]...)
default:
name = GameTypeStandard
stages = append(stages, standardRulesetStages[1:]...)
}
return rb.PipelineRuleset(name, NewPipeline(stages...))
}
// PipelineRuleset constructs a ruleset with the given name and pipeline using the parameters passed to the builder.
// This can be used to create custom rulesets.
func (rb rulesetBuilder) PipelineRuleset(name string, p Pipeline) Ruleset {
var settings Settings
if rb.settings != nil {
settings = *rb.settings
} else {
settings = NewSettings(rb.params).WithRand(rb.rand).WithSeed(rb.seed)
}
return &pipelineRuleset{
name: name,
pipeline: p,
settings: settings,
}
}
type pipelineRuleset struct {
pipeline Pipeline
name string
settings Settings
}
// impl Ruleset
func (r pipelineRuleset) Settings() Settings {
return r.settings
}
// impl Ruleset
func (r pipelineRuleset) Name() string { return r.name }
// impl Ruleset
func (r pipelineRuleset) Execute(bs *BoardState, sm []SnakeMove) (bool, *BoardState, error) {
return r.pipeline.Execute(bs, r.Settings(), sm)
}
func (r pipelineRuleset) Err() error {
return r.pipeline.Err()
}