feat: random generators, frozenset and __hash__/__iter__/__getitem__/__contains__ in Ren'Py python
renpy.random/random get choices, sample, randrange and Random(seed); frozenset, iter and hash are builtins. User classes may define __hash__ (dict keys), __iter__, __getitem__ and __contains__; the converter accepts them instead of rejecting the whole class, which removes the Perk/Skill/Status wall in SaintlyCorruption. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f9eb5c1212
commit
e1c596288e
8 files changed
+165
-18
No files matched your search
@@ -338,18 +338,68 @@ export class Engine {
|
||||
const kwless = (args: any[]) =>
|
||||
args.length && args[args.length - 1]?.__kw ? args.slice(0, -1) : args;
|
||||
const kwOf = (args: any[]) => (args.length && args[args.length - 1]?.__kw) || {};
|
||||
const random = {
|
||||
randint: (a: number, b: number) => a + Math.floor(Math.random() * (b - a + 1)),
|
||||
random: () => Math.random(),
|
||||
choice: (xs: any[]) => xs[Math.floor(Math.random() * xs.length)],
|
||||
shuffle: (xs: any[]) => {
|
||||
for (let i = xs.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[xs[i], xs[j]] = [xs[j], xs[i]];
|
||||
}
|
||||
},
|
||||
uniform: (a: number, b: number) => a + Math.random() * (b - a),
|
||||
const makeRandom = (next: () => number): Record<string, any> => {
|
||||
const below = (n: number) => Math.floor(next() * n);
|
||||
const self: Record<string, any> = {
|
||||
randint: (a: number, b: number) => a + below(b - a + 1),
|
||||
randrange: (a: number, b?: number, step = 1) => {
|
||||
const [lo, hi] = b === undefined ? [0, a] : [a, b];
|
||||
const count = Math.max(0, Math.ceil((hi - lo) / step));
|
||||
if (!count) throw new Error("randrange: пустой диапазон");
|
||||
return lo + below(count) * step;
|
||||
},
|
||||
random: () => next(),
|
||||
choice: (xs: any[]) => {
|
||||
if (!xs.length) throw new Error("choice: пустая последовательность");
|
||||
return xs[below(xs.length)];
|
||||
},
|
||||
choices: (xs: any[], ...a: any[]) => {
|
||||
const kw = kwOf(a);
|
||||
const weights: number[] | null = kw.weights ?? kwless(a)[0] ?? null;
|
||||
const k = kw.k ?? 1;
|
||||
const total = weights ? weights.reduce((s, w) => s + w, 0) : xs.length;
|
||||
const out: any[] = [];
|
||||
for (let n = 0; n < k; n++) {
|
||||
if (!weights) { out.push(xs[below(xs.length)]); continue; }
|
||||
let roll = next() * total, i = 0;
|
||||
while (i < xs.length - 1 && roll >= weights[i]) roll -= weights[i++];
|
||||
out.push(xs[i]);
|
||||
}
|
||||
return out;
|
||||
},
|
||||
sample: (xs: any[], k: number) => {
|
||||
const pool = [...xs];
|
||||
if (k > pool.length) throw new Error("sample: выборка больше совокупности");
|
||||
for (let i = 0; i < k; i++) {
|
||||
const j = i + below(pool.length - i);
|
||||
[pool[i], pool[j]] = [pool[j], pool[i]];
|
||||
}
|
||||
return pool.slice(0, k);
|
||||
},
|
||||
shuffle: (xs: any[]) => {
|
||||
for (let i = xs.length - 1; i > 0; i--) {
|
||||
const j = below(i + 1);
|
||||
[xs[i], xs[j]] = [xs[j], xs[i]];
|
||||
}
|
||||
},
|
||||
uniform: (a: number, b: number) => a + next() * (b - a),
|
||||
seed: () => null,
|
||||
};
|
||||
// `rng = renpy.random.Random(seed)`: an independent generator; a given seed makes it repeatable
|
||||
self.Random = (seed?: any) => {
|
||||
if (seed === undefined || seed === null) return makeRandom(Math.random);
|
||||
let state = [...String(seed)].reduce((h, c) => (Math.imul(h, 31) + c.charCodeAt(0)) | 0, 7) >>> 0;
|
||||
return makeRandom(() => {
|
||||
state = (state + 0x6d2b79f5) >>> 0;
|
||||
let t = state;
|
||||
t = Math.imul(t ^ (t >>> 15), t | 1);
|
||||
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
||||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
||||
});
|
||||
};
|
||||
return self;
|
||||
};
|
||||
const random = makeRandom(Math.random);
|
||||
const music = (ch: string) => ({
|
||||
play: (...a: any[]) => {
|
||||
const kw = kwOf(a);
|
||||
@@ -573,6 +623,7 @@ export class Engine {
|
||||
list: (xs: any = []) => [...(xs instanceof PyDict ? xs.map.keys() : xs)],
|
||||
tuple: (xs: any = []) => [...xs],
|
||||
set: (xs: any = []) => [...new Set(xs)],
|
||||
frozenset: (xs: any = []) => [...new Set(xs)],
|
||||
dict: (...a: any[]) => {
|
||||
const d = new PyDict(Array.isArray(kwless(a)[0]) ? kwless(a)[0] : []);
|
||||
for (const [k, v] of Object.entries(kwOf(a))) d.set(k, v);
|
||||
|
||||
Reference in new issue
Block a user