refactor: make getRelatedTo return an iterator
Convert `getRelatedTo` from returning an array to returning an `IterableIterator`. This improves memory efficiency by yielding entities lazily instead of allocating a new array on every call.
This commit is contained in:
parent
2469cdc7cb
commit
5d125167cc
|
|
@ -52,8 +52,8 @@ function clearSubtree(world: World, entity: Entity): void {
|
|||
}
|
||||
}
|
||||
|
||||
function childrenOf(world: World, parent: Entity): Entity[] {
|
||||
return world.getRelatedTo(parent, ChildOf);
|
||||
function* childrenOf(world: World, parent: Entity): IterableIterator<Entity> {
|
||||
yield* world.getRelatedTo(parent, ChildOf);
|
||||
}
|
||||
|
||||
function parentOf(world: World, child: Entity): Entity | null {
|
||||
|
|
@ -230,10 +230,9 @@ export class TaskRunner {
|
|||
}
|
||||
|
||||
private _executeRandom(entity: Entity): void {
|
||||
const children = childrenOf(this._world, entity);
|
||||
|
||||
// Check if any child already reached a terminal
|
||||
for (const child of children) {
|
||||
// Single pass: check terminals and collect eligible children
|
||||
const eligible: Entity[] = [];
|
||||
for (const child of childrenOf(this._world, entity)) {
|
||||
if (isTerminal(this._world, child)) {
|
||||
const status = terminalStatus(this._world, child)!;
|
||||
this._finish(
|
||||
|
|
@ -246,13 +245,14 @@ export class TaskRunner {
|
|||
);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!this._world.has(child, Running) &&
|
||||
!this._world.has(child, Scheduled)
|
||||
) {
|
||||
eligible.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
// Pick a random child that isn't already running
|
||||
const eligible = children.filter(
|
||||
(c) => !this._world.has(c, Running) && !this._world.has(c, Scheduled),
|
||||
);
|
||||
|
||||
if (eligible.length > 0) {
|
||||
const pick = eligible[Math.floor(Math.random() * eligible.length)];
|
||||
this._world.add(pick, Scheduled);
|
||||
|
|
@ -261,7 +261,7 @@ export class TaskRunner {
|
|||
}
|
||||
|
||||
private _executeRepeat(entity: Entity): void {
|
||||
const children = childrenOf(this._world, entity);
|
||||
const children = [...childrenOf(this._world, entity)];
|
||||
|
||||
// Repeat expects exactly one child
|
||||
if (children.length === 0) return;
|
||||
|
|
|
|||
15
src/world.ts
15
src/world.ts
|
|
@ -388,17 +388,22 @@ export class World {
|
|||
/**
|
||||
* Get all source entities that point to `target` via this relationship.
|
||||
*/
|
||||
getRelatedTo(target: Entity, rel: RelationshipDef): Entity[] {
|
||||
*getRelatedTo(
|
||||
target: Entity,
|
||||
rel: RelationshipDef,
|
||||
): IterableIterator<Entity> {
|
||||
const ti = entityIndex(target);
|
||||
if (!this._isAlive(ti, target)) return [];
|
||||
if (!this._isAlive(ti, target)) return;
|
||||
|
||||
const rev = this._relReverse.get(rel._key);
|
||||
if (!rev) return [];
|
||||
if (!rev) return;
|
||||
|
||||
const sources = rev.get(ti);
|
||||
if (!sources) return [];
|
||||
if (!sources) return;
|
||||
|
||||
return [...sources].map((si) => makeEntity(si, this._generations[si]));
|
||||
for (const si of sources) {
|
||||
yield makeEntity(si, this._generations[si]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ describe("Relationships", () => {
|
|||
world.relate(a, ChildOf, parent);
|
||||
world.relate(b, ChildOf, parent);
|
||||
|
||||
const children = world.getRelatedTo(parent, ChildOf);
|
||||
const children = [...world.getRelatedTo(parent, ChildOf)];
|
||||
expect(children).toHaveLength(2);
|
||||
expect(children).toContain(a);
|
||||
expect(children).toContain(b);
|
||||
|
|
@ -63,7 +63,7 @@ describe("Relationships", () => {
|
|||
|
||||
it("getRelatedTo returns empty when no edges", () => {
|
||||
const e = world.spawn();
|
||||
expect(world.getRelatedTo(e, ChildOf)).toEqual([]);
|
||||
expect([...world.getRelatedTo(e, ChildOf)]).toEqual([]);
|
||||
});
|
||||
|
||||
it("unrelate removes the relationship", () => {
|
||||
|
|
@ -74,7 +74,7 @@ describe("Relationships", () => {
|
|||
world.unrelate(child, ChildOf);
|
||||
|
||||
expect(world.getRelated(child, ChildOf)).toBeUndefined();
|
||||
expect(world.getRelatedTo(parent, ChildOf)).toEqual([]);
|
||||
expect([...world.getRelatedTo(parent, ChildOf)]).toEqual([]);
|
||||
});
|
||||
|
||||
it("unrelate is idempotent", () => {
|
||||
|
|
@ -89,13 +89,13 @@ describe("Relationships", () => {
|
|||
|
||||
world.relate(a, ChildOf, b);
|
||||
expect(world.getRelated(a, ChildOf)).toBe(b);
|
||||
expect(world.getRelatedTo(b, ChildOf)).toContain(a);
|
||||
expect([...world.getRelatedTo(b, ChildOf)]).toContain(a);
|
||||
|
||||
world.relate(a, ChildOf, c);
|
||||
expect(world.getRelated(a, ChildOf)).toBe(c);
|
||||
// a should no longer point to b
|
||||
expect(world.getRelatedTo(b, ChildOf)).toEqual([]);
|
||||
expect(world.getRelatedTo(c, ChildOf)).toContain(a);
|
||||
expect([...world.getRelatedTo(b, ChildOf)]).toEqual([]);
|
||||
expect([...world.getRelatedTo(c, ChildOf)]).toContain(a);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -243,7 +243,7 @@ describe("Destroy cleanup", () => {
|
|||
|
||||
world.destroy(child);
|
||||
expect(world.getRelated(child, ChildOf)).toBeUndefined();
|
||||
expect(world.getRelatedTo(parent, ChildOf)).toEqual([]);
|
||||
expect([...world.getRelatedTo(parent, ChildOf)]).toEqual([]);
|
||||
});
|
||||
|
||||
it("removes edges when target is destroyed", () => {
|
||||
|
|
@ -252,7 +252,7 @@ describe("Destroy cleanup", () => {
|
|||
world.relate(child, ChildOf, parent);
|
||||
|
||||
world.destroy(parent);
|
||||
expect(world.getRelatedTo(parent, ChildOf)).toEqual([]);
|
||||
expect([...world.getRelatedTo(parent, ChildOf)]).toEqual([]);
|
||||
expect(world.getRelated(child, ChildOf)).toBeUndefined();
|
||||
});
|
||||
|
||||
|
|
@ -376,6 +376,6 @@ describe("Dead entity safety", () => {
|
|||
it("getRelatedTo returns empty for dead entity", () => {
|
||||
const e = world.spawn();
|
||||
world.destroy(e);
|
||||
expect(world.getRelatedTo(e, ChildOf)).toEqual([]);
|
||||
expect([...world.getRelatedTo(e, ChildOf)]).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue