§
    5ç¾j>"  ã                  ó  — d Z ddlmZ ddlZddlmc mZ ddl	Z	ddl
Z
ddlZddlZ e
j        e¦  «                             ¦   «         j        j        j        Zdd„Z G d	„ d
ej        ¦  «        Z G d„ dej        ¦  «        ZdS )a  
Milestone M6 Phase 2 Challenger Empirical Adversarial Stress Suite (Part 2).
Stress Tests:
1. Fog of War and Minimap HUD:
   - Corrupted/truncated Base64 strings, dimension mismatches, malformed encodings.
   - Extreme zoom levels, dynamic window resizing, rapid pause/unpause toggling.
2. AI Pathfinding & Combat Encounter:
   - Massive concurrent monster chase steering across complex obstacles.
   - Multi-pack simultaneous wipes with atomic Boss Gate unlocking.
   - Waypoint safe zone boundary thrashing under pursuit.
é    )ÚannotationsNÚscriptÚstrÚreturnÚdictc                óÚ   — t          j        ddd| gddt          t          ¦  «        d¬¦  «        }d„ |j                             ¦   «         D ¦   «         }t          j        |d         ¦  «        S )zDExecutes a Node.js ES-module snippet and parses the final JSON line.Únodez--input-type=modulez-eT)Úcapture_outputÚtextÚcwdÚcheckc                ó^   — g | ]*}|                      ¦   «         ¯|                      ¦   «         ‘Œ+S © )Ústrip)Ú.0Úlns     úFC:\Projects\FreeExile\tests\unit\test_challenger_m6_2_fog_ai_stress.pyú
<listcomp>z"_run_node_json.<locals>.<listcomp>   s-   € ÐIÐIÐI˜B¸b¿hºh¹j¼jÐIˆR�XŠX‰ZŒZÐIÐIÐIó    éÿÿÿÿ)Ú
subprocessÚrunr   ÚPROJECT_ROOTÚstdoutÚ
splitlinesÚjsonÚloads)r   ÚprocÚliness      r   Ú_run_node_jsonr       sq   € åŒ>Ø	Ð&¨¨fÐ5ØØÝ•ÑÔØðñ ô €Dð JÐI $¤+×"8Ò"8Ñ":Ô":ÐIÑIÔI€EÝŒ:�e˜B”iÑ Ô Ð r   c                  ó*   — e Zd ZdZdd„Zdd„Zdd„ZdS )	Ú#TestChallengerM6FogAndMinimapStresszEAdversarial stress testing of Fog of War persistence and Minimap HUD.r   ÚNonec                ól  — d}t          |¦  «        }|                      |d         ¦  «         |                      |d         ¦  «         |                      |d         ¦  «         |                      |d         ¦  «         |                      |d         ¦  «         |                      |d         ¦  «         dS )	z>Verify robust error recovery against corrupted Base64 strings.ab  
        import { unpackFogBits, initFog, FOG_STATE } from './client/webapp/js/ui/war_fog.js';
        const w = 60, h = 45;
        const grid = initFog(w, h, 'zone_test', 42);

        // 1. Truncated Base64 (empty, 1 char, partial header)
        const t1 = unpackFogBits('', grid, w, h);
        const t2 = unpackFogBits('A', grid, w, h);
        const t3 = unpackFogBits('AAAA', grid, w, h); // w=0, h=0 -> mismatch

        // 2. Corrupted Base64 characters
        const c1 = unpackFogBits('!!!@@@###$$$', grid, w, h);
        const c2 = unpackFogBits('====', grid, w, h);

        // 3. Dimension mismatch (encoded for 10x10, unpack for 60x45)
        const header10x10 = Buffer.from([10, 0, 10, 0, 255]).toString('base64');
        const m1 = unpackFogBits(header10x10, grid, w, h);

        console.log(JSON.stringify({ t1, t2, t3, c1, c2, m1 }));
        Út1Út2Út3Úc1Úc2Úm1N)r    ÚassertFalse©Úselfr   Úress      r   Ú'test_corrupted_and_truncated_base64_fogzKTestChallengerM6FogAndMinimapStress.test_corrupted_and_truncated_base64_fog&   s®   € ðˆõ( ˜VÑ$Ô$ˆØ×Ò˜˜TœÑ#Ô#Ð#Ø×Ò˜˜TœÑ#Ô#Ð#Ø×Ò˜˜TœÑ#Ô#Ð#Ø×Ò˜˜TœÑ#Ô#Ð#Ø×Ò˜˜TœÑ#Ô#Ð#Ø×Ò˜˜TœÑ#Ô#Ð#Ð#Ð#r   c                ó   — d}t          |¦  «        }|                      |d         ¦  «         |                      |d         ¦  «         |                      |d         ¦  «         |                      |d         ¦  «         dS )zHVerify Minimap HUD survives extreme scale, zoom, and 0-dimension states.ao  
        import { MinimapHUD } from './client/webapp/js/ui/minimap_hud.js';
        const hud = new MinimapHUD();

        // 0 dimensions
        hud.setMap(new Uint8Array(0), 0, 0, 1);
        const p0 = hud.worldToMinimap(10, 10);

        // Huge dimensions
        hud.setMap(new Uint8Array(1000 * 1000), 1000, 1000, 1);
        const pHuge = hud.worldToMinimap(500, 500);

        // Out of bounds / extreme coordinates
        const pNeg = hud.worldToMinimap(-9999, -9999);
        const pInf = hud.worldToMinimap(1e6, 1e6);

        console.log(JSON.stringify({
          p0Valid: Number.isFinite(p0.x) && Number.isFinite(p0.y),
          pHugeValid: Number.isFinite(pHuge.x) && Number.isFinite(pHuge.y),
          pNegValid: Number.isFinite(pNeg.x) && Number.isFinite(pNeg.y),
          pInfValid: Number.isFinite(pInf.x) && Number.isFinite(pInf.y)
        }));
        Úp0ValidÚ
