Add side-view world initialization with sand and sky
This commit is contained in:
parent
e8e5691d83
commit
e89ee1afa2
2 changed files with 134 additions and 0 deletions
43
src/WorldInit.ts
Normal file
43
src/WorldInit.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { MAT_FOOD, MAT_HOME, MAT_SAND } from "./constants";
|
||||||
|
|
||||||
|
export function generateSideViewWorld(worldSize: number): Float32Array {
|
||||||
|
const data = new Float32Array(worldSize * worldSize * 4);
|
||||||
|
const sandHeight = Math.floor(worldSize * 0.6);
|
||||||
|
const surfaceRow = sandHeight - 1;
|
||||||
|
|
||||||
|
// fill bottom 60% with sand
|
||||||
|
for (let y = 0; y < sandHeight; y++) {
|
||||||
|
for (let x = 0; x < worldSize; x++) {
|
||||||
|
const idx = (y * worldSize + x) * 4;
|
||||||
|
data[idx] = MAT_SAND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// top 40% stays MAT_AIR (Float32Array is zero-initialized)
|
||||||
|
|
||||||
|
// place home on surface near center
|
||||||
|
const centerX = Math.floor(worldSize / 2);
|
||||||
|
const homeIdx = (surfaceRow * worldSize + centerX) * 4;
|
||||||
|
data[homeIdx] = MAT_HOME;
|
||||||
|
|
||||||
|
// scatter food on surface — deterministic spots spread across the row
|
||||||
|
const foodPositions = [
|
||||||
|
Math.floor(worldSize * 0.15),
|
||||||
|
Math.floor(worldSize * 0.35),
|
||||||
|
Math.floor(worldSize * 0.65),
|
||||||
|
Math.floor(worldSize * 0.85),
|
||||||
|
];
|
||||||
|
for (const fx of foodPositions) {
|
||||||
|
// place a small cluster (3 cells wide) at each position
|
||||||
|
for (let dx = -1; dx <= 1; dx++) {
|
||||||
|
const x = fx + dx;
|
||||||
|
if (x >= 0 && x < worldSize) {
|
||||||
|
const idx = (surfaceRow * worldSize + x) * 4;
|
||||||
|
if (data[idx] !== MAT_HOME) {
|
||||||
|
data[idx] = MAT_FOOD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
91
src/__tests__/worldInit.test.ts
Normal file
91
src/__tests__/worldInit.test.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { describe, expect, test } from "bun:test";
|
||||||
|
import { MAT_AIR, MAT_FOOD, MAT_HOME, MAT_SAND } from "../constants";
|
||||||
|
import { generateSideViewWorld } from "../WorldInit";
|
||||||
|
|
||||||
|
const SIZE = 64;
|
||||||
|
|
||||||
|
describe("generateSideViewWorld", () => {
|
||||||
|
test("output length is worldSize * worldSize * 4", () => {
|
||||||
|
const data = generateSideViewWorld(SIZE);
|
||||||
|
expect(data.length).toBe(SIZE * SIZE * 4);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("bottom 60% of rows have R = MAT_SAND", () => {
|
||||||
|
const data = generateSideViewWorld(SIZE);
|
||||||
|
const sandHeight = Math.floor(SIZE * 0.6);
|
||||||
|
for (let y = 0; y < sandHeight; y++) {
|
||||||
|
for (let x = 0; x < SIZE; x++) {
|
||||||
|
const idx = (y * SIZE + x) * 4;
|
||||||
|
const mat = data[idx];
|
||||||
|
// home and food are allowed on the surface row
|
||||||
|
if (y === sandHeight - 1) {
|
||||||
|
expect([MAT_SAND, MAT_HOME, MAT_FOOD]).toContain(mat);
|
||||||
|
} else {
|
||||||
|
expect(mat).toBe(MAT_SAND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("top 40% of rows have R = MAT_AIR", () => {
|
||||||
|
const data = generateSideViewWorld(SIZE);
|
||||||
|
const sandHeight = Math.floor(SIZE * 0.6);
|
||||||
|
for (let y = sandHeight; y < SIZE; y++) {
|
||||||
|
for (let x = 0; x < SIZE; x++) {
|
||||||
|
const idx = (y * SIZE + x) * 4;
|
||||||
|
expect(data[idx]).toBe(MAT_AIR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("G, B, A channels are 0 everywhere", () => {
|
||||||
|
const data = generateSideViewWorld(SIZE);
|
||||||
|
for (let i = 0; i < SIZE * SIZE; i++) {
|
||||||
|
expect(data[i * 4 + 1]).toBe(0); // G
|
||||||
|
expect(data[i * 4 + 2]).toBe(0); // B
|
||||||
|
expect(data[i * 4 + 3]).toBe(0); // A
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("exactly one cell has R = MAT_HOME on the surface row near center X", () => {
|
||||||
|
const data = generateSideViewWorld(SIZE);
|
||||||
|
const surfaceRow = Math.floor(SIZE * 0.6) - 1;
|
||||||
|
const centerX = Math.floor(SIZE / 2);
|
||||||
|
const tolerance = Math.floor(SIZE * 0.1);
|
||||||
|
|
||||||
|
let homeCount = 0;
|
||||||
|
for (let i = 0; i < SIZE * SIZE; i++) {
|
||||||
|
if (data[i * 4] === MAT_HOME) {
|
||||||
|
homeCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(homeCount).toBe(1);
|
||||||
|
|
||||||
|
// verify it's on the surface row — find the row
|
||||||
|
for (let x = 0; x < SIZE; x++) {
|
||||||
|
const idx = (surfaceRow * SIZE + x) * 4;
|
||||||
|
if (data[idx] === MAT_HOME) {
|
||||||
|
expect(Math.abs(x - centerX)).toBeLessThanOrEqual(tolerance);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we reach here, home wasn't on the surface row
|
||||||
|
expect("home not on surface row").toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("some cells have R = MAT_FOOD on the surface row", () => {
|
||||||
|
const data = generateSideViewWorld(SIZE);
|
||||||
|
const surfaceRow = Math.floor(SIZE * 0.6) - 1;
|
||||||
|
|
||||||
|
let foodCount = 0;
|
||||||
|
for (let x = 0; x < SIZE; x++) {
|
||||||
|
const idx = (surfaceRow * SIZE + x) * 4;
|
||||||
|
if (data[idx] === MAT_FOOD) {
|
||||||
|
foodCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(foodCount).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue