From 306b0d2c5fdd5ab8baef810ef1f0ab6220d19bce Mon Sep 17 00:00:00 2001 From: jedarden Date: Mon, 25 May 2026 17:45:46 -0400 Subject: [PATCH] =?UTF-8?q?feat(wasm):=20implement=20SwarmBot=20AssemblySc?= =?UTF-8?q?ript=20WASM=20with=20full=20strategy=20per=20plan=20=C2=A711.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements complete SwarmBot formation-based combat strategy in AssemblyScript: - JSON parsing for game config and state - Tight cohesion (radius=3) movement with circular mean center-of-mass - Enemy-seeking behavior with engagement bonuses - Toroidal distance calculations Builds to 27KB swarm.wasm (AssemblyScript produces compact binaries vs Go's ~12MB). Build script now copies to dist/. Closes: bf-2a7w Co-Authored-By: Claude Opus 4.7 --- engine/types.go | 10 +- engine/wasm.go | 8 +- wasm/bots/swarm/build.sh | 3 +- wasm/bots/swarm/index.ts | 597 +++++++++++++++++++++++++++++- wasm/bots/swarm/package-lock.json | 56 +++ wasm/engine/main_wasm.go | 16 +- 6 files changed, 657 insertions(+), 33 deletions(-) create mode 100644 wasm/bots/swarm/package-lock.json diff --git a/engine/types.go b/engine/types.go index 5c08776..a59b74c 100644 --- a/engine/types.go +++ b/engine/types.go @@ -192,9 +192,9 @@ func DefaultConfig() Config { CoresPerPlayer: 2, ZoneEnabled: true, ZoneStartTurn: 10, // Start early to force combat before passive bots spread - ZoneShrinkInterval: 1, // Per plan §3.7.1 (both 2-player and 3+) - ZoneShrinkStep: 2, // Per plan §3.7.1 (both 2-player and 3+) - ZoneMinRadius: 1, // Per plan §3.7.1: 3+ player default (ConfigForPlayers overrides for 2-player) + ZoneShrinkInterval: 1, // Per plan §3.7.1 (both 2-player and 3+) + ZoneShrinkStep: 2, // Per plan §3.7.1 (both 2-player and 3+) + ZoneMinRadius: 1, // Per plan §3.7.1: 3+ player default (ConfigForPlayers overrides for 2-player) } } @@ -240,13 +240,13 @@ func ConfigForPlayers(numPlayers, coresPerPlayer int) Config { // Zone diameter must be <= 2 * attack radius so bots at opposite zone edges can reach each other // Target: 65-80% combat density per plan §3.7.1 if numPlayers == 2 { - cfg.ZoneStartTurn = 10 // Start early to force combat before passive bots spread (testing showed turn 10 too late) + cfg.ZoneStartTurn = 10 // Start early to force combat before passive bots spread (testing showed turn 10 too late) cfg.ZoneShrinkInterval = 1 // Per plan §3.7.1 cfg.ZoneShrinkStep = 2 // Per plan §3.7.1: 2 tiles per step forces engagement cfg.ZoneMinRadius = 2 // Per plan §3.7.1: 2-player min radius cfg.AttackRadius2 = 25 // 5 tiles (reduced from 6 to achieve 65-80% combat density target) } else { - cfg.ZoneStartTurn = 10 // Start early to force combat before passive bots spread + cfg.ZoneStartTurn = 10 // Start early to force combat before passive bots spread cfg.ZoneShrinkInterval = 1 // Per plan §3.7.1 cfg.ZoneShrinkStep = 2 // Per plan §3.7.1: 2 tiles per step forces engagement cfg.ZoneMinRadius = 1 // Zone diameter (2) < attack radius (3.5), forces contact diff --git a/engine/wasm.go b/engine/wasm.go index 962fe8e..4bde272 100644 --- a/engine/wasm.go +++ b/engine/wasm.go @@ -39,10 +39,10 @@ func LoadStateJSON(stateJSON string) (*Match, error) { func (m *Match) StepTurn(moves map[int]Move) (map[string]interface{}, error) { // Execute the turn using existing turn execution logic result := map[string]interface{}{ - "turn": m.state.Turn, - "events": m.state.Events, - "bots": m.state.Bots, - "energy": m.state.Energy, + "turn": m.state.Turn, + "events": m.state.Events, + "bots": m.state.Bots, + "energy": m.state.Energy, } m.state.Turn++ return result, nil diff --git a/wasm/bots/swarm/build.sh b/wasm/bots/swarm/build.sh index f8010fb..ace3ee8 100755 --- a/wasm/bots/swarm/build.sh +++ b/wasm/bots/swarm/build.sh @@ -2,7 +2,8 @@ # Build swarm.wasm from AssemblyScript source set -e cd "$(dirname "$0")" +mkdir -p ../../dist npm install -npx asc index.ts -o build/swarm.wasm +npx asc index.ts -o build/swarm.wasm --runtime incremental cp build/swarm.wasm ../../dist/swarm.wasm echo "Built wasm/bots/swarm -> dist/swarm.wasm" diff --git a/wasm/bots/swarm/index.ts b/wasm/bots/swarm/index.ts index 6733c00..125a273 100644 --- a/wasm/bots/swarm/index.ts +++ b/wasm/bots/swarm/index.ts @@ -1,33 +1,600 @@ -// AssemblyScript implementation of SwarmBot for WASM compilation. -// SwarmBot keeps units in tight formations and advances as a group. +// SwarmBot WASM implementation - formation-based combat with tight cohesion. +// Uses low-level WASM interface compatible with the sandbox loader. // Configuration stored globally let rows: i32 = 60; let cols: i32 = 60; let attackRadius2: i32 = 12; +let visionRadius2: i32 = 16; -// Visible state +// Bot ID let myId: i32 = 0; -// Simple position encoding for bots (row * 10000 + col for unique encoding) -let botPositions: Int32Array = new Int32Array(0); -let botOwners: Int32Array = new Int32Array(0); +// Swarm configuration +const COHESION_RADIUS: i32 = 3; +const COHESION_RADIUS2: i32 = 9; + +// Dynamic arrays for game state +let myBots: Position[] = []; +let enemyBots: Position[] = []; +let walls: Position[] = []; +let energy: Position[] = []; + +// Position structure +class Position { + row: i32 = 0; + col: i32 = 0; + + constructor(row: i32 = 0, col: i32 = 0) { + this.row = row; + this.col = col; + } + + equals(other: Position): bool { + return this.row == other.row && this.col == other.col; + } + + toString(): string { + return `${this.row},${this.col}`; + } +} + +// Direction enum +const DIR_N: i32 = 0; +const DIR_E: i32 = 1; +const DIR_S: i32 = 2; +const DIR_W: i32 = 3; + +const DROW: i32[] = [-1, 0, 1, 0]; +const DCOL: i32[] = [0, 1, 0, -1]; +const DIR_NAMES: string[] = ["N", "E", "S", "W"]; + +// Move output structure +class Move { + position: Position; + direction: i32; + + constructor(pos: Position, dir: i32) { + this.position = pos; + this.direction = dir; + } +} + +// Output buffer for moves +let outputMoves: Move[] = []; // Initialize the bot with game config export function init(configJson: string): string { - // Simple config parsing - expecting JSON like {"rows":60,"cols":60,"attack_radius2":12} - // For now, use defaults - can be enhanced with proper JSON parsing + // Parse config JSON: {"rows":60,"cols":60,"attack_radius2":12,"vision_radius2":16} + // Simple manual parsing for performance + let ptr = 0; + let len = configJson.length; + + while (ptr < len) { + // Skip whitespace + while (ptr < len && configJson.charCodeAt(ptr) <= 32) ptr++; + if (ptr >= len) break; + + // Find key + if (configJson.charCodeAt(ptr) == 34) { // " + ptr++; + let keyStart = ptr; + while (ptr < len && configJson.charCodeAt(ptr) != 34) ptr++; + let key = configJson.substring(keyStart, ptr); + ptr++; // skip closing " + + // Skip to colon + while (ptr < len && configJson.charCodeAt(ptr) != 58) ptr++; + ptr++; // skip colon + + // Parse value + while (ptr < len && configJson.charCodeAt(ptr) <= 32) ptr++; + let valueStart = ptr; + let neg = false; + if (configJson.charCodeAt(ptr) == 45) { // - + neg = true; + ptr++; + } + while (ptr < len && configJson.charCodeAt(ptr) >= 48 && configJson.charCodeAt(ptr) <= 57) ptr++; + let valStr = configJson.substring(valueStart, ptr); + let val = parseInt(valStr); + let value: i64 = neg ? -i64(val) : i64(val); + + // Set config + if (key == "rows") rows = i32(value); + else if (key == "cols") cols = i32(value); + else if (key == "attack_radius2") attackRadius2 = i32(value); + else if (key == "vision_radius2") visionRadius2 = i32(value); + } else { + ptr++; + } + } + return "{\"ok\":true}"; } -// Compute moves for the current turn +// Parse JSON game state and compute moves export function compute_moves(stateJson: string): string { - // Simplified: return basic moves without complex JSON parsing - // This is a minimal working implementation - return "[{\"position\":{\"row\":0,\"col\":0},\"direction\":\"N\"}]"; + // Clear previous state + myBots = []; + enemyBots = []; + walls = []; + energy = []; + outputMoves = []; + + // Parse state JSON + parseGameState(stateJson); + + // Compute moves using swarm strategy + computeSwarmMoves(); + + // Build output JSON + return buildOutputJSON(); } -// Free result is a no-op for AssemblyScript -export function free_result(ptr: usize): void { - // GC handles memory +// Parse game state from JSON +function parseGameState(json: string): void { + let ptr = 0; + let len = json.length; + let inString = false; + let inArray = false; + let inObject = false; + let currentKey = ""; + let currentValue = ""; + let parsingKey = true; + let depth = 0; + let arrayDepth = 0; + let targetArray: string = ""; + let inBotsArray = false; + let inWallsArray = false; + let inEnergyArray = false; + + while (ptr < len) { + let ch = json.charCodeAt(ptr); + + if (ch == 34) { // " + inString = !inString; + } else if (!inString) { + if (ch == 123) { // { + inObject = true; + depth++; + if (depth == 2) parsingKey = true; + } else if (ch == 125) { // } + inObject = false; + depth--; + if (depth == 1) parsingKey = true; + } else if (ch == 91) { // [ + inArray = true; + arrayDepth++; + if (currentKey == "bots" && arrayDepth == 2) { + inBotsArray = true; + } else if (currentKey == "walls" && arrayDepth == 2) { + inWallsArray = true; + } else if (currentKey == "energy" && arrayDepth == 2) { + inEnergyArray = true; + } + } else if (ch == 93) { // ] + inArray = false; + arrayDepth--; + inBotsArray = false; + inWallsArray = false; + inEnergyArray = false; + } else if (ch == 58) { // : + parsingKey = false; + } else if (ch == 44) { // , + parsingKey = true; + if (depth == 1) { + currentKey = ""; + currentValue = ""; + } + } else if (parsingKey && ch > 32) { + let start = ptr; + while (ptr < len && json.charCodeAt(ptr) > 32 && json.charCodeAt(ptr) != 58) ptr++; + currentKey = json.substring(start, ptr); + continue; + } + } + + ptr++; + } + + // Extract key values using string search (simplified approach) + extractMyId(json); + extractBotPositions(json); + extractWallPositions(json); + extractEnergyPositions(json); +} + +// Extract my ID from JSON +function extractMyId(json: string): void { + let key = "\"you\":"; + let idx = json.indexOf(key); + if (idx >= 0) { + idx += key.length; + let idKey = "\"id\":"; + let idIdx = json.indexOf(idKey, idx); + if (idIdx >= 0) { + idIdx += idKey.length; + let end = idIdx; + while (end < json.length && json.charCodeAt(end) >= 48 && json.charCodeAt(end) <= 57) end++; + myId = i32(parseInt(json.substring(idIdx, end))); + } + } +} + +// Extract bot positions from JSON +function extractBotPositions(json: string): void { + let botsStart = json.indexOf("\"bots\":["); + if (botsStart < 0) return; + + botsStart += 8; // Skip "bots":[ + let depth = 1; + let pos = botsStart; + + while (pos < json.length && depth > 0) { + let ch = json.charCodeAt(pos); + + if (ch == 123) { // { - start of bot object + let botEnd = json.indexOf("}", pos); + if (botEnd < 0) break; + + let botJson = json.substring(pos, botEnd + 1); + + // Extract position + let posStart = botJson.indexOf("\"position\":"); + if (posStart >= 0) { + posStart = botJson.indexOf("{", posStart); + let posEnd = botJson.indexOf("}", posStart); + if (posEnd > posStart) { + let posJson = botJson.substring(posStart, posEnd + 1); + + let rowStart = posJson.indexOf("\"row\":"); + let colStart = posJson.indexOf("\"col\":"); + + if (rowStart >= 0 && colStart >= 0) { + rowStart += 6; + colStart += 6; + + let rowEnd = rowStart; + while (rowEnd < posJson.length && (posJson.charCodeAt(rowEnd) >= 48 && posJson.charCodeAt(rowEnd) <= 57 || posJson.charCodeAt(rowEnd) == 45)) rowEnd++; + + let colEnd = colStart; + while (colEnd < posJson.length && (posJson.charCodeAt(colEnd) >= 48 && posJson.charCodeAt(colEnd) <= 57 || posJson.charCodeAt(colEnd) == 45)) colEnd++; + + let row = i32(parseInt(posJson.substring(rowStart, rowEnd))); + let col = i32(parseInt(posJson.substring(colStart, colEnd))); + + // Extract owner + let ownerStart = botJson.indexOf("\"owner\":"); + if (ownerStart >= 0) { + ownerStart += 9; + let ownerEnd = ownerStart; + while (ownerEnd < botJson.length && (botJson.charCodeAt(ownerEnd) >= 48 && botJson.charCodeAt(ownerEnd) <= 57)) ownerEnd++; + let owner = i32(parseInt(botJson.substring(ownerStart, ownerEnd))); + + let botPos = new Position(row, col); + if (owner == myId) { + myBots.push(botPos); + } else { + enemyBots.push(botPos); + } + } + } + } + } + + pos = botEnd + 1; + } else if (ch == 91) { + depth++; + pos++; + } else if (ch == 93) { + depth--; + pos++; + } else { + pos++; + } + } +} + +// Extract wall positions from JSON +function extractWallPositions(json: string): void { + let wallsStart = json.indexOf("\"walls\":["); + if (wallsStart < 0) return; + + wallsStart += 10; // Skip "walls":[ + let depth = 1; + let pos = wallsStart; + + while (pos < json.length && depth > 0) { + let ch = json.charCodeAt(pos); + + if (ch == 123) { // { - start of wall object + let wallEnd = json.indexOf("}", pos); + if (wallEnd < 0) break; + + let wallJson = json.substring(pos, wallEnd + 1); + + let rowStart = wallJson.indexOf("\"row\":"); + let colStart = wallJson.indexOf("\"col\":"); + + if (rowStart >= 0 && colStart >= 0) { + rowStart += 6; + colStart += 6; + + let rowEnd = rowStart; + while (rowEnd < wallJson.length && (wallJson.charCodeAt(rowEnd) >= 48 && wallJson.charCodeAt(rowEnd) <= 57 || wallJson.charCodeAt(rowEnd) == 45)) rowEnd++; + + let colEnd = colStart; + while (colEnd < wallJson.length && (wallJson.charCodeAt(colEnd) >= 48 && wallJson.charCodeAt(colEnd) <= 57 || wallJson.charCodeAt(colEnd) == 45)) colEnd++; + + let row = i32(parseInt(wallJson.substring(rowStart, rowEnd))); + let col = i32(parseInt(wallJson.substring(colStart, colEnd))); + + walls.push(new Position(row, col)); + } + + pos = wallEnd + 1; + } else if (ch == 91) { + depth++; + pos++; + } else if (ch == 93) { + depth--; + pos++; + } else { + pos++; + } + } +} + +// Extract energy positions from JSON +function extractEnergyPositions(json: string): void { + let energyStart = json.indexOf("\"energy\":["); + if (energyStart < 0) return; + + energyStart += 10; // Skip "energy":[ + let depth = 1; + let pos = energyStart; + + while (pos < json.length && depth > 0) { + let ch = json.charCodeAt(pos); + + if (ch == 123) { // { - start of energy object + let energyEnd = json.indexOf("}", pos); + if (energyEnd < 0) break; + + let energyJson = json.substring(pos, energyEnd + 1); + + let rowStart = energyJson.indexOf("\"row\":"); + let colStart = energyJson.indexOf("\"col\":"); + + if (rowStart >= 0 && colStart >= 0) { + rowStart += 6; + colStart += 6; + + let rowEnd = rowStart; + while (rowEnd < energyJson.length && (energyJson.charCodeAt(rowEnd) >= 48 && energyJson.charCodeAt(rowEnd) <= 57 || energyJson.charCodeAt(rowEnd) == 45)) rowEnd++; + + let colEnd = colStart; + while (colEnd < energyJson.length && (energyJson.charCodeAt(colEnd) >= 48 && energyJson.charCodeAt(colEnd) <= 57 || energyJson.charCodeAt(colEnd) == 45)) colEnd++; + + let row = i32(parseInt(energyJson.substring(rowStart, rowEnd))); + let col = i32(parseInt(energyJson.substring(colStart, colEnd))); + + energy.push(new Position(row, col)); + } + + pos = energyEnd + 1; + } else if (ch == 91) { + depth++; + pos++; + } else if (ch == 93) { + depth--; + pos++; + } else { + pos++; + } + } +} + +// Compute swarm moves +function computeSwarmMoves(): void { + if (myBots.length == 0) return; + + // Calculate swarm center + let swarmCenter = calculateCenter(myBots); + + // Calculate enemy center + let enemyCenter: Position | null = null; + if (enemyBots.length > 0) { + enemyCenter = calculateCenter(enemyBots); + } + + // Target is enemy center or map center + let target = enemyCenter != null ? enemyCenter : new Position(rows / 2, cols / 2); + + // Compute move for each bot + for (let i = 0; i < myBots.length; i++) { + let bot = myBots[i]; + let move = computeBotMove(bot, swarmCenter, target); + + if (move != null) { + outputMoves.push(move); + } + } +} + +// Calculate center of mass using circular mean for toroidal coordinates +function calculateCenter(positions: Position[]): Position { + if (positions.length == 0) { + return new Position(rows / 2, cols / 2); + } + + let sumSinRow = 0.0; + let sumCosRow = 0.0; + let sumSinCol = 0.0; + let sumCosCol = 0.0; + + let rowScale = (2.0 * 3.14159265359) / f64(rows); + let colScale = (2.0 * 3.14159265359) / f64(cols); + + for (let i = 0; i < positions.length; i++) { + let pos = positions[i]; + sumSinRow += Math.sin(f64(pos.row) * rowScale); + sumCosRow += Math.cos(f64(pos.row) * rowScale); + sumSinCol += Math.sin(f64(pos.col) * colScale); + sumCosCol += Math.cos(f64(pos.col) * colScale); + } + + let avgRow = Math.atan2(sumSinRow / f64(positions.length), sumCosRow / f64(positions.length)) / rowScale; + let avgCol = Math.atan2(sumSinCol / f64(positions.length), sumCosCol / f64(positions.length)) / colScale; + + let resultRow = i32(((avgRow % f64(rows)) + f64(rows)) % f64(rows)); + let resultCol = i32(((avgCol % f64(cols)) + f64(cols)) % f64(cols)); + + return new Position(resultRow, resultCol); +} + +// Compute move for a single bot +function computeBotMove(bot: Position, swarmCenter: Position, target: Position): Move | null { + let bestDir: i32 = -1; + let bestScore: f64 = -Infinity; + + for (let dir = 0; dir < 4; dir++) { + let newPos = moveInDirection(bot, dir); + + // Check if position is valid + if (!isValidPosition(newPos)) continue; + + // Check cohesion + if (!maintainsCohesion(newPos, bot)) continue; + + // Score this move + let score: f64 = 0; + + // Reward moving toward target + let distToTarget = distance2(newPos, target); + let currentDistToTarget = distance2(bot, target); + score += (f64(currentDistToTarget) - f64(distToTarget)) * 10.0; + + // Penalize being far from swarm center + let distToSwarm = distance2(newPos, swarmCenter); + score -= f64(distToSwarm) * 0.5; + + // Bonus for moving toward enemies + let nearestEnemyDist: i32 = 999999; + for (let i = 0; i < enemyBots.length; i++) { + let dist = distance2(newPos, enemyBots[i]); + if (dist < nearestEnemyDist) { + nearestEnemyDist = dist; + } + } + + if (nearestEnemyDist < 999999) { + if (nearestEnemyDist <= attackRadius2) { + score += 50.0; + } + } + + if (score > bestScore) { + bestScore = score; + bestDir = dir; + } + } + + if (bestDir >= 0) { + return new Move(bot, bestDir); + } + + return null; +} + +// Move in a direction (with toroidal wrap) +function moveInDirection(pos: Position, dir: i32): Position { + let newRow = pos.row + DROW[dir]; + let newCol = pos.col + DCOL[dir]; + + // Toroidal wrap + if (newRow < 0) newRow = rows - 1; + if (newRow >= rows) newRow = 0; + if (newCol < 0) newCol = cols - 1; + if (newCol >= cols) newCol = 0; + + return new Position(newRow, newCol); +} + +// Check if position is valid (not wall, not enemy) +function isValidPosition(pos: Position): bool { + // Check walls + for (let i = 0; i < walls.length; i++) { + if (walls[i].equals(pos)) return false; + } + + // Check enemy bots + for (let i = 0; i < enemyBots.length; i++) { + if (enemyBots[i].equals(pos)) return false; + } + + return true; +} + +// Check if move maintains cohesion +function maintainsCohesion(newPos: Position, oldPos: Position): bool { + for (let i = 0; i < myBots.length; i++) { + let other = myBots[i]; + if (other.equals(oldPos)) continue; + + let dist2 = distance2(newPos, other); + if (dist2 <= COHESION_RADIUS2) { + return true; + } + } + + // Single bot case: always allow movement + if (myBots.length == 1) { + return true; + } + + return false; +} + +// Squared distance (toroidal) +function distance2(a: Position, b: Position): i32 { + let dr = Math.abs(a.row - b.row); + let dc = Math.abs(a.col - b.col); + + if (dr > rows / 2) dr = rows - dr; + if (dc > cols / 2) dc = cols - dc; + + return i32(dr * dr + dc * dc); +} + +// Build output JSON +function buildOutputJSON(): string { + if (outputMoves.length == 0) { + return "{\"moves\":[]}"; + } + + let result = "{\"moves\":["; + for (let i = 0; i < outputMoves.length; i++) { + let move = outputMoves[i]; + result += "{\"position\":{\"row\":"; + result += move.position.row.toString(); + result += ",\"col\":"; + result += move.position.col.toString(); + result += "},\"direction\":\""; + result += DIR_NAMES[move.direction]; + result += "\"}"; + + if (i < outputMoves.length - 1) { + result += ","; + } + } + result += "]}"; + + return result; +} + +// Free result is a no-op for AssemblyScript (GC handles memory) +export function free_result(ptr: usize): void { + // GC handles memory automatically } diff --git a/wasm/bots/swarm/package-lock.json b/wasm/bots/swarm/package-lock.json new file mode 100644 index 0000000..a59d6cc --- /dev/null +++ b/wasm/bots/swarm/package-lock.json @@ -0,0 +1,56 @@ +{ + "name": "swarm-wasm", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "swarm-wasm", + "version": "1.0.0", + "devDependencies": { + "assemblyscript": "^0.27.0" + } + }, + "node_modules/assemblyscript": { + "version": "0.27.37", + "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.27.37.tgz", + "integrity": "sha512-YtY5k3PiV3SyUQ6gRlR2OCn8dcVRwkpiG/k2T5buoL2ymH/Z/YbaYWbk/f9mO2HTgEtGWjPiAQrIuvA7G/63Gg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "binaryen": "116.0.0-nightly.20240114", + "long": "^5.2.4" + }, + "bin": { + "asc": "bin/asc.js", + "asinit": "bin/asinit.js" + }, + "engines": { + "node": ">=18", + "npm": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/assemblyscript" + } + }, + "node_modules/binaryen": { + "version": "116.0.0-nightly.20240114", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-116.0.0-nightly.20240114.tgz", + "integrity": "sha512-0GZrojJnuhoe+hiwji7QFaL3tBlJoA+KFUN7ouYSDGZLSo9CKM8swQX8n/UcbR0d1VuZKU+nhogNzv423JEu5A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "wasm-opt": "bin/wasm-opt", + "wasm2js": "bin/wasm2js" + } + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + } + } +} diff --git a/wasm/engine/main_wasm.go b/wasm/engine/main_wasm.go index 71065ea..6e6f06e 100644 --- a/wasm/engine/main_wasm.go +++ b/wasm/engine/main_wasm.go @@ -19,14 +19,14 @@ var ( func init() { c := make(chan struct{}) js.Global().Set("acbEngine", js.ValueOf(map[string]interface{}{ - "loadState": jsWrapper(loadState), - "step": jsWrapper(step), - "runMatch": jsWrapper(runMatch), - "getReplay": jsWrapper(getReplay), - "getBots": jsWrapper(getBots), - "getEnergy": jsWrapper(getEnergy), - "getConfig": jsWrapper(getConfig), - "getState": jsWrapper(getState), + "loadState": jsWrapper(loadState), + "step": jsWrapper(step), + "runMatch": jsWrapper(runMatch), + "getReplay": jsWrapper(getReplay), + "getBots": jsWrapper(getBots), + "getEnergy": jsWrapper(getEnergy), + "getConfig": jsWrapper(getConfig), + "getState": jsWrapper(getState), })) fmt.Println("ACB WASM Engine loaded") close(c)