Coverage for src/duelboard/models.py: 100%

44 statements  

« prev     ^ index     » next       coverage.py v7.10.3, created at 2025-08-14 19:18 +0900

1"""Data models for Elo rating calculations.""" 

2 

3from dataclasses import dataclass 

4from enum import Enum 

5 

6from .types import Metadata 

7 

8 

9class BattleOutcome(Enum): 

10 """Battle outcome enum.""" 

11 

12 WIN_A = "player_a" 

13 WIN_B = "player_b" 

14 TIE = "tie" 

15 TIE_BOTHBAD = "tie (bothbad)" 

16 

17 

18@dataclass 

19class Battle: 

20 """Represents a battle between two players.""" 

21 

22 player_a: str 

23 player_b: str 

24 outcome: BattleOutcome 

25 metadata: Metadata | None = None 

26 

27 def __post_init__(self) -> None: 

28 if isinstance(self.outcome, str): 

29 self.outcome = BattleOutcome(self.outcome) 

30 

31 

32@dataclass 

33class Player: 

34 """Represents a player in the rating system.""" 

35 

36 name: str 

37 rating: float = 1000.0 

38 battles: int = 0 

39 wins: int = 0 

40 losses: int = 0 

41 ties: int = 0 

42 

43 @property 

44 def win_rate(self) -> float: 

45 """Calculate win rate.""" 

46 if self.battles == 0: 

47 return 0.0 

48 return self.wins / self.battles 

49 

50 

51@dataclass 

52class EloRating: 

53 """Represents an Elo rating result.""" 

54 

55 player: str 

56 rating: float 

57 confidence_interval: tuple[float, float] | None = None 

58 battles: int = 0 

59 

60 def __lt__(self, other: "EloRating") -> bool: 

61 return self.rating < other.rating 

62 

63 def __le__(self, other: "EloRating") -> bool: 

64 return self.rating <= other.rating 

65 

66 def __gt__(self, other: "EloRating") -> bool: 

67 return self.rating > other.rating 

68 

69 def __ge__(self, other: "EloRating") -> bool: 

70 return self.rating >= other.rating