'use strict'; const test = require('node:test'); const assert = require('node:assert'); const { Vec3 } = require('vec3'); const { scanWaterAhead, isWaterName, BoatState } = require('../src/bot/autoBoat'); const { FakeBot } = require('./helpers/fakeBot'); /** * 构造一个「前方全是水」的假世界。 * @param {FakeBot} bot * @param {(x: number, z: number) => boolean} isWater */ function fillWorld(bot, isWater, y) { const level = y === undefined ? 63 : y; for (let x = -40; x <= 40; x++) { for (let z = -40; z <= 40; z++) { for (const dy of [-1, 0]) { bot.blockMap.set(`${x},${level + dy},${z}`, { name: isWater(x, z) ? 'water' : 'stone', boundingBox: 'block' }); } } } } test('isWaterName 识别水与气泡柱', () => { assert.strictEqual(isWaterName('water'), true); assert.strictEqual(isWaterName('flowing_water'), true); assert.strictEqual(isWaterName('bubble_column'), true); assert.strictEqual(isWaterName('stone'), false); assert.strictEqual(isWaterName(''), false); assert.strictEqual(isWaterName(null), false); }); test('前方开阔水域达到长度与宽度阈值时建议用船', () => { const bot = new FakeBot({ x: 0, y: 64, z: 0 }); // x 从 1 到 30 全是水,其余为石头 fillWorld(bot, (x) => x >= 1 && x <= 30); const scan = scanWaterAhead(bot, 1, 0, 6, 4); assert.strictEqual(scan.ok, true, scan.reason); assert.ok(scan.length >= 6); assert.ok(scan.width >= 4); }); test('水域长度不足时不建议用船', () => { const bot = new FakeBot({ x: 0, y: 64, z: 0 }); // 只有 3 格宽的水沟 fillWorld(bot, (x) => x >= 1 && x <= 3); const scan = scanWaterAhead(bot, 1, 0, 6, 1); assert.strictEqual(scan.ok, false); assert.ok(scan.reason.includes('长度')); }); test('水面宽度不足时不建议用船', () => { const bot = new FakeBot({ x: 0, y: 64, z: 0 }); // 一条沿 x 方向的长水道,但 z 方向只有 1 格宽 fillWorld(bot, (x, z) => x >= 1 && x <= 30 && z === 0); const scan = scanWaterAhead(bot, 1, 0, 6, 4); assert.strictEqual(scan.ok, false); assert.ok(scan.reason.includes('宽度')); }); test('前方完全没有水时不建议用船', () => { const bot = new FakeBot({ x: 0, y: 64, z: 0 }); fillWorld(bot, () => false); const scan = scanWaterAhead(bot, 1, 0, 6, 4); assert.strictEqual(scan.ok, false); assert.strictEqual(scan.length, 0); }); test('用船状态机初始为空闲且描述为空', () => { const bot = new FakeBot(); const { AutoBoatController } = require('../src/bot/autoBoat'); const { defaultConfig } = require('../src/config/expansionConfig'); const boat = new AutoBoatController(bot, defaultConfig(), null, null); assert.strictEqual(boat.isActive(), false); assert.strictEqual(boat.describe(), null); assert.strictEqual(boat.state, BoatState.IDLE); }); test('autoBoat 关闭时 tick 不会进入流程', () => { const bot = new FakeBot(); const { AutoBoatController } = require('../src/bot/autoBoat'); const { defaultConfig } = require('../src/config/expansionConfig'); const config = defaultConfig(); config.autoBoat = false; const boat = new AutoBoatController(bot, config, null, null); boat.tick({ running: true, canStart: true, target: { x: 1, z: 1 } }); assert.strictEqual(boat.isActive(), false); }); test('waterMode 非 boat 时 tick 不会进入流程(即使 autoBoat 打开)', () => { const bot = new FakeBot(); const { AutoBoatController } = require('../src/bot/autoBoat'); const { defaultConfig } = require('../src/config/expansionConfig'); const config = defaultConfig(); config.autoBoat = true; config.waterMode = 'avoid'; const boat = new AutoBoatController(bot, config, null, null); boat.tick({ running: true, canStart: true, target: { x: 1, z: 1 } }); assert.strictEqual(boat.isActive(), false); }); test('背包里没有船时不会启动用船流程', () => { const bot = new FakeBot({ x: 0, y: 64, z: 0, slots: [] }); fillWorld(bot, (x) => x >= 1 && x <= 30); const { AutoBoatController } = require('../src/bot/autoBoat'); const { defaultConfig } = require('../src/config/expansionConfig'); const config = defaultConfig(); config.autoBoat = true; config.waterMode = 'boat'; const boat = new AutoBoatController(bot, config, null, null); boat.tick({ running: true, canStart: true, target: { x: 2, z: 0 } }); assert.strictEqual(boat.isActive(), false); });