Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion PROJECT_DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ npm run build
| Grid pathfinding | `astar`, `dijkstra`, `jumpPointSearch`, `computeFlowField`, `buildNavMesh`, `findNavMeshPath`, `manhattanDistance`, `gridFromString` | `pathfinding/astar.ts`, `pathfinding/dijkstra.ts`, `pathfinding/jumpPointSearch.ts`, `pathfinding/flowField.ts`, `pathfinding/navMesh.ts` | `examples/astar.ts`, `examples/flowField.ts`, `examples/navMesh.ts` |
| Procedural textures & terrain | `perlin`, `perlin3D`, `simplex2D`, `simplex3D`, `worley`, `worleySample`, `waveFunctionCollapse`, `cellularAutomataCave`, `poissonDiskSampling`, `computeVoronoiDiagram`, `diamondSquare`, `generateLSystem`, `generateBspDungeon`, `generateRecursiveMaze`, `generatePrimMaze`, `generateKruskalMaze`, `generateWilsonMaze`, `generateAldousBroderMaze`, `generateRecursiveDivisionMaze` | `procedural/*.ts` | `examples/simplex.ts`, `examples/worley.ts`, `examples/waveFunctionCollapse.ts`, `examples/cellularAutomata.ts`, `examples/poissonDisk.ts`, `examples/voronoi.ts`, `examples/diamondSquare.ts`, `examples/lSystem.ts`, `examples/dungeonBsp.ts`, `examples/mazeRecursive.ts`, `examples/mazePrim.ts`, `examples/mazeKruskal.ts`, `examples/mazeWilson.ts`, `examples/mazeAldous.ts`, `examples/mazeDivision.ts` |
| Spatial queries & collision | `Quadtree`, `aabbCollision`, `aabbIntersection`, `satCollision`, `circleRayIntersection`, `sweptAABB` | `spatial/*.ts` | `examples/sat.ts` |
| Web performance & UI throttling | `debounce`, `throttle`, `LRUCache`, `memoize`, `deduplicateRequest`, `clearRequestDedup`, `calculateVirtualRange`, `createWeightedAliasSampler` | `util/*.ts` | `examples/requestDedup.ts`, `examples/virtualScroll.ts`, `examples/weightedAlias.ts` |
| Web performance & UI throttling | `debounce`, `throttle`, `LRUCache`, `memoize`, `deduplicateRequest`, `clearRequestDedup`, `calculateVirtualRange`, `createWeightedAliasSampler`, `createObjectPool` | `util/*.ts` | `examples/requestDedup.ts`, `examples/virtualScroll.ts`, `examples/weightedAlias.ts`, `examples/objectPool.ts` |
| Text & search | `fuzzySearch`, `fuzzyScore`, `Trie`, `binarySearch`, `levenshteinDistance` | `search/*.ts` | `examples/search.ts` |
| Data transforms & diffing | `diff`, `deepClone`, `groupBy`, `diffJson`, `applyJsonDiff` | `data/*.ts` | `examples/jsonDiff.ts` |
| Graph traversal | `graphBFS`, `graphDFS`, `topologicalSort` | `graph/traversal.ts` | `examples/graph.ts` |
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ CDN usage:
| Procedural generation | `perlin`, `perlin3D`, `simplex2D`, `simplex3D`, `worley`, `worleySample`, `waveFunctionCollapse`, `cellularAutomataCave`, `poissonDiskSampling`, `computeVoronoiDiagram`, `diamondSquare`, `generateLSystem`, `generateBspDungeon`, `generateRecursiveMaze`, `generatePrimMaze`, `generateKruskalMaze`, `generateWilsonMaze`, `generateAldousBroderMaze`, `generateRecursiveDivisionMaze` | `procedural/*.ts` | `examples/simplex.ts`, `examples/worley.ts`, `examples/waveFunctionCollapse.ts`, `examples/cellularAutomata.ts`, `examples/poissonDisk.ts`, `examples/voronoi.ts`, `examples/diamondSquare.ts`, `examples/lSystem.ts`, `examples/dungeonBsp.ts`, `examples/mazeRecursive.ts`, `examples/mazePrim.ts`, `examples/mazeKruskal.ts`, `examples/mazeWilson.ts`, `examples/mazeAldous.ts`, `examples/mazeDivision.ts` |
| Spatial queries & collision | `Quadtree`, `aabbCollision`, `aabbIntersection`, `satCollision`, `circleRayIntersection`, `sweptAABB` | `spatial/*.ts` | `examples/sat.ts` |
| AI behaviours & crowds | `seek`, `flee`, `arrive`, `pursue`, `wander`, `updateBoids`, `BehaviorTree`, `rvoStep` | `ai/steering.ts`, `ai/boids.ts`, `ai/behaviorTree.ts`, `ai/rvo.ts` | `examples/steering.ts`, `examples/boids.ts`, `examples/rvo.ts` |
| Web performance & UI | `debounce`, `throttle`, `LRUCache`, `memoize`, `deduplicateRequest`, `clearRequestDedup`, `calculateVirtualRange`, `createWeightedAliasSampler` | `util/*.ts` | `examples/requestDedup.ts`, `examples/virtualScroll.ts`, `examples/weightedAlias.ts` |
| Web performance & UI | `debounce`, `throttle`, `LRUCache`, `memoize`, `deduplicateRequest`, `clearRequestDedup`, `calculateVirtualRange`, `createWeightedAliasSampler`, `createObjectPool` | `util/*.ts` | `examples/requestDedup.ts`, `examples/virtualScroll.ts`, `examples/weightedAlias.ts`, `examples/objectPool.ts` |
| Search & text | `fuzzySearch`, `fuzzyScore`, `Trie`, `binarySearch`, `levenshteinDistance` | `search/*.ts` | `examples/search.ts` |
| Data & diff pipelines | `diff`, `deepClone`, `groupBy`, `diffJson`, `applyJsonDiff` | `data/*.ts` | `examples/jsonDiff.ts` |
| Graph algorithms | `graphBFS`, `graphDFS`, `topologicalSort` | `graph/traversal.ts` | `examples/graph.ts` |
Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
- Gameplay systems & utilities:
- [ ] Fixed-timestep game loop utility with interpolation helpers
- [ ] Delta-time manager for frame-independent timing
- [ ] Object pool helper for reusable entities
- [x] Object pool helper for reusable entities
- [x] Weighted random selector (alias method)
- [ ] Fisher–Yates shuffle implementation
- [ ] Bresenham line / raster traversal helpers
Expand Down
32 changes: 32 additions & 0 deletions docs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export const examples: {
readonly clearRequestDedup: 'examples/requestDedup.ts';
readonly calculateVirtualRange: 'examples/virtualScroll.ts';
readonly createWeightedAliasSampler: 'examples/weightedAlias.ts';
readonly createObjectPool: 'examples/objectPool.ts';
};
readonly ai: {
readonly seek: 'examples/steering.ts';
Expand Down Expand Up @@ -819,6 +820,37 @@ export function createWeightedAliasSampler<T>(
entries: ReadonlyArray<WeightedAliasEntry<T>>
): WeightedAliasSampler<T>;

/**
* Object pool options.
* Use for: configuring factories, reset functions, and pool sizing.
* Import: util/objectPool.ts
*/
export interface ObjectPoolOptions<T> {
factory: () => T;
reset?: (item: T) => void;
initialSize?: number;
maxSize?: number;
}

/**
* Object pool API exposing acquire/release.
* Import: util/objectPool.ts
*/
export interface ObjectPool<T> {
acquire(): T;
release(item: T): void;
available(): number;
size(): number;
}

/**
* Creates an object pool for reusing allocations.
* Use for: performance sensitive systems and resource recycling.
* Performance: O(1) acquire/release with optional reset handling.
* Import: util/objectPool.ts
*/
export function createObjectPool<T>(options: ObjectPoolOptions<T>): ObjectPool<T>;

/**
* Least recently used cache.
* Use for: memoizing responses, data loaders, pagination caches.
Expand Down
22 changes: 22 additions & 0 deletions examples/objectPool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createObjectPool } from '../src/index.js';

interface Particle {
id: number;
active: boolean;
}

let nextId = 1;
const pool = createObjectPool<Particle>({
factory: () => ({ id: nextId += 1, active: true }),
reset: (particle) => {
particle.active = true;
},
initialSize: 2,
maxSize: 5,
});

const particle = pool.acquire();
console.log('Acquired particle:', particle);
particle.active = false;
pool.release(particle);
console.log('Available after release:', pool.available());
8 changes: 8 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const examples = {
clearRequestDedup: 'examples/requestDedup.ts',
calculateVirtualRange: 'examples/virtualScroll.ts',
createWeightedAliasSampler: 'examples/weightedAlias.ts',
createObjectPool: 'examples/objectPool.ts',
},
ai: {
seek: 'examples/steering.ts',
Expand Down Expand Up @@ -415,6 +416,13 @@ export { calculateVirtualRange } from './util/virtualScroll.js';
*/
export { createWeightedAliasSampler } from './util/weightedAlias.js';

/**
* Object pool helper for reusing allocations.
*
* Example file: examples/objectPool.ts
*/
export { createObjectPool } from './util/objectPool.js';

/**
* Virtual scroll type exports to help define rendering contracts.
*/
Expand Down
69 changes: 69 additions & 0 deletions src/util/objectPool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
export interface ObjectPoolOptions<T> {
factory: () => T;
reset?: (item: T) => void;
initialSize?: number;
maxSize?: number;
}

export interface ObjectPool<T> {
acquire(): T;
release(item: T): void;
available(): number;
size(): number;
}

/**
* Creates an object pool for reusing allocations.
* Useful for: performance-critical loops, game entities, temporary buffers.
*/
export function createObjectPool<T>({
factory,
reset,
initialSize = 0,
maxSize = Number.POSITIVE_INFINITY,
}: ObjectPoolOptions<T>): ObjectPool<T> {
if (typeof factory !== 'function') {
throw new Error('factory must be a function.');
}
if (initialSize < 0) {
throw new Error('initialSize must be >= 0.');
}
if (maxSize < initialSize) {
throw new Error('maxSize must be >= initialSize.');
}

const freeList: T[] = [];
for (let i = 0; i < initialSize; i += 1) {
freeList.push(factory());
}

let totalCreated = initialSize;

function acquire(): T {
if (freeList.length > 0) {
return freeList.pop()!;
}
if (totalCreated >= maxSize) {
throw new Error('Object pool depleted and reached maxSize.');
}
totalCreated += 1;
return factory();
}

function release(item: T): void {
if (reset) {
reset(item);
}
freeList.push(item);
}

function available(): number {
return freeList.length;
}

function size(): number {
return totalCreated;
}

return { acquire, release, available, size };
}
14 changes: 14 additions & 0 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ describe('package entry point', () => {
expect(examples.procedural.generateAldousBroderMaze).toBe('examples/mazeAldous.ts');
expect(examples.procedural.generateRecursiveDivisionMaze).toBe('examples/mazeDivision.ts');
expect(examples.performance.createWeightedAliasSampler).toBe('examples/weightedAlias.ts');
expect(examples.performance.createObjectPool).toBe('examples/objectPool.ts');
expect(examples.performance.createWeightedAliasSampler).toBe('examples/weightedAlias.ts');
expect(examples.search.Trie).toBe('examples/search.ts');
expect(examples.pathfinding.buildNavMesh).toBe('examples/navMesh.ts');
});
Expand Down Expand Up @@ -72,5 +74,17 @@ describe('package entry point', () => {
| 'generateAldousBroderMaze'
| 'generateRecursiveDivisionMaze'
>();

expectTypeOf<ExampleName<'performance'>>().toEqualTypeOf<
| 'debounce'
| 'throttle'
| 'LRUCache'
| 'memoize'
| 'deduplicateRequest'
| 'clearRequestDedup'
| 'calculateVirtualRange'
| 'createWeightedAliasSampler'
| 'createObjectPool'
>();
});
});
35 changes: 35 additions & 0 deletions tests/objectPool.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, it } from 'vitest';

import { createObjectPool } from '../src/index.js';

describe('createObjectPool', () => {
it('reuses objects and respects reset', () => {
let created = 0;
const pool = createObjectPool({
factory: () => ({ used: false, id: created += 1 }),
reset: (item) => {
item.used = false;
},
initialSize: 1,
});

const first = pool.acquire();
first.used = true;
pool.release(first);

const second = pool.acquire();
expect(second).toBe(first);
expect(second.used).toBe(false);
});

it('throws when pool depleted beyond maxSize', () => {
const pool = createObjectPool({
factory: () => ({}),
initialSize: 0,
maxSize: 1,
});

pool.acquire();
expect(() => pool.acquire()).toThrow();
});
});