
    c-Oj)                       d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	 ddl
mZ ddlmZ  ee      j                         j                   d   Zedz  Zed	z  Zed
z  Zedz  dz  Zedz  ZddgZdZdZdZdYdZdZdZd[dZd\dZd]dZd^dZ ddd_dZ!ddddddd d!ddd"d#d$ddd%d&d'ddd(d)d*ddd+ed,ddd-d.d/ddd0d1d2ddgZ"d3d4g d5dd6d7d8g d9dd6d:d;g d<dd6d=d>g d?dd6d@dAg dBdd6d-dCg dDdd6gZ#d`dEZ$dadFZ%dbdGZ&dcdHZ'dddIZ(dJdKdedLZ)dfdMZ*dgdNZ+dhdOZ,dhdPZ-d]dQZ.didRZ/djdSZ0dkdTZ1dkdUZ2dkdVZ3dldWZ4e5dXk(  r e4        yy)mzKSummarize battle end, EXP/gold reward, and inactive item-drop handler flow.    )annotationsN)Counter)Path)Any   zHwanse2.exeweboutcapturesruntimezenemy_stat_table.jsonzlevelup-rewardzbattle-exp-level-goldivE i?B c                V    t        j                  t        | 
|             S d            S )N )htmlescapestrvalues    'tools/build_battle_end_reward_review.pyescr      s&    ;;sE$55>??2>??    c                    d| dS )N0x08x r   s    r   hxr       s    c{r   c            
     B   t         j                         } t        j                  d| d      d   }t        j                  d| |dz         d   }t        j                  d| |dz         d   }t        j                  d| |dz   dz         d   }|dz   |z   }g }t	        |      D ]~  }||d	z  z   }| ||d
z    j                  d      d   j                  d      }	t        j                  d| |d
z         \  }
}}}|j                  |	||z   ||z   t        |
|      z   |||
d        |S )N<I<   r   z<H            (          replaceerrorsz<IIII)namevaStartvaEnd	rawOffsetrawSizevirtualSize)	EXE
read_bytesstructunpack_fromrangesplitdecodeappendmax)data	pe_offsetsection_countoptional_header_size
image_basesection_offsetsectionsindexoffsetr(   virtual_sizevirtual_addressraw_size
raw_offsets                 r   pe_sectionsrD   $   sN   >>D""4t4Q7I&&tT9q=A!DM!--dD)b.I!L##D$	B0CDQGJ^&::NH}% 
%"*,FVaZ(..u5a8??y?Q>D>P>PQXZ^`fij`j>k;ox%7#o5L(8SS'#+		
	
 Or   c                    t               D ]:  }|d   | cxk  r	|d   k  sn |t        |d         | t        |d         z
  z   fc S  t        dt        |              )Nr)   r*   r+   zVA outside PE sections: )rD   int
ValueErrorr   )vasections     r   va_to_file_offsetrJ   =   sn    = W96gg&66C 45c')BT>U9UVVVW /2x8
99r   c                    t         j                         } t        t              \  }}t	        j
                  d| |      d   }| j                  t	        j                  dt                    }| j                  t	        j                  dt        dz               }t        t        t              |d   |t        |      |t        ||| ||dz    j                  d      ddS )	Nr   r      r(       zdglobal money dword; PE initial value is 200 and reward/shop handlers read or mutate the same address)address
addressHexrI   
fileOffsetfileOffsetHexinitialDwordcapdirectAddressRefCountplusTwoAddressRefCountinitialBytes
conclusion)r.   r/   rJ   GLOBAL_MONEY_VAr0   r1   countpackr   GLOBAL_MONEY_CAPhex)r7   rI   file_offsetinitial_valuedirect_ref_countplus_two_ref_counts         r   build_money_global_evidencerb   D   s    >>D,_=G[&&tT;?BMzz&++dO"DEFKKo6I$JK")6?!K%!1"4[;+;<@@E| r   c                `   	 t        j                  dddddd| dd|dt        t              gd	t         j                  t         j                  d	
      }dj                  d |j                  j                         D              S # t
        t         j                  f$ r}d| cY d }~S d }~ww xY w)Nobjdumpz-Mintelz-Dz-bzpei-i386z--start-address=0xr   z--stop-address=0xT)checkstdoutstderrtextz; disassembly unavailable: 
c              3  N   K   | ]  }t        j                  d |      s|  yw)z\s*[0-9a-f]{6,8}:N)rematch).0lines     r   	<genexpr>zdisassemble.<locals>.<genexpr>m   s     idBHHMacgDhTis   %%)

subprocessrunr   r.   PIPEOSErrorCalledProcessErrorjoinrf   
splitlines)start_vastop_varesultexcs       r   disassembler{   Y   s    3$XcN3#GC=1C	 ????
$ 99ifmm&>&>&@iii Z223 3,SE223s   AB B-B("B-(B-contextc          	       	 | j                         }g }t        |      D ]g  \  }	t        	fd|D              st        t	        d||z
        t        t        |      ||z   dz               D ]  }|j                  |||   f        i t               }g }|D ]:  \  }	||vs|j                  |       |j                  	j                                < |S )Nc              3  &   K   | ]  }|v  
 y wNr   )rm   needlern   s     r   ro   z compact_lines.<locals>.<genexpr>t   s     4&v~4s   r   r   )rv   	enumerateanyr2   r6   minlenr5   setaddrstrip)
disasmneedlesr}   linespickedr>   iseenry   rn   s
            @r   compact_linesr   p   s    E$&F ' -t4G443q%'/2CE
