
    Iim                    z   % S r SSKJr  SSKrSSKrSSKrSSKrSSKrSSKrSSK	r	SSK
JrJr  \(       a  SSKJr  \R                  " \5      rSqS\S'   SS	 jrSS
 jrSS jr\R.                  " S5      r\R.                  " S5      rSS jr      SS jr S         SS jjrSS jrSS jr " S S5      rg)zGNUBG subprocess integration for Backgammon bot AI.

Manages a persistent gnubg-cli --tty subprocess for move evaluation.
Falls back to random moves when GNUBG is unavailable.
    )annotationsN)TYPE_CHECKINGCallable   )BackgammonGameStatezbool | None
_availablec                 X    S H$  n [         R                  " U 5      nU(       d  M"  Us  $    g)z"Find gnubg-cli executable on PATH.)z	gnubg-clizgnubg-cli.exegnubgz	gnubg.exeN)shutilwhich)namepaths     <c:\Users\dbart\PlayPalace11\server\games\backgammon\gnubg.py_find_gnubgr      s*    D||D!4K E     c                     [         b  [         $ [        5       SLq [         (       d  [        R                  S5        [         $ )zBCheck if GNUBG binary is on PATH. Caches result after first check.Nz5GNUBG not found on PATH; bot will use random fallback)r   r   loginfo r   r   is_gnubg_availabler   #   s4     d*J:HIr   c                &   U R                   n/ n[        S5       H+  nUR                  [        SUR                  U   5      5        M-     UR
                  n/ n[        SSS5       H,  nUR                  [        SUR                  U   * 5      5        M.     UR                  nU R                  S:X  a  XVpX$pOX$pXVp/ nU H)  nUR                  S/U-  5        UR                  S5        M+     UR                  S/U-  5        UR                  S5        U	 H)  nUR                  S/U-  5        UR                  S5        M+     UR                  S/U
