Skip to content

Commit

Permalink
Merge pull request #110 from regal/feat-104/prototype-registry
Browse files Browse the repository at this point in the history
Prototype Registry: Replace numeric OutputLine.id and EventRecord.id with PKs
  • Loading branch information
jcowman2 committed Jul 22, 2019
2 parents 0e3a42a + 2000ece commit fc2b122
Show file tree
Hide file tree
Showing 28 changed files with 755 additions and 627 deletions.
4 changes: 2 additions & 2 deletions src/agents/agent-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Licensed under MIT License (see https://github.com/regal/regal)
*/

import { PK } from "../common";
import { FK } from "../common";
import { GameInstance } from "../state";
import { Agent } from "./agent";
import { PropertyChange } from "./agent-properties";
Expand All @@ -19,7 +19,7 @@ import { PropertyChange } from "./agent-properties";
*/
export interface AgentManager {
/** The managed agent's id. */
id: PK<Agent>;
id: FK<Agent>;

/** The `GameInstance` in which the agent is active. */
game: GameInstance;
Expand Down
8 changes: 4 additions & 4 deletions src/agents/agent-properties.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PK } from "../common";
import { FK } from "../common";
import { Agent } from "./agent";

/*
Expand All @@ -24,14 +24,14 @@ export enum PropertyOperation {
* Describes a single operation on an active agent's property.
*/
export interface PropertyChange {
/** The numeric id of the `TrackedEvent` during which the change took place (optional). */
eventId?: number;
/** The id of the `TrackedEvent` during which the change took place (optional). */
eventId?: FK<Agent>;

/** The name of the `TrackedEvent` during which the change took place (optional). */
eventName?: string;

/** The id of the registered `Agent` (optional). */
agentId?: PK<Agent>;
agentId?: FK<Agent>;

/** The operation performed on the agent's property. */
op: PropertyOperation;
Expand Down
5 changes: 4 additions & 1 deletion src/agents/impl/agent-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { buildPKProvider } from "../../common";
import { Agent } from "../agent";

/** The set of reserved `Agent` primary keys. */
export const AGENT_RESERVED_KEYS = {
Expand All @@ -21,4 +22,6 @@ export const AGENT_RESERVED_KEYS = {
};

/** The `Agent` `PKProvider` for static agents. */
export const STATIC_AGENT_PK_PROVIDER = buildPKProvider(AGENT_RESERVED_KEYS);
export const STATIC_AGENT_PK_PROVIDER = buildPKProvider<Agent>(
AGENT_RESERVED_KEYS
);
19 changes: 10 additions & 9 deletions src/agents/impl/agent-manager-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
* Licensed under MIT License (see https://github.com/regal/regal)
*/

import { PK } from "../../common";
import { FK, PK } from "../../common";
import { RegalError } from "../../error";
import { DEFAULT_EVENT_ID, EventRecord } from "../../events";
import { EventRecord, getUntrackedEventPK } from "../../events";
import { GameInstanceInternal } from "../../state";
import { Agent } from "../agent";
import { AgentManager } from "../agent-manager";
Expand All @@ -24,11 +24,11 @@ import { StaticAgentRegistry } from "../static-agent-registry";
export const buildAgentManager = (
id: PK<Agent>,
game: GameInstanceInternal
): AgentManager => new AgentManagerImpl(id, game);
): AgentManager => new AgentManagerImpl(id.ref(), game);

/** Implementation of `AgentManager`. */
class AgentManagerImpl implements AgentManager {
constructor(public id: PK<Agent>, public game: GameInstanceInternal) {}
constructor(public id: FK<Agent>, public game: GameInstanceInternal) {}

public hasPropertyRecord(property: PropertyKey): boolean {
if (property === "constructor") {
Expand Down Expand Up @@ -103,8 +103,8 @@ class AgentManagerImpl implements AgentManager {
const event = this.game.events.current;

const propChange: PropertyChange = {
agentId: this.id,
eventId: event.id,
agentId: this.id.ref(),
eventId: event.id.ref(),
eventName: event.name,
final: value,
init: initValue,
Expand Down Expand Up @@ -141,8 +141,8 @@ class AgentManagerImpl implements AgentManager {
const event = this.game.events.current;

const propChange: PropertyChange = {
agentId: this.id,
eventId: event.id,
agentId: this.id.ref(),
eventId: event.id.ref(),
eventName: event.name,
final: undefined,
init: initValue,
Expand Down Expand Up @@ -183,7 +183,8 @@ class AgentManagerImpl implements AgentManager {
// A change should be replaced if it happened during the same event,
// or if the change happened after any potential recycling
const shouldReplaceSingle = (pc: PropertyChange) =>
pc.eventId === event.id || pc.eventId > DEFAULT_EVENT_ID;
pc.eventId.equals(event.id) ||
pc.eventId.index() > getUntrackedEventPK().index();

// If the property history has two changes, update the more recent one.
// If property history has only change, check when the change happened.
Expand Down
4 changes: 4 additions & 0 deletions src/common/impl/pk-provider-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,8 @@ export class PKProviderImpl<PKClass> implements PKProvider<PKClass> {
tryNum >= PKProviderImpl.START_VALUE
);
}

public countGenerated(): number {
return this.lastPK.index();
}
}
5 changes: 5 additions & 0 deletions src/common/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ export interface PKProvider<T> {
* actually generated.
*/
isPossibleKeyValue(str: string): boolean;

/**
* Returns the number of keys generated by this `PKProvider`.
*/
countGenerated(): number;
}

/**
Expand Down
16 changes: 6 additions & 10 deletions src/events/event-record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,17 @@
*/

import { PropertyChange } from "../agents";
import { FK, PK } from "../common";
import { OutputLine } from "../output";
import { RandomRecord } from "../random";
import { TrackedEvent } from "./event-types";

/** Event ID for untracked `EventFunction`s. */
export const DEFAULT_EVENT_ID: number = 0;
/** Name of untracked `EventFunction`s. */
export const DEFAULT_EVENT_NAME: string = "DEFAULT";

/**
* Record of a `TrackedEvent`'s effects in a game cycle.
*/
export interface EventRecord {
/* The event's unique numeric ID. */
id: number;
/* The event's unique ID. */
id: PK<EventRecord>;

/* The event's name. */
name: string;
Expand All @@ -29,13 +25,13 @@ export interface EventRecord {
func: TrackedEvent;

/** The IDs of the `OutputLine`s emitted by the event. */
output?: number[];
output?: Array<FK<OutputLine>>;

/** The ID of the event that caused this event. */
causedBy?: number;
causedBy?: FK<EventRecord>;

/** The IDs of the events that were caused by this event. */
caused?: number[];
caused?: Array<FK<EventRecord>>;

/** The records of all changes to registered agents that were caused by this event. */
changes?: PropertyChange[];
Expand Down
2 changes: 1 addition & 1 deletion src/events/event-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export const isEventQueue = (o: any): o is EventQueue =>
* `noop` is short for *no operation*.
*
* Meant to be used in rare cases where an event cannot return `void`
* (i.e. forced by the TypeScript compiler).
* (e.g. forced by the TypeScript compiler).
*/
export const noop: TrackedEvent = (() => {
const nonEvent = (game: GameInstance) => undefined;
Expand Down
31 changes: 31 additions & 0 deletions src/events/impl/event-keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Contains the `EventRecord` primary key system.
*
* Copyright (c) Joseph R Cowman
* Licensed under MIT License (see https://github.com/regal/regal)
*/

import { buildPKProvider } from "../../common";
import { EventRecord } from "../event-record";

/** The set of reserved `EventRecord` primary keys. */
export const EVENT_RESERVED_KEYS = {
/**
* The key for all changes made in the default, untracked `EventRecord`.
*
* **No reserved event keys should have a greater value than this!**
*/
UNTRACKED: 100
};

/**
* Reference this `PKProvider` for helper functions to get reserved `EventRecord`
* PKs so we don't have to do additional instantiations.
*/
const INTERNAL_EVENT_PK_PROVIDER = buildPKProvider<EventRecord>(
EVENT_RESERVED_KEYS
);

/** Shorthand for the `EVENT_RESERVED_KEYS.UNTRACKED` primary key. */
export const getUntrackedEventPK = () =>
INTERNAL_EVENT_PK_PROVIDER.reserved(EVENT_RESERVED_KEYS.UNTRACKED);
37 changes: 22 additions & 15 deletions src/events/impl/event-record-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,44 @@
*/

import { PropertyChange } from "../../agents";
import { FK, PK } from "../../common";
import { OutputLine } from "../../output";
import { RandomRecord } from "../../random";
import {
DEFAULT_EVENT_ID,
DEFAULT_EVENT_NAME,
EventRecord
} from "../event-record";
import { EventRecord } from "../event-record";
import { noop, TrackedEvent } from "../event-types";
import { getUntrackedEventPK } from "./event-keys";

/** Name of untracked `EventFunction`s. */
export const DEFAULT_EVENT_NAME: string = "DEFAULT";

/**
* Builds a new `EventRecord`.
*
* @param id The event's unique numeric ID (optional).
* @param id The event's unique ID (optional).
* @param name The event's name (optional).
* @param func The event's `TrackedEvent`. Defaults to `noop`.
*/
export const buildEventRecord = (
id: number = DEFAULT_EVENT_ID,
id: PK<EventRecord> = undefined,
name: string = DEFAULT_EVENT_NAME,
func: TrackedEvent = noop
): EventRecord => new EventRecordImpl(id, name, func);
): EventRecord => {
if (id === undefined) {
id = getUntrackedEventPK();
}

return new EventRecordImpl(id, name, func);
};

class EventRecordImpl implements EventRecord {
public output?: number[];
public causedBy?: number;
public caused?: number[];
public output?: Array<FK<OutputLine>>;
public causedBy?: FK<EventRecord>;
public caused?: Array<FK<EventRecord>>;
public changes?: PropertyChange[];
public randoms?: RandomRecord[];

constructor(
public id: number,
public id: PK<EventRecord>,
public name: string,
public func: TrackedEvent
) {}
Expand All @@ -45,15 +52,15 @@ class EventRecordImpl implements EventRecord {
if (this.output === undefined) {
this.output = [];
}
this.output.push(line.id);
this.output.push(line.id.ref());
}

public trackCausedEvent(...events: EventRecord[]): void {
if (this.caused === undefined) {
this.caused = [];
}
this.caused.push(...events.map(e => e.id));
events.forEach(e => (e.causedBy = this.id));
this.caused.push(...events.map(e => e.id.ref()));
events.forEach(e => (e.causedBy = this.id.ref()));
}

public trackChange(propChange: PropertyChange): void {
Expand Down
1 change: 1 addition & 0 deletions src/events/impl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export { buildInstanceEvents } from "./instance-events-impl";
export { buildEventQueue, buildThenMethod } from "./event-queue-impl";
export { buildEventRecord } from "./event-record-impl";
export { enqueue, nq, on } from "./event-builders";
export { getUntrackedEventPK } from "./event-keys";
33 changes: 15 additions & 18 deletions src/events/impl/instance-events-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,45 @@
* Licensed under MIT License (see https://github.com/regal/regal)
*/

import { buildPKProvider, PKProvider } from "../../common";
import { GameInstanceInternal } from "../../state";
import { DEFAULT_EVENT_ID, EventRecord } from "../event-record";
import { EventRecord } from "../event-record";
import {
isEventQueue,
isTrackedEvent,
noop,
TrackedEvent
} from "../event-types";
import { InstanceEventsInternal } from "../instance-events-internal";
import { EVENT_RESERVED_KEYS } from "./event-keys";
import { buildEventRecord } from "./event-record-impl";

/**
* Builds an `InstanceEventsInternal`.
* @param game The game instance that owns this `InstanceEventsInternal`.
* @param startingEventId Optional starting ID for new `EventRecord`s.
* @param pkProvider The existing event PK provider (optional).
*/
export const buildInstanceEvents = (
game: GameInstanceInternal,
startingEventId?: number
): InstanceEventsInternal =>
startingEventId !== undefined
? new InstanceEventsImpl(game, startingEventId)
: new InstanceEventsImpl(game);
pkProvider?: PKProvider<EventRecord>
): InstanceEventsInternal => new InstanceEventsImpl(game, pkProvider);

class InstanceEventsImpl implements InstanceEventsInternal {
public history: EventRecord[] = [];

/** Internal member for the ID of the most recently generated `EventRecord`. */
private _lastEventId: number;

/** Internal queue of events that have yet to be executed. */
private _queue: EventRecord[] = [];

/** The internal `EventRecord` `PKProvider`. */
private _pkProvider: PKProvider<EventRecord>;

constructor(
public game: GameInstanceInternal,
startingEventId = DEFAULT_EVENT_ID
pkProvider: PKProvider<EventRecord>
) {
this._lastEventId = startingEventId;
this._pkProvider = pkProvider
? pkProvider
: buildPKProvider(EVENT_RESERVED_KEYS);
}

get current(): EventRecord {
Expand All @@ -55,17 +56,13 @@ class InstanceEventsImpl implements InstanceEventsInternal {
return event;
}

get lastEventId() {
return this._lastEventId;
}

public invoke(event: TrackedEvent): void {
this._addEvent(event);
this._executeCurrent();
}

public recycle(newInstance: GameInstanceInternal): InstanceEventsInternal {
return new InstanceEventsImpl(newInstance, this.lastEventId);
return new InstanceEventsImpl(newInstance, this._pkProvider);
}

/**
Expand All @@ -89,7 +86,7 @@ class InstanceEventsImpl implements InstanceEventsInternal {
}

const mapToRecord = (evObj: TrackedEvent) =>
buildEventRecord(++this._lastEventId, evObj.eventName, evObj);
buildEventRecord(this._pkProvider.next(), evObj.eventName, evObj);

const immediateEventRecords = immediateEvents.map(mapToRecord);
const delayedEventRecords = delayedEvents.map(mapToRecord);
Expand Down

0 comments on commit fc2b122

Please sign in to comment.