EGOVWDW4XY -q%(m,-- UDF )tHHUOMM$++-() Mr   zdefeated actor scan@ u   target range helper(0x433c99/0x433dc0)로 전투 대상 범위를 돌며 actor kind +0x04가 enemy 쪽이고 +0x62 bit 0x40(KO)인 actor만 보상 대상으로 처리한다.u   확정)steprH   meaningstatuszEXP fan-out to live playersi(@ u   player actor slot 0x4576e8 범위를 돌며 +0x62 bit 0x80 비활성/기절 제외 actor에게만 0x42157b 경험치 공식을 적용하고 player +0x14 current EXP에 더한다.zEXP formula{B u   player/enemy actor +0x06 level 차이를 최대 10으로 clamp하고, enemy actor +0x14 EXP base를 기준으로 보정한다. enemy actor +0x14는 enemy stat row actual +0x0c byte(HP-view +0x04)에서 온다.zequipment EXP penaltyiB u   player/equipment class table 0x457798 == 0x1d 또는 0x457799 in (0x0b, 0x17)이면 EXP를 절반으로 줄이되 1 미만으로 내리지 않는다.zgold reward6WC u`   defeated actor +0x44 gold를 global money 0x4576e0에 더하고 0x000f423f(999999)로 cap한다.zglobal money initial valueu   PE .data의 0x004576e0 dword 초기값이 200이다. 새 프로세스/처음부터 시작의 기본 소지금 200과 일치하며, 같은 주소가 보상/상점 흐름에서 읽고 쓰인다.zlevel-up carryoverB u   레벨업 처리 후 player actor +0x14 current EXP는 (current EXP - required EXP actor +0x16) * 3 / 4로 축소되어 다음 레벨 잔여 경험치가 된다.zitem-drop handleri|WC u   defeated actor +0x40 item id가 0이 아니면 +0x41 확률/분모로 random(0x427730)을 돌린다. 성공 시 0x422093(item id, 1)로 소지품을 1개 추가한다.zbattle KO reward loop)r   i@ )	0x433c990x433dc0z	[eax+0x4]ztest   cl,0x400x4576e8ztest   cl,0x80zcall   0x42157bz
[ecx+0x14]zcall   0x435736)labelr2   r   r}   zEXP reward formula)r   r   )z	[eax+0x6]
[eax+0x14]cmp    eax,0xazmov    WORD PTR [ebp-0x4],0x10x4577980x457799zshr    eax,0x1zgold reward / item-drop handler)r   iWC )z
[eax+0x44]0x4576e00xf423fz
[eax+0x40]z
[eax+0x41]zcall   0x427730zcall   0x422093zmoney add/subtract VM helper)i8j@ ij@ )r   r   zadd    DWORD PTR ds:0x4576e0sub    DWORD PTR ds:0x4576e0zshop/inventory money checks)i@ iO A )r   z
div    ebxr   r   )r   i3B )zinc    WORD PTR [eax+0x6]r   z
[eax+0x16]zlea    eax,[ecx+ecx*2]zsar    eax,0x2zmov    WORD PTR [ecx+0x14],axzmov    al,0x1c                
   t         j                         syt        t         j                  d|  d      d       }|sy|d   }|j	                  |j
                  j                  dd            }||j                         r|fS d fS )N)NNz*-z.manifest.jsonc                6    | j                         j                  S r   )statst_mtime)paths    r   <lambda>z&latest_runtime_trace.<locals>.<lambda>  s    VZV_V_VaVjVj r   )keyz.jsonl)CAPTURESexistssortedglob	with_namer(   r%   )trace_id	manifestsmanifestjsonls       r   latest_runtime_tracer     sz    ??x}}r(>%BCIjkI}Hx}}445ExPQEellnU66$66r   c                   t        | t              sy| j                         }|syt        j                  d|      D cg c]  }|s|	 }}	 t        d |D              j                  dd      S c c}w # t        $ r Y yw xY w)Nr   z[\s,]+c              3  4   K   | ]  }t        |d         yw)rM   N)rF   )rm   parts     r   ro   zdecode_cp949.<locals>.<genexpr>  s     5tSr]5s   cp949r%   r&   )
isinstancer   striprk   r3   bytesr4   rG   )rawrh   r   partss       r   decode_cp949r     s    c399;D hhy$7@d4T@E@5u55<<WY<WW A  s   A/A/"A4 4	B ?B c                   t        |       \  }}|r|sy t        j                  |j                  d            }t	               }g }t	               }t	               }g }g }	g }
g }ddddddd	d
d}dddddddddd	}dddddddd}|j                  ddd      5 }|D ]  }	 t        j                  |      }t        |j                  dd            }||xx   dz  cc<   |dk(  r|j                  |j                  d      |j                  d      |j                  d      xs |j                  d       t        |j                  d!      xs$ |j                  d"      xs |j                  d#            xs |j                  d$      d%       |d&v r|j                  d'      xs$ |j                  d(      xs |j                  d)      }t        |t              s<|j                  d*      s$|j                  d+      s|j                  d,      sq||xx   dz  cc<   |d-k(  rS|j                  d.      xs$ |j                  d/      xs |j                  d0      }|s|t        |      xx   dz  cc<   |d1k(  rt        |j                  d0            }t        |j                  d            }||v s||v s|j                  d2      xs i }|
j                  |j                  d      |||j                  |d      |j                  d3      |j                  d4      |j                  d5      |j                  d6      |j                  d7      |j                  d8      d9
       |
d:d  }
|d;k(  rt        |j                  d            }t        |j                  d<            }||v s||v s|j                  |j                  d      |||j                  d=      |j                  |      xs |j                  |d      |j                  d>      d?       |d@d  }~|dAk(  r|j                  |j                  d      |j                  d      |j                  dB      xs |j                  dC      |j                  dD      |j                  d!      xs |j                  d"      dE       |dFd  }|dGk(  s|	j                  |j                  d      |j                  dH      xs |j                  dI      |j                  d3      xs |j                  dJ      |j                  d4      xs |j                  dK      dL       |	dFd  }	 	 d d d        t        dM |
D              }t        dN |
D              }t        dO |
D              }i dP|j                  dQ      dR|j                  dS|j                  dT|j                  dTg       dU|j                  dV      |j                  dW      gdX|j                  dY      dZ|j                  d[      d\|dFd  d]|j                  d^      d_|j                  d[      d`|
da|db|dc|	dd|xs |xs |de|df||| t         k(  rdgdiS dhdiS # t        j                  $ r Y w xY w# 1 sw Y   <xY w)jNutf-8encodingzplayer levelzplayer max HP candidatezplayer current HP candidatezplayer max MP candidatezplayer current MP candidatezanimated/display HP candidatez)animated/display level-or-state candidatezanimated/display EXP candidate)
0x00457756
0x0045775A
0x0045775C
0x00457760
0x00457762
0x004870B2
0x004870B4
0x004870CAzanimated value incrementzanimated value wrap/resetzlevel-up threshold/applicationz
rng helperzrng return/write observer)	
0x00420674
0x00420678
0x00420685
0x00420690
0x00420693
0x00420695
0x00421802
0x00427730
0x00427763zlevel-up HP increase RNG callzlevel-up stat RNG callz level-up luck/random branch call)
0x004217DF
0x0042183B
0x00421897
0x004218DF
0x00421927
0x0042196F
0x004219ADrr%   )r   r'   eventr   r   z	text-drawframepcrectboundsr   rawBytesr   rh   )r   r   r   rh   >   draw-surfacesurface-uploadsurface-snapshotresource-surface-labelsourceResourceNamesourceLabelresourceNamez.cnsz.midz.wlkz	mem-write	watchNamer(   rO   zprobe-value-changestateoldnewcpu_eipcpu_eaxcpu_ecxcpu_edx)
r   r   rO   r   r   r   cpuEipcpuEaxcpuEcxcpuEdxizfunction-entercallerfunctionargs)r   r   r   r   r   r   izvm-opstreamstreamVaopcode)r   r   r   r   r   izobject-stream-changeobjectobjectVa	oldStream	newStream)r   r   r   r   c              3     K   | ]D  }|j                  d       dk(  xr* |j                  d      dk(  xr |j                  d      dk(   F yw)rO   r   r   1r   2Ngetrm   rows     r   ro   z*summarize_runtime_trace.<locals>.<genexpr>  sO      " 		l*^swwu~/D^QV[^I^^"   A
Ac              3     K   | ]D  }|j                  d       dk(  xr* |j                  d      dk(  xr |j                  d      dk(   F yw)rO   r   r   100r   0Nr  r  s     r   ro   z*summarize_runtime_trace.<locals>.<genexpr>  sO        		l*`swwu~/F`377SX>]`K`` r  c              3  B   K   | ]  }|j                  d       dv   yw)rO   >   r   r   Nr  r  s     r   ro   z*summarize_runtime_trace.<locals>.<genexpr>  s      qVYswwy15QQqs   traceIdr   r   r   profiles
frameRangestart_frame	end_frame
eventCountevent_counttopEventCounts   	textDrawsresourceLabelsr   memWriteNamesprobeValueChangesstaticAddressHitsvmTailobjectStreamTaildirectRewardWriteCaptureddirectLevelupCapturedexpRolloverCapturedu   levelup-reward 캡처는 value-probe로 EXP 88→100→0→7, level 1→2, HP/MP 성장 후보를 정적 level-up 주소 근처에서 직접 잡는다.uQ   이전 캡처는 보상 관찰치와 전투 종료 경계 bridge로 유지한다.)statGrowthCapturednote)r   jsonloads	read_textr   openJSONDecodeErrorr   r  r5   r   r   endswithr   r(   most_commonPRIMARY_RUNTIME_TRACE_ID)r   manifest_path
jsonl_pathr   counts
text_drawsresource_labelsmem_write_namesvm_tailobject_tailprobe_value_changesstatic_address_hitsimportant_probe_addressesimportant_pcsimportant_callershandlern   r   kindr(   rO   r   r   r   direct_levelup_capturedexp_rollover_capturedstat_growth_captureds                              r   summarize_runtime_tracer9     s     4X >M:
zz-1171CDH"9F')J$+IO$+IO$&G(*K0202$/3/35A6	! 1100006"1
M 6.....8 
wy	A P0V O	0D

4( uyy"-.D4LAL{"!!!&7!3#iio %		& 1 HUYYx5H ,UYYu-=-l:AV-lZ_ZcZcdkZl m  !Cqvqzqz  |B  rC	 iiyy!56o%))M:RoV[V_V_`nVodC(dmmF.Ct}}U[G\`d`m`mnt`u#D)Q.)$yy-Z61BZeiiPYFZ#CI.!3.--eii	234)772;N!IIg.4"E'..%*YYw%7"$'.%>%B%B7B%O#(99U#3#(99U#3&+ii	&:&+ii	&:&+ii	&:&+ii	&: +>cd*C'))4)UYYx01&&4E*E'..%*YYw%7"$&,(-		*(='8'<'<V'D'aHYHYZ\^`Ha$)IIf$5	 +>cd*C'!&7!3#iio"'))H"5"N:9N"'))H"5$yy/H599Z3H "#$-//""!&7!3"'))H"5"N:9N$yy/I599[3I$yy/I599[3I	 *#$/_O	0P0b " "&"     &   q]pqq8<<
+M&& 	 	HLLR0	
 	x||M2HLL4MN 	hll=1 	&,,R0 	Z% 	/55b9 	44R8 	0 	0 	' 	K 	$%<%m@U%mYm  	 !8!" 	4#$ 3 33 f) , e- m '' 	P0 P0sZ   Z Y&3D Z 3Z 
AZ AZ 4C2Z (DZ 2BZ &Y=9Z <Y==Z  Z
c                    | j                  dg       D ].  }|j                  d      |k(  s|j                  d      |k(  s,|c S  y )Nrows	cleanNamer(   r  )
enemy_stat
clean_namer  s      r   find_enemy_rowr?    sF    ~~fb) 77;:-J1NJ r   c                    | j                  dg       D ]3  }|j                  d      |k(  st        |j                  dd            c S  y )Nwords	offsetHexr   r   )r  rF   )r  r?   words      r   word_by_offsetrD    sH    $ -88K F*txx+,,- r   F)penaltyc                  t        t        t        |      t        |      z
        d      }t        |       }t        |      t        |      k  rt        |||z  dz  dz  z
  d      }n|||z  |z  dz  z   }|rt        |dz  d      }|S )N
   r  d   r   rL   )r   absrF   r6   )exp_baseplayer_levelenemy_levelrE  diffbaserewards          r   
