§
    ÊV¿jÌ4  ã                   ó  — d Z ddlZddlZddlZddlmZ ddlZ ee¦  «                             ¦   «         j	        j	        j	        Z
e
dz  dz  dz  dz  dz  Ze
dz  dz  dz  dz  d	z  Ze
dz  dz  dz  dz  d
z  Ze
dz  dz  dz  Ze
dz  dz  dz  dz  dz  Ze
dz  dz  dz  dz  dz  Ze
dz  dz  dz  dz  dz  Z G d„ d¦  «        Z G d„ d¦  «        Z G d„ d¦  «        Z G d„ d¦  «        Z G d„ d¦  «        Z G d„ d¦  «        ZdS )z¯
Unit Test Suite for Fog of War Multi-State Engine & Minimap HUD (Milestone M4).
Genuine Node.js runtime tests: 3-state lifecycle, persistence, 30Hz throttling, anti-maphack.
é    N)ÚPathÚclientÚwebappÚjsÚuiz
war_fog.jszwar_fog_renderer.jszminimap_hud.jsz
index.htmlÚenginezworld_renderer.jszentity_renderer.jszmonster_system.jsc                   ó   — e Zd Zd„ Zd„ ZdS )ÚTestCodeHygieneAndExportsc                 ó  — t          t                               d¬¦  «                             ¦   «         ¦  «        dk    sJ ‚t          t                               d¬¦  «                             ¦   «         ¦  «        dk    sJ ‚t          t
                               d¬¦  «                             ¦   «         ¦  «        dk    sJ ‚t          t                               d¬¦  «                             ¦   «         ¦  «        dk    sJ ‚dS )z*Verifies strict line budgets for M4 files.úutf-8©Úencodingi,  éú   i  N)ÚlenÚ
