
    Ii4                     x   S r SSKrSSKJrJr  SSKJr  SSKJr  SSK	J
r
Jr  SSKJr  \
(       a  SS	KJr   " S
 S\5      r " S S\5      r " S S\5      r\ " S S5      5       r\ " S S5      5       r\ " S S5      5       r\ " S S5      5       r\ " S S5      5       r\ " S S5      5       r " S S5      rg)z:Virtual bot management for simulating users on the server.    N)	dataclassfield)Enum)Path)TYPE_CHECKINGAny   )get_default_config_path)Serverc                   ,    \ rS rSrSrSrSrSrSrSr	Sr
g	)
VirtualBotState   zState machine for virtual bots.offlineonline_idlein_gameleaving_gamewaiting_for_table N)__name__
__module____qualname____firstlineno____doc__OFFLINEONLINE_IDLEIN_GAMELEAVING_GAMEWAITING_FOR_TABLE__static_attributes__r       7c:\Users\dbart\PlayPalace11\server\core\virtual_bots.pyr   r      s    )GKG!L+r    r   c                        \ rS rSrSrSrSrSrg)FallbackBehavior   z+Behavior when no guided-table rule applies.defaultdisabledr   N)r   r   r   r   r   DEFAULTDISABLEDr   r   r    r!   r#   r#      s    5GHr    r#   c                        \ rS rSrSrSrSrSrg)AllocationMode    z?How guided-table requirements are enforced when bots run short.best_effortstrictr   N)r   r   r   r   r   BEST_EFFORTSTRICTr   r   r    r!   r*   r*       s    IKFr    r*   c                      \ rS rSr% Sr\" \S9r\\   \	S'   Sr
\\	S'   Sr\\	S'   S	r\\	S
'   Sr\\	S'   Sr\\	S'   Sr\\	S'   Sr\\	S'   Sr\\	S'   Sr\\	S'   Sr\\	S'   Sr\\	S'   Sr\\	S'   Sr\\	S'   Sr\\	S'   Sr\\	S '   Sr\\	S!'   Sr\\	S"'   Sr\\	S#'   Sr\\	S$'   \R@                  r!\\	S%'   S&r"\\	S''   \#RH                  r%\#\	S('   S)r&g*)+VirtualBotConfig'   z7Configuration for virtual bots loaded from config.toml.default_factorynamesd   min_idle_ticksX  max_idle_ticks  min_online_ticksp  max_online_ticksmin_offline_ticks  max_offline_ticks   leave_game_delay_ticks  start_game_delay_ticks333333?join_game_chance皙?create_game_chance皙?go_offline_chanceQ?logout_after_game_chance(   logout_after_game_min_tickslogout_after_game_max_ticksr   max_tables_per_gamemin_bots_per_tablemax_bots_per_tablewaiting_min_tickswaiting_max_ticksfallback_behaviorr%   default_profileallocation_moder   N)'r   r   r   r   r   r   listr5   str__annotations__r7   intr9   r;   r=   r>   r@   rB   rD   rF   floatrH   rJ   rL   rN   rO   rP   rQ   rR   rS   rT   r#   r'   rU   rV   r*   r.   rW   r   r   r    r!   r1   r1   '   s   AT2E492 NCNC c  c  s !s!"%C%"%C% "e! ###u# '+e*'))'**  !   s s *:*B*B'B$OS$&4&@&@O^@r    r1   c                      \ rS rSr% Sr\\S'   Sr\S-  \S'   Sr	\S-  \S'   Sr
\S-  \S'   Sr\S-  \S'   Sr\S-  \S	'   Sr\S-  \S
'   Sr\S-  \S'   Sr\S-  \S'   Sr\S-  \S'   Sr\S-  \S'   Sr\S-  \S'   Sr\S-  \S'   Sr\S-  \S'   Sr\S-  \S'   Sr\S-  \S'   Sr\S-  \S'   Sr\S-  \S'   Sr\S-  \S'   Srg)VirtualBotProfileOverrideO   z8Per-profile overrides layered on top of the base config.nameNr7   r9   r;   r=   r>   r@   rB   rD   rF   rH   rJ   rL   rN   rO   rQ   rR   rS   rT   r   )r   r   r   r   r   rY   rZ   r7   r[   r9   r;   r=   r>   r@   rB   rD   rF   r\   rH   rJ   rL   rN   rO   rQ   rR   rS   rT   r   r   r    r!   r^   r^   O   s   B
I!%NC$J%!%NC$J%#'cDj'#'cDj'$(sTz($(sTz()-C$J-)-C$J-%)edl)'++&*ut|*-1edl1.2t2.2t2%)d
)%)d
)$(sTz($(sTz(r    r^   c                   H    \ rS rSr% Sr\\S'   \\   \S'   Sr\S-  \S'   Sr	g)BotGroupConfigh   z8Explicit grouping of bot names for guided-table routing.r`   botsNprofiler   )
r   r   r   r   r   rY   rZ   rX   re   r   r   r    r!   rb   rb   h   s!    B
I
s)OGS4Zr    rb   c                       \ rS rSr% Sr\\S'   \\S'   \\S'   \\S'   \\   \S'   Sr	\S-  \S	'   S
r
\\S'   Sr\\S'   Sr\\\4   S-  \S'   Srg)GuidedTableConfigq   z0Static configuration for a guided table/channel.r`   gamemin_botsmax_bots
bot_groupsNre   r6   priorityr   cycle_ticksactive_windowr   )r   r   r   r   r   rY   rZ   r[   rX   re   rm   rn   ro   tupler   r   r    r!   rg   rg   q   s\    :
I
IMMS	GS4ZHcK,0M5c?T)0r    rg   c                   b    \ rS rSr% Sr\\S'   Sr\S-  \S'   \	" \
S9r\
\   \S'   Sr\\S	'   S
rg)GuidedTableState   z-Runtime state for a guided table requirement.configNtable_idr3   assigned_botsFwarned_shortager   )r   r   r   r   r   rg   rZ   ru   rY   r   setrv   rw   boolr   r   r    r!   rr   rr      s6    7HcDj#C8M3s88!OT!r    rr   c                       \ rS rSr% Sr\\S'   \R                  r	\\S'   Sr
\\S'   Sr\\S'   Sr\\S'   Sr\\S	'   S
r\S
-  \S'   Sr\\S'   Sr\\S'   Sr\\S'   \" \S9r\\S4   \S'   S
r\S
-  \S'   Srg
)
VirtualBot   z(State tracking for a single virtual bot.r`   stater   cooldown_ticksonline_tickstarget_online_ticksthink_ticksNru   game_join_tickFlogout_after_gamer%   re   r3   .groupstarget_ruler   )r   r   r   r   r   rY   rZ   r   r   r}   r~   r[   r   r   r   ru   r   r   ry   re   r   rp   r   r   r   r   r    r!   r{   r{      s    2
I,44E?4 NCL#  K  HcDjNC#t#GS#E:FE#s(O:"Kt"r    r{   c                       \ rS rSrSrSMS jrSNS\\-  S-  SS4S jjrS\	\\
4   S\	\\4   4S	 jrS
\	\\
4   S\	\\4   4S jrS\	\\\   4   4S jrS\	\\4   4S jrS\\	\\
4      S\	\\4   4S jrS\S\\   4S jrSOS jrS\S\4S jrS\S\S-  4S jrS\S\4S jrS\\   4S jrSOS jrS\4S jr SOS jr!SOS jr"SOS jr#S\SS4S jr$S\%\\4   4S  jr&S\%\\4   4S! jr'S\SS4S" jr(S\	\\4   4S# jr)S\	\\
4   4S$ jr*S\	\\
4   4S% jr+S\	\\4   4S& jr,S'\	\\4   S\\	\\
4      4S( jr-S\\	\\
4      4S) jr.S*\\   S\%\	\\4   \\   4   4S+ jr/S\\	\\
4      4S, jr0SS-S\	\\
4   4S. jr1SS-S\%\\4   4S/ jr2SS-S\%\\S-  \\4   4S0 jr3SOS1 jr4S\SS4S2 jr5S\SS4S3 jr6S\SS4S4 jr7S\SS4S5 jr8S\SS4S6 jr9S\SS4S7 jr:S\S\4S8 jr;S\S\4S9 jr<S\SS4S: jr=SNS\S;\S-  S\4S< jjr>S\S\S\4S= jr?S\S\S\4S> jr@S\S\4S? jrAS\SS4S@ jrBS\SS4SA jrCS\SS4SB jrDS\SS4SC jrES\S\4SD jrFSE\S\4SF jrGSE\S\4SG jrHS\4SH jrIS\S\4SI jrJSJ\SS4SK jrKSLrLg)PVirtualBotManager   z
Manages virtual bots that simulate real users on the server.

Virtual bots navigate menus, create/join games, and play autonomously.
They come online and go offline on their own schedules to create
a natural-feeling server population.
c                     Xl         [        5       U l        0 U l        0 U l        0 U l        0 U l        0 U l        0 U l        SU l	        g)z0Initialize the virtual bot manager for a server.r   N)
_serverr1   _config_bots	_profiles_bot_groups_bot_memberships_bot_profiles_map_guided_tables_tick_counter)selfservers     r!   __init__VirtualBotManager.__init__   sG    '),.
?A685713;=r    Npathreturnc           
         Uc
  [        5       n[        U5      nUR                  5       (       d  [        SU S35        g SSKn[        US5       nUR                  U5      nSSS5        WR                  S0 5      nUR                  SS5      nUR                  S	S
5      n [        U5      n [        U5      n
[        S40 SUR                  S/ 5      _SUR                  SS5      _SUR                  SS5      _SUR                  SS5      _SUR                  SS5      _SUR                  SS5      _SUR                  SS5      _SUR                  SS5      _SUR                  SS5      _SUR                  SS5      _S UR                  S S!5      _S"UR                  S"S#5      _S$UR                  S$S%5      _S&UR                  S&S'5      _S(UR                  S(S5      _S)UR                  S)S5      _S*UR                  S*S5      _S+UR                  S+S5      _S,UR                  S,S'5      _S-UR                  S-S5      _SU_S.UR                  S.S5      _S	U
_6U l        U R                  R                  (       d  [        S/5      eUR                  S00 5      nU R!                  U5      U l        U R"                  R%                  S['        SS195        UR                  S20 5      nU R)                  U5      U l        U R-                  5       U l        U R1                  5       U l        UR                  S3/ 5      nU R5                  U5      U l        U R9                  5         U R:                  R=                  5        H  nU R2                  R                  UR>                  U R                  R@                  5      Ul!        [E        [G        U R.                  R                  UR>                  [I        5       5      5      5      Ul%        M     g! [
         a    SSKn GNf = f! , (       d  f       GN= f! [         a  n	[        SU S35      U	eSn	A	ff = f! [         a  n	[        SU S35      U	eSn	A	ff = f)5z(Load bot configuration from config.toml.Nz!Virtual bots config not found at z, using defaultsr   rbvirtual_botsrU   r%   rW   r,   zInvalid fallback_behavior ''zInvalid allocation_mode 'r5   r7   r6   r9   r8   r;   r:   r=   r<   r>   r@   r?   rB   rA   rD   rC   rF   rE   rH   rG   rJ   rI   rL   rK   rN   rM   rO   rP   rQ   rR   rS   rT   rV   z-virtual_bots.names must list at least one botprofilesr`   rl   guided_tablesr   )&r
   r   existsprinttomllibImportErrortomliopenloadgetr#   