exp_rewardrP    s    s3{#c,&778"=Dx=D
;#l++TTD[2-#55q9t+33Vq[!$Mr   c                    t        | d      S )N+0x04)rD  )r  s    r   exp_base_for_rowrS    s    #w''r   c                   g }| j                  dg       D ]  }t        |      }|t        |d      }|j                  d      }|j                  |j                  d      |j                  d      xs |j                  d      ||j                  d      |j                  d      |||j                  d	      xs d
d        |S )Nr;  z+0x36levelr>   r<  r(   hpgoldlevelSourcezEactual stat row +0x06 / exported HP-view -0x02; copied to actor +0x06)r>   r(   expBaserV  rW  
enemyLevelnextRowLevelAtHpView36rX  )r  rS  rD  r5   )r=  optionsr  rJ  next_row_levelrU  s         r   monster_exp_optionsr^    s    G~~fb) 
#C('W5 ),?#ggdm#*8"ww}5   A  :A		

$ Nr   c                ^   t        | d      }|rt        |      nd}|rt        |j                  dd            nd}g }t	        dd      D ]H  }t        t        |xs d      ||      }|j                  ||t        t        ||z
        d      |d       J d	g d
g dd||d|dt        |       dS )N	   원숭이   rU  rL   r      rG  )playerLevelrZ  rM  rO  u   EXP 획득 계산)u   몬스터별 EXP 기준값은 enemy stat HP-view +0x04에서 온다. 전투 actor에서는 enemy actor +0x14로 materialize된다.u   몬스터 레벨은 실제 stat row prefix +0x06에서 온다. HP-view export 기준으로는 -0x02이며, 전투 actor +0x06으로 복사되어 EXP 공식이 소비한다.ut   HP-view +0x36은 다음 실제 row prefix +0x06을 읽는 값이므로 한 행 밀린 다음 몬스터 레벨이다.u1   레벨 차이는 최대 10까지만 반영된다.uf   몬스터 레벨이 주인공보다 낮으면 EXP가 감소하고, 그래도 최소 1은 획득한다.uM   몬스터 레벨이 같거나 높으면 EXP 기준값 이상을 획득한다.um   일부 장비/상태 페널티가 걸리면 계산된 EXP를 절반으로 줄이되 최소 1은 유지한다.)u-   enemyExpBase = 몬스터 EXP 기준값(+0x04)z-diff = min(abs(enemyLevel - playerLevel), 10)uU   enemyLevel < playerLevel 이면 max(enemyExpBase - enemyExpBase * diff * 12 / 100, 1)uP   enemyLevel >= playerLevel 이면 enemyExpBase + enemyExpBase * diff * diff / 100u;   EXP 페널티 장비/상태가 있으면 max(result / 2, 1)z actual row +0x06 / HP-view -0x02)monsterenemyExpBaserZ  enemyLevelSourcer;  )titleformulaSummarystepsexamplemonsterOptions)
r?  rS  rF   r  r2   rP  r5   r   rI  r^  )r=  monkey
monkey_expmonkey_levelexample_rowsrK  rO  s          r   build_exp_readablerp    s    J4F-3!&)J283vzz'1-.aLLa 	
C
 0b1<N+*C| ;<bA 		
	
 %

 #&& B 
 .j93 r   c                X   t        | d      }|rt        |d      nd }d }|r>|j                  dg       D ])  }|j                  d      dk(  s|j                  d      } n t        D cg c]  }t	        |      x}s| }}t        d |D        d       }||r|d   }d	}t        |xs d      }	d
}
||	z   }t        d||
z
        }di ddd|d|d|ddd|d|
