@twardoch/namzy 1.0.21 → 1.0.23

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/src/mangle.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  // this_file: src/mangle.ts
2
2
 
3
- import { ROTATIONS, STEMS } from "./wordlist.js";
3
+ import { BAD_SEAMS, ROTATIONS, STEMS } from "./wordlist.js";
4
+
5
+ const MAX_LEN = 12;
6
+ const MAX_TRIES = 8;
4
7
 
5
- /** Seeded mulberry32 RNG — yields [0,1) floats. */
6
8
  export function mulberry32(seed: number): () => number {
7
9
  let s = seed >>> 0;
8
10
  return () => {
@@ -17,35 +19,65 @@ function pick<T>(arr: readonly T[], rng: () => number): T {
17
19
  return arr[Math.floor(rng() * arr.length)];
18
20
  }
19
21
 
20
- const ROT_MAP = new Map<string, string>(ROTATIONS as readonly (readonly [string, string])[]);
22
+ function hasTripleLetter(s: string): boolean {
23
+ for (let i = 2; i < s.length; i++) {
24
+ if (s[i] === s[i - 1] && s[i] === s[i - 2]) return true;
25
+ }
26
+ return false;
27
+ }
21
28
 
22
- function rotateAt(s: string, pos: number): string {
23
- const ch = s[pos];
24
- const lower = ch.toLowerCase();
25
- const repl = ROT_MAP.get(lower);
26
- if (repl === undefined) return s;
27
- const out = ch === lower ? repl : repl.toUpperCase();
28
- return s.slice(0, pos) + out + s.slice(pos + 1);
29
+ function junctionUgly(compound: string, junction: number): boolean {
30
+ // Inspect the 4-char window centered on the junction.
31
+ const start = Math.max(0, junction - 2);
32
+ const end = Math.min(compound.length, junction + 2);
33
+ const win = compound.slice(start, end);
34
+ for (const seam of BAD_SEAMS) {
35
+ if (win.includes(seam)) return true;
36
+ }
37
+ return false;
29
38
  }
30
39
 
31
- /** Apply 1 or 2 rotations on randomly selected positions of the compound. */
32
- export function applyRotations(compound: string, rng: () => number): string {
33
- if (compound.length === 0) return compound;
34
- const passes = rng() < 0.5 ? 1 : 2;
35
- let out = compound;
36
- for (let i = 0; i < passes; i++) {
37
- out = rotateAt(out, Math.floor(rng() * out.length));
40
+ /** Apply one rotation at a randomly chosen matching position. */
41
+ export function applyRotation(s: string, rng: () => number): string {
42
+ type Match = { i: number; src: string; dst: string };
43
+ const matches: Match[] = [];
44
+ for (let i = 0; i < s.length; i++) {
45
+ for (const [src, dst] of ROTATIONS) {
46
+ if (s.slice(i, i + src.length) === src) {
47
+ matches.push({ i, src, dst });
48
+ }
49
+ }
38
50
  }
39
- return out;
51
+ if (matches.length === 0) return s;
52
+ const m = matches[Math.floor(rng() * matches.length)];
53
+ return s.slice(0, m.i) + m.dst + s.slice(m.i + m.src.length);
40
54
  }
41
55
 
42
56
  function capitalize(s: string): string {
43
57
  return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
44
58
  }
45
59
 
46
- /** Build one name: any two stems, concatenated, lightly rotated, capitalized. */
60
+ /** Build one name with junction validation + one rotation. */
47
61
  export function buildName(rng: () => number): string {
48
- const a = pick(STEMS, rng);
49
- const b = pick(STEMS, rng);
50
- return capitalize(applyRotations(a + b, rng));
62
+ let best = "";
63
+ for (let attempt = 0; attempt < MAX_TRIES; attempt++) {
64
+ const a = pick(STEMS, rng);
65
+ const b = pick(STEMS, rng);
66
+ const compound = a + b;
67
+ if (compound.length > MAX_LEN) {
68
+ best = best || compound;
69
+ continue;
70
+ }
71
+ if (hasTripleLetter(compound)) {
72
+ best = best || compound;
73
+ continue;
74
+ }
75
+ if (junctionUgly(compound, a.length)) {
76
+ best = best || compound;
77
+ continue;
78
+ }
79
+ return capitalize(applyRotation(compound, rng));
80
+ }
81
+ // All attempts had some flaw; rotate and ship the first candidate anyway.
82
+ return capitalize(applyRotation(best, rng));
51
83
  }
package/src/web.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // this_file: src/web.ts
2
2
  // Browser entry point — exposes namzy.generate() on window.namzy.
3
3
 
4
- export { generate, generateMany, applyRotations, buildName, mulberry32, STEMS, ROTATIONS } from "./index.js";
4
+ export { generate, generateMany, applyRotation, buildName, mulberry32, STEMS, ROTATIONS } from "./index.js";
5
5
  export type { NamzyOptions } from "./index.js";