-  5        UR                  S5        [        U5      S:  a"  UR                  S5        [        U5      S:  a  M"  [        S5      n[        US	S 5       H#  u  p>U(       d  M  XS
-  ==   SUS
-  -  -  ss'   M%     [        R                  " [        U5      5      R                  S5      R!                  S5      $ )a#  Encode the board as a GNUBG Position ID (14-char base64 string).

GNUBG position key is 80 bits built from unary-encoded checker counts:
  First half: NOT-on-roll player's points 1-24 (from their perspective) + bar
  Second half: on-roll player's points 1-24 (from their perspective) + bar
Each point: N ones followed by a zero separator. 80 bits total.
Packed little-endian into 10 bytes, then base64-encoded (no padding).

We always encode from the on-roll player's perspective, with the
opponent (not-on-roll) encoded first per the GNUBG spec.
   r      redr   P   
   N   ascii=)boardrangeappendmaxpointsbar_red	bar_whitecurrent_colorextendlen	bytearray	enumeratebase64	b64encodebytesdecoderstrip)stater!   
red_countsired_barwhite_counts	white_barfirst_counts	first_barsecond_counts
second_barbitscount	key_bytesbits                  r   encode_position_idr@   3   s    KKE
 J2Y#aa12 mmGL2r2CELLO#345 I e#".i$.z",i$0z DQC%K A  	KKi KKNQC%K A  	KKj !KKN d)b.A d)b. "ID"I&31fq1u- ' E),-44W=DDSIIr   z.^\s*(\d+)\.\s+Cubeful\s+\d+-ply\s+(.*?)\s+Eq\.z!(bar|\d+)/(off|\d+)(?:\((\d+)\))?c                   [         R                  U 5      nU(       d  gUR                  S5      R                  5       n/ n[        R                  U5       Hj  nUR                  S5      nUR                  S5      nUR                  S5      (       a  [        UR                  S5      5      OSnUR                  XVU45        Ml     U(       a  U$ S$ )zParse a single hint line into a list of (source, dest, count) tuples.

Returns None if the line doesn't match.
Source/dest are strings: "bar", "off", or point number strings.
Count is how many checkers make that sub-move (default 1).
N   r      )_HINT_REmatchgroupstrip_SUBMOVE_REfinditerintr#   )linemmove_strsubmovessmsrcdstr=   s           r   parse_hint_linerR      s     	tAwwqz!HH""8,hhqkhhqk$&HHQKKBHHQK Q5)*	 -
  8)T)r   c                    / nU  HT  u  p4n[        [        U5      U5      n[        [        U5      U5      n[        U5       H  nUR                  Xg45        M     MV     U$ )u  Convert parsed GNUBG hint sub-moves to checker goals.

Returns a list of (source_idx, dest_idx) pairs representing where
each checker should START and where it should END UP. These are NOT
individual die moves — compound moves like "24/14" become a single
goal (24, 14). The bot resolves each goal into individual die steps
at execution time using the game's legal move generator, which
correctly handles bar-first rules, forced dice, and overshoot.
)_linear_to_index_parse_linearr"   r#   )	hint_submovescolorgoalssrc_strdst_strr=   src_idxdst_idx_s	            r   hint_to_goalsr^      s^     $&E#0%"=#95A"=#95AuALL'+,  $1
 Lr   c                \   SSK Jn  SSKJnJn  U" U5      nUb  U Vs/ s H  oU;   d  M
  UPM     nnU(       d  U R                  5         g0 n	U HU  n
U" XU
5       HE  nU	R                  UR                  / 5      R                  UR                  UR                  U
45        MG     MW     U" X5      S:  nU(       a  U	R                  S/ 5      nU(       d  U R                  5         g[        U 5       HM  u  nu  nnUS:X  d  M  U H  u  nnnUU:X  d  M  UU4X'   SU SU 3s  s  $    US   u  nnnUU4X'   SU SU 3s  $    US   u  nnnSU SU 3$ [        U 5       H  u  nu  nnUU:X  a  M  U	R                  U/ 5      nU(       d  M,  U H  u  nnnUU:X  d  M  UU4X'   SU SU 3s  s  $    SnS	nU HA  u  nnnUS
:X  a  US
:X  a  SX'   SU SU 3s  s  $ M%  [        UU-
  5      nUU:  d  M;  UnUU4nMC     Uc  M  US   U4X'   SUS    SUS    3s  $    U R                  5         gs  snf )aK  Pick the next legal move that progresses toward a GNUBG goal.

Examines the goal list and finds a legal move that advances a checker
toward its target. Modifies `goals` in place (removes completed goals,
updates source of in-progress goals). Returns an action string like
"point_{src}_{dst}", or None if no goal can be progressed.
r   )generate_legal_moves)	bar_countremaining_dice_uniqueNr   r   point_r]     r   )r   r   )movesr`   r2   ra   rb   clear
setdefaultsourcer#   destinationgetr,   abs)rX   r2   rW   forced_dicer`   ra   rb   usable_dicedlegal_by_sourcedie_valrL   on_bar	bar_movesr4   gsrcgdstrP   rQ   dvmoves_from_srcbest	best_distdists                           r   resolve_next_actionrz      s    ,7'.K"-B+Qk1Aq+B >@O%eG<A&&qxx4;;QXXq}}V]<^_ = 
 u$q(F#''B/	KKM(/OA|drz$-LCbd{$($<!'uAcU33	 %.  )|S";uAcU++  0 !|S"uAcU## %U+<D$4<(,,T26 +LCbd{ $<uAcU++ + 	*LCbby2:'EH#C5#//sTz?Di 	Sz + QEHDG9Ad1gY//I ,N 
KKMY Cs
   	H)H)c                4    U S:X  a  gU S:X  a  g[        U 5      $ )z>Parse a GNUBG point string to linear position (bar=25, off=0).bar   offr   )rJ   )	point_strs    r   rU   rU   
  s"    EEy>r   c                >    U S:X  a  gU S::  a  gUS:X  a  U S-
  $ SU -
  $ )u   Convert a linear position to a board index.

Linear space: bar=25, points 24→1, off=0
Board indices: bar=-1, points 0-23, off=24

Red:   point N → index N-1  (point 1 = index 0)
White: point N → index 24-N (point 1 = index 23)
r}   r   r   r   r   r   r   )posrW   s     r   rT   rT     s1     by
ax~QwCxr   c                     \ rS rSrSrSrSrSSS j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#S jjr\S$S j5       r\S%S j5       r        S&S jr        S!S jr S'         S(S jjrS)S jrS*S jr  S+     S,S jjr S     S-S jjrS.S jrSS jrSrg)/GnubgProcessi+  aD  Manages a persistent gnubg-cli subprocess for move evaluation.

Uses a persistent reader thread to consume stdout lines into a queue,
since select() doesn't work with subprocess pipes on Windows.