ValueErrorr*   r1   r   r5   _parse_profilesr   
setdefaultr^   _parse_bot_groupsr   _build_bot_membershipsr   _resolve_bot_profilesr   _parse_guided_tablesr   _validate_guided_tablesr   valuesr`   rV   re   rp   sortedrx   r   )r   r   r   fdata	vb_configrU   rW   fallback_enumexcallocation_enumprofiles_sectionbot_groups_sectionguided_tables_sectionbots                  r!   load_configVirtualBotManager.load_config   sW   <*,DDz{{}}5dV;KLM	$ $<<?D  HH^R0	%MM*=yI#--(9=I	Z,->?M	V,_=O ( 
--,
$==)93?
 %==)93?
 ']]+=tD	

 ']]+=tD
 (mm,?E
 (mm,?F
 $-==1I3#O
 $-==1I3#O
 ']]+=sC
  )}}-A3G
 (mm,?F
 &/]]3Mt%T
 )26SUW(X
 )26SUX(Y
  !*.CQ G!
"  )}}-A1E#
$  )}}-A1E%
& (mm,?D'
( (mm,?E)
* ,+
, &MM*;YG-
. ,/
4 ||!!LMM$==R8--.>?!!)-FI-VW&]]<<112DE $ ; ; =!%!;!;!= )or B"778MN$$& ::$$&C0044SXXt||?[?[\CKvd&;&;&?&?#%&PQRCJ 'E  	$#	$   	Z:;L:MQOPVYY	Z
  	V88IKLRUU	VsM   O; P P" ,Q ;PP
P"
Q,P<<Q
Q#QQ#r   c           	         0 nUR                  5        GHo  u  p4[        U[        5      (       d  M  [        S0 SU_SUR	                  S5      _SUR	                  S5      _SUR	                  S5      _SUR	                  S5      _SUR	                  S5      _SUR	                  S5      _SUR	                  S5      _S	UR	                  S	5      _S
UR	                  S
5      _SUR	                  S5      _SUR	                  S5      _SUR	                  S5      _SUR	                  S5      _SUR	                  S5      _SUR	                  S5      _SUR	                  S5      _SUR	                  S5      _SUR	                  S5      _6X#'   GMr     U$ )z/Parse bot profile overrides from configuration.r`   r7   r9   r;   r=   r>   r@   rB   rD   rF   rH   rJ   rL   rN   rO   rQ   rR   rS   rT   r   )items
isinstancedictr^   r   )r   r   r   r`   	overridess        r!   r   !VirtualBotManager._parse_profiles  s    :</557ODi..6 (}}-=>  )}}-=> "+/A!B	
 "+/A!B #,--0C"D #,--0C"D (1}}5M'N (1}}5M'N "+/A!B $-==1E#F #,--0C"D *37Q)R -6MM:W,X -6MM:W,X  $-==1E#F!" $-==1E#F#$ #,--0C"D%& #,--0C"D'HN  80 r    groups_sectionc                     0 nUR                  5        Ha  u  p4[        U[        5      (       d  M  UR                  S/ 5      nU(       d  [	        SU S35      eUR                  S5      n[        X5US9X#'   Mc     U$ )z*Parse named bot groups from configuration.rd   zvirtual_bots.bot_groups.z must list at least one botre   )r`   rd   re   )r   r   r   r   r   rb   )r   r   r   r`   payloadrd   re   s          r!   r   #VirtualBotManager._parse_bot_groups&  sx    ,.+113MDgt,,;;vr*D #;D6A\!]^^kk),G)tPFL 4 r    c           	      P   U R                   R                   Vs0 s H  o[        5       _M     nnU R                  R	                  5        HT  nUR
                   HA  nXB;  a  [        SU SUR                   S35      eX$   R                  UR                  5        MC     MV     U$ s  snf )z8Build a mapping of bot names to their group memberships.zBot 'z' referenced in group 'z' is not defined in names list)	r   r5   rx   r   r   rd   r   r`   add)r   r`   membershipsgroupbot_names        r!   r   (VirtualBotManager._build_bot_memberships3  s    /3||/A/AB/AtSU{/AB%%,,.E!JJ.$z)@Ljk  %))%**5 ' /  Cs   B#c                 t   0 nU R                   R                  nU R                  R                  U[	        US95        U R                   R
                   H  nUnU R                  R                  U[        5       5       Vs1 s H<  nU R                  U   R                  (       d  M#  U R                  U   R                  iM>     nnUR                  S5        [        U5      S:  a  [        SU S35      eU(       a  UR                  5       nU R                  R                  U[	        US95        XAU'   M     U$ s  snf )z+Resolve the effective profile for each bot.r   Nr	   z)Conflicting profile assignments for bot 'z' via bot groups)r   rV   r   r   r^   r5   r   r   rx   r   re   discardlenr   pop)r   resolvedrV   r   profile_namer   profile_overridess          r!   r   'VirtualBotManager._resolve_bot_profiles?  s"   #%,,66!!/3LRa3bc**H*L "2266xG!GE##E*22 0  '//G  !
 %%d+$%) ?zIYZ  !0446NN%%l4MS_4`a!-X +  !s   ="D5#D5guided_sectionc                 2   0 nU GH  n[        U[        5      (       d  M  UR                  S5      nU(       d  [        S5      eXB;   a  [        SU S35      eUR                  S5      nU(       d  [        SU S35      eUR                  SS	5      nUR                  S
