
    i9,                        S r SSKJr  SSKrSSKJr  \(       a  SSKJr  SSKJ	r	  SS	K
Jr  SS
KJrJrJrJrJrJr  SSKJrJrJrJrJrJrJr  SS jrSS jrSS jrSS jrSS jrSS jrSS jr SS jr!SS jr"g)a  Phase 10 bot AI.

Strategy:
  1. Draw: prefer discard pile if top card helps current phase; else draw from deck.
  2. Lay down: attempt to complete phase whenever hand contains enough cards.
  3. Hit: after laying down, hit any cards that extend table groups, prioritising
     high-penalty cards first (Wilds 25 pts, Skips 15 pts).
  4. Skip: play a Skip card on the opponent closest to finishing (highest phase
     or already laid down), but only if we cannot use the Skip turn productively
     (i.e. we already want to discard it anyway).
  5. Discard: the card least useful to the current phase, prioritising high-penalty
     dead weight (Wild > Skip > 10-12 > 1-9).
    )annotationsN)TYPE_CHECKING   )Phase10Game)Phase10Player   )Card)P10_RANK_WILDP10_RANK_SKIPPHASES	GROUP_SET	GROUP_RUNGROUP_COLOR)is_wildis_skipis_numbered
score_cardfind_phase_assignmentcan_hit_groupp10_card_namec                   U R                   (       d  [        X5      $ U R                  (       a  [        X5      $ U R                  (       a  [        X5      $ U R                  (       a  [        X5      $ UR                  (       d+  U R                  U5      n[        UR                  U5      nUb  gUR                  (       a$  U R                  (       a  [        X5      nU(       a  g[        X5      $ )z:Return the next action ID for the bot to execute, or None.lay_down_phasehit)turn_has_drawn_choose_drawlay_down_active_handle_lay_down_mode
hit_active_handle_hit_modeskip_discard_active_choose_skip_targetphase_laid_down_current_phase_reqsr   handtable_groups	_find_hit_choose_discard)gameplayerreqs
assignment
hit_targets        7C:\Users\dbart\PlayPalace11\server\games\phase10\bot.py	bot_thinkr.   *   s     D)) $T22 -- "400 !!''/*6;;=
!# $"3"3t,
 4((    c                    U R                   (       d  gU R                   S   n[        U5      (       a  g[        X!R                  U R	                  U5      5      (       a  gg)z!Choose draw_deck or draw_discard.	draw_deckdraw_discard)discard_piler   _discard_helps_phaser$   r#   )r(   r)   tops      r-   r   r   T   sL    


B
Cs||Cd.F.Fv.NOOr/   c                n    [        U 5      (       a  [        X5      SLnU(       + $ X/-   n[        XB5      SL$ )u  Return True if drawing the discard top card would help complete the phase.

Wilds: always helpful (we keep them), but only draw from discard if the
phase assignment isn't already complete without it — otherwise taking it
just wastes a Wild slot and creates ping-pong loops.
N)r   r   )cardr$   r*   already_done	test_hands        r-   r5   r5   `   s>     t}},T8DvI 1==r/   c                @   U R                  U5      nX R                     n[        5       nU R                   H  nUR	                  U5        M     UR
                   Vs/ s H  ofR                  U;  d  M  UPM     nn[        XrU R                  S 5      nUc  g[        S US    5       5      n	[        U R                  5      n
UR
                   HI  nUR                  U;   a  M  UR                  U	;   nUR                  U
;   nX:w  d  M:  SUR                   3s  $    gs  snf )z:During lay-down mode, toggle the right cards then confirm.Ncancel_lay_downc              3  8   #    U  H  oR                   v   M     g 7fN)id.0cs     r-   	<genexpr>(_handle_lay_down_mode.<locals>.<genexpr>   s     1=aTT=   r   card_confirm_group)	r#   lay_down_group_indexsetlay_down_stagedupdater$   r?   r   lay_down_current)r(   r)   r*   reqalready_staged	group_idsrB   	availabler+   
target_idscurrent_idsr8   	in_target
in_currents                 r-   r   r   t   s   ##F+D
((
)C  #uN))	i( * #KKFKq44~+EKIF&yt7P7P7Q2RSJ 1:a=11Jd++,K 77n$GGz)	WW+
"477)$$  ' Gs   D3Dc                0  ^  T R                   c'  [        T U5      nU(       d  gUu  p4SUR                   3$ [        U 4S jUR                   5       S5      nU(       d  g[        T R                  5       H   u  pV[        Xc5      u  pxU(       d  M  SU 3s  $    g)z+During hit mode, select the card and group.N
cancel_hitrF   c              3  ^   >#    U  H"  oR                   TR                  :X  d  M  Uv   M$     g 7fr>   )r?   hit_card_id)rA   rB   r(   s     r-   rC   #_handle_hit_mode.<locals>.<genexpr>   s"     H1ttt7G7G/GQQs   -	-
hit_group_)rX   r&   r?   nextr$   	enumerater%   r   )	r(   r)   hit_pairr8   
_group_idxigroupok_s	   `        r-   r   r      s    T6*#twwi   HH$O!$"3"34HA!%.EBr#A3'' 5 r/   c                *   [        UR                   Vs/ s H  n[        U5      (       a  M  UPM     snS SS9nU HO  n[        U R                  5       H3  u  pV[        Xd5      u  pxU(       d  M  UR                  (       d  M.  XE4s  s  $    MQ     gs  snf )z5Return (card, group_index) for the best hit, or None.c                    [        U 5      $ r>   r   rB   s    r-   <lambda>_find_hit.<locals>.<lambda>       jmr/   TkeyreverseN)sortedr$   r   r\   r%   r   r"   )	r(   r)   rB   
candidatesr8   r_   r`   ra   rb   s	            r-   r&   r&      s     KK2KqwqzK2#J
 !$"3"34HA!%.EBr--w 5   	3s
   BBc                <   U R                  5        Vs/ s H   o"R                  UR                  :w  d  M  UPM"     nnU(       d  gU Vs/ s H!  nUR                  U R                  ;  d  M  UPM#     nnU(       d  g[        US S9nSUR                   3$ s  snf s  snf )uB   Choose which player to skip — target the one closest to winning.cancel_skipc                D    U R                   [        U R                  5      4$ r>   )current_phaseintr"   )ps    r-   rg   %_choose_skip_target.<locals>.<lambda>   s    !//3q?P?P;Q)Rr/   rk   skip_target_)_active_playersr?   skip_targets_this_handmax)r(   r)   rt   activeeligibletargets         r-   r!   r!      s    --/E/A446993Da/FE a44t222 	
6   RSF&))%% F
s   BBB*Bc                2   UR                   (       d  gU R                  U5      n[        5       n[        UR                   U5      nU(       a.  U H'  nU H  nUR	                  UR
                  5        M      M)     O[        UR                   U5      nUR                    H0  n[        U5      (       d  M  UR	                  UR
                  5        M2     [        UR                    Vs/ s H  ofR
                  U;  d  M  UPM     snS SS9nU(       a  SUS   R
                   3$ UR                    Vs/ s H  n[        U5      (       a  M  UPM     nnU(       a  S[        US S9R
                   3$ S[        UR                   S	 S9R
                   3$ s  snf s  snf )
zChoose which card to discard.Nc                    [        U 5      $ r>   re   rf   s    r-   rg   !_choose_discard.<locals>.<lambda>   ri   r/   Trj   rF   r   c                    [        U 5      $ r>   re   rf   s    r-   rg   r     s    JqMr/   rv   c                    [        U 5      $ r>   re   rf   s    r-   rg   r     s    *Q-r/   )
r$   r#   rI   r   addr?   _partial_useful_idsr   rm   min)	r(   r)   r*   
useful_idsr+   r`   rB   dead	non_wildss	            r-   r'   r'      sK   ;;##F+D 5J&v{{D9JEqtt$    )d;
 [[1::NN144   KK:Kq44z#9K:#D
 tAwzzl## #KK:KqwqzKI:s9*ABEEFGG3v{{(?@CCDEE 	; ;s   "F9F.FFc                  ^^^ SSK Jn  U  Vs/ s H;  n[        U5      (       a  M  [        U5      (       a  M'  [	        U5      (       d  M9  UPM=     nn[        5       nU GH  nUR                  [        :X  aJ  U(       aA  U" S U 5       5      R                  S5      S   S   mUR                  U4S jU 5       5        M`  Mb  UR                  [        :X  a  [        [        S U 5       5      5      nU(       d  M  / nUS   /n	USS  H>  n
XS   S-   :X  a  U	R                  U
5        M!  [        U	5      [        U5      :  a  U	nU
/n	M@     [        U	5      [        U5      :  a  U	n[        U5      mUR                  U4S	 jU 5       5        GM,  UR                  [        :X  d  GMC  U(       d  GMM  U" S
 U 5       5      R                  S5      S   S   mUR                  U4S jU 5       5        GM     U$ s  snf )a-  Return card IDs worth keeping when the full phase can't yet be assembled.

For each requirement we keep the cards that best contribute to that group:
- SET: all naturals of the most common rank
- RUN: all naturals that form the longest consecutive chain
- COLOR: all naturals of the most common color
r   )Counterc              3  8   #    U  H  oR                   v   M     g 7fr>   rankr@   s     r-   rC   &_partial_useful_ids.<locals>.<genexpr>       #9DqFFDrE   r   c              3  ^   >#    U  H"  oR                   T:X  d  M  UR                  v   M$     g 7fr>   r   r?   )rA   rB   	best_ranks     r-   rC   r     s      HDqFFi4GdaddD   --c              3  8   #    U  H  oR                   v   M     g 7fr>   r   r@   s     r-   rC   r     r   rE   Nr2   c              3  ^   >#    U  H"  oR                   T;   d  M  UR                  v   M$     g 7fr>   r   )rA   rB   best_sets     r-   rC   r   ,  s      C1(0B$!$$r   c              3  8   #    U  H  oR                   v   M     g 7fr>   )suitr@   s     r-   rC   r   0  s     $:TVVTrE   c              3  ^   >#    U  H"  oR                   T:X  d  M  UR                  v   M$     g 7fr>   )r   r?   )rA   rB   
best_colors     r-   rC   r   1  s      IDqFFj4HdaddDr   )collectionsr   r   r   r   rI   kindr   most_commonrK   r   rm   appendlenr   )r$   r*   r   rB   natsusefulrM   
seen_ranksbestcurrentrr   r   r   s              @@@r-   r   r     s    $Tt!71:AgajA[QR^AtDTuF88y ##9D#99EEaHKAN	HDHH  XX"#9D#9 9:J D",Q-G^a'NN1%7|c$i/& cG $ 7|c$i'4yHMMCCCXX$t$$:T$::FFqI!LQO
IDII9 < MC Us   G1G1G1
G1)r(   'Phase10Game'r)   'Phase10Player'returnz
str | None)r(   r   r)   r   r   str)r8   r	   r$   
list[Card]r   bool)r(   r   r)   r   r   ztuple[Card, int] | None)r$   r   r   zset[int])#__doc__
__future__r   randomtypingr   r(   r   stater   game_utils.cardsr	   r
   r   r   r   r   r   	evaluatorr   r   r   r   r   r   r   r.   r   r5   r   r   r&   r!   r'   r    r/   r-   <module>r      sg    #   !$ $ Z Z   ")T	>(L*0&.'FT+r/   