From 1e0181dde2d8c64137436c5f20f5cd44fc411319 Mon Sep 17 00:00:00 2001 From: Corban Riley Date: Fri, 29 Mar 2024 12:46:01 -0400 Subject: [PATCH] Clean up EntityPool and tests --- src/EntityPool.ts | 63 ++++++++++++++++++++++++++-------------- tests/EntityPool.test.ts | 52 ++++++++++++++++++++++----------- 2 files changed, 77 insertions(+), 38 deletions(-) diff --git a/src/EntityPool.ts b/src/EntityPool.ts index bb3595b..5742711 100644 --- a/src/EntityPool.ts +++ b/src/EntityPool.ts @@ -1,46 +1,67 @@ import { ComponentOf, ComponentTypes } from './Component' import { Entity } from './Entity' +/** + * Manages a pool of reusable Entities. + * Once an Entity is released, it can be renewed with new components + */ export class EntityPool { - head: number = -1 - entities: Entity[] + readonly entities: Entity[] - constructor(public size: number) { + /** Size of the pool */ + readonly size: number = 0 + + /** Cursor to the next available entity in the pool */ + private head: number = -1 + + constructor(size: number) { + this.size = size this.entities = new Array(size) + // Fill the pool with new entities for (let idx = 0; idx < size; idx++) { this.entities[++this.head] = new Entity() } } + /** + * The count of used entities in the pool + * */ get length(): number { return this.size - this.head - 1 } - // Take an Entity from the pool - renew(components: ComponentOf[] = []): Entity { - if (this.head >= 0) { - const entity = this.entities[this.head--] - return entity.renew(components) - } else { + /** + * Whether the pool is full or not + * */ + get isExhausted(): boolean { + return this.head === -1 + } + + /** + * Get an Entity from the pool + * */ + getEntity(components: ComponentOf[] = []): Entity { + if (this.isExhausted) { throw new Error( 'EntityPool: Attempted to take an Entity from an exhausted pool.' ) } + + const entity = this.entities[this.head--] + return entity.renew(components) } - // Release an Entity back into the pool - release(entity: Entity) { - if (entity instanceof Entity) { - if (this.head < this.size - 1) { - this.entities[++this.head] = entity - } else { - throw new Error( - 'EntityPool: Attempted to release an Entity back into a full pool.' - ) - } - } else { - throw new Error('EntityPool: Released object was not an Entity.') + /** + * Release an Entity back into the pool + * */ + releaseEntity(entity: Entity) { + if (this.head === this.size - 1) { + throw new Error( + 'EntityPool: Attempted to release an Entity back into a full pool.' + ) } + + this.entities[++this.head] = entity } } diff --git a/tests/EntityPool.test.ts b/tests/EntityPool.test.ts index e6ea27b..9b58477 100644 --- a/tests/EntityPool.test.ts +++ b/tests/EntityPool.test.ts @@ -5,7 +5,7 @@ import { EntityPool } from '../src/EntityPool' const SIZE = 3 -describe('EntityPool', () => { +describe.only('EntityPool', () => { test('can create pool', () => { const pool = new EntityPool(SIZE) @@ -16,47 +16,65 @@ describe('EntityPool', () => { expect(pool.entities[SIZE - 1]).toBeInstanceOf(Entity) }) - test('can renew an entity', () => { + test('can get an entity', () => { const pool = new EntityPool(SIZE) expect(pool.length).toBe(0) - const entity = pool.renew() + const entity = pool.getEntity() expect(pool.length).toBe(1) expect(entity).toBeInstanceOf(Entity) }) - test('can release an entity back into the pool', () => { + test.only('can release an entity back into the pool', () => { const pool = new EntityPool(SIZE) - expect(pool.length).toBe(0) + expect(pool.length, 'starting pool size to be empty').toBe(0) - const entity = pool.renew() - const id = entity.id + // Get an entity from the pool + const entity1 = pool.getEntity() - expect(pool.length).toBe(1) - expect(entity).toBeInstanceOf(Entity) + expect(pool.length, 'pool size to increase after getting an entity').toBe(1) + expect(entity1).toBeInstanceOf(Entity) - pool.release(entity) + // Release the entity back into the pool + pool.releaseEntity(entity1) - expect(entity.id).toBe(id + 1) - expect(pool.length).toBe(0) + expect( + pool.length, + 'the pool length to be zero after releasing entity' + ).toBe(0) - const another = pool.renew() + // Get another entity from the pool + const entity2 = pool.getEntity() + expect( + entity2, + 'the next entity to share the same reference as the released entity' + ).toBe(entity1) - expect(another.id).toBe(id + 1) + // Get another entity from the pool + const entity3 = pool.getEntity() + expect(entity3, 'the next entity to be a different entity').not.toBe( + entity1 + ) }) test('exhausting the pool throws an error', () => { const pool = new EntityPool(SIZE) + expect(pool.length).toBe(0) + // Take all entities from pool for (let i = 0; i < SIZE; i++) { - pool.renew() + pool.getEntity() } - // Take one more - expect(() => pool.renew()).toThrow() + expect(pool.length).toBe(SIZE) + + expect(pool.isExhausted).toBe(true) + + // // Take one more + // expect(() => pool.getEntity()).toThrow() }) })