d|d|ddd|dz  dz  ddddddt        |xs d      z   d |d!k(  xr |dk(  xr |dz  dz  d"k(  d#d$||d%S c c}w )&Nr`  rR  confirmedFieldsr   rW  r   c              3  R   K   | ]  }|j                  d       t        k(  s| ! yw)r
  N)r  r&  rm   items     r   ro   z'build_runtime_bridge.<locals>.<genexpr>+  s!     aT)1DH`1`Das   ''r   X   rH  u   levelup-reward runtime: 전투 중 캡처에서 EXP 88→100→0→7, level 1→2, HP 30→35, MP 18→21이 정적 레벨업 구간 근처에서 잡힘. battle-exp-level-gold 관찰치의 gold 212→215도 원숭이 gold 3과 일치.enemyenemyHpViewOffset04ExpBaseenemyActorExpBase	enemyGoldplayerLevelBeforer   playerExpBeforelevelRequirementexpAfterRewardBeforeLevelUpcarryBeforeScalecarryoverFormulaz"(currentExp - requiredExp) * 3 / 4playerExpAfterLevelUp      playerLevelAfterrL   
goldBefore   	goldAftermatchesUserObservationra     statSourceNotezenemy_stat_table rows are HP-view rows. EXE materializer uses actual stat row +0x0c byte for enemy actor +0x14 EXP base; in HP-view export this is +0x04.)observationstaticRewardCheckruntimeTraceruntimeTraces)r?  rD  r  RUNTIME_TRACE_IDSr9  nextrF   r6   )r=  rl  rJ  rW  fieldr   r   runtimesplayer_exp_beforeexp_base_intrequiredbefore_levelupcarry_before_scales                r   build_runtime_bridger  !  s   J4F28~fg.dHDZZ 126 	Eyy!V+yy)	 ):lHI`aiIj>jg>jlHlaXacghG81+x}1%LH&5NQ 9: D
[
((
  
 	

  
 0
 
 *>
  2
  D
 $&81&<%B
 
 #
 s491~-
 %h"n&i&iHZ]^H^cdGdhiGi
    z!
$  !+  ms   'D':D'c                 ,   g } t         D ]W  }|d   \  }}t        ||      }| j                  |d   t        |       dt        |       t	        ||d   |d         d       Y t        j                  t        j                  d	            }g }|j                  d
g       D ]  }|j                  dg       D ci c]-  }|j                  d      t        |j                  dd            / }	}|	j                  dd      }
|
dz  }|
dz	  dz  }|s|so|j                  |j                  d      |j                  d      xs |j                  d      ||d        t               }ddt        t        t              j                  t                     dddddddd|d     d!|d"    d#|d$    d%|d&    d'	d(d)d*d+gt"        D cg c]  }i |d,t        |d-         i c}d.d/d0d1d2|t%        |      |d3t'        |      |sd4nd5d6t)        |      | g d7S c c}w c c}w )8Nr2   r   z..r   r}   r|   )r   r2   keyLinesr   r   r;  rA  rB  r   r   z+0x2a   r#   r>   r<  r(   )r>   r(   itemIddropRater   zhwanse-battle-end-reward-reviewz@battle-end-exp-gold-grounded-item-drop-handler-grounded-inactiveug   전투 종료 보상은 KO된 enemy actor 기준으로 돌아간다. +0x62 bit 0x40이 KO 플래그다.u   경험치는 KO enemy마다 살아있는 player actor에게 fan-out된다. +0x62 bit 0x80 actor는 보상 fan-out에서 제외된다.u  경험치 공식은 enemy actor EXP base(+0x14), player/enemy actor level(+0x06) 차이, 장비/아이템 class penalty를 사용하며 최종 보상은 1 미만으로 떨어지지 않는다. enemy actor +0x14는 enemy stat actual +0x0c byte(HP-view +0x04)에서 materialize된다.u   레벨업 후 잔여 EXP는 단순 carry가 아니라 (current EXP - required EXP) * 3 / 4로 축소된다. battle-exp-level-gold 캡처의 88 + 원숭이 22 = 110, 요구치 100, 잔여 10 → 7과 일치한다.u3  levelup-reward 캡처에서 EXP 표시/후보 주소 0x004870ca가 88→100→0→7로 진행되고, player level 후보 0x00457756이 1→2로 바뀌는 것을 확인했다. 같은 프레임에서 0x004217df/0x0042183b/... → 0x00427730 RNG 호출도 잡혀 레벨업 스탯 증가 루틴과 연결된다.u`   골드는 enemy actor +0x44에서 읽어 global money 0x4576e0에 더하고 999999로 cap한다.u3   global money 0x4576e0은 PE .data 파일 오프셋 rR   u   의 dword이며 초기값은 rS   u    이다. 주소 직접 참조는 rU   u$   개이고 0x4576e2 직접 참조는 rV   u$   개라 32-bit money dword로 본다.u   아이템 드롭 핸들러는 enemy actor +0x40 item id와 +0x41 확률/분모를 사용해 성공 시 0x422093으로 1개 추가한다.u   다만 현재 80개 연속 몬스터 stat row의 source HP-view +0x2a/+0x2b는 모두 0이다. 따라서 일반 몬스터 보상은 EXP/골드만 있고 아이템 드롭은 비활성으로 본다.u   회복/부활은 별도 흐름이다. 리프레시 워터/마수석은 기절 actor의 0x40/0x80을 해제하고, 다른 회복류는 기절하지 않은 actor의 HP/MP 회복으로 분리된다.u   레벨업/스탯 상승/승리 UI 연출은 이 보상 루프 뒤쪽의 별도 화면 흐름이다. 여관/세이브포인트/전투 밖 메뉴 회복도 map/menu event 쪽 흐름이므로 이 보고서 범위에서 제외한다.vaHexrH   a(  enemyExpBase = enemy actor +0x14 = enemy stat actual +0x0c byte(HP-view +0x04). diff = min(abs(enemy.level - player.level), 10). enemy lower: max(enemyExpBase - enemyExpBase*diff*12/100, 1). enemy same/higher: enemyExpBase + enemyExpBase*diff*diff/100. Equipment class penalties halve with min 1.zif currentExp(+0x14) reaches requiredExp(+0x16): level++, then currentExp = (currentExp - requiredExp) * 3 / 4. Example: 88 + 22 = 110, required 100, carry 10*3/4 = 7.zmoney global dword = 0x004576e0. PE initial value = 200. Reward: money = min(999999, money + defeatedActor.gold(+0x44)). Shop/VM helpers also add/subtract this same address.zif itemId(+0x40) != 0 and random(dropRate +0x41) == 0: add itemId x1 via 0x422093. Current continuous enemy stat table sets all itemId/dropRate pairs to 0.)explevelUpCarryoverrW  itemDropz9enemy_stat_table HP-view +0x2a/+0x2b -> actor +0x40/+0x41zWcontinuous enemy stat table has no active item drops; monster rewards are EXP/gold onlyz!some enemy rows define item drops)sourcenonZeroRowsrX   )versionr5  r  r   summaryflowRowsformulamoneyGlobalexpReadableitemDropSourceRowsitemDropSourceConclusionruntimeBridgeevidenceopenQuestions)FUNCTION_SPECSr{   r5   r   r   r  r   ENEMY_STAT_TABLEr!  r  rF   rb   r   r   __file__relative_toROOT	FLOW_ROWSrp  r   r  )r  specstartstopr   r=  drop_source_rowsr  rC  rA  packeditem_id	drop_ratemoney_globals                 r   buildr  L  s   H 	
7mtUD)guI;bD
3)&$y/4PY?[	
	
 ,666HIJ~~fb) NQggV]_aNbcd+&DHHWa,@(AAcc7A&4-q[D(	i## WWW-GGK0CCGGFO% )	 /0L1d8n0067Tu T h c BnA,B_A``}  K  LZ  [  ~\  \|  }I  Ja  }b  |c  cG  HT  Um  Hn  Go  oS  T W S R t
 AJJ44WbTm4J ~ !J D v	
 $)*5.Q/0 |Ls  Ru%

 .j9G$ $ d@ Ks   72H<Hc                   dj                  d |D              }g }| D ]  }g }|D ]j  \  }}|j                  |d      }|j                  d      s|dk(  r|j                  dt	        |       d       M|j                  dt	        |       d       l |j                  d	dj                  |      z   d
z           d| ddj                  |       dS )Nr   c              3  @   K   | ]  \  }}d t        |       d  yw)z<th>z</th>Nr   )rm   _r   s      r   ro   zrender_table.<locals>.<genexpr>  s!     D5T#e*U+Ds   Hexr2   z
<td><code>z</code></td>z<td>z</td>z<tr>z</tr>z<table><thead><tr>z</tr></thead><tbody>z</tbody></table>)ru   r  r$  r5   r   )	r;  columnsheadbodyr  cellsr   r  r   s	            r   render_tabler    s    77DGDDDD 7 	7FCGGC$E||E"cWnz#e*\BCtCJ<u56	7 	FRWWU^+g567  v%9"''$-HXYYr   c                   dj                  d | j                  dg       D              }dj                  d | j                  dg       D              }| j                  di       }t        |j                  dg       g d      }t        j                  | j                  d	g       d
      }d| d| dt        |j                  d             dt        |j                  d             dt        |j                  d             d| d| dS )Nr   c              3  :   K   | ]  }d t        |       d  ywz<li></li>Nr  rt  s     r   ro   z.render_exp_readable_section.<locals>.<genexpr>  s     ]$SYKu-]   rh  c              3  :   K   | ]  }d t        |       d  ywr  r  rt  s     r   ro   z.render_exp_readable_section.<locals>.<genexpr>  s     Rd3t9+U+Rr  ri  rj  r;  ))rc  u   주인공 Lv)rZ  u   몬스터 Lv)rM  u   차이)rO  u
   획득 EXPrk  F)ensure_asciiuL    <section class="exp-calc" id="expCalc">
    <h2>EXP 획득 계산</h2>
    <p class="sub">몬스터별 EXP 기준값과 Lv를 EXE stat row에서 자동으로 채운다. Lv는 actual row +0x06, 즉 HP-view -0x02다.</p>
    <div class="formula-grid">
      <article class="formula-card">
        <h3>계산 순서</h3>
        <ol>ug   </ol>
      </article>
      <article class="formula-card">
        <h3>핵심 규칙</h3>
        <ul>u  </ul>
      </article>
    </div>
    <div class="calculator-panel">
      <div class="controls">
        <label>몬스터
          <select id="expMonsterSelect"></select>
        </label>
        <label>EXP 기준값
          <input id="expBaseInput" type="number" min="1" step="1">
        </label>
        <label>주인공 Lv
          <input id="playerLevelInput" type="number" min="1" max="99" step="1" value="1">
        </label>
        <label>몬스터 Lv
          <input id="enemyLevelInput" type="number" min="1" max="99" step="1" value="1">
        </label>
        <label class="check">
          <input id="expPenaltyInput" type="checkbox"> EXP 절반 페널티
        </label>
      </div>
      <div class="result-card">
        <div class="result-label">획득 EXP</div>
        <div id="expRewardResult" class="result-value">-</div>
        <div id="expRewardFormula" class="formula-line"></div>
        <div id="expMonsterMeta" class="meta-line"></div>
      </div>
    </div>
    <h3>예시: rd  u    EXP 기준값 re  u   , 몬스터 Lv rZ  z
