Stability | unstable |
---|---|
Maintainer | Eirik <clux> Albrigtsen |
Safe Haskell | Safe-Infered |
Game.Tournament
Contents
Description
Tournament construction and maintenance including competition based structures and helpers.
This library is intended to be imported qualified as it exports functions that clash with Prelude.
import Game.Tournament as T
The Tournament structure contain a Map of GameId
-> Game
for its internal
representation and the GameId
keys are the location in the Tournament.
Duel tournaments are based on the theory from http://clux.org/entries/view/2407. By using the seeding definitions listed there, there is almost only one way to generate a tournament, and the ambivalence appears only in Double elimination.
We have additionally chosen that brackets should converge by having the losers bracket move upwards. This is not necessary, but improves the visual layout when presented in a standard way.
FFA tournaments use a collection of sensible assumptions on how to optimally split n people into s groups while minimizing the sum of seeds difference between groups for fairness. At the end of each round, groups are recalculated from the scores of the winners, and new groups are created for the next round.
- seeds :: Int -> Int -> (Int, Int)
- duelExpected :: Integral a => a -> (a, a) -> Bool
- groups :: Int -> Int -> [[Int]]
- robin :: Integral a => a -> [[(a, a)]]
- data GameId = GameId {}
- data Elimination
- data Bracket
- data Rules
- type Results = [Result]
- results :: Tournament -> Maybe Results
- data Result
- player :: Result -> Int
- placement :: Result -> Int
- wins :: Result -> Int
- total :: Result -> Int
- type Size = Int
- data Tournament
- type Score = Int
- type GroupSize = Int
- type Advancers = Int
- tournament :: Rules -> Size -> Tournament
- score :: GameId -> [Score] -> Tournament -> Tournament
- count :: Tournament -> Bracket -> Int
- scorable :: GameId -> Tournament -> Bool
- keys :: Tournament -> [GameId]
- testcase :: IO ()
Building Block A: Duel helpers
seeds :: Int -> Int -> (Int, Int)Source
Power of a tournament. It's defined as 2^num_players rounded up to nearest power of 2. type Power = Int
Computes both the upper and lower player seeds for a duel elimiation match. The first argument is the power of the tournament:
p :: 2^num_players rounding up to nearest power of 2
The second parameter is the game number i (in round one).
The pair (p,i) must obey
p > 0 && 0 < i <= 2^(p-1).
duelExpected :: Integral a => a -> (a, a) -> BoolSource
Check if the 3 criteria for perfect seeding holds for the current power and seed pair arguments. This can be used to make a measure of how good the seeding was in retrospect
Building Block B: Group helpers
groups :: Int -> Int -> [[Int]]Source
Splits a numer of players into groups of as close to equal seeding sum as possible. When groupsize is even and s | n, the seed sum is constant. Fixes the number of groups as ceil $ n / s, but will reduce s when all groups not full.
robin :: Integral a => a -> [[(a, a)]]Source
Round robin schedules a list of n players and returns a list of rounds (where a round is a list of pairs). Uses http:en.wikipedia.orgwikiRound-robin_tournament#Scheduling_algorithm
Tournament Types
The location of a game is written as to simulate the classical shorthand WBR2, but includes additionally the game number for complete positional uniqueness.
A Single
elimination final will have the unique identifier
let wbf = GameId WB p 1
where 'p == count t WB'.
data Elimination Source
Duel Tournament option.
Single
elimation is a standard power of 2 tournament tree,
wheras Double
elimination grants each loser a second chance in the lower bracket.
Instances
Results in descending order of placement.
Only constructed by score
once the last game was played.
results :: Tournament -> Maybe ResultsSource
Record of each player's accomplishments in the current tournament.
data Tournament Source
Tournament Interface
tournament :: Rules -> Size -> TournamentSource
Create match shells for an FFA elimination tournament.
Result comes pre-filled in with either top advancers or advancers intersect
seedList.
This means what the player numbers represent is only fixed per round.
TODO: Either String Tournament as return for intelligent error handling
score :: GameId -> [Score] -> Tournament -> TournamentSource
Score a match in a tournament and propagate winners/losers.
If match is not scorable
, the Tournament will pass through unchanged.
For a Duel tournament, winners (and losers if Double) are propagated immediately, wheras FFA tournaments calculate winners at the end of the round (when all games played).
There is no limitation on re-scoring old games, so care must be taken to not update too far back ones and leaving the tournament in an inconsistent state. When scoring games more than one round behind the corresponding active round, the locations to which these propagate must be updated manually.
To prevent yourself from never scoring older matches, only score games for which
safeScorable
returns True. Though this has not been implemented yet.
gid = (GameId WB 2 1) tUpdated = if safeScorable gid then score gid [1,0] t else t
TODO: strictify this function TODO: better to do a scoreSafe? call this scoreUnsafe
count :: Tournament -> Bracket -> IntSource
Count the number of rounds in a given bracket in a Tournament. TODO: rename to length once it has been less awkwardly moved into an internal part
scorable :: GameId -> Tournament -> BoolSource
Check if a GameId exists and is ready to be scored through score
.
keys :: Tournament -> [GameId]Source
Get the list of all GameIds in a Tournament. This list is also ordered by GameId's Ord, and in fact, if the corresponding games were scored in this order, the tournament would finish, and scorable would only return False for a few special walkover games. TODO: if introducing crossovers, this would not be true for LB crossovers => need to place them in WB in an 'interim round'