
    Iin.                         S r SSKJr  SSKJrJrJr  SSKJr  \(       a  SSK	J
r
  SSKJr  \ " S	 S
5      5       r " S S5      r\ " S S5      5       r " S S5      rg)z
Stats helper utilities for leaderboards and player ratings.

Provides:
- LeaderboardHelper: Build leaderboards from game results
- RatingHelper: Track player skill ratings using OpenSkill (Plackett-Luce model)
    )	dataclass)AnyCallableTYPE_CHECKING)PlackettLuce   )
GameResult   )Databasec                   H    \ rS rSr% Sr\\S'   \\S'   \\-  \S'   \\S'   Sr	g)	LeaderboardEntry   zEntry in a leaderboard.

Attributes:
    player_id: Player UUID.
    player_name: Display name.
    value: Aggregated metric value.
    rank: 1-based rank.
	player_idplayer_namevaluerank N)
__name__
__module____qualname____firstlineno____doc__str__annotations__intfloat__static_attributes__r       >c:\Users\dbart\PlayPalace11\server\game_utils\stats_helpers.pyr   r      s#     N;
Ir   r   c                       \ rS rSrSr\ SS\S   S\S\/\	\
-  S-  4   S\S\\   4S	 jj5       r\ SS\S   S
\S/\S-  4   S\\   4S jj5       rSrg)LeaderboardHelper$   zBuild leaderboards from game results.

Supports aggregation modes (sum, max, avg, count) and custom score
extraction functions.
resultsr	   score_extractorN	aggregatereturnc           
      $   0 n0 nU  H  nUR                    H  nUR                  (       a  UR                  (       d  M'  U" XVR                  5      nUc  M>  UR                  U;  a&  / X6R                  '   UR                  XFR                  '   X6R                     R                  U5        M     M     0 nUR                  5        Hz  u  pUS:X  a  [        U
5      X'   M  US:X  a  [        U
5      X'   M/  US:X  a$  U
(       a  [        U
5      [        U
5      -  OSX'   MY  US:X  a  [        U
5      X'   Mn  [        SU 35      e   [        UR                  5       S SS	9n/ n[        US
5       H%  u  nu  pUR                  [        U	XI   UUS95        M'     U$ )ao  
Build a leaderboard from game results.

Args:
    results: List of game results to aggregate
    score_extractor: Function that extracts a score for a player from a result.
                     Takes (result, player_id) and returns score or None.
    aggregate: Aggregation mode - "sum", "max", "avg", or "count"

Returns:
    Sorted list of LeaderboardEntry (highest first)

Example:
    # Total points scored across all Pig games
    leaderboard = LeaderboardHelper.build_from_results(
        results,
        score_extractor=lambda r, pid: r.custom_data.get("final_scores", {}).get(pid),
        aggregate="sum"
    )
summaxavgr   countzUnknown aggregate mode: c                     U S   $ )Nr   r   )xs    r   <lambda>6LeaderboardHelper.build_from_results.<locals>.<lambda>d   s    !A$r   T)keyreverser   )r   r   r   r   )player_resultsis_botis_virtual_botr   r   appenditemsr(   r)   len
ValueErrorsorted	enumerater   )r#   r$   r%   player_scoresplayer_namesresultplayerscore
aggregatedr   scoressorted_playersentriesr   r   s                  r   build_from_results$LeaderboardHelper.build_from_results+   s   6 79')F //==)>)>'0@0@A$''}<:<&6&679?9K9K%5%56!"2"23::5A 0  .0
!.!4!4!6IE!(+F
%e#(+F
%e#EKFc&k(AQR
%g%(+F
% #;I;!GHH "7  
 0 0 2PTU(1.!(D$D$9NN ' , 7	 )E r   winner_extractorc                    ^ Tc  SSS[         S-  4S jmSSS[         S[        S-  4U4S jjn[        R                  XS	S
9$ )a'  
Build a leaderboard based on win count.

Args:
    results: List of game results
    winner_extractor: Function to extract winner player_id from result.
                      Defaults to checking custom_data["winner_name"].

Returns:
    Sorted list of LeaderboardEntry by wins (highest first)
Nrr	   r&   c                     U R                   R                  S5      nU(       a1  U R                   H!  nUR                  U:X  d  M  UR                  s  $    g)z-Extract winner player_id from the GameResult.winner_nameN)custom_datagetr2   r   r   )rH   rJ   ps      r   rF   BLeaderboardHelper.build_wins_leaderboard.<locals>.winner_extractor   sD    mm//>--==K7#$;;. . r   r=   r   c                 "   > T" U 5      nX!:X  a  gg)z;Return 1 for wins, 0 otherwise for leaderboard aggregation.r   r   r   )r=   r   	winner_idrF   s      r   r$   ALeaderboardHelper.build_wins_leaderboard.<locals>.score_extractor   s    (0I%r   r(   )r%   )r   r   r!   rD   )r#   rF   r$   s    ` r   build_wins_leaderboard(LeaderboardHelper.build_wins_leaderboards   sY      #L S4Z 	L 	S 	S4Z 	 !33GX]3^^r   r   )r(   N)r   r   r   r   r   staticmethodlistr   r   r   r   r   rD   rR   r   r   r   r   r!   r!   $   s      El#E!<"5sU{T7I"IJE E 
		E EN  BF!_l#!_"L>3:#=>!_ 
	!_ !_r   r!   c                   ^    \ rS rSr% Sr\\S'   \\S'   \\S'   \S\4S j5       r	S\4S jr
S	rg
)PlayerRating   zPlayer skill rating values.