</h3>
    z=
  </section>
  <script>
    (() => {
      const monsters = u  ;
      const select = document.getElementById("expMonsterSelect");
      const expBaseInput = document.getElementById("expBaseInput");
      const playerLevelInput = document.getElementById("playerLevelInput");
      const enemyLevelInput = document.getElementById("enemyLevelInput");
      const penaltyInput = document.getElementById("expPenaltyInput");
      const result = document.getElementById("expRewardResult");
      const formula = document.getElementById("expRewardFormula");
      const meta = document.getElementById("expMonsterMeta");

      function clampInt(value, min, max, fallback) {
        const parsed = Number.parseInt(value, 10);
        if (!Number.isFinite(parsed)) return fallback;
        return Math.max(min, Math.min(max, parsed));
      }

      function calcExp(expBase, playerLevel, enemyLevel, penalty) {
        const diff = Math.min(Math.abs(enemyLevel - playerLevel), 10);
        let value;
        let branch;
        if (enemyLevel < playerLevel) {
          value = Math.max(expBase - Math.trunc((expBase * diff * 12) / 100), 1);
          branch = "몬스터 레벨이 낮음";
        } else {
          value = expBase + Math.trunc((expBase * diff * diff) / 100);
          branch = "몬스터 레벨이 같거나 높음";
        }
        if (penalty) value = Math.max(Math.trunc(value / 2), 1);
        return { value, diff, branch };
      }

      function selectedMonster() {
        return monsters[Number.parseInt(select.value, 10)] || monsters[0] || null;
      }

      function syncMonster() {
        const monster = selectedMonster();
        if (!monster) return;
        expBaseInput.value = String(monster.expBase || 1);
        if (monster.enemyLevel) enemyLevelInput.value = String(monster.enemyLevel);
        meta.textContent = `HP ${monster.hp ?? "-"} · gold ${monster.gold ?? "-"} · row #${monster.index ?? "-"} · Lv ${monster.enemyLevel ?? "-"} · HP-view +0x36=${monster.nextRowLevelAtHpView36 ?? "-"}(다음 행 Lv)`;
        update();
      }

      function update() {
        const expBase = clampInt(expBaseInput.value, 1, 9999, 1);
        const playerLevel = clampInt(playerLevelInput.value, 1, 99, 1);
        const enemyLevel = clampInt(enemyLevelInput.value, 1, 99, 1);
        const penalty = penaltyInput.checked;
        const computed = calcExp(expBase, playerLevel, enemyLevel, penalty);
        result.textContent = String(computed.value);
        const penaltyText = penalty ? " → 절반 페널티 적용" : "";
        if (enemyLevel < playerLevel) {
          formula.textContent = `diff=${computed.diff} · ${computed.branch} · max(${expBase} - floor(${expBase}*${computed.diff}*12/100), 1)${penaltyText}`;
        } else {
          formula.textContent = `diff=${computed.diff} · ${computed.branch} · ${expBase} + floor(${expBase}*${computed.diff}*${computed.diff}/100)${penaltyText}`;
        }
      }

      if (select) {
        select.innerHTML = monsters.map((monster, index) =>
          `<option value="${index}">${monster.index} · ${monster.name} · Lv ${monster.enemyLevel ?? "-"} · EXP ${monster.expBase}</option>`
        ).join("");
        select.addEventListener("change", syncMonster);
        for (const input of [expBaseInput, playerLevelInput, enemyLevelInput, penaltyInput]) {
          input.addEventListener("input", update);
          input.addEventListener("change", update);
        }
        syncMonster();
      }
    })();
  </script>
)ru   r  r  r  dumpsr   )exp_datar  ri  rj  ro  options_jsons         r   render_exp_readable_sectionr    s(   gg](,,GWY[:\]]GGGRWb8QRREll9b)GFBwL ::hll+;R@uUL G  I 8 W[[+,-_S^A\=]<^^mnqryr}r}  K  sL  oM  nN NN  %~ H&Ws sr   c                j    t         dz  j                  t        j                  | dd      dz   d       y )Nzbattle_end_reward_review.jsonFrL   )r  indentri   r   r   )OUT
write_textr  r  r7   s    r   
write_jsonr    s1    **66tzz$UZcd7ehl7lw~6r   c                T   g d}|| d   D cg c]  }d| 	 c}z  }|g dz  }|ddgz  }| d   D ],  }|j                  d|d	    d
|d    d|d    d|d    d	       . |g dz  }| d   j                         D ]  \  }}|j                  d| d|         | j                  di       }|g dz  }|r|d|j                  d       dd|j                  d       d|j                  d       dd|j                  d       dd |j                  d!       dd"|j                  d#       dd$|j                  d%       dd&|j                  d'       dd(|j                  d)       gz  }| j                  d*i       }|j                  d+i       }|g d,z  }||j                  d-g       D cg c]  }d| 	 c}z  }|g d.z  }|j                  d/g       D ]  }|j                  d0|         |d1d2|j                  d3       d4|j                  d5       d6|j                  d7       d1d8d9gz  }|j                  d:g       D ]J  }|j                  d|j                  d;       d|j                  d<       d|j                  d=       d       L | j                  d>i       }	|	j                  d?i       }
|g d@z  }|	r|j                  dA|	j                  dB              |j                  dC|
j                  dD              |j                  dE|
j                  dF       dG       |j                  dH|
j                  dI       dJ|
j                  dF       dK|
j                  dL       dM|
j                  dN       dO|
j                  dP       
       |j                  dQ|
