Byte-snake-engine/royale.go
Torben 397d925110
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.
2022-03-16 16:58:05 -07:00

97 lines
2.4 KiB
Go

package rules
import (
"errors"
"math/rand"
)
type RoyaleRuleset struct {
StandardRuleset
Seed int64
ShrinkEveryNTurns int32
}
func (r *RoyaleRuleset) Name() string { return GameTypeRoyale }
func (r *RoyaleRuleset) CreateNextBoardState(prevState *BoardState, moves []SnakeMove) (*BoardState, error) {
if r.StandardRuleset.HazardDamagePerTurn < 1 {
return nil, errors.New("royale damage per turn must be greater than zero")
}
nextBoardState, err := r.StandardRuleset.CreateNextBoardState(prevState, moves)
if err != nil {
return nil, err
}
// Royale's only job is now to populate the hazards for next turn - StandardRuleset takes care of applying hazard damage.
err = r.populateHazards(nextBoardState)
if err != nil {
return nil, err
}
return nextBoardState, nil
}
func (r *RoyaleRuleset) populateHazards(b *BoardState) error {
_, err := r.callStageFunc(PopulateHazardsRoyale, b, []SnakeMove{})
return err
}
func PopulateHazardsRoyale(b *BoardState, settings Settings, moves []SnakeMove) (bool, error) {
b.Hazards = []Point{}
// Royale uses the current turn to generate hazards, not the previous turn that's in the board state
turn := b.Turn + 1
if settings.RoyaleSettings.ShrinkEveryNTurns < 1 {
return false, errors.New("royale game can't shrink more frequently than every turn")
}
if turn < settings.RoyaleSettings.ShrinkEveryNTurns {
return false, nil
}
randGenerator := rand.New(rand.NewSource(settings.RoyaleSettings.seed))
numShrinks := turn / settings.RoyaleSettings.ShrinkEveryNTurns
minX, maxX := int32(0), b.Width-1
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 y := int32(0); y < b.Height; y++ {
if x < minX || x > maxX || y < minY || y > maxY {
b.Hazards = append(b.Hazards, Point{x, y})
}
}
}
return false, nil
}
func (r RoyaleRuleset) Settings() Settings {
s := r.StandardRuleset.Settings()
s.RoyaleSettings = RoyaleSettings{
seed: r.Seed,
ShrinkEveryNTurns: r.ShrinkEveryNTurns,
}
return s
}
// Adaptor for integrating stages into RoyaleRuleset
func (r *RoyaleRuleset) callStageFunc(stage StageFunc, boardState *BoardState, moves []SnakeMove) (bool, error) {
return stage(boardState, r.Settings(), moves)
}