S	5      nUS	:  a  [        SU S35      eUS	:w  a  Xv:  a  [        SU S35      eUR                  S5      =(       d    / nU(       d  [        SU S35      eUR                  S5      n	U	(       a#  U R                  R                  U	[        U	S95        UR                  SS5      n
UR                  SS	5      nUR                  S5      nSnU(       aO  [        U[        5      (       a  [        U5      S:w  a  [        SU S35      e[        US	   5      [        US   5      4n[        UU[        U5      [        U5      [        U5      U	[        U
5      [        U5      US9	n[        US9X$'   GM     U$ )z4Parse guided table configuration into runtime state.tablez4Each guided table entry must include a 'table' labelz'Duplicate guided table definition for 'r   ri   Guided table 'z' must set 'game'rj   r   rk   z' min_bots cannot be negativez1' max_bots must be >= min_bots or 0 for unlimitedrl   z"' must list at least one bot groupre   r   rm   r6   rn   active_ticksN   z4' active_ticks must be a two-value list [start, end]r	   )	r`   ri   rj   rk   rl   re   rm   rn   ro   )rt   )r   r   r   r   r   r   r^   rX   r   r[   rg   rr   )r   r   guidedentry
table_nameri   rj   rk   rl   re   rm   rn   ro   window_tuplert   s                  r!   r   &VirtualBotManager._parse_guided_tablesW  s    /1#EeT**7+J !WXX# #J:,VW!XYY99V$D >*=N!OPPyyQ/HyyQ/H!| >*=Z![\\1}!4 $ZL0ab  <06BJ >*=_!`aaii	*G))'3LRY3Z[yyS1H))M15K!IIn5M37L!-66#m:LPQ:Q$(4hi  !$M!$4 5s=;K7LM&XX
+X,*
F "2!@Fi $j r    rt   c                     [        5       nUR                   H\  nU R                  R                  U5      nU(       d  [	        SUR
                   SU S35      eUR                  UR                  5        M^     U$ )z?Return the set of bots eligible to satisfy a guided table rule.r   z ' references unknown bot group 'r   )rx   rl   r   r   r   r`   updaterd   )r   rt   eligible
group_namer   s        r!   _eligible_bots_for_rule)VirtualBotManager._eligible_bots_for_rule  sr     U ++J$$((4E $V[[M1QR\Q]]^_  OOEJJ' , r    c           	      6   SSK Jn  UR                  5        Vs1 s H  o"R                  5       iM     nnU R                  R                  5        GH  nUR                  nUR                  U;  a&  [        SUR                   SUR                   S35      eUR                  (       a@  UR                  U R                  ;  a&  [        SUR                   SUR                   S35      eU R                  U5      nUR                  [        U5      :  aj  SUR                   SUR                   S[        U5       S	3nU R                  R                   ["        R$                  :X  a  [        U5      e['        S
U 35        UR(                  (       d  GM?  UR(                  u  pUR*                  S::  a  [        SUR                   S35      eSUs=::  a  UR*                  :  a  O  OSU	s=::  a  UR*                  ::  a  GM  O  [        SUR                   S35      e   gs  snf )zCValidate guided table rules against available games and bot counts.r   GameRegistryr   z' references unknown game 'r   z ' references undefined profile 'z' requires z bots but only z eligible bots are taggedz[virtual_bots] WARNING: z.' defines active_ticks without cycle_ticks > 0z2' active_ticks must fall within cycle_ticks boundsN)server.games.registryr   get_allget_typer   r   rt   ri   r   r`   re   r   r   rj   r   r   rW   r*   r/   r   ro   rn   )
r   r   clsavailable_gamesr}   rt   r   messagestartends
             r!   r   )VirtualBotManager._validate_guided_tables  s   65A5I5I5KL5Kc<<>5KL((//1E\\F{{/1 $V[[M1LV[[MYZ[  ~~&.."F $V[[M1QRXR`R`Qaabc  33F;HX.$V[[MV__<M_8}o%>@  <<//>3H3HH$W--0	:;####11
%%*$(5cd  U7V%7%77A<ZHZHZ<Z<Z$(5gh 9 2 Ms   Hr}   c                    UR                   nUR                  S::  d  UR                  (       d  gUR                  u  p4UR                  nU R                  U-  nX4:X  a  gX4:  a  X6s=:*  =(       a    U:  $ s  $ Xc:  =(       d    Xd:  $ )z>Return True if the guided rule is active for the current tick.r   TF)rt   rn   ro   r   )r   r}   rt   r   r   cycleticks          r!   _rule_is_active!VirtualBotManager._rule_is_active  s    "&*>*>))
""!!E)<;&&3&&&&}*
*r    c                    UR                   nUR                  S::  d  UR                  (       d  gUR                  u  p4UR                  nU R                  U-  nU R	                  U5      nX4:X  a  gU(       a-  X4:  a  [        XF-
  S5      $ Xc:  a  XV-
  U-   $ [        XF-
  S5      $ X4:  a  Xc:  a  X6-
  $ XV-
  U-   $ Xc:  a	  Xd:  a  X6-
  $ XV-
  U-   $ )zBReturn ticks until the next active/inactive transition for a rule.r   N)rt   rn   ro   r   r   max)r   r}   rt   r   r   r   r   actives           r!   _ticks_until_next_change*VirtualBotManager._ticks_until_next_change  s    "&*>*>))
""!!E)%%e,<{3:q))}++sz1%%;||#LE))<DK<%%r    r   r   c                     U R                   R                  UR                  5      nU(       a   [        X25      (       a  [	        X25      nUb  U$ [	        U R
                  U5      $ )zCResolve a config field from the bot's profile override or defaults.)r   r   re   hasattrgetattrr   )r   r   r   re   values        r!   _get_config_value#VirtualBotManager._get_config_value  sM    ..$$S[[1ww..G+E t||U++r    c                 H    [        U R                  R                  5       S S9$ )z8Return guided table states ordered by priority and name.c                 Z    U R                   R                  U R                   R                  4$ N)rt   rm   r`   )r}   s    r!   <lambda>7VirtualBotManager._iter_guided_states.<locals>.<lambda>  s    u||44ell6G6GHr    )key)r   r   r   r   s    r!   _iter_guided_states%VirtualBotManager._iter_guided_states  s$    &&(H
 	
r    c           
      V   U R                   R                  nU(       d  gUR                  5         U R                  R	                  5        H]  nUR                  UR                  UR                  R                  UR                  UR                  UR                  UR                  S9  M_     g)z;Save all virtual bot state to the database for persistence.Nr`   r}   r   r   ru   r   )r   _dbdelete_all_virtual_botsr   r   save_virtual_botr`   r}   r   r   r   ru   r   )r   dbr   s      r!   
save_stateVirtualBotManager.save_state  s    \\ 	""$ ::$$&CXXiioo --$'$;$;"11    'r    c           
         U R                   R                  nU(       d  gUR                  5       nSnU GH,  nUS   nXPR                  R                  ;  a  M$  [        U[        US   5      US   US   US   US   S9nU R                  R                  XPR                  R                  5      Ul
        [        [        U R                  R                  U[        5       5      5      5      Ul        X`R                   U'   US	-  nUR"                  [        R$                  [        R&                  [        R(                  [        R*                  4;   d  GM  U R-                  U5        GM/     U$ )
zO
Load virtual bot state from the database.

Returns the number of bots loaded.
r   r`   r}   r   r   ru   r   r  r	   )r   r  load_all_virtual_botsr   r5   r{   r   r   r   rV   re   rp   r   r   rx   r   r   r}   r   r   r   r   _restore_bot_user)r   r  bot_datacountr   r`   r   s          r!   
load_stateVirtualBotManager.load_state  s:    \\++-D<D<<---%d7m4!.1$()>$?j)#$45C 0044T<<;W;WXCKvd&;&;&?&?ce&LMNCJ"JJtQJE yy++'',,11	  &&s+5 8 r    c                     U R                   (       d)  U R                  R                  5        H
  nSUl        M     gU R	                  5         U R                  5         g)z2Sync guided table targets with live server tables.N)r   r   r   r   _prune_missing_tables_resolve_guided_assignmentsr   r   s     r!   _refresh_guided_tables(VirtualBotManager._refresh_guided_tables=  sD    ""zz((*"& +""$((*r    c                    U R                   R                  5        H  nUR                  (       d  M  U R                  R                  R                  UR                  5      nU(       a&  UR                  UR                  R                  :w  d  Mr  UR                  nSUl        UR                   H@  nU R                  R                  U5      nU(       d  M'  UR                  U:X  d  M9  SUl        MB     M     g)z?Clear guided table references for missing or mismatched tables.N)r   r   ru   r   _tables	get_table	game_typert   ri   rv   r   r   )r   r}   r   stale_idr   r   s         r!   r  'VirtualBotManager._prune_missing_tablesG  s    ((//1E>>LL((225>>BEEOOu||/@/@@ >>!% % 3 3H**..2Css||x7'+ !4 2r    c                 x  ^ U R                   R                  5        H
  nSUl        M     [        U R                   R	                  5       5      nU R                  5        GHY  nU R                  U5      (       d  UR                  R                  5         M6  U R                  UR                  5      R                  U5      m[        U4S jUR                   5       5      nU H  nUR                  U5        M     UnUR                  R                  S:  a  UR                  R                  OSn[        T5       H@  nXV;   a  M
  Ub  [        U5      U:  a    O&UR                  U5        UR!                  U5        MB     [        U5      Ul        U H+  nUR                  R"                  U R                   U   l        M-     [        U5      UR                  R$                  :  a  U R&                  R(                  [*        R,                  :X  aF  [/        SUR                  R"                   SUR                  R$                   S[        U5       S35      eUR0                  (       dP  [3        SUR                  R"                   S	[        U5       S
UR                  R$                   S35        SUl        GMO  GMR  SUl        GM\     g)z=Assign bots to guided tables based on availability and rules.Nc              3   6   >#    U  H  oT;   d  M
  Uv   M     g 7fr  r   ).0r`   r   s     r!   	<genexpr>@VirtualBotManager._resolve_guided_assignments.<locals>.<genexpr>e  s     W/BthFVdd/Bs   		r   r   z' could not allocate z bots (only z available).z&[virtual_bots] WARNING: guided table 'z' underfilled (/)TF)r   r   r   rx   keysr
  r   rv   clearr   rt   intersectionr   r   rk   r   appendr`   rj   r   rW   r*   r/   RuntimeErrorrw   r   )	r   r   	availabler}   retainedr`   assignedrk   r   s	           @r!   r  -VirtualBotManager._resolve_guided_assignmentsV  s=   ::$$&C"CO ' 

)*	--/E''..##))+33ELLANNyYH Wu/B/BWWH !!$' !  H050E0E0Iu||,,tHx(#'CMX,E!!$'% ) #&h-E /4||/@/@

4 , ! 8}u||444<<//>3H3HH&():):(;;P <<001c(m_LZ  ,,@ARAR@S T((+Ha8M8M7NaQ -1E) - ).%Q 0r    c                 8   SSK Jn  U R                  R                  R	                  UR
                  5      nU(       a  [        US5      (       a  UR                  (       a  UR                  [        R                  :X  a&  SS0U R                  R                  UR
                  '   gUR                  [        R                  [        R                  4;   a0  SUR                  S.U R                  R                  UR
                  '   g[        R                  Ul        [         R"                  " S	S
5      Ul        gU" UR
                  5      nX@R                  R                  UR
                  '   UR                  [        R                  [        R&                  4;   a&  SS0U R                  R                  UR
                  '   gUR                  [        R                  [        R                  4;   a1  SUR                  S.U R                  R                  UR
                  '   gg)z0Restore a VirtualUser for a bot that was online.r	   VirtualUseris_virtual_botmenu	main_menur   r;  ru   NrA   rC   )users.virtual_userr9  r   _usersr   r`   r   r:  r}   r   r   _user_statesr   r   ru   r   randomrandintr~   r   )r   r   r9  existing_userusers        r!   r  #VirtualBotManager._restore_bot_user  s   3 ++//9}&677M<X<X99 ; ;;;A;:ODLL--chh7  YY?#:#:O<X<X"YY )$'LL;DLL--chh7  ,33	%+^^C%=" 388$(,CHH%9944o6W6WXX39;2GDLL%%chh/YY?22O4P4PQQ!LL3DLL%%chh/ Rr    c           
      r   U R                   R                  (       d  gU R                   R                   Vs/ s H  oU R                  ;  d  M  UPM     nnU(       d  g[        R                  " U5        [        U5      S-  nSnSn[        U5       GH  u  paU R                  R                  XR                   R                  5      n[        [        U R                  R                  U[        5       5      5      5      nXc:  a@  [        U[        R                   SUUS9n	XR                  U'   U R#                  U	5        US-  nOh[        U[        R                   UUS9n	U R%                  U	S5      n
U R%                  U	S5      n[        R&                  " X5      nXl        XR                  U'   US-  nGM      XE4$ s  snf )	z
Instantiate bots from config that don't already exist.

50% come online immediately, rest stay offline with random cooldowns.
Does not replace existing bots or delete bots not in config.

Returns tuple of (bots_added, bots_brought_online).
)r   r   r   r   )r`   r}   r~   re   r   r	   )r`   r}   re   r   r>   r@   )r   r5   r   rA  shuffler   	enumerater   r   rV   rp   r   r   rx   r{   r   r   _bring_bot_onliner  rB  r~   )r   r`   	new_nameshalfaddedonlineir   r   r   min_offlinemax_offlinecooldowns                r!   fill_serverVirtualBotManager.fill_server  s    ||!! '+ll&8&8S&8d

<RT&8	S 	y!9~" +GA1155dLL<X<XYL6$"7"7";";D#%"HIJFx )11#$(! $'

4 &&s+! !)11(!	 #44S:MN"44S:MN!>>+C%-"#&

4 QJE; ,> }U Ts   F4F4c                    [        U R                  5      n[        5       n[        U R                  R	                  5       5       GH$  u  p4UR
                  (       a  U R                  R                  R                  UR
                  5      nU(       a  UR
                  U;  av  UR                  (       a  UR                  R                  S5        U R                  R                  R                  UR
                  5        UR                  UR
                  5        UR                  [        R                  [        R                   [        R"                  4;   d  GM  U R%                  U5        GM'     U R                  R'                  5         U R                  R(                  (       a$  U R                  R(                  R+                  5         U[        U5      4$ )zk
Remove all instantiated bots and kill tables they're in.

Returns tuple of (bots_cleared, tables_killed).
zvirtual-bot-table-closed)r   r   rx   rX   r   ru   r   r"  r#  ri   broadcast_lremove_tabler   r}   r   r   r   r   _take_bot_offline_silentr/  r  r  )r   	bot_counttables_killedr`   r   r   s         r!   
clear_botsVirtualBotManager.clear_bots  s6    

O	djj..01ID||,,66s||DS\\>zz

../IJLL((55cllC!%%cll3 yy++'',, 
 --c2% 2( 	

 <<LL446#m,,,r    c                 
   U R                   R                  R                  UR                  S5        U R                   R                  R                  UR                  S5        [
        R                  Ul        SUl        SUl	        g)z1Take a bot offline without broadcasting presence.Nr   )
r   r?  r   r`   r@  r   r   r}   r   ru   r  s     r!   rW  *VirtualBotManager._take_bot_offline_silent  s^     	$/!!%%chh5 $++	r    c                    SnSnSnU R                   R                  5        H  nUR                  [        R                  :X  a  US-  nM(  UR                  [        R
                  [        R                  4;   a  US-  nM]  UR                  [        R                  [        R                  4;   d  M  US-  nM     [        U R                   5      UUUS.$ )z\
Get counts of bots in each state.

Returns dict with keys: total, offline, online, in_game
r   r	   )totalr   rM  r   )
r   r   r}   r   r   r   r   r   r   r   )r   r   rM  r   r   s        r!   
get_statusVirtualBotManager.get_status  s     ::$$&CyyO3331::O<]<]^^!668T8TUU1 ' _	
 	
r    c                     U R                  5       nU R                  5       nUU R                  U5      U R                  5       U R	                  5       S.$ )z0Collect structured debug info for admin tooling.)rt   r   r   r   )_build_admin_config_summary_build_admin_profile_usage_build_admin_profiles_snapshot_build_admin_groups_snapshot_build_admin_guided_snapshot)r   config_summaryprofile_usages      r!   get_admin_snapshot$VirtualBotManager.get_admin_snapshot,  sQ    99;779$;;MJ779!>>@	
 	
r    c                 (   U R                   R                  R                  U R                   R                  R                  U R                   R                  [        U R                   R                  5      [        U R                  5      U R                  S.$ )z1Summarize configuration values for admin tooling.)rW   rU   rV   configured_botsinstantiated_botstick_counter)	r   rW   r   rU   rV   r   r5   r   r   r	  s    r!   rc  -VirtualBotManager._build_admin_config_summary7  sg      $||;;AA!%!?!?!E!E#||;;"4<<#5#56!$TZZ ..
 	
r    c                 ~    0 nU R                   R                  5        H  u  p#UR                  US5      S-   X'   M     U$ )z%Count how many bots use each profile.r   r	   )r   r   r   )r   ri  	_bot_namere   s       r!   rd  ,VirtualBotManager._build_admin_profile_usageB  sD    (*"&"8"8">">"@I%2%6%6w%BQ%FM" #Ar    ri  c           	      $   / n[        U R                  R                  5       5       Ha  nU R                  U   nS Vs0 s H  n[        XE5      c  M  U[        XE5      _M     nnUR	                  UUUR                  US5      S.5        Mc     U$ s  snf )z,Build snapshot data for configured profiles.)r7   r9   r;   r=   r>   r@   rB   rD   rF   rH   rJ   rL   rN   rO   rQ   rR   rS   rT   r   )r`   r   rX  )r   r   r.  r   r1  r   )r   ri  profiles_snapshotr`   re   r   r   s          r!   re  0VirtualBotManager._build_admin_profiles_snapshotI  s    4>>..01DnnT*GE( 7*+ /ww..  0 $$ !*!.!2!24!;5 2B ! ?s   BBc           
      0   / n[        U R                  R                  5       5       Hl  nU R                  U   nU R                  UR                  5      u  pEUR                  UUR                  U[        UR                  5      [        U5      S.5        Mn     U$ )z#Build snapshot data for bot groups.)r`   re   counts	bot_namesassigned_rules)r   r   r.  _summarize_bot_grouprd   r1  re   rX   )r   groups_snapshotr   r   rx  rz  s         r!   rf  .VirtualBotManager._build_admin_groups_snapshoto  s     !1!1!6!6!89J$$Z0E%)%>%>uzz%J"F""&$}}$!%ejj!1&,^&< : r    ry  c                 `   SSSSSS.n[        5       nU GH  nUS==   S-  ss'   U R                  R                  U5      nU(       d  US==   S-  ss'   MB  UR                  (       a  UR	                  UR                  5        UR
                  [        R                  [        R                  4;   a  US==   S-  ss'   M  UR
                  [        R                  :X  a  US==   S-  ss'   M  UR
                  [        R                  :X  a  US==   S-  ss'   GM  US==   S-  ss'   GM     X#4$ )	z0Count bot states and assigned rules for a group.r   )r_  rM  waitingr   r   r_  r	   r   r   r  rM  )rx   r   r   r   r   r}   r   r   r   r   r   )r   ry  rx  rz  r   r   s         r!   r{  &VirtualBotManager._summarize_bot_group  s    aARST#&5!H7Oq O**..*Cy!Q&!""3??3yy_44o6R6RSSy!Q&!o???y!Q&!o999x A% y!Q&! "  %%r    c                 j    U R                  5        Vs/ s H  oR                  U5      PM     sn$ s  snf )z+Build snapshot data for guided table rules.)r
  _build_guided_state_snapshot)r   r}   s     r!   rg  .VirtualBotManager._build_admin_guided_snapshot  s.    FJF^F^F`aF`U11%8F`aaas   0GuidedTableRuleStatec                    UR                   n[        UR                  5      nU R                  U5      nU R	                  U5      u  pVU R                  U5      nU R                  U5      nU R                  U5      u  pp0 SUR                  _SUR                  _SUR                  _SUR                  _SUR                  S:  a  UR                  OS_SU_S	U_S
U_S[        UR                  5      _SUR                  _SU_SU	_SUR                   _SU_SU_SU
_SUR"                  _UR$                  UUR&                  =(       d    X2R                  :  US.E$ )z0Build a snapshot for a single guided rule state.r`   ri   rm   rj   rk   r   Nrv   seated_botswaiting_botsrl   re   r   table_stateru   human_playerstotal_playershostrn   )ro   ticks_until_next_changewarningunavailable_bots)rt   r   rv   _count_rule_bots_count_guided_availabilityr   r   _describe_guided_tabler`   ri   rm   rj   rk   rX   rl   re   ru   rn   ro   rw   )r   r}   rt   r5  seatedr  unavailabler   ticks_until_nextr  r  r  r  s                r!   r  .VirtualBotManager._build_guided_state_snapshot  s   u**+&&u-#>>uE%%e,88?:>:U:UV[:\7=
FKK
FKK
 
 	

 6??Q+>D
 X
 6
 G
 $v001
 v~~
 f
 ;
 
 ]
 ]
  D!
" 6--#
$ $11'7,,LOO1K ++
 	
r    c                 p   SnSnUR                    H  nU R                  R                  U5      nU(       d  US-  nM,  UR                  UR                  :X  a0  UR                  [
        R                  [
        R                  4;   a  Mv  UR                  [
        R                  :X  a  US-  nM  US-  nM     X#4$ )z4Count guided bots waiting vs unavailable for a rule.r   r	   )	rv   r   r   ru   r}   r   r   r   r   )r   r}   r  r  r`   r   s         r!   r  ,VirtualBotManager._count_guided_availability  s    ''D**..&Cq ||u~~-#))'',,@ 3 yyO333q 1 ( ##r    c                    UR                   (       d  gU R                  R                  R                  UR                   5      nU(       a  UR                  (       d  g[        UR                  R                   Vs/ s H   o3R                  U R                  ;  d  M  UPM"     sn5      nSUR                  R                  [        UR                  R                  5      U4$ s  snf )z1Describe the guided table link and player counts.)
unassignedNr   r   )staleNr   r   linked)
ru   r   r"  r#  ri   r   playersr`   r   r  )r   r}   r   playerr  s        r!   r  (VirtualBotManager._describe_guided_table  s     ~~+$$..u~~>EJJ&"'**"4"4V"44::8UV"4V
 #ejj.@.@*A=PP Ws   8CCc                     U =R                   S-  sl         U R                  5         [        U R                  R	                  5       5       H  nU R                  U5        M     g)z'Process bot decisions each server tick.r	   N)r   r  rX   r   r   _process_bot_tickr  s     r!   on_tickVirtualBotManager.on_tick  sI    a##%

))+,C""3' -r    c                 0   UR                   S:  a  U=R                   S-  sl         gUR                  [        R                  :X  a  U R	                  U5        gUR                  [        R
                  :X  a  U R                  U5        gUR                  [        R                  :X  a  U R                  U5        gUR                  [        R                  :X  a  U R                  U5        gUR                  [        R                  :X  a  U R                  U5        gg)zProcess a single bot's tick.r   r	   N)r~   r}   r   r   _process_offline_botr   _process_online_idle_botr   _process_in_game_botr   _process_leaving_game_botr   _process_waiting_botr  s     r!   r  #VirtualBotManager._process_bot_tick  s     !!#99///%%c*YY/555))#.YY/111%%c*YY/666**3/YY/;;;%%c* <r    c                    U R                   R                  [        R                  :X  aN  UR                  (       d=  [
        R                  " U R                  US5      U R                  US5      5      Ul        gU R                  U5        g)z<Process a bot that is currently offline - bring them online.r>   r@   N)
r   rU   r#   r(   r   rA  rB  r  r~   rI  r  s     r!   r  &VirtualBotManager._process_offline_bot  sg    <<))-=-F-FFs!'&&s,?@&&s,?@"C s#r    c                 D   U=R                   S-  sl         UR                  S:  a  U=R                  S-  sl        gUR                  (       a  U R                  U5      (       a  gU R                  nUR                   U R                  US5      :  aU  UR                   UR                  :  a;  [        R                  " 5       U R                  US5      :  a  U R                  U5        g[        R                  " 5       U R                  US5      :  a  U R                  U5      (       a  g[        R                  " 5       U R                  US5      :  a  U R                  U5      (       a  g[        R                  " U R                  US5      U R                  US	5      5      Ul        g)
z&Process a bot that is online and idle.r	   r   Nr;   rJ   rF   rH   r7   r9   )r   r   r   _handle_guided_botr   r  r   rA  _take_bot_offline_try_join_game_try_create_gamerB  )r   r   rt   s      r!   r  *VirtualBotManager._process_online_idle_bot  sN   A ??QOOq O??t66s;;   6 6s<N OO  C$;$;;$"8"8>Q"RR""3' ==?T33C9KLL""3'' ==?T33C9MNN$$S)) !..""3(89""3(89
r    c                    U=R                   S-  sl         UR                  (       Ga@  U R                  R                  R	                  UR                  5      nU(       a  UR
                  (       d  U R                  U5        gUR
                  nUR                  S:X  a  U R                  U5        gUR                  S:X  a  UR                   UR                  -
  nUR                  UR                  :X  at  [        UR                  5      UR                  5       :  aL  X@R                  US5      :  a6  UR                  UR                  5      nU(       a  UR!                  US5        ggggggg)z Process a bot that is in a game.r	   Nfinishedr  rD   
start_game)r   ru   r   r"  r#  ri   _start_leaving_gamestatusr   r  r`   r   r  get_min_playersr  get_player_by_nameexecute_action)r   r   r   ri   ticks_in_gamer  s         r!   r  &VirtualBotManager._process_in_game_bot,  s"   A <<<LL((223<<@E

((-::D{{j(((-	) !$ 0 033E3E EII)DLL)T-A-A-CC%)?)?E])^^!44SXX>F++FLA  _ D * * r    c                 ,   U=R                   S-  sl         U R                  U5        UR                  (       a`  SUl        [        R                  Ul        [        R                  " U R                  US5      U R                  US5      5      Ul	        SUl
        gUR                   UR                  :  a  U R                  U5        g[        R                  Ul        [        R                  " U R                  US5      U R                  US5      5      Ul        g)	z;Process a bot that is leaving a game (staggered departure).r	   FrN   rO   r   r7   r9   N)r   _leave_current_tabler   r   r   r}   rA  rB  r  r~   r   r  r   r  s     r!   r  +VirtualBotManager._process_leaving_game_botI  s    A 	!!#&   $)C!'33CI!'&&s,IJ&&s,IJ"C
 '(C#!8!88""3''33CI$nn&&s,<=&&s,<=COr    c                     UR                   S:  a  U=R                   S-  sl         g[        R                  Ul        SUl        g)zABots waiting for a guided table retry after their waiting window.r   r	   N)r~   r   r   r}   r   r  s     r!   r  &VirtualBotManager._process_waiting_botd  s8    !!##//	r    c                 Z   U R                   R                  UR                  =(       d    S5      nU(       a  U R                  U5      (       d  SUl        gUR                  nUR
                  (       a+  UR
                  UR
                  :w  a  UR
                  Ul        SnUR                  (       aY  U R                  R                  R                  UR                  5      nU(       a  UR                  UR                  :w  a	  SnSUl        UR                  (       aP  UR                  UR                  =(       d    UR                  :w  a#  U R                  U5        U R                  U5        gU(       d?  U R                  U5      (       a  U R                  X5      (       a  gU R                  U5        gUR                  UR                  :X  a  gU R!                  XU5      (       d  U R                  U5        gU R#                  X5      (       a  UR                  Ul        gU R                  U5        g)z5Direct bot behavior toward its assigned guided table. NFT)r   r   r   r   rt   re   ru   r   r"  r#  r$  ri   r  _enter_waiting_for_table_should_try_create_guided_table_create_guided_table_should_bot_join_rule_join_specific_table)r   r   r}   rt   r   s        r!   r  $VirtualBotManager._handle_guided_botl  s   ##''(=2>D0077"CO>>ckkV^^; ..CK>>LL((225>>BEEOOv{{:!%<<CLLU^^-Ks||L%%c*))#.33C88,,S88))#. <<5>>)))#e<<))#.$$S00"^^EN%%c*r    c                 ,    U R                  US5      S:H  $ )zFHost-style bots (min_bots_per_table == 0) are allowed to spawn tables.rQ   r   )r  r  s     r!   r  1VirtualBotManager._should_try_create_guided_table  s    %%c+?@AEEr    c                     U R                  US5      nU R                  US5      n[        R                  Ul        [        R
                  " X#5      Ul        SUl        g)z:Move a bot into the waiting-for-table state with cooldown.rS   rT   r   N)r  r   r   r}   rA  rB  r~   r   )r   r   wait_minwait_maxs       r!   r  *VirtualBotManager._enter_waiting_for_table  sM    ))#/BC))#/BC#55	#^^H?r    excludec                 Z   UR                   (       d  gSnUR                   H  nU(       a  XB:X  a  M  U R                  R                  U5      nU(       d  M5  UR                   UR                   :X  d  MQ  UR                  [
        R                  [
        R                  4;   d  M  US-  nM     U$ )z6Count bots currently seated for a guided rule's table.r   r	   )ru   rv   r   r   r}   r   r   r   )r   r}   r  r  r`   r   s         r!   r  "VirtualBotManager._count_rule_bots  s    ~~''D4?**..&CLLENN2II#++#00 
 ( r    c                    UR                   nU(       a  UR                  S:w  a  g[        S UR                   5       5      UR	                  5       :  a  gU R                  X!R                  S9nU R                  US5      nU R                  US5      nXV:  a  gUS:  a
  US-   U:  a  gUR                  R                  S:  a  US-   UR                  R                  :  a  gg	)
z>Return True if the bot should join the guided table right now.r  Fc              3   N   #    U  H  n[        US S5      (       a  M  Sv   M     g7fis_spectatorFr	   Nr   r)  ps     r!   r*  :VirtualBotManager._should_bot_join_rule.<locals>.<genexpr>       Q<awq.%/P<   %	%)r  rQ   rR   r   r	   T)
ri   r  sumr  get_max_playersr  r`   r  rt   rk   )r   r   r}   r   ri   currentmin_requiredmax_alloweds           r!   r  'VirtualBotManager._should_bot_join_rule  s    zzt{{i/Q4<<QQ##%& ''xx'@--c3GH,,S2FG!?!{:<<  1$'A+9N9N)Nr    c                 z   SSK Jn  U R                  R                  R	                  UR
                  5      nU(       d  gU R                  UR                  R                  5      (       d  gUR                  UR                  R                  5      nU(       d  gU R                  R                  R                  UR                  R                  UR
                  U5      nU" 5       nXvl        Xgl        UR                  UR
                  U5        U R                  R                  UR
                  UR                  R                  5        [        R                   Ul        UR$                  Ul        UR&                  Ul        SUR$                  S.U R                  R*                  UR
                  '   UR$                  Ul        g)z/Create a guided table and seat the bot as host.r   r   Fr   r=  T)r   r   r   r?  r   r`   _can_create_game_typert   ri   get_game_classr"  create_table_tableinitialize_lobby_broadcast_table_createdr   r   r}   ru   r   r   r@  )r   r   r}   r   rD  
game_classr   ri   s           r!   r  &VirtualBotManager._create_guided_table  s/   6||""&&sxx0))%,,*;*;<<!001B1BC
$$11%,,2C2CSXXtT|
chh---chh8I8IJ#++	~~ --7@enn.]!!#((+r    c                    UR                   nU(       a  UR                  S:w  a  g[        S UR                   5       5      UR	                  5       :  a  gU R
                  R                  R                  UR                  5      nU(       d  gUR                  UR                  USS9  UR                  UR                  U5        UR                  SUR                  S9  UR                  S5        UR                  5         [        R                  Ul        UR"                  Ul        UR$                  Ul        SUR"                  S	.U R
                  R(                  UR                  '   g
)z3Join a specific table instance for guided matching.r  Fc              3   N   #    U  H  n[        US S5      (       a  M  Sv   M     g7fr  r  r  s     r!   r*  9VirtualBotManager._join_specific_table.<locals>.<genexpr>  r  r  as_spectatortable-joinedr  join.oggr   r=  T)ri   r  r  r  r  r   r?  r   r`   
add_member
add_playerrU  broadcast_soundrebuild_all_menusr   r   r}   ru   r   r   r@  )r   r   r   ri   rD  s        r!   r  &VirtualBotManager._join_specific_table  s   zzt{{i/Q4<<QQ##%& ||""&&sxx04e<$'9Z( #++	~~ --/
!!#((+ r    c                    SSK Jn  UR                  U R                  R                  ;   a  [
        R                  " SS5      Ul        gU" UR                  5      nX0R                  R                  UR                  '   SS0U R                  R                  UR                  '   [        R                  Ul        SUl        [
        R                  " U R                  US	5      U R                  US
5      5      Ul        [
        R                  " U R                  US5      U R                  US5      5      Ul        U R                  R!                  SUR                  S5        g)zBring a bot online.r	   r8  rA   rC   Nr;  r<  r   r;   r=   r7   r9   zuser-onlinez
online.ogg)r>  r9  r`   r   r?  rA  rB  r~   r@  r   r   r}   r   r  r   r   _broadcast_presence_l)r   r   r9  rD  s       r!   rI  #VirtualBotManager._bring_bot_online  s   3 88t||***!'S!9C 388$(,CHH%/5{.C!!#((+ $//	"(..""3(:;""3(:;#
 !..""3(89""3(89
 	**=#((LQr    c                    U R                  U5        U R                  R                  R                  UR                  S5        U R                  R
                  R                  UR                  S5        U R                  R                  SUR                  S5        [        R                  Ul	        [        R                  " U R                  US5      U R                  US5      5      Ul        SUl        SUl        g)zTake a bot offline.Nzuser-offlinezoffline.oggr>   r@   r   )r  r   r?  r   r`   r@  r  r   r   r}   rA  rB  r  r~   r   ru   r  s     r!   r  #VirtualBotManager._take_bot_offline*  s     	!!#& 	$/!!%%chh5 	**>388]S $++	#^^""3(;<""3(;<
 r    c                    UR                   (       d  gU R                  R                  R                  UR                   5      nU(       a  UR                  (       a  U R                  R
                  R                  UR                  5      nU(       aH  UR                  R                  UR                  5      nU(       a  UR                  R                  US5        UR                  UR                  5        SUl         g)z"Leave the current table if in one.Nleave)ru   r   r"  r#  ri   r?  r   r`   r  r  remove_member)r   r   r   rD  r  s        r!   r  &VirtualBotManager._leave_current_table?  s    ||$$..s||<UZZ<<&&**3884D66sxx@JJ--fg> )r    c                     [         R                  Ul        [        R                  " SU R                  US5      5      Ul        [        R                  " 5       U R                  US5      :  Ul        g)z6Start the leaving game process with a staggered delay.r   rB   rL   N)r   r   r}   rA  rB  r  r~   r   r  s     r!   r  %VirtualBotManager._start_leaving_gameS  s[    #00	#^^t%%c+CD
 !'$2H2H+3
 !
r    c                 P   U R                   R                  R                  5       nU(       d  g[        R                  " U5      nUR
                  nU(       d  gUR                  S:w  a  g[        S UR                   5       5      UR                  5       :  a  gU R                   R                  R                  UR                  5      nU(       d  gUR                  UR                  USS9  UR                  UR                  U5        UR                  SUR                  S9  UR!                  S5        UR#                  5         [$        R&                  Ul        UR*                  Ul        UR,                  Ul        SUR*                  S	.U R                   R0                  UR                  '   g
)z>Try to join an existing waiting table. Returns True if joined.Fr  c              3   N   #    U  H  n[        US S5      (       a  M  Sv   M     g7fr  r  r  s     r!   r*  3VirtualBotManager._try_join_game.<locals>.<genexpr>q  r  r  r  r  r  r  r   r=  T)r   r"  get_waiting_tablesrA  choiceri   r  r  r  r  r?  r   r`   r  r  rU  r  r  r   r   r}   ru   r   r   r@  )r   r   tablesr   ri   rD  s         r!   r   VirtualBotManager._try_join_game^  sO    %%88: f%zz ;;)# Q4<<QQ##%&  ||""&&sxx04e<$'9Z(  $++	~~ --/
!!#((+
 r    r$  c                 0   SnU R                   R                  R                  5        Hk  nUR                  (       d  M  UR                  R	                  5       U:w  a  M6  UR                  R
                  nU(       d  MU  X@R                  ;   d  Mf  US-  nMm     U$ )z?Count how many tables of a game type are owned by virtual bots.r   r	   )r   r"  get_all_tablesri   r   r  r   )r   r$  r  r   r  s        r!   _count_bot_owned_tables)VirtualBotManager._count_bot_owned_tables  sq    \\))88:E::zz""$	1::??Dt

*
 ; r    c                 f    U R                   R                  nUS:X  a  gU R                  U5      nX2:  $ )z9Check if bots can create another table of this game type.r   T)r   rP   r  )r   r$  
max_tablesr  s       r!   r  'VirtualBotManager._can_create_game_type  s4    \\55
?..y9##r    c                     SSK Jn  / nUR                  5        H<  nUR                  5       nU R	                  U5      (       d  M+  UR                  U5        M>     U$ )z7Get game classes that bots can still create tables for.r   r   )r   r   r   r   r  r1  )r   r   r3  r  r$  s        r!   _get_available_game_types+VirtualBotManager._get_available_game_types  sR    6	&..0J"++-I)))44  , 1 r    c                    U R                  5       nU(       d  U R                  U5      (       a  gg[        R                  " U5      nUR	                  5       nU R
                  R                  R                  UR                  5      nU(       d  gU R
                  R                  R                  XAR                  U5      nU" 5       nXvl        Xgl        UR                  UR                  U5        U R
                  R                  UR                  U5        [        R                   Ul        UR$                  Ul        UR&                  Ul        SUR$                  S.U R
                  R*                  UR                  '   g)z8Try to create a new game table. Returns True if created.TFr   r=  )r  r  rA  r  r   r   r?  r   r`   r"  r  ri   r  r  r  r   r   r}   ru   r   r   r@  )r   r   available_game_classesr  r$  rD  r   ri   s           r!   r  "VirtualBotManager._try_create_game  s$    "&!?!?!A%""3'' ]]#9:
'')	||""&&sxx0 $$11)XXtL |
chh- 	--chh	B $++	~~ --/
!!#((+
 r    ru   c                     U R                   R                  5        HF  nUR                  U:X  d  M  UR                  [        R
                  :X  d  M5  U R                  U5        MH     g)zx
Called when a game ends. Triggers bots to start leaving.

This is called from the server when a table's game finishes.
N)r   r   ru   r}   r   r   r  )r   ru   r   s      r!   on_game_endedVirtualBotManager.on_game_ended  sF     ::$$&C||x'CII9P9P,P((- 'r    )	r   r   r   r   r   r   r   r   r   )r   r   r  )r   N)Mr   r   r   r   r   r   rY   r   r   r   r   r^   r   rb   r   rx   r   r   rX   rr   r   rg   r   r   ry   r   r[   r   r{   r  r
  r  r  r  r  r  r  rp   rR  rZ  rW  r`  rj  rc  rd  re  rf  r{  rg  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  rI  r  r  r  r  r  r  r  r  r  r   r   r    r!   r   r      s   
PSd
T 1 PST PSd $S#X	c,,	->S#X 4^H[C\ 
S#c(](; 
tCH~ 0:"4S>2:	c##	$:x
.? 
CH 
#J+%5 +$ +&.> &3: &6,Z , ,
T*:%; 
()C )V+,/.b!Z !D !F7U38_ 7r#-E#s(O #-J	J 	4 	
DcN 
2	
DcN 	
	
T#s(^ 	
DcN $!DcN $!tTXY\^aYaTbOc $!Ld4S>.B "&d3i &E$sCx.RUVYRZBZ<[ &,bd4S>.B b 
2H  
TRUWZRZ^  
D$0F $5QTVYQY? $(Q+Q	sC$JS(	)Q (+Z +D +$	$
 	$t 	$&
J &
4 &
PB
 Bt B:Z D 6
 t ,j ,T ,\F: F$ FJ 4 &6 t WZ * <L X\ ,
 ;K PT :
 d <RZ RD R<Z D *
 t (	
z 	
d 	
,* , ,\  $s $t $	4 	(J (4 (T.c .d .r    r   )r   rA  dataclassesr   r   enumr   pathlibr   typingr   r   config_pathsr
   r   r   r   r#   r*   r1   r^   rb   rg   rr   r{   r   r   r    r!   <module>r     s    @  (   % 1,d ,t T  $A $A $AN ) ) )0    1 1 1 " " " # # #*. .r    