WAR_FOG_JSÚ	read_textÚ
splitlinesÚWAR_FOG_RENDERER_JSÚMINIMAP_HUD_JSÚ
INDEX_HTML©Úselfs    ú8C:\Projects\FreeExile\tests\unit\test_fog_and_minimap.pyÚtest_line_count_capsz.TestCodeHygieneAndExports.test_line_count_caps   sâ   € å•:×'Ò'°Ð'Ñ9Ô9×DÒDÑFÔFÑGÔGÈ3ÒNÐNÐNÐNÝÕ&×0Ò0¸'Ð0ÑBÔB×MÒMÑOÔOÑPÔPÐTWÒWÐWÐWÐWÝ•>×+Ò+°WÐ+Ñ=Ô=×HÒHÑJÔJÑKÔKÈsÒRÐRÐRÐRÝ•:×'Ò'°Ð'Ñ9Ô9×DÒDÑFÔFÑGÔGÈ3ÒNÐNÐNÐNÐNÐNó    c                 óà   — t                                d¬¦  «        }dD ]}||v sJ d|› d�¦   «         ‚Œd|v s
J d¦   «         ‚t                               d¬¦  «        }d|v sJ ‚d	|v sJ ‚d
|v sJ ‚dS )z1Verifies required symbols and DOM bindings exist.r   r   )
Ú	FOG_STATEÚinitFogÚgetFogStateÚsetFogStateÚupdatePlayerVisionÚpackFogBitsÚunpackFogBitsÚsaveFogÚloadFogÚgetStorageKeyzMissing symbol z in war_fog.jsu   Bá»Š CHáº¶N Bá»žI RÃ€O Cáº¢Nz5Notice string must exist for modal test compatibilityzid="minimap-container"zid="minimap-canvas"zsrc="js/ui/minimap_hud.js"N)r   r   r   )r   ÚwfÚsymÚhtmls       r   Ú$test_es_exports_and_elements_presentz>TestCodeHygieneAndExports.test_es_exports_and_elements_present   s¶   € å×!Ò!¨7Ð!Ñ3Ô3ˆð[ð 	Dð 	DˆCà˜"�9�9�9ÐC°ÐCÐCÐC‘9”9�9�9Ø-°Ð3Ð3Ð3Ð5lÑ3Ô3Ð3å×#Ò#¨WÐ#Ñ5Ô5ˆØ'¨4Ð/Ð/Ð/Ð/Ø$¨Ð,Ð,Ð,Ð,Ø+¨tÐ3Ð3Ð3Ð3Ð3Ð3r   N)Ú__name__Ú
__module__Ú__qualname__r   r*   © r   r   r
   r
      s5   € € € € € ðOð Oð Oð4ð 4ð 4ð 4ð 4r   r
   c                   ó6   — e Zd Zed„ ¦   «         Zd„ Zd„ Zd„ ZdS )ÚTestWarFogNodeRuntimec                 ó¢   — d}t          j        dd|gddt          t          ¦  «        d¬¦  «        }t	          j        |j        ¦  «        | _        d S )Na²	  
        const fs = require('fs');
        let code = fs.readFileSync('client/webapp/js/ui/war_fog.js', 'utf8')
          .replace(/import\s+[^;]+;\s*/g, '').replace(/export\s+(const|let)\s+/g, 'var ')
          .replace(/export\s+function\s+/g, 'function ').replace(/export\s+\{[^}]+\};?\s*/g, '')
          .replace(/export\s+default\s+[^;]+;?\s*/g, '');

        const mockStorage = {};
        global.localStorage = {
          getItem: (k) => mockStorage[k] || null,
          setItem: (k, v) => { mockStorage[k] = v; },
          removeItem: (k) => { delete mockStorage[k]; }
        };
        global.document = { getElementById: () => null, addEventListener: () => {} };
        global.window = {};
        const GRID_SIZE = 32, TILE_PX = 15, BIOMES = [{ id: 'TEST', name: 'Test' }];
        function generateMazeData() { return { biomeIdx: 0, mapTiles: [], fogMatrix: [], barricades: [], bossGate: null, bossRoom: null, playerPos: {x:0, y:0}, bossPos: {x:10, y:10}, simulatedPath: [] }; }
        function renderProceduralMap() {}

        eval(code);

        const results = {};
        initFog(60, 45, 'zone_tang_kiem_nhai', 101);
        results.init = { w: mapW, h: mapH, at00: getFogState(0, 0), atNeg: getFogState(-1, 0), atOob: getFogState(60, 45) };

        updatePlayerVision(10, 10, 8);
        const r10_10 = getFogState(10, 10), rEdge = getFogState(10 + 8, 10), rOut = getFogState(10 + 9, 10);
        updatePlayerVision(25, 25, 8);
        results.vision = { cBefore: r10_10, edgeBefore: rEdge, outBefore: rOut, cDecayed: getFogState(10, 10), cNew: getFogState(25, 25) };

        initFog(120, 90, 'zone_boundless_celestial_palace', 777);
        for (let i = 0; i < 120 * 90; i++) {
          if ((i * 37 + 13) % 7 === 0) fogGrid[i] = (i % 2 === 0) ? 1 : 2;
        }
        const storageKey = getStorageKey('zone_boundless_celestial_palace', 777);
        const packed = packFogBits(fogGrid, 120, 90);
        saveFog('zone_boundless_celestial_palace', 777);
        const savedRaw = localStorage.getItem(storageKey);
        const fresh = new Uint8Array(120 * 90);
        const unpackOk = unpackFogBits(savedRaw, fresh, 120, 90);
        let bitMatches = 0;
        for (let i = 0; i < 120 * 90; i++) {
          if ((fogGrid[i] > 0) === (fresh[i] > 0)) bitMatches++;
        }
        results.persist = { storageKey, packedBytesKb: (packed.length / 1024), unpackOk, total: 120 * 90, bitMatches };

        console.log(JSON.stringify(results));
        Únodeú-eT©Úcapture_outputÚtextÚcwdÚcheck)Ú