Attributes:
    player_id: Player UUID.
    mu: Mean skill estimate.
    sigma: Uncertainty (standard deviation).
r   musigmar&   c                 :    U R                   SU R                  -  -
  $ )z+Conservative skill estimate (mu - 3*sigma).   rZ   r[   selfs    r   ordinalPlayerRating.ordinal   s     wwTZZ''r   c                 <    U R                   S SU R                  S 3$ )z!Return a formatted rating string.z.1fu    ± r^   r_   s    r   __str__PlayerRating.__str__   s     ''#d4::c"233r   r   N)r   r   r   r   r   r   r   r   propertyra   rd   r   r   r   r   rX   rX      s=     NIL( ( (4 4r   rX   c            	          \ rS rSrSrSrSrSSS\4S jrS	\S
\	4S jr
S\\   S
\\\	4   4S jrS\\\      S
\\\	4   4S jr SSSS\S/\\\      4   S-  S
\\\	4   4S jjrSS\S
\\	   4S jjrS\S\S
\4S jrSrg)RatingHelper   zTrack player ratings using OpenSkill (Plackett-Luce).

Supports multi-player games, teams, and ties. Ratings are persisted per
game type and updated after each completed game.
g      9@g @dbr   	game_typec                 :    Xl         X l        [        5       U l        g)z
Create a rating helper for a specific game type.

Args:
    db: Database connection for persistence
    game_type: The game type these ratings are for
N)rj   rk   r   model)r`   rj   rk   s      r   __init__RatingHelper.__init__   s     "!^
r   r   r&   c                     U R                   R                  XR                  5      nU(       a  Uu  p4[        XUS9$ [        UU R                  U R
                  S9$ )zY
Get a player's current rating.

Returns default rating if player has no rating history.
r   rZ   r[   )rj   get_player_ratingrk   rX   
DEFAULT_MUDEFAULT_SIGMA)r`   r   r=   rZ   r[   s        r   
get_ratingRatingHelper.get_rating   sS     **9nnEIB)%HH$$
 	
