@myclaw163/clawclaw-cli 0.6.67 → 0.6.68
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/bin/clawclaw-cli.mjs +3 -3
- package/package.json +1 -1
- package/scripts/sync-bundled-skill.mjs +1 -1
- package/skills/clawclaw/references/COMMANDS.md +4 -4
- package/skills/clawclaw/references/KNOWLEDGE.md +14 -12
- package/src/commands/config.ts +30 -30
- package/src/commands/knowledge.test.ts +4 -10
- package/src/commands/knowledge.ts +10 -39
- package/src/commands/setup/hermes.test.ts +96 -96
- package/src/commands/setup/hermes.ts +76 -76
- package/src/commands/setup/index.ts +13 -13
- package/src/commands/setup/openclaw.test.ts +114 -114
- package/src/commands/setup/openclaw.ts +147 -147
- package/src/lib/host-config-patcher.test.ts +130 -130
- package/src/lib/host-config-patcher.ts +151 -151
- package/src/lib/hub-reminder.ts +19 -19
- package/src/lib/knowledge-store.test.ts +28 -38
- package/src/lib/knowledge-store.ts +52 -57
- package/src/sdk/index.ts +2 -3
- package/src/sdk/types.ts +2 -0
- package/src/strategies/avoid-players.knowledge.md +7 -8
- package/src/strategies/avoid-players.ts +1 -1
- package/src/strategies/corpse-patrol.ts +1 -1
- package/src/strategies/game-utils.ts +29 -17
- package/src/strategies/goals/avoid-players-top.ts +3 -3
- package/src/strategies/goals/corpse-patrol-top.ts +23 -1
- package/src/strategies/goals/leaf-goal.ts +2 -0
- package/src/strategies/goals/lone-kill-task-top.ts +39 -8
- package/src/strategies/goals/normal-shrimp-top.ts +11 -11
- package/src/strategies/goals/paradise-fish-top.ts +32 -15
- package/src/strategies/goals/warrior-shrimp-top.test.ts +4 -3
- package/src/strategies/goals/warrior-shrimp-top.ts +140 -80
- package/src/strategies/kill-lone.knowledge.md +6 -9
- package/src/strategies/lone-kill-task.ts +1 -1
- package/src/strategies/paradise-fish.knowledge.md +7 -8
- package/src/strategies/paradise-fish.ts +1 -1
- package/src/strategies/shrimp-memory.knowledge.md +7 -8
- package/src/strategies/shrimp-memory.ts +1 -1
- package/src/strategies/warrior-memory.knowledge.md +9 -10
- package/src/strategies/warrior-memory.ts +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { GameState } from '../../sdk/types.js';
|
|
2
2
|
import {
|
|
3
|
+
emergencyRushDecision,
|
|
3
4
|
firstAvailableTask,
|
|
4
5
|
killCooldownSecs,
|
|
5
6
|
nonTeammatesVisible,
|
|
@@ -11,7 +12,7 @@ import { Goal } from './goal.js';
|
|
|
11
12
|
import { LoneKillCore } from './lone-kill-core.js';
|
|
12
13
|
import { LoneKillGoal } from './lone-kill-goal.js';
|
|
13
14
|
import { WanderTaskGoal } from './wander-task-goal.js';
|
|
14
|
-
import { emitLeaf, setBehavior, URGENT_GOAL_PRIORITY, WANDER_GOAL_PRIORITY } from './leaf-goal.js';
|
|
15
|
+
import { emitLeaf, EMERGENCY_GOAL_PRIORITY, setBehavior, URGENT_GOAL_PRIORITY, WANDER_GOAL_PRIORITY } from './leaf-goal.js';
|
|
15
16
|
import { corpseReportWithNonTeammate, immediateLoneKillDecision } from './crab-octopus-reflexes.js';
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -22,7 +23,9 @@ import { corpseReportWithNonTeammate, immediateLoneKillDecision } from './crab-o
|
|
|
22
23
|
* 立刻出刀;否则有尸体且附近有非队友 → 报警/靠近报警(不管尸体是谁造成的)。
|
|
23
24
|
* 1. 落单猎杀(LoneKillGoal,URGENT):core.assess() 判 hunt(刀好且有落单目标)→ 叶子 pursue() 靠近 / 出刀。
|
|
24
25
|
* 刀在冷却时 assess 返回 idle,不追不等,直接下沉到任务伪装。
|
|
25
|
-
* 2.
|
|
26
|
+
* 2. 残局抢修紧急任务(EMERGENCY,仅已知存活≤6):低于落单猎杀(击杀优先),高于伪装任务——
|
|
27
|
+
* 紧急任务超时则蟹阵营直接获胜,中立章鱼靠活到最后取胜,故残局抢着把维修做掉、掐断蟹的破坏取胜线。
|
|
28
|
+
* 3. 任务伪装 / 巡逻(WanderTaskGoal,WANDER):做已分配的伪装任务(排除当前紧急任务,那个走第 2 步),
|
|
26
29
|
* 跳过紧挨已知尸体(含看见过、已离开视野的)的任务,没任务就按房间巡逻,绝不停在原地。
|
|
27
30
|
*/
|
|
28
31
|
export class LoneKillTaskTop extends Goal {
|
|
@@ -30,7 +33,14 @@ export class LoneKillTaskTop extends Goal {
|
|
|
30
33
|
private readonly patrol = new PatrolState();
|
|
31
34
|
private readonly loneKillGoal = new LoneKillGoal(this.killCore);
|
|
32
35
|
private readonly wanderGoal = new WanderTaskGoal(
|
|
33
|
-
|
|
36
|
+
// 排除当前紧急任务:它由 tick 里的残局抢修分支按 EMERGENCY 优先级处理,不在此当普通伪装任务做。
|
|
37
|
+
(state, ctx) => firstAvailableTask(
|
|
38
|
+
ctx.taskData,
|
|
39
|
+
t => t.task_name !== ctx.emergency?.task_name,
|
|
40
|
+
undefined,
|
|
41
|
+
ctx.blockedMoveTarget,
|
|
42
|
+
ctx.knownCorpses,
|
|
43
|
+
),
|
|
34
44
|
() => this.killCore.patrolStopOnPlayer(),
|
|
35
45
|
this.patrol,
|
|
36
46
|
);
|
|
@@ -40,28 +50,49 @@ export class LoneKillTaskTop extends Goal {
|
|
|
40
50
|
}
|
|
41
51
|
|
|
42
52
|
tick(state: GameState, ctx: StrategyContext): BehaviorDecision[] {
|
|
43
|
-
this.emitProgress(state, ctx);
|
|
44
|
-
|
|
45
53
|
const immediateKill = immediateLoneKillDecision(state, ctx);
|
|
46
|
-
if (immediateKill)
|
|
54
|
+
if (immediateKill) {
|
|
55
|
+
this.emitProgress(state, ctx, false);
|
|
56
|
+
return emitLeaf(this, immediateKill, URGENT_GOAL_PRIORITY);
|
|
57
|
+
}
|
|
47
58
|
|
|
48
59
|
const corpseReport = corpseReportWithNonTeammate(state, ctx);
|
|
49
|
-
if (corpseReport)
|
|
60
|
+
if (corpseReport) {
|
|
61
|
+
this.emitProgress(state, ctx, false);
|
|
62
|
+
return emitLeaf(this, corpseReport, URGENT_GOAL_PRIORITY);
|
|
63
|
+
}
|
|
50
64
|
|
|
51
65
|
const target = this.killCore.assess(state, ctx);
|
|
52
66
|
if (target.kind === 'hunt') {
|
|
67
|
+
this.emitProgress(state, ctx, false);
|
|
53
68
|
this.loneKillGoal.setTarget(target.target);
|
|
54
69
|
return setBehavior(this, this.loneKillGoal, URGENT_GOAL_PRIORITY);
|
|
55
70
|
}
|
|
71
|
+
|
|
72
|
+
// 残局抢修紧急任务(已知存活≤6):高于伪装任务,低于上面的落单猎杀/反射。
|
|
73
|
+
const emergency = emergencyRushDecision(state, ctx);
|
|
74
|
+
if (emergency) {
|
|
75
|
+
this.emitProgress(state, ctx, true);
|
|
76
|
+
return emitLeaf(this, [emergency], EMERGENCY_GOAL_PRIORITY);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.emitProgress(state, ctx, false);
|
|
56
80
|
return setBehavior(this, this.wanderGoal, WANDER_GOAL_PRIORITY);
|
|
57
81
|
}
|
|
58
82
|
|
|
59
|
-
private emitProgress(state: GameState, ctx: StrategyContext): void {
|
|
83
|
+
private emitProgress(state: GameState, ctx: StrategyContext, emergencyRush: boolean): void {
|
|
60
84
|
const now = Date.now();
|
|
61
85
|
if (now - ctx.lastProgressNotifyAt < PROGRESS_INTERVAL_MS) return;
|
|
62
86
|
ctx.lastProgressNotifyAt = now;
|
|
63
87
|
|
|
64
88
|
const room = state.you.room ?? '未知';
|
|
89
|
+
if (emergencyRush) {
|
|
90
|
+
const name = ctx.emergency?.task_name ?? '紧急维修';
|
|
91
|
+
ctx.notifications.push(
|
|
92
|
+
`[进度] 当前在${room},残局仅剩${state.alive_count}人,抢着去做紧急任务「${name}」,阻止蟹靠破坏倒计时取胜。`,
|
|
93
|
+
);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
65
96
|
const cd = killCooldownSecs(state);
|
|
66
97
|
const killsRemaining = state.you.kills_remaining;
|
|
67
98
|
const nonTeammates = nonTeammatesVisible(state, ctx);
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
dist,
|
|
5
5
|
firstAvailableTask,
|
|
6
6
|
hasKnownCorpse,
|
|
7
|
-
|
|
7
|
+
isKnowledgeHostile,
|
|
8
8
|
isKnowledgeTrusted,
|
|
9
9
|
nonTeammatesVisible,
|
|
10
10
|
PROGRESS_INTERVAL_MS,
|
|
@@ -39,8 +39,8 @@ type Tier = 'flee-bad' | 'keep-distance' | 'task';
|
|
|
39
39
|
*
|
|
40
40
|
* - P0 发现尸体:一律报警(reportCorpseDecision),必要时提示 Agent 更新尸体旁玩家记忆。
|
|
41
41
|
* - P1 规避危险:
|
|
42
|
-
* · A
|
|
43
|
-
* · B
|
|
42
|
+
* · A 视野里有坏人(知识库 hostile)→ 寻路躲避(KeepAwayGoal,不限距离)。
|
|
43
|
+
* · B 无可信同伴且被怀疑者(未标记 = 默认被怀疑)贴近(≤270px)→ 先拉开距离(KeepAwayGoal,拉开到 300px 即回任务)。
|
|
44
44
|
* 目击者豁免:当前见到 ≥2 个陌生人时不触发后撤(蟹不敢当众动手,独处才危险);
|
|
45
45
|
* 进行中的后撤也会重新判定,只有仍是单陌生人危险时才拉满到 300px。豁免期间陌生人也不算任务威胁点。
|
|
46
46
|
* - P2 紧急任务:无危险时优先处理。
|
|
@@ -54,7 +54,7 @@ export class NormalShrimpTop extends Goal {
|
|
|
54
54
|
// 豁免期间陌生人不算威胁点(与 B 档不触发保持视野半径对齐),认定坏人始终算。
|
|
55
55
|
(s, c) => nonTeammatesVisible(s, c)
|
|
56
56
|
.filter(p => !this.isTrusted(p, c))
|
|
57
|
-
.filter(p => this.
|
|
57
|
+
.filter(p => this.isHostile(p, c) || !this.witnessExempt)
|
|
58
58
|
.map(p => ({ x: p.x, y: p.y })),
|
|
59
59
|
);
|
|
60
60
|
private readonly strangerSeenAt = new Map<string, number>();
|
|
@@ -91,10 +91,10 @@ export class NormalShrimpTop extends Goal {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
// ── P1-A 视野里有认定坏人 → 寻路躲避 ──
|
|
94
|
-
const threats = visible.filter(p => this.
|
|
94
|
+
const threats = visible.filter(p => this.isHostile(p, ctx));
|
|
95
95
|
if (threats.length > 0) {
|
|
96
96
|
this.taskGoal.planTask(state, ctx, { holdUnsafeCurrentForMs: TASK_RETURN_INERTIA_MS });
|
|
97
|
-
this.setKeepAway(FLEE_BAD_KEY, (s, c) => nonTeammatesVisible(s, c).filter(p => this.
|
|
97
|
+
this.setKeepAway(FLEE_BAD_KEY, (s, c) => nonTeammatesVisible(s, c).filter(p => this.isHostile(p, c)), {
|
|
98
98
|
threatRadius: Infinity,
|
|
99
99
|
noun: '认定坏人',
|
|
100
100
|
});
|
|
@@ -153,9 +153,9 @@ export class NormalShrimpTop extends Goal {
|
|
|
153
153
|
this.witnessExempt = this.strangerSeenAt.size >= WITNESS_EXEMPT_COUNT;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
/**
|
|
157
|
-
private
|
|
158
|
-
return
|
|
156
|
+
/** 坏人:知识库 hostile(明确敌对)。被怀疑者(未标记)不在此列,走 B 档保持距离。 */
|
|
157
|
+
private isHostile(p: PlayerInfo, ctx: StrategyContext): boolean {
|
|
158
|
+
return isKnowledgeHostile(p, ctx);
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
/** 可信同伴:知识库 trusted。 */
|
|
@@ -185,7 +185,7 @@ export class NormalShrimpTop extends Goal {
|
|
|
185
185
|
if (!key || this.corpseWitnessNotified.has(key)) continue;
|
|
186
186
|
this.corpseWitnessNotified.add(key);
|
|
187
187
|
|
|
188
|
-
if (this.
|
|
188
|
+
if (this.isHostile(player, ctx)) bad.push(player.name);
|
|
189
189
|
else if (this.isKnownParadiseFish(player, ctx)) paradise.push(player.name);
|
|
190
190
|
else if (this.isTrusted(player, ctx)) trusted.push(player.name);
|
|
191
191
|
else unknown.push(player.name);
|
|
@@ -217,7 +217,7 @@ export class NormalShrimpTop extends Goal {
|
|
|
217
217
|
|
|
218
218
|
let msg = `[进度] 当前在${room}`;
|
|
219
219
|
if (tier === 'flee-bad') {
|
|
220
|
-
const names = visible.filter(p => this.
|
|
220
|
+
const names = visible.filter(p => this.isHostile(p, ctx)).map(p => p.name).join('、');
|
|
221
221
|
msg += `,发现认定坏人${names},正在远离。`;
|
|
222
222
|
} else if (tier === 'keep-distance') {
|
|
223
223
|
msg += `,陌生人靠得太近,先拉开距离再做任务。`;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { GameState, PlayerInfo, Position } from '../../sdk/types.js';
|
|
2
2
|
import {
|
|
3
3
|
corpseAtScene,
|
|
4
|
+
emergencyRushDecision,
|
|
4
5
|
hasKnownCorpse,
|
|
5
|
-
|
|
6
|
+
isKnowledgeHostile,
|
|
6
7
|
isKnowledgeTrusted,
|
|
7
8
|
nonTeammatesVisible,
|
|
8
9
|
PatrolState,
|
|
@@ -20,7 +21,7 @@ const SUB_PRIORITY = 0.5;
|
|
|
20
21
|
const AVOID_ROUTE_MEMORY_MS = 10_000;
|
|
21
22
|
const WITNESS_EXEMPT_COUNT = 2;
|
|
22
23
|
|
|
23
|
-
type Tier = 'corpse' | 'flee-bad' | 'keep-distance' | 'huddle' | 'wander';
|
|
24
|
+
type Tier = 'emergency' | 'corpse' | 'flee-bad' | 'keep-distance' | 'huddle' | 'wander';
|
|
24
25
|
|
|
25
26
|
export class ParadiseFishTop extends Goal {
|
|
26
27
|
private readonly linger = new LingerCorpseGoal((state, ctx) => this.visibleAvoidTargets(state, ctx));
|
|
@@ -39,16 +40,16 @@ export class ParadiseFishTop extends Goal {
|
|
|
39
40
|
|
|
40
41
|
tick(state: GameState, ctx: StrategyContext): BehaviorDecision[] {
|
|
41
42
|
const visible = nonTeammatesVisible(state, ctx);
|
|
42
|
-
const threatTargets = visible.filter(p => this.
|
|
43
|
+
const threatTargets = visible.filter(p => this.isHostile(p, ctx));
|
|
43
44
|
const trustedTargets = visible.filter(p => this.isTrusted(p, ctx));
|
|
44
45
|
const strangerAvoidTargets = this.strangerAvoidTargets(visible, ctx);
|
|
45
46
|
this.rememberAvoidPoints(visible, ctx, strangerAvoidTargets.length > 0);
|
|
46
|
-
const decisions: BehaviorDecision[] = [];
|
|
47
|
-
const greeting = this.greeting.tryGreeting(state);
|
|
48
|
-
if (greeting) decisions.push(greeting);
|
|
49
47
|
|
|
50
48
|
if (threatTargets.length > 0) {
|
|
51
|
-
|
|
49
|
+
const decisions: BehaviorDecision[] = [];
|
|
50
|
+
const greeting = this.greeting.tryGreeting(state);
|
|
51
|
+
if (greeting) decisions.push(greeting);
|
|
52
|
+
this.setKeepAway('pf-flee-bad', (s, c) => nonTeammatesVisible(s, c).filter(p => this.isHostile(p, c)), {
|
|
52
53
|
threatRadius: Infinity,
|
|
53
54
|
noun: '认定带刀坏人',
|
|
54
55
|
});
|
|
@@ -56,6 +57,18 @@ export class ParadiseFishTop extends Goal {
|
|
|
56
57
|
return decisions;
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
// 残局抢修紧急任务(已知存活≤6):仅次于躲避带刀坏人,先于保持距离/贴尸/抱团。
|
|
61
|
+
const emergency = emergencyRushDecision(state, ctx);
|
|
62
|
+
if (emergency) {
|
|
63
|
+
this.clearSub();
|
|
64
|
+
this.emitProgress(state, ctx, visible, 'emergency');
|
|
65
|
+
return [emergency];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const decisions: BehaviorDecision[] = [];
|
|
69
|
+
const greeting = this.greeting.tryGreeting(state);
|
|
70
|
+
if (greeting) decisions.push(greeting);
|
|
71
|
+
|
|
59
72
|
if (strangerAvoidTargets.length > 0) {
|
|
60
73
|
this.setKeepAway('pf-keep-distance', (s, c) => this.strangerAvoidTargets(nonTeammatesVisible(s, c), c), {
|
|
61
74
|
threatRadius: Infinity,
|
|
@@ -91,8 +104,9 @@ export class ParadiseFishTop extends Goal {
|
|
|
91
104
|
return [...this.wander(state, ctx), ...decisions];
|
|
92
105
|
}
|
|
93
106
|
|
|
94
|
-
|
|
95
|
-
|
|
107
|
+
/** 坏人:知识库 hostile(明确敌对带刀者)。被怀疑者(未标记)走下面的保持距离档。 */
|
|
108
|
+
private isHostile(p: PlayerInfo, ctx: StrategyContext): boolean {
|
|
109
|
+
return isKnowledgeHostile(p, ctx);
|
|
96
110
|
}
|
|
97
111
|
|
|
98
112
|
private isTrusted(p: PlayerInfo, ctx: StrategyContext): boolean {
|
|
@@ -101,14 +115,14 @@ export class ParadiseFishTop extends Goal {
|
|
|
101
115
|
|
|
102
116
|
private strangerAvoidTargets(visible: PlayerInfo[], ctx: StrategyContext): PlayerInfo[] {
|
|
103
117
|
if (visible.some(p => this.isTrusted(p, ctx))) return [];
|
|
104
|
-
const nonBad = visible.filter(p => !this.
|
|
118
|
+
const nonBad = visible.filter(p => !this.isHostile(p, ctx));
|
|
105
119
|
if (nonBad.length >= WITNESS_EXEMPT_COUNT) return [];
|
|
106
|
-
return visible.filter(p => !this.isTrusted(p, ctx) && !this.
|
|
120
|
+
return visible.filter(p => !this.isTrusted(p, ctx) && !this.isHostile(p, ctx));
|
|
107
121
|
}
|
|
108
122
|
|
|
109
123
|
private visibleAvoidTargets(state: GameState, ctx: StrategyContext): PlayerInfo[] {
|
|
110
124
|
const visible = nonTeammatesVisible(state, ctx);
|
|
111
|
-
const threats = visible.filter(p => this.
|
|
125
|
+
const threats = visible.filter(p => this.isHostile(p, ctx));
|
|
112
126
|
return threats.length > 0 ? threats : this.strangerAvoidTargets(visible, ctx);
|
|
113
127
|
}
|
|
114
128
|
|
|
@@ -132,7 +146,7 @@ export class ParadiseFishTop extends Goal {
|
|
|
132
146
|
this.recentAvoidPoints.delete(key);
|
|
133
147
|
continue;
|
|
134
148
|
}
|
|
135
|
-
const threat = this.
|
|
149
|
+
const threat = this.isHostile(p, ctx);
|
|
136
150
|
if (!threat && !avoidNonThreat) {
|
|
137
151
|
this.recentAvoidPoints.delete(key);
|
|
138
152
|
continue;
|
|
@@ -183,7 +197,10 @@ export class ParadiseFishTop extends Goal {
|
|
|
183
197
|
|
|
184
198
|
const room = state.you.room ?? '未知';
|
|
185
199
|
let msg = `[进度] 当前在${room}`;
|
|
186
|
-
if (tier === '
|
|
200
|
+
if (tier === 'emergency') {
|
|
201
|
+
const name = ctx.emergency?.task_name ?? '紧急维修';
|
|
202
|
+
msg += `,残局仅剩${state.alive_count}人,抢着去做紧急任务「${name}」,阻止蟹靠破坏倒计时取胜。`;
|
|
203
|
+
} else if (tier === 'corpse') {
|
|
187
204
|
// corpseAtScene=已贴近尸体(≤150px);仅凭记忆走向尸体房间、人还没到时别谎报「在尸体附近游走」。
|
|
188
205
|
if (!corpseAtScene(state)) {
|
|
189
206
|
msg += ',记得有尸体,正前往尸体处(不报警)。';
|
|
@@ -193,7 +210,7 @@ export class ParadiseFishTop extends Goal {
|
|
|
193
210
|
: ',发现尸体,附近无人,贴着尸体游走制造嫌疑(不报警)。';
|
|
194
211
|
}
|
|
195
212
|
} else if (tier === 'flee-bad') {
|
|
196
|
-
const names = visible.filter(p => this.
|
|
213
|
+
const names = visible.filter(p => this.isHostile(p, ctx)).map(p => p.name).join('、');
|
|
197
214
|
msg += `,发现认定带刀坏人${names},正在远离。`;
|
|
198
215
|
} else if (tier === 'keep-distance') {
|
|
199
216
|
msg += `,附近有${visible.length}人但无可信同伴,保持距离中。`;
|
|
@@ -29,7 +29,7 @@ function state(): GameState {
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function knowledge(mark: '
|
|
32
|
+
function knowledge(mark: 'hostile' | 'trusted') {
|
|
33
33
|
const ts = Date.now();
|
|
34
34
|
const file: KnowledgeFile = {
|
|
35
35
|
version: 1,
|
|
@@ -68,10 +68,11 @@ function context(overrides: Partial<StrategyContext> = {}): StrategyContext {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
describe('WarriorShrimpTop knowledge tiers', () => {
|
|
71
|
-
it('
|
|
71
|
+
it('keeps distance from an unmarked (suspected-by-default) player even when the weapon is ready', () => {
|
|
72
72
|
const top = new WarriorShrimpTop();
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
// 不打任何标记:未标记 = 默认被怀疑 → 只保持距离,刀好也不主动出刀。
|
|
75
|
+
top.tick(state(), context());
|
|
75
76
|
|
|
76
77
|
expect(top.subGoal).toBeInstanceOf(KeepAwayGoal);
|
|
77
78
|
});
|