Module server.rating

Type definitions for player ratings

Functions

def default_rating() ‑> Rating

Classes

class InclusiveRange (lo: Optional[float] = None, hi: Optional[float] = None)

A simple inclusive range.

Examples

assert 10 in InclusiveRange() assert 10 in InclusiveRange(0) assert 10 in InclusiveRange(0, 10) assert -1 not in InclusiveRange(0, 10) assert 11 not in InclusiveRange(0, 10)

Expand source code
class InclusiveRange():
    """
    A simple inclusive range.

    # Examples
    assert 10 in InclusiveRange()
    assert 10 in InclusiveRange(0)
    assert 10 in InclusiveRange(0, 10)
    assert -1 not in InclusiveRange(0, 10)
    assert 11 not in InclusiveRange(0, 10)
    """

    def __init__(self, lo: Optional[float] = None, hi: Optional[float] = None):
        self.lo = lo
        self.hi = hi

    def __contains__(self, rating: float) -> bool:
        if self.lo is not None and rating < self.lo:
            return False
        if self.hi is not None and rating > self.hi:
            return False
        return True

    def __eq__(self, other: object) -> bool:
        return (
            isinstance(other, type(self))
            and self.lo == other.lo
            and self.hi == other.hi
        )
class Leaderboard (id: int, technical_name: str, initializer: Optional[ForwardRef('Leaderboard')] = None)

Leaderboard(id: int, technical_name: str, initializer: Optional[ForwardRef('Leaderboard')] = None)

Expand source code
@dataclass(init=False)
class Leaderboard():
    id: int
    technical_name: str
    # Need the type annotation here so that the dataclass decorator sees it as
    # a field and includes it in generated methods (such as __eq__)
    initializer: WeakAttribute["Leaderboard"] = WeakAttribute["Leaderboard"]()

    def __init__(
        self,
        id: int,
        technical_name: str,
        initializer: Optional["Leaderboard"] = None
    ):
        self.id = id
        self.technical_name = technical_name
        if initializer:
            self.initializer = initializer

    def __repr__(self) -> str:
        initializer = self.initializer
        initializer_name = "None"
        if initializer:
            initializer_name = initializer.technical_name
        return (
            f"{self.__class__.__name__}("
            f"id={self.id}, technical_name={self.technical_name}, "
            f"initializer={initializer_name})"
        )

Class variables

var id : int
var technical_name : str

Instance variables

var initializerWeakAttribute[Leaderboard]

Transparently allow an object attribute to reference another object via a weak reference.

Expand source code
def __get__(self, obj: object, objclass: type) -> Optional[T]:
    ref = obj.__dict__.get(self.name)
    if ref:
        return ref()
    return None
class PlayerRatings (leaderboards: dict[str, Leaderboard], init: bool = True)

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

Expand source code
class PlayerRatings(dict[str, Rating]):
    def __init__(self, leaderboards: dict[str, Leaderboard], init: bool = True):
        self.leaderboards = leaderboards
        # Rating types which are present but should be recomputed.
        self.transient: set[str] = set()
        # Rating types which have been computed since the last rating update
        self.clean: set[str] = set()

        # DEPRECATED: Initialize known rating types so the client can display them
        if init:
            _ = self[RatingType.GLOBAL]
            _ = self[RatingType.LADDER_1V1]

    def __setitem__(self, rating_type: str, value: AnyRating) -> None:
        self.transient.discard(rating_type)
        # This could be optimized further by walking backwards along the
        # initialization chain and only unmarking the ratings we come accross,
        # but this adds complexity so we won't bother unless it really becomes
        # a performance bottleneck, which is unlikely.
        self.clean.clear()
        super().__setitem__(rating_type, Rating.of(value))

    def __getitem__(
        self,
        rating_type: str,
        history: Optional[set[str]] = None,
    ) -> Rating:
        history = history or set()
        entry = self.get(rating_type)

        if (
            entry is None or
            (rating_type not in self.clean and rating_type in self.transient)
        ):
            # Check for cycles
            if rating_type in history:
                return default_rating()

            rating = self._get_initial_rating(rating_type, history=history)

            self.transient.add(rating_type)
            self.clean.add(rating_type)
            super().__setitem__(rating_type, rating)
            return rating

        return super().__getitem__(rating_type)

    def _get_initial_rating(
        self,
        rating_type: str,
        history: set[str],
    ) -> Rating:
        """Create an initial rating when no rating exists yet."""
        leaderboard = self.leaderboards.get(rating_type)
        if leaderboard is None or leaderboard.initializer is None:
            return default_rating()

        history.add(rating_type)
        init_rating_type = leaderboard.initializer.technical_name
        rating = self.__getitem__(init_rating_type, history=history)

        if rating.dev > 250 or init_rating_type in self.transient:
            return rating

        return Rating(rating.mean, min(rating.dev + 150, 250))

    def update(self, other: dict[str, Rating]):
        self.transient -= set(other)
        self.clean.clear()
        if isinstance(other, PlayerRatings):
            self.transient |= other.transient
        else:
            other = {key: Rating.of(value) for key, value in other.items()}
        super().update(other)

Ancestors

  • builtins.dict

Methods

def update(self, other: dict[str, Rating])

D.update([E, ]**F) -> None. Update D from dict/iterable E and F. If E is present and has a .keys() method, then does: for k in E: D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]

class Rating (mean: float, dev: float)

A container for holding a mean, deviation pair and computing the displayed rating.

Uses mean, dev to differentiate from the trueskill.Rating type which uses mu, sigma.

Expand source code
class Rating(NamedTuple):
    """
    A container for holding a mean, deviation pair and computing the displayed
    rating.

    Uses mean, dev to differentiate from the trueskill.Rating type which uses
    mu, sigma.
    """
    mean: float
    dev: float

    def of(value: AnyRating) -> "Rating":
        if isinstance(value, trueskill.Rating):
            return Rating(value.mu, value.sigma)
        elif isinstance(value, Rating):
            return value

        return Rating(*value)

    def displayed(self) -> float:
        return self.mean - 3 * self.dev

Ancestors

  • builtins.tuple

Instance variables

var dev : float

Alias for field number 1

var mean : float

Alias for field number 0

Methods

def displayed(self) ‑> float
def of(value: Union[ForwardRef('Rating'), trueskill.Rating, tuple[float, float]]) ‑> Rating
class RatingType
Expand source code
class RatingType():
    GLOBAL = "global"
    LADDER_1V1 = "ladder_1v1"

Class variables

var GLOBAL
var LADDER_1V1