import { expect } from "@esm-bundle/chai"; import { CaveGenerator } from "../../src/generation/CaveGenerator.js"; import { VoxelGrid } from "../../src/grid/VoxelGrid.js"; // Mock OffscreenCanvas for texture generation if (typeof OffscreenCanvas === "undefined") { class MockCanvas { constructor(width, height) { this.width = width; this.height = height; } getContext() { return { createImageData: (w, h) => ({ data: new Uint8ClampedArray(w * h * 4), }), putImageData: () => {}, drawImage: () => {}, fillStyle: "", fillRect: () => {}, }; } } globalThis.OffscreenCanvas = MockCanvas; } describe("Generation: CaveGenerator", () => { let grid; let generator; beforeEach(() => { grid = new VoxelGrid(20, 10, 20); generator = new CaveGenerator(grid, 12345); }); it("CoA 1: Should initialize with texture generators", () => { expect(generator.floorGen).to.exist; expect(generator.wallGen).to.exist; expect(generator.generatedAssets).to.have.property("palette"); }); it("CoA 2: preloadTextures should generate texture palette", () => { generator.preloadTextures(); // Should have wall variations (100-109) expect(generator.generatedAssets.palette[100]).to.exist; expect(generator.generatedAssets.palette[109]).to.exist; // Should have floor variations (200-209) expect(generator.generatedAssets.palette[200]).to.exist; expect(generator.generatedAssets.palette[209]).to.exist; }); it("CoA 3: generate should create foundation layer", () => { generator.generate(0.5, 2); // Foundation (y=0) should be solid for (let x = 0; x < grid.size.x; x++) { for (let z = 0; z < grid.size.z; z++) { expect(grid.getCell(x, 0, z)).to.not.equal(0); } } }); it("CoA 4: generate should create extruded walls", () => { generator.generate(0.5, 2); // Check a spot that is a wall (has block at y=5 for example) // It should be solid all the way down to y=0 let foundWall = false; for (let x = 0; x < grid.size.x; x++) { for (let z = 0; z < grid.size.z; z++) { const midY = Math.floor(grid.size.y / 2); if (grid.getCell(x, midY, z) !== 0) { foundWall = true; // Valid wall stack expect(grid.getCell(x, 0, z)).to.not.equal(0); expect(grid.getCell(x, 1, z)).to.not.equal(0); } } } expect(foundWall).to.be.true; }); it("CoA 5: smoothMap should apply 2D cellular automata", () => { // 5x5 Map // 1 1 1 0 0 // 1 0 1 0 0 // 1 1 1 0 0 // 0 0 0 0 0 // 0 0 0 0 0 // Center (2, 2) has neighbors: (1,1)=0, (1,2)=1, (1,3)=0, (2,1)=1, (2,3)=1, (3,1)=0, (3,2)=0, (3,3)=0 // Total 1s = 3. Less than 4 -> should become 0. // Actually let's just test simple behavior: dense area stays dense, sparse area clears. let map = [ [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], ]; // Mock dimensions temporarily for this test if smoothMap uses this.width/depth // implementation uses map.length so it might be fine or we need to override width/depth // My implementation used `this.width`. I should mock it or use the real grid size if I constructed it right. // The real grid is 20x10x20. // Let's use a full size map initialized to match grid size to avoid bounds errors. map = []; for (let x = 0; x < 20; x++) { map[x] = []; for (let z = 0; z < 20; z++) { map[x][z] = x < 10 && z < 10 ? 1 : 0; // Block of walls } } // Creating a hole in the wall block map[5][5] = 0; // Surrounded by 1s (8 neighbors). Should become 1. const newMap = generator.smoothMap(map); // (5,5) should be filled expect(newMap[5][5]).to.equal(1); }); it("CoA 6: applyTextures should assign floor and wall IDs", () => { // Setup manual grid state replicating generate() output // Wall at 5,5 (Column) for (let y = 0; y < grid.size.y; y++) { grid.setCell(5, y, 5, 100); } // Floor at 6,6 (Only y=0) grid.setCell(6, 0, 6, 100); // y=1 is air grid.setCell(6, 1, 6, 0); generator.applyTextures(); // Wall Check const wallId = grid.getCell(5, 5, 5); expect(wallId).to.be.greaterThanOrEqual(100); expect(wallId).to.be.lessThanOrEqual(109); // Floor Check const floorId = grid.getCell(6, 0, 6); expect(floorId).to.be.greaterThanOrEqual(200); expect(floorId).to.be.lessThanOrEqual(209); }); it("CoA 7: generate should scatter cover objects", () => { generator.generate(0.5, 2); // Check for cover objects (ID 10) let coverCount = 0; for (let x = 0; x < grid.size.x; x++) { for (let z = 0; z < grid.size.z; z++) { for (let y = 0; y < grid.size.y; y++) { if (grid.getCell(x, y, z) === 10) { coverCount++; } } } } // Should have some cover objects expect(coverCount).to.be.greaterThan(0); }); it("CoA 8: generate with same seed should produce consistent results", () => { const grid1 = new VoxelGrid(20, 10, 20); const gen1 = new CaveGenerator(grid1, 12345); gen1.generate(0.5, 2); const grid2 = new VoxelGrid(20, 10, 20); const gen2 = new CaveGenerator(grid2, 12345); gen2.generate(0.5, 2); // Same seed should produce same results expect(grid1.cells).to.deep.equal(grid2.cells); }); it("CoA 9: ensureConnectivity should remove disconnected small regions", () => { // Create a map with two disconnected regions // Region A: 3x3 (Total 9) // Region B: 1x1 (Total 1) let map = []; for (let x = 0; x < 20; x++) { map[x] = []; for (let z = 0; z < 20; z++) { map[x][z] = 1; // Walls everywhere } } // Region A (Large) for (let x = 1; x <= 3; x++) { for (let z = 1; z <= 3; z++) { map[x][z] = 0; } } // Region B (Small, disconnected) map[10][10] = 0; const connectedMap = generator.ensureConnectivity(map); // Region A should remain expect(connectedMap[2][2]).to.equal(0); // Region B should be filled expect(connectedMap[10][10]).to.equal(1); }); it("CoA 10: scatterWallDetails should place objects on wall faces", () => { // Setup a single wall column at 5,5 from y=0 to y=10 // And air at 6,5 (adjacent) for (let y = 0; y < grid.size.y; y++) { grid.setCell(5, y, 5, 100); grid.setCell(6, y, 5, 0); } // Force seed/density to ensure placement // Or just check that it CAN place // Let's rely on randomness with a loop or high density generator.scatterWallDetails(11, 1.0, 2); // 100% density for test // Check y=2 to y=height-2 (safe range) let foundDetail = false; for (let y = 2; y < grid.size.y - 1; y++) { const id = grid.getCell(6, y, 5); if (id === 11) foundDetail = true; } expect(foundDetail).to.be.true; // Ensure it didn't place below minHeight (y=1) expect(grid.getCell(6, 1, 5)).to.equal(0); }); });