pHugeValidÚ	pNegValidÚ	pInfValidN)r    Ú
assertTruer,   s      r   Ú%test_extreme_zoom_and_window_resizingzITestChallengerM6FogAndMinimapStress.test_extreme_zoom_and_window_resizingD   sy   € ðˆõ. ˜VÑ$Ô$ˆØ�Š˜˜IœÑ'Ô'Ð'Ø�Š˜˜LÔ)Ñ*Ô*Ð*Ø�Š˜˜KÔ(Ñ)Ô)Ð)Ø�Š˜˜KÔ(Ñ)Ô)Ð)Ð)Ð)r   c                ó–   — d}t          |¦  «        }|                      |d         d¦  «         |                      |d         ¦  «         dS )zCVerify 1,000 rapid pause toggles do not desynchronize engine state.a"  
        global.isGamePaused = false;
        let toggleCount = 0;
        for (let i = 0; i < 1000; i++) {
          global.isGamePaused = !global.isGamePaused;
          toggleCount++;
        }
        console.log(JSON.stringify({ toggleCount, isPaused: global.isGamePaused }));
        ÚtoggleCountiè  ÚisPausedN)r    ÚassertEqualr+   r,   s      r   Ú test_rapid_pause_unpause_toggleszDTestChallengerM6FogAndMinimapStress.test_rapid_pause_unpause_togglesc   sQ   € ðˆõ ˜VÑ$Ô$ˆØ×Ò˜˜]Ô+¨TÑ2Ô2Ð2Ø×Ò˜˜ZœÑ)Ô)Ð)Ð)Ð)r   N©r   r#   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r/   r6   r;   r   r   r   r"   r"   #   sV   € € € € € ØOÐOð$ð $ð $ð $ð<*ð *ð *ð *ð>*ð *ð *ð *ð *ð *r   r"   c                  ó*   — e Zd ZdZdd„Zdd„Zdd„ZdS )	Ú!TestChallengerM6CombatAndAIStresszMAdversarial stress testing of concurrent AI pathfinding, wipes, and leashing.r   r#   c                ó–   — d}t          |¦  «        }|                      |d         ¦  «         |                      |d         d¦  «         dS )zT100 concurrent monsters steering around U-shaped obstacle must not penetrate or NaN.aK  
        import { steerMonsterChase, findPath } from './client/webapp/js/engine/grid_pathfinder.js';
        global.currentMapWidth = 50;
        global.currentMapHeight = 50;
        const grid = new Uint8Array(50 * 50);
        grid.fill(1); // FLOOR

        // U-shaped wall: x=20..30, y=20 (bottom), x=20, y=10..20 (left), x=30, y=10..20 (right)
        for (let x = 20; x <= 30; x++) grid[20 * 50 + x] = 2;
        for (let y = 10; y <= 20; y++) {
          grid[y * 50 + 20] = 2;
          grid[y * 50 + 30] = 2;
        }
        global.getTileAt = (tx, ty) => (tx < 0 || tx >= 50 || ty < 0 || ty >= 50) ? 2 : grid[ty * 50 + tx];

        // 100 monsters inside U-pocket trying to chase player outside at (25, 5)
        const playerX = 25.5, playerY = 5.5;
        let successSteers = 0;
        let naNEncountered = false;

        for (let i = 0; i < 100; i++) {
          const mob = { wx: 21.0 + (i % 8), wy: 15.0 + Math.floor(i / 20) };
          const steer = steerMonsterChase(mob, playerX, playerY, 0.016, 1.0);
          if (Number.isNaN(steer.vx) || Number.isNaN(steer.vy)) {
            naNEncountered = true;
          }
          if (steer.hasPath || (steer.vx !== 0 || steer.vy !== 0)) {
            successSteers++;
          }
        }

        console.log(JSON.stringify({ total: 100, successSteers, naNEncountered }));
        ÚnaNEncounteredÚsuccessSteerséP   N)r    r+   ÚassertGreaterr,   s      r   Ú.test_massive_concurrent_monster_chase_steeringzPTestChallengerM6CombatAndAIStress.test_massive_concurrent_monster_chase_steeringv   sT   € ð ˆõB ˜VÑ$Ô$ˆØ×Ò˜Ð-Ô.Ñ/Ô/Ð/Ø×Ò˜3˜Ô/°Ñ4Ô4Ð4Ð4Ð4r   c                ó  — d}t          |¦  «        }|                      |d         d¦  «         |                      |d         d¦  «         |                      |d         d¦  «         |                      |d         d	¦  «         d