Commands are batched and sent together; only a single short idle wait
is needed after the final command to collect all output.
g?g      ?c                    S U l         [        R                  " 5       U l        Xl        SU l        [        R                  " 5       U l        g )NF)	_proc	threadingLock_lock_ply_startedqueueQueue_output_queue)selfplys     r   __init__GnubgProcess.__init__;  s1    .2
^^%
	6;kkmr   c           	        [        5       nU(       d  g [        R                  " USSS/[        R                  [        R                  [        R                  SSS9U l        [        R                  " 5       U l        [        R                  " U R                  SS9nUR                  5         U R                  U R                  S	9  U R                  S
U R                    3S/5        U R                  U R                  S	9  SU l        [$        R'                  SU R                   5        g! [(        [*        4 a'  n[$        R-                  SU5        SU l         SnAgSnAff = f)z4Start the GNUBG subprocess. Returns True on success.Fz-tz-rz-qTr   )stdinstdoutstderrtextbufsize)targetdaemon)	idle_waitz,set evaluation chequerplay evaluation plies znew gamez!GNUBG subprocess started (ply=%d)zFailed to start GNUBG: %sN)r   
subprocessPopenPIPEDEVNULLr   r   r   r   r   Thread_reader_loopstart_read_until_idle_STARTUP_IDLE_WAIT_send_batchr   r   r   r   OSErrorFileNotFoundErrorwarning)r   exereaderes       r   r   GnubgProcess.startB  s"   m	#))dD$' oo!!))DJ "'D%%T->->tLFLLN !!D,C,C!DB499+N !!D,C,C!D DMHH8$))D*+ 	KK3Q7DJ	s   DD# #E3EEc                    U R                   (       a  U R                   R                  (       a  U R                   R                  R                  5       nU(       d  U R                  R	                  S5        gU R                  R	                  UR                  S5      5        U R                   (       a  U R                   R                  (       a  M  gggg! [        [        4 a    U R                  R	                  S5         gf = f)z9Persistent thread that reads stdout lines into the queue.Nz
)r   r   readliner   putr1   
ValueErrorr   )r   rK   s     r   r   GnubgProcess._reader_loopg  s    	)**!2!2zz((113&&**40""&&t{{6':; **!2!2!2*!2* G$ 	)""4(	)s   A2C 5AC +C?>C?c                   U R                      U R                  (       aq   U R                  R                  R                  S5        U R                  R                  R	                  5         U R                  R                  SS9  SU l        SU l        SSS5        g! [         a/     U R                  R                  5          N>! [         a      NKf = ff = f! , (       d  f       g= f)zTerminate the subprocess.zquit
rC   timeoutNF)	r   r   r   writeflushwait	Exceptionkillr   r   s    r   stopGnubgProcess.stops  s    ZZzzJJ$$**84JJ$$**,JJOOAO. "
 % Z ! 

)$  ZsM   CA"BC
C$C >C 
C
CCCCC
C#c                V   U R                      U R                  (       a  U R                  (       d
   SSS5        g U R                  XUSS9sSSS5        $ ! [         a8  n[
        R                  SU5        U R                  5          SnASSS5        gSnAff = f! , (       d  f       g= f)zqGet the best move for the current position.

Returns a list of (source_idx, dest_idx) pairs, or None on failure.
NF
pick_worstGNUBG query failed: %sr   r   r   _query_hintr   r   r   _restartr   r2   rW   r   r   s        r   get_best_moveGnubgProcess.get_best_move  s     ZZ::T]] Z''g%'P	 Z
  4a8 Z
 Z.   $BA
B&BBBB
B(c                V   U R                      U R                  (       a  U R                  (       d
   SSS5        g U R                  XUSS9sSSS5        $ ! [         a8  n[
        R                  SU5        U R                  5          SnASSS5        gSnAff = f! , (       d  f       g= f)zBGet the worst move (Whackgammon). Asks for many hints, picks last.NTr   r   r   r   s        r   get_worst_moveGnubgProcess.get_worst_move  s     ZZ::T]] Z''g$'O	 Z
  4a8 Z
 Zr   c                X   U R                      U R                  (       a  U R                  (       d
   SSS5        g U R                  XU5      sSSS5        $ ! [         a8  n[
        R                  SU5        U R                  5          SnASSS5        gSnAff = f! , (       d  f       g= f)uv  Get GNUBG's cube decision for the current position.

Always queries from the on-roll player's perspective. Returns one of:
    "no-double"    — should not double
    "too-good"     — too good to double (would lose gammon value)
    "double-take"  — should double, opponent should take
    "double-pass"  — should double, opponent should drop
    None — on failure
NzGNUBG cube query failed: %s)r   r   r   _query_cuber   r   r   r   r   s        r   get_cube_decisionGnubgProcess.get_cube_decision  sz     ZZ::T]] Z''g>	 Z
  91= Z
 Zs.   $BA
B &BBBB
B)c                   U R                      U R                  (       a  U R                  (       d
   SSS5        g U R                  XU5      nU H$  nSU;   d  M  UR	                  5       s  sSSS5        $     SSS5        g! [
         a8  n[        R                  SU5        U R                  5          SnASSS5        gSnAff = f! , (       d  f       g= f)znGet a human-readable cube hint string from GNUBG.

Returns the "Proper cube action" line, or None on failure.
NzProper cube actionzGNUBG cube hint failed: %s)	r   r   r   _setup_and_query_cuberG   r   r   r   r   )r   r2   rW   r   outputrK   r   s          r   get_cube_hint_textGnubgProcess.get_cube_hint_text  s     ZZ::T]] Z	33E'J"D+t3#zz|+ Z
 #  Z  8!< Z Zs:   $CBB5B
C&B>1C>CC
Cc           	     f   U R                      U R                  (       a  U R                  (       d
   SSS5        g [        UR                  UR
                  5       VVs/ s H  u  pVU(       a  M  UPM     nnn[        U5      S:  a
   SSS5        gU R                  / U R                  U5      QSUS    SUS    3PSU 3P5        U R                  S US	9n/ n	U Hc  n
[        R                  U
5      nU(       d  M!  U	R                  UR                  S5       S
UR                  S5      R                  5        35        Me     U	=(       d    S sSSS5        $ s  snnf ! [         a8  n[         R#                  SU5        U R%                  5          SnASSS5        gSnAff = f! , (       d  f       g= f)ztGet ranked move hints as human-readable strings.

Returns a list like ["1. 8/5 6/5", "2. 13/7"] or None on failure.
NrB   	set dice r    r   hint c                0    [         R                  U 5      S L$ NrD   rE   rK   s    r   <lambda>1GnubgProcess.get_move_hint_text.<locals>.<lambda>  s    !5T!Ar   r   z. zGNUBG move hint failed: %s)r   r   r   zipdice	dice_usedr*   r   _position_commands_read_until_matchrD   rE   r#   rF   rG   r   r   r   r   )r   r2   rW   
hint_countr   rn   uunused_dicer   hintsrK   rL   r   s                r   get_move_hint_textGnubgProcess.get_move_hint_text  s    ZZ::T]] Z-0U__-MW-MTQUVq-MW{#a' Z   007#KN#31[^4DE  
|, //A# 0 
 "D t,Aq
|2aggaj6F6F6H5I%JK # }$3 Z X,  8!<; Z45 ZsT   $F"#EE/E5EA'E<AEE
F'&FF"FF""
F0c                "    S[        U 5       3S/$ )z.Build the commands to set up a board position.z
set board z
set turn 1)r@   )r2   s    r   r   GnubgProcess._position_commands  s"     +E234
 	
r   c                    SU R                    3/nU R                  (       d  UR                  S5        U$ U R                  U:X  a  UR                  S5        U$ UR                  S5        U$ )z(Build the commands to set up cube state.zset cube value zset cube centrezset cube owner 1zset cube owner 0)
cube_value
cube_ownerr#   )r2   rW   cmdss      r   _cube_commandsGnubgProcess._cube_commands  so     "%"2"2!345KK)*
 	 &KK*+  KK*+r   c                    U R                  / U R                  U5      QU R                  X5      QSP5        U R                  S US9$ )z:Set position + cube, send hint 0, return raw output lines.zhint 0c                    SU R                  5       ;   =(       d+    U R                  5       R                  S5      =(       a    SU ;   $ )Nproper cube action1.	No double)lowerrG   
startswithr   s    r   r   4GnubgProcess._setup_and_query_cube.<locals>.<lambda>"  s:    -= G

''-E+2EGr   r   )r   r   r   r   )r   r2   rW   r   s       r   r   "GnubgProcess._setup_and_query_cube  sj     	((/$$U2 	
 %%G & 
 	
r   c                   U R                  XU5      nU Hc  nUR                  5       nSU;  a  M  SU;   d  SU;   a
  SU;   a    g  gSU;   d  SU;   a  S	U;   d  S
U;   a    g  gSU;   a    gS	U;   d  S
U;   d  Mc    g   0 nU H  nUR                  5       nUR                  S5      (       a(  SU;   a"   [	        UR                  5       S   5      US'   MQ  S	UR                  5       ;   a(  SU;   a"   [	        UR                  5       S   5      US'   M  SUR                  5       ;   d  M  SU;   d  M   [	        UR                  5       S   5      US'   M     U(       aO  UR                  SS5      n	UR                  SS5      n
UR                  SS5      n[        XU5      nX:X  a  gX:X  a  ggg! [         a     GM4  f = f! [        [        4 a     GML  f = f! [        [        4 a     GMd  f = f)uL  Send position to GNUBG and parse the cube evaluation.

Returns one of:
    "no-double"    — should not double
    "too-good"     — too good to double (would lose gammon value)
    "double-take"  — should double, opponent should take
    "double-pass"  — should double, opponent should drop
    None           — on failure
r   z	no doublezno redoubleztoo goodztoo-goodz	no-doubledoubleredoublepassdropzdouble-passzdouble-taketaker   r   r   DoubleiN)
r   r   rG   r   floatsplitr   
IndexErrorrj   r$   )r   r2   rW   r   r   rK   lowequitiesline_snddtdprw   s                r   r   GnubgProcess._query_cube'  s    ++E'B D**,C#3.c!]c%9$%"3*"3S=FcM($}$}#$ & &(DZZ\F  &&;&+@,1&,,.2D,EH[) 6<<>)h&.@.3FLLN24F.GH]+ 6<<>)h&.@.3FLLN24F.GH]+ $ k40BmT2BmT2Brr?Dz"z$ 1 " 
 #J/ 
 #J/ s6   0F&,F8,G&
F54F58GGG%$G%c                   [        UR                  UR                  5       VVs/ s H  u  pVU(       a  M  UPM     nnn[        U5      S:  a&  U(       a	  US   nXU/nO[        R                  S5        gU(       a  SOSnU R                  / U R                  U5      QSUS    SUS    3PS	U 3P5        U R                  S
 US9n	[        R                  S[        U	5      5        Sn
SnU	 H  n[        U5      nU(       d  M  Uc  UnUn
M      U(       a  U
OUnU(       a#  [        X5      n[        R                  SU5        U$ [        R                  S5        gs  snnf )zSend position to GNUBG and parse the hint response.

Returns a list of checker goals as (source_idx, dest_idx) pairs.
These are final destinations, not individual die steps.
rB   r   z#GNUBG query skipped: no unused diceNrd   r   r   r   r   c                0    [         R                  U 5      S L$ r   r   r   s    r   r   *GnubgProcess._query_hint.<locals>.<lambda>  s    -T9r   r   zGNUBG hint response: %d lineszGNUBG goals: %sz!GNUBG: no hint parsed from output)r   r   r   r*   r   debugr   r   r   rR   r^   )r   r2   rW   r   r   rn   r   r   r   r   last_submovesfirst_submovesrK   rN   chosenrX   s                   r   r   GnubgProcess._query_hintl  sf    &)U__%EO%ETQQq%EO{aN f		?@&SA
((/KN+1[^,<= 
|$	
 ''9 ( 
 			13v;? D&t,Hx!)%-N (  #-.!&0EII'/L		56O Ps
   EEc                    U R                   (       ai  U R                   R                  (       aM  U R                   R                  R                  US-   5        U R                   R                  R                  5         ggg)zSend a single command to GNUBG.
N)r   r   r   r   )r   commands     r   _sendGnubgProcess._send  sN    ::$****JJ""7T>2JJ""$ +:r   c                   U R                   (       a|  U R                   R                  (       a`  U R                   R                  R                  SR                  S U 5       5      5        U R                   R                  R	                  5         ggg)zSend multiple commands to GNUBG at once.

All commands are written before flushing, so GNUBG processes them
back-to-back with no idle gaps between them.
 c              3  *   #    U  H	  oS -   v   M     g7f)r  Nr   ).0cmds     r   	<genexpr>+GnubgProcess._send_batch.<locals>.<genexpr>  s     *J#:s   N)r   r   r   joinr   )r   commandss     r   r   GnubgProcess._send_batch  s[     ::$****JJ""277*J*J#JKJJ""$ +:r   Nc                   SSK nUc  U R                  n/ nUR                  5       U-   nUR                  5       U:  an  XSR                  5       -
  n[        X&5      n U R                  R                  [        SU5      S9nUc   U$ UR                  U5        UR                  5       U:  a  Mn  U$ ! [        R                   a    U(       a   U$  N8f = f)zRead GNUBG output from the queue until idle.

GNUBG is considered idle when no new output arrives for `idle_wait`
seconds after receiving at least one line. Use only for startup/setup
where there's no specific pattern to wait for.
r   N{Gz?r   )
time
_IDLE_WAIT	monotonicminr   rj   r$   r#   r   Empty)	r   r   r   r$  linesdeadline	remaining	wait_timeitems	            r   r   GnubgProcess._read_until_idle  s     	I>>#g-nn) >>#33II1I))--c$	6J-K<  T" nn) 	 ;;  s   &B- B- -CCc                   SSK n/ nUR                  5       U-   nUR                  5       U:  a  XSR                  5       -
  n U R                  R                  [	        SU5      S9nUc   U$ UR                  U5        U" U5      (       a  U R                  U5         U$  UR                  5       U:  a  M  U$ ! [        R                   a     N.f = f)u   Read GNUBG output until a line matches the predicate.

Returns all lines read (including the match). This is the correct
way to wait for query results — it doesn't depend on idle timing,
so it works regardless of how long GNUBG takes to compute.
r   Nr#  r   )	r$  r&  r   rj   r$   r#   _drain_quickr   r(  )r   	predicater   r$  r)  r*  r+  r-  s           r   r   GnubgProcess._read_until_match  s     	>>#g-nn) >>#33I
))--c$	6J-K<  T"T??%%e,  # nn)  ;; s   &B5 */B5 5CCc                f   SSK nUR                  5       U R                  -   nUR                  5       U:  ab  X2R                  5       -
  n U R                  R	                  [        SU5      S9nUc  gUR                  U5        UR                  5       U:  a  Ma  gg! [        R                   a     gf = f)zDrain any output that arrives quickly after a match.

After seeing the first hint line, there may be more (equity details,
additional hints for pick_worst). Grab them without a long wait.
r   NgMbP?r   )	r$  r&  r%  r   rj   r$   r#   r   r(  )r   r)  r$  r*  r+  r-  s         r   r0  GnubgProcess._drain_quick  s     	>>#doo5nn) >>#33I))--c%6K-L<T" nn) ;; s   	&B 0B B0/B0c                    [         R                  S5        U R                  (       a"   U R                  R                  5         SU l        SU l        U R                  5         g! [         a     N+f = f)z Kill and restart the subprocess.zRestarting GNUBG subprocessNF)r   r   r   r   r   r   r   r   s    r   r   GnubgProcess._restart	  sV    ./::

! DJ

	  s   A! !
A.-A.)r   r   r   r   r   )r   )r   rJ   returnbool)r8  None)      @)r2   r   rW   strr   r  r8  list[tuple[int, int]] | None)r2   r   rW   r<  r   r  r8  
str | None)rC   r;  )
r2   r   rW   r<  r   rJ   r   r  r8  zlist[str] | None)r2   r   r8  	list[str])r2   r   rW   r<  r8  r?  )r2   r   rW   r<  r   r  r8  r?  )F)
r2   r   rW   r<  r   r  r   r9  r8  r=  )r  r<  r8  r:  )r   r?  r8  r:  )r;  N)r   r  r   zfloat | Noner8  r?  )r1  zCallable[[str], bool]r   r  r8  r?  )r)  r?  r8  r:  )__name__
__module____qualname____firstlineno____doc__r%  r   r   r   r   r   r   r   r   r   r   staticmethodr   r   r   r   r   r  r   r   r   r0  r   __static_attributes__r   r   r   r   r   +  s<    JD#J
)&* HK(14?D	%$ HK(14?D	% HK(14?D	. HK(14?D	2 ("( ( 	(
 ( 
(\ 
 
 	 	
"
 
 	

 

(C"C C 	C
 
CT !3"3 3 	3
 3 
&3r%% "&   
	H (  
	@&
r   r   )r8  r>  r7  )r2   r   r8  r<  )rK   r<  r8  z!list[tuple[str, str, int]] | None)rV   zlist[tuple[str, str, int]]rW   r<  r8  list[tuple[int, int]]r   )
rX   rG  r2   z'BackgammonGameState'rW   r<  rl   zlist[int] | Noner8  r>  )r   r<  r8  rJ   )r   rJ   rW   r<  r8  rJ   ) rD  
__future__r   r-   loggingr   rer   r   r   typingr   r   r2   r   	getLoggerr@  r   r   __annotations__r   r   r@   compilerD   rH   rR   r^   rz   rU   rT   r   r   r   r   <module>rO     s    #    	    **!
K  ;JF ::5 jj=>*(- 4 %)	^ ^ ^ ^ "	^
 ^B0h hr   