wip
This commit is contained in:
@@ -1,10 +1,8 @@
|
||||
import type { RNG } from "../../../server/util/RNG.js";
|
||||
|
||||
import { sha512 } from '../../../server/util/sha2.js';
|
||||
|
||||
import { convertBytesLikeToUint8Array } from "@sammo/util/convertBytesLikeToUint8Array.js";
|
||||
import type { BytesLike } from "../../../server/util/BytesLike.js";
|
||||
import { delay } from "./delay.js";
|
||||
import type { RNG } from "./RNG.js";
|
||||
import { sha512 } from './SHA2.js';
|
||||
import { convertBytesLikeToUint8Array } from "@sammo/util/converter";
|
||||
import type { BytesLike } from "@sammo/util";
|
||||
import { delay } from "@sammo/util";
|
||||
|
||||
const maxRngSupportBit = 53;
|
||||
const maxInt = 0x1f_ffff_ffff_ffff; // NOTE: b 0, 10000110011, 11...11
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
export interface RNG {
|
||||
|
||||
/**
|
||||
* nextInt()가 반환 가능한 최댓값
|
||||
*/
|
||||
getMaxInt(): number;
|
||||
|
||||
nextBytes(bytes: number): Promise<Uint8Array>;
|
||||
nextBits(bits: number): Promise<Uint8Array>;
|
||||
|
||||
nextInt(max?: number): Promise<number>;
|
||||
nextFloat1(): Promise<number>;
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
import type { RNG } from './RNG.js';
|
||||
|
||||
export class RandUtil {
|
||||
constructor(protected rng: RNG) {
|
||||
|
||||
}
|
||||
|
||||
public nextFloat1(): Promise<number> {
|
||||
return this.rng.nextFloat1();
|
||||
}
|
||||
|
||||
public async nextRange(min: number, max: number): Promise<number> {
|
||||
const range = max - min;
|
||||
return await this.nextFloat1() * (range) + min;
|
||||
}
|
||||
|
||||
public async nextRangeInt(min: number, max: number): Promise<number> {
|
||||
const range = max - min;
|
||||
return await this.rng.nextInt(range) + min;
|
||||
}
|
||||
|
||||
public nextInt(max?: number): Promise<number> {
|
||||
return this.rng.nextInt(max);
|
||||
}
|
||||
|
||||
public async nextBit(): Promise<boolean> {
|
||||
const view = new DataView(await this.rng.nextBits(1) as ArrayBufferLike);
|
||||
return view.getUint8(0) != 0;
|
||||
}
|
||||
|
||||
public async nextBool(prob = 0.5): Promise<boolean> {
|
||||
if (prob >= 1) {
|
||||
return true;
|
||||
}
|
||||
if (prob === 0.5){
|
||||
return this.nextBit();
|
||||
}
|
||||
if (prob <= 0){
|
||||
return false;
|
||||
}
|
||||
return await this.nextFloat1() < prob;
|
||||
}
|
||||
|
||||
public async shuffle<T>(srcArray: T[]): Promise<T[]> {
|
||||
const cnt = srcArray.length;
|
||||
if(cnt === 0){
|
||||
return [];
|
||||
}
|
||||
if (cnt > this.rng.getMaxInt()) {
|
||||
throw 'Invalid random int range';
|
||||
}
|
||||
|
||||
const result: T[] = Array.from(srcArray);
|
||||
for (let srcIdx = 0; srcIdx < cnt; srcIdx += 1) {
|
||||
const destIdx = await this.rng.nextInt(cnt - srcIdx - 1) + srcIdx;
|
||||
if(srcIdx === destIdx){
|
||||
continue;
|
||||
}
|
||||
[result[srcIdx], result[destIdx]] = [result[destIdx], result[srcIdx]];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//Object는 integer key에 예외가 있어 shuffleAssoc은 없음
|
||||
|
||||
public async choice<T>(items: T[] | Record<string | number, T> | Set<T>): Promise<T> {
|
||||
if (items instanceof Array) {
|
||||
if(items.length === 0){
|
||||
throw new Error('Empty items');
|
||||
}
|
||||
const idx = await this.rng.nextInt(items.length - 1);
|
||||
return items[idx];
|
||||
}
|
||||
|
||||
if (items instanceof Set) {
|
||||
return this.choice(Array.from(items.values()));
|
||||
}
|
||||
|
||||
return items[await this.choice(Array.from(Object.keys(items)))];
|
||||
}
|
||||
|
||||
public async choiceUsingWeight(items: Record<string | number, number>): Promise<string | number> {
|
||||
if(Object.keys(items).length === 0){
|
||||
throw new Error('Empty items');
|
||||
}
|
||||
let sum = 0;
|
||||
for (const value of Object.values(items)) {
|
||||
if (value <= 0) {
|
||||
continue;
|
||||
}
|
||||
sum += value;
|
||||
}
|
||||
|
||||
let rd = await this.nextFloat1() * sum;
|
||||
|
||||
for (const [item, value] of Object.entries(items)) {
|
||||
if (value <= 0) {
|
||||
if (rd <= 0) {
|
||||
return item;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rd <= value) {
|
||||
return item;
|
||||
}
|
||||
rd -= value;
|
||||
}
|
||||
|
||||
throw new Error('Unreacheable');
|
||||
}
|
||||
|
||||
public async choiceUsingWeightPair<T>(items: [T, number][]): Promise<T> {
|
||||
if(items.length === 0){
|
||||
throw new Error('Empty items');
|
||||
}
|
||||
let sum = 0;
|
||||
for (const [, value] of items) {
|
||||
if (value <= 0) {
|
||||
continue;
|
||||
}
|
||||
sum += value;
|
||||
}
|
||||
|
||||
let rd = await this.nextFloat1() * sum;
|
||||
|
||||
for (const [item, value] of items) {
|
||||
if (value <= 0) {
|
||||
if (rd <= 0) {
|
||||
return item;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rd <= value) {
|
||||
return item;
|
||||
}
|
||||
rd -= value;
|
||||
}
|
||||
|
||||
throw new Error('Unreacheable');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user