j                  dR       dJ|
j                  dS       dK|
j                  dT              |j                  dU|
j                  dV              |	j                  dW      }|r|j                  dX|j                  dY       dZ|j                  d[              |j                  d\|j                  d]       d^|j                  d_       d`       |j                  da      r|g dbz  }|dcddgz  }|j                  dag       ded  D ]  }|j                  d|j                  df       d
|j                  dg       dh|j                  di       d|j                  dj       d|j                  dk       d|j                  dl       d        |j                  dm      r|g dnz  }|dodpgz  }|j                  dmg       D ]n  }|j                  d|j                  df       d
|j                  dq       dh|j                  dg       d|j                  dr       d|j                  d       d       p | ds   }|d1dtd1du|dv    ddw|dx    d(|d)    gz  }|g dyz  }| dz   D ]&  }|d{|dj    d||d}    dd1d~gz  }||d   z  }|dd1gz  }( |dd1gz  }| d   r|| d   D cg c]  }d| 	 c}z  }n|j                  d       t        dz  j	                  dj                  |      j                         dz   d       y c c}w c c}w c c}w )N)z# Battle End Reward Reviewr   z
## Summaryr   r  z- )r   z## Flowr   z | step | VA | status | meaning |z|---|---:|---|---|r  z| r   z | `r  z` | r   z | r   z |)r   z
## Formular   r  z- `z`: r  )r   z## Global Moneyr   z- address: `rP   `z- section/file offset: `rI   z` / `rR   z- PE initial dword: `rS   z- cap: `rT   z- direct address refs: `rU   z- `0x004576e2` direct refs: `rV   z- initial bytes: `rW   z- conclusion: rX   r  rj  )r   z## EXP Gain Calculator Notesr   rh  )r   z	### Stepsr   ri  z1. r   z### Example: rd  z
 EXP base re  z, enemy level rZ  z| player level | diff | EXP |z|---:|---:|---:|r;  rc  rM  rO  r  r  )r   z## Runtime Bridger   z- observation: r  z	- enemy: rw  z- EXP base: ry  z (`enemy stat HP-view +0x04`)z- level-up carryover: r|   +  = r~  ; carry r   * 3 / 4 = r  z- gold: r  rz  r  z- matches observation: r  r  z- runtime trace: `r   z	` frames r  z - direct reward write captured: r  z (r  )r  )r   z### Key Runtime Value Changesr   z,| frame | PC | address | label | old | new |z|---:|---:|---:|---|---:|---:|ir   r   z` | `rO   r   r   r   r  )r   z### Runtime Static Address Hitsr   z,| frame | caller | PC | function | meaning |z|---:|---:|---:|---|---|r   r   r  z## Item Drop Sourcez- source: `r  z- non-zero rows: r  )r   z## Evidencer   r  z### z `r2   z```asmr  z```z## Open Questionsr  u   - 없음zbattle_end_reward_review.mdri   r   r   )r5   itemsr  r  r  ru   r   )r7   r   ru  r  r   r   moneyr  rj  bridgestaticr   rX   evs                 r   write_mdr  "  s0   E 