subprocessÚrunÚstrÚROOTÚjsonÚloadsÚstdoutÚnode_res©ÚclsÚjs_codeÚprocs      r   Úsetup_classz!TestWarFogNodeRuntime.setup_class-   sP   € ð/ˆõ` Œ~˜v t¨WÐ5ÀdÐQUÕ[^Õ_cÑ[dÔ[dÐlpÐqÑqÔqˆÝ”z $¤+Ñ.Ô.ˆŒˆˆr   c                 ó    — | j         d         }|d         dk    r|d         dk    sJ ‚|d         dk    r|d         dk    r|d	         dk    sJ ‚d S )
NÚinitÚwé<   Úhé-   Úat00r   ÚatNegÚatOob©r@   )r   Ústs     r   Útest_node_init_and_boundsz/TestWarFogNodeRuntime.test_node_init_and_boundsb   sa   € ØŒ]˜6Ô"ˆØ�#Œw˜"Š}ˆ}  C¤¨B¢  Ð.Ø�&Œz˜QŠˆ 2 g¤;°!Ò#3Ð#3¸¸7¼ÀqÒ8HÐ8HÐ8HÐHÐ8HÐ8Hr   c                 ó´   — | j         d         }|d         dk    r|d         dk    r|d         dk    sJ ‚|d         dk    s
J d	¦   «         ‚|d
         dk    sJ ‚d S )NÚvisionÚcBeforeé   Ú
edgeBeforeÚ	outBeforer   ÚcDecayedé   z>Tiles outside current radius must decay to EXPLORED_FOGGED (1)ÚcNewrO   )r   Úviss     r   Ú(test_node_3state_vision_reveal_and_decayz>TestWarFogNodeRuntime.test_node_3state_vision_reveal_and_decayg   s{   € ØŒm˜HÔ%ˆØ�9Œ~ Ò"Ð" s¨<Ô'8¸AÒ'=Ð'=À#ÀkÔBRÐVWÒBWÐBWÐBWÐWØ�:Œ !Ò#Ð#Ð#Ð%eÑ#Ô#Ð#Ø�6Œ{˜aÒÐÐÐÐÐr   c                 óÌ   — | j         d         }|d         dk    sJ ‚|d         du sJ ‚|d         dk     sJ d|d         › d	�¦   «         ‚|d
         |d         k    s
J d¦   «         ‚d S )NÚpersistÚ
storageKeyÚ*fe_fog_zone_boundless_celestial_palace_777ÚunpackOkTÚpackedBytesKbç       @zPacked size z KB exceeds 2.0 KBÚ
bitMatchesÚtotalz100% bit preservation requiredrO   )r   Úps     r   Útest_node_compact_bitpackingz2TestWarFogNodeRuntime.test_node_compact_bitpackingm   s�   € ØŒM˜)Ô$ˆØ�ŒÐ"NÒNÐNÐNÐNØ�Œ} Ð$Ð$Ð$Ð$Ø�Ô! CÒ'Ð'Ð'Ð)^¸¸/Ô8JÐ)^Ð)^Ð)^Ñ'Ô'Ð'Ø�Œ ! G¤*Ò,Ð,Ð,Ð.NÑ,Ô,Ð,Ð,Ð,r   N)r+   r,   r-   ÚclassmethodrE   rQ   r\   rg   r.   r   r   r0   r0   ,   sc   € € € € € Øð2/ð 2/ñ „[ð2/ðhIð Ið Ið
 ð  ð  ðOð Oð Oð Oð Or   r0   c                   ó   — e Zd Zd„ ZdS )Ú TestMinimapProjectionAndGeometryc                 ó8  — t          dd¦  «        }t          j        |dd¬¦  «        sJ ‚ddd|z  z
  dz  z   }|dk    sJ ‚t          dd	¦  «        }t          j        |d	d¬¦  «        sJ ‚dd
|z  z   }dd|z  z   }d|cxk    rdk     rn nd|cxk    rdk     sn J ‚dS )z:Aspect ratio preservation with padding 2 on 120x80 canvas.gïîîîîîþ?g[°[°û?g-Cëâ6?)Úrel_tolrc   g      ]@g      N@gïîîîîîî?g[°[°ë?g     À]@g     @V@r   éx   éP   N)ÚminÚmathÚisclose)r   ÚscaleÚpad_xÚscale_largeÚcorner_xÚcorner_ys         r   Ú,test_aspect_ratio_preservation_and_centeringzMTestMinimapProjectionAndGeometry.test_aspect_ratio_preservation_and_centeringv   sí   € õ �L +Ñ.Ô.ˆÝŒ|˜E ;¸Ð=Ñ=Ô=Ð=Ð=Ð=Ø�u˜t e™|Ñ+¨sÑ2Ñ2ˆØ�sŠ{ˆ{ˆ{ˆ{õ ˜-¨Ñ5Ô5ˆÝŒ|˜K¨¸dÐCÑCÔCÐCÐCÐCØ˜ Ñ,Ñ,ˆØ˜ Ñ+Ñ+ˆØ�HÐ"Ð"Ò"Ð"˜sÒ"Ð"Ð"Ð"Ð" q¨HÐ'9Ð'9Ò'9Ð'9°rÒ'9Ð'9Ð'9Ð'9Ð'9Ð9Ð'9Ð'9r   N)r+   r,   r-   rw   r.   r   r   rj   rj   u   s#   € € € € € ð:ð :ð :ð :ð :r   rj   c                   ó0   — e Zd Zed„ ¦   «         Zd„ Zd„ ZdS )Ú)TestMinimapRuntimeThrottlingAndIndicatorsc                 ó¢   — d}t          j        dd|gddt          t          ¦  «        d¬¦  «        }t	          j        |j        ¦  «        | _        d S )Naã	  
        const fs = require('fs');
        let code = fs.readFileSync('client/webapp/js/ui/minimap_hud.js', 'utf8')
          .replace(/import\s+[^;]+;\s*/g, '').replace(/export\s+(const|let)\s+/g, 'var ')
          .replace(/export\s+function\s+/g, 'function ').replace(/export\s+class\s+MinimapHUD/g, 'MinimapHUD = class MinimapHUD')
          .replace(/export\s+\{[^}]+\};?\s*/g, '').replace(/export\s+default\s+[^;]+;?\s*/g, '');

        global.window = { isGamePaused: false };
        global.document = { getElementById: () => null };
        var MinimapHUD;
        eval(code);

        const hud = new MinimapHUD('minimap-canvas');
        hud.init();
        hud.setMap(new Uint8Array(60 * 45).fill(1), 60, 45, 1, null);

        let throttledRenders = 0;
        hud.render = () => { throttledRenders++; };
        for (let frame = 0; frame < 120; frame++) {
          hud.update(frame * 8.3333, 10, 10, 0);
        }

        global.window.isGamePaused = true;
        let pauseRenders = 0;
        hud.render = () => { pauseRenders++; };
        for (let i = 0; i < 50; i++) {
          hud.update(1000 + i * 100, 10, 10, 0);
        }
        global.window.isGamePaused = false;

        const fog = new Uint8Array(60 * 45);
        fog[10 * 60 + 10] = 1;
        global.fogGrid = fog;
        global.window.fogGrid = fog;
        const metadata = {
          pois: [{ x: 5, y: 5 }, { x: 10, y: 10 }],
          bossGate: { x: 20, y: 20 },
          spawn: { x: 0, y: 0 }
        };
        hud.setMap(new Uint8Array(60 * 45).fill(1), 60, 45, 1, metadata);

        let drawnArcs = [];
        const mockCtx = {
          fillStyle: '', strokeStyle: '', lineWidth: 1,
          beginPath: () => {},
          arc: (x, y, r) => { drawnArcs.push({ x, y, r, fill: mockCtx.fillStyle }); },
          fill: () => {}, stroke: () => {}, moveTo: () => {}, lineTo: () => {}
        };

        global.window.bossGateBreached = false;
        hud.renderIndicators(mockCtx, 0, 0, 0, 1000);
        const lockedGateRed = drawnArcs.some(a => a.fill === '#ef4444');
        const poiArcs = drawnArcs.filter(a => a.fill === '#fbbf24');

        drawnArcs = [];
        global.window.bossGateBreached = true;
        hud.renderIndicators(mockCtx, 0, 0, 0, 1000);
        const unlockedGateGreen = drawnArcs.some(a => a.fill === '#10b981' && a.r === 2.5);

        console.log(JSON.stringify({
          throttledRenders, pauseRenders, lockedGateRed, unlockedGateGreen,
          exploredPoiCount: poiArcs.length
        }));
        r2   r3   Tr4   ©r9   r:   r;   r<   r=   r>   r?   ÚresrA   s      r   rE   z5TestMinimapRuntimeThrottlingAndIndicators.setup_class‡   sP   € ð?ˆõ@ Œ~˜v t¨WÐ5ÀdÐQUÕ[^Õ_cÑ[dÔ[dÐlpÐqÑqÔqˆÝ”*˜Tœ[Ñ)Ô)ˆŒˆˆr   c                 ód   — d| j         d         cxk    rdk    sn J ‚| j         d         dk    sJ ‚dS )z>Verifies 30Hz throttling and pause freeze via Node.js runtime.é   ÚthrottledRendersé   ÚpauseRendersr   N©r|   r   s    r   Ú!test_battery_throttling_and_pausezKTestMinimapRuntimeThrottlingAndIndicators.test_battery_throttling_and_pauseÌ   sM   € à�T”XÐ0Ô1Ð7Ð7Ò7Ð7°RÒ7Ð7Ð7Ð7Ð7Ð7ØŒx˜Ô'¨1Ò,Ð,Ð,Ð,Ð,Ð,r   c                 óp   — | j         d         du sJ ‚| j         d         du sJ ‚| j         d         dk    sJ ‚dS )zDVerifies Boss Gate colors and POI fog filtering via Node.js runtime.ÚlockedGateRedTÚunlockedGateGreenÚexploredPoiCountrY   Nr‚   r   s    r   Útest_indicator_state_logiczDTestMinimapRuntimeThrottlingAndIndicators.test_indicator_state_logicÑ   sR   € àŒx˜Ô(¨DÐ0Ð0Ð0Ð0ØŒxÐ+Ô,°Ð4Ð4Ð4Ð4ØŒxÐ*Ô+¨qÒ0Ð0Ð0Ð0Ð0Ð0r   N)r+   r,   r-   rh   rE   rƒ   rˆ   r.   r   r   ry   ry   †   sO   € € € € € ØðB*ð B*ñ „[ðB*ðH-ð -ð -ð
1ð 1ð 1ð 1ð 1r   ry   c                   ó   — e Zd Zd„ Zd„ ZdS )ÚTestEntitySuppressionWiringc                 óh   — t                                d¬¦  «        }d|v rd|v sJ ‚d|v sJ ‚d|v sJ ‚dS )zOVerifies world_renderer.js has drop, waypoint, boss gate and fog overlay hooks.r   r   zdrop.wxúWarFog.getFogStateÚwpFogÚWarFogRendererN)ÚWORLD_RENDERER_JSr   ©r   Úcontents     r   Ú%test_world_renderer_suppression_wiredzATestEntitySuppressionWiring.test_world_renderer_suppression_wiredÙ   s\   € å#×-Ò-°wÐ-Ñ?Ô?ˆØ˜GÐ#Ð#Ð(<ÀÐ(GÐ(GÐ(GÐGØ˜'Ð!Ð!Ð!Ð!Ø 7Ð*Ð*Ð*Ð*Ð*Ð*r   c                 óp   — t                                d¬¦  «        }d|v sJ ‚d|v rd|v sJ ‚d|v rd|v sJ ‚dS )zMVerifies entity_renderer.js suppresses non-visible monsters, NPCs, and props.r   r   ÚpropFogzmob.wxrŒ   znpc.wxN)ÚENTITY_RENDERER_JSr   r�   s     r   Ú&test_entity_renderer_suppression_wiredzBTestEntitySuppressionWiring.test_entity_renderer_suppression_wiredà   sg   € å$×.Ò.¸Ð.Ñ@Ô@ˆØ˜GÐ#Ð#Ð#Ð#Ø˜7Ð"Ð"Ð';¸wÐ'FÐ'FÐ'FÐFØ˜7Ð"Ð"Ð';¸wÐ'FÐ'FÐ'FÐFÐ'FÐ'Fr   N)r+   r,   r-   r’   r–   r.   r   r   rŠ   rŠ   Ø   s7   € € € € € ð+ð +ð +ðGð Gð Gð Gð Gr   rŠ   c                   ó<   — e Zd Zed„ ¦   «         Zd„ Zd„ Zd„ Zd„ ZdS )Ú"TestCombatTargetAntiMaphackRuntimec                 ó¢   — d}t          j        dd|gddt          t          ¦  «        d¬¦  «        }t	          j        |j        ¦  «        | _        d S )Na6  
        const fs = require('fs');
        let code = fs.readFileSync('client/webapp/js/engine/monster_system.js', 'utf8');
        const match = code.match(/function getBestCombatTarget[\s\S]*?\n}/);
        if (!match) throw new Error('getBestCombatTarget not found');
        eval(match[0]);

        const results = {};
        global.window = { WarFog: { getFogState: () => 0 } };
        global.activeMonsters = [{ id: 'm_unexp', wx: 3, wy: 3, hp: 100 }];
        global.window.monster = global.activeMonsters[0];
        results.unexploredTarget = getBestCombatTarget(0, 0, 7.5);
        global.window.WarFog.getFogState = () => 1;
        results.foggedTarget = getBestCombatTarget(0, 0, 7.5);
        global.activeMonsters = [];
        global.window.monster = { id: 'boss_fogged', wx: 3, wy: 3, hp: 100 };
        results.fallbackFoggedTarget = getBestCombatTarget(0, 0, 7.5);
        global.window.WarFog.getFogState = () => 2;
        global.activeMonsters = [{ id: 'm_visible', wx: 3, wy: 3, hp: 100 }];
        global.window.monster = global.activeMonsters[0];
        const resVis = getBestCombatTarget(0, 0, 7.5);
        results.visibleTargetId = resVis ? resVis.id : null;
        global.activeMonsters = [];
        global.window.monster = { id: 'boss_visible', wx: 4, wy: 4, hp: 500 };
        const resFallback = getBestCombatTarget(0, 0, 7.5);
        results.fallbackVisibleId = resFallback ? resFallback.id : null;
        global.window.monster = { id: 'boss_far', wx: 10, wy: 10, hp: 500 };
        results.beyondRangeTarget = getBestCombatTarget(0, 0, 7.5);
        global.activeMonsters = [{ id: 'm_dead', wx: 3, wy: 3, hp: 0 }];
        global.window.monster = { id: 'boss_dead', wx: 3, wy: 3, hp: 0 };
        results.deadTarget = getBestCombatTarget(0, 0, 7.5);
        console.log(JSON.stringify(results));
        r2   r3   Tr4   r{   rA   s      r   rE   z.TestCombatTargetAntiMaphackRuntime.setup_classé   sP   € ð ˆõB Œ~˜v t¨WÐ5ÀdÐQUÕ[^Õ_cÑ[dÔ[dÐlpÐqÑqÔqˆÝ”*˜Tœ[Ñ)Ô)ˆŒˆˆr   c                 ó`   — | j         d         �J ‚| j         d         �J ‚| j         d         �J ‚dS )zBMonsters in unexplored (0) or fogged (1) tiles cannot be targeted.ÚunexploredTargetNÚfoggedTargetÚfallbackFoggedTargetr‚   r   s    r   Ú.test_unexplored_and_fogged_target_returns_nullzQTestCombatTargetAntiMaphackRuntime.test_unexplored_and_fogged_target_returns_null  sC   € àŒxÐ*Ô+Ð3Ð3Ð3ØŒx˜Ô'Ð/Ð/Ð/ØŒxÐ.Ô/Ð7Ð7Ð7Ð7Ð7r   c                 óR   — | j         d         dk    sJ ‚| j         d         dk    sJ ‚dS )z.Visible monsters within maxRange are acquired.ÚvisibleTargetIdÚ	m_visibleÚfallbackVisibleIdÚboss_visibleNr‚   r   s    r   Ú0test_visible_target_within_range_returns_monsterzSTestCombatTargetAntiMaphackRuntime.test_visible_target_within_range_returns_monster  s:   € àŒxÐ)Ô*¨kÒ9Ð9Ð9Ð9ØŒxÐ+Ô,°Ò>Ð>Ð>Ð>Ð>Ð>r   c                 ó$   — | j         d         �J ‚dS )z-Visible monsters beyond maxRange return null.ÚbeyondRangeTargetNr‚   r   s    r   Ú-test_visible_target_beyond_range_returns_nullzPTestCombatTargetAntiMaphackRuntime.test_visible_target_beyond_range_returns_null  s   € àŒxÐ+Ô,Ð4Ð4Ð4Ð4Ð4r   c                 ó$   — | j         d         �J ‚dS )z+Dead monsters (hp <= 0) cannot be targeted.Ú
