
    Ii                         S r SSKrSSKrSSKrSSKrSSKrSSKJr  SSK	J
r
Jr  \R                  " S5      r\
(       a  SSKJr  SSKJr  SS	KJr   " S
 S5      rg)z8Mixin providing game duration estimation via simulation.    N)Path)TYPE_CHECKINGAnyz'playpalace.game_utils.duration_estimate   )Player)User)
TrustLevelc                       \ rS rSrSrSrSrSrSSS\S	S
4S jr	SS jr
S\\   S\S	\4S jrS\\   S	\\   4S jrS\S	\4S jrSrg
)DurationEstimateMixin   a  Estimate game duration via CLI simulations.

This mixin spawns background simulations and reports estimated duration
based on tick counts.

Expected Game attributes:
    _estimate_threads: list.
    _estimate_results: list.
    _estimate_errors: list.
    _estimate_running: bool.
    _estimate_lock: threading.Lock.
    players: list[Player].
    get_user(player) -> User | None.
    broadcast_l() / broadcast().
    get_type() -> str.
    get_min_players() -> int.
    TICKS_PER_SECOND: int (inherited or defined).

   r      playerr   	action_idreturnNc                   ^ ^ T R                  U5      nU(       a2  UR                  R                  [        R                  R                  :  a  gT R
                  (       a  U(       a  UR                  S5        g/ n[        T S5      (       aK  T R                  R                   H1  n[        T R                  U5      nUR                  SU SU 3/5        M3     [        [        T R                   Vs/ s H  owR                  (       a  M  UPM     sn5      T R!                  5       5      n[#        [$        5      R&                  R&                  S-  n	[(        R*                  [-        U	5      ST R/                  5       S[-        U5      S	S
/U-   m/ T l        / T l        / T l        UU 4S jn
[6        R8                  " U
SS9nUR;                  5         U/T l        ST l        T R=                  S5        gs  snf )z=Start duration estimation by spawning CLI simulation threads.Nzestimate-already-runningoptionsz-o=zcli.pysimulatez--botsz--jsonz--quietc            	        > [        TR                  5       GH  n  [        R                  " TSSSSS9nUR                  S:X  a  UR
                  (       av  [        R                  " UR
                  5      nSU;   aN  UR                  SS5      (       d5  TR                     TR                  R                  US   5        SSS5        M  M  M  UR                  (       aN  TR                     TR                  R                  UR                  R                  5       SS	 5        SSS5        GM  GM     g! , (       d  f       GM)  = f! , (       d  f       GM<  = f! [         a_  nTR                     TR                  R                  [!        U5      SS	 5        SSS5         SnAGM  ! , (       d  f        SnAGM  = fSnAff = f)
z9Run all simulations sequentially and collect tick counts.Tx   F)capture_outputtexttimeoutshellr   ticks	timed_outN   )rangeNUM_ESTIMATE_SIMULATIONS
subprocessrun
returncodestdoutjson_moduleloadsget_estimate_lock_estimate_resultsappendstderr_estimate_errorsstrip	Exceptionstr)_resultdataebase_cmdselfs       Hc:\Users\dbart\PlayPalace11\server\game_utils\duration_estimate_mixin.pyrun_simulationsHDurationEstimateMixin._action_estimate_duration.<locals>.run_simulationsT   sP   4889C'^^ '+! ##F ((A-&--*00?"d?488K3O3O!%!4!4 $ 6 6 = =d7m L "5!4 4P?  !00 11889L9L9NtPS9TU 10 ' : "5!4 10  C,,--44SVDS\B -,,,,Csx   BED0=EE(7EE0
E 	:E E
E	EE
F? F:,(F#F:#
F7-F:7F::F?T)targetdaemonzestimate-computing)get_usertrust_levelvaluer	   ADMIN_estimate_runningspeak_lhasattrr   __dataclass_fields__getattrextendmaxlenplayersis_spectatorget_min_playersr   __file__parentsys
executabler/   get_typer)   r,   _estimate_threads	threadingThreadstartbroadcast_l)r5   r   r   useroptions_args
field_namer=   pnum_botscli_pathr7   threadr4   s   `           @r6   _action_estimate_duration/DurationEstimateMixin._action_estimate_duration,   s   }}V$t''--
0@0@0F0FF!!78 4##"ll??
j9##Tj\5'+B$CD @
 st||J|!>>A|JKTMaMaMcd >((//(:NNMMMOM	
 	 "$ "!#	C. !!F"(!%-.e Ks   .G)G)c                    U R                   (       a  U R                  (       d  g[        S U R                   5       5      nU(       d  gU R                     [	        U R
                  5      n[	        U R                  5      nSSS5        / U l        / U l        / U l        SU l         W(       a  [        U5      [        U5      -  nU R                  X$5      nU R                  U5      nU R                  U5      nU R                  U5      nU R                  X@R                  -  5      n	U(       a#  [        U5       S[        U5      S:  a  SOS S3n
OSn
U R                  S	UUU
U	S
9  gW(       a8  [        R                  S[        U5      US   SS 5        U R                  S5        gU R                  S5        g! , (       d  f       GN;= f)z_Check if duration estimation simulations have completed.

Called automatically from on_tick().
Nc              3   J   #    U  H  oR                  5       (       + v   M     g 7f)N)is_alive).0ts     r6   	<genexpr>BDurationEstimateMixin.check_estimate_completion.<locals>.<genexpr>{   s     H1GA::<''1Gs   !#Fz outlier   s z
 removed. zestimate-result)bot_timestd_devoutlier_info
human_timez/Duration estimation failed with %d error(s): %sr   r   zestimate-error)r?   rO   allr(   listr)   r,   sumrF   _calculate_std_dev_detect_outliers_format_durationHUMAN_SPEED_MULTIPLIERrS   LOGwarning)r5   all_donetick_countserrors	avg_ticksstd_dev_ticksoutliersrg   rh   rj   ri   s              r6   check_estimate_completion/DurationEstimateMixin.check_estimate_completionr   s   
 %%T-C-C H1G1GHH   t556K$//0F !
 "$!# "!& K(3{+;;I 33KKM,,[9H ,,Y7H++M:G..y;V;V/VWJ 8}oXS]Q5FcB-OzZ   "!!)%   EK4C   !12  !12[ ! s   +F33
Gvaluesmeanc                 p   ^ [        U5      S:  a  g[        U4S jU 5       5      [        U5      -  nUS-  $ )z1Calculate standard deviation of a list of values.r   g        c              3   2   >#    U  H  oT-
  S -  v   M     g7f)r   N )r`   xr}   s     r6   rb   ;DurationEstimateMixin._calculate_std_dev.<locals>.<genexpr>   s     71DQs   g      ?)rF   rm   )r5   r|   r}   variances     ` r6   rn   (DurationEstimateMixin._calculate_std_dev   s4    v;?777#f+E}    c                     [        U5      S:  a  / $ [        U5      n[        U5      nX#S-     nUSU-  S-     nXT-
  nUSU-  -
  nUSU-  -   nU V	s/ s H  oU:  d  X:  d  M  U	PM     sn	$ s  sn	f )zADetect outliers using IQR method. Returns list of outlier values.      g      ?)rF   sorted)
r5   r|   sorted_valsnq1q3iqrlower_boundupper_boundvs
             r6   ro   &DurationEstimateMixin._detect_outliers   s    v;?IVna !a%A&g39n39n!H6a_6HHHs   A-$A-r   c                     [        XR                  -  5      nUS-  nUS-  S-  nUS-  nUS:  a  U SUS SUS 3$ US:  a  U SUS 3$ U S3$ )zFormat a tick count as a human-readable duration string.

Args:
    ticks: Number of game ticks (50ms each).

Returns:
    Formatted string like "1:23:45" or "5:30" or "45 seconds".
i  <   r   :02dz seconds)intTICKS_PER_SECOND)r5   r   total_secondshoursminutessecondss         r6   rp   &DurationEstimateMixin._format_duration   s     E$9$99:% 4'B."$19WAgc]!GC=99q[Ya}--Yh''r   )r,   r)   r?   rO   )r   N)__name__
__module____qualname____firstlineno____doc__r    rq   r   r/   r[   rz   rl   r   floatrn   ro   rp   __static_attributes__r   r   r6   r   r      s    (  "D/ D/S D/T D/L;3zc % E ItCy IT#Y I (e ( (r   r   )r   loggingr!   rL   jsonr%   rP   pathlibr   typingr   r   	getLoggerrr   
games.baser   server.core.users.baser   r	   r   r   r   r6   <module>r      sF    >   
    %AB#+ -H( H(r   