d9o6d4&k66E	  E	02FGGEJ cr#f+d3w<.S]O3sS\~N^^`abc	##E9o++- ,
Us3%s5'*+,HH]B'E	((E599\2315&uyy';&<E%))OB\A]]^_#EIIn$=#>a@uyy'(*&uyy1H'I&J!L+EII6N,O+PPQR >!: ;1=UYY|456	
 		
 xxr*Hll9b)G	55E	hll3CR&HId4&kIIE	""EWb) #s4&\"#	

I./z'++n:U9VVdelepepq}e~d  	A
' E {{62& `r#''-01SWWV_4ESQYIZH[[]^_`XXor*FZZ+R0F	**Evzz-'@&ABCyG!4 567|FJJ/B$C#DDabc-fjj9J.K-LCPVPZPZ[nPoOppstzt~t~  @]  u^  t_  _g  hn  hr  hr  sE  hF  gG  GR  SY  S]  S]  ^u  Sv  Rw  x  	yx

< 89VZZ=T<UUXY_YcYcdoYpXqrs.vzz:R/S.TUV**^,LL-gkk*.E-FiPWP[P[\hPiOjklLL;GKKHc<d;eeghohshstzh{g||}~{{./BBHJjkk";;':B?E CLLSWWW-.d3774=/swwyGYFZZ^_b_f_fgn_o^ppstwt{t{  }B  uC  tD  DG  HK  HO  HO  PU  HV  GW  WY  Z {{./DDHJdee";;':B? CLLSWWW-.d37783D2EU377SW=/Y]^a^e^efp^q]rruvyv}v}  H  wI  vJ  JL  M 01J	


j*+1-
J}567
L123 E 
$$E: DGR7}A6HEEJ% 
!2&&EO$*?@$Btf+@@Z ((44TYYu5E5L5L5NQU5U`g4hi 70 Jr As   Z*Z =Z%c                   dj                  d | d   D              }dj                  d | d   D              }| j                  di       }|j                  di       }|j                  d      xs i }d}|j                  d	      rt        |d	   g d
      }dj                  d |j                  dg       D              }dj                  d |j                  dg       d d D              }d}	|j                  d      rt        |d   dd  g d      }	d}
|j                  d      rt        |d   g d      }
d}|j                  d      r}t        |j                  dg       D cg c]U  }|j                  d      |j                  d      |j                  d      |j                  d      |j                  d      dW c}g d      }| j                  di       }d}|rt        |j                  d      |j                  d      |j                  d       |j                  d!      |j                  d"      |j                  d#      |j                  d$      |j                  d%      |j                  d&      d'	gg d(      }g }| d)   D ]Y  }|j                  d*t	        |d+          d,t	        |d-          d.t	        t        d/      j                  |d0                d1       [ dj                  g d2t        | d3          d4| d5t        | d6   g d7       d8t	        | d9   d:          d;t	        | d9   d<          d=t	        | d9   d>          d?t	        | d9   d@          dA| dBt	        |j                  dC             dDt	        |j                  dE             dFt	        |j                  dG             dHt	        |j                  dI             dJt	        |j                  dG             dKt	        |j                  dL             dMt	        |j                  dN             dOt	        |j                  dP             dQt	        |j                  dR             dJt	        |j                  dS             dKt	        |j                  dT             dUt	        |j                  dV             dWt	        |j                  d             dXt	        |j                  d             dYt	        |j                  d             dZt	        |j                  d             d[| d\| d]| d^|	 d_|
 d`| dat	        | db   dc          ddt	        | db   de          dft	        | db   d&          dgdj                  |       dh| di      }t        djz  j                  |dkl       t        djz  j                  |dkl       y c c}w )mNr   c              3  :   K   | ]  }d t        |       d  ywr  r  rt  s     r   ro   zwrite_html.<locals>.<genexpr>  s     J$SYKu-Jr  r  c              3  :   K   | ]  }d t        |       d  ywr  r  rt  s     r   ro   zwrite_html.<locals>.<genexpr>  s     ST4D	{%0Sr  r  r  r  r  r  )r   r   r   PC)r   r   )rh   rh   c              3  X   K   | ]"  \  }}d t        |       dt        |       d $ ywz