S )zRSimultaneous kill of 5 packs must atomically increment kills and unlock Boss Gate.aÏ  
        import { BossGateController } from './client/webapp/js/engine/boss_gate_controller.js';
        import { registerMonsterKill } from './client/webapp/js/engine/monster_pack_system.js';
        const z = 'zone_tang_kiem_nhai';
        global.currentMapGrid = new Uint8Array(60 * 45);
        global.currentMapWidth = 60;
        global.currentMapGrid[15 * 60 + 20] = 10; // BOSS_GATE

        global.zoneEncounterProgress = {
          [z]: { zoneId: z, totalPacks: 5, alivePacks: 5, clearedPacks: 0, totalKills: 0, requiredPacks: 5 }
        };
        const bgc = new BossGateController({ gateX: 20, gateY: 15, zoneId: z, requiredKills: 5 });
        global.bossGateController = bgc;

        const mobs = [];
        for (let p = 0; p < 5; p++) {
          mobs.push({ packId: 'pack_' + p, hp: 0 });
        }

        // Wipe all 5 packs simultaneously
        for (let m of mobs) {
          registerMonsterKill(m, mobs, z);
        }

        console.log(JSON.stringify({
          clearedPacks: global.zoneEncounterProgress[z].clearedPacks,
          alivePacks: global.zoneEncounterProgress[z].alivePacks,
          gateState: bgc.state,
          tileAfter: global.currentMapGrid[15 * 60 + 20]
        }));
        ÚclearedPacksé   Ú
alivePacksr   Ú	gateStateÚUNLOCKEDÚ	tileAfteré   N©r    r:   r,   s      r   Ú"test_multi_pack_simultaneous_wipeszDTestChallengerM6CombatAndAIStress.test_multi_pack_simultaneous_wipes�   sŠ   € ðˆõ> ˜VÑ$Ô$ˆØ×Ò˜˜^Ô,¨aÑ0Ô0Ð0Ø×Ò˜˜\Ô*¨AÑ.Ô.Ð.Ø×Ò˜˜[Ô)¨:Ñ6Ô6Ð6Ø×Ò˜˜[Ô)¨1Ñ-Ô-Ð-Ð-Ð-r   c                ó˜   — d}t          |¦  «        }|                      |d         d¦  «         |                      |d         d¦  «         dS )zNRapidly crossing r=8.0 safe boundary 1,000 times under chase preserves sanity.a>  
        import { isInWaypointSafeRadius } from './client/webapp/js/data/wilderness_zone_packs.js';
        const z = 'zone_tang_kiem_nhai';
        let safeCount = 0;
        let unsafeCount = 0;

        for (let i = 0; i < 1000; i++) {
          // Thrash between 7.95 (inside safe) and 8.05 (outside safe)
          const dist = (i % 2 === 0) ? 7.95 : 8.05;
          const isSafe = isInWaypointSafeRadius(dist, 0.0, z);
          if (isSafe) safeCount++;
          else unsafeCount++;
        }

        console.log(JSON.stringify({ safeCount, unsafeCount }));
        Ú	safeCountiô  ÚunsafeCountNrQ   r,   s      r   Ú*test_waypoint_safe_zone_boundary_thrashingzLTestChallengerM6CombatAndAIStress.test_waypoint_safe_zone_boundary_thrashingÄ   sT   € ðˆõ  ˜VÑ$Ô$ˆØ×Ò˜˜[Ô)¨3Ñ/Ô/Ð/Ø×Ò˜˜]Ô+¨SÑ1Ô1Ð1Ð1Ð1r   Nr<   )r=   r>   r?   r@   rH   rR   rV   r   r   r   rB   rB   s   sX   € € € € € ØWÐWð%5ð %5ð %5ð %5ðN%.ð %.ð %.ð %.ðN2ð 2ð 2ð 2ð 2ð 2r   rB   )r   r   r   r   )r@   Ú
__future__r   ÚbuiltinsÚ@py_builtinsÚ_pytest.assertion.rewriteÚ	assertionÚrewriteÚ
@pytest_arr   Úpathlibr   ÚunittestÚPathÚ__file__ÚresolveÚparentr   r    ÚTestCaser"   rB   r   r   r   ú<module>re      s  ðð
ð 
ð #Ð "Ð "Ð "Ð "Ð "Ø  € € € € € € € € € € € € €€€€Ø €€€Ø Ð Ð Ð Ø €€€àˆwŒ|˜HÑ%Ô%×-Ò-Ñ/Ô/Ô6Ô=ÔD€ð
!ð 
!ð 
!ð 
!ðM*ð M*ð M*ð M*ð M*¨(Ô*;ñ M*ô M*ð M*ð`e2ð e2ð e2ð e2ð e2¨Ô(9ñ e2ô e2ð e2ð e2ð e2r   