
    Ii|                       S r SSKJr  SSKrSSKJr  SSK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K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
K J!r!J"r"J#r#  \(       a  SSK$J%r%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-S2S jr.S3S jr/S4S jr0S-S jr1S-S jr2S-S jr3        S5S jr4      S6S jr5        S7S jr6S8S jr7        S9S jr8S:S jr9S;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rAS=S' jrB        S>S( jrCS?S) jrD      S@S* jrE        SAS+ jrFS.S, jrGg)BzBot AI for Age of Heroes.    )annotationsN)TYPE_CHECKING   )CardCardTypeResourceType	EventType)	GamePhasePlaySubPhase
ActionTypeBuildingTypeWarGoalBUILDING_COSTSTRIBE_SPECIAL_RESOURCE)	can_buildget_affordable_buildingsget_road_targetsexecute_single_build)can_declare_warget_valid_war_targetsget_valid_war_goalscheck_olympics_defensedeclare_warprepare_forcesexecute_war_battle)create_offerannounce_offercheck_and_execute_trades)AgeOfHeroesGameAgeOfHeroesPlayerc                @   UR                   (       a  gU R                  [        R                  :X  a  UR                  U R
                  ;  a  ggU R                  [        R                  :X  a  [        X5      $ U R                  [        R                  :X  a  [        X5      $ g)z9Main bot AI decision function. Returns action ID or None.N	roll_dice)
is_spectatorphaser
   SETUPidsetup_rollsPLAYbot_think_play_phaseFAIRbot_think_fair_phasegameplayers     ;c:\Users\dbart\PlayPalace11\server\games\ageofheroes\bot.py	bot_thinkr0   &   sw      zzY__$99D,,, zzY^^##D11 zzY^^##D11    c                   U R                   [        R                  :X  as  U R                  5       nX;  a  gUR	                  U5      nU R
                  nX4R                  :X  a  UR                  S:X  a  gX4R                  :X  a  UR                  S:X  a  gU R                  5       nX;   aL  UR	                  U5      nU R                  U:X  a+  U R                  S:  a  [        R                  " 5       S:  a  ggU R                  U:w  a  gU R                   [        R                  :X  a  [        X5      $ U R                   [        R                   :X  a  [#        X5      $ g)z&Bot decision making during play phase.Nr   war_roll_diceg?approve_road	deny_road)	sub_phaser   
WAR_BATTLEget_active_playersindex	war_stateattacker_indexattacker_rolldefender_indexdefender_rollroad_request_toroad_request_fromrandomcurrent_playerSELECT_ACTIONbot_select_actionDISCARD_EXCESSbot_discard_excess)r-   r.   active_playersplayer_indexwars        r/   r)   r)   =   s%    ~~000002'%++F3nn ---#2C2Cq2H"---#2C2Cq2H" ,,.N%++F3</D4J4Ja4O }}$%"f$~~333 ..~~444!$//r1   c                T   UR                   (       d  S[        R                  R                   3$ UR                   R                  nUR                   R                  5       nUR                   R                  n[        X5      n[        X5      nU(       a  U$ [        XR5      nU(       a  U$ [        U5      nU(       a  U$ [        XU5      n	U	(       a  U	$ [        XS5      n
U
(       a  U
$ [        U5      nU(       a  U$ [        U5      nU(       a  U$ S[        R                  R                   3$ )zBot selects main turn action.action_)tribe_stater   
DO_NOTHINGvaluecitiesget_available_armiesmonument_progressr   _bot_disaster_action_bot_city_build_action_bot_monument_tax_action_bot_war_action_bot_army_build_action_bot_other_build_action_bot_tax_action)r-   r.   rO   armiesmonument
affordabledisaster_actioncity_actionmonument_action
war_actionarmy_actionother_build_action
tax_actions                r/   rD   rD   g   s   ..44566&&F446F!!33H *$7J +48O(<K.x8O v6J(<K0<!! (J Z**00122r1   c                :    U R                   S::  a  g[        X5      $ )z8Consider playing a disaster card if the timing is right.r   N)current_daybot_should_play_disasterr,   s     r/   rR   rR      s    1#D11r1   c                   US:X  a1  [         R                  U ;   a  S[        R                  R                   3$ [         R                  U ;   a<  US:  a6  [
        R
                  " 5       S:  a  S[        R                  R                   3$ g)z0Prefer building cities when it advances victory.   rK      皙?N)r   CITYr   CONSTRUCTIONrN   rA   )r[   rO   s     r/   rS   rS      so    {|((J60066788J&6A:==?S Z44::;<<r1   c                J    U S:  a  S[         R                  R                   3$ g)z(Collect cards to push monument progress.   rK   Nr   TAX_COLLECTIONrN   )rZ   s    r/   rT   rT      s&    1}22889::r1   c                \   US:  a  g[        X5      b  g[        X5      nU GH  u  pE[        US5      (       a  UR                  (       d  M*  UR                  R	                  5       nUR                  R
                  nXg-   nX(-
  n	U	S:  a  S[        R                  R                   3s  $ U	S:  a:  [        R                  " 5       S:  a  S[        R                  R                   3s  $ M  U	S:  d  M  [        R                  " 5       S	:  d  M  S[        R                  R                   3s  $    g)
zDecide whether to declare war.r   NrL      rK   r   g      ?      ?)
r   r   hasattrrL   rP   
fortressesr   WARrN   rA   )
r-   r.   rY   targets_target_idxtargettarget_armiestarget_fortresseseffective_defense	advantages
             r/   rU   rU      s   zt$0#D1G&v}--V5G5G**??A"..99)=.	>Z^^11233>}}% !5!5 677?}}$ !5!5 677!  '" r1   c                    US:  aJ  [         R                  U ;   a6  [        R                  " 5       S:  a  S[        R                  R
                   3$ g)z!Build armies when low on defense.rm   ffffff?rK   N)r   ARMYrA   r   rk   rN   )r[   rY   s     r/   rV   rV      sA    zl'':5==?S Z44::;<<r1   c                ~    U (       a6  [         R                   " 5       S:  a  S[        R                  R                   3$ g)z,Chance to build other affordable structures.皙?rK   N)rA   r   rk   rN   )r[   s    r/   rW   rW      s/    fmmo+0066788r1   c                J    U S:  a  S[         R                  R                   3$ g)z6Fallback tax collection when at least one city exists.r   rK   Nrn   )rO   s    r/   rX   rX      s&    {22889::r1   c                    g)z7Bot decides which card to discard when over hand limit.N r,   s     r/   rF   rF      s     r1   c                (    UR                   (       d  gg)z.Bot decision making during fair/trading phase.stop_tradingN)has_stopped_tradingr,   s     r/   r+   r+      s     %%r1   c                   UR                   (       d  g[        X5      nU(       d  gUR                   R                  nUR                   R                  5       nUR                   R                  nUS:  a$  [
        R                  U;   a  [
        R                  $ US:  a$  [
        R                  U;   a  [
        R                  $ US:X  a$  [
        R                  U;   a  [
        R                  $ UR                   R                  nXFS-   :  a$  [
        R                  U;   a  [
        R                  $ [
        R                  U;   a;  [        X5      nU(       a)  [        R                  " 5       S:  a  [
        R                  $ [
        R                  U;   a  [
        R                  $ [
        R                  U;   a  [
        R                  $ U(       a  [        R                  " U5      $ S$ )zBot selects what to build.Nrm   rq   r   r   g333333?)rL   r   rO   rP   ru   r   rj   r   FORTRESSgeneralsGENERALROADr   rA   choice)r-   r.   r[   rO   rY   ru   r   rw   s           r/   bot_select_constructionr      st   )$7J&&F446F##..J {|((J6    zl'':5    Q<00J>$$$ !!**H1!5!5!C### J&"40v}},$$$ J&   J&    )36==$<<r1   c                   SSK Jn  [        R                  U ;   a  UR                  S:  a  UR                  S:  a  [        R                  $ UR                  S:  a)  [
        R
                  " 5       S:  a  [        R                  $ [
        R
                  " 5       S:  a  [        R                  $ [        R                  U ;   as  UR                  S:  d  UR                  S:  a*  [
        R
                  " 5       S:  a  [        R                  $ O)[
        R
                  " 5       S	:  a  [        R                  $ [        R                  U ;   a)  [
        R
                  " 5       S
:  a  [        R                  $ U (       a  U S   $ [        R                  $ )z;Select the best war goal based on strategic considerations.r   )
TribeStaterq   rg   rm   ri   rs   r   r   g333333?r   )	stater   r   DESTRUCTIONrQ   rA   CONQUESTrO   PLUNDER)available_goalstarget_state	our_stater   s       r/   _select_war_goalr   .  s&    " o-,2P2PTU2U))Q.&&&++q0V]]_s5J&&&]]_s"&&& ?*!#y'7'71'<}}$''' % ]]_s"### /)==?S ??" "1?1Fg6F6FFr1   c                   UR                   (       d  g[        X5      nU(       d  gUR                   R                  5       nSnSn[        R                  nU GH  u  px[        US5      (       a  UR                   (       d  M*  UR                   R                  5       n	UR                   R                  n
X-   nX;S-
  :  a  Mh  [        XU5      nU(       d  M}  X;-
  n[        XR                   UR                   5      nU[        R                  :X  a   UR                   R                  S:  a  US-  nO3U[        R                  :X  a  UR                   R                  S:  a  US-  nX:  d  GM  UnUnUnGM     Ub  XF4$ g)	zFBot selects war target and goal. Returns (target_index, goal) or None.NrL   rq   rg   
   rm      )rL   r   rP   r   r   rt   ru   r   r   rO   r   rQ   )r-   r.   rw   
our_armiesbest_target
best_score	best_goal
target_idxry   rz   r{   r|   goalsscoreselected_goals                  r/   bot_select_war_targetr   T  si    #D1G##88:J KJ  I%
v}--V5G5G**??A"..99 *=A-- $D&9 . )0B0BFDVDVW G,,,1C1C1J1Ja1ORKEg111f6H6H6Z6Z^_6_QJEJ$K%IC &F ''r1   c                   UR                   (       d  gUR                   R                  5       nUR                   R                  5       n[        S UR                   5       5      nU(       a  US:  a  US-
  nOUnUnUnSn	OUnUnSnUn	XgX4$ )ztBot selects armies, generals, heroes for battle.

Returns (armies, generals, heroes_as_armies, heroes_as_generals).
)r   r   r   r   c              3     #    U  HG  nUR                   [        R                  :X  d  M#  UR                  [        R
                  :X  d  MC  S v   MI     g7f)r   N)	card_typer   EVENTsubtyper	   HERO).0cards     r/   	<genexpr>$bot_select_armies.<locals>.<genexpr>  s>      D>>X^^+ 	
04	0N 	
s   "AA	Arm   r   r   )rL   rP   get_available_generalssumhand)
r-   r.   is_attackingavailable_armiesavailable_generals
hero_cardsrY   r   heroes_as_armiesheroes_as_generalss
             r/   bot_select_armiesr     s     ))>>@++BBD  KK J  q %)F%F%% "%'.CCr1   c                   UR                   (       d  gUR                   R                  5       nUR                   R                  nUR                   R                  nU R                  nUS:X  a  gUR
                  [        R                  :X  a  US::  a  gUR
                  [        R                  :X  a  US:  a  gUR                  5       UR                  5       -   nX!R                   R                  -   nXgS-  :  a  gg)z7Bot decides whether to use Olympic Games to cancel war.Fr   Tr   rg   rq   )rL   rP   rO   rQ   r:   goalr   r   r   get_attacker_total_armiesget_attacker_total_generalsru   )r-   r.   r   
our_citiesour_monumentrI   attacker_strengthdefender_strengths           r/   bot_should_use_olympicsr     s     ##88:J##**J%%77L
..C Q xx7###
a xx7&&&<1+< 557#:Y:Y:[["%7%7%B%BBq00r1   c                "   U(       d  gUR                   (       d  gU R                  nU R                  5       nUR                  U5      nXSR                  :X  a  UR                  5       S:  a  gXSR                  :X  a  UR                  5       S::  a  gg)z-Bot decides whether to use Fortune to reroll.Fr   Tr   )rL   r:   r8   r9   r;   r   r=   get_defender_total_armies)r-   r.   	lost_rollrI   rG   rH   s         r/   bot_should_use_fortuner     s     
..C,,.N!''/L )))((*Q. )))((*a/r1   c                   UR                   (       d  gSnU R                  [        R                  :X  a-  UR                   R	                  5       nU R
                  U:X  a  gSnU R                  [        R                  :X  a@  U R
                  nSn[        R                  " 5        H  u  pgXG;   d  M  US-  nM     SUS-  -
  nU R                  [        R                  :X  a  U R
                  [        R                  :X  a  SnU$ U R
                  [        R                  :X  a  SnU$ U R
                  [        R                  :X  a'  UR                   R                  5       S:  a  SnU$ S	n U$ S
nU$ )zCScore a card for discard decision. Higher = more likely to discard.2   r      r   <   r      rq   (   F   )rL   r   r   SPECIALget_special_resourcer   RESOURCEr   itemsr   r	   r   FORTUNEOLYMPICSrP   )r   r.   r   own_specialresourceneeded_countbuildingcostss           r/   score_card_for_discardr     sH   E ~~)))((==?<<;& E ~~***<<-335OH !  6
 lR'( ~~'<<9>>)E L \\Y...E L \\Y///!!6681< L 
 L ELr1   c                    UR                   (       d  gSnSn[        UR                   5       H  u  pE[        XQ5      nXc:  d  M  UnUnM     U$ )z6Bot selects which card to discard. Returns card index.r   rr   )r   	enumerater   )r-   r.   
best_indexr   ir   r   s          r/   bot_select_card_to_discardr   )  sO    ;; JJV[[)&t4JJ	 * r1   c                    SSK Jn  UR                  (       a  gUR                  (       d  [	        X5        SUl        g[        U 5      nU(       a  SUl        gU=R                  S-  sl        UR                  U:  a  U R                  US5        gg)z/Bot performs trading actions during fair phase.r   )TRADING_TIMEOUT_TICKSNTr   r   )r-   r   r   has_made_offersbot_make_trade_offersr   trading_ticks_waited_action_stop_trading)r-   r.   r   trades_mades       r/   bot_do_tradingr   A  s     ,!! !!d+!% +40K &'# 1$ ""&;;!!&.9 <r1   c           	     >   UR                   (       d  g[        R                  " UR                   R                  5      n[	        UR
                  5       H  u  p4UR                  [        R                  :X  a  UR                  U:X  a  M5  UR                  [        R                  :X  a.  [        U UU[        R                  US9nU(       a  [        XXB5        UR                  5       (       d  M  [        U UU[        R                  US9nU(       d  M  [        XXB5        M     g)z+Bot makes trade offers for cards they want.N)wanted_typewanted_subtype)rL   r   gettriber   r   r   r   r   r   r   r   is_disaster)r-   r.   wanted_specialr   r   offers         r/   r   r   _  s     ,//0B0B0H0HIN V[[)>>X---||~- >>X--- $,,-E tTB  $,,-E utTB= *r1   c                   UR                   (       d  U R                  U5        g [        X5      nU(       d  U R                  U5        g[        X5      nU(       d  U R                  U5        g[	        XUSS9nU(       d#  UR                   (       a  U R                  U5        gU R
                  [        R                  :X  a  gM  )z?Bot performs construction - can build multiple things per turn.NT)	auto_road)rL   _end_actionr   r   r   r6   r   ROAD_PERMISSION)r-   r.   r[   building_typesuccesss        r/   bot_perform_constructionr     s      -d;
V$ 0=V$ 't]dS!!  ( >>\9993 r1   c                   SSK Jn  UR                  (       d  U R                  U5        g[	        X5      nU(       d  U R                  U5        gUu  pE[        XXE5      (       d  U R                  U5        gU R                  5       nXd   n[        Xr5      (       d,  U R                  R                  5         U R                  U5        g[        XSS9u  pp[        XXX5        UR                  (       a  [        XSS9u  pp[        XXX5        OSUR                  (       aB  UR                  R                  5       nUR                  R                  5       n[        XXSS5        [        U 5        g)z(Bot performs war declaration and combat.r   )r    NT)r   Fr   )r-   r    rL   r   r   r   r8   
isinstancer:   resetr   r   is_botrP   r   r   )r-   r.   	AOHPlayerresulttarget_indexr   rG   defender
att_armiesatt_generals
att_heroesatt_hero_generals
def_armiesdef_generals
def_heroesdef_hero_generalss                   r/   bot_perform_warr    sI    5  #40F L t\88  ,,.N+Hh**  ?P4?;Jj 4:Y BSC
?
* 	tz_ !--BBDJ#//FFHL4:QJ
 tr1   c                    SSK Jn  [        UR                  5      U:  a8  SnUR                  R	                  U5        [        UR                  5      U:  a  M8  SUl        U R                  5         g)z>Bot executes discarding excess cards (orchestration function).r   )MAX_HAND_SIZEr   N)r-   r	  lenr   poppending_discard	_end_turn)r-   r.   r	  worst_indexs       r/   bot_execute_discard_excessr    sR    #
fkk
]
*$ fkk
]
* FNNr1   c                `   UR                   (       d  g/ n/ n[        UR                  5       H  u  pEUR                  [        R
                  :X  d  M%  UR                  [        R                  :X  a  UR                  U5        MV  UR                  [        R                  :X  d  Mv  UR                  U5        M     U(       d  U(       d  gU R                  5       n/ n[        U5       HD  u  pHX:w  d  M  [        US5      (       d  M  UR                   (       d  M2  UR                  XH45        MF     U(       d  gSn	Sn
U H2  nU H)  u  p[        UR                   U5      nX:  d  M"  Un	SU 3n
M+     M4     U H2  nU H)  u  p[        UR                   U5      nX:  d  M"  Un	SU 3n
M+     M4     U	S:  a  U
$ g)zDCheck if bot should play a disaster card. Returns action_id or None.NrL   r   play_earthquake_play_eruption_)rL   r   r   r   r   r   r   r	   
EARTHQUAKEappendERUPTIONr8   rt   _score_earthquake_target_score_eruption_target)r-   r.   earthquake_indiceseruption_indicesr   r   rG   rw   pr   best_action
card_indexr   ry   r   s                  r/   re   re     s    V[[)>>X^^+||y333"))!,!3!33 ''* * &6 ,,.NG.);71m44NNA6" *  JK )
")J,V-?-?HE!"
 0=	 #* ) '
")J*6+=+=vFE!"
 .zl;	 #* ' Br1   c                F   UR                   (       a  U (       d  gUR                   R                  5       nUS:X  a  gSnX2S-  -  nUR                   R                  S:  a  US-  nUR                   R                  S:  a  US-  nUR                   R                  S::  a  US-  nU$ )z6Score a target for Earthquake. Higher = better target.r   r   r   rg   r   r   r   )rL   rP   rO   rQ   )r   ry   rz   r   s       r/   r  r  )  s    Y&&;;=M E 
RE   A%++q0   A%Lr1   c                    UR                   (       a  U (       d  gUR                   R                  nUS:X  a  gSnUS:  a  US-  nOUS:  a  US-  nUS:  a  US-  nUS	-  nU$ )
z4Score a target for Eruption. Higher = better target.r   r   rg   d   rm   r   rq   r   r   )rL   rO   )r   ry   target_citiesr   s       r/   r  r  F  sz    Y&&--M E 	!	  
RKELr1   c                   SSK JnJn  UR                  (       d  U R	                  U5        g[        UR                  U5      nUc  U R	                  U5        g[        X5      nU(       d  U R	                  U5        g[        X!R                  U5      u  pxU(       a  US::  a  U R	                  U5        gUR                  R                  U5      n	U R                  R                  U	5        U[        R                  :X  a
  U" XU5        OU[        R                  :X  a	  U" XU5        [        R                   U l        U R%                  5         g)z}Bot plays a disaster card against the best target.

Called after bot selects a disaster action via bot_should_play_disaster.
r   )apply_earthquake_effectapply_eruption_effectNr   )eventsr#  r$  rL   r   _find_disaster_card_indexr   _get_disaster_targets_select_best_disaster_targetr  discard_piler  r	   r  r  r   rC   r6   rebuild_all_menus)
r-   r.   disaster_typer#  r$  r  rw   r   r   r   s
             r/   bot_play_disaster_on_targetr,  c  s    G *6;;FJ #D1G :))7K */  ;;??:&DT"	,,,k:	),,	,dK8 "//DNr1   c                    [        U 5       H9  u  p#UR                  [        R                  :X  d  M%  UR                  U:X  d  M7  Us  $    g)z0Locate the first matching disaster card in hand.N)r   r   r   r   r   )r   r+  r   r   s       r/   r&  r&    s7    T?>>X^^+0MH # r1   c                    U R                  5       n[        U5       VVs/ s H5  u  p4XA:w  d  M  [        US5      (       d  M  UR                  (       d  M2  X44PM7     snn$ s  snnf )zCollect valid disaster targets.rL   )r8   r   rt   rL   )r-   r.   rG   r   r  s        r/   r'  r'    sd     ,,.N n--DA; 	"1m4 	9: 	-  s   AAAAc                    SnSnU [         R                  :X  a  [        nOU [         R                  :X  a  [        nOSU4$ U H  u  pgU" X5      nX:  d  M  UnUnM     X44$ )z<Pick the highest scoring target for the given disaster type.Nr   )r	   r  r  r  r  )	r+  r   rw   r   r   score_fnrx   ry   r   s	            r/   r(  r(    sq     KJ	,,,+	),,	,)Z&+J K	  ' ""r1   c                    [        X5      $ )z9Bot selects a main action. Wrapper for bot_select_action.)rD   r,   s     r/   bot_do_select_actionr2    s    T**r1   )r-   r   r.   r    return
str | None)r-   r   r.   r    r3  str)r[   list[BuildingType]rO   intr3  r4  )rZ   r7  r3  r4  )r-   r   r.   r    rY   r7  r3  r4  )r[   r6  rY   r7  r3  r4  )r[   r6  r3  r4  )rO   r7  r3  r4  )r   z	list[str]r   'TribeState'r   r8  r3  r5  )r-   r   r.   r    r3  ztuple[int, str] | None)r-   r   r.   r    r   boolr3  ztuple[int, int, int, int])r-   r   r.   r    r3  r9  )r-   r   r.   r    r   r9  r3  r9  )r   r   r.   r    r3  r7  )r-   r   r.   r    r3  r7  )r-   r   r.   r    r3  None)r   z'TribeState' | Nonery   r    r3  r7  )r-   r   r.   r    r+  r5  r3  r:  )r   z
list[Card]r+  r5  r3  z
int | None)r-   r   r.   r    r3  #list[tuple[int, AgeOfHeroesPlayer]])r+  r5  r   r8  rw   r;  r3  z$tuple[AgeOfHeroesPlayer | None, int])H__doc__
__future__r   rA   typingr   cardsr   r   r   r	   r   r
   r   r   r   r   r   r   constructionr   r   r   r   combatr   r   r   r   r   r   r   tradingr   r   r   r-   r   r    r0   r)   rD   rR   rS   rT   rU   rV   rW   rX   rF   r+   r   r   r   r   r   r   r   r   r   r   r   r  r  re   r  r  r,  r&  r'  r(  r2  r   r1   r/   <module>rC     s    "    : :      L K8.'T43n284=n#G#G#G #G 		#GL8
8#488v'D
'D#4'DDH'D'DT!H
#4AE	:,^0:<'CT J4n	5p::)
)#4)EH)	)X	
	#4	(	### 1# *	#2+r1   