Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

config.gno

5.52 Kb · 200 lines
  1// Package penalty holds everything about a penalty kick that is pure
  2// computation: the constants a game is played with, the ten parameters of a
  3// kick, how 32 bytes of entropy map onto them, the shootout rules, and
  4// SimulateShot.
  5//
  6// It has no state and no realm dependencies, so every version of the game
  7// realm agrees on these types, and a client can run the same code to check a
  8// result. All quantities are integers: millimetres, mm/s, milliseconds,
  9// per-mille (0..1000) and milli-radii (thousandths of the ball's radius), so
 10// the outcome is identical on every node.
 11package penalty
 12
 13import (
 14	"errors"
 15	"strconv"
 16	"strings"
 17)
 18
 19// Config is the set of keeper and ball constants a game is created with.
 20type Config struct {
 21	GoalWidth  int64 // inside width, mm
 22	GoalHeight int64 // inside height, mm
 23
 24	KeeperScale int64 // keeper size relative to a ~1.85 m keeper, per-mille
 25	ReactionMin int64 // keeper reaction delay range, ms
 26	ReactionMax int64
 27	StarShare   int64 // share of the keeper's speed range that means "star jump", per-mille
 28	ReadShare   int64 // share that means "wait and read the shot", per-mille
 29	DiveMin     int64 // committed dive speed range, mm/s
 30	DiveMax     int64
 31	JumpMin     int64 // keeper vertical launch speed range, mm/s (negative = collapse low)
 32	JumpMax     int64
 33
 34	BallMinSpeed int64 // ball speed at 0% power, mm/s
 35	BallMaxSpeed int64 // ball speed at 100% power, mm/s
 36	FuzzBase     int64 // std-dev of the mishit at 0% power, milli-radii
 37	FuzzPower    int64 // extra std-dev at 100% power, milli-radii
 38	MaxContact   int64 // farthest contact offset a taker may choose, milli-radii
 39}
 40
 41// DefaultConfig returns the constants the reference simulation is tuned for.
 42func DefaultConfig() Config {
 43	return Config{
 44		GoalWidth:    6555,
 45		GoalHeight:   2424,
 46		KeeperScale:  1176,
 47		ReactionMin:  20,
 48		ReactionMax:  120,
 49		StarShare:    60,
 50		ReadShare:    240, // as the browser engine (PLAN.READ_SHARE)
 51		DiveMin:      2600,
 52		DiveMax:      5200,
 53		JumpMin:      -1400,
 54		JumpMax:      4000,
 55		BallMinSpeed: 10000,
 56		BallMaxSpeed: 34000,
 57		FuzzBase:     30,
 58		FuzzPower:    100,
 59		MaxContact:   500,
 60	}
 61}
 62
 63type bound struct {
 64	name   string
 65	lo, hi int64
 66}
 67
 68// The range each constant may be set to, so a game cannot be created with
 69// physics the simulation was never meant to handle. Order matches field().
 70var bounds = []bound{
 71	{"goalWidth", 5000, 7320},
 72	{"goalHeight", 1800, 2440},
 73	{"keeperScale", 900, 1300},
 74	{"reactionMin", 0, 300},
 75	{"reactionMax", 0, 400},
 76	{"starShare", 0, 300},
 77	{"readShare", 0, 700},
 78	{"diveMin", 1500, 7000},
 79	{"diveMax", 1500, 7000},
 80	{"jumpMin", -2500, 0},
 81	{"jumpMax", 0, 5000},
 82	{"ballMinSpeed", 5000, 40000},
 83	{"ballMaxSpeed", 5000, 40000},
 84	{"fuzzBase", 0, 150},
 85	{"fuzzPower", 0, 300},
 86	{"maxContact", 200, 800},
 87}
 88
 89func (c *Config) field(i int) *int64 {
 90	switch i {
 91	case 0:
 92		return &c.GoalWidth
 93	case 1:
 94		return &c.GoalHeight
 95	case 2:
 96		return &c.KeeperScale
 97	case 3:
 98		return &c.ReactionMin
 99	case 4:
100		return &c.ReactionMax
101	case 5:
102		return &c.StarShare
103	case 6:
104		return &c.ReadShare
105	case 7:
106		return &c.DiveMin
107	case 8:
108		return &c.DiveMax
109	case 9:
110		return &c.JumpMin
111	case 10:
112		return &c.JumpMax
113	case 11:
114		return &c.BallMinSpeed
115	case 12:
116		return &c.BallMaxSpeed
117	case 13:
118		return &c.FuzzBase
119	case 14:
120		return &c.FuzzPower
121	case 15:
122		return &c.MaxContact
123	}
124	panic("penalty: no config field " + strconv.Itoa(i))
125}
126
127var (
128	ErrConfigRange   = errors.New("penalty: config value out of range")
129	ErrConfigOrder   = errors.New("penalty: config range is inverted")
130	ErrConfigShares  = errors.New("penalty: starShare + readShare must not exceed 900")
131	ErrConfigUnknown = errors.New("penalty: unknown config key")
132	ErrConfigSyntax  = errors.New("penalty: config must be key=value pairs separated by commas")
133)
134
135// Validate checks every constant against its allowed range.
136func (c Config) Validate() error {
137	for i, b := range bounds {
138		v := *c.field(i)
139		if v < b.lo || v > b.hi {
140			return errors.New(ErrConfigRange.Error() + ": " + b.name + "=" + strconv.FormatInt(v, 10) +
141				" (allowed " + strconv.FormatInt(b.lo, 10) + ".." + strconv.FormatInt(b.hi, 10) + ")")
142		}
143	}
144	if c.ReactionMin > c.ReactionMax || c.DiveMin > c.DiveMax ||
145		c.JumpMin > c.JumpMax || c.BallMinSpeed > c.BallMaxSpeed {
146		return ErrConfigOrder
147	}
148	if c.StarShare+c.ReadShare > 900 {
149		return ErrConfigShares
150	}
151	return nil
152}
153
154// ParseConfig applies "key=value,key=value" overrides to DefaultConfig and
155// validates the result. An empty string gives the defaults. Keys are the
156// lower-camel names String prints, e.g. "goalWidth=6400,reactionMax=150".
157func ParseConfig(s string) (Config, error) {
158	c := DefaultConfig()
159	s = strings.TrimSpace(s)
160	if s != "" {
161		for _, kv := range strings.Split(s, ",") {
162			parts := strings.Split(strings.TrimSpace(kv), "=")
163			if len(parts) != 2 {
164				return Config{}, ErrConfigSyntax
165			}
166			key := strings.TrimSpace(parts[0])
167			v, err := strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 64)
168			if err != nil {
169				return Config{}, ErrConfigSyntax
170			}
171			found := false
172			for i, b := range bounds {
173				if b.name == key {
174					*c.field(i) = v
175					found = true
176					break
177				}
178			}
179			if !found {
180				return Config{}, errors.New(ErrConfigUnknown.Error() + ": " + key)
181			}
182		}
183	}
184	if err := c.Validate(); err != nil {
185		return Config{}, err
186	}
187	return c, nil
188}
189
190// String prints the config in the form ParseConfig accepts.
191func (c Config) String() string {
192	var b strings.Builder
193	for i, bd := range bounds {
194		if i > 0 {
195			b.WriteString(",")
196		}
197		b.WriteString(bd.name + "=" + strconv.FormatInt(*c.field(i), 10))
198	}
199	return b.String()
200}