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

v0 source pure

v0 - Unaudited: This is an initial version that has not yet been formally audited. Use in production at your own risk.

Overview

v0 - Unaudited: This is an initial version that has not yet been formally audited. Use in production at your own risk.

Package payout defines how the stakes of a gnofly arena round are shared out once the round is over: a Rule turns what happened, who died and by whose hand, into who gets what.

The package is pure: it holds no state, moves no coin and knows nothing about roles. A realm keeps the stakes, asks a rule for a Result and makes the payments itself:

Example
 1players, err := payout.ParsePlayers("g1aaa...,g1bbb...,g1ccc...")
 2if err != nil {
 3	panic(err)
 4}
 5deaths, err := payout.ParseDeaths("g1bbb...:g1aaa...;g1ccc...:")
 6if err != nil {
 7	panic(err)
 8}
 9g := payout.Game{Stake: 10000000, Lives: 3, KillTarget: 0, FeeBps: 500}
10res, err := payout.Lookup(payout.ModeDeathmatch).Settle(g, players, deaths)

Two rules ship with the package, both stateless values with no field:

  • BattleRoyale: the pot goes to the players still in at the end, or to the last one out;
  • Deathmatch: the pot goes to the top three by kills, 60%, 30% and 10%.

Every rule conserves the pot. For any input a rule accepts,

Example
1sum(res.Payouts) + res.Fee == Stake * len(players)

and nobody but a player of the round is ever paid. What is not paid to a player is the house's cut, the fee. Amounts are rounded down, and what rounding leaves over goes to the fee. A realm that pays out real coins should still check both properties on the Result it gets before it sends anything: that check is cheap, and it is what keeps a mistake in a rule from becoming a loss.

Constants 2

Variables 1

var ErrStake, ErrLives, ErrKillTarget, ErrFee, ErrTooFewPlayers, ErrTooManyPlayers, ErrEmptyPlayer, ErrDuplicatePlayer, ErrUnknownVictim, ErrDeadTooOften, ErrUnknownKiller, ErrSelfKill, ErrTooManyDeaths, ErrNoPlayers, ErrPlayersFormat, ErrDeathsFormat

 1var (
 2	ErrStake           = errors.New("payout: stake must be between 1 and 1000000000000 ugnot")
 3	ErrLives           = errors.New("payout: lives must be between 1 and 3")
 4	ErrKillTarget      = errors.New("payout: the kill target must be between 0 and 20")
 5	ErrFee             = errors.New("payout: the fee must be between 0 and 5000 bps")
 6	ErrTooFewPlayers   = errors.New("payout: at least 2 players are required")
 7	ErrTooManyPlayers  = errors.New("payout: at most 8 players are allowed")
 8	ErrEmptyPlayer     = errors.New("payout: a player has no address")
 9	ErrDuplicatePlayer = errors.New("payout: a player is listed twice")
10	ErrUnknownVictim   = errors.New("payout: a victim is not a player of the round")
11	ErrDeadTooOften    = errors.New("payout: a player died more often than they have lives")
12	ErrUnknownKiller   = errors.New("payout: a killer is not a player of the round")
13	ErrSelfKill        = errors.New("payout: a player cannot be their own killer")
14	ErrTooManyDeaths   = errors.New("payout: more deaths than lives in the round")
15
16	ErrNoPlayers     = errors.New("payout: players must list at least one address")
17	ErrPlayersFormat = errors.New("payout: players must be comma-separated addresses")
18	ErrDeathsFormat  = errors.New(`payout: deaths must be "victim:killer" entries separated by ";"`)
19)
source

Functions 3

func Modes

1func Modes() []string
source

Modes lists the modes of the shipped rules. The slice is a fresh copy.

func ParseDeaths

1func ParseDeaths(s string) ([]Death, error)
source

ParseDeaths parses a deaths string: "victim:killer" entries separated by ";", in the order they happened, "victim:" when nobody shot the victim down, for example

Example
1g1bbb...:g1aaa...;g1ccc...:

The empty string means nobody died and gives an empty list. At most MaxDeaths entries are accepted, and every address must be valid. Whitespace, empty entries and a trailing separator are refused.

It only checks the syntax. Whether the victims and the killers are players of the round, and how often they died, is decided by Rule.Settle.

func Lookup

1func Lookup(mode string) Rule
source

Lookup returns the rule of a mode, or nil for a mode nobody knows.

Types 7

type BattleRoyale

struct
1type BattleRoyale struct{}
source

BattleRoyale is the winner-takes-all rule. The pot is every stake; the fee is taken from it, and the rest goes to:

  • the last player in;
  • when the clock ran out with several in, the one of them with the most kills, and players still in with as many kills share it equally;
  • and when everyone is out, the player who went out last.

It is won by staying in the air, not by scoring: the kill target is ignored, and the arena realm refuses a battle-royale spec that has one.

What the split leaves over goes to the fee.

Methods on BattleRoyale

func Mode

method on BattleRoyale
1func (BattleRoyale) Mode() string
source

func Settle

method on BattleRoyale
1func (BattleRoyale) Settle(g Game, players []address, deaths []Death) (Result, error)
source

type Death

struct
1type Death struct {
2	Victim address
3	Killer address // "" when nobody shot the victim down
4}
source

Death is one life lost in a round: Victim was shot down by Killer, or by nobody (a crash, a disconnect). A pilot dies up to Game.Lives times and is out after the last one.

type Deathmatch

struct
1type Deathmatch struct{}
source

Deathmatch pays the most kills. The pot is every stake; the fee is taken from it, and the rest goes to the top three by kills: 60%, 30% and 10%.

  • Only players with at least one kill are ranked. With fewer than three of them, the shares of the ranks they fill are rescaled to the whole: one killer takes everything, two take 2/3 and 1/3.
  • Players with as many kills share the ranks they hold: two tied first split 60% + 30%, and the next one is third.
  • When nobody killed anybody, every player gets their stake back and the house takes nothing.

Deaths cost nothing here beyond the lives they take. What rounding leaves over goes to the fee.

Methods on Deathmatch

func Mode

method on Deathmatch
1func (Deathmatch) Mode() string
source

func Settle

method on Deathmatch
1func (Deathmatch) Settle(g Game, players []address, deaths []Death) (Result, error)
source

type Game

struct
1type Game struct {
2	Stake      int64 // ugnot, what each player paid
3	Lives      int64 // deaths a player can take, the last one puts them out
4	KillTarget int64 // kills that end the round, 0 for the clock only
5	FeeBps     int64 // the house's share, in basis points
6}
source

Game is what a rule needs to know about the arena a round was played in.

type Payout

struct
1type Payout struct {
2	Address address
3	Amount  int64 // ugnot
4}
source

Payout is an amount of ugnot to send to an address.

type Result

struct
1type Result struct {
2	Payouts []Payout // in the order of the players, players paid nothing left out
3	Fee     int64
4}
source

Result is what a rule decides: who gets what, and the house's cut.

type Rule

interface
1type Rule interface {
2	Mode() string
3	Settle(g Game, players []address, deaths []Death) (Result, error)
4}
source

Rule turns what happened in a round into who gets what.

Imports 1

  • errors stdlib

Source Files 6