@myclaw163/clawclaw-cli 0.6.68 → 0.6.70
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/package.json +1 -1
- package/skills/clawclaw/references/KNOWLEDGE.md +1 -1
- package/skills/clawclaw/references/STRATEGIES.md +5 -3
- package/src/commands/game.ts +15 -0
- package/src/commands/strategy.test.ts +10 -0
- package/src/commands/strategy.ts +11 -10
- package/src/commands/watch.test.ts +11 -0
- package/src/commands/watch.ts +2 -3
- package/src/lib/auth.test.ts +15 -0
- package/src/pipeline/event-format.test.ts +82 -2
- package/src/pipeline/event-format.ts +114 -5
- package/src/pipeline/event-hints.ts +20 -3
- package/src/runtime/event-daemon.test.ts +34 -0
- package/src/runtime/event-daemon.ts +51 -3
- package/src/sdk/index.ts +1 -1
- package/src/strategies/avoid-lone.ts +1 -0
- package/src/strategies/avoid-players.ts +1 -0
- package/src/strategies/corpse-patrol.ts +1 -0
- package/src/strategies/crab-sabotage.ts +1 -0
- package/src/strategies/custom-module.test.ts +1 -0
- package/src/strategies/find-player.ts +1 -0
- package/src/strategies/game-utils.test.ts +53 -1
- package/src/strategies/game-utils.ts +69 -17
- package/src/strategies/goals/crab-octopus-reflexes.ts +11 -3
- package/src/strategies/goals/keep-away-goal.ts +9 -5
- package/src/strategies/goals/lone-kill-task-top.ts +25 -9
- package/src/strategies/goals/warrior-shrimp-top.ts +13 -306
- package/src/strategies/hide-spots.ts +11 -75
- package/src/strategies/hide.ts +1 -0
- package/src/strategies/kill-frenzy.ts +1 -0
- package/src/strategies/kill-lone.ts +1 -0
- package/src/strategies/kill-target.ts +1 -0
- package/src/strategies/loader.ts +9 -2
- package/src/strategies/lone-kill-task.ts +1 -0
- package/src/strategies/move-room.ts +1 -0
- package/src/strategies/off-route-points.ts +105 -0
- package/src/strategies/paradise-fish.ts +1 -0
- package/src/strategies/patrol.ts +1 -0
- package/src/strategies/report-patrol.ts +1 -0
- package/src/strategies/shrimp-memory.ts +1 -0
- package/src/strategies/social-task.ts +1 -0
- package/src/strategies/task-kill-report.ts +1 -0
- package/src/strategies/task-only.ts +1 -0
- package/src/strategies/task-report.ts +1 -0
- package/src/strategies/types.ts +7 -0
- package/src/strategies/warrior-memory.knowledge.md +2 -2
- package/src/strategies/warrior-memory.ts +2 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { Position } from '../sdk/types.js';
|
|
2
|
+
import { dist } from './game-utils.js';
|
|
3
|
+
import { assessRoutes } from './pathfind/escape-planner.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 离线从烘焙地图算出的「离路点」:远离任务点/出生点、避开走廊与枢纽的低人流坐标
|
|
7
|
+
* (世界像素,与 GameState / 可行走网格同坐标系)。HIDE_SPOTS(藏身角落)是它的具体集合,
|
|
8
|
+
* 共用下面的选点器。
|
|
9
|
+
*/
|
|
10
|
+
export interface OffRoutePoint extends Position {
|
|
11
|
+
/** 所在房间,仅供日志/调试。 */
|
|
12
|
+
room: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface NearestSafePointOptions<T extends Position = Position> {
|
|
16
|
+
/** 点离任一威胁多近就硬排除。 */
|
|
17
|
+
threatExcludeRadius: number;
|
|
18
|
+
/** 去该点的测地路径经过威胁这个距离内也排除(避免走向把守者)。 */
|
|
19
|
+
pathThreatRadius: number;
|
|
20
|
+
/** 当前已选点:仍是合法候选就保持不变,杜绝等距摇摆。 */
|
|
21
|
+
stickyTo?: T | null;
|
|
22
|
+
/** 被标记为不可达的移动目标:在其附近的点跳过。 */
|
|
23
|
+
blockedTarget?: Position | null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const BLOCKED_RADIUS = 12;
|
|
27
|
+
|
|
28
|
+
function samePoint(a: Position, b: Position): boolean {
|
|
29
|
+
return a.x === b.x && a.y === b.y;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** 所有点都被威胁封死时的兜底:离威胁最远的点(仍好过原地等死)。 */
|
|
33
|
+
function farthestFromThreats<T extends Position>(
|
|
34
|
+
points: readonly T[],
|
|
35
|
+
threatPoints: Position[],
|
|
36
|
+
blocked?: Position | null,
|
|
37
|
+
): T | null {
|
|
38
|
+
let best: T | null = null;
|
|
39
|
+
let bestMin = -Infinity;
|
|
40
|
+
for (const s of points) {
|
|
41
|
+
if (blocked && dist(s.x, s.y, blocked.x, blocked.y) <= BLOCKED_RADIUS) continue;
|
|
42
|
+
const minD = threatPoints.length === 0 ? 0 : Math.min(...threatPoints.map(p => dist(s.x, s.y, p.x, p.y)));
|
|
43
|
+
if (minD > bestMin) {
|
|
44
|
+
bestMin = minD;
|
|
45
|
+
best = s;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return best;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 从离线点集里选「测地最近的安全点」:威胁旁的点硬排除、去路经过威胁附近的点排除,余者取测地最近
|
|
53
|
+
* (distance-field 一次扫描;测地不可达按欧氏垫底),带粘性(当前点仍合法就不换)。全部被威胁封死
|
|
54
|
+
* 时退回「离威胁最远」的点。nearestSafeHideSpot 复用此选点器。
|
|
55
|
+
*/
|
|
56
|
+
export function nearestSafePoint<T extends Position>(
|
|
57
|
+
points: readonly T[],
|
|
58
|
+
from: Position,
|
|
59
|
+
threatPoints: Position[],
|
|
60
|
+
opts: NearestSafePointOptions<T>,
|
|
61
|
+
): T | null {
|
|
62
|
+
const excludeR = opts.threatExcludeRadius;
|
|
63
|
+
const blocked = opts.blockedTarget ?? null;
|
|
64
|
+
|
|
65
|
+
const candidates = points.filter(s =>
|
|
66
|
+
(!blocked || dist(s.x, s.y, blocked.x, blocked.y) > BLOCKED_RADIUS)
|
|
67
|
+
&& !threatPoints.some(p => dist(s.x, s.y, p.x, p.y) <= excludeR));
|
|
68
|
+
|
|
69
|
+
if (candidates.length === 0) return farthestFromThreats(points, threatPoints, blocked);
|
|
70
|
+
|
|
71
|
+
// 无威胁时粘性可零成本短路(不必为路径检查扫距离场)。
|
|
72
|
+
if (opts.stickyTo && threatPoints.length === 0) {
|
|
73
|
+
const sticky = candidates.find(c => samePoint(c, opts.stickyTo!));
|
|
74
|
+
if (sticky) return sticky;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const routes = assessRoutes(
|
|
78
|
+
from,
|
|
79
|
+
candidates.map(c => ({ x: c.x, y: c.y })),
|
|
80
|
+
threatPoints,
|
|
81
|
+
opts.pathThreatRadius,
|
|
82
|
+
);
|
|
83
|
+
const indexed = candidates.map((c, i) => ({ c, i }));
|
|
84
|
+
const viable = routes == null ? indexed : indexed.filter(({ i }) => !routes[i].nearThreat);
|
|
85
|
+
const pool = viable.length > 0 ? viable : indexed;
|
|
86
|
+
|
|
87
|
+
if (opts.stickyTo) {
|
|
88
|
+
const sticky = pool.find(({ c }) => samePoint(c, opts.stickyTo!));
|
|
89
|
+
if (sticky) return sticky.c;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 测地最近(distance-field 距离;测地不可达按欧氏垫底)。
|
|
93
|
+
let best = pool[0].c;
|
|
94
|
+
let bestScore = Infinity;
|
|
95
|
+
for (const { c, i } of pool) {
|
|
96
|
+
const euclid = dist(from.x, from.y, c.x, c.y);
|
|
97
|
+
const geo = routes?.[i].distancePx;
|
|
98
|
+
const score = geo == null ? euclid : geo !== Infinity ? geo : euclid + 1e6;
|
|
99
|
+
if (score < bestScore) {
|
|
100
|
+
bestScore = score;
|
|
101
|
+
best = c;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return best;
|
|
105
|
+
}
|
|
@@ -17,6 +17,7 @@ class ParadiseFishStrategy extends GoalRootStrategy {
|
|
|
17
17
|
|
|
18
18
|
export const strategy: StrategyEntry = {
|
|
19
19
|
id: 'paradise-fish',
|
|
20
|
+
name: '天堂鱼',
|
|
20
21
|
description:
|
|
21
22
|
'天堂鱼·进阶版(中立,不报警、不做任务,靠被投票出局取胜;corpse-patrol 的带记忆升级)。读取 ccl knowledge 两档标记(hostile=坏人 / trusted=好人),未标记的一律默认被怀疑:hostile 视为危险硬寻路远离,被怀疑者按目击者规则保持距离,trusted 视为可抱团的可信同伴。发现尸体绝不报警,贴着尸体游走制造嫌疑;附近有人时远离那人但仍在尸体附近活动。残局(已知存活≤6)出现紧急维修任务时,会在没有带刀坏人需要躲避时优先抢着去做(先于对陌生人保持距离/贴尸/抱团),阻止蟹靠破坏倒计时取胜。传入打招呼话术时,视野内出现人就随机发送一条,之后120秒内不再发言。',
|
|
22
23
|
create(args?: string[]) {
|
package/src/strategies/patrol.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { PatrolTop } from './goals/patrol-top.js';
|
|
|
4
4
|
|
|
5
5
|
export const strategy: StrategyEntry = {
|
|
6
6
|
id: 'patrol',
|
|
7
|
+
name: '巡逻',
|
|
7
8
|
description: '按地图房间顺序依次游走,到达一个房间后自动前往下一个,循环往复。卡路时会跳过当前目标换下一个房间。纯游走,不做任务、不打人、不报告尸体。',
|
|
8
9
|
create() {
|
|
9
10
|
return new GoalRootStrategy('patrol', () => new PatrolTop(), { resetOnMeetingResume: false });
|
|
@@ -4,6 +4,7 @@ import { ReportPatrolTop } from './goals/report-patrol-top.js';
|
|
|
4
4
|
|
|
5
5
|
export const strategy: StrategyEntry = {
|
|
6
6
|
id: 'report-patrol',
|
|
7
|
+
name: '报警',
|
|
7
8
|
description: '专门找尸体:达到报告距离立刻报告,看到尸体就靠近;没有尸体就巡逻各房间。不做任何任务,不打人,目标是让会议尽快开起来。',
|
|
8
9
|
create() {
|
|
9
10
|
return new GoalRootStrategy('report-patrol', () => new ReportPatrolTop(), { resetOnMeetingResume: false });
|
|
@@ -17,6 +17,7 @@ class ShrimpMemoryStrategy extends GoalRootStrategy {
|
|
|
17
17
|
|
|
18
18
|
export const strategy: StrategyEntry = {
|
|
19
19
|
id: 'shrimp-memory',
|
|
20
|
+
name: '精英虾',
|
|
20
21
|
description:
|
|
21
22
|
'普通虾·记忆进阶版(好人、不带刀;task-report 的带记忆升级)。读取 ccl knowledge 两档标记(hostile=坏人 / trusted=好人),未标记的一律默认被怀疑:hostile 硬寻路远离,trusted 视为可信同伴,被怀疑者按目击者规则保持距离。发现尸体最优先报警,并提示复核尸体旁身份不明者;无可信同伴且单个被怀疑者贴近270内时先拉开距离到300再做任务;再处理紧急任务,最后做任务/巡逻(选测地最近、跳过威胁旁或必经威胁的任务)。永不出刀。传入打招呼话术时,视野内出现人就随机发送一条,之后120秒内不再发言。',
|
|
22
23
|
create(args?: string[]) {
|
|
@@ -33,6 +33,7 @@ export function parseSocialArgs(args: string[]): SocialTarget[] {
|
|
|
33
33
|
|
|
34
34
|
export const strategy: StrategyEntry = {
|
|
35
35
|
id: 'social-task',
|
|
36
|
+
name: '社交',
|
|
36
37
|
description: '针对指定玩家社交(参数格式 座位=话术 或纯座位,可多人)。视野内出现目标就靠近,没打过招呼先发一次招呼;对方说话就停下等你回复,10秒没回应就转去做任务。全程见到可报告的尸体就报、见到尸体就靠近。不打人。参数:座位=话术 / 座位(可多人)。',
|
|
37
38
|
create(args?: string[]) {
|
|
38
39
|
if (!args || args.length === 0) {
|
|
@@ -4,6 +4,7 @@ import { TaskKillReportTop } from './goals/task-kill-report-top.js';
|
|
|
4
4
|
|
|
5
5
|
export const strategy: StrategyEntry = {
|
|
6
6
|
id: 'task-kill-report',
|
|
7
|
+
name: '任务刺客',
|
|
7
8
|
description: '需传入嫌疑目标(座位号或名字,可多人)。看到目标且冷却好,50内就出刀,否则追上去;冷却中贴身跟随。目标不在视野时,遇到尸体就报告;没尸体就做普通任务。',
|
|
8
9
|
create(args?: string[]) {
|
|
9
10
|
if (!args || args.length === 0) {
|
|
@@ -4,6 +4,7 @@ import { TaskOnlyTop } from './goals/task-only-top.js';
|
|
|
4
4
|
|
|
5
5
|
export const strategy: StrategyEntry = {
|
|
6
6
|
id: 'task-only',
|
|
7
|
+
name: '纯任务',
|
|
7
8
|
description: '专心做任务。紧急任务出现时优先处理,其次按顺序完成普通任务,如果是蟹,则蟹的伪装任务也会做。没有尸体报告和战斗行为。',
|
|
8
9
|
create() {
|
|
9
10
|
return new GoalRootStrategy('task-only', () => new TaskOnlyTop(), { resetOnMeetingResume: false });
|
|
@@ -15,6 +15,7 @@ class TaskReportStrategy extends GoalRootStrategy {
|
|
|
15
15
|
|
|
16
16
|
export const strategy: StrategyEntry = {
|
|
17
17
|
id: 'task-report',
|
|
18
|
+
name: '尽责虾',
|
|
18
19
|
description: '遇尸体最优先:达到报告距离立刻报告,没达到距离就靠近准备报告。紧急任务出现时优先处理,其次按顺序完成普通任务。传入打招呼话术时,视野内出现人就随机发送一条,之后120秒内不再发言。(普通虾默认;进阶版见 shrimp-memory)参数:可选:1~3 条打招呼话术。',
|
|
19
20
|
create(args?: string[]) {
|
|
20
21
|
return new TaskReportStrategy(parseGreetingArgs(args, 'task-report'));
|
package/src/strategies/types.ts
CHANGED
|
@@ -45,6 +45,13 @@ export interface Strategy {
|
|
|
45
45
|
|
|
46
46
|
export interface StrategyEntry {
|
|
47
47
|
readonly id: string;
|
|
48
|
+
/**
|
|
49
|
+
* 中文展示名/代词,玩家与 agent 沟通时使用(如「守尸」「武士虾」)。会出现在
|
|
50
|
+
* `ccl strategy --list`/`--info` 输出里,并可作为 `ccl strategy <名>` 的输入。
|
|
51
|
+
* 注意:这与 `Strategy.name`(`create()` 返回对象的内部 id 名)不是一回事。
|
|
52
|
+
* 须全局唯一。
|
|
53
|
+
*/
|
|
54
|
+
readonly name: string;
|
|
48
55
|
readonly description: string;
|
|
49
56
|
create(args?: string[]): Strategy;
|
|
50
57
|
}
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|------|------|
|
|
7
7
|
| `hostile`(坏人) | 敌对:刀可用时主动追杀;刀冷却或枪虾无次数时硬回避 |
|
|
8
8
|
| `trusted`(好人) | 可信:绝不攻击,也不会因其在场而对被怀疑者后撤 |
|
|
9
|
-
| 未标记(被怀疑) |
|
|
9
|
+
| 未标记(被怀疑) | 默认回避观察,刀可用也**绝不出刀**(无自卫例外) |
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
**不对被怀疑者出刀(无自卫机制)**:武士虾只对【已确认 hostile / 启动猎杀目标】出刀。历史上的「被怀疑者持续贴身追击 → 诱饵死角试探 → 绝境自卫先手」整套已删除:几何上无法把「贴身跟我观察/盯防的好人」与「贴身追我的凶手」区分开(两者跟随轨迹一样),自卫出刀迟早误杀同阵营好人、触发 warrior_shrimp_self_destruct 直接崩盘,代价远大于收益。被怀疑者贴身追来也只回避(拉开距离 / 借人多互为目击掩护),绝不反杀。
|
|
12
12
|
|
|
13
13
|
被怀疑者(未标记)平时按陌生人处理:不主动猎杀;无 trusted 同伴且单个被怀疑者贴近 270px 内时先拉开距离,多个互为目击者时照常做任务;坏人刀冷时硬躲所有非好人。尸体场景对【未报警的身份不明者】只报警不灭口:站在尸体旁即便身边有被怀疑者,也绝不先手击杀,一律靠近报警;但若尸体旁是 hostile 或启动猎杀目标、刀可用,仍会现场先手击杀(凶手就在身边照杀)。
|
|
14
14
|
|
|
@@ -5,8 +5,9 @@ import { parseTargetArgs } from './player-targets.js';
|
|
|
5
5
|
|
|
6
6
|
export const strategy: StrategyEntry = {
|
|
7
7
|
id: 'warrior-memory',
|
|
8
|
+
name: '武士虾',
|
|
8
9
|
description:
|
|
9
|
-
'带刀虾·记忆进阶版(武士虾/枪虾通用,好人带刀;kill-lone 的带记忆升级)。读取 ccl knowledge 两档标记(hostile=坏人 / trusted=好人),未标记的一律默认被怀疑:hostile 刀好时主动追杀、刀不好时硬回避,trusted
|
|
10
|
+
'带刀虾·记忆进阶版(武士虾/枪虾通用,好人带刀;kill-lone 的带记忆升级)。读取 ccl knowledge 两档标记(hostile=坏人 / trusted=好人),未标记的一律默认被怀疑:hostile 刀好时主动追杀、刀不好时硬回避,trusted 视为可信同伴且绝不攻击。**只对【已确认 hostile / 启动猎杀目标】出刀**——被怀疑者一律只回避、绝不出刀(无可信同伴且单个被怀疑者贴近270内时先拉开到300再做任务,多人互为目击者则照常做任务);不再有任何贴身追击判定/诱饵试探/绝境自卫(几何上分不开"贴身跟我观察的好人"和"贴身追我的凶手",自卫迟早误杀同阵营好人触发武士虾自爆,整套已删除)。发现尸体优先报警;尸体旁若是 hostile/启动猎杀目标且刀可用会现场先手击杀,但绝不会因尸体旁有未报警的身份不明者就灭口(已根治该误杀);无危险时优先紧急任务,否则做任务/巡逻。枪虾会尊重剩余出刀次数。',
|
|
10
11
|
create(args?: string[]) {
|
|
11
12
|
const huntTargets = args ? parseTargetArgs(args) : [];
|
|
12
13
|
return new GoalRootStrategy('warrior-memory', () => new WarriorShrimpTop(huntTargets), {
|