r   
player_idsc                 P    U Vs0 s H  o"U R                  U5      _M     sn$ s  snf )z!Get ratings for multiple players.)ru   )r`   rw   pids      r   get_ratingsRatingHelper.get_ratings   s&    5?@ZcT__S))Z@@@s   #rankingsc           	         U VVs/ s H  o"  H  o3PM     M     nnnU R                  U5      n/ nU H`  n/ nU HD  nXS   nUR                  U R                  R                  UR                  UR
                  S95        MF     UR                  U5        Mb     U R                  R                  U5      n	0 n
[        U5       H|  u  p[        U5       Hh  u  pX   U   nU R                  R                  X0R                  UR                  UR
                  5        [        UUR                  UR
                  S9X'   Mj     M~     U
$ s  snnf )a  
Update ratings based on game outcome.

Args:
    rankings: Ordered list of player groups by placement.
              First group = 1st place, second = 2nd place, etc.
              Players in same group = tie for that position.

Returns:
    Dictionary of updated ratings for all players.

Example:
    # Alice won, Bob and Charlie tied for 2nd
    helper.update_ratings([["alice_id"], ["bob_id", "charlie_id"]])

    # Free-for-all: Alice 1st, Bob 2nd, Charlie 3rd
    helper.update_ratings([["alice"], ["bob"], ["charlie"]])
r^   rq   )rz   r5   rm   ratingrZ   r[   rater:   rj   set_player_ratingrk   rX   )r`   r|   groupry   all_playerscurrent_ratingsteamsteam_ratingsrH   	new_teamsupdated_ratings	group_idx
player_idx
new_ratings                 r   update_ratingsRatingHelper.update_ratings   s/   . )1BuESsEsB **;7 EL#(##DJJ$5$5QWW$5$MN  LL&  JJOOE*	 46 )( 3I#,U#3
&1*=
))#~~z}}jN^N^_'3!!}}$**($ $4 !4 = Cs   E Nr=   r	   ranking_extractorc                     Uc  SSS[         [         [              4S jnU" U5      nU(       d  0 $ U R                  U5      $ )z
Update ratings from a GameResult.

Args:
    result: The game result
    ranking_extractor: Function to extract rankings from result.
                       Defaults to simple winner-vs-rest extraction.

Returns:
    Dictionary of updated ratings.
rH   r	   r&   c                    U R                   R                  S5      nU R                   Vs/ s H(  o"R                  (       a  UR                  (       d  M&  UPM*     nnU(       d  / $ U(       a]  Sn/ nU H<  nUR
                  U:X  a  UR                  nM!  UR                  UR                  5        M>     U(       a  U(       a  U/U/$ U//$ U Vs/ s H  o"R                  PM     sn/$ s  snf s  snf )z0Build winner-vs-rest rankings from a GameResult.rJ   N)rK   rL   r2   r3   r4   r   r   r5   )rH   rJ   rM   human_playersrP   otherss         r   r   :RatingHelper.update_from_result.<locals>.ranking_extractor$  s    mm//>,-,<,< a,<qHHPQP`P`,< a$I $IF*==K7()I"MM!++6	 + !!%.K#88!*}, /<<mm<==) !b( =s   %C%C%C*)rV   r   r   )r`   r=   r   r|   s       r   update_from_resultRatingHelper.update_from_result  sJ      $>\ >d49o >4 %V,I""8,,r   limitc                     U R                   R                  U R                  U5      nU VVVs/ s H  u  p4n[        X4US9PM     snnn$ s  snnnf )zr
Get the rating leaderboard for this game type.

Returns players sorted by ordinal (conservative skill estimate).
rq   )rj   get_rating_leaderboardrk   rX   )r`   r   rowsry   rZ   r[   s         r   get_leaderboardRatingHelper.get_leaderboardD  sC     ww--dnneDVZ[VZNCUs?VZ[[[s   A	
player1_id
player2_idc                 B   U R                  U5      nU R                  U5      nU R                  R                  UR                  UR                  S9nU R                  R                  UR                  UR                  S9nU R                  R                  U/U//5      S   $ )zW
Predict the probability that player1 beats player2.

Returns a value between 0 and 1.
r^   r   )ru   rm   r~   rZ   r[   predict_win)r`   r   r   r1r2rating1rating2s          r   predict_win_probability$RatingHelper.predict_win_probabilityM  s     __Z(__Z(**##ruuBHH#=**##ruuBHH#= zz%%y7)&<=a@@r   )rj   rk   rm   rT   )
   )r   r   r   r   r   rs   rt   r   rn   rX   ru   rV   dictrz   r   r   r   r   r   r   r   r   r   r   r   rh   rh      s    JM
$: 
$# 
$
C 
L 
 Ad3i ADl9J4K A5tCy/5 
c<	 5t OS0-0- $\NDcO$CDtK0- 
c<	 	0-d\S \$|2D \A# A3 A5 Ar   rh   N)r   dataclassesr   typingr   r   r   openskill.modelsr   game_resultr	   persistence.databaser   r   r!   rX   rh   r   r   r   <module>r      sp    " / / )'/    q_ q_h 4 4 4.jA jAr   