buddy-finder-by-userid 1.0.0
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/README.md +79 -0
- package/cli.d.ts +6 -0
- package/cli.js +204 -0
- package/core.d.ts +30 -0
- package/core.js +97 -0
- package/index.d.ts +6 -0
- package/index.js +6 -0
- package/package.json +27 -0
- package/search.d.ts +73 -0
- package/search.js +183 -0
- package/types.d.ts +38 -0
- package/types.js +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Buddy Finder by UserID
|
|
2
|
+
|
|
3
|
+
Claude Code Buddy 宠物属性穷举工具 - 通过穷举 userID 找到理想的 buddy 属性。
|
|
4
|
+
|
|
5
|
+
## 功能特点
|
|
6
|
+
|
|
7
|
+
- 🎯 **穷举搜索** - 随机生成 userID 并计算对应的 buddy 属性
|
|
8
|
+
- 🔍 **条件过滤** - 按稀有度、物种、帽子、闪光、属性值筛选
|
|
9
|
+
- 📊 **评分系统** - 计算与目标的匹配分数
|
|
10
|
+
- ⚡ **进度显示** - 实时显示搜索进度和速率
|
|
11
|
+
- 💾 **结果导出** - 保存匹配结果到 JSON 文件
|
|
12
|
+
|
|
13
|
+
## 安装
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# 克隆仓库
|
|
17
|
+
git clone https://github.com/YOUR_USERNAME/buddy-finder-by-userID.git
|
|
18
|
+
cd buddy-finder-by-userID
|
|
19
|
+
|
|
20
|
+
# 确保 Node.js >= 18
|
|
21
|
+
node --version
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 使用方法
|
|
25
|
+
|
|
26
|
+
### 命令行
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# 显示帮助
|
|
30
|
+
node cli.js --help
|
|
31
|
+
|
|
32
|
+
# 列出所有可用选项
|
|
33
|
+
node cli.js --list
|
|
34
|
+
|
|
35
|
+
# 测试指定 userID
|
|
36
|
+
node cli.js --test 3a36224b57ca29e7f91a973131a565289378e898bcaebfe397f88483b5ef45d0
|
|
37
|
+
|
|
38
|
+
# 使用预设搜索
|
|
39
|
+
node cli.js -p perfect -i 50000000
|
|
40
|
+
|
|
41
|
+
# 自定义条件搜索
|
|
42
|
+
node cli.js -r legendary -s dragon --shiny
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 预设列表
|
|
46
|
+
|
|
47
|
+
| 预设名 | 说明 |
|
|
48
|
+
|--------|------|
|
|
49
|
+
| `legendary_dragon` | 传奇龙 |
|
|
50
|
+
| `legendary_dragon_shiny` | 闪光传奇龙 |
|
|
51
|
+
| `legendary_dragon_wizard` | 传奇龙 + 巫师帽 |
|
|
52
|
+
| `perfect` | 完美搭配(闪光传奇龙+巫师帽+高DEBUGGING)|
|
|
53
|
+
| `any_legendary` | 任意传奇宠物 |
|
|
54
|
+
| `shiny_hunter` | 闪光传奇宠物 |
|
|
55
|
+
|
|
56
|
+
### 可用选项
|
|
57
|
+
|
|
58
|
+
- `-p, --preset <name>` - 使用预设配置
|
|
59
|
+
- `-r, --rarity <rarity>` - 目标稀有度 (common/uncommon/rare/epic/legendary)
|
|
60
|
+
- `-s, --species <name>` - 目标物种
|
|
61
|
+
- `-h, --hat <hat>` - 目标帽子
|
|
62
|
+
- `--shiny` - 要求闪光
|
|
63
|
+
- `--min-stat <stat:value>` - 最低属性值
|
|
64
|
+
- `--peak-stat <stat>` - 峰值属性
|
|
65
|
+
- `-i, --iterations <n>` - 最大迭代次数 (默认: 10000000)
|
|
66
|
+
- `-o, --output <file>` - 输出结果到文件
|
|
67
|
+
|
|
68
|
+
## 技术实现
|
|
69
|
+
|
|
70
|
+
本工具实现了与 Claude Code buddy 系统完全一致的算法:
|
|
71
|
+
|
|
72
|
+
- **Mulberry32 PRNG** - 确定性伪随机数生成器
|
|
73
|
+
- **FNV-1a hash** - 字符串转数字种子
|
|
74
|
+
- **稀有度权重抽取** - 基于权重的稀有度计算
|
|
75
|
+
- **属性值生成** - 峰值/垃圾属性分配逻辑
|
|
76
|
+
|
|
77
|
+
## 许可证
|
|
78
|
+
|
|
79
|
+
MIT
|
package/cli.d.ts
ADDED
package/cli.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Buddy Finder CLI
|
|
4
|
+
* 穷举 userID 以获得特定的 Claude Code buddy 宠物属性
|
|
5
|
+
*/
|
|
6
|
+
import { parseArgs } from 'util';
|
|
7
|
+
import { search, PRESETS, formatResult, calculateScore } from './search.js';
|
|
8
|
+
import { rollBuddy } from './core.js';
|
|
9
|
+
import { RARITIES, SPECIES, HATS, STAT_NAMES } from './types.js';
|
|
10
|
+
import { writeFileSync } from 'fs';
|
|
11
|
+
const HELP = `
|
|
12
|
+
Buddy Finder - Claude Code 宠物属性穷举工具
|
|
13
|
+
|
|
14
|
+
用法:
|
|
15
|
+
buddy-finder [options]
|
|
16
|
+
|
|
17
|
+
选项:
|
|
18
|
+
-p, --preset <name> 使用预设配置 (见下方预设列表)
|
|
19
|
+
-r, --rarity <rarity> 目标稀有度 (common|uncommon|rare|epic|legendary)
|
|
20
|
+
-s, --species <name> 目标物种 (duck|dragon|cat|...)
|
|
21
|
+
-h, --hat <hat> 目标帽子 (none|crown|tophat|wizard|...)
|
|
22
|
+
--shiny 要求闪光
|
|
23
|
+
--min-stat <stat:value> 最低属性值 (例: --min-stat DEBUGGING:80)
|
|
24
|
+
--peak-stat <stat> 峰值属性
|
|
25
|
+
-i, --iterations <n> 最大迭代次数 (默认: 10000000)
|
|
26
|
+
-o, --output <file> 输出结果到文件
|
|
27
|
+
--test <userId> 测试指定 userId 并显示结果
|
|
28
|
+
--list 列出所有可用选项
|
|
29
|
+
--help 显示帮助信息
|
|
30
|
+
|
|
31
|
+
预设:
|
|
32
|
+
legendary_dragon 传奇龙
|
|
33
|
+
legendary_dragon_shiny 闪光传奇龙
|
|
34
|
+
legendary_dragon_wizard 传奇龙 + 巫师帽
|
|
35
|
+
perfect 完美搭配 (闪光传奇龙+巫师帽+高DEBUGGING)
|
|
36
|
+
any_legendary 任意传奇宠物
|
|
37
|
+
shiny_hunter 闪光传奇宠物
|
|
38
|
+
|
|
39
|
+
示例:
|
|
40
|
+
buddy-finder -p perfect -i 50000000
|
|
41
|
+
buddy-finder -r legendary -s dragon --shiny
|
|
42
|
+
buddy-finder --test 3a36224b57ca29e7f91a973131a565289378e898bcaebfe397f88483b5ef45d0
|
|
43
|
+
`;
|
|
44
|
+
function listOptions() {
|
|
45
|
+
console.log('\n=== 可用选项 ===\n');
|
|
46
|
+
console.log('稀有度:', RARITIES.join(', '));
|
|
47
|
+
console.log('\n物种:', SPECIES.join(', '));
|
|
48
|
+
console.log('\n帽子:', HATS.join(', '));
|
|
49
|
+
console.log('\n属性:', STAT_NAMES.join(', '));
|
|
50
|
+
console.log('\n预设:');
|
|
51
|
+
for (const [name, preset] of Object.entries(PRESETS)) {
|
|
52
|
+
console.log(` ${name}: ${preset.description}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function testUserId(userId) {
|
|
56
|
+
console.log(`\n测试 userID: ${userId}\n`);
|
|
57
|
+
const result = rollBuddy(userId);
|
|
58
|
+
console.log(formatResult(result));
|
|
59
|
+
// 计算各预设的分数
|
|
60
|
+
console.log('\n=== 各预设匹配分数 ===');
|
|
61
|
+
for (const [name, preset] of Object.entries(PRESETS)) {
|
|
62
|
+
const score = calculateScore(result, preset.criteria);
|
|
63
|
+
console.log(`${name}: ${score}/100`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function runSearch(options) {
|
|
67
|
+
// 构建搜索条件
|
|
68
|
+
const criteria = {};
|
|
69
|
+
if (options.preset) {
|
|
70
|
+
const preset = PRESETS[options.preset];
|
|
71
|
+
if (!preset) {
|
|
72
|
+
console.error(`未知预设: ${options.preset}`);
|
|
73
|
+
console.log('可用预设:', Object.keys(PRESETS).join(', '));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
Object.assign(criteria, preset.criteria);
|
|
77
|
+
console.log(`\n使用预设: ${options.preset} (${preset.description})`);
|
|
78
|
+
}
|
|
79
|
+
if (options.rarity) {
|
|
80
|
+
if (!RARITIES.includes(options.rarity)) {
|
|
81
|
+
console.error(`未知稀有度: ${options.rarity}`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
criteria.rarity = options.rarity;
|
|
85
|
+
}
|
|
86
|
+
if (options.species) {
|
|
87
|
+
if (!SPECIES.includes(options.species)) {
|
|
88
|
+
console.error(`未知物种: ${options.species}`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
criteria.species = options.species;
|
|
92
|
+
}
|
|
93
|
+
if (options.hat) {
|
|
94
|
+
if (!HATS.includes(options.hat)) {
|
|
95
|
+
console.error(`未知帽子: ${options.hat}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
criteria.hat = options.hat;
|
|
99
|
+
}
|
|
100
|
+
if (options.shiny) {
|
|
101
|
+
criteria.shiny = true;
|
|
102
|
+
}
|
|
103
|
+
if (options.minStats.length > 0) {
|
|
104
|
+
criteria.minStats = {};
|
|
105
|
+
for (const spec of options.minStats) {
|
|
106
|
+
const [stat, value] = spec.split(':');
|
|
107
|
+
if (!STAT_NAMES.includes(stat)) {
|
|
108
|
+
console.error(`未知属性: ${stat}`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
criteria.minStats[stat] = parseInt(value, 10);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (options.peakStat) {
|
|
115
|
+
if (!STAT_NAMES.includes(options.peakStat)) {
|
|
116
|
+
console.error(`未知属性: ${options.peakStat}`);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
criteria.peakStat = options.peakStat;
|
|
120
|
+
}
|
|
121
|
+
console.log(`\n搜索条件:`);
|
|
122
|
+
console.log(JSON.stringify(criteria, null, 2));
|
|
123
|
+
console.log(`最大迭代: ${options.iterations.toLocaleString()}`);
|
|
124
|
+
console.log('');
|
|
125
|
+
const startTime = Date.now();
|
|
126
|
+
let lastUpdate = startTime;
|
|
127
|
+
const result = search({
|
|
128
|
+
maxIterations: options.iterations,
|
|
129
|
+
criteria,
|
|
130
|
+
onProgress: (iterations, bestScore) => {
|
|
131
|
+
const now = Date.now();
|
|
132
|
+
if (now - lastUpdate > 1000) { // 每秒更新一次
|
|
133
|
+
const elapsed = ((now - startTime) / 1000).toFixed(1);
|
|
134
|
+
const rate = (iterations / (now - startTime) * 1000).toFixed(0);
|
|
135
|
+
process.stdout.write(`\r已搜索 ${iterations.toLocaleString()} (${rate}/s) | 最佳分数: ${bestScore} | 耗时: ${elapsed}s`);
|
|
136
|
+
lastUpdate = now;
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
onMatch: (match, score) => {
|
|
140
|
+
console.log(`\n\n★ 找到匹配! (分数: ${score})`);
|
|
141
|
+
console.log(formatResult(match));
|
|
142
|
+
console.log('');
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
146
|
+
console.log(`\n\n搜索完成! 耗时: ${elapsed}s`);
|
|
147
|
+
if (result) {
|
|
148
|
+
console.log('\n=== 最终最佳结果 ===');
|
|
149
|
+
console.log(formatResult(result));
|
|
150
|
+
if (options.output) {
|
|
151
|
+
const output = {
|
|
152
|
+
...result,
|
|
153
|
+
searchedAt: new Date().toISOString(),
|
|
154
|
+
elapsedSeconds: parseFloat(elapsed)
|
|
155
|
+
};
|
|
156
|
+
writeFileSync(options.output, JSON.stringify(output, null, 2));
|
|
157
|
+
console.log(`\n结果已保存到: ${options.output}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
console.log('\n未找到匹配,尝试增加迭代次数');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// 解析命令行参数
|
|
165
|
+
const { values } = parseArgs({
|
|
166
|
+
options: {
|
|
167
|
+
preset: { type: 'string', short: 'p' },
|
|
168
|
+
rarity: { type: 'string', short: 'r' },
|
|
169
|
+
species: { type: 'string', short: 's' },
|
|
170
|
+
hat: { type: 'string', short: 'h' },
|
|
171
|
+
shiny: { type: 'boolean' },
|
|
172
|
+
'min-stat': { type: 'string', multiple: true },
|
|
173
|
+
'peak-stat': { type: 'string' },
|
|
174
|
+
iterations: { type: 'string', short: 'i', default: '10000000' },
|
|
175
|
+
output: { type: 'string', short: 'o' },
|
|
176
|
+
test: { type: 'string' },
|
|
177
|
+
list: { type: 'boolean' },
|
|
178
|
+
help: { type: 'boolean' }
|
|
179
|
+
},
|
|
180
|
+
strict: true
|
|
181
|
+
});
|
|
182
|
+
if (values.help) {
|
|
183
|
+
console.log(HELP);
|
|
184
|
+
process.exit(0);
|
|
185
|
+
}
|
|
186
|
+
if (values.list) {
|
|
187
|
+
listOptions();
|
|
188
|
+
process.exit(0);
|
|
189
|
+
}
|
|
190
|
+
if (values.test) {
|
|
191
|
+
testUserId(values.test);
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
194
|
+
runSearch({
|
|
195
|
+
preset: values.preset,
|
|
196
|
+
rarity: values.rarity,
|
|
197
|
+
species: values.species,
|
|
198
|
+
hat: values.hat,
|
|
199
|
+
shiny: values.shiny,
|
|
200
|
+
minStats: values['min-stat'] || [],
|
|
201
|
+
peakStat: values['peak-stat'],
|
|
202
|
+
iterations: parseInt(values.iterations, 10),
|
|
203
|
+
output: values.output
|
|
204
|
+
});
|
package/core.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 核心算法 - 与 Claude Code buddy 系统完全一致的实现
|
|
3
|
+
*/
|
|
4
|
+
import { Rarity, StatName, BuddyResult } from './types.js';
|
|
5
|
+
export { RARITY_FLOOR } from './types.js';
|
|
6
|
+
export declare const SALT = "friend-2026-401";
|
|
7
|
+
/**
|
|
8
|
+
* Mulberry32 PRNG - 确定性伪随机数生成器
|
|
9
|
+
*/
|
|
10
|
+
export declare function mulberry32(seed: number): () => number;
|
|
11
|
+
/**
|
|
12
|
+
* FNV-1a hash - 字符串转数字种子
|
|
13
|
+
*/
|
|
14
|
+
export declare function hashString(s: string): number;
|
|
15
|
+
/**
|
|
16
|
+
* 根据权重抽取稀有度
|
|
17
|
+
*/
|
|
18
|
+
export declare function rollRarity(rng: () => number): Rarity;
|
|
19
|
+
/**
|
|
20
|
+
* 生成属性值
|
|
21
|
+
*/
|
|
22
|
+
export declare function rollStats(rng: () => number, rarity: Rarity): Record<StatName, number>;
|
|
23
|
+
/**
|
|
24
|
+
* 根据 userId 生成 buddy 属性
|
|
25
|
+
*/
|
|
26
|
+
export declare function rollBuddy(userId: string): BuddyResult;
|
|
27
|
+
/**
|
|
28
|
+
* 生成随机 userId (64字符十六进制)
|
|
29
|
+
*/
|
|
30
|
+
export declare function generateRandomUserId(): string;
|
package/core.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 核心算法 - 与 Claude Code buddy 系统完全一致的实现
|
|
3
|
+
*/
|
|
4
|
+
import { RARITIES, SPECIES, EYES, HATS, STAT_NAMES, RARITY_WEIGHTS, RARITY_FLOOR } from './types.js';
|
|
5
|
+
export { RARITY_FLOOR } from './types.js';
|
|
6
|
+
export const SALT = 'friend-2026-401';
|
|
7
|
+
/**
|
|
8
|
+
* Mulberry32 PRNG - 确定性伪随机数生成器
|
|
9
|
+
*/
|
|
10
|
+
export function mulberry32(seed) {
|
|
11
|
+
let a = seed >>> 0;
|
|
12
|
+
return function () {
|
|
13
|
+
a |= 0;
|
|
14
|
+
a = (a + 0x6d2b79f5) | 0;
|
|
15
|
+
let t = Math.imul(a ^ (a >>> 15), 1 | a);
|
|
16
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
17
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* FNV-1a hash - 字符串转数字种子
|
|
22
|
+
*/
|
|
23
|
+
export function hashString(s) {
|
|
24
|
+
let h = 2166136261;
|
|
25
|
+
for (let i = 0; i < s.length; i++) {
|
|
26
|
+
h ^= s.charCodeAt(i);
|
|
27
|
+
h = Math.imul(h, 16777619);
|
|
28
|
+
}
|
|
29
|
+
return h >>> 0;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 从数组中随机选择一个元素
|
|
33
|
+
*/
|
|
34
|
+
function pick(rng, arr) {
|
|
35
|
+
return arr[Math.floor(rng() * arr.length)];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 根据权重抽取稀有度
|
|
39
|
+
*/
|
|
40
|
+
export function rollRarity(rng) {
|
|
41
|
+
const total = Object.values(RARITY_WEIGHTS).reduce((a, b) => a + b, 0);
|
|
42
|
+
let roll = rng() * total;
|
|
43
|
+
for (const rarity of RARITIES) {
|
|
44
|
+
roll -= RARITY_WEIGHTS[rarity];
|
|
45
|
+
if (roll < 0)
|
|
46
|
+
return rarity;
|
|
47
|
+
}
|
|
48
|
+
return 'common';
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 生成属性值
|
|
52
|
+
*/
|
|
53
|
+
export function rollStats(rng, rarity) {
|
|
54
|
+
const floor = RARITY_FLOOR[rarity];
|
|
55
|
+
const peak = pick(rng, STAT_NAMES);
|
|
56
|
+
let dump = pick(rng, STAT_NAMES);
|
|
57
|
+
while (dump === peak)
|
|
58
|
+
dump = pick(rng, STAT_NAMES);
|
|
59
|
+
const stats = {};
|
|
60
|
+
for (const name of STAT_NAMES) {
|
|
61
|
+
if (name === peak) {
|
|
62
|
+
stats[name] = Math.min(100, floor + 50 + Math.floor(rng() * 30));
|
|
63
|
+
}
|
|
64
|
+
else if (name === dump) {
|
|
65
|
+
stats[name] = Math.max(1, floor - 10 + Math.floor(rng() * 15));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
stats[name] = floor + Math.floor(rng() * 40);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return stats;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 根据 userId 生成 buddy 属性
|
|
75
|
+
*/
|
|
76
|
+
export function rollBuddy(userId) {
|
|
77
|
+
const key = userId + SALT;
|
|
78
|
+
const rng = mulberry32(hashString(key));
|
|
79
|
+
const rarity = rollRarity(rng);
|
|
80
|
+
const species = pick(rng, SPECIES);
|
|
81
|
+
const eye = pick(rng, EYES);
|
|
82
|
+
const hat = rarity === 'common' ? 'none' : pick(rng, HATS);
|
|
83
|
+
const shiny = rng() < 0.01;
|
|
84
|
+
const stats = rollStats(rng, rarity);
|
|
85
|
+
return { userId, rarity, species, eye, hat, shiny, stats };
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 生成随机 userId (64字符十六进制)
|
|
89
|
+
*/
|
|
90
|
+
export function generateRandomUserId() {
|
|
91
|
+
const chars = '0123456789abcdef';
|
|
92
|
+
let userId = '';
|
|
93
|
+
for (let i = 0; i < 64; i++) {
|
|
94
|
+
userId += chars[Math.floor(Math.random() * chars.length)];
|
|
95
|
+
}
|
|
96
|
+
return userId;
|
|
97
|
+
}
|
package/index.d.ts
ADDED
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "buddy-finder-by-userid",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Claude Code Buddy 宠物属性穷举工具 - 通过穷举 userID 找到理想的 buddy 属性",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"buddy-finder": "./cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node cli.js",
|
|
12
|
+
"test": "node cli.js --test"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"claude-code",
|
|
16
|
+
"buddy",
|
|
17
|
+
"pet",
|
|
18
|
+
"finder",
|
|
19
|
+
"userID",
|
|
20
|
+
"brute-force"
|
|
21
|
+
],
|
|
22
|
+
"author": "",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18.0.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/search.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 搜索策略
|
|
3
|
+
*/
|
|
4
|
+
import { BuddyResult, SearchCriteria, SearchOptions } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* 计算匹配分数
|
|
7
|
+
*/
|
|
8
|
+
export declare function calculateScore(result: BuddyResult, criteria: SearchCriteria): number;
|
|
9
|
+
/**
|
|
10
|
+
* 检查是否满足最低要求
|
|
11
|
+
*/
|
|
12
|
+
export declare function meetsCriteria(result: BuddyResult, criteria: SearchCriteria): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 随机穷举搜索
|
|
15
|
+
*/
|
|
16
|
+
export declare function search(options: SearchOptions): BuddyResult | null;
|
|
17
|
+
/**
|
|
18
|
+
* 预设搜索配置
|
|
19
|
+
*/
|
|
20
|
+
export declare const PRESETS: {
|
|
21
|
+
readonly legendary_dragon: {
|
|
22
|
+
readonly criteria: {
|
|
23
|
+
readonly rarity: "legendary";
|
|
24
|
+
readonly species: "dragon";
|
|
25
|
+
};
|
|
26
|
+
readonly description: "传奇龙";
|
|
27
|
+
};
|
|
28
|
+
readonly legendary_dragon_shiny: {
|
|
29
|
+
readonly criteria: {
|
|
30
|
+
readonly rarity: "legendary";
|
|
31
|
+
readonly species: "dragon";
|
|
32
|
+
readonly shiny: true;
|
|
33
|
+
};
|
|
34
|
+
readonly description: "闪光传奇龙";
|
|
35
|
+
};
|
|
36
|
+
readonly legendary_dragon_wizard: {
|
|
37
|
+
readonly criteria: {
|
|
38
|
+
readonly rarity: "legendary";
|
|
39
|
+
readonly species: "dragon";
|
|
40
|
+
readonly hat: "wizard";
|
|
41
|
+
};
|
|
42
|
+
readonly description: "传奇龙 + 巫师帽";
|
|
43
|
+
};
|
|
44
|
+
readonly perfect: {
|
|
45
|
+
readonly criteria: {
|
|
46
|
+
readonly rarity: "legendary";
|
|
47
|
+
readonly species: "dragon";
|
|
48
|
+
readonly hat: "wizard";
|
|
49
|
+
readonly shiny: true;
|
|
50
|
+
readonly minStats: {
|
|
51
|
+
readonly DEBUGGING: 90;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
readonly description: "完美搭配: 闪光传奇龙 + 巫师帽 + 高DEBUGGING";
|
|
55
|
+
};
|
|
56
|
+
readonly any_legendary: {
|
|
57
|
+
readonly criteria: {
|
|
58
|
+
readonly rarity: "legendary";
|
|
59
|
+
};
|
|
60
|
+
readonly description: "任意传奇宠物";
|
|
61
|
+
};
|
|
62
|
+
readonly shiny_hunter: {
|
|
63
|
+
readonly criteria: {
|
|
64
|
+
readonly shiny: true;
|
|
65
|
+
readonly rarity: "legendary";
|
|
66
|
+
};
|
|
67
|
+
readonly description: "闪光传奇宠物";
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* 格式化输出结果
|
|
72
|
+
*/
|
|
73
|
+
export declare function formatResult(result: BuddyResult): string;
|
package/search.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 搜索策略
|
|
3
|
+
*/
|
|
4
|
+
import { rollBuddy, generateRandomUserId, RARITY_FLOOR } from './core.js';
|
|
5
|
+
/**
|
|
6
|
+
* 计算匹配分数
|
|
7
|
+
*/
|
|
8
|
+
export function calculateScore(result, criteria) {
|
|
9
|
+
let score = 0;
|
|
10
|
+
let maxScore = 0;
|
|
11
|
+
// 稀有度得分
|
|
12
|
+
maxScore += 100;
|
|
13
|
+
if (criteria.rarity && criteria.rarity !== 'any') {
|
|
14
|
+
if (result.rarity === criteria.rarity)
|
|
15
|
+
score += 100;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
score += RARITY_FLOOR[result.rarity];
|
|
19
|
+
}
|
|
20
|
+
// 物种得分
|
|
21
|
+
maxScore += 50;
|
|
22
|
+
if (criteria.species && criteria.species !== 'any') {
|
|
23
|
+
if (result.species === criteria.species)
|
|
24
|
+
score += 50;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
score += 25; // 任意物种给一半分
|
|
28
|
+
}
|
|
29
|
+
// 帽子得分
|
|
30
|
+
maxScore += 50;
|
|
31
|
+
if (criteria.hat && criteria.hat !== 'any') {
|
|
32
|
+
if (result.hat === criteria.hat)
|
|
33
|
+
score += 50;
|
|
34
|
+
}
|
|
35
|
+
else if (result.hat !== 'none') {
|
|
36
|
+
score += 25; // 有帽子但任意类型
|
|
37
|
+
}
|
|
38
|
+
// 闪光得分
|
|
39
|
+
maxScore += 30;
|
|
40
|
+
if (criteria.shiny === true) {
|
|
41
|
+
if (result.shiny)
|
|
42
|
+
score += 30;
|
|
43
|
+
}
|
|
44
|
+
else if (result.shiny) {
|
|
45
|
+
score += 15; // 闪光但不是必须
|
|
46
|
+
}
|
|
47
|
+
// 属性值得分
|
|
48
|
+
if (criteria.minStats) {
|
|
49
|
+
for (const [stat, min] of Object.entries(criteria.minStats)) {
|
|
50
|
+
maxScore += 50;
|
|
51
|
+
if (result.stats[stat] >= min) {
|
|
52
|
+
score += 50;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
score += (result.stats[stat] / min) * 50;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// 峰值属性奖励
|
|
60
|
+
if (criteria.peakStat) {
|
|
61
|
+
maxScore += 30;
|
|
62
|
+
const peak = findPeakStat(result);
|
|
63
|
+
if (peak === criteria.peakStat)
|
|
64
|
+
score += 30;
|
|
65
|
+
}
|
|
66
|
+
// 归一化得分 (0-100)
|
|
67
|
+
return Math.round((score / maxScore) * 100);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 找出峰值属性
|
|
71
|
+
*/
|
|
72
|
+
function findPeakStat(result) {
|
|
73
|
+
let max = 0;
|
|
74
|
+
let peak = 'DEBUGGING';
|
|
75
|
+
for (const [stat, value] of Object.entries(result.stats)) {
|
|
76
|
+
if (value > max) {
|
|
77
|
+
max = value;
|
|
78
|
+
peak = stat;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return peak;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 检查是否满足最低要求
|
|
85
|
+
*/
|
|
86
|
+
export function meetsCriteria(result, criteria) {
|
|
87
|
+
if (criteria.rarity && criteria.rarity !== 'any' && result.rarity !== criteria.rarity)
|
|
88
|
+
return false;
|
|
89
|
+
if (criteria.species && criteria.species !== 'any' && result.species !== criteria.species)
|
|
90
|
+
return false;
|
|
91
|
+
if (criteria.hat && criteria.hat !== 'any' && result.hat !== criteria.hat)
|
|
92
|
+
return false;
|
|
93
|
+
if (criteria.shiny !== undefined && result.shiny !== criteria.shiny)
|
|
94
|
+
return false;
|
|
95
|
+
if (criteria.minStats) {
|
|
96
|
+
for (const [stat, min] of Object.entries(criteria.minStats)) {
|
|
97
|
+
if (result.stats[stat] < min)
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 随机穷举搜索
|
|
105
|
+
*/
|
|
106
|
+
export function search(options) {
|
|
107
|
+
const { maxIterations, criteria, onProgress, onMatch } = options;
|
|
108
|
+
let bestMatch = null;
|
|
109
|
+
let bestScore = 0;
|
|
110
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
111
|
+
const userId = generateRandomUserId();
|
|
112
|
+
const result = rollBuddy(userId);
|
|
113
|
+
if (meetsCriteria(result, criteria)) {
|
|
114
|
+
const score = calculateScore(result, criteria);
|
|
115
|
+
if (score > bestScore) {
|
|
116
|
+
bestMatch = result;
|
|
117
|
+
bestScore = score;
|
|
118
|
+
onMatch?.(result, score);
|
|
119
|
+
// 完美匹配 (score >= 95) 可以提前结束
|
|
120
|
+
if (score >= 95) {
|
|
121
|
+
onProgress?.(i + 1, score);
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (i % 100000 === 0 && i > 0) {
|
|
127
|
+
onProgress?.(i, bestScore);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
onProgress?.(maxIterations, bestScore);
|
|
131
|
+
return bestMatch;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 预设搜索配置
|
|
135
|
+
*/
|
|
136
|
+
export const PRESETS = {
|
|
137
|
+
legendary_dragon: {
|
|
138
|
+
criteria: { rarity: 'legendary', species: 'dragon' },
|
|
139
|
+
description: '传奇龙'
|
|
140
|
+
},
|
|
141
|
+
legendary_dragon_shiny: {
|
|
142
|
+
criteria: { rarity: 'legendary', species: 'dragon', shiny: true },
|
|
143
|
+
description: '闪光传奇龙'
|
|
144
|
+
},
|
|
145
|
+
legendary_dragon_wizard: {
|
|
146
|
+
criteria: { rarity: 'legendary', species: 'dragon', hat: 'wizard' },
|
|
147
|
+
description: '传奇龙 + 巫师帽'
|
|
148
|
+
},
|
|
149
|
+
perfect: {
|
|
150
|
+
criteria: {
|
|
151
|
+
rarity: 'legendary',
|
|
152
|
+
species: 'dragon',
|
|
153
|
+
hat: 'wizard',
|
|
154
|
+
shiny: true,
|
|
155
|
+
minStats: { DEBUGGING: 90 }
|
|
156
|
+
},
|
|
157
|
+
description: '完美搭配: 闪光传奇龙 + 巫师帽 + 高DEBUGGING'
|
|
158
|
+
},
|
|
159
|
+
any_legendary: {
|
|
160
|
+
criteria: { rarity: 'legendary' },
|
|
161
|
+
description: '任意传奇宠物'
|
|
162
|
+
},
|
|
163
|
+
shiny_hunter: {
|
|
164
|
+
criteria: { shiny: true, rarity: 'legendary' },
|
|
165
|
+
description: '闪光传奇宠物'
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* 格式化输出结果
|
|
170
|
+
*/
|
|
171
|
+
export function formatResult(result) {
|
|
172
|
+
const lines = [
|
|
173
|
+
`userID: ${result.userId}`,
|
|
174
|
+
`稀有度: ${result.rarity} (基础属性 ${RARITY_FLOOR[result.rarity]})`,
|
|
175
|
+
`物种: ${result.species}`,
|
|
176
|
+
`眼睛: ${result.eye}`,
|
|
177
|
+
`帽子: ${result.hat}`,
|
|
178
|
+
`闪光: ${result.shiny ? '是 ★' : '否'}`,
|
|
179
|
+
'属性:',
|
|
180
|
+
...Object.entries(result.stats).map(([k, v]) => ` ${k}: ${v}`)
|
|
181
|
+
];
|
|
182
|
+
return lines.join('\n');
|
|
183
|
+
}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 类型定义
|
|
3
|
+
*/
|
|
4
|
+
export declare const RARITIES: readonly ["common", "uncommon", "rare", "epic", "legendary"];
|
|
5
|
+
export type Rarity = typeof RARITIES[number];
|
|
6
|
+
export declare const SPECIES: readonly ["duck", "goose", "blob", "cat", "dragon", "octopus", "owl", "penguin", "turtle", "snail", "ghost", "axolotl", "capybara", "cactus", "robot", "rabbit", "mushroom", "chonk"];
|
|
7
|
+
export type Species = typeof SPECIES[number];
|
|
8
|
+
export declare const EYES: readonly ["·", "✦", "×", "◉", "@", "°"];
|
|
9
|
+
export type Eye = typeof EYES[number];
|
|
10
|
+
export declare const HATS: readonly ["none", "crown", "tophat", "propeller", "halo", "wizard", "beanie", "tinyduck"];
|
|
11
|
+
export type Hat = typeof HATS[number];
|
|
12
|
+
export declare const STAT_NAMES: readonly ["DEBUGGING", "PATIENCE", "CHAOS", "WISDOM", "SNACK"];
|
|
13
|
+
export type StatName = typeof STAT_NAMES[number];
|
|
14
|
+
export declare const RARITY_WEIGHTS: Record<Rarity, number>;
|
|
15
|
+
export declare const RARITY_FLOOR: Record<Rarity, number>;
|
|
16
|
+
export interface BuddyResult {
|
|
17
|
+
userId: string;
|
|
18
|
+
rarity: Rarity;
|
|
19
|
+
species: Species;
|
|
20
|
+
eye: Eye;
|
|
21
|
+
hat: Hat;
|
|
22
|
+
shiny: boolean;
|
|
23
|
+
stats: Record<StatName, number>;
|
|
24
|
+
}
|
|
25
|
+
export interface SearchCriteria {
|
|
26
|
+
rarity?: Rarity | 'any';
|
|
27
|
+
species?: Species | 'any';
|
|
28
|
+
hat?: Hat | 'any';
|
|
29
|
+
shiny?: boolean;
|
|
30
|
+
minStats?: Partial<Record<StatName, number>>;
|
|
31
|
+
peakStat?: StatName;
|
|
32
|
+
}
|
|
33
|
+
export interface SearchOptions {
|
|
34
|
+
maxIterations: number;
|
|
35
|
+
criteria: SearchCriteria;
|
|
36
|
+
onProgress?: (iterations: number, bestScore: number) => void;
|
|
37
|
+
onMatch?: (result: BuddyResult, score: number) => void;
|
|
38
|
+
}
|
package/types.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 类型定义
|
|
3
|
+
*/
|
|
4
|
+
export const RARITIES = ['common', 'uncommon', 'rare', 'epic', 'legendary'];
|
|
5
|
+
export const SPECIES = [
|
|
6
|
+
'duck', 'goose', 'blob', 'cat', 'dragon',
|
|
7
|
+
'octopus', 'owl', 'penguin', 'turtle', 'snail',
|
|
8
|
+
'ghost', 'axolotl', 'capybara', 'cactus', 'robot',
|
|
9
|
+
'rabbit', 'mushroom', 'chonk'
|
|
10
|
+
];
|
|
11
|
+
export const EYES = ['·', '✦', '×', '◉', '@', '°'];
|
|
12
|
+
export const HATS = ['none', 'crown', 'tophat', 'propeller', 'halo', 'wizard', 'beanie', 'tinyduck'];
|
|
13
|
+
export const STAT_NAMES = ['DEBUGGING', 'PATIENCE', 'CHAOS', 'WISDOM', 'SNACK'];
|
|
14
|
+
export const RARITY_WEIGHTS = {
|
|
15
|
+
common: 60,
|
|
16
|
+
uncommon: 25,
|
|
17
|
+
rare: 10,
|
|
18
|
+
epic: 4,
|
|
19
|
+
legendary: 1
|
|
20
|
+
};
|
|
21
|
+
export const RARITY_FLOOR = {
|
|
22
|
+
common: 5,
|
|
23
|
+
uncommon: 15,
|
|
24
|
+
rare: 25,
|
|
25
|
+
epic: 35,
|
|
26
|
+
legendary: 50
|
|
27
|
+
};
|