From aa33515f37ffe2644f270279055ac76841a334b3 Mon Sep 17 00:00:00 2001 From: goetchstone Date: Thu, 9 Jul 2026 15:00:31 -0400 Subject: [PATCH] =?UTF-8?q?Map:=2064u=20grid=20=E2=80=94=20pixel-exact=20l?= =?UTF-8?q?ane=20deduction=20from=20the=20full-res=20pathing=20map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner-directed: "the full res map is accurate if you can deduce the lanes correctly." Measured, the original is authored on WC3's 64u grid (at 64u cells the wpm quantizes with ZERO fractional cells), while real channels bottom out at 128u and real walls at 64u — widths a 128u grid provably cannot represent when unaligned (channels lost) or sub-cell (walls gone, lanes merged). The emitted grid is now 64u (162x226, EMIT_SUBDIV=2): a pixel-exact copy of the original's enforced ship-pathing. - terrain.py: geometry subdivision; land-biased tie sampler (dormant here — the map aligns — but correct for any future non-aligned source) + a provably-merge-safe straddled-channel restore pass; island/access constants scaled (moat cores 9x9, cycleLen 40, single entrances hold); NEW HARD GATE G6: pairwise water-connectivity of all 22 gameplay anchors on the emitted grid must equal the raw 32u wpm's — any lost channel or false merge fails the build. - Sim: cell-denominated steering constants converted to world units (dock-approach hand-off 128u, nav local-goal basin 768u, derived from mask cellSize) so grid resolution is a data property, not behavior. - Probes: bot-vs-bot long-run window 6000->9000 ticks — on the true tighter lanes the first tower-chipping leaker lands ~tick 5000 (verified steady: 3.5k tower hits by tick 24000, 10 captains); all other windows unchanged. - Owner-verified fixes hold at 64u: NE/east lanes separate; the SE Library docks bottom-lane SW; corner-reach probe still reaches every quadrant. Full suite 1,245 green (trader haul, creep clash/grind, AI siege, fog/ cliffs, determinism replays); lint clean. Co-Authored-By: Claude Fable 5 --- data/json/terrain.json | 2 +- packages/core/src/sim/movement.ts | 21 +- packages/core/src/sim/types.ts | 9 +- packages/core/test/fog.test.ts | 2 +- .../core/test/terrain-integration.test.ts | 11 +- packages/server/test/ai-match.test.ts | 7 +- tools/extractor/terrain.py | 228 +++++++++++++++++- 7 files changed, 256 insertions(+), 24 deletions(-) diff --git a/data/json/terrain.json b/data/json/terrain.json index dcc7d78..4a68760 100644 --- a/data/json/terrain.json +++ b/data/json/terrain.json @@ -1 +1 @@ -{"_comment":"Static land/water mask. SAILABLE WATER = the map's own PATHING MAP (data/extracted/war3map.wpm, bit 0x40 = no-water, 4x4 32u subcells per 128u tile, majority vote): the engine's truth for where ships could actually sail in the original. This REPLACES the minimap NON-BLUE colour key (~0.66 water), which over-watered: green 'shallow' paint is often visually-wet-but-UNSAILABLE, merging lanes the real map separates (owner-reported 2026-07-08: the NE lane must NOT merge into the east-edge lane; the wpm separates them). The wpm reads ~0.55 sailable over the playable crop and all 26 known anchors (HQs, harbours, spawns, lane waypoints) sit sailable. The ONLY additions on top of the raw wpm grid are MINIMAL 1-cell connectivity necks (so every shop + dock/spawn reaches the sea and the two bases stay water-connected) PLUS the two owner-approved WEST sail-around island moats (Swedish Lumber Mill, Goblin Potion Dealer: land core + thin 1-cell navigable ring + EXACTLY ONE entrance; deterministic post-step). water=true is ship-navigable. The OPTIONAL `depth` field (0=land,1=deep,2=shallow,3=pink) is additive render metadata the SIM IGNORES; the minimap colours band each FINAL water cell so a client paints the water shades like the original. Regenerate with: make terrain (tools/extractor/terrain.py; pure stdlib, reads wpm+w3e+PNG, no venv). yOrientation 'top-down' means rle row 0 is the NORTH (max-Y) edge.","source":"data/extracted/war3map.wpm (pathing map, bit 0x40=no-water; THE sailability authority) + data/extracted/war3map.w3e (grid geometry) + data/reference/war3mapMap.png (depth render metadata only)","target":"the original map's enforced ship-pathing (wpm no-water), cropped to the playable rectangle","rule":"water = wpm NOT-no-water majority over the 16 32u subcells per 128u tile, MINUS singleton speckle, PLUS minimal 1-cell connectivity necks (Dijkstra cost 1 water / 30 land) for shops, docks/spawns and base-to-base, PLUS the two carved WEST sail-around island moats (compact land core + thin 1-cell water ring + exactly one entrance) around the Swedish Lumber Mill and Goblin Potion Dealer shops. depth sub-classifies FINAL water cells from the minimap colours for RENDER only: DEEP (R-B>35 AND R>=G), PINK (R>150 AND B>120 AND R-G>15), else SHALLOW; a water cell whose pixels read land-blue falls back to SHALLOW.","playableBounds":{"minX":-5376.0,"minY":-7424.0,"maxX":4864.0,"maxY":6912.0},"bounds":{"minX":-5440.0,"minY":-7488.0,"maxX":4928.0,"maxY":6976.0},"cols":81,"rows":113,"cellSizeX":128.0,"cellSizeY":128.0,"yOrientation":"top-down","yOrientationNote":"rle row 0 = max-Y (north); last row = min-Y (south). col 0 = min-X (west). col=floor((x-minX)/cellSizeX), row=floor((maxY-y)/cellSizeY). The minimap is north-up; tiles are sampled at their world centers via the content-box registration, emit row 0 = north, matching sim isWater.","rleFormat":"water[r] = [leadingValue, run0, run1, ...]; runs alternate from leadingValue (0=land,1=water) and sum to cols.","depthRleFormat":"OPTIONAL render metadata, the sim IGNORES it. depth[r] = [value0, run0, value1, run1, ...] (explicit value per run; values 0=land, 1=deep, 2=shallow, 3=pink; runs sum to cols). depth[r][col]>0 IFF water[r][col]==1 (exactly consistent with the authoritative `water` mask). Carved necks/moats with no minimap colour emit as shallow (2).","waterFraction":0.552824,"validation":{"hqsOnWater":"2/2","harboursOnWater":"4/4","playerSpawnsOnWater":"12/12","lanes":["south-west: spawnWater=True connToEnemyHQ=True","south-east: spawnWater=True connToEnemyHQ=True","north-west: spawnWater=True connToEnemyHQ=True","north-east: spawnWater=True connToEnemyHQ=True"],"basesConnected":true,"shopsReachable":"16/16","shopReach":["Swedish Lumber Mill: reachable=True nearestSeaCells=1","Goblin Potion Dealer: reachable=True nearestSeaCells=1","Elven Library: reachable=True nearestSeaCells=0","Ship Wreck: reachable=True nearestSeaCells=0","Elven Library: reachable=True nearestSeaCells=2","Ale Brewery: reachable=True nearestSeaCells=1","Equipment Merchant: reachable=True nearestSeaCells=1","Heavy Weapons Merchant: reachable=True nearestSeaCells=1","Weapons Merchant: reachable=True nearestSeaCells=2","Equipment Merchant: reachable=True nearestSeaCells=1","Bill (Trade Master): reachable=True nearestSeaCells=1","Pirate Boat Merchant: reachable=True nearestSeaCells=0","Will (Trade Master): reachable=True nearestSeaCells=2","Weapons Merchant: reachable=True nearestSeaCells=2","Heavy Weapons Merchant: reachable=True nearestSeaCells=2","Goblin Bomb Shop: reachable=True nearestSeaCells=0"],"repairRegionsReachable":"2/2","repairRegionReach":["Repair_Station_South: reachable=True","Repair_Station_North: reachable=True"],"waterFraction":0.5528,"eastThirdWaterRun":"median=4.0 mean=5.40 count=248","bottomRightWaterRun":"median=3.0 mean=3.77 count=90","necks":{"basePlatformCellsAdded":0,"connectivityNeckCellsAdded":0,"shopNeckCellsAdded":2,"repairRegionNeckCellsAdded":0,"waterFractionBefore":0.5527,"waterFractionAfter":0.5529,"shopsReachable":"16/16","repairRegionsReachable":"2/2","singletonCellsDropped":4,"westIslandLoops":{"Swedish Lumber Mill":{"shopCell":[6,61],"anchor":[4,61],"shopInCore":true,"shopInRing":false,"shopOnLand":true,"cycleLen":24,"entranceCells":[[4,65]],"entrances":1,"cellsChanged":44},"Goblin Potion Dealer":{"shopCell":[3,96],"anchor":[3,94],"shopInCore":true,"shopInRing":false,"shopOnLand":true,"cycleLen":24,"entranceCells":[[0,98]],"entrances":1,"cellsChanged":29},"waterFractionBefore":0.5529,"waterFractionAfter":0.5528,"totalCellsChanged":73}},"sideRoutes":{"westLumberMillIsland":{"islandLandCells":25,"ringWaterCells":20,"entrances":1},"westGoblinPotionIsland":{"islandLandCells":25,"ringWaterCells":20,"entrances":1},"eastBreweryWrap":{"brewerySeaReachable":true,"farEastNorthWaterCells":151},"bottomRightWinding":{"tanSegments":88,"tanMaxRun":17,"windingNotBlob":false}},"wpmAgreement":0.9918,"wpmConfusion":"agree=0.9918 ours-only(necks/moats)=0.0042 ref-only(denoised)=0.004","depthSplitLandDeepShallowPink":[0.4472,0.2434,0.3007,0.0087],"colourKeyAgreement":0.6793},"water":[[0,81],[0,36,2,43],[0,35,4,42],[0,28,3,4,4,42],[0,23,2,3,11,42],[0,23,16,42],[0,24,18,23,3,13],[0,28,15,20,6,12],[0,28,14,19,10,10],[0,28,11,22,3,5,2,10],[0,28,10,23,2,7,2,9],[0,28,10,23,2,7,2,9],[0,4,3,15,16,3,5,10,1,4,2,8,2,8],[0,3,5,13,26,3,2,3,3,3,2,8,2,6,1,1],[0,3,6,11,28,2,14,4,5,4,4],[0,4,14,1,29,1,15,3,6,3,5],[0,4,14,1,44,4,14],[0,6,58,2,14,1],[0,6,51,5,8,2,9],[0,6,8,1,37,1,1,9,7,2,8,1],[1,2,4,43,15,6,3,5,3],[1,49,6,6,20],[1,15,1,33,6,11,15],[0,1,4,1,9,1,33,6,15,10,1],[0,1,4,1,8,2,17,5,2,1,8,4,7,2,10,6,3],[1,9,7,15,7,2,1,8,3,8,9,4,4,3,1],[1,9,10,14,5,11,3,7,12,8,2],[1,9,10,14,6,10,3,6,14,5,4],[1,9,7,2,1,13,6,3,1,8,2,5,4,2,5,1,4,4,4],[1,7,9,2,1,12,7,3,1,8,2,5,3,3,4,4,10],[1,7,9,15,2,24,1,6,3,5,9],[0,3,8,6,22,1,33,8],[0,5,8,4,59,5],[0,6,9,2,14,2,44,4],[0,10,21,3,2,4,38,3],[0,10,22,2,2,3,39,3],[0,11,66,4],[0,16,60,5],[0,16,60,5],[0,6,3,8,33,2,23,6],[0,4,7,7,32,4,3,2,2,2,7,11],[0,3,10,6,31,2,5,24],[0,2,3,2,6,7,11,1,1,3,2,1,18,24],[1,5,2,5,7,12,5,21,20,1,3],[0,1,4,2,4,8,13,4,20,3,2,10,1,3,4,2],[0,1,3,3,5,7,32,2,3,3,2,5,13,2],[0,2,2,3,6,6,32,2,3,10,12,3],[0,2,12,3,34,2,3,11,11,3],[0,3,7,2,2,2,16,4,15,1,5,10,11,3],[0,4,10,3,16,3,3,1,16,5,2,4,12,2],[0,4,10,6,13,3,3,2,14,6,3,3,4,2,5,3],[0,2,13,5,10,6,3,2,13,6,5,2,3,5,3,3],[1,16,4,9,7,3,2,11,5,8,3,1,6,6],[0,1,2,2,4,3,5,3,8,6,6,1,10,5,9,11,5],[0,2,1,2,3,6,4,3,7,5,6,2,10,5,10,12,3],[0,5,3,6,4,3,8,1,9,2,25,5,2,5,2,1],[0,5,3,7,4,2,18,2,25,3,5,2,4,1],[0,15,4,2,17,2,26,2,12,1],[0,1,7,7,4,2,16,3,11,3,12,3,10,2],[0,1,1,5,1,6,15,1,21,5,10,4,8,3],[0,1,1,5,1,6,15,1,20,6,3,3,5,3,7,4],[0,1,1,5,1,6,18,5,13,12,6,2,3,8],[0,1,1,5,1,5,5,3,11,5,13,12,3,6,2,8],[0,1,1,5,1,5,3,5,10,6,15,9,3,7,2,5,2,1],[0,1,7,4,3,5,11,6,14,10,3,7,3,3,3,1],[0,4,1,7,3,4,12,6,14,9,5,5,4,3,3,1],[0,4,5,3,3,3,13,6,19,3,7,1,6,3,4,1],[1,2,1,7,2,3,4,13,4,20,6,11,3,5],[0,3,2,2,3,2,3,5,13,2,21,5,20],[0,3,2,2,7,6,13,1,22,5,20],[0,3,10,6,32,2,6,1,19,2],[0,3,9,8,30,3,6,2,5,2,11,2],[0,4,2,3,2,9,12,2,16,3,4,5,5,1,5,1,6,1],[0,19,12,3,21,10,8,2,6],[0,18,13,3,2,2,17,12,2,1,4,1,6],[0,13,2,3,18,2,13,2,2,5,1,6,2,1,3,3,1,1,3],[0,13,2,3,19,1,12,3,2,4,3,5,2,8,4],[0,13,37,3,2,4,2,6,2,12],[0,15,18,2,16,2,3,11,2,12],[0,15,16,4,2,3,11,2,3,10,4,11],[0,14,23,3,18,8,4,7,2,2],[0,14,16,1,7,2,18,5,2,2,3,6,4,1],[0,14,16,1,23,8,3,4,2,4,6],[0,13,17,1,23,8,4,3,2,2,8],[0,13,26,1,17,5,4,3,6,5,1],[0,11,28,1,11,3,4,3,5,4,3,5,3],[0,10,6,3,11,8,14,3,3,3,6,4,1,4,5],[0,11,5,3,11,8,15,4,11,7,6],[0,11,19,8,15,4,6,3,3,5,3,4],[0,12,18,8,10,9,5,5,3,2,4,5],[0,12,19,1,15,21,7,6],[1,7,5,2,3,30,5,1,17,4,5,2],[1,1,5,1,5,2,4,30,3,2,18,2,6,1,1],[1,1,5,1,5,36,2,4,10,1,12,1,3],[1,1,5,1,4,37,2,5,7,5,10,1,3],[1,1,5,1,3,45,4,9,8,2,3],[1,1,5,1,3,45,3,11,6,3,3],[1,7,3,41,2,18,4,4,2],[1,1,8,11,1,29,4,10,4,4,2,3,4],[1,1,3,15,3,28,4,6,1,3,4,9,4],[1,18,5,27,4,2,1,2,3,3,4,7,5],[1,1,2,12,9,24,5,3,7,3,4,5,6],[0,12,2,17,9,4,1,8,3,7,4,14],[0,31,9,13,3,7,4,14],[0,31,9,14,4,5,4,14],[0,31,10,14,10,16],[0,28,14,15,7,17],[0,27,16,16,1,21],[0,28,14,39],[0,30,4,47],[0,30,4,47],[0,30,4,47],[0,31,2,48]],"depth":[[0,81],[0,36,2,2,0,43],[0,35,2,3,1,1,0,42],[0,28,2,3,0,4,2,4,0,42],[0,23,2,2,0,3,2,11,0,42],[0,23,2,8,1,5,2,3,0,42],[0,24,2,6,1,8,2,4,0,23,2,3,0,13],[0,28,2,2,1,8,2,5,0,20,2,6,0,12],[0,28,2,2,1,8,2,4,0,19,2,4,1,2,2,4,0,10],[0,28,2,1,1,10,0,22,2,1,1,2,0,5,2,2,0,10],[0,28,1,10,0,23,2,2,0,7,2,2,0,9],[0,28,1,10,0,23,2,2,0,7,2,2,0,9],[0,4,2,1,1,2,0,15,1,16,0,3,1,5,0,10,2,1,0,4,2,2,0,8,2,2,0,8],[0,3,2,2,1,3,0,13,1,26,0,3,2,2,0,3,2,3,0,3,2,2,0,8,2,2,0,6,2,1,0,1],[0,3,2,2,1,4,0,11,1,28,0,2,2,14,0,4,2,5,0,4,2,4],[0,4,2,1,1,5,2,4,1,2,2,2,0,1,1,13,2,1,1,2,2,3,1,1,2,4,1,5,0,1,2,15,0,3,2,6,0,3,2,5],[0,4,2,2,1,1,2,1,1,3,2,5,1,2,0,1,1,12,2,11,1,6,2,15,0,4,2,14],[0,6,2,2,1,1,2,6,1,13,2,14,1,1,2,1,1,3,2,11,1,1,2,2,1,1,2,2,0,2,2,14,0,1],[0,6,2,3,3,1,1,1,3,3,2,1,1,2,2,1,1,10,2,1,1,1,2,7,1,2,2,5,1,3,2,10,0,5,2,8,0,2,2,9],[0,6,2,1,3,2,2,2,3,3,0,1,2,3,1,11,2,8,3,2,1,1,2,2,1,1,2,1,1,4,2,4,0,1,2,1,0,9,2,7,0,2,2,8,0,1],[1,2,0,4,2,1,3,2,2,4,3,2,2,3,1,12,2,7,3,2,1,1,2,1,1,1,2,2,1,4,2,1,0,15,2,6,0,3,2,5,0,3],[1,3,2,4,3,1,2,5,3,3,2,3,1,13,2,5,3,2,1,1,2,5,1,4,0,6,2,1,1,3,2,2,0,20],[1,2,2,11,3,2,0,1,2,3,1,11,2,3,3,2,2,3,1,3,2,2,1,2,2,3,1,1,0,6,2,1,1,3,2,7,0,15],[0,1,1,1,2,3,0,1,2,9,0,1,2,3,1,11,2,3,3,1,1,1,2,3,1,2,2,4,1,2,2,3,0,6,1,1,2,1,1,2,2,11,0,10,2,1],[0,1,1,1,2,3,0,1,2,8,0,2,2,3,1,11,2,3,0,5,2,2,0,1,2,6,1,1,2,1,0,4,2,2,1,2,2,1,1,1,2,1,0,2,2,10,0,6,2,3],[2,9,0,7,1,1,2,2,1,11,2,1,0,7,2,2,0,1,2,4,1,4,0,3,2,3,1,4,2,1,0,9,2,4,0,4,2,3,0,1],[2,9,0,10,1,11,2,3,0,5,2,7,1,4,0,3,1,7,0,12,1,3,2,5,0,2],[2,8,1,1,0,10,1,11,2,3,0,6,2,5,1,5,0,3,1,6,0,14,1,3,2,2,0,4],[2,4,1,5,0,7,1,2,0,1,1,11,2,2,0,6,2,3,0,1,2,1,1,7,0,2,1,5,0,4,1,2,0,5,1,1,0,4,1,4,0,4],[2,4,3,3,0,9,1,2,0,1,1,11,2,1,0,7,2,3,0,1,1,8,0,2,1,5,0,3,1,3,0,4,1,4,0,10],[2,7,0,9,1,14,2,1,0,2,1,1,2,7,1,16,0,1,1,6,0,3,1,5,0,9],[0,3,2,8,0,6,1,14,2,2,1,2,2,1,1,1,2,1,1,1,0,1,2,1,1,32,0,8],[0,5,2,8,0,4,1,14,2,2,1,2,2,5,1,11,2,3,1,1,2,3,1,2,2,1,1,15,0,5],[0,6,2,9,0,2,1,14,0,2,1,1,2,2,1,16,2,6,1,2,2,1,1,16,0,4],[0,10,2,9,1,12,0,3,2,1,1,1,0,4,1,10,2,11,1,1,2,1,1,3,2,6,1,2,2,2,1,2,0,3],[0,10,2,10,1,11,2,1,0,2,2,1,1,1,0,3,1,11,2,28,0,3],[0,11,2,9,1,10,2,5,1,14,2,1,1,1,2,7,1,1,2,18,0,4],[0,16,2,5,1,7,2,7,1,3,2,6,1,5,2,27,0,5],[0,16,2,5,1,7,2,14,1,1,2,1,1,5,2,27,0,5],[0,6,2,3,0,8,2,2,1,9,2,15,1,7,0,2,2,23,0,6],[0,4,2,7,0,7,1,12,2,13,1,7,0,4,2,3,0,2,2,2,0,2,3,3,2,4,0,11],[0,3,2,2,1,1,2,1,1,1,2,5,0,6,1,12,2,11,1,8,0,2,2,5,0,24],[0,2,2,3,0,2,2,6,0,7,1,11,0,1,2,1,0,3,2,2,0,1,2,2,1,11,2,5,0,24],[2,5,0,2,2,5,0,7,1,12,0,5,2,6,1,10,2,5,0,20,2,1,0,3],[0,1,1,1,2,3,0,2,2,4,0,8,1,12,2,1,0,4,2,7,1,9,2,4,0,3,2,2,0,10,2,1,0,3,2,4,0,2],[0,1,2,3,0,3,2,5,0,7,1,11,2,1,1,1,2,11,1,8,0,2,2,3,0,3,2,2,0,5,2,13,0,2],[0,2,2,2,0,3,1,1,2,5,0,6,1,10,2,8,1,1,2,4,1,9,0,2,2,2,1,1,0,10,2,12,0,3],[0,2,2,4,1,4,2,4,0,3,2,2,1,8,2,10,1,1,2,6,1,7,0,2,2,3,0,11,2,11,0,3],[0,3,2,4,1,3,0,2,2,2,0,2,2,3,1,8,2,5,0,4,2,7,1,8,0,1,2,5,0,10,2,4,3,1,2,6,0,3],[0,4,2,3,1,3,2,4,0,3,2,3,1,7,2,6,0,3,3,1,1,2,0,1,2,3,1,1,2,1,1,2,2,1,1,3,2,5,0,5,2,2,0,4,2,4,3,1,2,7,0,2],[0,4,2,10,0,6,1,7,2,5,3,1,0,3,1,3,0,2,2,8,1,2,2,4,0,6,1,2,2,1,0,3,1,1,2,3,0,2,2,5,0,3],[0,2,2,13,0,5,2,1,1,6,2,3,0,6,1,3,0,2,2,8,1,2,2,3,0,6,1,4,2,1,0,2,1,1,2,2,0,5,2,3,0,3],[3,1,1,3,2,12,0,4,2,1,1,5,2,1,1,2,0,7,1,2,2,1,0,2,2,6,1,4,2,1,0,5,2,1,1,7,0,3,2,1,0,6,2,6],[0,1,1,2,0,2,2,4,0,3,2,5,0,3,2,1,1,5,2,1,1,1,0,6,1,4,2,2,0,1,2,6,1,3,2,1,0,5,1,9,0,11,2,5],[0,2,1,1,0,2,2,3,0,6,2,4,0,3,1,5,2,2,0,5,1,4,2,2,0,2,2,4,1,6,0,5,1,8,2,2,0,12,2,2,1,1],[0,5,2,3,0,6,2,4,0,3,1,5,2,3,0,1,1,7,2,2,0,2,2,1,1,9,2,5,1,8,2,2,0,5,2,2,0,5,1,1,2,1,0,1],[0,5,3,1,2,2,0,7,2,4,0,2,1,5,2,4,1,6,2,3,0,2,2,2,1,8,2,6,3,1,1,5,2,1,1,2,0,3,2,5,0,2,1,2,2,2,0,1],[0,15,2,4,0,2,1,5,2,5,1,7,0,2,2,3,1,9,2,6,1,5,2,3,0,2,2,12,0,1],[0,1,2,1,3,5,2,1,0,7,2,4,0,2,1,5,2,4,3,1,1,3,2,3,0,3,2,3,1,8,0,3,2,6,1,2,2,4,0,3,2,10,0,2],[0,1,2,1,0,5,3,1,0,6,2,6,1,8,2,1,0,1,3,4,2,9,1,8,0,5,2,10,0,4,2,8,0,3],[0,1,1,1,0,5,3,1,0,6,2,6,1,9,0,1,2,1,1,2,3,1,2,9,1,7,0,6,2,3,0,3,2,5,0,3,2,7,0,4],[0,1,1,1,0,5,2,1,0,6,2,6,1,11,2,1,0,5,2,6,1,7,0,12,2,4,1,1,2,1,0,2,2,3,0,8],[0,1,1,1,0,5,2,1,0,5,2,5,0,3,1,10,3,1,0,5,2,6,1,7,0,12,2,2,3,1,0,6,2,2,0,8],[0,1,1,1,0,5,2,1,0,5,2,3,0,5,1,9,3,1,0,6,3,2,2,4,1,9,0,9,2,3,0,7,2,2,0,5,2,2,0,1],[0,1,1,4,3,2,2,1,0,4,2,3,0,5,1,10,3,1,0,6,3,2,2,4,1,8,0,10,2,3,0,7,2,3,0,3,2,3,0,1],[0,4,1,1,0,7,2,3,0,4,2,1,1,10,2,1,0,6,3,1,2,5,1,8,0,9,2,5,0,5,2,4,0,3,2,3,0,1],[0,4,1,3,2,2,0,3,2,3,0,3,2,3,1,8,2,2,0,6,2,6,1,9,2,4,0,3,2,6,1,1,0,1,2,6,0,3,2,4,0,1],[1,2,0,1,3,1,1,3,2,2,3,1,0,2,2,3,0,4,2,3,1,8,2,2,0,4,2,7,1,4,2,1,1,5,2,3,0,6,2,3,1,3,2,1,1,2,2,2,0,3,2,5],[0,3,3,1,1,1,0,2,1,1,2,1,3,1,0,2,2,3,0,5,2,3,1,9,2,1,0,2,2,1,1,10,2,2,1,5,2,3,0,5,2,5,1,3,2,12],[0,3,3,1,2,1,0,2,1,1,3,1,2,5,0,6,2,2,1,9,2,2,0,1,2,1,1,12,2,2,1,4,2,3,0,5,2,4,1,4,2,12],[0,3,2,3,1,3,2,4,0,6,2,3,1,8,2,5,1,13,2,1,1,2,0,2,2,2,1,1,2,3,0,1,1,1,2,8,1,1,2,9,0,2],[0,3,2,3,1,2,3,1,2,3,0,8,2,2,1,8,2,5,1,6,2,1,1,8,0,3,2,6,0,2,2,5,0,2,2,6,1,2,2,3,0,2],[0,4,2,2,0,3,2,2,0,9,2,2,1,6,2,4,0,2,2,2,1,2,2,5,1,7,0,3,2,4,0,5,2,5,0,1,2,4,1,1,0,1,2,1,1,2,2,3,0,1],[0,19,2,2,1,7,2,3,0,3,2,3,1,1,2,6,1,6,2,5,0,10,2,8,0,2,1,3,2,2,3,1],[0,18,2,2,1,9,2,2,0,3,2,2,0,2,2,5,1,8,2,4,0,12,2,2,0,1,2,4,0,1,1,2,3,1,2,3],[0,13,2,2,0,3,2,1,1,11,2,1,1,1,2,4,0,2,2,3,1,9,2,1,0,2,2,2,0,5,2,1,0,6,2,2,0,1,2,3,0,3,2,1,0,1,2,3],[0,13,2,2,0,3,1,12,2,1,1,1,2,5,0,1,2,3,1,9,0,3,2,2,0,4,2,3,0,5,2,2,0,8,2,4],[0,13,2,4,1,1,2,2,1,9,2,12,1,9,0,3,2,2,0,4,3,2,0,6,2,2,0,12],[0,15,1,3,2,2,1,7,2,6,0,2,2,6,1,10,0,2,2,3,0,11,2,2,0,12],[0,15,1,3,2,3,1,6,2,4,0,4,2,2,0,3,2,1,1,1,2,1,1,8,0,2,2,3,0,10,2,4,0,11],[0,14,2,1,1,3,2,3,1,6,2,1,1,1,2,8,0,3,2,3,1,8,2,7,0,8,2,4,0,7,2,2,0,2],[0,14,2,1,1,12,2,3,0,1,2,1,1,6,0,2,2,3,1,8,2,7,0,5,2,2,0,2,2,3,0,6,2,4,0,1],[0,14,2,1,1,13,2,2,0,1,2,1,1,6,2,5,1,8,2,3,0,8,2,3,0,4,2,2,0,4,2,6],[0,13,2,3,1,12,2,2,0,1,2,1,1,6,2,5,1,7,2,3,1,1,0,8,2,4,0,3,2,2,0,2,2,8],[0,13,1,1,2,3,1,11,2,1,1,1,2,1,1,7,2,1,0,1,2,2,1,7,2,8,0,5,2,4,0,3,2,6,0,5,2,1],[0,11,2,1,1,2,2,4,1,11,2,3,1,6,2,1,0,1,2,1,1,8,2,2,0,3,2,4,0,3,2,5,0,4,2,3,0,5,2,3],[0,10,2,1,1,2,2,3,0,3,1,10,2,1,0,8,2,3,1,10,2,1,0,3,2,3,0,3,2,6,0,4,2,1,0,4,2,5],[0,11,1,2,2,3,0,3,1,9,2,2,0,8,2,3,1,5,2,1,1,3,2,2,1,1,0,4,2,11,0,7,2,6],[0,11,1,3,2,5,1,8,2,3,0,8,2,3,1,4,2,3,1,2,2,3,0,4,2,6,0,3,2,3,0,5,2,3,0,4],[0,12,1,5,2,2,1,9,2,2,0,8,2,2,1,5,2,3,0,9,2,3,1,1,2,1,0,5,2,3,0,2,2,4,0,5],[0,12,1,16,2,3,0,1,2,8,1,5,2,2,0,21,2,7,0,6],[2,7,0,5,1,2,0,3,1,12,2,11,1,5,2,2,0,5,2,1,0,17,2,4,0,5,2,2],[2,1,0,5,2,1,0,5,1,2,0,4,1,12,2,5,1,3,2,2,1,5,2,3,0,3,2,2,0,18,2,2,0,6,2,1,0,1],[2,1,0,5,2,1,0,5,3,1,2,4,1,13,2,4,1,4,2,2,1,6,2,2,0,2,2,4,0,10,2,1,0,12,2,1,0,3],[2,1,0,5,2,1,0,4,2,8,1,16,2,2,1,10,2,1,0,2,2,5,0,7,2,5,0,10,2,1,0,3],[2,1,0,5,2,1,0,3,2,8,1,29,2,1,1,1,2,5,1,1,0,4,2,9,0,8,2,2,0,3],[2,1,0,5,2,1,0,3,2,5,1,32,2,8,0,3,2,11,0,6,2,3,0,3],[2,1,1,3,2,3,0,3,2,5,1,9,2,2,1,22,2,3,0,2,2,18,0,4,2,4,0,2],[3,1,0,8,2,5,1,6,0,1,1,3,2,2,1,1,2,2,1,21,0,4,2,1,1,2,2,7,0,4,2,2,1,2,0,2,1,1,2,2,0,4],[1,1,0,3,2,5,1,1,2,3,1,5,2,1,0,3,2,1,1,1,2,6,1,20,0,4,2,1,1,1,2,4,0,1,2,3,0,4,2,6,1,1,2,2,0,4],[3,1,1,1,2,7,1,2,2,2,1,4,2,1,0,5,2,9,1,18,0,4,2,2,0,1,2,2,0,3,2,3,0,4,2,5,1,2,0,5],[2,1,0,2,2,6,1,6,0,9,2,9,1,15,0,5,2,3,0,7,2,3,0,4,2,4,1,1,0,6],[0,12,1,1,2,1,0,17,2,7,1,2,0,4,1,1,0,8,2,3,0,7,2,4,0,14],[0,31,2,8,1,1,0,13,2,3,0,7,2,4,0,14],[0,31,2,9,0,14,2,4,0,5,2,4,0,14],[0,31,2,10,0,14,2,10,0,16],[0,28,2,14,0,15,2,7,0,17],[0,27,3,1,2,15,0,16,2,1,0,21],[0,28,2,14,0,39],[0,30,2,4,0,47],[0,30,2,4,0,47],[0,30,2,4,0,47],[0,31,1,1,2,1,0,48]]} +{"_comment":"Static land/water mask. SAILABLE WATER = the map's own PATHING MAP (data/extracted/war3map.wpm, bit 0x40 = no-water, 4x4 32u subcells per 128u tile, majority vote): the engine's truth for where ships could actually sail in the original. This REPLACES the minimap NON-BLUE colour key (~0.66 water), which over-watered: green 'shallow' paint is often visually-wet-but-UNSAILABLE, merging lanes the real map separates (owner-reported 2026-07-08: the NE lane must NOT merge into the east-edge lane; the wpm separates them). The wpm reads ~0.55 sailable over the playable crop and all 26 known anchors (HQs, harbours, spawns, lane waypoints) sit sailable. The ONLY additions on top of the raw wpm grid are MINIMAL 1-cell connectivity necks (so every shop + dock/spawn reaches the sea and the two bases stay water-connected) PLUS the two owner-approved WEST sail-around island moats (Swedish Lumber Mill, Goblin Potion Dealer: land core + thin 1-cell navigable ring + EXACTLY ONE entrance; deterministic post-step). water=true is ship-navigable. The OPTIONAL `depth` field (0=land,1=deep,2=shallow,3=pink) is additive render metadata the SIM IGNORES; the minimap colours band each FINAL water cell so a client paints the water shades like the original. Regenerate with: make terrain (tools/extractor/terrain.py; pure stdlib, reads wpm+w3e+PNG, no venv). yOrientation 'top-down' means rle row 0 is the NORTH (max-Y) edge.","source":"data/extracted/war3map.wpm (pathing map, bit 0x40=no-water; THE sailability authority) + data/extracted/war3map.w3e (grid geometry) + data/reference/war3mapMap.png (depth render metadata only)","target":"the original map's enforced ship-pathing (wpm no-water), cropped to the playable rectangle","rule":"water = wpm NOT-no-water majority over the 16 32u subcells per 128u tile, MINUS singleton speckle, PLUS minimal 1-cell connectivity necks (Dijkstra cost 1 water / 30 land) for shops, docks/spawns and base-to-base, PLUS the two carved WEST sail-around island moats (compact land core + thin 1-cell water ring + exactly one entrance) around the Swedish Lumber Mill and Goblin Potion Dealer shops. depth sub-classifies FINAL water cells from the minimap colours for RENDER only: DEEP (R-B>35 AND R>=G), PINK (R>150 AND B>120 AND R-G>15), else SHALLOW; a water cell whose pixels read land-blue falls back to SHALLOW.","playableBounds":{"minX":-5376.0,"minY":-7424.0,"maxX":4864.0,"maxY":6912.0},"bounds":{"minX":-5440.0,"minY":-7488.0,"maxX":4928.0,"maxY":6976.0},"cols":162,"rows":226,"cellSizeX":64.0,"cellSizeY":64.0,"yOrientation":"top-down","yOrientationNote":"rle row 0 = max-Y (north); last row = min-Y (south). col 0 = min-X (west). col=floor((x-minX)/cellSizeX), row=floor((maxY-y)/cellSizeY). The minimap is north-up; tiles are sampled at their world centers via the content-box registration, emit row 0 = north, matching sim isWater.","rleFormat":"water[r] = [leadingValue, run0, run1, ...]; runs alternate from leadingValue (0=land,1=water) and sum to cols.","depthRleFormat":"OPTIONAL render metadata, the sim IGNORES it. depth[r] = [value0, run0, value1, run1, ...] (explicit value per run; values 0=land, 1=deep, 2=shallow, 3=pink; runs sum to cols). depth[r][col]>0 IFF water[r][col]==1 (exactly consistent with the authoritative `water` mask). Carved necks/moats with no minimap colour emit as shallow (2).","waterFraction":0.513165,"validation":{"hqsOnWater":"2/2","harboursOnWater":"4/4","playerSpawnsOnWater":"12/12","lanes":["south-west: spawnWater=True connToEnemyHQ=True","south-east: spawnWater=True connToEnemyHQ=True","north-west: spawnWater=True connToEnemyHQ=True","north-east: spawnWater=True connToEnemyHQ=True"],"basesConnected":true,"shopsReachable":"16/16","shopReach":["Swedish Lumber Mill: reachable=True nearestSeaCells=1","Goblin Potion Dealer: reachable=True nearestSeaCells=1","Elven Library: reachable=True nearestSeaCells=0","Ship Wreck: reachable=True nearestSeaCells=0","Elven Library: reachable=True nearestSeaCells=4","Ale Brewery: reachable=True nearestSeaCells=2","Equipment Merchant: reachable=True nearestSeaCells=3","Heavy Weapons Merchant: reachable=True nearestSeaCells=2","Weapons Merchant: reachable=True nearestSeaCells=4","Equipment Merchant: reachable=True nearestSeaCells=2","Bill (Trade Master): reachable=True nearestSeaCells=3","Pirate Boat Merchant: reachable=True nearestSeaCells=0","Will (Trade Master): reachable=True nearestSeaCells=4","Weapons Merchant: reachable=True nearestSeaCells=2","Heavy Weapons Merchant: reachable=True nearestSeaCells=3","Goblin Bomb Shop: reachable=True nearestSeaCells=0"],"repairRegionsReachable":"2/2","repairRegionReach":["Repair_Station_South: reachable=True","Repair_Station_North: reachable=True"],"waterFraction":0.5132,"eastThirdWaterRun":"median=6.0 mean=8.56 count=568","bottomRightWaterRun":"median=5.0 mean=5.87 count=200","necks":{"basePlatformCellsAdded":1,"connectivityNeckCellsAdded":0,"shopNeckCellsAdded":4,"repairRegionNeckCellsAdded":0,"waterFractionBefore":0.5136,"waterFractionAfter":0.5137,"shopsReachable":"16/16","repairRegionsReachable":"2/2","singletonCellsDropped":0,"straddledChannelCellsRestored":0,"westIslandLoops":{"Swedish Lumber Mill":{"shopCell":[12,123],"anchor":[8,123],"shopInCore":true,"shopInRing":false,"shopOnLand":true,"cycleLen":40,"entranceCells":[[7,117]],"entrances":1,"cellsChanged":93},"Goblin Potion Dealer":{"shopCell":[7,192],"anchor":[7,188],"shopInCore":true,"shopInRing":false,"shopOnLand":true,"cycleLen":40,"entranceCells":[[7,194]],"entrances":1,"cellsChanged":49},"waterFractionBefore":0.5137,"waterFractionAfter":0.5132,"totalCellsChanged":142}},"laneTopologyVsWpm":{"anchors":22,"pairwiseMismatches":0},"sideRoutes":{"westLumberMillIsland":{"islandLandCells":81,"ringWaterCells":36,"entrances":1},"westGoblinPotionIsland":{"islandLandCells":81,"ringWaterCells":36,"entrances":1},"eastBreweryWrap":{"brewerySeaReachable":true,"farEastNorthWaterCells":226},"bottomRightWinding":{"tanSegments":196,"tanMaxRun":27,"windingNotBlob":false}},"wpmAgreement":0.996,"wpmConfusion":"agree=0.996 ours-only(necks/moats)=0.0018 ref-only(denoised)=0.0022","depthSplitLandDeepShallowPink":[0.4868,0.2337,0.272,0.0075],"colourKeyAgreement":0.6694},"water":[[0,162],[0,162],[0,162],[0,71,6,85],[0,71,6,85],[0,57,2,12,6,85],[0,57,2,12,6,85],[0,47,2,7,7,8,6,85],[0,47,2,7,7,8,6,85],[0,47,4,4,22,85],[0,47,4,4,22,85],[0,47,32,54,2,27],[0,47,32,54,2,27],[0,55,30,44,8,25],[0,55,30,44,8,25],[0,57,28,40,14,23],[0,57,28,37,20,20],[0,57,20,45,7,8,5,20],[0,57,20,45,7,8,5,20],[0,57,20,45,5,11,5,19],[0,57,20,45,5,12,5,18],[0,57,18,46,4,15,4,18],[0,57,18,46,4,16,2,19],[0,57,18,48,2,16,4,17],[0,57,19,47,2,16,4,17],[0,7,8,28,33,5,12,18,4,8,2,18,2,17],[0,7,8,28,37,1,13,6,4,6,5,8,2,18,3,16],[0,7,10,24,54,5,5,5,7,6,4,16,3,11,3,2],[0,7,10,24,54,5,5,1,2,2,8,5,4,9,4,2,4,11,3,2],[0,7,11,15,2,4,56,4,30,6,11,7,3,1,5],[0,7,12,9,2,3,2,4,56,3,31,6,2,1,8,7,9],[0,9,26,4,56,3,29,8,11,7,9],[0,9,26,4,56,3,28,9,13,5,9],[0,9,3,2,21,4,88,6,28,1],[0,9,2,1,116,5,18,1,9,1],[0,12,106,5,6,4,6,4,12,1,4,2],[0,11,104,8,6,4,6,4,18,1],[0,13,14,3,79,15,15,5,18],[0,13,15,2,74,1,4,16,14,6,16,1],[0,13,15,2,68,29,12,6,13,4],[0,2,2,9,84,30,13,5,5,1,6,5],[1,4,9,84,20,2,18,1,1,1,10,6,6],[1,3,1,2,2,89,13,2,4,3,18,2,23],[1,97,12,14,39],[1,29,3,65,12,17,36],[0,3,26,3,65,13,23,29],[0,2,7,4,16,3,66,12,23,29],[0,2,7,4,16,3,35,8,4,4,16,11,10,3,18,18,3],[0,2,7,4,16,4,34,8,4,4,16,7,14,3,18,1,2,15,3],[0,2,7,4,5,13,31,15,2,4,14,8,15,7,2,3,13,10,6,1],[0,3,7,3,4,14,31,15,2,4,14,8,15,7,2,8,9,9,6,1],[1,17,19,26,15,2,4,14,8,15,18,2,1,5,7,6,3],[1,17,20,27,12,6,1,14,8,12,24,7,5,6,3],[1,17,21,28,10,21,8,12,26,12,7],[1,17,21,28,12,20,6,13,26,12,7],[1,19,14,2,3,27,13,21,5,11,8,2,20,9,8],[1,19,14,2,3,27,11,5,4,15,4,11,8,2,20,9,8],[1,13,20,2,3,26,12,5,4,15,4,10,9,2,10,4,9,6,8],[1,13,20,2,3,24,15,4,4,15,5,9,6,5,10,5,22],[1,13,20,2,3,24,3,2,9,5,4,15,3,11,5,8,8,8,19],[1,13,20,29,13,6,4,15,3,10,3,12,7,8,19],[0,4,9,6,2,12,29,3,12,4,32,3,13,6,10,17],[0,5,11,3,2,12,44,4,32,3,29,17],[0,6,17,11,43,4,64,4,2,11],[0,9,15,10,44,3,64,1,6,10],[0,10,17,7,30,2,87,9],[0,11,17,6,29,3,87,9],[0,19,12,3,28,4,5,9,73,9],[0,19,43,5,4,9,76,6],[0,21,41,7,2,9,76,5,1],[0,21,43,4,3,7,78,5,1],[0,21,43,4,5,4,79,6],[0,21,42,1,9,1,81,7],[0,25,129,8],[0,25,2,4,120,11],[0,32,119,11],[0,33,118,11],[0,33,66,4,48,11],[0,33,66,4,48,11],[0,11,8,18,62,10,5,2,34,12],[0,11,8,18,62,9,6,3,5,3,16,21],[0,7,16,14,62,9,6,4,4,40],[0,7,16,14,63,4,2,3,4,49],[0,5,7,3,12,11,62,3,10,49],[0,4,7,4,12,14,20,3,3,6,3,2,21,3,3,1,7,49],[0,3,7,5,10,16,20,3,2,7,3,2,35,48,1],[1,2,1,7,5,10,13,2,1,20,12,3,2,35,48,1],[1,2,1,7,5,7,16,23,12,40,6,2,32,4,5],[0,3,6,6,6,17,26,8,39,8,2,32,4,5],[0,3,6,6,6,17,26,8,31,4,4,6,4,20,4,4,8,5],[0,2,7,5,10,15,28,3,33,4,4,6,4,20,4,4,8,5],[0,2,5,7,11,13,63,6,5,7,2,10,26,5],[0,5,3,6,11,13,64,5,5,7,2,10,26,5],[0,5,3,5,14,11,64,5,5,21,22,7],[0,5,3,5,14,10,64,6,4,22,23,6],[0,5,14,1,2,3,2,6,33,2,33,4,6,23,22,6],[0,5,14,6,2,6,32,6,30,4,8,22,21,6],[0,6,13,6,2,6,31,9,5,3,20,4,8,22,22,5],[0,8,12,5,2,6,31,9,4,4,20,3,9,9,2,11,23,4],[0,8,19,12,28,6,4,4,29,12,5,8,6,4,13,4],[0,8,19,12,28,5,6,3,30,11,6,7,6,4,11,6],[0,8,19,13,22,10,6,5,26,12,8,6,6,8,7,6],[0,4,26,10,21,12,5,5,26,12,8,6,6,8,7,6],[0,3,27,10,19,14,5,5,22,16,8,6,4,11,7,5],[1,30,10,19,14,5,5,22,16,9,5,4,12,6,5],[1,6,5,6,1,15,7,16,17,6,4,21,9,17,21,11],[0,2,4,5,6,6,10,7,16,17,6,4,20,10,17,21,11],[0,2,4,5,6,9,9,5,16,11,12,4,18,12,17,25,7],[0,4,2,5,6,10,9,5,15,11,12,4,18,12,19,23,7],[0,4,2,5,4,14,7,7,13,9,12,6,18,10,21,25,5],[0,11,4,14,7,7,12,1,2,2,4,1,12,6,18,10,20,11,4,10,4,2],[0,11,4,14,8,6,15,2,17,6,48,10,6,9,4,2],[0,11,4,14,8,6,15,3,16,6,48,9,7,5,7,3],[0,9,8,14,6,6,34,4,50,6,10,5,7,3],[0,9,8,14,6,6,34,4,50,6,13,2,7,3],[0,7,6,18,6,6,30,1,2,5,20,6,26,4,22,3],[0,7,6,13,2,3,6,6,31,7,20,6,26,4,22,3],[0,7,1,18,2,1,8,6,14,4,13,7,20,10,21,9,16,5],[0,3,11,14,9,5,15,4,40,10,20,10,16,5],[0,3,1,9,1,14,29,4,40,12,7,4,7,10,14,7],[0,3,1,9,1,14,29,4,40,11,7,6,6,10,14,7],[0,3,1,9,1,14,29,4,2,7,30,12,6,7,10,6,4,17],[0,3,1,9,1,14,32,1,2,10,26,26,10,5,5,17],[0,3,1,9,1,13,10,1,25,11,25,26,4,4,2,5,5,17],[0,3,1,9,1,13,10,5,21,12,25,25,4,4,2,6,4,17],[0,3,1,9,1,1,2,10,7,8,21,12,25,23,6,13,3,17],[0,3,1,9,1,1,2,10,6,9,21,12,29,19,6,14,2,17],[0,3,1,9,1,1,2,8,4,1,2,9,20,14,29,19,4,16,2,10,6,1],[0,3,11,1,2,7,5,12,20,14,28,20,4,16,5,7,6,1],[0,15,2,7,5,10,22,14,27,19,6,16,5,7,4,3],[0,9,8,8,4,10,22,14,26,15,2,3,6,15,5,8,4,3],[0,9,8,8,4,8,24,14,26,8,2,5,2,1,12,8,8,8,4,3],[0,8,9,8,5,7,24,14,26,5,5,7,13,8,8,8,4,3],[0,1,2,4,12,6,5,7,25,13,36,7,6,1,7,1,13,5,10,1],[0,1,2,4,12,6,5,7,27,11,37,7,2,3,22,5,2,2,6,1],[0,1,2,4,2,6,4,6,5,11,24,6,41,12,22,3,1,2,1,2,7],[0,1,2,3,3,6,4,6,5,11,24,6,41,10,28,2,10],[0,6,3,6,4,4,7,11,24,4,44,9,40],[0,6,3,6,4,4,6,12,25,3,43,10,38,1,1],[0,5,4,6,12,12,27,3,34,3,6,10,35,4,1],[0,5,4,6,12,12,29,1,34,3,12,2,37,4,1],[0,7,18,14,63,4,12,2,12,5,1,2,10,1,6,5],[0,7,18,14,62,5,12,5,9,5,1,2,17,5],[0,7,6,4,6,18,23,3,33,6,12,5,9,5,22,3],[0,7,6,4,6,18,22,4,32,7,7,10,9,5,9,3,10,3],[0,41,22,6,30,7,4,2,1,15,6,2,10,3,12,1],[0,41,22,5,31,7,4,2,1,16,17,3,12,1],[0,37,24,7,3,6,34,21,15,4,10,1],[0,37,24,8,2,6,34,22,5,2,7,4,7,1,2,1],[0,27,2,8,24,8,2,6,26,2,4,25,4,2,7,4,11],[0,27,2,8,25,2,2,2,3,6,26,2,4,25,3,3,7,4,2,3,6],[0,27,2,8,36,4,23,7,2,10,4,10,4,3,2,11,1,2,6],[0,26,3,7,37,4,22,8,2,10,4,10,4,16,9],[0,26,5,4,38,4,22,8,2,10,4,12,2,18,2,4,1],[0,26,73,8,2,10,4,12,2,18,2,4,1],[0,26,2,2,37,4,30,6,4,6,2,16,2,25],[0,30,37,4,30,6,4,6,2,15,4,6,2,16],[0,30,33,8,2,1,27,6,4,23,5,4,3,16],[0,31,32,7,3,6,22,6,4,21,7,4,2,17],[0,29,33,8,3,8,22,4,6,19,7,16,2,5],[0,29,34,5,2,1,2,8,22,4,7,18,7,16,2,5],[0,29,33,1,12,6,36,15,7,16,2,5],[0,29,30,3,13,6,35,12,2,4,5,16,2,5],[0,29,30,3,15,4,26,18,5,4,2,1,4,10,10,1],[0,29,30,4,15,3,26,18,5,7,4,9,11,1],[0,27,32,4,44,18,6,8,2,9,12],[0,27,32,4,44,18,6,8,2,5,16],[0,27,32,4,14,1,31,16,6,8,2,4,7,9,1],[0,27,50,3,29,15,7,8,2,4,6,10,1],[0,27,50,3,26,1,8,9,7,8,9,13,1],[0,26,51,3,22,5,9,6,9,8,8,14,1],[0,21,10,7,22,11,6,3,22,7,7,6,9,10,5,9,7],[0,21,11,6,22,3,2,10,2,3,21,8,7,7,8,10,4,10,7],[0,21,11,6,22,17,28,5,7,6,12,16,11],[0,21,11,6,21,17,29,8,4,6,13,15,11],[0,23,9,5,22,17,29,9,13,6,4,12,7,6],[0,23,9,5,22,18,29,9,12,6,4,12,5,8],[0,23,36,18,29,9,10,8,6,8,7,8],[0,23,37,17,20,17,11,8,6,7,5,11],[0,25,35,17,16,21,4,16,5,4,8,11],[0,25,35,14,19,42,5,3,8,11],[0,25,4,3,30,2,29,45,9,15],[0,25,4,4,29,2,29,11,2,33,8,15],[0,2,11,12,2,7,59,11,2,34,7,10,5],[0,2,1,9,1,11,3,9,60,6,4,16,2,17,6,10,4,1],[0,2,1,9,1,11,3,9,61,4,6,15,2,38],[0,2,1,9,1,11,4,6,63,4,6,55],[0,2,1,9,1,10,72,6,8,18,4,24,1,6],[0,2,1,9,1,10,72,6,9,14,2,1,7,21,1,6],[0,2,1,9,1,1,1,6,74,6,9,13,12,20,1,5,1],[0,2,1,9,1,1,1,6,74,6,9,8,2,3,12,20,1,5,1],[0,2,1,9,1,1,1,6,89,7,20,14,5,6],[0,2,1,9,1,1,1,6,89,6,22,13,5,6],[0,2,11,8,89,5,23,13,5,6],[0,7,1,12,82,3,5,5,27,9,6,5],[0,7,5,8,20,2,60,4,24,5,8,6,8,5],[0,7,6,7,20,3,57,7,22,6,9,5,6,7],[0,7,6,4,23,3,57,8,11,3,5,10,7,5,5,8],[0,7,2,1,3,4,22,4,57,8,11,3,5,9,10,2,5,9],[1,3,6,29,9,53,8,4,3,4,3,5,9,17,9],[1,36,11,53,8,4,3,4,5,3,12,14,9],[1,36,11,50,1,2,7,5,3,2,7,7,8,12,11],[1,1,5,25,16,50,9,6,3,2,8,6,8,12,11],[1,1,5,8,4,4,2,6,28,22,7,4,15,6,15,6,29],[1,1,24,4,34,16,8,4,15,6,15,6,29],[0,25,2,36,16,27,6,15,6,29],[0,25,2,36,16,28,5,15,6,29],[0,63,16,28,6,14,8,27],[0,63,16,28,6,14,8,27],[0,63,16,29,9,8,4,33],[0,63,16,30,13,3,4,33],[0,63,20,29,17,33],[0,63,20,30,16,33],[0,55,30,29,6,42],[0,55,30,33,2,42],[0,55,30,33,2,42],[0,55,30,77],[0,59,8,4,1,90],[0,59,8,95],[0,61,6,95],[0,61,6,95],[0,61,6,95],[0,61,6,95],[0,61,6,95],[0,61,6,95],[0,63,2,97]],"depth":[[0,162],[0,162],[0,162],[0,71,2,6,0,85],[0,71,2,6,0,85],[0,57,2,2,0,12,2,6,0,85],[0,57,2,2,0,12,2,6,0,85],[0,47,2,2,0,7,2,7,0,8,2,6,0,85],[0,47,2,2,0,7,2,7,0,8,2,6,0,85],[0,47,2,4,0,4,2,22,0,85],[0,47,2,4,0,4,2,22,0,85],[0,47,2,15,1,10,2,7,0,54,2,2,0,27],[0,47,2,14,1,13,2,5,0,54,2,2,0,27],[0,55,2,5,1,16,2,9,0,44,2,8,0,25],[0,55,2,5,1,16,2,9,0,44,2,8,0,25],[0,57,2,4,1,16,2,8,0,40,2,14,0,23],[0,57,2,4,1,16,2,8,0,37,2,20,0,20],[0,57,2,3,1,17,0,45,2,7,0,8,2,5,0,20],[0,57,2,2,1,18,0,45,2,2,1,2,2,3,0,8,2,5,0,20],[0,57,2,2,1,18,0,45,2,2,1,3,0,11,2,5,0,19],[0,57,1,20,0,45,2,3,1,1,2,1,0,12,2,5,0,18],[0,57,1,18,0,46,2,4,0,15,2,4,0,18],[0,57,1,18,0,46,2,4,0,16,2,2,0,19],[0,57,1,18,0,48,2,2,0,16,2,4,0,17],[0,57,1,19,0,47,2,2,0,16,2,4,0,17],[0,7,2,4,1,4,0,28,1,33,0,5,1,3,2,2,1,7,0,18,2,4,0,8,2,2,0,18,2,2,0,17],[0,7,2,3,1,5,0,28,1,37,0,1,1,13,0,6,2,4,0,6,2,5,0,8,2,2,0,18,2,3,0,16],[0,7,2,3,1,7,0,24,1,54,0,5,2,5,0,5,2,7,0,6,2,4,0,16,2,3,0,11,2,3,0,2],[0,7,2,3,1,7,0,24,1,54,0,5,2,5,0,1,2,2,0,2,2,8,0,5,2,4,0,9,2,4,0,2,2,4,0,11,2,3,0,2],[0,7,2,3,1,8,0,15,2,1,1,1,0,4,1,56,0,4,2,30,0,6,2,11,0,7,2,3,0,1,2,5],[0,7,1,1,2,2,1,9,0,9,2,1,1,1,0,3,1,1,2,1,0,4,1,31,2,2,1,3,2,6,1,1,2,2,1,11,0,3,2,31,0,6,2,2,0,1,2,8,0,7,2,9],[0,9,2,1,1,11,2,8,1,4,2,2,0,4,1,25,2,3,1,3,2,7,1,1,2,7,1,10,0,3,2,29,0,8,2,11,0,7,2,9],[0,9,2,2,1,11,2,8,1,3,2,2,0,4,1,24,2,22,1,10,0,3,2,28,0,9,2,13,0,5,2,9],[0,9,2,3,0,2,2,2,1,6,2,11,1,2,0,4,1,23,2,22,1,12,2,31,0,6,2,28,0,1],[0,9,2,2,0,1,2,1,1,1,2,2,1,3,2,13,1,26,2,26,1,12,2,32,0,5,2,18,0,1,2,9,0,1],[0,12,2,4,1,2,2,13,1,5,2,1,1,20,2,30,1,6,2,23,1,1,2,1,0,5,1,1,2,5,0,4,2,6,0,4,2,12,0,1,2,4,0,2],[0,11,2,13,3,3,2,4,1,3,2,3,1,19,2,19,3,1,1,2,2,9,1,6,2,22,0,8,1,1,2,5,0,4,2,6,0,4,2,18,0,1],[0,13,2,6,3,2,1,1,2,1,3,4,0,3,2,1,1,3,2,3,1,19,2,2,1,2,2,14,3,1,1,4,2,8,1,7,2,15,0,15,2,15,0,5,2,18],[0,13,2,5,1,1,3,9,0,2,2,3,1,1,2,3,1,21,2,16,3,2,1,4,2,7,1,7,2,10,0,1,2,4,0,16,2,14,0,6,2,16,0,1],[0,13,2,2,3,4,2,4,3,5,0,2,2,7,1,22,2,15,3,4,1,3,2,7,1,7,2,3,0,29,2,12,0,6,2,13,0,4],[0,2,1,2,0,9,2,1,3,5,2,5,3,6,2,7,1,22,2,15,3,4,1,3,2,3,1,1,2,3,1,7,2,2,0,30,2,13,0,5,2,5,0,1,2,6,0,5],[1,4,0,9,2,1,3,5,2,7,3,4,2,7,1,23,2,15,3,4,1,2,2,2,1,2,2,3,1,8,2,1,0,20,1,2,0,18,2,1,0,1,2,1,0,10,2,6,0,6],[1,3,0,1,1,2,0,2,2,6,3,5,2,7,3,5,2,7,1,22,2,15,3,3,1,3,2,8,1,8,0,13,2,1,1,1,0,4,1,2,2,1,0,18,2,2,0,23],[1,7,2,7,3,3,2,10,3,5,2,6,1,26,2,11,3,3,1,3,2,8,1,8,0,12,2,2,1,6,2,6,0,39],[1,6,2,9,3,2,2,10,3,2,0,3,2,6,1,24,2,1,1,1,2,12,3,1,1,4,2,5,1,2,2,2,1,7,0,12,2,2,1,6,2,9,0,36],[0,3,1,2,2,11,3,1,2,10,3,2,0,3,2,6,1,23,2,5,3,4,2,6,3,1,1,5,2,4,1,4,2,6,1,1,0,13,2,1,1,6,2,16,0,29],[0,2,1,2,2,5,0,4,2,14,3,2,0,3,2,6,1,23,2,5,3,5,2,5,3,1,1,5,2,5,1,4,2,7,0,12,1,7,2,16,0,29],[0,2,1,2,2,5,0,4,2,16,0,3,2,6,1,22,2,6,3,1,0,8,2,1,3,1,1,2,0,4,2,5,1,3,2,8,0,11,1,2,2,2,1,3,2,3,0,3,2,18,0,18,2,3],[0,2,1,3,2,4,0,4,2,16,0,4,2,4,1,23,2,6,3,1,0,8,2,4,0,4,2,10,1,2,2,4,0,7,2,4,1,3,2,1,1,3,2,3,0,3,2,1,1,1,2,16,0,1,2,2,0,15,2,3],[0,2,1,3,2,4,0,4,2,5,0,13,2,7,1,23,2,1,0,15,2,2,0,4,2,10,1,3,2,1,0,8,2,5,1,4,2,2,1,1,2,3,0,7,2,2,0,3,2,13,0,10,2,6,0,1],[0,3,1,1,2,6,0,3,2,4,0,14,2,8,1,22,2,1,0,15,2,2,0,4,2,9,1,5,0,8,2,5,1,7,2,3,0,7,2,2,0,8,2,9,0,9,2,6,0,1],[2,17,0,19,2,3,1,22,2,1,0,15,2,2,0,4,2,7,1,7,0,8,2,4,1,9,2,2,0,18,2,2,0,1,2,4,1,1,0,7,2,6,0,3],[1,2,2,15,0,20,2,2,1,23,2,2,0,12,2,6,0,1,2,7,1,7,0,8,2,1,1,11,0,24,1,7,0,5,2,6,0,3],[2,17,0,21,2,1,1,22,2,5,0,10,1,1,2,13,1,7,0,8,1,12,0,26,1,5,2,7,0,7],[2,17,0,21,2,1,1,22,2,5,0,12,2,12,1,8,0,6,1,13,0,26,1,6,2,6,0,7],[2,10,1,1,2,6,1,2,0,14,2,2,0,3,2,1,1,22,2,4,0,13,2,10,1,11,0,5,1,11,0,8,1,2,0,20,1,5,2,4,0,8],[2,8,1,7,2,2,1,2,0,14,2,2,0,3,1,23,2,4,0,11,2,5,0,4,2,2,1,13,0,4,1,11,0,8,1,2,0,20,1,5,2,4,0,8],[2,8,1,5,0,20,1,2,0,3,1,23,2,3,0,12,2,5,0,4,1,15,0,4,1,10,0,9,1,2,0,10,1,4,0,9,1,6,0,8],[2,9,1,4,0,20,1,2,0,3,1,23,2,1,0,15,2,4,0,4,1,15,0,5,1,9,0,6,1,5,0,10,1,5,0,22],[2,9,3,4,0,20,1,2,0,3,1,22,2,2,0,3,2,2,0,9,2,5,0,4,1,15,0,3,1,11,0,5,1,8,0,8,1,8,0,19],[2,13,0,20,1,27,2,2,0,13,2,6,0,4,1,15,0,3,1,10,0,3,1,12,0,7,1,8,0,19],[0,4,2,9,0,6,2,2,0,12,1,28,2,1,0,3,2,2,1,2,2,8,0,4,2,2,1,30,0,3,1,13,0,6,1,10,0,17],[0,5,2,11,0,3,2,2,0,12,1,30,2,4,1,3,2,7,0,4,2,2,1,30,0,3,1,29,0,17],[0,6,2,17,0,11,1,29,2,4,1,3,2,2,1,2,2,3,0,4,2,1,1,63,0,4,1,2,0,11],[0,9,2,15,0,10,1,29,2,4,1,3,2,2,1,2,2,4,0,3,1,22,2,4,1,38,0,1,1,5,2,1,0,10],[0,10,2,17,0,7,1,29,2,1,0,2,2,1,1,3,2,10,1,22,2,5,1,2,2,6,1,4,2,2,1,31,2,1,0,9],[0,11,2,17,0,6,1,29,0,3,2,1,1,3,2,4,1,29,2,4,1,1,2,7,1,4,2,3,1,31,0,9],[0,19,2,12,0,3,2,1,1,27,0,4,2,1,1,2,2,2,0,9,1,23,2,4,1,1,2,7,1,5,2,1,1,28,2,1,1,3,0,9],[0,19,2,17,1,26,0,5,2,4,0,9,1,20,2,18,1,15,2,6,1,9,2,2,1,6,0,6],[0,21,2,18,1,23,0,7,2,1,1,1,0,9,1,19,2,20,1,1,2,2,1,1,2,3,1,5,2,13,1,3,2,4,1,4,2,1,0,5,2,1],[0,21,2,18,1,24,2,1,0,4,2,2,1,1,0,7,1,21,2,18,1,3,2,32,1,4,0,5,2,1],[0,21,2,19,1,22,2,2,0,4,2,2,1,3,0,4,1,22,2,18,1,1,2,1,1,1,2,36,0,6],[0,21,2,20,1,20,2,2,0,1,2,6,1,3,0,1,1,24,2,19,1,1,2,37,0,7],[0,25,2,16,1,19,2,10,1,28,2,2,1,2,2,14,1,2,2,36,0,8],[0,25,2,2,0,4,2,8,1,1,2,2,1,15,2,13,1,28,2,19,1,1,2,33,0,11],[0,32,2,10,1,14,2,15,1,1,2,1,1,1,2,1,1,1,2,11,1,11,2,53,0,11],[0,33,2,10,1,14,2,1,1,2,2,28,1,10,2,53,0,11],[0,33,2,10,1,14,2,31,1,10,2,1,0,4,2,48,0,11],[0,33,2,10,1,14,2,29,1,12,2,1,0,4,2,48,0,11],[0,11,2,8,0,18,2,2,1,17,2,30,1,13,0,10,2,5,0,2,2,13,3,1,2,20,0,12],[0,11,2,8,0,18,1,20,2,29,1,13,0,9,2,6,0,3,2,5,0,3,2,3,3,3,2,10,0,21],[0,7,2,16,0,14,1,23,2,26,1,13,0,9,2,6,0,4,2,4,0,40],[0,7,2,16,0,14,2,2,1,22,2,24,1,15,0,4,2,2,0,3,2,4,0,49],[0,5,2,6,1,1,0,3,1,2,2,10,0,11,2,1,1,24,2,21,1,16,0,3,1,1,2,9,0,49],[0,4,2,7,0,4,1,1,2,11,0,14,1,20,0,3,2,3,0,6,2,3,0,2,2,4,1,17,0,3,1,3,0,1,2,7,0,49],[0,3,2,7,0,5,2,10,0,16,1,20,0,3,1,1,2,1,0,7,2,3,0,2,2,4,1,21,2,10,0,48,2,1],[2,2,0,1,2,7,0,5,2,10,0,13,1,2,0,1,1,20,0,12,2,1,1,2,0,2,2,6,1,18,2,11,0,48,1,1],[2,2,0,1,2,7,0,5,2,7,0,16,1,23,0,12,2,12,1,18,2,10,0,6,2,2,0,32,2,4,0,5],[0,3,1,1,2,5,0,6,2,6,0,17,1,25,2,1,0,8,2,13,1,18,2,8,0,8,2,2,0,32,2,2,1,2,0,5],[0,3,1,2,2,4,0,6,2,6,0,17,1,24,2,2,0,8,2,14,1,17,0,4,2,4,0,6,2,4,0,20,2,4,0,4,2,6,1,1,2,1,0,5],[0,2,2,1,1,2,2,4,0,5,2,10,0,15,1,22,2,6,0,3,2,16,1,17,0,4,2,4,0,6,2,4,0,20,2,4,0,4,2,8,0,5],[0,2,2,5,0,7,2,11,0,13,1,22,2,2,1,2,2,22,1,15,0,6,2,4,1,1,0,7,2,2,0,10,2,26,0,5],[0,5,2,3,0,6,2,11,0,13,1,21,2,27,1,16,0,5,2,3,1,2,0,7,2,2,0,10,2,26,0,5],[0,5,2,3,0,5,1,4,2,10,0,11,2,1,1,19,2,16,1,3,2,9,1,16,0,5,2,3,1,2,0,21,2,22,0,7],[0,5,2,3,0,5,1,6,2,8,0,10,2,2,1,17,2,18,1,4,2,9,1,14,0,6,2,4,0,22,2,23,0,6],[0,5,2,8,1,6,0,1,2,2,0,3,2,2,0,6,2,6,1,16,2,11,0,2,2,7,1,2,2,10,1,14,0,4,2,6,0,23,2,22,0,6],[0,5,2,8,1,6,0,6,2,2,0,6,2,6,1,16,2,10,0,6,2,16,1,14,0,4,2,8,0,22,2,5,3,1,2,15,0,6],[0,6,2,8,1,5,0,6,2,2,0,6,2,6,1,16,2,9,0,9,2,5,0,3,2,5,1,15,0,4,2,8,0,22,2,7,3,1,2,14,0,5],[0,8,2,7,1,5,0,5,2,2,0,6,2,6,1,16,2,9,0,9,2,1,1,3,0,4,2,5,1,7,2,2,1,6,0,3,2,9,0,9,2,2,0,11,2,6,3,3,2,14,0,4],[0,8,2,7,1,5,2,7,0,12,2,1,1,14,2,13,0,6,3,1,1,3,0,4,2,5,1,2,2,1,1,4,2,3,1,6,2,8,0,12,2,5,0,8,2,6,0,4,2,13,0,4],[0,8,2,9,1,3,2,7,0,12,2,1,1,14,2,13,0,5,3,1,1,5,0,3,2,10,1,2,2,4,1,5,2,1,1,1,2,7,0,11,2,6,0,7,2,6,0,4,2,11,0,6],[0,8,2,19,0,13,2,1,1,13,2,8,0,10,1,6,0,5,2,10,1,1,2,3,1,5,2,7,0,12,2,1,1,3,2,3,1,1,0,6,2,6,0,8,2,7,0,6],[0,4,2,26,0,10,2,3,1,11,2,7,0,12,1,5,0,5,2,11,1,1,2,3,1,3,2,8,0,12,1,5,2,2,1,1,0,6,1,1,2,5,0,8,2,7,0,6],[0,3,2,27,0,10,2,3,1,12,2,4,0,14,1,5,0,5,2,12,1,1,2,1,1,4,2,4,0,16,1,6,2,2,0,6,1,1,2,3,0,11,2,7,0,5],[2,3,1,4,2,23,0,10,2,2,1,11,2,1,1,4,2,1,0,14,1,5,0,5,2,11,1,8,2,3,0,16,1,7,2,2,0,5,1,1,2,3,0,12,2,6,0,5],[3,3,1,3,0,5,2,6,0,1,2,15,0,7,2,2,1,10,2,3,1,1,0,17,1,4,2,2,0,4,2,11,1,8,2,2,0,9,2,3,1,14,0,21,2,11],[0,2,1,4,0,5,2,6,0,6,2,10,0,7,2,3,1,9,2,3,1,1,0,17,1,4,2,2,0,4,2,11,1,8,2,1,0,10,1,17,0,21,2,11],[0,2,1,4,0,5,2,6,0,9,2,9,0,5,2,3,1,9,2,3,1,1,0,11,1,9,2,3,0,4,2,10,1,7,2,1,0,12,3,1,1,16,0,25,2,7],[0,4,1,2,0,5,2,6,0,10,2,9,0,5,2,1,1,10,2,4,0,11,1,8,2,4,0,4,2,9,1,9,0,12,1,16,2,3,0,23,2,7],[0,4,1,2,0,5,2,4,0,14,2,7,0,7,1,10,2,3,0,9,1,9,2,3,0,6,2,6,1,12,0,10,1,17,2,4,0,25,2,3,1,2],[0,11,2,4,0,14,2,7,0,7,1,10,2,2,0,1,2,2,0,2,2,1,1,3,0,1,1,9,2,3,0,6,2,3,1,1,2,1,1,13,0,10,1,16,2,2,1,1,2,1,0,11,2,4,0,10,1,1,2,3,0,2],[0,11,3,1,2,3,0,14,2,8,0,6,1,10,2,5,0,2,1,14,2,3,0,6,2,3,1,16,2,2,1,2,2,6,1,15,2,1,1,3,0,10,2,6,0,9,1,1,2,3,0,2],[0,11,3,2,2,2,0,14,2,8,0,6,1,10,2,5,0,3,1,12,2,4,0,6,2,3,1,16,2,12,3,1,1,11,2,2,1,3,0,9,2,7,0,5,1,4,2,3,0,3],[0,9,3,3,2,5,0,14,2,6,0,6,1,10,2,8,1,13,2,3,0,4,2,5,1,17,2,11,3,1,1,10,2,4,1,1,2,1,0,6,2,10,0,5,1,1,2,6,0,3],[0,9,3,3,2,5,0,14,2,6,0,6,1,10,2,9,1,15,0,4,2,5,1,17,2,13,1,9,2,6,0,6,2,13,0,2,2,7,0,3],[0,7,3,6,0,18,2,6,0,6,1,10,2,8,3,1,1,8,2,3,0,1,1,2,0,5,2,5,1,15,0,6,2,12,1,5,2,9,0,4,2,22,0,3],[0,7,3,6,0,13,2,2,0,3,2,6,0,6,1,10,2,8,3,1,1,6,2,6,0,7,2,5,1,15,0,6,2,13,1,3,2,10,0,4,2,22,0,3],[0,7,3,1,0,18,2,2,0,1,2,8,0,6,1,10,2,4,0,4,3,2,1,2,3,4,2,5,0,7,2,5,1,15,0,10,2,9,1,3,2,9,0,9,2,16,0,5],[0,3,2,1,1,2,3,7,2,1,0,14,2,9,0,5,1,15,0,4,3,8,2,17,1,15,0,10,2,20,0,10,2,16,0,5],[0,3,2,1,0,9,2,1,0,14,1,1,2,12,1,16,0,4,2,1,3,7,2,17,1,15,0,12,2,7,0,4,2,7,0,10,2,14,0,7],[0,3,1,1,0,9,2,1,0,14,1,1,2,12,1,16,0,4,2,1,3,1,1,3,3,2,2,18,1,15,0,11,2,7,0,6,2,6,0,10,2,14,0,7],[0,3,1,1,0,9,2,1,0,14,2,13,1,16,0,4,2,1,3,1,0,7,2,3,3,1,2,12,1,14,0,12,2,6,0,7,2,10,0,6,2,4,0,17],[0,3,1,1,0,9,2,1,0,14,2,13,1,19,0,1,1,2,0,10,2,13,1,13,0,26,2,6,1,2,2,2,0,5,2,5,0,17],[0,3,1,1,0,9,2,1,0,13,2,10,0,1,2,3,1,22,0,11,2,12,1,13,0,26,2,4,0,4,3,2,0,5,2,5,0,17],[0,3,1,1,0,9,2,1,0,13,2,10,0,5,1,21,0,12,2,11,1,14,0,25,2,3,3,1,0,4,3,2,0,6,2,4,0,17],[0,3,1,1,0,9,2,1,0,1,2,2,0,10,2,7,0,8,1,19,3,2,0,12,2,11,1,14,0,23,2,6,0,13,2,3,0,17],[0,3,1,1,0,9,2,1,0,1,2,2,0,10,2,6,0,9,1,19,3,2,0,12,3,3,2,8,1,17,2,1,0,19,2,6,0,14,2,2,0,17],[0,3,1,1,0,9,3,1,0,1,2,2,0,8,2,4,0,1,2,2,0,9,1,20,0,14,3,3,2,8,1,17,2,1,0,19,2,4,0,16,2,2,0,10,2,6,0,1],[0,3,1,7,3,4,0,1,2,2,0,7,2,5,0,12,1,19,3,1,0,14,3,3,2,8,1,17,0,20,2,4,0,16,2,5,0,7,2,6,0,1],[0,15,2,2,0,7,2,5,0,10,2,2,1,19,3,1,0,14,3,3,2,8,1,16,0,19,2,6,0,16,2,5,0,7,2,4,0,3],[0,9,1,3,3,2,2,3,0,8,2,4,0,10,2,2,1,19,2,1,0,14,3,2,2,9,1,15,0,15,2,2,0,3,2,6,0,15,2,5,0,8,2,4,0,3],[0,9,1,5,2,3,0,8,2,4,0,8,2,5,1,17,2,2,0,14,1,1,2,10,1,15,0,8,2,2,0,5,2,2,0,1,2,12,0,8,2,8,0,8,2,4,0,3],[0,8,1,6,3,1,2,2,0,8,2,5,0,7,2,5,1,17,2,2,0,14,2,11,1,15,0,5,2,5,0,7,2,12,1,1,0,8,2,8,0,8,2,4,0,3],[0,1,1,2,0,4,1,7,3,1,2,4,0,6,2,5,0,7,2,6,1,17,2,2,0,13,2,11,1,18,2,7,0,7,2,6,0,1,2,4,1,3,0,1,1,2,2,4,1,1,2,6,0,5,2,10,0,1],[0,1,1,2,0,4,3,1,1,6,3,1,2,3,3,1,0,6,2,5,0,7,2,8,1,16,2,3,0,11,2,11,1,7,2,2,1,10,2,7,0,7,2,2,0,3,2,5,1,6,2,3,1,3,2,5,0,5,2,2,0,2,2,6,0,1],[0,1,1,2,0,4,3,2,0,6,2,3,3,1,0,6,2,5,0,11,2,5,1,15,2,3,1,1,0,6,2,4,1,6,2,4,1,7,2,3,1,11,2,6,0,12,2,8,1,4,2,3,1,2,2,5,0,3,2,1,0,2,2,1,0,2,2,7],[0,1,1,2,0,3,3,3,0,6,1,1,2,3,0,6,2,5,0,11,2,5,1,16,2,1,1,2,0,6,2,2,1,19,2,3,1,11,2,6,0,10,2,10,1,5,2,13,0,2,2,10],[0,6,3,3,0,6,1,2,2,2,0,4,2,7,0,11,2,4,1,18,2,2,0,4,2,3,1,20,2,4,1,10,2,7,0,9,2,8,1,7,2,25],[0,6,3,2,2,1,0,6,1,2,3,1,2,1,0,4,2,6,0,12,2,4,1,18,2,3,0,3,2,2,1,23,2,3,1,9,2,6,0,10,2,8,1,8,2,22,0,1,2,1],[0,5,3,2,2,2,0,6,1,3,2,9,0,12,2,5,1,18,2,4,0,3,2,1,1,25,2,3,1,4,2,1,0,3,2,6,0,10,2,14,1,3,2,8,1,1,2,9,0,4,2,1],[0,5,3,1,2,3,0,6,1,3,2,9,0,12,2,5,1,17,2,7,0,1,2,1,1,26,2,2,1,5,0,3,2,4,1,2,2,6,0,2,1,2,2,16,1,1,2,18,0,4,2,1],[0,7,2,6,1,5,2,7,0,14,2,4,1,18,2,9,1,32,0,4,2,12,0,2,2,12,0,5,2,1,0,2,2,8,1,2,0,1,2,6,0,5],[0,7,2,6,1,3,3,2,2,7,0,14,2,5,1,16,2,10,1,12,2,3,1,16,0,5,2,12,0,5,2,9,0,5,2,1,0,2,2,8,1,4,2,5,0,5],[0,7,2,5,1,1,0,4,3,2,2,4,0,18,2,3,1,14,2,6,0,3,2,4,1,8,2,6,1,15,0,6,2,12,0,5,2,9,0,5,2,9,1,7,2,6,0,3],[0,7,2,6,0,4,2,6,0,18,2,3,1,13,2,6,0,4,2,5,1,5,2,9,1,13,0,7,2,7,0,10,2,9,0,5,2,7,1,2,0,3,2,1,1,4,2,5,0,3],[0,41,2,3,1,12,2,7,0,6,2,4,1,3,2,11,1,12,0,7,2,4,0,2,2,1,0,15,2,6,0,2,2,8,1,2,0,3,2,1,1,5,2,4,3,2,0,1],[0,41,2,1,1,15,2,6,0,5,2,7,1,1,2,11,1,12,0,7,2,4,0,2,2,1,0,16,2,16,1,1,0,3,2,1,1,6,2,3,3,2,0,1],[0,37,2,4,1,18,2,2,0,7,2,3,0,6,2,10,1,12,2,12,0,21,2,15,0,4,1,5,2,3,3,2,0,1],[0,37,2,4,1,18,2,2,0,8,2,2,0,6,2,8,1,16,2,10,0,22,2,5,0,2,2,7,0,4,1,2,3,3,2,2,0,1,2,2,0,1],[0,27,2,2,0,8,2,3,1,20,2,1,0,8,2,2,0,6,2,7,1,18,2,1,0,2,2,4,0,25,2,4,0,2,2,7,0,4,1,2,2,9],[0,27,2,2,0,8,2,2,1,21,2,2,0,2,1,1,2,1,0,2,2,3,0,6,2,5,1,18,2,3,0,2,2,4,0,25,2,3,0,3,2,7,0,4,2,2,0,3,2,6],[0,27,2,2,0,8,2,1,1,22,2,3,1,2,2,8,0,4,2,6,1,17,0,7,2,2,0,10,2,4,0,10,2,4,0,3,2,2,0,11,2,1,0,2,2,6],[0,26,2,3,0,7,2,1,1,24,2,2,1,2,2,8,0,4,2,6,1,16,0,8,2,2,0,10,2,4,0,10,2,4,0,16,2,9],[0,26,2,4,1,1,0,4,2,1,1,24,2,13,0,4,2,6,1,16,0,8,2,2,0,10,2,4,0,12,2,2,0,18,2,2,0,4,2,1],[0,26,2,4,1,1,2,3,1,2,2,4,1,19,2,24,1,16,0,8,2,2,0,10,3,2,2,2,0,12,2,2,0,18,2,2,0,4,2,1],[0,26,2,2,0,2,2,1,1,5,2,5,1,17,2,9,0,4,2,13,1,17,0,6,2,4,0,6,1,2,0,16,2,2,0,25],[0,30,1,7,2,4,1,14,2,12,0,4,2,12,1,18,0,6,2,4,0,6,1,2,0,15,2,4,0,6,2,2,0,16],[0,30,1,7,2,1,1,1,2,4,1,11,2,9,0,8,2,2,0,1,2,9,1,18,0,6,2,4,0,23,2,5,0,4,2,3,0,16],[0,31,1,6,2,6,1,11,2,9,0,7,2,3,0,6,2,4,1,1,2,1,1,16,0,6,2,4,0,21,2,7,0,4,2,2,0,17],[0,29,2,1,1,7,2,6,1,11,2,2,1,2,2,4,0,8,2,3,0,8,2,5,1,16,2,1,0,4,2,6,0,19,2,7,0,16,2,2,0,5],[0,29,2,1,1,7,2,3,1,1,2,1,1,12,2,2,1,2,2,5,0,5,2,2,0,1,2,2,0,8,2,5,1,16,2,1,0,4,2,7,0,18,2,7,0,16,2,2,0,5],[0,29,2,2,1,7,2,1,1,15,2,8,0,1,2,4,1,6,2,2,0,6,2,5,1,16,2,15,0,15,2,7,0,16,2,2,0,5],[0,29,2,2,1,24,2,4,0,3,2,3,1,10,0,6,2,5,1,16,2,14,0,12,2,2,0,4,2,5,0,16,2,2,0,5],[0,29,2,2,1,24,2,4,0,3,2,2,1,12,2,1,0,4,2,5,1,16,2,5,0,18,2,5,0,4,2,2,0,1,2,4,0,10,2,10,0,1],[0,29,2,2,1,25,2,3,0,4,2,1,1,13,2,1,0,3,2,5,1,16,2,5,0,18,2,5,0,7,2,4,0,9,2,11,0,1],[0,27,2,5,1,24,2,3,0,4,2,1,1,13,2,8,1,16,2,5,1,1,0,18,2,6,0,8,2,2,0,9,2,12],[0,27,2,6,1,24,2,2,0,4,2,1,1,13,2,8,1,15,2,6,1,1,0,18,2,6,0,8,2,2,0,5,2,16],[0,27,2,6,1,23,2,2,1,1,0,4,2,1,1,13,0,1,2,6,1,15,2,7,1,2,2,1,0,16,2,6,0,8,2,2,0,4,2,7,0,9,2,1],[0,27,1,1,2,6,1,22,2,2,1,2,2,3,1,14,0,3,2,4,1,14,2,11,0,15,2,7,0,8,2,2,0,4,2,6,0,10,2,1],[0,27,1,1,2,6,1,22,2,2,1,2,2,3,1,14,0,3,2,3,1,15,2,8,0,1,2,8,0,9,2,7,0,8,2,9,0,13,2,1],[0,26,1,2,2,8,1,23,2,5,1,13,0,3,2,3,1,15,2,4,0,5,2,9,0,6,2,9,0,8,2,8,0,14,2,1],[0,21,2,2,1,4,2,4,0,7,1,20,2,2,0,11,1,6,0,3,2,3,1,16,2,3,0,7,2,7,0,6,2,8,1,1,0,10,2,5,0,9,2,7],[0,21,2,1,1,5,2,5,0,6,1,20,2,2,0,3,2,1,3,1,0,10,1,1,2,1,0,3,2,3,1,18,0,8,2,7,0,7,2,8,0,10,2,4,0,10,2,7],[0,21,1,6,2,5,0,6,1,19,2,3,0,17,2,5,1,10,2,1,1,8,2,4,0,5,2,7,0,6,2,12,0,16,2,11],[0,21,1,6,2,5,0,6,1,18,2,3,0,17,2,6,1,9,2,3,1,5,2,5,1,1,0,8,2,4,0,6,2,13,0,15,2,11],[0,23,1,5,2,4,0,5,2,1,1,18,2,3,0,17,2,6,1,8,2,5,1,4,2,6,0,9,2,13,0,6,2,4,0,12,2,7,0,6],[0,23,1,6,2,3,0,5,2,2,1,16,2,4,0,18,2,5,1,7,2,7,1,4,2,6,0,9,2,12,0,6,2,4,0,12,2,5,0,8],[0,23,1,6,2,10,1,16,2,4,0,18,2,4,1,8,2,9,1,2,2,3,1,3,0,9,2,10,0,8,2,6,0,8,2,7,0,8],[0,23,1,11,2,5,1,17,2,4,0,17,2,4,1,8,2,8,0,17,2,6,1,1,2,4,0,8,2,6,0,7,2,5,0,11],[0,25,1,10,2,3,1,18,2,4,0,17,2,4,1,11,2,1,0,21,2,4,0,16,2,5,0,4,2,8,0,11],[0,25,1,32,2,3,0,14,2,7,1,9,2,3,0,42,2,5,0,3,2,8,0,11],[0,25,1,4,0,3,1,25,2,5,0,2,2,17,1,8,2,4,0,45,2,9,0,15],[0,25,1,4,0,4,1,25,2,4,0,2,2,17,1,8,2,4,0,11,2,2,0,33,2,8,0,15],[0,2,2,11,0,12,1,2,0,7,1,25,2,3,1,2,2,9,1,1,2,7,1,8,2,4,0,11,2,2,0,34,2,7,0,10,2,5],[0,2,2,1,0,9,2,1,0,11,1,3,0,9,1,24,2,10,1,6,2,5,1,9,2,6,0,6,2,4,0,16,2,2,0,17,2,6,0,10,2,4,0,1],[0,2,2,1,0,9,2,1,0,11,1,1,3,2,0,9,2,1,1,23,2,9,1,7,2,5,1,10,2,6,0,4,2,6,0,15,2,2,0,38],[0,2,2,1,0,9,2,1,0,11,3,3,2,1,0,6,2,1,1,1,2,1,1,24,2,8,1,7,2,4,1,12,2,5,0,4,2,6,0,55],[0,2,2,1,0,9,2,1,0,10,1,1,2,10,1,2,2,2,1,27,2,4,1,2,2,4,1,1,2,4,1,13,2,2,0,6,2,8,0,18,2,4,0,24,2,1,0,6],[0,2,2,1,0,9,2,1,0,10,2,15,1,33,2,3,1,20,2,1,0,6,2,8,1,1,0,14,2,2,0,1,2,7,0,21,2,1,0,6],[0,2,2,1,0,9,2,1,0,1,2,1,0,6,2,17,1,56,2,1,0,6,2,6,1,3,0,13,2,12,0,20,2,1,0,5,1,1],[0,2,2,1,0,9,2,1,0,1,2,1,0,6,2,16,1,57,2,1,0,6,2,6,1,2,2,1,0,8,2,2,0,3,2,12,0,20,2,1,0,5,1,1],[0,2,2,1,0,9,2,1,0,1,2,1,0,6,2,12,1,60,2,17,0,7,2,20,0,14,2,5,0,6],[0,2,2,1,0,9,2,1,0,1,2,1,0,6,2,9,1,63,2,17,0,6,2,22,0,13,2,5,0,6],[0,2,2,1,1,3,3,3,2,4,0,8,2,9,1,64,2,16,0,5,2,16,1,1,2,6,0,13,2,5,0,6],[0,7,1,1,0,12,2,10,1,18,2,5,1,42,2,2,1,1,2,4,0,3,2,5,0,5,2,16,1,1,2,10,0,9,2,6,0,5],[0,7,1,1,2,4,0,8,2,9,1,11,0,2,1,6,2,5,1,46,2,3,0,4,2,6,1,2,2,10,1,2,2,4,0,5,2,4,1,4,0,6,2,8,0,5],[0,7,1,1,2,5,0,7,2,9,1,11,0,3,1,5,2,5,1,2,2,4,1,41,0,7,2,3,1,4,2,15,0,6,2,5,1,4,0,5,1,1,2,5,0,7],[0,7,1,1,2,5,0,4,2,11,1,12,0,3,1,1,2,2,1,2,2,5,1,2,2,5,1,40,0,8,2,2,1,3,2,6,0,3,2,5,0,10,2,7,0,5,1,1,2,4,0,8],[0,7,2,2,0,1,2,3,0,4,2,2,1,2,2,6,1,10,2,1,1,1,0,4,1,1,2,2,1,2,2,12,1,40,0,8,2,2,1,1,2,8,0,3,2,5,0,9,2,10,0,2,1,1,2,4,0,9],[3,1,1,2,0,6,2,10,1,3,2,5,1,8,2,3,0,9,2,15,1,38,0,8,2,4,0,3,2,4,0,3,2,5,0,9,2,11,1,3,2,3,0,9],[3,2,1,2,2,15,1,4,2,4,1,7,2,2,0,11,2,17,1,36,0,8,2,4,0,3,1,1,2,3,0,5,2,3,0,12,2,8,1,4,2,2,0,9],[2,19,1,6,2,3,1,5,2,3,0,11,2,18,1,32,0,1,1,2,0,7,2,5,0,3,1,2,0,7,2,7,0,8,2,8,1,4,0,11],[2,1,0,5,2,13,1,7,2,1,1,4,0,16,2,19,1,29,2,2,0,9,2,6,0,3,1,2,0,8,2,6,0,8,2,8,1,4,0,11],[2,1,0,5,2,8,0,4,2,1,1,3,0,2,1,6,0,28,2,16,1,6,0,7,1,4,0,15,2,6,0,15,2,6,0,29],[2,1,0,24,1,1,2,3,0,34,2,14,1,2,0,8,1,4,0,15,2,6,0,15,2,6,0,29],[0,25,2,2,0,36,2,15,1,1,0,27,2,6,0,15,2,6,0,29],[0,25,2,2,0,36,2,16,0,28,2,5,0,15,2,6,0,29],[0,63,2,16,0,28,2,6,0,14,2,8,0,27],[0,63,2,16,0,28,2,6,0,14,2,8,0,27],[0,63,2,16,0,29,2,9,0,8,2,4,0,33],[0,63,2,16,0,30,2,13,0,3,2,4,0,33],[0,63,2,20,0,29,2,17,0,33],[0,63,2,20,0,30,2,16,0,33],[0,55,3,1,2,29,0,29,2,6,0,42],[0,55,3,1,2,29,0,33,2,2,0,42],[0,55,3,1,2,29,0,33,2,1,1,1,0,42],[0,55,2,30,0,77],[0,59,2,8,0,4,2,1,0,90],[0,59,2,8,0,95],[0,61,2,6,0,95],[0,61,2,6,0,95],[0,61,2,6,0,95],[0,61,2,6,0,95],[0,61,2,6,0,95],[0,61,2,1,1,2,2,3,0,95],[0,63,3,2,0,97]]} diff --git a/packages/core/src/sim/movement.ts b/packages/core/src/sim/movement.ts index e7c5c61..58fab2c 100644 --- a/packages/core/src/sim/movement.ts +++ b/packages/core/src/sim/movement.ts @@ -725,7 +725,7 @@ function laneNavGoal( field, entity.x, entity.y, - isShortApproach ? DOCK_APPROACH_LOCAL_GOAL_CELLS : undefined, + isShortApproach ? dockApproachCells(mask.cellSizeX) : undefined, ); if (step !== null) return step; } @@ -742,7 +742,7 @@ function laneNavGoal( orderY, entity.x, entity.y, - isShortApproach ? DOCK_APPROACH_LOCAL_GOAL_CELLS : undefined, + isShortApproach ? dockApproachCells(ruleset.map.waterMask.cellSizeX) : undefined, ); return step ?? { x: orderX, y: orderY }; } @@ -780,11 +780,18 @@ function fieldToPoint(mask: WaterMask, x: number, y: number): NavField | null { return field; } -/** Hand-off distance (in cells) for a SHIP rounding land onto a dock: follow the - * field gradient to within ONE cell of the goal's water-access cell before the - * straight-line final step, so the ship rounds the coastal spit instead of - * beelining into it. Open hauls keep navStepToward's default (6). */ -const DOCK_APPROACH_LOCAL_GOAL_CELLS = 1; +/** Hand-off distance for a SHIP rounding land onto a dock, in WORLD UNITS + * (~one 128u legacy cell): follow the field gradient to within this range of + * the goal's water-access cell before the straight-line final step, so the + * ship rounds the coastal spit instead of beelining into it. Converted to + * grid hops per-mask so behavior is resolution-independent (the emitted + * grid is 64u cells as of the full-res lane deduction). Open hauls keep + * navStepToward's unit-derived default. */ +const DOCK_APPROACH_LOCAL_GOAL_UNITS = 128; + +function dockApproachCells(cellSize: number): number { + return Math.max(1, Math.round(DOCK_APPROACH_LOCAL_GOAL_UNITS / cellSize)); +} /** True if the straight segment (x0,y0)->(x1,y1) passes over any LAND cell * (sampled at half-cell resolution incl. the endpoint), i.e. a naive diff --git a/packages/core/src/sim/types.ts b/packages/core/src/sim/types.ts index 133d9e7..3d0e060 100644 --- a/packages/core/src/sim/types.ts +++ b/packages/core/src/sim/types.ts @@ -1773,17 +1773,22 @@ export function navStepToward( field: NavField, x: number, y: number, - localGoalDistCells = 6, + localGoalDistCells?: number, ): { x: number; y: number } | null { const { bounds, cols, rows, cellSizeX, cellSizeY, dist } = field; if (dist.length === 0) return null; // stub field (no real mask) -> straight line + // Default local-goal basin: ~768 WORLD UNITS (six 128u legacy cells), + // derived from the field's cell size so the hand-off distance is + // resolution-independent (the emitted grid is 64u cells as of the + // full-res lane deduction). + const localGoal = localGoalDistCells ?? Math.max(1, Math.round(768 / cellSizeX)); if (x < bounds.minX || x >= bounds.maxX || y <= bounds.minY || y > bounds.maxY) return null; const col = Math.floor((x - bounds.minX) / cellSizeX); const row = Math.floor((bounds.maxY - y) / cellSizeY); if (col < 0 || col >= cols || row < 0 || row >= rows) return null; const here = dist[row * cols + col] ?? NAV_UNREACHABLE; if (here === NAV_UNREACHABLE) return null; // on land / unreachable -> let slide handle it - if (here <= localGoalDistCells) return null; // near the goal -> straight line in + if (here <= localGoal) return null; // near the goal -> straight line in // Lowest-distance navigable 8-neighbour (deterministic neighbour order). const NEIGHBOURS: readonly [number, number][] = [ diff --git a/packages/core/test/fog.test.ts b/packages/core/test/fog.test.ts index c9fb66c..4bcf2c8 100644 --- a/packages/core/test/fog.test.ts +++ b/packages/core/test/fog.test.ts @@ -85,7 +85,7 @@ function findPair(blocked: boolean, minD: number, maxD: number): { a: { x: numbe for (let c = 2; c < cols - 2; c += 2) { if (cells[r * cols + c] !== 1) continue; const a = world(c, r); - const reach = Math.ceil(maxD / 128) + 1; + const reach = Math.ceil(maxD / cellSizeX) + 1; for (let dr = -reach; dr <= reach; dr += 2) { for (let dc = -reach; dc <= reach; dc += 2) { const nc = c + dc; diff --git a/packages/core/test/terrain-integration.test.ts b/packages/core/test/terrain-integration.test.ts index 184e3a5..1e47661 100644 --- a/packages/core/test/terrain-integration.test.ts +++ b/packages/core/test/terrain-integration.test.ts @@ -78,7 +78,8 @@ describe('terrain integration (real water mask)', () => { expect(ruleset.map.waterMask.cells.length).toBeGreaterThan(0); // The mask is SAILABILITY from the map's own PATHING MAP (war3map.wpm bit // 0x40 = no-water — the engine's enforced truth; see terrain.py), cropped - // to the 81x113 PLAYABLE tilepoint grid (the unplayable border removed; + // to the 162x226 PLAYABLE grid (64u cells — 2x the tilepoint spacing; + // lane-topology equivalence vs the raw 32u wpm is gate G6) (the unplayable border removed; // the WEST bound extended 3 cells west of the camera bounds so the Goblin // Potion Dealer shop sits off the grid edge — see docs/TERRAIN.md // WEST-BOUND EXTENSION), PLUS only MINIMAL 1-cell connectivity necks (so @@ -91,10 +92,10 @@ describe('terrain integration (real water mask)', () => { // real map separates. const water = ruleset.map.waterMask.cells.reduce((n, c) => n + c, 0); const total = ruleset.map.waterMask.cells.length; - expect(total).toBe(81 * 113); - // ~0.553: the wpm sailable fraction over the playable crop (+ necks + - // moats). Same band as the extractor's fail-loud gate [0.50, 0.62]: - // well above = colour-key-style over-watering, well below = over-dry. + expect(total).toBe(162 * 226); // 64u cells (full-res lane deduction) + // ~0.513: the wpm sailable fraction at 64u cells with LAND-BIASED ties + // (straddling walls are kept, conservatively thickening land). Same band + // as the extractor's fail-loud gate [0.50, 0.62]. expect(water / total).toBeGreaterThan(0.5); expect(water / total).toBeLessThan(0.62); // Nav fields are populated (a real flood from each base goal). diff --git a/packages/server/test/ai-match.test.ts b/packages/server/test/ai-match.test.ts index 409c476..ae59a63 100644 --- a/packages/server/test/ai-match.test.ts +++ b/packages/server/test/ai-match.test.ts @@ -351,7 +351,12 @@ describe('bot-vs-bot match (real brain, both teams AI)', () => { // touched. The HQ-damage milestone is therefore replaced by the funnel's real // signal: enemy towers keep taking chip from held creeps across the run. it('over a long match the funnel keeps engaging the enemy towers, without idling in retreat', async () => { - const LONG_CAP = 6000; + // 9000 ticks: on the TRUE 64u map (full-res lane deduction) the lanes are + // tighter than the old blobby 128u mask — opposing waves clash in narrow + // chokes and the first tower-chipping leaker lands around tick ~5000 in a + // 10-captain probe (later with only these 2 captains). Same window as the + // core terrain-integration grind test. + const LONG_CAP = 9000; const invPeak = new Map(); const retreatThinks = new Map(); const totalThinks = new Map(); diff --git a/tools/extractor/terrain.py b/tools/extractor/terrain.py index ff731e5..f1cede6 100644 --- a/tools/extractor/terrain.py +++ b/tools/extractor/terrain.py @@ -170,7 +170,17 @@ SAMPLE_PATCH = 3 # NxN minimap pixel patch sampled per tile (majority vote) # --- connectivity-neck knobs (kept small/explicit so additions are auditable) -- -ACCESS_CELLS = 2 # a shop is "sea-reachable" if main-sea water is within this +# EMIT SUBDIVISION (owner-directed 2026-07-09: "the full res map is accurate if +# you can deduce the lanes correctly"): the emitted grid is 64u cells — HALF the +# 128u tilepoint spacing, double resolution. Measured on the wpm: real channels +# are never narrower than 128u (4 subcells) and real walls never thinner than +# 64u (2 subcells); a 128u grid provably cannot represent an unaligned 128u +# channel (both straddling cells fall under 50% water -> lane LOST) nor a 64u +# wall inside a majority-water cell (-> lanes MERGED). At 64u with land-biased +# ties, every real channel and wall survives quantization by construction. +EMIT_SUBDIV = 2 # 64u cells (128 / EMIT_SUBDIV) + +ACCESS_CELLS = 2 * EMIT_SUBDIV # a shop is "sea-reachable" if main-sea water is within this # Manhattan radius (~256u). Kept tight so the carved neck # brings navigable water RIGHT UP to the shop on its sea-facing # side -- a shop with water only 3 cells away on the far side @@ -197,7 +207,7 @@ # are matched by their owner-circled world coords (deterministic, explicit -- NOT # "western-most x", which would grab the north Pigfarm Elven Library instead). WEST_ISLAND_SHOPS = ((-4640.0, -928.0), (-4960.0, -5344.0)) # LumberMill, GoblinPotion -WEST_ISLAND_CORE_R = 2 # filled (2R+1)x(2R+1) land core; moat ring at chebyshev R+1. +WEST_ISLAND_CORE_R = 2 * EMIT_SUBDIV # filled (2R+1)x(2R+1) land core; moat ring at chebyshev R+1. # --------------------------------------------------------------------------- @@ -230,22 +240,34 @@ def wpm_sailable_grid(geom: dict, wpm: dict, map_min_x: float, map_min_y: float) 16 wpm subcells (4x4 x 32u) under the 128u cell that do NOT carry the no-water bit. Same shape/orientation as classify_grid's output.""" cols, nrows = geom["cols"], geom["rows"] + csx, csy = geom["csx"], geom["csy"] ww, wh, flags = wpm["width"], wpm["height"], wpm["flags"] + # Subcell sample offsets: centers of the 32u wpm cells under one emitted + # cell. LAND-BIASED tie (water only when STRICTLY more than half the + # subcells sail): preserves the original's 64u walls when they straddle a + # cell boundary — both straddling cells land as LAND (wall kept, + # conservatively thickened) instead of both as water (lanes merged). Real + # channels are >=128u wide (measured), so a straddling channel still owns + # a fully-water cell and survives. + nsx = max(1, round(csx / 32)) + nsy = max(1, round(csy / 32)) + offs_x = [(-csx / 2) + (i + 0.5) * 32 for i in range(nsx)] + offs_y = [(-csy / 2) + (i + 0.5) * 32 for i in range(nsy)] grid: list[list[int]] = [] for r in range(nrows): line: list[int] = [] for c in range(cols): x, y = cell_center(c, r, geom) total = sailable = 0 - for oy in (-48.0, -16.0, 16.0, 48.0): - for ox in (-48.0, -16.0, 16.0, 48.0): + for oy in offs_y: + for ox in offs_x: wx = int((x + ox - map_min_x) // 32) wy = int((y + oy - map_min_y) // 32) if 0 <= wx < ww and 0 <= wy < wh: total += 1 if not (flags[wy * ww + wx] & 0x40): sailable += 1 - line.append(1 if (total > 0 and sailable / total >= 0.5) else 0) + line.append(1 if (total > 0 and sailable * 2 > total) else 0) grid.append(line) return grid @@ -298,7 +320,10 @@ def crop_geometry(w3e: dict, cols_idx: list[int], rows_idx: list[int], playable: max_x = cx + cols_idx[-1] * TILE_SPACING + half min_y = cy + rows_idx[0] * TILE_SPACING - half max_y = cy + rows_idx[-1] * TILE_SPACING + half - cols, nrows = len(cols_idx), len(rows_idx) + # EMIT_SUBDIV: same playable bounds, finer cells (64u at 2) — see the + # constant's doc. Every downstream consumer (cell_for/cell_center, carves, + # gates, the sim) works off cols/rows/csx/csy generically. + cols, nrows = len(cols_idx) * EMIT_SUBDIV, len(rows_idx) * EMIT_SUBDIV bounds = {"minX": min_x, "minY": min_y, "maxX": max_x, "maxY": max_y} return { "bounds": bounds, @@ -1104,6 +1129,189 @@ def _water_connected(rows: list[list[int]], cols: int, nrows: int, # --------------------------------------------------------------------------- +def restore_straddled_channels(rows: list[list[int]], wpm: dict, geom: dict, + map_min_x: float, map_min_y: float) -> int: + """Undo the land-biased tie's channel NARROWING without undoing its wall + PRESERVATION. The tie votes a half-water cell to land; when a real >=128u + channel straddles the 64u grid, that shaves it to a single 64u cell — + narrower than the original (creeps are ~60u wide; opposing waves plug a + 64u choke that the real 128u choke lets flow). Flip a tied-to-land cell + back to water ONLY when every sailable wpm subcell under it AND under its + 8-adjacent water cells belongs to ONE full-res component — then the flip + widens an existing channel and provably cannot bridge two distinct + channels (the walls the tie exists to protect). Single deterministic + scan; returns cells flipped.""" + cols, nrows = geom["cols"], geom["rows"] + csx, csy = geom["csx"], geom["csy"] + ww, wh, flags = wpm["width"], wpm["height"], wpm["flags"] + + def sail(sx: int, sy: int) -> bool: + return 0 <= sx < ww and 0 <= sy < wh and not (flags[sy * ww + sx] & 0x40) + + # Full-res component labels over the whole wpm (cheap, ~200k cells). + lbl = [-1] * (ww * wh) + next_id = 0 + for start in range(ww * wh): + if lbl[start] >= 0 or (flags[start] & 0x40): + continue + stack = [start] + lbl[start] = next_id + while stack: + idx = stack.pop() + x, y = idx % ww, idx // ww + for dx in (-1, 0, 1): + for dy in (-1, 0, 1): + nx, ny = x + dx, y + dy + if (dx or dy) and 0 <= nx < ww and 0 <= ny < wh: + nidx = ny * ww + nx + if lbl[nidx] < 0 and not (flags[nidx] & 0x40): + lbl[nidx] = next_id + stack.append(nidx) + next_id += 1 + + nsx = max(1, round(csx / 32)) + nsy = max(1, round(csy / 32)) + offs_x = [(-csx / 2) + (i + 0.5) * 32 for i in range(nsx)] + offs_y = [(-csy / 2) + (i + 0.5) * 32 for i in range(nsy)] + + def cell_labels(c: int, r: int) -> set[int]: + x, y = cell_center(c, r, geom) + out: set[int] = set() + for oy in offs_y: + for ox in offs_x: + sx = int((x + ox - map_min_x) // 32) + sy = int((y + oy - map_min_y) // 32) + if 0 <= sx < ww and 0 <= sy < wh and not (flags[sy * ww + sx] & 0x40): + out.add(lbl[sy * ww + sx]) + return out + + flipped = 0 + for r in range(nrows): + for c in range(cols): + if rows[r][c]: + continue + own = cell_labels(c, r) + if len(own) != 1: + continue # no water under it, or it touches two channels: keep land + union = set(own) + for dc in (-1, 0, 1): + for dr in (-1, 0, 1): + nc, nr = c + dc, r + dr + if (dc or dr) and 0 <= nc < cols and 0 <= nr < nrows and rows[nr][nc]: + union |= cell_labels(nc, nr) + if union == own: + rows[r][c] = 1 + flipped += 1 + return flipped + + +def lane_topology_gate(rows: list[list[int]], wpm: dict, layout: dict, geom: dict, + map_min_x: float, map_min_y: float) -> dict: + """G6 (hard gate): the emitted grid must preserve the FULL-RES lane + topology — for every pair of gameplay anchors (HQs, harbours, shops, lane + spawns), 'water-connected on the emitted grid' must equal 'water-connected + on the raw 32u wpm' within the playable crop. This is the owner's + 2026-07-09 directive made permanent: the full-res map is the truth; the + grid is only correct if the lanes deduce correctly. Fails loud on any + lost channel (connected in wpm, separated in grid) or false merge + (separated in wpm, connected in grid).""" + cols, nrows = geom["cols"], geom["rows"] + b = geom["bounds"] + ww, wh, flags = wpm["width"], wpm["height"], wpm["flags"] + + def sail(sx: int, sy: int) -> bool: + return 0 <= sx < ww and 0 <= sy < wh and not (flags[sy * ww + sx] & 0x40) + + # 8-connected component labels on the emitted grid. + lbl_g = [[-1] * cols for _ in range(nrows)] + next_id = 0 + for r0 in range(nrows): + for c0 in range(cols): + if not rows[r0][c0] or lbl_g[r0][c0] >= 0: + continue + q = deque([(c0, r0)]) + lbl_g[r0][c0] = next_id + while q: + c, r = q.popleft() + for dc in (-1, 0, 1): + for dr in (-1, 0, 1): + nc, nr = c + dc, r + dr + if (dc or dr) and 0 <= nc < cols and 0 <= nr < nrows \ + and rows[nr][nc] and lbl_g[nr][nc] < 0: + lbl_g[nr][nc] = next_id + q.append((nc, nr)) + next_id += 1 + + # 8-connected component labels on the raw wpm subcells within the crop. + sx0, sy0 = int((b["minX"] - map_min_x) // 32), int((b["minY"] - map_min_y) // 32) + sx1, sy1 = int((b["maxX"] - map_min_x) // 32), int((b["maxY"] - map_min_y) // 32) + lbl_w: dict[tuple[int, int], int] = {} + wid = 0 + for sy in range(sy0, sy1): + for sx in range(sx0, sx1): + if not sail(sx, sy) or (sx, sy) in lbl_w: + continue + q = deque([(sx, sy)]) + lbl_w[(sx, sy)] = wid + while q: + x, y = q.popleft() + for dx in (-1, 0, 1): + for dy in (-1, 0, 1): + n = (x + dx, y + dy) + if (dx or dy) and n not in lbl_w and sx0 <= n[0] < sx1 \ + and sy0 <= n[1] < sy1 and sail(*n): + lbl_w[n] = wid + q.append(n) + wid += 1 + + anchors: list[tuple[str, float, float]] = [] + for s in layout.get("structures", []): + if s.get("role") in ("hq", "harbour", "shop") and s.get("x") is not None: + anchors.append((str(s.get("name") or s.get("role")), s["x"], s["y"])) + for lane in layout.get("creepSpawns", {}).get("lanes", []): + sp = lane.get("spawnPoint", {}) + anchors.append((str(lane.get("id")), sp["x"], sp["y"])) + + def near_g(x: float, y: float, rad: int = 6): + c0, r0 = cell_for(x, y, geom) + best = None + for dr in range(-rad, rad + 1): + for dc in range(-rad, rad + 1): + c, r = c0 + dc, r0 + dr + if 0 <= c < cols and 0 <= r < nrows and rows[r][c]: + d = dc * dc + dr * dr + if best is None or d < best[0]: + best = (d, lbl_g[r][c]) + return best and best[1] + + def near_w(x: float, y: float, rad: int = 12): + s0 = (int((x - map_min_x) // 32), int((y - map_min_y) // 32)) + best = None + for dy in range(-rad, rad + 1): + for dx in range(-rad, rad + 1): + n = (s0[0] + dx, s0[1] + dy) + if n in lbl_w: + d = dx * dx + dy * dy + if best is None or d < best[0]: + best = (d, lbl_w[n]) + return best and best[1] + + lab_g = [near_g(x, y) for (_, x, y) in anchors] + lab_w = [near_w(x, y) for (_, x, y) in anchors] + mismatches: list[str] = [] + for i in range(len(anchors)): + for j in range(i + 1, len(anchors)): + same_g = lab_g[i] is not None and lab_g[i] == lab_g[j] + same_w = lab_w[i] is not None and lab_w[i] == lab_w[j] + if same_g != same_w: + kind = "LOST CHANNEL" if same_w else "FALSE MERGE" + mismatches.append(f"{kind}: {anchors[i][0]} <-> {anchors[j][0]}") + if mismatches: + raise SystemExit("terrain: lane-topology gate G6 FAILED (emitted grid vs full-res wpm):\n " + + "\n ".join(mismatches)) + return {"anchors": len(anchors), "pairwiseMismatches": 0} + + def validate(rows: list[list[int]], layout: dict, geom: dict) -> dict: """Fail-loud fidelity gate (G3 shops / G5 structures+spawns / base-to-base / G2 lane widths / fraction band).""" @@ -1336,7 +1544,7 @@ def entrance_count(island_cells: set[tuple[int, int]]) -> int: stack.append(nb) return groups - def island_land(world_x: float, world_y: float, max_cells: int = 60) -> set[tuple[int, int]]: + def island_land(world_x: float, world_y: float, max_cells: int = 60 * EMIT_SUBDIV * EMIT_SUBDIV) -> set[tuple[int, int]]: """4-connected LAND component containing the shop cell (the island the owner sails around). Capped so a connection to the mainland is not counted as 'the island'.""" @@ -1688,6 +1896,7 @@ def main() -> None: # rows = the working mask; keep the denoised raw wpm grid as the reference # the G2 agreement compares the final (carved) mask against. rows = [list(r) for r in sail] + restored = restore_straddled_channels(rows, wpm, geom, w3e["centerX"], w3e["centerY"]) removed = drop_singletons(rows, geom["cols"], geom["rows"]) ref_after_denoise = [list(r) for r in rows] # the reference G2 compares against @@ -1696,6 +1905,7 @@ def main() -> None: # fire; they only guarantee every shop/base/dock reaches the sea (G3/G4). neck_report = carve_connectivity(rows, soft, layout, geom) neck_report["singletonCellsDropped"] = removed + neck_report["straddledChannelCellsRestored"] = restored # 6. WEST-ISLAND LOOPS (owner-approved): ring each of the two west-island shops # with a closed 1-cell navigable moat + EXACTLY ONE entrance (deterministic @@ -1707,10 +1917,14 @@ def main() -> None: west_island_report = carve_west_island_loops(rows, layout, geom) neck_report["westIslandLoops"] = west_island_report + # G6: lane-topology equivalence vs the full-res pathing map (hard gate). + topo_report = lane_topology_gate(rows, wpm, layout, geom, w3e["centerX"], w3e["centerY"]) + rle = [rle_encode_row(r) for r in rows] wf = water_fraction(rows) report = validate(rows, layout, geom) report["necks"] = neck_report + report["laneTopologyVsWpm"] = topo_report report["sideRoutes"] = confirm_side_routes(rows, ref_after_denoise, layout, geom) # OPTIONAL depth metadata (0=land,1=deep,2=shallow,3=pink): the minimap colour