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`, `createObjectPool` | `util/*.ts` | `examples/requestDedup.ts`, `examples/virtualScroll.ts`, `examples/weightedAlias.ts`, `examples/objectPool.ts` |
| Web performance & UI throttling | `debounce`, `throttle`, `LRUCache`, `memoize`, `deduplicateRequest`, `clearRequestDedup`, `calculateVirtualRange`, `createWeightedAliasSampler`, `createObjectPool`, `fisherYatesShuffle` | `util/*.ts` | `examples/requestDedup.ts`, `examples/virtualScroll.ts`, `examples/weightedAlias.ts`, `examples/objectPool.ts`, `examples/fisherYates.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`, `createObjectPool` | `util/*.ts` | `examples/requestDedup.ts`, `examples/virtualScroll.ts`, `examples/weightedAlias.ts`, `examples/objectPool.ts` |
| Web performance & UI | `debounce`, `throttle`, `LRUCache`, `memoize`, `deduplicateRequest`, `clearRequestDedup`, `calculateVirtualRange`, `createWeightedAliasSampler`, `createObjectPool`, `fisherYatesShuffle` | `util/*.ts` | `examples/requestDedup.ts`, `examples/virtualScroll.ts`, `examples/weightedAlias.ts`, `examples/objectPool.ts`, `examples/fisherYates.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 @@ -48,7 +48,7 @@
- [ ] Delta-time manager for frame-independent timing
- [x] Object pool helper for reusable entities
- [x] Weighted random selector (alias method)
- [ ] Fisher–Yates shuffle implementation
- [x] Fisher–Yates shuffle implementation
- [ ] Bresenham line / raster traversal helpers
- Real-time systems:
- [ ] 2D camera system (smooth follow, dead zones, screen shake)
Expand Down
8 changes: 8 additions & 0 deletions docs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export const examples: {
readonly calculateVirtualRange: 'examples/virtualScroll.ts';
readonly createWeightedAliasSampler: 'examples/weightedAlias.ts';
readonly createObjectPool: 'examples/objectPool.ts';
readonly fisherYatesShuffle: 'examples/fisherYates.ts';
};
readonly ai: {
readonly seek: 'examples/steering.ts';
Expand Down Expand Up @@ -851,6 +852,13 @@ export interface ObjectPool<T> {
*/
export function createObjectPool<T>(options: ObjectPoolOptions<T>): ObjectPool<T>;

/**
* Shuffles an array in place using Fisher–Yates.
* Use for: unbiased permutations, testing, random ordering.
* Import: util/fisherYates.ts
*/
export function fisherYatesShuffle<T>(items: T[], options?: { random?: () => number }): T[];

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

const rng = () => 0.42;
const items = ['a', 'b', 'c', 'd'];
console.log('Original:', items);
fisherYatesShuffle(items, { random: rng });
console.log('Shuffled:', items);
8 changes: 8 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export const examples = {
calculateVirtualRange: 'examples/virtualScroll.ts',
createWeightedAliasSampler: 'examples/weightedAlias.ts',
createObjectPool: 'examples/objectPool.ts',
fisherYatesShuffle: 'examples/fisherYates.ts',
},
ai: {
seek: 'examples/steering.ts',
Expand Down Expand Up @@ -423,6 +424,13 @@ export { createWeightedAliasSampler } from './util/weightedAlias.js';
*/
export { createObjectPool } from './util/objectPool.js';

/**
* Fisher–Yates shuffling utility for unbiased permutations.
*
* Example file: examples/fisherYates.ts
*/
export { fisherYatesShuffle } from './util/fisherYates.js';

/**
* Virtual scroll type exports to help define rendering contracts.
*/
Expand Down
23 changes: 23 additions & 0 deletions src/util/fisherYates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export interface FisherYatesOptions {
random?: () => number;
}

/**
* Shuffles an array in place using the Fisher–Yates algorithm.
* Useful for: unbiased random permutations for gameplay, sampling, or testing.
*/
export function fisherYatesShuffle<T>(items: T[], options: FisherYatesOptions = {}): T[] {
const { random = Math.random } = options;
if (!Array.isArray(items)) {
throw new Error('items must be an array');
}
for (let i = items.length - 1; i > 0; i -= 1) {
const r = random();
if (r < 0 || r >= 1) {
throw new Error('random function must return value in [0,1).');
}
const j = Math.floor(r * (i + 1));
[items[i], items[j]] = [items[j], items[i]];
}
return items;
}
25 changes: 25 additions & 0 deletions tests/fisherYates.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { describe, expect, it } from 'vitest';

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

describe('fisherYatesShuffle', () => {
it('shuffles array in place using deterministic random', () => {
const items = [1, 2, 3, 4];
let index = 0;
const randomValues = [0.1, 0.9, 0.3, 0.7];
const random = () => {
const value = randomValues[index % randomValues.length] ?? 0;
index += 1;
return value;
};

const result = fisherYatesShuffle(items, { random });
expect(result).toEqual([2, 4, 3, 1]);
expect(result).toBe(items); // in place
});

it('throws when random returns value outside [0,1)', () => {
const arr = [1, 2];
expect(() => fisherYatesShuffle(arr, { random: () => 1 })).toThrow();
});
});
2 changes: 2 additions & 0 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ describe('package entry point', () => {
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.fisherYatesShuffle).toBe('examples/fisherYates.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 @@ -85,6 +86,7 @@ describe('package entry point', () => {
| 'calculateVirtualRange'
| 'createWeightedAliasSampler'
| 'createObjectPool'
| 'fisherYatesShuffle'
>();
});
});