deadTargetNr‚   r   s    r   Útest_dead_target_ignoredz;TestCombatTargetAntiMaphackRuntime.test_dead_target_ignored  s   € àŒx˜Ô%Ð-Ð-Ð-Ð-Ð-r   N)	r+   r,   r-   rh   rE   rž   r¤   r§   rª   r.   r   r   r˜   r˜   è   sj   € € € € € Øð#*ð #*ñ „[ð#*ðJ8ð 8ð 8ð?ð ?ð ?ð
5ð 5ð 5ð.ð .ð .ð .ð .r   r˜   )Ú__doc__r=   rp   r9   Úpathlibr   ÚpytestÚ__file__ÚresolveÚparentr<   r   r   r   r   r�   r•   ÚMONSTER_SYSTEM_JSr
   r0   rj   ry   rŠ   r˜   r.   r   r   ú<module>r²      sR  ððð ð
 €€€Ø €€€Ø Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø €€€à€tˆH�~„~×ÒÑÔÔ&Ô-Ô4€Ø�H‰_˜xÑ'¨$Ñ.°Ñ5¸ÑD€
Ø˜X‘o¨Ñ0°4Ñ7¸$Ñ>ÐAVÑVÐ Ø˜‘ 8Ñ+¨dÑ2°TÑ9Ð<LÑL€Ø�H‰_˜xÑ'¨,Ñ6€
Ø˜8‘O hÑ.°Ñ5¸Ñ@ÐCVÑVÐ Ø˜H‘_ xÑ/°$Ñ6¸ÑAÐDXÑXÐ Ø˜8‘O hÑ.°Ñ5¸Ñ@ÐCVÑVÐ ð4ð 4ð 4ð 4ð 4ñ 4ô 4ð 4ð,FOð FOð FOð FOð FOñ FOô FOð FOðR:ð :ð :ð :ð :ñ :ô :ð :ð"O1ð O1ð O1ð O1ð O1ñ O1ô O1ð O1ðdGð Gð Gð Gð Gñ Gô Gð Gð 8.ð 8.ð 8.ð 8.ð 8.ñ 8.ô 8.ð 8.ð 8.ð 8.r   