Documentation
¶
Overview ¶
Package scoring computes challenge point values. It is pure: numbers in, numbers out — no I/O, no clock, no imports beyond stdlib (docs/v0.1/07-scoring.md).
Index ¶
- Constants
- func Deregister(name string)
- func Engines() map[string]ScoringEngine
- func IsBuiltinMode(mode string) bool
- func IsRegisteredMode(mode string) bool
- func Register(name string, e ScoringEngine, override bool) error
- func Value(mode string, params ChallengeScoring, solves int) int
- type ChallengeScoring
- type DynamicEngine
- type Registry
- func (r *Registry) Deregister(name string)
- func (r *Registry) Engines() map[string]ScoringEngine
- func (r *Registry) Get(mode string) (ScoringEngine, bool)
- func (r *Registry) Register(name string, e ScoringEngine, override bool) error
- func (r *Registry) Value(mode string, params ChallengeScoring, solves int) int
- type ScoringEngine
- type StaticEngine
Constants ¶
const ( ModeStatic = "static" ModeDynamic = "dynamic" )
ModeStatic and ModeDynamic are the built-in scoring mode ids. Their value is a pure function of the solve count, recomputed from the formula on every read. Any other mode is plugin-provided and scored locked-at-solve from a recorded value (0007) — never by calling the plugin on the read path.
Variables ¶
This section is empty.
Functions ¶
func Deregister ¶
func Deregister(name string)
Deregister removes a plugin-registered mode from the default registry (revert-before-death). A name that overrode a built-in restores the built-in; a name that only ever was a built-in is a no-op. Mirrors Register as the plugin loader's revert entry.
func Engines ¶
func Engines() map[string]ScoringEngine
Engines returns a snapshot of the default registry's engines (replaces the former Registry() function).
func IsBuiltinMode ¶
IsBuiltinMode reports whether mode is a platform built-in (static/dynamic). It is a CONSTANT check — no registry lookup, no plugin access, no I/O — so a scoreboard recompute can classify a solve and stay deterministic regardless of plugin or registry state (a plugin deregistered mid-read must not change how a past solve is scored). Everything else is a plugin mode, whose value the recompute reads from the per-solve record, not from any engine.
func IsRegisteredMode ¶
IsRegisteredMode reports whether a scoring mode id resolves in the default registry (built-in static/dynamic plus any loaded scoring plugins). It is the write-time guard for challenges.scoring — replacing the dropped DB CHECK, so an unknown mode is still rejected.
Types ¶
type ChallengeScoring ¶
type ChallengeScoring struct {
Initial int // points_initial
Min int // points_min (dynamic only)
Decay int // decay (dynamic only; solve count at which value reaches Min)
}
ChallengeScoring holds the scoring parameters of a challenge.
type DynamicEngine ¶
type DynamicEngine struct{}
DynamicEngine decays the value with solve count (CTFd-compatible shape):
Value = max(Min, round( Initial - (Initial-Min) * solves² / Decay² ))
func (DynamicEngine) Value ¶
func (DynamicEngine) Value(params ChallengeScoring, solves int) int
Value implements ScoringEngine.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry resolves a scoring mode name to its engine. v0.3 makes the previously-static map mutable + override-protected so the plugin loader can add scoring modes; with only the built-ins registered it is behaviourally identical to v0.2.2.
Concurrency (v0.3 decision): an atomic pointer to an IMMUTABLE map. Readers (Get/Value, the scoreboard hot path) are lock-free; Register copies-on-write and swaps the pointer under a writer mutex, so a swap is atomic from a reader's view — a lookup in flight resolves to the old map or the new one, never a torn or missing engine. Pinned by TestScoringRegistryReaderAtomicContention.
func NewRegistry ¶
func NewRegistry(builtins ...ScoringEngine) *Registry
NewRegistry builds a registry with the given engines registered as protected built-ins.
func (*Registry) Deregister ¶
Deregister removes a plugin-registered mode (revert-before-death when its plugin stops). If the name overrode a built-in, the built-in is RESTORED (the key keeps working via the built-in); otherwise the entry is removed and the mode falls back to static on use (Value's unknown-mode path). Deregistering a name that only ever was a built-in is a no-op (a built-in is not a plugin and cannot "stop"). Swapped atomically, so a reader never sees a torn map.
func (*Registry) Engines ¶
func (r *Registry) Engines() map[string]ScoringEngine
Engines returns a snapshot copy of the current mode→engine map (for listing).
func (*Registry) Get ¶
func (r *Registry) Get(mode string) (ScoringEngine, bool)
Get resolves an engine by mode name. Lock-free.
type ScoringEngine ¶
type ScoringEngine interface {
Name() string // "static" | "dynamic"
Value(params ChallengeScoring, solves int) int
}
ScoringEngine computes a challenge's current point value given its valid solve count.
type StaticEngine ¶
type StaticEngine struct{}
StaticEngine always returns the initial value.
func (StaticEngine) Value ¶
func (StaticEngine) Value(params ChallengeScoring, _ int) int
Value implements ScoringEngine: the value never changes.