murow 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -0
- package/dist/core/binary-codec/binary-codec.d.ts +159 -0
- package/dist/core/binary-codec/binary-codec.js +336 -0
- package/dist/core/binary-codec/index.d.ts +1 -0
- package/dist/core/binary-codec/index.js +1 -0
- package/dist/core/events/event-system.d.ts +71 -0
- package/dist/core/events/event-system.js +88 -0
- package/dist/core/events/index.d.ts +1 -0
- package/dist/core/events/index.js +1 -0
- package/dist/core/fixed-ticker/fixed-ticker.d.ts +105 -0
- package/dist/core/fixed-ticker/fixed-ticker.js +91 -0
- package/dist/core/fixed-ticker/index.d.ts +1 -0
- package/dist/core/fixed-ticker/index.js +1 -0
- package/dist/core/generate-id/generate-id.d.ts +21 -0
- package/dist/core/generate-id/generate-id.js +25 -0
- package/dist/core/generate-id/index.d.ts +1 -0
- package/dist/core/generate-id/index.js +1 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.js +8 -0
- package/dist/core/lerp/index.d.ts +1 -0
- package/dist/core/lerp/index.js +1 -0
- package/dist/core/lerp/lerp.d.ts +40 -0
- package/dist/core/lerp/lerp.js +42 -0
- package/dist/core/navmesh/index.d.ts +1 -0
- package/dist/core/navmesh/index.js +1 -0
- package/dist/core/navmesh/navmesh.d.ts +116 -0
- package/dist/core/navmesh/navmesh.js +666 -0
- package/dist/core/pooled-codec/index.d.ts +1 -0
- package/dist/core/pooled-codec/index.js +1 -0
- package/dist/core/pooled-codec/pooled-codec.d.ts +140 -0
- package/dist/core/pooled-codec/pooled-codec.js +213 -0
- package/dist/core/prediction/index.d.ts +1 -0
- package/dist/core/prediction/index.js +1 -0
- package/dist/core/prediction/prediction.d.ts +64 -0
- package/dist/core/prediction/prediction.js +90 -0
- package/dist/core.esm.js +1 -0
- package/dist/core.js +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +18 -0
- package/dist/protocol/index.d.ts +43 -0
- package/dist/protocol/index.js +43 -0
- package/dist/protocol/intent/index.d.ts +39 -0
- package/dist/protocol/intent/index.js +38 -0
- package/dist/protocol/intent/intent-registry.d.ts +54 -0
- package/dist/protocol/intent/intent-registry.js +73 -0
- package/dist/protocol/intent/intent.d.ts +12 -0
- package/dist/protocol/intent/intent.js +1 -0
- package/dist/protocol/snapshot/index.d.ts +44 -0
- package/dist/protocol/snapshot/index.js +43 -0
- package/dist/protocol/snapshot/snapshot-codec.d.ts +48 -0
- package/dist/protocol/snapshot/snapshot-codec.js +56 -0
- package/dist/protocol/snapshot/snapshot-registry.d.ts +100 -0
- package/dist/protocol/snapshot/snapshot-registry.js +136 -0
- package/dist/protocol/snapshot/snapshot.d.ts +19 -0
- package/dist/protocol/snapshot/snapshot.js +30 -0
- package/package.json +54 -0
- package/src/core/binary-codec/README.md +60 -0
- package/src/core/binary-codec/binary-codec.test.ts +300 -0
- package/src/core/binary-codec/binary-codec.ts +430 -0
- package/src/core/binary-codec/index.ts +1 -0
- package/src/core/events/README.md +47 -0
- package/src/core/events/event-system.test.ts +243 -0
- package/src/core/events/event-system.ts +140 -0
- package/src/core/events/index.ts +1 -0
- package/src/core/fixed-ticker/README.md +77 -0
- package/src/core/fixed-ticker/fixed-ticker.test.ts +151 -0
- package/src/core/fixed-ticker/fixed-ticker.ts +158 -0
- package/src/core/fixed-ticker/index.ts +1 -0
- package/src/core/generate-id/README.md +18 -0
- package/src/core/generate-id/generate-id.test.ts +79 -0
- package/src/core/generate-id/generate-id.ts +37 -0
- package/src/core/generate-id/index.ts +1 -0
- package/src/core/index.ts +8 -0
- package/src/core/lerp/README.md +79 -0
- package/src/core/lerp/index.ts +1 -0
- package/src/core/lerp/lerp.test.ts +90 -0
- package/src/core/lerp/lerp.ts +42 -0
- package/src/core/navmesh/README.md +124 -0
- package/src/core/navmesh/index.ts +1 -0
- package/src/core/navmesh/navmesh.test.ts +344 -0
- package/src/core/navmesh/navmesh.ts +850 -0
- package/src/core/pooled-codec/README.md +70 -0
- package/src/core/pooled-codec/index.ts +1 -0
- package/src/core/pooled-codec/pooled-codec.test.ts +349 -0
- package/src/core/pooled-codec/pooled-codec.ts +239 -0
- package/src/core/prediction/README.md +64 -0
- package/src/core/prediction/index.ts +1 -0
- package/src/core/prediction/prediction.test.ts +422 -0
- package/src/core/prediction/prediction.ts +101 -0
- package/src/index.ts +20 -0
- package/src/protocol/README.md +310 -0
- package/src/protocol/index.ts +44 -0
- package/src/protocol/intent/index.ts +40 -0
- package/src/protocol/intent/intent-registry.test.ts +237 -0
- package/src/protocol/intent/intent-registry.ts +88 -0
- package/src/protocol/intent/intent.ts +12 -0
- package/src/protocol/snapshot/index.ts +45 -0
- package/src/protocol/snapshot/snapshot-codec.test.ts +138 -0
- package/src/protocol/snapshot/snapshot-codec.ts +71 -0
- package/src/protocol/snapshot/snapshot-registry.test.ts +302 -0
- package/src/protocol/snapshot/snapshot-registry.ts +162 -0
- package/src/protocol/snapshot/snapshot.test.ts +76 -0
- package/src/protocol/snapshot/snapshot.ts +41 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { describe, expect, test, beforeEach } from "bun:test";
|
|
2
|
+
import { NavMesh } from "./navmesh";
|
|
3
|
+
|
|
4
|
+
describe("NavMesh - Grid Navigation", () => {
|
|
5
|
+
let navmesh: NavMesh;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
navmesh = new NavMesh("grid");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("should initialize with grid type", () => {
|
|
12
|
+
expect(navmesh).toBeDefined();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("should find straight path with no obstacles", () => {
|
|
16
|
+
const path = navmesh.findPath({ from: { x: 0, y: 0 }, to: { x: 3, y: 0 } });
|
|
17
|
+
expect(path.length).toBeGreaterThan(0);
|
|
18
|
+
// Path returns cell centers (0.5, 0.5) not exact coordinates
|
|
19
|
+
expect(path[0].x).toBeCloseTo(0.5, 1);
|
|
20
|
+
expect(path[0].y).toBeCloseTo(0.5, 1);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("should add circle obstacle", () => {
|
|
24
|
+
const id = navmesh.addObstacle({
|
|
25
|
+
type: "circle",
|
|
26
|
+
pos: { x: 5, y: 5 },
|
|
27
|
+
radius: 2,
|
|
28
|
+
});
|
|
29
|
+
expect(id).toBeGreaterThan(0);
|
|
30
|
+
expect(navmesh.getObstacles().length).toBe(1);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("should add rectangle obstacle", () => {
|
|
34
|
+
const id = navmesh.addObstacle({
|
|
35
|
+
type: "rect",
|
|
36
|
+
pos: { x: 5, y: 5 },
|
|
37
|
+
size: { x: 2, y: 3 },
|
|
38
|
+
});
|
|
39
|
+
expect(id).toBeGreaterThan(0);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("should add polygon obstacle", () => {
|
|
43
|
+
const id = navmesh.addObstacle({
|
|
44
|
+
type: "polygon",
|
|
45
|
+
points: [
|
|
46
|
+
{ x: 0, y: 0 },
|
|
47
|
+
{ x: 1, y: 0 },
|
|
48
|
+
{ x: 1, y: 1 },
|
|
49
|
+
{ x: 0, y: 1 },
|
|
50
|
+
],
|
|
51
|
+
pos: { x: 10, y: 10 },
|
|
52
|
+
});
|
|
53
|
+
expect(id).toBeGreaterThan(0);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("should find path around circle obstacle", () => {
|
|
57
|
+
navmesh.addObstacle({
|
|
58
|
+
type: "circle",
|
|
59
|
+
pos: { x: 2, y: 0 },
|
|
60
|
+
radius: 1,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const path = navmesh.findPath({ from: { x: 0, y: 0 }, to: { x: 4, y: 0 } });
|
|
64
|
+
expect(path.length).toBeGreaterThan(2); // Should go around, not through
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("should find path around rectangle obstacle", () => {
|
|
68
|
+
navmesh.addObstacle({
|
|
69
|
+
type: "rect",
|
|
70
|
+
pos: { x: 2, y: -1 },
|
|
71
|
+
size: { x: 2, y: 2 },
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const path = navmesh.findPath({ from: { x: 0, y: 0 }, to: { x: 5, y: 0 } });
|
|
75
|
+
expect(path.length).toBeGreaterThan(0);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("should return empty path when completely surrounded", () => {
|
|
79
|
+
// Surround the start position completely
|
|
80
|
+
for (let x = -2; x <= 2; x++) {
|
|
81
|
+
for (let y = -2; y <= 2; y++) {
|
|
82
|
+
if (x === 0 && y === 0) continue; // Skip the start position
|
|
83
|
+
navmesh.addObstacle({
|
|
84
|
+
type: "circle",
|
|
85
|
+
pos: { x, y },
|
|
86
|
+
radius: 0.9,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const path = navmesh.findPath({ from: { x: 0, y: 0 }, to: { x: 10, y: 10 } });
|
|
92
|
+
expect(path.length).toBe(0);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("should remove obstacle", () => {
|
|
96
|
+
const id = navmesh.addObstacle({
|
|
97
|
+
type: "circle",
|
|
98
|
+
pos: { x: 5, y: 5 },
|
|
99
|
+
radius: 1,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(navmesh.getObstacles().length).toBe(1);
|
|
103
|
+
navmesh.removeObstacle(id);
|
|
104
|
+
expect(navmesh.getObstacles().length).toBe(0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("should move obstacle", () => {
|
|
108
|
+
const id = navmesh.addObstacle({
|
|
109
|
+
type: "circle",
|
|
110
|
+
pos: { x: 5, y: 5 },
|
|
111
|
+
radius: 1,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
navmesh.moveObstacle(id, { x: 10, y: 10 });
|
|
115
|
+
const obstacles = navmesh.getObstacles();
|
|
116
|
+
expect(obstacles[0].pos.x).toBe(10);
|
|
117
|
+
expect(obstacles[0].pos.y).toBe(10);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("should handle multiple obstacles", () => {
|
|
121
|
+
navmesh.addObstacle({ type: "circle", pos: { x: 2, y: 2 }, radius: 1 });
|
|
122
|
+
navmesh.addObstacle({ type: "circle", pos: { x: 4, y: 2 }, radius: 1 });
|
|
123
|
+
navmesh.addObstacle({ type: "circle", pos: { x: 6, y: 2 }, radius: 1 });
|
|
124
|
+
|
|
125
|
+
expect(navmesh.getObstacles().length).toBe(3);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("should rebuild navigation when obstacles change", () => {
|
|
129
|
+
const id = navmesh.addObstacle({
|
|
130
|
+
type: "circle",
|
|
131
|
+
pos: { x: 2, y: 0 },
|
|
132
|
+
radius: 1,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const path1 = navmesh.findPath({
|
|
136
|
+
from: { x: 0, y: 0 },
|
|
137
|
+
to: { x: 4, y: 0 },
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
navmesh.removeObstacle(id);
|
|
141
|
+
|
|
142
|
+
const path2 = navmesh.findPath({
|
|
143
|
+
from: { x: 0, y: 0 },
|
|
144
|
+
to: { x: 4, y: 0 },
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Path should be shorter after removing obstacle
|
|
148
|
+
expect(path2.length).toBeLessThanOrEqual(path1.length);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("should handle rotated rectangle obstacle", () => {
|
|
152
|
+
const id = navmesh.addObstacle({
|
|
153
|
+
type: "rect",
|
|
154
|
+
pos: { x: 5, y: 5 },
|
|
155
|
+
size: { x: 3, y: 1 },
|
|
156
|
+
rotation: Math.PI / 4, // 45 degrees
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
expect(navmesh.getObstacles().length).toBe(1);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("should handle rotated polygon obstacle", () => {
|
|
163
|
+
const id = navmesh.addObstacle({
|
|
164
|
+
type: "polygon",
|
|
165
|
+
points: [
|
|
166
|
+
{ x: -1, y: -1 },
|
|
167
|
+
{ x: 1, y: -1 },
|
|
168
|
+
{ x: 1, y: 1 },
|
|
169
|
+
{ x: -1, y: 1 },
|
|
170
|
+
],
|
|
171
|
+
pos: { x: 5, y: 5 },
|
|
172
|
+
rotation: Math.PI / 6,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
expect(navmesh.getObstacles().length).toBe(1);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("should handle non-solid obstacles", () => {
|
|
179
|
+
navmesh.addObstacle({
|
|
180
|
+
type: "circle",
|
|
181
|
+
pos: { x: 2, y: 0 },
|
|
182
|
+
radius: 1,
|
|
183
|
+
solid: false,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const path = navmesh.findPath({ from: { x: 0, y: 0 }, to: { x: 4, y: 0 } });
|
|
187
|
+
// Path should go through non-solid obstacle
|
|
188
|
+
expect(path.length).toBeGreaterThan(0);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("should find optimal path", () => {
|
|
192
|
+
const path = navmesh.findPath({ from: { x: 0, y: 0 }, to: { x: 0, y: 5 } });
|
|
193
|
+
// Direct path should be approximately 6 steps (0 to 5)
|
|
194
|
+
expect(path.length).toBeLessThanOrEqual(10);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe("NavMesh - Graph Navigation", () => {
|
|
199
|
+
let navmesh: NavMesh;
|
|
200
|
+
|
|
201
|
+
beforeEach(() => {
|
|
202
|
+
navmesh = new NavMesh("graph");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("should initialize with graph type", () => {
|
|
206
|
+
expect(navmesh).toBeDefined();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("should find direct path with no obstacles", () => {
|
|
210
|
+
const path = navmesh.findPath({ from: { x: 0, y: 0 }, to: { x: 10, y: 10 } });
|
|
211
|
+
expect(path.length).toBeGreaterThan(0);
|
|
212
|
+
expect(path[0].x).toBeCloseTo(0, 1);
|
|
213
|
+
expect(path[0].y).toBeCloseTo(0, 1);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("should add obstacles", () => {
|
|
217
|
+
const id = navmesh.addObstacle({
|
|
218
|
+
type: "circle",
|
|
219
|
+
pos: { x: 5, y: 5 },
|
|
220
|
+
radius: 2,
|
|
221
|
+
});
|
|
222
|
+
expect(id).toBeGreaterThan(0);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("should find path around obstacles", () => {
|
|
226
|
+
navmesh.addObstacle({
|
|
227
|
+
type: "circle",
|
|
228
|
+
pos: { x: 5, y: 5 },
|
|
229
|
+
radius: 2,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const path = navmesh.findPath({ from: { x: 0, y: 5 }, to: { x: 10, y: 5 } });
|
|
233
|
+
expect(path.length).toBeGreaterThan(0);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe("NavMesh - Edge Cases", () => {
|
|
238
|
+
test("should handle same start and end position", () => {
|
|
239
|
+
const navmesh = new NavMesh("grid");
|
|
240
|
+
const path = navmesh.findPath({ from: { x: 5, y: 5 }, to: { x: 5, y: 5 } });
|
|
241
|
+
expect(path.length).toBeGreaterThan(0);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("should handle negative coordinates", () => {
|
|
245
|
+
const navmesh = new NavMesh("grid");
|
|
246
|
+
const path = navmesh.findPath({
|
|
247
|
+
from: { x: -5, y: -5 },
|
|
248
|
+
to: { x: 5, y: 5 },
|
|
249
|
+
});
|
|
250
|
+
expect(path.length).toBeGreaterThan(0);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("should handle large distances", () => {
|
|
254
|
+
const navmesh = new NavMesh("grid");
|
|
255
|
+
const path = navmesh.findPath({
|
|
256
|
+
from: { x: 0, y: 0 },
|
|
257
|
+
to: { x: 100, y: 100 },
|
|
258
|
+
});
|
|
259
|
+
expect(path.length).toBeGreaterThan(0);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("should handle very small obstacles", () => {
|
|
263
|
+
const navmesh = new NavMesh("grid");
|
|
264
|
+
navmesh.addObstacle({
|
|
265
|
+
type: "circle",
|
|
266
|
+
pos: { x: 5, y: 5 },
|
|
267
|
+
radius: 0.1,
|
|
268
|
+
});
|
|
269
|
+
const path = navmesh.findPath({ from: { x: 0, y: 0 }, to: { x: 10, y: 10 } });
|
|
270
|
+
expect(path.length).toBeGreaterThan(0);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("should handle overlapping obstacles", () => {
|
|
274
|
+
const navmesh = new NavMesh("grid");
|
|
275
|
+
navmesh.addObstacle({ type: "circle", pos: { x: 5, y: 5 }, radius: 2 });
|
|
276
|
+
navmesh.addObstacle({ type: "circle", pos: { x: 5, y: 5 }, radius: 3 });
|
|
277
|
+
const path = navmesh.findPath({ from: { x: 0, y: 0 }, to: { x: 10, y: 10 } });
|
|
278
|
+
expect(path.length).toBeGreaterThanOrEqual(0);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
test("should generate unique obstacle IDs", () => {
|
|
282
|
+
const navmesh = new NavMesh("grid");
|
|
283
|
+
const id1 = navmesh.addObstacle({
|
|
284
|
+
type: "circle",
|
|
285
|
+
pos: { x: 1, y: 1 },
|
|
286
|
+
radius: 1,
|
|
287
|
+
});
|
|
288
|
+
const id2 = navmesh.addObstacle({
|
|
289
|
+
type: "circle",
|
|
290
|
+
pos: { x: 2, y: 2 },
|
|
291
|
+
radius: 1,
|
|
292
|
+
});
|
|
293
|
+
const id3 = navmesh.addObstacle({
|
|
294
|
+
type: "circle",
|
|
295
|
+
pos: { x: 3, y: 3 },
|
|
296
|
+
radius: 1,
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
expect(id1).not.toBe(id2);
|
|
300
|
+
expect(id2).not.toBe(id3);
|
|
301
|
+
expect(id1).not.toBe(id3);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("should handle complex polygon shapes", () => {
|
|
305
|
+
const navmesh = new NavMesh("grid");
|
|
306
|
+
navmesh.addObstacle({
|
|
307
|
+
type: "polygon",
|
|
308
|
+
points: [
|
|
309
|
+
{ x: -2, y: -1 },
|
|
310
|
+
{ x: 0, y: -2 },
|
|
311
|
+
{ x: 2, y: -1 },
|
|
312
|
+
{ x: 2, y: 1 },
|
|
313
|
+
{ x: 0, y: 2 },
|
|
314
|
+
{ x: -2, y: 1 },
|
|
315
|
+
],
|
|
316
|
+
pos: { x: 10, y: 10 },
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const path = navmesh.findPath({ from: { x: 0, y: 10 }, to: { x: 20, y: 10 } });
|
|
320
|
+
expect(path.length).toBeGreaterThanOrEqual(0);
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
describe("NavMesh - Performance", () => {
|
|
325
|
+
test("should handle many obstacles efficiently", () => {
|
|
326
|
+
const navmesh = new NavMesh("grid");
|
|
327
|
+
|
|
328
|
+
// Add 100 random obstacles
|
|
329
|
+
for (let i = 0; i < 100; i++) {
|
|
330
|
+
navmesh.addObstacle({
|
|
331
|
+
type: "circle",
|
|
332
|
+
pos: { x: Math.random() * 50, y: Math.random() * 50 },
|
|
333
|
+
radius: 0.5,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const start = performance.now();
|
|
338
|
+
const path = navmesh.findPath({ from: { x: 0, y: 0 }, to: { x: 50, y: 50 } });
|
|
339
|
+
const duration = performance.now() - start;
|
|
340
|
+
|
|
341
|
+
expect(duration).toBeLessThan(1000); // Should complete in under 1 second
|
|
342
|
+
expect(path).toBeDefined();
|
|
343
|
+
});
|
|
344
|
+
});
|