Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import type { OnInit } from '@angular/core';
import { Component, EventEmitter, Output } from '@angular/core';
import { createSelector, Store } from '@ngrx/store';
import type {
import {
UUID,
Vehicle,
Personnel,
Material,
Patient,
WithPosition,
UnloadArrivingVehiclesBehaviorState,
} from 'digital-fuesim-manv-shared';
import {
SimulatedRegion,
UnloadArrivedVehiclesBehaviorState,
} from 'digital-fuesim-manv-shared';
import type { Observable } from 'rxjs';
import { ExerciseService } from 'src/app/core/exercise.service';
Expand Down Expand Up @@ -93,7 +93,7 @@ export class SimulatedRegionPopupComponent implements OnInit {
this.exerciseService.proposeAction({
type: '[SimulatedRegion] Add Behavior',
simulatedRegionId: this.simulatedRegionId,
behaviorState: UnloadArrivedVehiclesBehaviorState.create(5000),
behaviorState: UnloadArrivingVehiclesBehaviorState.create(5000),
});
}
}
18 changes: 16 additions & 2 deletions shared/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"dependencies": {
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"hash.js": "^1.1.7",
"immer": "^9.0.17",
"lodash-es": "^4.17.21",
"rbush": "^3.0.1",
Expand Down
4 changes: 2 additions & 2 deletions shared/src/simulation/activities/unload-vehicle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import type {
} from './simulation-activity';

export class UnloadVehicleActivityState implements SimulationActivityState {
@IsValue('unloadVehiclesActivity' as const)
public readonly type = 'unloadVehiclesActivity';
@IsValue('unloadVehicleActivity' as const)
public readonly type = 'unloadVehicleActivity';

@IsUUID(4, uuidValidationOptions)
public readonly id: UUID = uuid();
Expand Down
10 changes: 5 additions & 5 deletions shared/src/simulation/behaviors/unload-arrived-vehicles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import type {
SimulationBehaviorState,
} from './simulation-behavior';

export class UnloadArrivedVehiclesBehaviorState
export class UnloadArrivingVehiclesBehaviorState
implements SimulationBehaviorState
{
@IsValue('unloadVehiclesBehavior' as const)
readonly type = 'unloadVehiclesBehavior';
@IsValue('unloadArrivingVehiclesBehavior' as const)
readonly type = 'unloadArrivingVehiclesBehavior';

@IsUUID(4, uuidValidationOptions)
public readonly id: UUID = uuid();
Expand All @@ -33,9 +33,9 @@ export class UnloadArrivedVehiclesBehaviorState
static readonly create = getCreate(this);
}

export const unloadArrivingVehiclesBehavior: SimulationBehavior<UnloadArrivedVehiclesBehaviorState> =
export const unloadArrivingVehiclesBehavior: SimulationBehavior<UnloadArrivingVehiclesBehaviorState> =
{
behaviorState: UnloadArrivedVehiclesBehaviorState,
behaviorState: UnloadArrivingVehiclesBehaviorState,
handleEvent(draftState, simulatedRegion, behaviorState, event) {
if (event.type === 'vehicleArrivedEvent') {
addActivity(
Expand Down
78 changes: 26 additions & 52 deletions shared/src/simulation/utils/randomness.ts
Original file line number Diff line number Diff line change
@@ -1,93 +1,67 @@
/* eslint-disable no-bitwise */
import {
ArrayMaxSize,
ArrayMinSize,
IsArray,
IsInt,
Max,
Min,
ValidateIf,
} from 'class-validator';
import { IsInt, Min, ValidateIf } from 'class-validator';
import hash from 'hash.js';
import { v4 } from 'uuid';
import { getCreate } from '../../models/utils';
import type { ExerciseState } from '../../state';
import type { Mutable, UUID } from '../../utils';
import { IsLiteralUnion, IsValue } from '../../utils/validators';

// 4 unsigned 32-bit integers
type Sfc32state = readonly [number, number, number, number];

export class RandomState {
@IsValue('randomState' as const)
readonly type = 'randomState';

@IsLiteralUnion({ sfc32: true })
readonly algo = 'sfc32';

@ValidateIf((o) => o.algo === 'sfc32')
@IsArray()
@ArrayMinSize(4)
@ArrayMaxSize(4)
@IsInt({ each: true })
@Min(0, { each: true })
@Max(4294967295 /* UINT32_MAX */, { each: true })
readonly sfc32state!: Sfc32state;
@IsLiteralUnion({ 'sha256-id-ctr': true })
readonly algo = 'sha256-id-ctr';

/**
* @deprecated Use {@link create} instead
*/
constructor(state: Sfc32state) {
this.sfc32state = state;
}
@ValidateIf((o) => o.algo === 'sha256-id-ctr')
@IsInt()
@Min(0)
readonly counter: number = 0;

static readonly create = getCreate(this);
}

export function seededRandomState() {
return RandomState.create(
Array.from({ length: 4 }).map(() =>
Math.trunc(Math.random() * 4294967295)
) as unknown as Sfc32state
);
return RandomState.create();
}

export function nextBool(
draftState: Mutable<ExerciseState>,
probability: number = 0.5
): boolean {
return nextInt(draftState, 4294967296) / 4294967296 < probability;
return nextInt(draftState, 4294967295) / 4294967296 < probability;
}

export function nextUUID(draftState: Mutable<ExerciseState>): UUID {
return v4({
random: Array.from({ length: 16 }).map(() => nextInt(draftState, 256)),
random: advance(draftState),
});
}

// Returns 0 to UINT32_MAX
Comment thread
lukasrad02 marked this conversation as resolved.
Outdated
export function nextInt(
draftState: Mutable<ExerciseState>,
upperBound: number
): number {
return Math.trunc(advance(draftState) % upperBound);
const state = advance(draftState)
.slice(4)
Comment thread
lukasrad02 marked this conversation as resolved.
Outdated
.map((b, i) => Math.trunc(b * 256 ** i))
.reduce((a, b) => a | b);
return Math.trunc(state % upperBound);
}

function advance(draftState: Mutable<ExerciseState>): number {
// Returns 32 numbers from 0-255
Comment thread
lukasrad02 marked this conversation as resolved.
Outdated
function advance(draftState: Mutable<ExerciseState>): number[] {
const state = draftState.randomState;
let [a, b, c, d] = state.sfc32state;

// Adapted from https://github.com/bryc/code/blob/master/jshash/PRNGs.md#sfc32
a = Math.trunc(a);
b = Math.trunc(b);
c = Math.trunc(c);
d = Math.trunc(d);
const t = Math.trunc(Math.trunc(a + b) + d);
d = Math.trunc(d + 1);
a = b ^ (b >>> 9);
b = Math.trunc(c + (c << 3));
c = (c << 21) | (c >>> 11);
c = Math.trunc(c + t);
const result = t >>> 0;
state.sfc32state = [a, b, c, d];
const result = hash
.sha256()
.update(draftState.id)
.update(state.counter.toString())
.digest()
.map((b) => b & 0xff);

state.counter++;
return result;
}
20 changes: 19 additions & 1 deletion shared/src/state-migrations/20-add-simulation-properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,25 @@ import type { UUID } from '../utils';
import type { Migration } from './migration-functions';

export const addSimulationProperties20: Migration = {
actions: null,
actions: (_initialState, actions) => {
actions.forEach((action) => {
if (
(action as { type: string } | null)?.type ===
'[SimulatedRegion] Add simulated region'
) {
const typedAction = action as {
simulatedRegion: {
inEvents: any[];
behaviors: any[];
activities: object;
};
};
typedAction.simulatedRegion.inEvents = [];
typedAction.simulatedRegion.behaviors = [];
typedAction.simulatedRegion.activities = {};
}
});
},
state: (state: any) => {
state.randomState = seededRandomState();

Expand Down