
    i2                    b   S r 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JrJrJrJrJr  S S jrS S	 jrS S
 jrS!S"S jjrS!S#S jjrS!S$S jjrS!S%S j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"S-S jr#S.S jr$S.S jr%S.S jr&S/S jr'S0S jr(S1S jr)g)2zJPhase 10 evaluation: group validation, hit checking, scoring, card naming.    )annotations   )Card)Localization   )PhaseRequirement
TableGroup	GROUP_SET	GROUP_RUNGROUP_COLORP10_RANK_WILDP10_RANK_SKIPP10_COLOR_NAMESPHASESPHASE_DESC_KEYSEVEN_PHASESc                (    U R                   [        :H  $ N)rankr   cards    =C:\Users\dbart\PlayPalace11\server\games\phase10\evaluator.pyis_wildr          99%%    c                (    U R                   [        :H  $ r   )r   r   r   s    r   is_skipr      r   r   c                @    SU R                   s=:*  =(       a    S:*  $ s  $ )Nr      r   r   s    r   is_numberedr!   "   s    		Rr   c                   [        U 5      (       a  [        R                  " US5      $ [        U 5      (       a  [        R                  " US5      $ [        R                  " U R
                  S5      nU(       a  [        R                  " X5      O[        U R
                  5      n[        R                  " USU R                  US9$ )z+Return the spoken name for a Phase 10 card.zphase10-card-wildzphase10-card-skip zphase10-card-numbered)numbercolor)r   r   getr   r   suitstrr   )r   locale	color_keyr%   s       r   p10_card_namer+   +   s    t}}(;<<t}}(;<<##DIIr2I3<LV/#dii.EF$;DIIUZ[[r   c                    U (       d  [         R                  " US5      $ U  Vs/ s H  n[        X!5      PM     nn[         R                  " X5      $ s  snf )z2Format a list of Phase 10 cards for speech output.zno-cards)r   r&   r+   format_list_and)cardsr)   cnamess       r   p10_cards_namer1   6   sG    
33/45u!]1%uE5''66 6s   Ac                   U R                   [        :X  a   [        R                  " USU R                  S9$ U R                   [
        :X  a   [        R                  " USU R                  S9$ [        R                  " USU R                  S9$ )zAShort spoken description of a phase requirement, e.g. 'set of 3'.zphase10-req-set)countzphase10-req-runzphase10-req-color)kindr
   r   r&   r3   r   )reqr)   s     r   req_descriptionr6   >   sh    
xx9(9KK
xx9(9KKF$7syyIIr   c                t    [         R                  " U S5      nU(       a  [        R                  " X5      $ SU  3$ )z@Full spoken description of a phase, e.g. 'Phase 1: 2 sets of 3'.r#   zPhase )r   r&   r   )	phase_numr)   keys      r   phase_descriptionr:   G   s3    


i
,C,/<F(Ivi[5IIr   c                j    [        U 5      (       a  g[        U 5      (       a  gU R                  S:  a  gg)z=Penalty point value of a card remaining in hand at round end.      
      )r   r   r   r   s    r   
score_cardr@   R   s+    t}}t}}yyBr   c                &    [        S U  5       5      $ )z)Total penalty points for a list of cards.c              3  8   #    U  H  n[        U5      v   M     g 7fr   )r@   .0r/   s     r   	<genexpr>score_hand.<locals>.<genexpr>_   s     ,ez!}}e   )sum)r.   s    r   
score_handrI   ]   s    ,e,,,r   c                V    U  Vs/ s H  n[        U5      (       a  M  UPM     sn$ s  snf r   )r   )r.   r/   s     r   	_naturalsrK   g   s     /u!GAJAu///s   &&c                   ^ [        U 5      U:  a  g[        U 5      nU(       d  gUS   R                  m[        U4S jU 5       5      (       a  gg)zEValidate that cards form a valid set (same rank, at least 1 natural).Fzphase10-err-need-cardsFzphase10-err-need-naturalr   c              3  @   >#    U  H  oR                   T:g  v   M     g 7fr   r    )rD   r/   ref_ranks     r   rE   &_validate_set_cards.<locals>.<genexpr>s   s     
,t!66Xt   )Fzphase10-err-invalid-setTr#   )lenrK   r   any)r.   	min_countnatsrP   s      @r   _validate_set_cardsrX   k   sF    
5zI.UD0Aw||H

,t
,,,/r   c                   [        U 5      U:  a  g[        U 5      nU(       d  g[        S U 5       5      (       a  g[        S U 5       5      (       a  g[        U 5      [        U5      -
  n[        S U 5       5      n[        U5      [        [	        U5      5      :w  a  gUS   US   peXe-
  S	-   [        U5      -
  nXs:  a  gg
)zValidate that cards form a valid run (consecutive numbers, at least 1 natural).

Wilds fill internal gaps and may extend the run at either end.
The combined span must be achievable with the available wilds.
rM   rN   c              3  8   #    U  H  n[        U5      v   M     g 7fr   )r   rC   s     r   rE   &_validate_run_cards.<locals>.<genexpr>   s     
$t!71::trG   )Fzphase10-err-invalid-runc              3  B   #    U  H  n[        U5      (       + v   M     g 7fr   r!   rC   s     r   rE   r[      s     
,t!{1~ts   c              3  8   #    U  H  oR                   v   M     g 7fr   r    rC   s     r   rE   r[      s     ,t!vvtrG   r   r   rS   )rT   rK   rU   sortedset)r.   rV   rW   
wild_count	nat_ranksmin_rmax_rinternal_gapss           r   _validate_run_cardsrg   x   s     5zI.UD0

$t
$$$/

,t
,,,/Uc$i'J,t,,I 9~S^,,/Q<25]Q&#i.8M!/r   c                   ^ [        U 5      U:  a  g[        U 5      nU(       d  gUS   R                  m[        U4S jU 5       5      (       a  gg)z;Validate that cards are all one color (at least 1 natural).rM   rN   r   c              3  @   >#    U  H  oR                   T:g  v   M     g 7fr   )r'   )rD   r/   	ref_colors     r   rE   (_validate_color_cards.<locals>.<genexpr>   s     
-166YrR   )Fzphase10-err-invalid-colorrS   )rT   rK   r'   rU   )r.   rV   rW   rj   s      @r   _validate_color_cardsrl      sF    
5zI.UD0QI

-
---1r   c                    UR                   [        :X  a  [        XR                  5      $ UR                   [        :X  a  [        XR                  5      $ [        XR                  5      $ )zValidate a list of cards against a phase requirement.

Returns:
    (True, "") on success or (False, error_ftl_key) on failure.
)r4   r
   rX   r3   r   rg   rl   )r.   r5   s     r   validate_grouprn      sI     xx9"5))44
xx9"5))44 		22r   c                   [        U5      (       a?  U R                  R                  [        :X  a  gU R                  R                  [        :X  a  gg[        U5      (       a  gU R                  R                  [        :X  a;  [        U R                  5      nU(       a  UR                  US   R                  :w  a  ggU R                  R                  [        :X  a0  U R                  U/-   n[        U[        U5      5      u  pEU(       d  gg[        U R                  5      nU(       a  UR                  US   R                  :w  a  gg)zCheck whether new_card can legally be added to an existing table group.

Returns:
    (True, "") if valid, (False, reason_ftl_key) otherwise.
)Fzphase10-hit-invalid-set)Fzphase10-hit-invalid-run)Fzphase10-hit-invalid-colorrS   r   )r   requirementr4   r
   r   r   rK   r.   r   rg   rT   r'   )groupnew_cardrW   testok_s         r   can_hit_grouprv      s     x!!Y.3!!Y.31x*%HMMT!W\\13* {{hZ'#D#d)43 U[[!Da-1r   c                    U  Vs/ s H  n[        U5      (       a  M  UPM     nn/ nU H>  n[        X55      nUc    gUR                  U5        U H  nUR                  U5        M     M@     U$ s  snf )ao  Try to find a valid assignment of hand cards to phase requirements.

Returns a list of card groups (one per requirement) if successful, else None.
Only numbered cards and wilds are candidates; skips are excluded from phases.

Uses a greedy approach: satisfy requirements in order, preferring naturals
before committing wilds. Good enough for bot use; not exhaustive.
N)r   _pick_groupappendremove)hand
phase_reqsr/   	availablegroupsr5   rq   s          r   find_phase_assignmentr      so     !3Dq
DI3!FI+=eAQ   M 4s
   A-A-c                   U  Vs/ s H  n[        U5      (       d  M  UPM     nnU  Vs/ s H)  n[        U5      (       a  M  [        U5      (       a  M'  UPM+     nnUR                  [        :X  a  [	        XCUR
                  5      $ UR                  [        :X  a  [        XCUR
                  5      $ [        XCUR
                  5      $ s  snf s  snf )zBGreedily pick cards from available to satisfy req, or return None.)	r   r   r4   r
   	_pick_setr3   r   	_pick_run_pick_color)r}   r5   r/   wildsrW   s        r   rx   rx      s    !0	1WQZQ	E0 Fy!
A71:AyDF
xx9cii00
xx9cii00tCII.. 1Fs   CCC
C
C
c                j   SSK Jn  0 nU  H.  nUR                  UR                  / 5      R	                  U5        M0     S nSnUR                  5        HU  u  p[        U	5      n
[        SX*-
  5      nU[        U5      ::  d  M/  X:  d  M6  XS [        SU[        U	5      -
  5       -   nU
nMW     Ub  [        U5      U:  as  U  Vs/ s H8  oUR                  US   R                  :X  d  M!  [        US   5      (       a  M6  UPM:     nn[        SU[        U5      -
  5      nU[        U5      ::  a  XS U -   $ g s  snf )Nr   )Counterr_   )	collectionsr   
setdefaultr   ry   itemsrT   maxr   )rW   r   r3   r   rank_groupsr/   bestbest_nat_countr   r.   	nat_countneeded_wildsnat_for_ranks                r   r   r     s'   #)+Kqvvr*11!4  #DN"((*J	1e/03u:%)*D!<#aU);"<==D&N + CI.#'Z4a66T!W\\+AQUVWQXIY4Z1ec,&7783u:%"666  [s     D0$D09D0c                h  ^^ [        S U  5       S S9nU(       d  gSn[        [        S U 5       5      5      n[        [        U5      5       H  n/ mSnXV   S-
  n[        XV   S5       H  m[	        UU4S	 jU 5       S5      n	U	(       a  TnTR                  U	5        OU[        U5      :  a  US-  nTnO  OB[        T5      U-   n
X:  d  Mc  USU nTU-   nUb  [        U5      [        U5      :  d  M  UnM     U(       d  M  [        U5      U:  d  M  Us  $    U(       a  [        U5      U:  a  U$ S$ )
z0Find a run of at least `count` among nats+wilds.c              3  J   #    U  H  n[        U5      (       d  M  Uv   M     g 7fr   r]   rC   s     r   rE   _pick_run.<locals>.<genexpr>(  s     9$Q+a.qq$s   #	#c                    U R                   $ r   r    )r/   s    r   <lambda>_pick_run.<locals>.<lambda>(  s    r   )r9   Nc              3  8   #    U  H  oR                   v   M     g 7fr   r    rC   s     r   rE   r   .  s     5HqFFHrG   r   r      c              3  Z   >#    U  H   oR                   T:X  d  M  UT;  d  M  Uv   M"     g 7fr   r    )rD   r/   r   run_natss     r   rE   r   8  s%      ]Hq$1T\K\Hs   ++	+)r`   ra   rangerT   nextry   )rW   r   r3   numberedr   
seen_ranks	start_idx
wilds_used	prev_rankr   total
used_wilds	candidater   r   s                @@r   r   r   &  s-   9$9?OPH #D5H556J3z?+	!
)A-	*/4D ]H ]_cdL 	-c%j(a
 	MJ.E~";J/
$z1	<3y>CI#=$D' 5* 4CI&K9 ,< CI.48D8r   c                *   SSK Jn  U" [        5      nU  H2  n[        U5      (       d  M  XER                     R                  U5        M4     UR                  5        H5  u  pg[        SU[        U5      -
  5      nU[        U5      ::  d  M.  XqSU -   s  $    g)z'Find a color group of at least `count`.r   )defaultdictN)	r   r   listr!   r'   ry   r   r   rT   )	rW   r   r3   r   color_groupsr/   r%   r.   r   s	            r   r   r   Q  s    '*5d*;Lq>> ''*  %**,1ec%j013u:%,/// -
 r   c                F    U (       a  [         $ [        [        SS5      5      $ )zCReturn the ordered list of phase numbers used in this game variant.r      )r   r   r   	even_onlys    r   active_phasesr   f  s    #;;eArl);;r   c                    [        U5      n UR                  U 5      nUS-   [        U5      :  a  X#S-      $ S$ ! [         a     gf = f)z<Return the next phase number, or 11 if the game is complete.r   r   )r   indexrT   
ValueError)currentr   phasesidxs       r   
next_phaser   k  sQ    9%Fll7#"%'CK"7vAg?R? s   )9 9 
AAc                    [        U 5      S   $ )Nr   )r   r   s    r   starting_phaser   u  s    #A&&r   N)r   r   returnbool)en)r   r   r)   r(   r   r(   )r.   
list[Card]r)   r(   r   r(   )r5   r   r)   r(   r   r(   )r8   intr)   r(   r   r(   )r   r   r   r   )r.   r   r   r   )r.   r   r   r   )r.   r   rV   r   r   tuple[bool, str])r.   r   r5   r   r   r   )rq   r	   rr   r   r   r   )r{   r   r|   zlist[PhaseRequirement]r   zlist[list[Card]] | None)r}   r   r5   r   r   list[Card] | None)rW   r   r   r   r3   r   r   r   )r   r   r   z	list[int])r   r   r   r   r   r   )r   r   r   r   )*__doc__
__future__r   game_utils.cardsr   messages.localizationr   stater   r	   r
   r   r   r   r   r   r   r   r   r   r   r!   r+   r1   r6   r:   r@   rI   rK   rX   rg   rl   rn   rv   r   rx   r   r   r   r   r   r    r   r   <module>r      s    P " $ 1   &&& \7JJ-0
D

3$$X
& 4	/6(9V*<
'r   