@zoodogood/utils 2.0.2-change.1508 → 3.0.0-change.2568
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/lib/discordjs/helpers.d.ts +2 -2
- package/lib/discordjs/helpers.js +29 -36
- package/lib/nodejs/getChildProcessUtils.d.ts +0 -1
- package/lib/objectives/BusyNumeric.d.ts +29 -0
- package/lib/objectives/BusyNumeric.js +167 -0
- package/lib/objectives/CustomCollector.d.ts +0 -1
- package/lib/objectives/getRandomElementFromArray.d.ts +24 -4
- package/lib/objectives/getRandomElementFromArray.js +92 -13
- package/lib/primitives/BracketsParser.js +2 -2
- package/lib/primitives/TextTableBuilder.js +1 -1
- package/lib/primitives/binary_search.d.ts +1 -0
- package/lib/primitives/binary_search.js +19 -0
- package/lib/primitives/createDefaultPreventable.d.ts +4 -0
- package/lib/primitives/createDefaultPreventable.js +8 -0
- package/lib/primitives/normalize.d.ts +1 -0
- package/lib/primitives/normalize.js +5 -0
- package/package.json +16 -16
|
@@ -6,7 +6,7 @@ export type APIBaseButton = import("discord-api-types/v10").APIButtonComponentBa
|
|
|
6
6
|
*
|
|
7
7
|
* @param {Partial<APIBaseButton>[]} resolable
|
|
8
8
|
*/
|
|
9
|
-
export function justButtonComponents(...resolable: Partial<
|
|
9
|
+
export function justButtonComponents(...resolable: Partial<APIBaseButton>[]): {
|
|
10
10
|
label?: string | undefined;
|
|
11
11
|
style: any;
|
|
12
12
|
emoji?: import("discord-api-types/v10").APIMessageComponentEmoji | undefined;
|
|
@@ -15,4 +15,4 @@ export function justButtonComponents(...resolable: Partial<import("discord-api-t
|
|
|
15
15
|
customId: string;
|
|
16
16
|
}[];
|
|
17
17
|
export function justSendMessage(target: any, options: any): Promise<any>;
|
|
18
|
-
import { ComponentType
|
|
18
|
+
import { ComponentType } from "discord.js";
|
package/lib/discordjs/helpers.js
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import { BaseInteraction, ButtonStyle, ComponentType } from "discord.js";
|
|
11
2
|
import { CreateMessage } from "./simplify.js";
|
|
12
3
|
const DEFAULTS_FOR_BUTTON = {
|
|
@@ -21,34 +12,36 @@ const DEFAULTS_FOR_BUTTON = {
|
|
|
21
12
|
* @param {Partial<APIBaseButton>[]} resolable
|
|
22
13
|
*/
|
|
23
14
|
function justButtonComponents(...resolable) {
|
|
24
|
-
const buttons = resolable.map((data, i) => (
|
|
15
|
+
const buttons = resolable.map((data, i) => ({
|
|
16
|
+
...DEFAULTS_FOR_BUTTON,
|
|
17
|
+
customId: `button.${i + 1}`,
|
|
18
|
+
...data,
|
|
19
|
+
}));
|
|
25
20
|
return buttons;
|
|
26
21
|
}
|
|
27
|
-
function justSendMessage(target, options) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
?
|
|
32
|
-
? target.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return message;
|
|
52
|
-
});
|
|
22
|
+
async function justSendMessage(target, options) {
|
|
23
|
+
const messagePayload = CreateMessage(options);
|
|
24
|
+
const message = target instanceof BaseInteraction
|
|
25
|
+
? await (options.edit
|
|
26
|
+
? target.replied
|
|
27
|
+
? target.editReply(messagePayload)
|
|
28
|
+
: target.update(messagePayload)
|
|
29
|
+
: target.reply(messagePayload))
|
|
30
|
+
: await (options.edit
|
|
31
|
+
? target.edit(messagePayload)
|
|
32
|
+
: target.send(messagePayload));
|
|
33
|
+
if (options.delete) {
|
|
34
|
+
setTimeout(() => message.delete(), options.delete);
|
|
35
|
+
}
|
|
36
|
+
if (options.reactions) {
|
|
37
|
+
options.reactions
|
|
38
|
+
.filter(Boolean)
|
|
39
|
+
.filter((react) => {
|
|
40
|
+
var _a;
|
|
41
|
+
return !((_a = message.reactions) === null || _a === void 0 ? void 0 : _a.cache.some((compared) => compared.emoji.code === react));
|
|
42
|
+
})
|
|
43
|
+
.forEach((react) => message.react(react));
|
|
44
|
+
}
|
|
45
|
+
return message;
|
|
53
46
|
}
|
|
54
47
|
export { justButtonComponents, justSendMessage };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MARK: Представьте отель
|
|
3
|
+
* Часть его номеров свободны, а часть — заняты
|
|
4
|
+
* Есть две особенности.
|
|
5
|
+
* 1) При заселении ищется случайный номер
|
|
6
|
+
* 2) Хотя у нас есть список занятых номеров, у нас не может быть списка свободных (за условием задачи)
|
|
7
|
+
* Этот алгоритм используется для эффективного поиска тех самых свободных номеров, которые удовлетворяют заданному условию
|
|
8
|
+
*/
|
|
9
|
+
export declare class BusyNumeric {
|
|
10
|
+
#private;
|
|
11
|
+
readonly range: number;
|
|
12
|
+
readonly busy_areas: (readonly [number, number])[];
|
|
13
|
+
get peak_start_busy(): boolean;
|
|
14
|
+
get peak_end_busy(): boolean;
|
|
15
|
+
constructor(range: number);
|
|
16
|
+
bifurcate(point: number): void;
|
|
17
|
+
segments_count(): number;
|
|
18
|
+
segment(at: number): {
|
|
19
|
+
size: number;
|
|
20
|
+
left: readonly [number, number];
|
|
21
|
+
right: readonly [number, number];
|
|
22
|
+
} | undefined;
|
|
23
|
+
insert_area(start: number, end: number): void;
|
|
24
|
+
refresh: {
|
|
25
|
+
_areas: () => number;
|
|
26
|
+
_peak: () => void;
|
|
27
|
+
full: () => void;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// task: честный алгоритм , для которого каждый из элеметов списка имеет равный шанс на реализацию своей вероятности.
|
|
2
|
+
// Обычно весь список фильтруется и после выбирается элемент. В нашем случае фильтр будет срабатывать только для тех элементов,
|
|
3
|
+
// которые на этапе фильтрации «выбраны» — то есть имеют свою стопроцентную вероятность быть окончательными после успешного прохождения фильтра
|
|
4
|
+
// требования: честная вероятность; итоговая производительность должна быть выше обычной при проходе списка фильтром
|
|
5
|
+
// Алгоритм:
|
|
6
|
+
/*
|
|
7
|
+
Оптимальный алгоритм поиска случайного элемента списка, удовлетворяющего условию. Поддерживается возможность задавать элементам коэффициент вероятности
|
|
8
|
+
|
|
9
|
+
Перед выбором элемента выбираем случайный сегмент, далее случайный элемент в этом сегменте
|
|
10
|
+
|
|
11
|
+
Если соответствует условию, возвращаем элемент. Завершаем поиск
|
|
12
|
+
|
|
13
|
+
Иначе исключаем элемент путем раздвоения текущего сегмента так, чтобы элемент не оказался ни в одном сегменте списка
|
|
14
|
+
|
|
15
|
+
Повторяем
|
|
16
|
+
*/
|
|
17
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
18
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
19
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
20
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
21
|
+
};
|
|
22
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
23
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
24
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
25
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
26
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
27
|
+
};
|
|
28
|
+
var _BusyNumeric_peak_start_busy, _BusyNumeric_peak_end_busy;
|
|
29
|
+
import { binary_search } from "../primitives/binary_search.js";
|
|
30
|
+
/**
|
|
31
|
+
* MARK: Представьте отель
|
|
32
|
+
* Часть его номеров свободны, а часть — заняты
|
|
33
|
+
* Есть две особенности.
|
|
34
|
+
* 1) При заселении ищется случайный номер
|
|
35
|
+
* 2) Хотя у нас есть список занятых номеров, у нас не может быть списка свободных (за условием задачи)
|
|
36
|
+
* Этот алгоритм используется для эффективного поиска тех самых свободных номеров, которые удовлетворяют заданному условию
|
|
37
|
+
*/
|
|
38
|
+
// Убежден что в алгоритме список busy_areas можно применить структуру бинарных деревьев.
|
|
39
|
+
// Если основное количество производительности не реализуется из-за особенностей структуры списка, то это можно оптимизировать
|
|
40
|
+
// Интересно что ветки дерева можно определять по индексу. Да, это интересное замечание.
|
|
41
|
+
// В BusyNumeric можно добавить ещё метод separate
|
|
42
|
+
export class BusyNumeric {
|
|
43
|
+
get peak_start_busy() {
|
|
44
|
+
var _a;
|
|
45
|
+
if (__classPrivateFieldGet(this, _BusyNumeric_peak_start_busy, "f")) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
if (((_a = this.busy_areas[0]) === null || _a === void 0 ? void 0 : _a[0]) !== 0) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
__classPrivateFieldSet(this, _BusyNumeric_peak_start_busy, true, "f");
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
get peak_end_busy() {
|
|
55
|
+
var _a;
|
|
56
|
+
if (__classPrivateFieldGet(this, _BusyNumeric_peak_end_busy, "f")) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
if (((_a = this.busy_areas.at(-1)) === null || _a === void 0 ? void 0 : _a[1]) !== this.range) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
__classPrivateFieldSet(this, _BusyNumeric_peak_end_busy, true, "f");
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
constructor(range) {
|
|
66
|
+
this.busy_areas = [];
|
|
67
|
+
_BusyNumeric_peak_start_busy.set(this, false);
|
|
68
|
+
_BusyNumeric_peak_end_busy.set(this, false);
|
|
69
|
+
this.refresh = {
|
|
70
|
+
_areas: () => (this.busy_areas.length = 0),
|
|
71
|
+
_peak: () => {
|
|
72
|
+
__classPrivateFieldSet(this, _BusyNumeric_peak_start_busy, false, "f");
|
|
73
|
+
__classPrivateFieldSet(this, _BusyNumeric_peak_end_busy, false, "f");
|
|
74
|
+
},
|
|
75
|
+
full: () => {
|
|
76
|
+
this.refresh._areas();
|
|
77
|
+
this.refresh._peak();
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
if (range <= 0) {
|
|
81
|
+
throw new Error("Assertion: range must be positive");
|
|
82
|
+
}
|
|
83
|
+
this.range = range;
|
|
84
|
+
}
|
|
85
|
+
bifurcate(point) {
|
|
86
|
+
this.insert_area(point, point);
|
|
87
|
+
}
|
|
88
|
+
segments_count() {
|
|
89
|
+
return (this.busy_areas.length + 1 - +this.peak_start_busy - +this.peak_end_busy);
|
|
90
|
+
}
|
|
91
|
+
segment(at) {
|
|
92
|
+
if (!this.range) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
const { peak_start_busy, peak_end_busy } = this;
|
|
96
|
+
const segments_count = this.segments_count();
|
|
97
|
+
if (at < 0) {
|
|
98
|
+
if (segments_count + at < 0) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
at = segments_count + at;
|
|
102
|
+
}
|
|
103
|
+
if (peak_start_busy) {
|
|
104
|
+
at += 1;
|
|
105
|
+
}
|
|
106
|
+
if (peak_end_busy && at > segments_count) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
if (at > segments_count + 1) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
const left = this.busy_areas[at - 1];
|
|
113
|
+
const right = this.busy_areas[at];
|
|
114
|
+
// formula: busy_area_point ± 1 (jump to segment)
|
|
115
|
+
const start = (left === null || left === void 0 ? void 0 : left[1]) + 1 || 0;
|
|
116
|
+
const end = right ? right[0] - 1 : this.range;
|
|
117
|
+
const size = end - start + 1;
|
|
118
|
+
return { size, left, right };
|
|
119
|
+
}
|
|
120
|
+
insert_area(start, end) {
|
|
121
|
+
if (!Number.isInteger(start) || !Number.isInteger(end)) {
|
|
122
|
+
throw new Error(`Assertion error: start or end not integer ${start} ${end}, maybe float?`);
|
|
123
|
+
}
|
|
124
|
+
if (start > end) {
|
|
125
|
+
[start, end] = [end, start];
|
|
126
|
+
}
|
|
127
|
+
if (start < 0) {
|
|
128
|
+
throw new Error("Assertion error: start < 0");
|
|
129
|
+
}
|
|
130
|
+
if (end > this.range) {
|
|
131
|
+
throw new Error("Assertion error: end > this.range");
|
|
132
|
+
}
|
|
133
|
+
const place_index = binary_search(this.busy_areas.length - 1, (index) => {
|
|
134
|
+
var _a, _b, _c;
|
|
135
|
+
const value = (_a = this.busy_areas[index]) === null || _a === void 0 ? void 0 : _a[1];
|
|
136
|
+
const biggest = end < value;
|
|
137
|
+
if (biggest) {
|
|
138
|
+
return 1;
|
|
139
|
+
}
|
|
140
|
+
return (+(((_c = (_b = this.busy_areas[index + 1]) === null || _b === void 0 ? void 0 : _b[1]) !== null && _c !== void 0 ? _c : Number.MAX_SAFE_INTEGER) > end) - 1);
|
|
141
|
+
}) + 1;
|
|
142
|
+
const left = this.busy_areas[place_index - 1];
|
|
143
|
+
const right = this.busy_areas[place_index];
|
|
144
|
+
const is_left_included = !!left && left[1] + 1 >= start;
|
|
145
|
+
const is_right_included = !!right && right[0] - 1 <= end;
|
|
146
|
+
// Merge strategy 3
|
|
147
|
+
if (is_left_included && is_right_included) {
|
|
148
|
+
this.busy_areas.splice(place_index - 1, 2, [left[0], right[1]]);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
// Merge strategy 2
|
|
152
|
+
if (is_left_included || is_right_included) {
|
|
153
|
+
const target = is_left_included ? left : right;
|
|
154
|
+
const [index, points] = is_left_included
|
|
155
|
+
? [place_index - 1, [target[0], end]]
|
|
156
|
+
: [place_index, [start, target[1]]];
|
|
157
|
+
if (target[0] <= start && target[1] >= end) {
|
|
158
|
+
throw new Error("Assertion error: [start, end] in range");
|
|
159
|
+
}
|
|
160
|
+
this.busy_areas.splice(index, 1, points);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// Merge strategy 1
|
|
164
|
+
this.busy_areas.splice(place_index, 0, [start, end]);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
_BusyNumeric_peak_start_busy = new WeakMap(), _BusyNumeric_peak_end_busy = new WeakMap();
|
|
@@ -1,7 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { create_default_preventable } from "../primitives/createDefaultPreventable.js";
|
|
2
|
+
import { BusyNumeric } from "./BusyNumeric.js";
|
|
3
|
+
interface IParams<T> {
|
|
3
4
|
associatedWeights?: number[];
|
|
5
|
+
filter?: (item: T) => boolean;
|
|
4
6
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
interface IPickContext<T> {
|
|
8
|
+
busy_preventable: ReturnType<typeof create_default_preventable>;
|
|
9
|
+
item: T;
|
|
10
|
+
pick: (context: IPickContext<T>) => boolean;
|
|
11
|
+
threshold: number;
|
|
12
|
+
point: number;
|
|
13
|
+
}
|
|
14
|
+
export declare class RandomizerContext<T> {
|
|
15
|
+
hotel: BusyNumeric;
|
|
16
|
+
array: T[];
|
|
17
|
+
thresholds?: number[];
|
|
18
|
+
constructor(array: T[], thresholds?: number[]);
|
|
19
|
+
static from<T>({ array, associatedWeights, }: {
|
|
20
|
+
array: T[];
|
|
21
|
+
associatedWeights?: IParams<T>["associatedWeights"];
|
|
22
|
+
}): RandomizerContext<T>;
|
|
23
|
+
pickRandom(pick?: IPickContext<T>["pick"]): T | undefined;
|
|
24
|
+
}
|
|
25
|
+
export declare function getRandomElementIndexInWeights(weights: NonNullable<IParams<unknown>["associatedWeights"]>): number | never;
|
|
26
|
+
export declare function getRandomElementFromArray<T>(array: T[], { associatedWeights, filter }?: IParams<T>): void;
|
|
7
27
|
export {};
|
|
@@ -1,24 +1,103 @@
|
|
|
1
|
+
import { binary_search } from "../primitives/binary_search.js";
|
|
2
|
+
import { create_default_preventable } from "../primitives/createDefaultPreventable.js";
|
|
3
|
+
import { BusyNumeric } from "./BusyNumeric.js";
|
|
1
4
|
import { getRandomNumberInRange } from "./getRandomNumberInRange.js";
|
|
2
|
-
export
|
|
5
|
+
export class RandomizerContext {
|
|
6
|
+
constructor(array, thresholds) {
|
|
7
|
+
var _a;
|
|
8
|
+
this.array = array;
|
|
9
|
+
this.thresholds = thresholds;
|
|
10
|
+
const range = (_a = thresholds === null || thresholds === void 0 ? void 0 : thresholds.at(-1)) !== null && _a !== void 0 ? _a : array.length;
|
|
11
|
+
this.hotel = new BusyNumeric(range);
|
|
12
|
+
}
|
|
13
|
+
static from({ array, associatedWeights, }) {
|
|
14
|
+
const thresholds = associatedWeights &&
|
|
15
|
+
_getRandomElementIndexInWeight_thresholds(associatedWeights);
|
|
16
|
+
return new RandomizerContext(array, thresholds);
|
|
17
|
+
}
|
|
18
|
+
pickRandom(pick = () => true) {
|
|
19
|
+
var _a;
|
|
20
|
+
const { hotel, thresholds, array } = this;
|
|
21
|
+
while (true) {
|
|
22
|
+
const segments_count = hotel.segments_count();
|
|
23
|
+
if (!segments_count) {
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
const { left, size } = hotel.segment(getRandomNumberInRange({ max: segments_count - 1 }));
|
|
27
|
+
const point = ((_a = left === null || left === void 0 ? void 0 : left[1]) !== null && _a !== void 0 ? _a : -1) + getRandomNumberInRange({ min: 1, max: size });
|
|
28
|
+
const index = thresholds
|
|
29
|
+
? _getRandomElementIndexInWeight_of_thresholds(thresholds, point)
|
|
30
|
+
: point;
|
|
31
|
+
const element = array[index];
|
|
32
|
+
const pickContext = {
|
|
33
|
+
busy_preventable: create_default_preventable(),
|
|
34
|
+
item: element,
|
|
35
|
+
pick,
|
|
36
|
+
threshold: index,
|
|
37
|
+
point,
|
|
38
|
+
};
|
|
39
|
+
if (pick(pickContext)) {
|
|
40
|
+
return element;
|
|
41
|
+
}
|
|
42
|
+
if (!pickContext.busy_preventable.default_prevented()) {
|
|
43
|
+
thresholds
|
|
44
|
+
? hotel.insert_area(thresholds[index - 1] + 1 || 0, thresholds[index])
|
|
45
|
+
: hotel.bifurcate(index);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function _getRandomElementIndexInWeight_of_thresholds(thresholds, value) {
|
|
52
|
+
if (!thresholds.length) {
|
|
53
|
+
return -1;
|
|
54
|
+
}
|
|
55
|
+
return binary_search(thresholds.length - 1, (index) => {
|
|
56
|
+
var _a;
|
|
57
|
+
const hold = thresholds[index];
|
|
58
|
+
if (hold >= value && ((_a = thresholds[index - 1]) !== null && _a !== void 0 ? _a : -1) < value) {
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
if (hold < value) {
|
|
62
|
+
return -1;
|
|
63
|
+
}
|
|
64
|
+
return 1;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function _getRandomElementIndexInWeight_thresholds(weights) {
|
|
3
68
|
if (weights.length < 1) {
|
|
4
69
|
new Error("Invalid array length");
|
|
5
70
|
}
|
|
6
71
|
let previousLimit = 0;
|
|
7
|
-
|
|
72
|
+
return weights.map((weight) => (previousLimit += weight));
|
|
73
|
+
}
|
|
74
|
+
export function getRandomElementIndexInWeights(weights) {
|
|
75
|
+
const thresholds = _getRandomElementIndexInWeight_thresholds(weights);
|
|
8
76
|
const lotterySecretNumber = Math.random() * thresholds.at(-1);
|
|
9
|
-
return thresholds
|
|
77
|
+
return _getRandomElementIndexInWeight_of_thresholds(thresholds, lotterySecretNumber);
|
|
10
78
|
}
|
|
11
|
-
|
|
79
|
+
// task: честный алгоритм , для которого каждый из элеметов списка имеет равный шанс на реализацию своей вероятности.
|
|
80
|
+
// Обычно весь список фильтруется и после выбирается элемент. В нашем случае фильтр будет срабатывать только для тех элементов,
|
|
81
|
+
// которые на этапе фильтрации «выбраны» — то есть имеют свою стопроцентную вероятность быть окончательными после успешного прохождения фильтра
|
|
82
|
+
// требования: честная вероятность; итоговая производительность должна быть выше обычной при проходе списка фильтром
|
|
83
|
+
// Алгоритм:
|
|
84
|
+
/*
|
|
85
|
+
Оптимальный алгоритм поиска случайного элемента списка, удовлетворяющего условию. Поддерживается возможность задавать элементам коэффициент вероятности
|
|
86
|
+
|
|
87
|
+
Перед выбором элемента выбираем случайный сегмент, далее случайный элемент в этом сегменте
|
|
88
|
+
|
|
89
|
+
Если соответствует условию, возвращаем элемент. Завершаем поиск
|
|
90
|
+
|
|
91
|
+
Иначе исключаем элемент путем раздвоения текущего сегмента так, чтобы элемент не оказался ни в одном сегменте списка
|
|
92
|
+
|
|
93
|
+
Повторяем
|
|
94
|
+
*/
|
|
95
|
+
export function getRandomElementFromArray(array, { associatedWeights, filter = () => true } = {}) {
|
|
12
96
|
if (associatedWeights && associatedWeights.length !== array.length) {
|
|
13
97
|
throw new Error("Incorrectly passed argument associatedWeights: The length of the associatedWeights must exactly match the length of the weights array");
|
|
14
98
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (needPop) {
|
|
20
|
-
array.splice(index, 1);
|
|
21
|
-
associatedWeights === null || associatedWeights === void 0 ? void 0 : associatedWeights.splice(index, 1);
|
|
22
|
-
}
|
|
23
|
-
return input;
|
|
99
|
+
RandomizerContext.from({
|
|
100
|
+
array,
|
|
101
|
+
associatedWeights,
|
|
102
|
+
}).pickRandom(({ item }) => filter(item));
|
|
24
103
|
}
|
|
@@ -97,7 +97,7 @@ export class BracketsParser {
|
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
99
|
const start = context.stack.pop();
|
|
100
|
-
const end = StackElement.from(
|
|
100
|
+
const end = StackElement.from({ ...match, key: variant.key, variant });
|
|
101
101
|
const group = this._createGroup(start, end, context);
|
|
102
102
|
context.appendGroup(group);
|
|
103
103
|
return end;
|
|
@@ -127,7 +127,7 @@ export class BracketsParser {
|
|
|
127
127
|
return null;
|
|
128
128
|
}
|
|
129
129
|
const { match, variant } = result;
|
|
130
|
-
const start = StackElement.from(
|
|
130
|
+
const start = StackElement.from({ ...match, key: variant.key, variant });
|
|
131
131
|
context.stack.push(start);
|
|
132
132
|
return start;
|
|
133
133
|
}
|
|
@@ -227,7 +227,7 @@ var SpecialRowTypeEnum;
|
|
|
227
227
|
class TextTableBuilder {
|
|
228
228
|
constructor() {
|
|
229
229
|
this.rows = [];
|
|
230
|
-
this.options =
|
|
230
|
+
this.options = { ...DEFAULT_TABLE_OPTIONS };
|
|
231
231
|
}
|
|
232
232
|
setBorderOptions(callback = () => "|", directions = [
|
|
233
233
|
BorderDirectionEnum.BorderLeft,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function binary_search(max: number, compare: (index: number) => number): number;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Если большее, то идёт к меньшим
|
|
2
|
+
export function binary_search(max, compare) {
|
|
3
|
+
let min = 0;
|
|
4
|
+
while (true) {
|
|
5
|
+
const index = Math.floor((min + max) / 2);
|
|
6
|
+
const compared = compare(index);
|
|
7
|
+
if (compared === 0) {
|
|
8
|
+
return index;
|
|
9
|
+
}
|
|
10
|
+
if (min === max) {
|
|
11
|
+
return -1;
|
|
12
|
+
}
|
|
13
|
+
if (compared > 0) {
|
|
14
|
+
max = index - 1;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
min = index + 1;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function normalize_to_integer(list: number[]): number[];
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zoodogood/utils",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.0-change.2568",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
|
-
"types": "lib/index.d.ts",
|
|
8
7
|
"homepage": "https://zoodogood.github.io/utils",
|
|
8
|
+
"types": "lib/index.d.ts",
|
|
9
9
|
"typings": "lib/index",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": "./lib/index.js",
|
|
@@ -23,19 +23,19 @@
|
|
|
23
23
|
"author": "",
|
|
24
24
|
"license": "ISC",
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@types/node": "^
|
|
27
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
28
|
-
"@typescript-eslint/parser": "^6.
|
|
29
|
-
"@vitest/ui": "^
|
|
30
|
-
"discord-api-types": "^0.37.
|
|
31
|
-
"discord.js": "^14.
|
|
32
|
-
"eslint": "^8.
|
|
33
|
-
"eslint-config-prettier": "^9.
|
|
34
|
-
"eslint-plugin-prettier": "^5.
|
|
35
|
-
"prettier": "^3.
|
|
36
|
-
"retypeapp-linux-x64": "^3.
|
|
37
|
-
"typescript": "^5.
|
|
38
|
-
"vitest": "^0.
|
|
26
|
+
"@types/node": "^22.5.4",
|
|
27
|
+
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
28
|
+
"@typescript-eslint/parser": "^6.21.0",
|
|
29
|
+
"@vitest/ui": "^2.0.5",
|
|
30
|
+
"discord-api-types": "^0.37.99",
|
|
31
|
+
"discord.js": "^14.16.1",
|
|
32
|
+
"eslint": "^8.57.0",
|
|
33
|
+
"eslint-config-prettier": "^9.1.0",
|
|
34
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
35
|
+
"prettier": "^3.3.3",
|
|
36
|
+
"retypeapp-linux-x64": "^3.5.0",
|
|
37
|
+
"typescript": "^5.5.4",
|
|
38
|
+
"vitest": "^2.0.5"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"discord.js": ">=14.x.x"
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
"scripts": {
|
|
69
|
-
"test": "vitest
|
|
69
|
+
"test": "vitest --help",
|
|
70
70
|
"docs-build": "cd ./docs && retype build --output ./public",
|
|
71
71
|
"docs-watch": "cd ./docs && retype watch",
|
|
72
72
|
"build": "tsc",
|