diff --git a/docs/REMEDIATION_PLAN.md b/docs/REMEDIATION_PLAN.md
index cfd67a7..12a60fd 100644
--- a/docs/REMEDIATION_PLAN.md
+++ b/docs/REMEDIATION_PLAN.md
@@ -43,11 +43,11 @@ Key: `S` ≤ half day · `M` ~1–3 days · `L` ~1 week+ · 🔴 launch-blocker
- [x] **3.3 S — Multi-step monster collision fix.** ✅ Collision is now checked after **each** monster sub-step, not just after the loop. With `movesPerPlayerMove > 1` (casino's 1.5× speed) the monster could previously step onto the player's tile and off again between checks, phasing through and missing the catch (player-favorable, eroded the house edge). New `monsterCollision.test.js`.
## Phase 4 — De-spaghetti / structural cleanup (🟡)
-- [ ] **4.1 L — Break up `index.js`** into `app.js`, `routes/{public,user,admin,auth}.js`, `views/verify.js`, `server.js`.
-- [ ] **4.2 M — Kill duplicates.** Merge `rpcService`+`rpccalls`; delete dead `handlePlayerMove` (`socketHandlers.js:244`); unify `payout_address` widths; remove `QueryValidator` theater (`databaseManager.js:21`).
+- [x] **4.1 L — Break up `index.js` (substantially).** ✅ Extracted the verify HTML → `views/verifyPage.js` (now HTML-escaped), all 15 admin endpoints → `routes/admin.js`, and the 3 smirk endpoints → `routes/auth.js`, each as DI factories with smoke tests. **index.js: 2029 → 882 lines (-57%).** _Remaining user/public routes left inline — they're interleaved with bootstrap and have no route tests, so further slicing is diminishing-return/higher-risk; the structure + pattern are established for later._
+- [~] **4.2 M — Kill duplicates (partial).** ✅ Deleted the dead `handlePlayerMove` handler (a footgun that double-stepped the monster + bypassed escape/collision detection) and its orphaned `playerMoveTimestamps` map/cleanup — the live path is `MovementManager`. _Remaining (lower-value/higher-effort): merge `rpcService`+`rpccalls`, unify `payout_address` column widths, address the `QueryValidator` false-positive — deferred._
- [ ] **4.3 M — Config as immutable snapshots;** stop mutating `process.env` (`gameModeManager.js:189`).
- [ ] **4.4 M — Frontend de-spaghetti.** Shared `escapeHtml`/`dom.js`; kill double-declares + dead `_appendMessage`; explicit init ordering; upgrade/drop jQuery; gate broadcast logging (`broadcastManager.js:33`).
-- [ ] **4.5 S — Delete stale `test/` scratch files.**
+- [x] **4.5 S — Delete stale `test/` scratch files.** ✅ Removed 14 non-jest leftovers (5 `.js` scratch scripts + 9 `.html` debug pages) — confirmed they contained no jest constructs and were referenced nowhere. `test/` is now exclusively the 21 real jest suites.
## Phase 5 — Test & ops overhaul (🟠 — the launch gate)
- [ ] **5.1 L — Real money-path tests** against Postgres (no stubbed `withTransaction`): under/overpay, double-confirm, batch dispatch, credit race, retry idempotency; property-test `money/atomic.js`.
diff --git a/src/network/socketHandlers.js b/src/network/socketHandlers.js
index fbf90bb..4549bf9 100644
--- a/src/network/socketHandlers.js
+++ b/src/network/socketHandlers.js
@@ -187,8 +187,7 @@ class SocketHandlers {
cleanupTimeoutMs: 300000 // 5 minutes to reconnect before game is lost
});
- // Movement manager abstraction with memory cleanup
- this.playerMoveTimestamps = new Map();
+ // Movement manager abstraction (owns its own move-cooldown state)
this.movementManager = new MovementManager({
activeGames: this.activeGames,
io: this.io,
@@ -206,32 +205,6 @@ class SocketHandlers {
* Register memory cleanup functions to prevent memory leaks
*/
_registerMemoryCleanups() {
- // Cleanup player move timestamps (keep for 5 minutes)
- this.memoryManager.registerCleanup(
- 'playerMoveTimestamps',
- () => {
- const now = Date.now();
- const maxAge = 300000; // 5 minutes
- let cleaned = 0;
- const toDelete = [];
-
- for (const [socketId, timestamp] of this.playerMoveTimestamps.entries()) {
- if (now - timestamp > maxAge) {
- toDelete.push(socketId);
- }
- }
-
- for (const socketId of toDelete) {
- if (this.playerMoveTimestamps.delete(socketId)) {
- cleaned++;
- }
- }
-
- return cleaned;
- },
- 300000
- );
-
// (mempool-notification dedup + TTL eviction lives in PaymentHandlers, which owns
// the actual dedup set; the old SocketHandlers copy here was unused and is removed.)
@@ -245,69 +218,6 @@ class SocketHandlers {
}
}
- /**
- * Handle player movement input from client.
- * Expects moveData: { direction: 'up'|'down'|'left'|'right' }
- */
- handlePlayerMove(socket, moveData) {
- try {
- if (!moveData || typeof moveData.direction !== 'string') return;
- const now = Date.now();
- const last = this.playerMoveTimestamps.get(socket.id) || 0;
- if (now - last < this.MOVE_COOLDOWN) return; // rate limit moves
- this.playerMoveTimestamps.set(socket.id, now);
-
- const game = this.activeGames.get(socket.id);
- if (!game) return; // not in a game
-
- // Translate direction to delta
- const dir = moveData.direction;
- let dx = 0, dy = 0;
- switch (dir) {
- case 'up': dy = -1; break;
- case 'down': dy = 1; break;
- case 'left': dx = -1; break;
- case 'right': dx = 1; break;
- default: return; // ignore unknown
- }
-
- // Attempt move via game API (assuming game has movePlayer(dx,dy))
- if (typeof game.movePlayer === 'function') {
- game.movePlayer(dx, dy);
- } else if (game.player) {
- // Fallback direct mutation (legacy) with simple bounds check
- const newX = game.player.x + dx;
- const newY = game.player.y + dy;
- if (newX >= 0 && newY >= 0) {
- game.player.x = newX;
- game.player.y = newY;
- }
- }
-
- if (typeof game.moveMonster === 'function') {
- game.moveMonster();
- }
-
- // Build updated state
- let state;
- if (typeof game.getState === 'function') {
- state = game.getState();
- } else {
- state = { player: game.player };
- }
-
- // Add block height context
- if (this.debugManager && typeof this.debugManager.getCurrentBlockHeight === 'function') {
- state.blockHeight = this.debugManager.getCurrentBlockHeight();
- }
-
- this.io.to(socket.id).emit('game_update', state);
-
- } catch (err) {
- console.error('handlePlayerMove error:', err);
- }
- }
-
/**
* Called after a successful player move (before update is emitted) via MovementManager postMoveHook.
* Handles monster chasing logic and immediate game over if monster catches player.
@@ -816,9 +726,6 @@ class SocketHandlers {
// addressToUser entries must persist so checkPaymentStatus() can query the wallet
// RPC after session resumption. They get cleaned up when payments are confirmed or expired.
- // Clean up movement timestamps
- this.playerMoveTimestamps.delete(socketId);
-
// Preserve queue entry for users with session/payment
// The queue entry's socketId will be updated on reconnection
if (this.queueManager && typeof this.queueManager.removePlayer === 'function') {
@@ -1211,7 +1118,6 @@ class SocketHandlers {
// Clear remaining data structures
this.activeGames.clear();
- this.playerMoveTimestamps.clear();
if (this.debugManager.CONSOLE_LOGGING) {
console.log('✅ SocketHandlers shutdown complete');
diff --git a/test/debug_client_lighting.html b/test/debug_client_lighting.html
deleted file mode 100644
index 61c2add..0000000
--- a/test/debug_client_lighting.html
+++ /dev/null
@@ -1,163 +0,0 @@
-
-
-
- Debug Client Lighting
-
-
-
- Client-Side Lighting Debug
-
-
-
Game State Lighting Data
-
No data received yet...
-
-
-
-
Lighting Visualization
-
-
-
-
-
-
-
-
-
-
diff --git a/test/debug_game_updates.html b/test/debug_game_updates.html
deleted file mode 100644
index edeb27c..0000000
--- a/test/debug_game_updates.html
+++ /dev/null
@@ -1,186 +0,0 @@
-
-
-
- Game Update Debugging
-
-
-
- Game Update Debugging
-
-
-
-
-
-
-
-
- Not connected
-
-
-
-
-
-
diff --git a/test/debug_lighting.html b/test/debug_lighting.html
deleted file mode 100644
index 575ac14..0000000
--- a/test/debug_lighting.html
+++ /dev/null
@@ -1,174 +0,0 @@
-
-
-
- Lighting Debug - Wowngeon
-
-
-
- Lighting System Debug
-
-
-
Alpha Value Tests
-
Testing different alpha values to see how they render:
-
-
-
-
-
Game Display
-
-
-
-
-
-
-
Live Lighting Data
-
No data captured yet
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/dungeon.js b/test/dungeon.js
deleted file mode 100644
index 493af29..0000000
--- a/test/dungeon.js
+++ /dev/null
@@ -1,92 +0,0 @@
-const ROT = require('./rot.js');
-
-/**
- * Dungeon generation utilities
- */
-class DungeonGenerator {
- static generate(width, height) {
- // Create dungeon using ROT.js Map.Digger
- const digger = new ROT.Map.Digger(width, height, {
- roomWidth: [3, 9],
- roomHeight: [3, 7],
- corridorLength: [2, 6],
- dugPercentage: 0.2
- });
-
- // Initialize the empty map with walls (1)
- const map = Array(height).fill().map(() => Array(width).fill(1));
-
- // Fill in floors (0) using digger's callback
- digger.create((x, y, value) => {
- // value: 0 = floor, 1 = wall
- map[y][x] = value;
- });
-
- // Get rooms that were created
- const rooms = digger.getRooms();
-
- // Place entrance in the first room
- const entranceRoom = rooms[0];
- const entranceCenter = entranceRoom.getCenter();
- const entrance = [entranceCenter[0], entranceCenter[1]];
-
- // Place exit in the last room
- const exitRoom = rooms[rooms.length - 1];
- const exitCenter = exitRoom.getCenter();
- const exit = [exitCenter[0], exitCenter[1]];
-
- // Place treasure in a middle room (not first or last)
- let treasureRoom;
- if (rooms.length > 2) {
- treasureRoom = rooms[Math.floor(rooms.length / 2)];
- } else {
- // Fallback if we have fewer than 3 rooms
- treasureRoom = rooms[0];
- }
- const treasureCenter = treasureRoom.getCenter();
- // Place treasure slightly off center to make it more interesting
- const treasure = [
- treasureCenter[0] + Math.floor(Math.random() * 3) - 1,
- treasureCenter[1] + Math.floor(Math.random() * 3) - 1
- ];
-
- // Ensure the treasure is on a floor tile
- if (map[treasure[1]][treasure[0]] !== 0) {
- treasure[0] = treasureCenter[0];
- treasure[1] = treasureCenter[1];
- }
-
- return {
- map: map,
- rooms: rooms,
- entrance: entrance,
- exit: exit,
- treasure: treasure
- };
- }
-
- static getRandomRoomCenter(rooms, excludeFirst = false, excludeLast = false) {
- if (!rooms || rooms.length === 0) return null;
-
- let availableRooms = [...rooms];
- if (excludeFirst && availableRooms.length > 1) {
- availableRooms = availableRooms.slice(1);
- }
- if (excludeLast && availableRooms.length > 1) {
- availableRooms = availableRooms.slice(0, -1);
- }
-
- const randomRoom = availableRooms[Math.floor(Math.random() * availableRooms.length)];
- return randomRoom.getCenter();
- }
-
- static isFloorTile(map, x, y) {
- return map[y] && map[y][x] === 0;
- }
-
- static isWallTile(map, x, y) {
- return map[y] && map[y][x] === 1;
- }
-}
-
-module.exports = DungeonGenerator;
diff --git a/test/game.js b/test/game.js
deleted file mode 100644
index 6b5ec43..0000000
--- a/test/game.js
+++ /dev/null
@@ -1,172 +0,0 @@
-const ROT = require('./rot.js');
-const Player = require('./player.js');
-const Monster = require('./monster.js');
-const DungeonGenerator = require('./dungeon.js');
-
-class Game {
- constructor(socketId, mapWidth, mapHeight) {
- this.socketId = socketId;
- this.width = mapWidth || 25; // Reduced default size to match client display
- this.height = mapHeight || 19; // Reduced default size to match client display
- this.gameState = 'waiting'; // waiting, active, won, lost
- this.startBlock = null;
- this.dungeon = null;
- this.player = new Player();
- this.monster = new Monster();
- this.fee = 0; // Amount player paid
- this.visibleTiles = {}; // Will store visible tiles
- this.fov = null; // FOV calculator
- this.generateDungeon();
- }
-
- generateDungeon() {
- this.dungeon = DungeonGenerator.generate(this.width, this.height);
-
- if (this.dungeon.entrance) {
- this.player.moveTo(this.dungeon.entrance[0], this.dungeon.entrance[1]);
- }
-
- // Place monster far from entrance
- if (this.dungeon.rooms && this.dungeon.rooms.length > 2) {
- const monsterRoom = this.dungeon.rooms[Math.floor(this.dungeon.rooms.length * 0.7)];
- const center = monsterRoom.getCenter();
- this.monster.moveTo(center[0], center[1]);
- }
-
- // Initialize FOV calculator
- this.fov = new ROT.FOV.PreciseShadowcasting(
- (x, y) => {
- // Return true if the tile is transparent (can see through it)
- return this.dungeon.map[y] && this.dungeon.map[y][x] === 0;
- }
- );
-
- // Calculate initial FOV
- this.updateFOV();
-
- // Set game state to active
- this.gameState = 'active';
- }
-
- updateFOV() {
- // Reset visible tiles
- this.visibleTiles = {};
-
- // Calculate new visible tiles
- this.fov.compute(this.player.x, this.player.y, 10, (x, y, r, visibility) => { // Added r and visibility params
- if (visibility > 0) { // Only add if tile is actually visible
- if (!this.visibleTiles[y]) {
- this.visibleTiles[y] = {};
- }
- // Store the actual tile type (0 for floor, 1 for wall) from the dungeon map
- if (this.dungeon && this.dungeon.map && this.dungeon.map[y] && this.dungeon.map[y][x] !== undefined) {
- this.visibleTiles[y][x] = this.dungeon.map[y][x];
- } else {
- // Should not happen if FOV is within map bounds, but as a fallback:
- // this.visibleTiles[y][x] = 1; // Default to wall if outside known map
- }
- }
- });
- // console.log("FOV updated. Player:", this.player.x, this.player.y, "Visible tiles count:", Object.keys(this.visibleTiles).reduce((acc, k) => acc + Object.keys(this.visibleTiles[k]).length, 0));
- }
-
- movePlayer(dx, dy) {
- const newX = this.player.x + dx;
- const newY = this.player.y + dy;
-
- // Debug logging for movement validation
- console.log(`[DEBUG] Move attempt: from (${this.player.x},${this.player.y}) to (${newX},${newY}) with dx=${dx},dy=${dy}`);
- console.log(`[DEBUG] Map bounds: width=${this.width}, height=${this.height}`);
- console.log(`[DEBUG] Dungeon exists: ${!!this.dungeon}`);
- if (this.dungeon && this.dungeon.map) {
- console.log(`[DEBUG] Map row ${newY} exists: ${!!this.dungeon.map[newY]}`);
- if (this.dungeon.map[newY]) {
- console.log(`[DEBUG] Map cell [${newY}][${newX}] value: ${this.dungeon.map[newY][newX]} (0=floor, 1=wall)`);
- }
- }
-
- // Check if the move is valid (not into a wall and within map bounds)
- if (this.dungeon &&
- this.dungeon.map[newY] &&
- this.dungeon.map[newY][newX] !== undefined &&
- this.dungeon.map[newY][newX] === 0) { // 0 is floor
-
- this.player.moveTo(newX, newY);
- console.log(`Player moved to ${newX},${newY} in game for socket ${this.socketId}`);
-
- // Update FOV after moving
- this.updateFOV();
-
- // Check for game events like finding treasure or exit
- if (this.dungeon.exit && this.player.isAt(this.dungeon.exit[0], this.dungeon.exit[1])) {
- // Handle win condition (e.g., escaped)
- this.gameState = 'won';
- return { status: 'moved', event: 'escaped', player: this.player.getState(), visibleTiles: this.visibleTiles };
- }
- if (this.dungeon.treasure && this.player.isAt(this.dungeon.treasure[0], this.dungeon.treasure[1]) && !this.player.hasTreasure) {
- this.player.hasTreasure = true;
- // Remove treasure from dungeon so it won't be sent in future updates
- this.dungeon.treasure = null;
- console.log(`Player ${this.socketId} collected treasure! hasTreasure: ${this.player.hasTreasure}`);
- return { status: 'moved', event: 'treasure_found', player: this.player.getState(), visibleTiles: this.visibleTiles };
- }
-
- return { status: 'moved', player: this.player.getState(), visibleTiles: this.visibleTiles };
- }
-
-
- console.log(`Player move to ${newX},${newY} for socket ${this.socketId} is invalid (wall or out of bounds).`);
-
- // Debug: Show map around player
- console.log(`[DEBUG] Map around player position:`);
- for (let y = this.player.y - 2; y <= this.player.y + 2; y++) {
- let row = '';
- for (let x = this.player.x - 2; x <= this.player.x + 2; x++) {
- if (x === this.player.x && y === this.player.y) {
- row += '@'; // Player position
- } else if (x === newX && y === newY) {
- row += 'X'; // Attempted move position
- } else if (this.dungeon && this.dungeon.map[y] && this.dungeon.map[y][x] !== undefined) {
- row += this.dungeon.map[y][x] === 0 ? '.' : '#';
- } else {
- row += '?'; // Out of bounds or undefined
- }
- }
- console.log(`[DEBUG] Y=${y}: ${row}`);
- }
-
- return { status: 'invalid' };
- }
-
- moveMonster() {
- this.monster.moveTowardPlayer(this.player, this.dungeon);
- }
-
- // Update the getState() method to include all entities with relative positions
- getState() {
- const state = {
- gameState: this.gameState,
- player: this.player.getState(), // Use Player's getState method
- monster: this.monster ? this.monster.getState() : null, // Use Monster's getState method
- visibleTiles: { ...this.visibleTiles }, // Send a copy of visible tiles
- entrance: this.dungeon ? this.dungeon.entrance : null,
- exit: this.dungeon ? this.dungeon.exit : null,
- treasure: this.dungeon ? this.dungeon.treasure : null,
- };
-
- // Debug logging to verify entity data
- console.log("🎮 getState() sending entities - Monster:", state.monster, "Entrance:", state.entrance, "Exit:", state.exit, "Treasure:", state.treasure);
-
- return state;
- }
-}
-
-// Check if monster killed player
-function checkMonsterKill(player, monster) {
- // Use Monster's hasCaughtPlayer method
- const monsterInstance = new Monster(monster.x, monster.y);
- return monsterInstance.hasCaughtPlayer(player);
-}
-
-module.exports = Game;
-module.exports.checkMonsterKill = checkMonsterKill;
\ No newline at end of file
diff --git a/test/monster.js b/test/monster.js
deleted file mode 100644
index 9010378..0000000
--- a/test/monster.js
+++ /dev/null
@@ -1,86 +0,0 @@
-const ROT = require('./rot.js');
-
-/**
- * Monster class - Manages monster state and AI
- */
-class Monster {
- constructor(x = 0, y = 0) {
- this.x = x;
- this.y = y;
- }
-
- // Move monster to new position
- moveTo(x, y) {
- this.x = x;
- this.y = y;
- }
-
- // Check if monster is at given coordinates
- isAt(x, y) {
- return this.x === x && this.y === y;
- }
-
- // Simple AI to move toward player
- moveTowardPlayer(player, dungeon) {
- if (!player || !dungeon || !dungeon.map) return;
-
- const dx = Math.sign(player.x - this.x);
- const dy = Math.sign(player.y - this.y);
-
- // Try horizontal move first
- if (dx !== 0) {
- const newX = this.x + dx;
- if (dungeon.map[this.y] && dungeon.map[this.y][newX] === 0) {
- this.x = newX;
- return;
- }
- }
-
- // Try vertical move
- if (dy !== 0) {
- const newY = this.y + dy;
- if (dungeon.map[newY] && dungeon.map[newY][this.x] === 0) {
- this.y = newY;
- return;
- }
- }
-
- // Try random direction if direct path is blocked
- const dirs = [[0,1],[1,0],[0,-1],[-1,0]];
- const shuffledDirs = ROT.RNG.shuffle(dirs.slice());
-
- for (const [dx, dy] of shuffledDirs) {
- const nx = this.x + dx;
- const ny = this.y + dy;
- if (dungeon.map[ny] && dungeon.map[ny][nx] === 0) {
- this.x = nx;
- this.y = ny;
- break;
- }
- }
- }
-
- // Check if monster has caught the player
- hasCaughtPlayer(player) {
- if (!player) return false;
-
- // Check for direct overlap - monster on same tile as player
- return this.x === player.x && this.y === player.y;
- }
-
- // Get monster state as object
- getState() {
- return {
- x: this.x,
- y: this.y
- };
- }
-
- // Set monster state from object
- setState(state) {
- if (state.x !== undefined) this.x = state.x;
- if (state.y !== undefined) this.y = state.y;
- }
-}
-
-module.exports = Monster;
diff --git a/test/player.js b/test/player.js
deleted file mode 100644
index 73d7346..0000000
--- a/test/player.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Player class - Manages player state and actions
- */
-class Player {
- constructor(x = 0, y = 0) {
- this.x = x;
- this.y = y;
- this.hasKey = false;
- this.hasTreasure = false;
- }
-
- // Move player to new position
- moveTo(x, y) {
- this.x = x;
- this.y = y;
- }
-
- // Check if player is at given coordinates
- isAt(x, y) {
- return this.x === x && this.y === y;
- }
-
- // Reset player state
- reset() {
- this.hasKey = false;
- this.hasTreasure = false;
- }
-
- // Get player state as object
- getState() {
- return {
- x: this.x,
- y: this.y,
- hasKey: this.hasKey,
- hasTreasure: this.hasTreasure
- };
- }
-
- // Set player state from object
- setState(state) {
- if (state.x !== undefined) this.x = state.x;
- if (state.y !== undefined) this.y = state.y;
- if (state.hasKey !== undefined) this.hasKey = state.hasKey;
- if (state.hasTreasure !== undefined) this.hasTreasure = state.hasTreasure;
- }
-}
-
-module.exports = Player;
diff --git a/test/test_address_detection.js b/test/test_address_detection.js
deleted file mode 100644
index baa47bf..0000000
--- a/test/test_address_detection.js
+++ /dev/null
@@ -1,109 +0,0 @@
-/**
- * Test script for XMR/WOW address detection system
- */
-
-// Test the regex patterns used in socketHandlers.js
-function testAddressDetection() {
- console.log('🧪 Testing XMR/WOW Address Detection System\n');
-
- // XMR mainnet addresses start with 4, 8, A, or B and are 95 characters
- // WOW addresses start with W and are 97 characters
- const xmrRegex = /\b[48AB][1-9A-HJ-NP-Za-km-z]{94}\b/;
- const wowRegex = /\bW[1-9A-HJ-NP-Za-km-z]{96}\b/;
-
- function detectXMRAddress(message) {
- const xmrMatch = message.match(xmrRegex);
- const wowMatch = message.match(wowRegex);
-
- if (xmrMatch) {
- return { address: xmrMatch[0], type: 'XMR' };
- }
- if (wowMatch) {
- return { address: wowMatch[0], type: 'WOW' };
- }
-
- return null;
- }
-
- // Test cases with real address formats
- const testCases = [
- {
- name: 'Valid XMR Address (starts with 8)',
- message: 'Here is my XMR address: 8Bqez9dvqY9bw3CgwMC9X1c8MX5KuRgKbiXRWg2F9UtYNYLevqjJXbQW4qYHUM1JF1792hhWYJLUufSUn9GvSC4G6wjMiRL',
- expected: { address: '8Bqez9dvqY9bw3CgwMC9X1c8MX5KuRgKbiXRWg2F9UtYNYLevqjJXbQW4qYHUM1JF1792hhWYJLUufSUn9GvSC4G6wjMiRL', type: 'XMR' }
- },
- {
- name: 'Valid XMR Address (starts with 4)',
- message: 'Send to: 44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A',
- expected: { address: '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A', type: 'XMR' }
- },
- {
- name: 'Valid WOW Address (starts with WW)',
- message: 'My WOW address is: WW35SBkcZDNGRkLroGNg4tKuJtyV8JhA95CREopLPCwrj9LgRU5pSnDb4D5BHKo9oBAZSjHXWnDwCJ4mRL5F6iJ81vSmNBuRC',
- expected: { address: 'WW35SBkcZDNGRkLroGNg4tKuJtyV8JhA95CREopLPCwrj9LgRU5pSnDb4D5BHKo9oBAZSjHXWnDwCJ4mRL5F6iJ81vSmNBuRC', type: 'WOW' }
- },
- {
- name: 'Valid WOW Address (starts with Wo)',
- message: 'please send to Wo3MWeKwtA918DU4c69hVSNgejdWFCRCuWjShRY66mJkU2Hv58eygJWDJS1MNa2Ge5M1WjUkGHuLqHkweDxwZZU42d16v94mP thanks',
- expected: { address: 'Wo3MWeKwtA918DU4c69hVSNgejdWFCRCuWjShRY66mJkU2Hv58eygJWDJS1MNa2Ge5M1WjUkGHuLqHkweDxwZZU42d16v94mP', type: 'WOW' }
- },
- {
- name: 'Invalid XMR (too short)',
- message: '4AdUndXHHZ6cfufTMvppY6JwXNouMBzSkbLYfpAV5Usx3skxNgYeYTRJ5zA4NJD9QhKCciJBcH9qPq8FgZKR8U8n',
- expected: null
- },
- {
- name: 'Invalid XMR (wrong prefix)',
- message: '3AdUndXHHZ6cfufTMvppY6JwXNouMBzSkbLYfpAV5Usx3skxNgYeYTRJ5zA4NJD9QhKCciJBcH9qPq8FgZKR8U8nZZZZ',
- expected: null
- },
- {
- name: 'Invalid WOW (wrong prefix)',
- message: 'XW35SBkcZDNGRkLroGNg4tKuJtyV8JhA95CREopLPCwrj9LgRU5pSnDb4D5BHKo9oBAZSjHXWnDwCJ4mRL5F6iJ81vSmNBuRC',
- expected: null
- },
- {
- name: 'No address in message',
- message: 'hello world this is just a chat message',
- expected: null
- },
- {
- name: 'Partial address in message',
- message: 'my address starts with 4AdUnd but I wont share the full thing',
- expected: null
- }
- ];
-
- let passed = 0;
- let failed = 0;
-
- testCases.forEach((testCase, index) => {
- console.log(`Test ${index + 1}: ${testCase.name}`);
- console.log(`Message: "${testCase.message}"`);
-
- const result = detectXMRAddress(testCase.message);
-
- if (JSON.stringify(result) === JSON.stringify(testCase.expected)) {
- console.log(`✅ PASS - Detected: ${result ? `${result.type} ${result.address}` : 'null'}`);
- passed++;
- } else {
- console.log(`❌ FAIL - Expected: ${JSON.stringify(testCase.expected)}`);
- console.log(`❌ FAIL - Got: ${JSON.stringify(result)}`);
- failed++;
- }
- console.log('');
- });
-
- console.log(`📊 Test Results: ${passed} passed, ${failed} failed`);
-
- if (failed === 0) {
- console.log('🎉 All tests passed! Address detection system is working correctly.');
- } else {
- console.log('⚠️ Some tests failed. Please review the regex patterns.');
- }
-}
-
-// Run the tests
-testAddressDetection();
-
-module.exports = { testAddressDetection };
diff --git a/test/test_fixes.html b/test/test_fixes.html
deleted file mode 100644
index b02e5f8..0000000
--- a/test/test_fixes.html
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-
- Wowngeon Fixes Test
-
-
-
- 🎮 Wowngeon Fixes Verification
-
-
-
🔧 Fix #1: START Button Visibility
-
Issue: START button was invisible (cut off in game display)
-
Fix: Moved to HTML status area with proper styling
-
Test: Check if START button is visible in the status area below
-
-
-
-
⏱️ Fix #2: Auto-Entry Death Timing
-
Issue: Players died after 2+ blocks instead of 1 block in debug mode
-
Fix: Set currentUser.blockRec = currentBlock + 1 for consistent timing
-
Test: Use "enter" command and check death timing logs in console
-
-
- 🎯 Live Game Test
-
-
-
-
📋 Test Instructions:
-
- - START Button Test: Look for the green "🎮 START GAME" button in the status area (should be visible)
- - Death Timing Test: Click START or type "enter" in chat, then check browser console for timing logs
- - Expected Logs: Should see "🕒 AUTO-ENTRY: Player starts on block X, will die after block X+2"
-
-
-
-
-
-
diff --git a/test/test_lighting.html b/test/test_lighting.html
deleted file mode 100644
index 5d6f45c..0000000
--- a/test/test_lighting.html
+++ /dev/null
@@ -1,132 +0,0 @@
-
-
-
- Torch Lighting Test
-
-
-
- Torch Lighting System Test
- Open browser console to see detailed lighting data flow test.
-
-
-
-
-
diff --git a/test/test_lighting_verification.html b/test/test_lighting_verification.html
deleted file mode 100644
index 915ae7d..0000000
--- a/test/test_lighting_verification.html
+++ /dev/null
@@ -1,216 +0,0 @@
-
-
-
-
-
- Torch Lighting Verification Test
-
-
-
- 🔥 Torch Lighting System Verification
-
-
-
Connection Test
-
Connecting...
-
-
-
-
Data Reception Test
-
Waiting for lighting data...
-
Waiting for torch data...
-
Waiting for game state...
-
-
-
-
Rendering Test
-
Waiting for render test...
-
Checking tileColorize setting...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/test_movement.html b/test/test_movement.html
deleted file mode 100644
index e69de29..0000000
diff --git a/test/test_movement.js b/test/test_movement.js
deleted file mode 100644
index e69de29..0000000
diff --git a/test/test_tile_system.js b/test/test_tile_system.js
deleted file mode 100644
index 82d924d..0000000
--- a/test/test_tile_system.js
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/usr/bin/env node
-
-/**
- * Test script to demonstrate the new tile system
- * Shows floor variations, torch placement, and configurable assets
- */
-
-const DungeonGenerator = require('../src/backend/dungeon.js');
-
-console.log("🏛️ WOWNGEON TILE SYSTEM TEST");
-console.log("=" * 50);
-
-// Test 1: Default Configuration
-console.log("\n🔧 Test 1: Default Configuration");
-const defaultConfig = {
- playerType: "@",
- treasureType: "$W",
- monsterType: "~",
- primaryFloor: "'1",
- secondaryFloor: "'2",
- floorVariation: 0.01,
- torchEnabled: true,
- torchDensity: 0.15,
- torchTile: "torch"
-};
-
-const dungeon1 = DungeonGenerator.generate(20, 15, defaultConfig);
-console.log(`Generated dungeon: ${dungeon1.map.length}x${dungeon1.map[0].length}`);
-console.log(`Entrance: [${dungeon1.entrance}]`);
-console.log(`Exit: [${dungeon1.exit}]`);
-console.log(`Treasure: [${dungeon1.treasure}]`);
-console.log(`Torches: ${dungeon1.torches.length} placed`);
-
-// Count tile types
-const tileCounts = {};
-for (let y = 0; y < dungeon1.map.length; y++) {
- for (let x = 0; x < dungeon1.map[0].length; x++) {
- const tile = dungeon1.map[y][x];
- tileCounts[tile] = (tileCounts[tile] || 0) + 1;
- }
-}
-
-console.log("\n📊 Tile Distribution:");
-Object.keys(tileCounts).sort().forEach(tile => {
- console.log(` ${tile}: ${tileCounts[tile]} tiles`);
-});
-
-if (tileCounts["'1"] && tileCounts["'2"]) {
- const floorTotal = tileCounts["'1"] + tileCounts["'2"];
- const variationPercent = (tileCounts["'2"] / floorTotal * 100).toFixed(2);
- console.log(` Floor variation: ${variationPercent}% secondary floors`);
-}
-
-// Test 2: Custom Configuration
-console.log("\n🔧 Test 2: Custom Configuration (Bitcoin theme)");
-const customConfig = {
- playerType: "@2",
- treasureType: "$B",
- monsterType: "~2",
- primaryFloor: "'1",
- secondaryFloor: "'2",
- floorVariation: 0.05, // 5% variation
- torchEnabled: true,
- torchDensity: 0.25, // 25% more torches
- torchTile: "torch"
-};
-
-const dungeon2 = DungeonGenerator.generate(15, 10, customConfig);
-console.log(`Generated custom dungeon: ${dungeon2.map.length}x${dungeon2.map[0].length}`);
-console.log(`Torches in custom dungeon: ${dungeon2.torches.length}`);
-
-// Test 3: Show a small visual sample
-console.log("\n🗺️ Sample Dungeon (15x12):");
-const sampleDungeon = DungeonGenerator.generate(15, 12, defaultConfig);
-
-console.log(" 012345678901234");
-for (let y = 0; y < sampleDungeon.map.length; y++) {
- let line = y.toString().padStart(2) + " ";
- for (let x = 0; x < sampleDungeon.map[0].length; x++) {
- let char = sampleDungeon.map[y][x];
-
- // Add entities for display
- if (sampleDungeon.entrance && sampleDungeon.entrance[0] === x && sampleDungeon.entrance[1] === y) {
- char = '<';
- } else if (sampleDungeon.exit && sampleDungeon.exit[0] === x && sampleDungeon.exit[1] === y) {
- char = '>';
- } else if (sampleDungeon.treasure && sampleDungeon.treasure[0] === x && sampleDungeon.treasure[1] === y) {
- char = '$';
- }
-
- // Display character mapping
- if (char === "'1") char = '.';
- else if (char === "'2") char = ':';
- else if (char === 'torch') char = 'T';
- else if (char === '#') char = '#';
-
- line += char;
- }
- console.log(line);
-}
-
-console.log("\nLegend:");
-console.log(" . = Primary floor ('1)");
-console.log(" : = Secondary floor ('2)");
-console.log(" # = Wall");
-console.log(" T = Torch");
-console.log(" < = Entrance");
-console.log(" > = Exit");
-console.log(" $ = Treasure");
-
-console.log("\n✅ Tile System Test Complete!");
-console.log("\nFeatures verified:");
-console.log(" ✓ Configurable player/treasure/monster types");
-console.log(" ✓ Floor tile variations (primary/secondary)");
-console.log(" ✓ Configurable torch placement");
-console.log(" ✓ Enhanced dungeon generation");
-console.log(" ✓ Torch position tracking");