<li><code>z	</code>: r  Nr  rm   r(   rZ   s      r   ro   zwrite_html.<locals>.<genexpr>  s4       FT_TXZ_z#d)Ic%j\O  F   (*r  c              3  X   K   | ]"  \  }}d t        |       dt        |       d $ ywr  r  r  s      r   ro   zwrite_html.<locals>.<genexpr>  s4        NWbW[]b*SYKyUE R   Nr  r  r  r  i)r  r  )rO   rO   )r   r   )r   r   )r   r   )r   zcpu eipr  )r  )r   r   r  )r   r   r   r   r  r
  r   r  r  r  )r
  r   framesdirectr  ))r
  trace)r   r   )r  r  )r  r  )r  r  r  rP   rI   rR   rS   rT   rU   rV   rW   rX   )	rP   rI   rR   rS   rT   rU   rV   rW   rX   )	)rP   VA)rI   rI   )rR   zfile offset)rS   initial)rT   rT   )rU   refs)rV   z	0x+2 refs)rW   zinitial bytes)rX   rX   r  z<details><summary>r   z <code>r2   z</code></summary><pre>rG  r  z</pre></details>u  <!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <title>Battle End Reward Review</title>
  <style>
    body { margin: 24px; font-family: system-ui, sans-serif; background: #f7f7f4; color: #202124; }
    h1 { margin-bottom: 4px; }
    .sub { color: #5f6368; margin-top: 0; }
    table { border-collapse: collapse; width: 100%; background: white; }
    th, td { border: 1px solid #ddd; padding: 8px; vertical-align: top; }
    th { background: #eeeeea; text-align: left; }
    code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
    pre { white-space: pre-wrap; background: #111; color: #eee; padding: 12px; overflow-x: auto; }
    section { margin: 20px 0; }
    details { margin: 10px 0; }
    .formula-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; }
    .formula-card, .calculator-panel, .result-card { background: #fff; border: 1px solid #d9d9d0; border-radius: 8px; padding: 14px; }
    .formula-card h3 { margin-top: 0; }
    .calculator-panel { display: grid; grid-template-columns: minmax(280px, 2fr) minmax(220px, 1fr); gap: 14px; margin: 14px 0; }
    .controls { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; align-items: end; }
    .controls label { display: grid; gap: 4px; font-size: 13px; color: #4b4c4d; }
    .controls input, .controls select { min-height: 34px; border: 1px solid #cfcfc8; border-radius: 6px; padding: 6px 8px; font: inherit; background: #fff; }
    .controls .check { display: flex; align-items: center; gap: 8px; min-height: 34px; }
    .result-card { border-color: #b9c2a7; background: #fbfff5; }
    .result-label { color: #667057; font-size: 13px; }
    .result-value { font-size: 44px; line-height: 1; font-weight: 800; color: #26320f; margin: 6px 0 10px; }
    .formula-line, .meta-line { color: #4d5543; font-size: 13px; line-height: 1.45; }
    @media (max-width: 760px) { .calculator-panel { grid-template-columns: 1fr; } }
  </style>
</head>
<body>
  <h1>Battle End Reward Review</h1>
  <p class="sub">KO enemy actor → live player EXP fan-out → gold reward and inactive item-drop handler.</p>
  r  z 
  <section><h2>Summary</h2><ul>z(</ul></section>
  <section><h2>Flow</h2>r  ))r   r   )r  r  )r   r   r  zE</section>
  <section><h2>Formula</h2><ul>
    <li><code>exp</code>: r  r  z-</li>
    <li><code>levelUpCarryover</code>: r  z!</li>
    <li><code>gold</code>: rW  z%</li>
    <li><code>itemDrop</code>: r  z8</li>
  </ul></section>
  <section><h2>Global Money</h2>z5</section>
  <section><h2>Runtime Bridge</h2>
    <p>r  z4</p>
    <table><tbody>
      <tr><th>enemy</th><td>rw  z0</td></tr>
      <tr><th>enemy EXP base</th><td>ry  zZ <code>enemy stat HP-view +0x04</code></td></tr>
      <tr><th>level-up carryover</th><td>r|  r  r  r~  r  r  r  r  z&</td></tr>
      <tr><th>gold</th><td>r  rz  r  z5</td></tr>
      <tr><th>matches observation</th><td>r  z5</td></tr>
      <tr><th>runtime trace</th><td><code>z</code> frames z></td></tr>
      <tr><th>direct reward write captured</th><td>z. zE</td></tr>
    </tbody></table>
    <h3>Runtime Event Counts</h3><ul>z.</ul>
    <h3>Runtime Resource Labels</h3><ul>z(</ul>
    <h3>Runtime Trace Summary</h3>z'
    <h3>Key Runtime Value Changes</h3>z)
    <h3>Runtime Static Address Hits</h3>z 
    <h3>Runtime Text Draws</h3>zT
  </section>
  <section><h2>Item Drop Source</h2><ul>
    <li><code>source</code>: r  r  z(</li>
    <li><code>nonZeroRows</code>: r  z'</li>
    <li><code>conclusion</code>: z4</li>
  </ul></section>
  <section><h2>Evidence</h2>z1</section>
  <section><h2>Open Questions</h2><ul>z </ul></section>
</body>
</html>
zbattle_end_reward_review.htmlr   r   )
ru   r  r  r5   r   chrr  r  r  WEB)r7   r  
open_itemsr  r  r   runtime_text_drawsruntime_countsruntime_resourcesruntime_probe_changesruntime_static_hitsruntime_traces_summaryru  r  money_tabler  r  	html_texts                     r   
write_htmlr    sl	   ggJ$y/JJGST/=RSSJXXor*FZZ+R0Fjj(.BG{{;)K R
 WW  Fcjcncno  BD  dE  F  FN   Nfmfqfq  sC  EG  gH  IL  JL  gM   N  N{{&' ,'(.!
 {{&'*'(	
  zz/"!- #JJ;	   $xx	2 $ 4"hh|4"hh'BC HHV,	 {"
 HH]B'EK" #())L"9$yy3%*YY%?$)IIn$= 99U+-2YY7N-O.3ii8P.Q$)IIn$="'))L"9


4 H: 
 R[!1 2'#bk:J9K LBR
^4566FH	


G G " GD tM234EGD5 EGF !(yGGF)GGH &d:&6  9J  K  LIGHLIGL tIu567MGL8(MGN ),DO<N,O(P'QOGNROGP  Y 789QGP: QGR !$DOJ$? @ASGRB!SGV "-WGV.WGZ 	FJJ}%&'[GZ([G^ !G!456_G^7&_G` '*&**5H*I&J%KaG`L*aGb +.fjj9J.K*L)McGb NQcGb RUU[U_U_`sUtQuPvcGb wzcGb {~  E  I  I  Jg  h  {i  zjcGb jrcGb sv  w}  wA  wA  BT  wU  sV  rWcGb WbcGb cf  gm  gq  gq  rI  gJ  cK  bLcGbLcGd  

< 89:eGd ;>eGd ?B&**[BY>Z=[eGd \_eGd `ccicmcmnycz_{^|eGd}+eGf ,/vzz:R/S+T*UgGfV+gGh ,/w{{:/F+G*HiGh IXiGh Y\\c\g\ght\uXvWwiGhx4iGj 58D_8`4a3bkGj cekGj fiipititu{i|e}d~kGj&kGn '5%5oGn6)oGp *;(;qGp<#qGr $:":sGr;'sGt (=&=uGt>)uGv *=(=wGv> wGx !33yGx4yG~ "$'A"B8"LMNG~O#G@ $'t,F'G'V#W"XAG@Y"AGB #&d+E&F|&T"U!VCGBWCGF  WWX./GGF0'GGH (2lIGH3IGIP 	**66y76S**66y76Sq	s   AW%c                     t         j                  d       t        j                  d       t               } t	        |        t        |        t        |        y )NT)exist_ok)r  mkdirr  r  r  r  r  r  s    r   mainr  &  s<    IItIIItI7DtTNtr   __main__)r   r   returnr   )r   rF   r	  r   )r	  list[dict[str, Any]])rH   rF   r	  ztuple[dict[str, Any], int])r	  dict[str, Any])rw   rF   rx   rF   r	  r   )r   r   r   	list[str]r}   rF   r	  r  )r   r   r	  ztuple[Path | None, Path | None])r   r   r	  r   )r   r   r	  dict[str, Any] | None)r=  r  r>  r   r	  r  )r  r  r?   r   r	  
int | None)
rJ  rF   rK  rF   rL  rF   rE  boolr	  rF   )r  r  r	  r  )r=  r  r	  r
  )r=  r  r	  r  )r;  r
  r  zlist[tuple[str, str]]r	  r   )r  r  r	  r   )r7   r  r	  None)r	  r  )6__doc__
__future__r   r   r  rk   r0   rp   collectionsr   pathlibr   typingr   r  resolveparentsr  r.   r  r  r   r  r  r&  rY   r\   r   r   rD   rJ   rb   r{   r   r  r  r   r   r9  r?  rD  rP  rS  r^  rp  r  r  r  r  r  r  r  r  __name__r   r   r   <module>r     s   Q "   	      H~''*
]
Ul
Ul*y(00 %'>? +  @2:*j. FG $ & C	 . H	  g	 ( k	 u	 - V	 % w	 $ }	W1	l ))

 " &)
  3)
  0)
 
 /)
 
 &)
 GQh7
]@ W\ 	(.)X(VAHZ|~@[i|cTL zF r   