const ModeBattleRoyale, ModeDeathmatch
The modes of the rules shipped with the package.
v0 - Unaudited: This is an initial version that has not yet been formally audited. Use in production at your own risk.
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:
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:
Every rule conserves the pot. For any input a rule accepts,
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.
The modes of the rules shipped with the package.
1const (
2 MinPlayers = 2
3 MaxPlayers = 8
4 MinLives = 1
5 MaxLives = 3
6 MaxKillTarget = 20
7 MinStake = 1 // ugnot
8 MaxStake = 1000000000000 // ugnot, 1e12
9 BpsScale = 10000 // basis points in a whole
10 MaxFeeBps = 5000 // the fee never takes more than half
11 MaxDeaths = MaxPlayers * MaxLives
12)Bounds every rule enforces.
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)Modes lists the modes of the shipped rules. The slice is a fresh copy.
ParseDeaths parses a deaths string: "victim:killer" entries separated by ";", in the order they happened, "victim:" when nobody shot the victim down, for 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.
Lookup returns the rule of a mode, or nil for a mode nobody knows.
BattleRoyale is the winner-takes-all rule. The pot is every stake; the fee is taken from it, and the rest goes to:
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.
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.
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%.
Deaths cost nothing here beyond the lives they take. What rounding leaves over goes to the fee.
Game is what a rule needs to know about the arena a round was played in.
Payout is an amount of ugnot to send to an address.
Result is what a rule decides: who gets what, and the house's cut.
Rule turns what happened in a round into who gets what.