poi-plugin-compass 0.1.2 → 0.1.3

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/docs/research.md CHANGED
@@ -131,7 +131,7 @@ const label = maps?.[mapKey]?.route?.[Number(cell)]?.[1]
131
131
 
132
132
  无法确定“约 80%”是否为长期规则、样本估计还是攻略近似时,应使用 `approximate` 或 `unknown`,并把原文保留到审阅队列。
133
133
 
134
- 本轮已将通常海域 36 张 `/{带路条件}` 子页以及 5-6 主页面的原始 wikitext 缓存到 `data/sources/kcwiki/routes/`,并在 `manifest.json` 中记录 revision ID、修订时间和 SHA-256。规则校正以这批缓存为审阅入口:`CV系`/`CV*` 归入 `CV_ALL`,明确的 `CV+CVB` 归入 `CV_MAIN`;`CL系`、`CL+CT`、`BB系` 和单独 `BB` 也分别使用不同令牌,避免把 CVL、CT 或 BBV 静默漏掉或误计入。
134
+ 本轮已将通常海域 36 张 `/{带路条件}` 子页以及 5-6 主页面的原始 wikitext 缓存到 `data/sources/kcwiki/routes/`,并在 `manifest.json` 中记录 revision ID、修订时间和 SHA-256。规则校正以这批缓存为审阅入口:`CA系`/`CA+CAV` 归入 `CA_ALL`,`CV系`/`CV*` 归入 `CV_ALL`,明确的 `CV+CVB` 归入 `CV_MAIN`;`CL系`、`CL+CT`、`BB系` 和单独 `BB` 也分别使用不同令牌。`CV_ALL` 是 CV+CVB+CVL,`CV_MAIN` 只含 CV+CVB;`CL_ALL` 是 CL+CLT+CT;`BB_ALL` 是 BB+FBB+BBV,而单独 `BB` 只含 BB+FBB,避免把 CVL、CT、BBVCAV 静默漏掉或误计入。
135
135
 
136
136
  ### 4.2 日文 Wiki
137
137
 
@@ -247,6 +247,8 @@ interface RouteRule {
247
247
  to: NodeLabel
248
248
  probability: number | 'unknown'
249
249
  }>
250
+ // 原文明确表示本次概率判定失败后继续检查后续规则
251
+ continueOnFailure?: boolean
250
252
  appliesWhen?: { difficulty?: string; gauge?: number; phase?: number }
251
253
  sourceRefs: SourceRef[]
252
254
  confidence: 'exact' | 'verified' | 'parsed' | 'approximate' | 'unknown'
@@ -287,11 +289,11 @@ interface EnemyComposition {
287
289
 
288
290
  1. 按 `priority` 排序;
289
291
  2. 依次求值规则的 predicate;
290
- 3. 取第一个确定匹配的规则;
291
- 4. 若匹配规则产生多个出口,使用其明确权重;
292
- 5. 若没有规则或规则含未知状态,返回 `unknown` 并附上原因。
292
+ 3. 对命中的规则按其出口概率消费当前剩余概率质量;
293
+ 4. 若规则标记 `continueOnFailure`,或已知出口概率之和小于 1,则只消费已知命中概率,把失败的剩余质量交给同节点后续规则;
294
+ 5. 没有后续规则时,才将明确的未知随机出口均分并标记估算;predicate 本身无法判断时返回 `unknown`,不越过该条件猜测后续规则。
293
295
 
294
- 这里的“第一个”不是实现者猜测的优先级,而必须来自页面表格顺序或人工标注的 `priority`。
296
+ 因此页面中的“第一条规则”只是在没有续判语义时消耗全部质量;`4-3 H`、`2-3 J`、`4-5 Q` 等节点会把多个命中规则组合成复合概率,而不是把前一条的失败质量强行并入另一个出口。
295
297
 
296
298
  ### 6.2 概率质量传播
297
299
 
package/docs/sources.md CHANGED
@@ -82,4 +82,4 @@ data/sources/kcwiki/routes/manifest.json
82
82
 
83
83
  `1-1`~`1-6`、`2-1`~`2-5`、`3-1`~`3-5`、`4-1`~`4-5`、`5-1`~`5-6`、`6-1`~`6-5`、`7-1`~`7-5`。5-6 的路线分支直接记录在海域主页面 `南方海域/5-6`,因此缓存脚本对该图使用主页面标题,而其他通常图继续使用 `/{带路条件}` 子页。
84
84
 
85
- 其中带有“约”“随机但未给比例”“原文带问号”或依赖分歧点系数/司令部系数的条件,使用 `approximate` 或 `unknown`;这类记录仍提供可能出口,但不产生伪造的全局百分比。后续继续补录时,先以同一 API 子页取得规则原文,再用日文 Wiki 或浏览器中的 NGA 页面复核有冲突的条件。
85
+ 其中带有“约”“随机但未给比例”“原文带问号”或依赖分歧点系数/司令部系数的条件,使用 `approximate` 或 `unknown`;这类记录仍提供可能出口,但不产生伪造的全局百分比。原文明确写出“判定失败后继续”“继续下面判定”或“逐条依次判定”时,规则会保留已知命中概率并设置 `continueOnFailure`,将剩余概率交给同节点后续规则。当前临时估计约定为:未给出比例的两个出口按 50%/50% 并标记 `?`;索敌区间内“概率随索敌变化”的两个出口也按 50%/50% 并标记 `?`;只有一个出口且明确表示失败后继续判定时,该出口按 50% 并把剩余 50% 交给后续规则。固定单出口仍按 100% 处理,但保留未知/近似标记。规则面板对 KCWiki 原文中的多条同向条件逐行显示,评估器仍按来源顺序处理。7-3 A 点曾将去 B 和去 C 的条件错误合并,已按缓存原文顺序拆开并修正出口。后续继续补录时,先以同一 API 子页取得规则原文,再用日文 Wiki 或浏览器中的 NGA 页面复核有冲突的条件。
package/index.js CHANGED
@@ -404,6 +404,58 @@ function outcomeText(outcome) {
404
404
  return `${outcome.to} ${formatPercent(outcome.probability, outcome.estimated)}`
405
405
  }
406
406
 
407
+ // These predicates came from separate list items in the cached KCWiki route
408
+ // tables. Keep the evaluator's compact OR semantics for compatibility, but
409
+ // render each source line separately so the UI does not invent one combined
410
+ // rule where the source did not have one.
411
+ const SEPARATE_SOURCE_LINE_RULE_IDS = new Set([
412
+ '2-5-C-heavy',
413
+ '1-5-F-heavy',
414
+ '1-6-start-heavy', '1-6-M-heavy',
415
+ '2-3-D-special',
416
+ '4-2-C-light-l',
417
+ '6-1-start-heavy3',
418
+ '6-2-C-a', '6-2-D-f', '6-2-E-f',
419
+ '6-4-start-right-lha', '6-4-A-noakitsu-heavy', '6-4-E-special', '6-4-E-g', '6-4-J-l', '6-4-J-i', '6-4-K-h',
420
+ '6-5-start-left', '6-5-C-e', '6-5-E-i', '6-5-I-h',
421
+ '7-1-B-a', '7-1-H-k',
422
+ '7-2-C-d', '7-2-E-g', '7-2-I-j',
423
+ '7-3-C-heavy', '7-3-G-special', '7-3-I-heavy', '7-3-J-m', '7-3-J-p', '7-3-M-n', '7-3-M-o',
424
+ '7-4-start-c', '7-4-start-a', '7-4-C-d', '7-4-J-k', '7-4-J-l', '7-4-M-heavy',
425
+ '7-5-B-c', '7-5-D-e', '7-5-D-fast', '7-5-J-N', '7-5-J-O-heavy', '7-5-P-r',
426
+ ])
427
+
428
+ function visibleRuleLabel(rule) {
429
+ const label = predicateLabel(rule?.predicate)
430
+ return label
431
+ .replace(/([^)]*(?:近似|约|原文|比例未知|分歧系数)[^)]*)/g, '')
432
+ .replace(/\([^)]*(?:approx|about|original|unknown|coefficient)[^)]*\)/gi, '')
433
+ .replace(/,?\s*失败后继续判定/g, '')
434
+ .replace(/:\s*[^,;:]+概率(?:未知|随索敌变化)/g, '')
435
+ .replace(/;\s*(?:近似|约)/g, '')
436
+ .replace(/(原文(?:标记\s*\?|比例未知)[^)]*)/g, '')
437
+ .replace(/(约)/g, '')
438
+ .replace(/\s{2,}/g, ' ')
439
+ .replace(/:\s*$/g, '')
440
+ .trim()
441
+ }
442
+
443
+ function rulePopoverNotes(rule, rawLabel, visibleLabel) {
444
+ const notes = []
445
+ if (rawLabel !== visibleLabel) notes.push(`原文补充:${rawLabel}`)
446
+ if (rule?.continueOnFailure === true) notes.push('本条判定失败后继续检查后续规则')
447
+ if (rule?.confidence === 'approximate') notes.push('该条件或概率来自近似资料')
448
+ if (rule?.confidence === 'unknown' || rule?.outcomes?.some((outcome) => outcome?.probability == null)) {
449
+ notes.push('来源没有给出明确概率;当前按均等概率暂估')
450
+ }
451
+ return notes
452
+ }
453
+
454
+ function displayPredicatesForRule(rule) {
455
+ if (!SEPARATE_SOURCE_LINE_RULE_IDS.has(rule?.id) || rule?.predicate?.kind !== 'any') return [rule?.predicate]
456
+ return rule.predicate.predicates?.length ? rule.predicate.predicates : [rule.predicate]
457
+ }
458
+
407
459
  function conditionIcon(status, active) {
408
460
  if (active) return '✓'
409
461
  if (status === 'true') return '•'
@@ -423,11 +475,16 @@ function ruleRows(mapDefinition, node, context) {
423
475
  return (mapDefinition.rules || [])
424
476
  .filter((rule) => rule.node === node)
425
477
  .sort((left, right) => number(left.priority) - number(right.priority))
426
- .map((rule) => ({
427
- rule,
428
- result: evaluatePredicate(rule.predicate, context),
429
- outcomes: normalizeOutcomes(rule.outcomes, rule),
430
- }))
478
+ .reduce((rows, rule) => {
479
+ displayPredicatesForRule(rule).forEach((predicate, index) => {
480
+ rows.push({
481
+ rule: { ...rule, id: `${rule.id}::${index}`, predicate, sourceRuleId: rule.id },
482
+ result: evaluatePredicate(predicate, context),
483
+ outcomes: normalizeOutcomes(rule.outcomes, rule),
484
+ })
485
+ })
486
+ return rows
487
+ }, [])
431
488
  }
432
489
 
433
490
  function fleetSummary(context) {
@@ -613,10 +670,10 @@ class Compass extends React.Component {
613
670
  `制空 ${air.min === air.max ? air.min : `${air.min}~${air.max}`}`)
614
671
  }
615
672
 
616
- setManualOverride(node, to, edgeId = null) {
673
+ setManualOverride(node, to, edgeId = null, selectedNode = node) {
617
674
  this.setState((state) => ({
618
675
  overrides: { ...state.overrides, [node]: to },
619
- selectedNode: node,
676
+ selectedNode,
620
677
  selectedEdge: edgeId,
621
678
  exportRoute: null,
622
679
  exportError: null,
@@ -747,7 +804,7 @@ class Compass extends React.Component {
747
804
  const activateNode = (event) => {
748
805
  if (event.key !== 'Enter' && event.key !== ' ') return
749
806
  event.preventDefault()
750
- if (manualTarget) this.setManualOverride(manualSource, node)
807
+ if (manualTarget) this.setManualOverride(manualSource, node, null, node)
751
808
  else this.selectNode(node)
752
809
  }
753
810
  return h('g', {
@@ -755,7 +812,7 @@ class Compass extends React.Component {
755
812
  className: classNames,
756
813
  role: 'button',
757
814
  tabIndex: 0,
758
- onClick: () => manualTarget ? this.setManualOverride(manualSource, node) : this.selectNode(node),
815
+ onClick: () => manualTarget ? this.setManualOverride(manualSource, node, null, node) : this.selectNode(node),
759
816
  onKeyDown: activateNode,
760
817
  },
761
818
  h('title', null, nodeInfo?.label ? `${node} · ${nodeInfo.label}` : node),
@@ -960,22 +1017,31 @@ class Compass extends React.Component {
960
1017
  h('h4', null, `${node} 点`),
961
1018
  rows.length
962
1019
  ? h('div', { className: 'compass-conditions' }, ...rows.map(({ rule, result, outcomes: ruleOutcomes }) => {
963
- const active = decision?.rule?.id === rule.id
964
- const displayedOutcomes = active ? decision.outcomes : ruleOutcomes
1020
+ const sourceRuleId = rule.sourceRuleId || rule.id
1021
+ const sourceWasMatched = decision?.matchedRuleIds?.includes(sourceRuleId) || decision?.rule?.id === sourceRuleId
1022
+ const splitSourceRule = SEPARATE_SOURCE_LINE_RULE_IDS.has(sourceRuleId)
1023
+ const active = splitSourceRule ? sourceWasMatched && result.status === 'true' : sourceWasMatched
1024
+ const displayedOutcomes = active && (decision?.matchedRuleIds?.length || 0) <= 1
1025
+ ? decision.outcomes
1026
+ : ruleOutcomes
965
1027
  const outcomes = displayedOutcomes.length ? displayedOutcomes.map(outcomeText).join(' / ') : '—'
1028
+ const rawLabel = predicateLabel(rule.predicate)
1029
+ const label = visibleRuleLabel(rule)
1030
+ const notes = rulePopoverNotes(rule, rawLabel, label)
966
1031
  return h('div', {
967
1032
  key: rule.id,
968
1033
  className: `compass-condition is-${result.status}${active ? ' is-active' : ''}`,
969
1034
  tabIndex: 0,
970
- 'aria-label': `${conditionStatus(result.status)}:${predicateLabel(rule.predicate)}`,
1035
+ 'aria-label': `${conditionStatus(result.status)}:${label}`,
971
1036
  },
972
1037
  h('span', { className: 'compass-condition-icon', 'aria-hidden': 'true' }, conditionIcon(result.status, active)),
973
- h('span', { className: 'compass-condition-label' }, predicateLabel(rule.predicate)),
1038
+ h('span', { className: 'compass-condition-label' }, label),
974
1039
  h('span', { className: 'compass-condition-outcomes' }, outcomes),
975
1040
  h('div', { className: 'compass-condition-popover', role: 'tooltip' },
976
1041
  h('div', null, `${conditionStatus(result.status)}${active ? ' · 当前出口规则' : ''}`),
1042
+ h('div', null, `条件:${rawLabel}`),
977
1043
  h('div', null, `出口:${outcomes}`),
978
- rule.confidence === 'approximate' ? h('div', null, '概率为资料中的近似值') : null,
1044
+ ...notes.map((note, index) => h('div', { key: `${rule.id}-note-${index}`, className: 'compass-condition-note' }, note)),
979
1045
  ),
980
1046
  )
981
1047
  }))
@@ -1100,4 +1166,7 @@ exports.__test = {
1100
1166
  phaseOptionsForMap,
1101
1167
  pluginState,
1102
1168
  routeEdges,
1169
+ ruleRows,
1170
+ visibleRuleLabel,
1171
+ separateSourceLineRuleIds: SEPARATE_SOURCE_LINE_RULE_IDS,
1103
1172
  }
package/logic.js CHANGED
@@ -9,19 +9,20 @@ const SHIP_CATEGORIES = {
9
9
  CL_ALL: new Set([3, 4, 21]),
10
10
  CA: new Set([5]),
11
11
  CAV: new Set([6]),
12
+ CA_ALL: new Set([5, 6]),
12
13
  CVL: new Set([7]),
13
14
  BB: new Set([8, 9]),
14
15
  BBV: new Set([10]),
15
16
  BB_ALL: new Set([8, 9, 10]),
16
- CV: new Set([11, 12, 18]),
17
- CV_ALL: new Set([7, 11, 12, 18]),
17
+ CV: new Set([11, 12]),
18
+ CV_ALL: new Set([7, 11, 12]),
18
19
  SS: new Set([13, 14]),
19
20
  AO: new Set([15]),
20
21
  AV: new Set([16]),
21
22
  LHA: new Set([17]),
22
23
  AR: new Set([19]),
23
24
  AS: new Set([20]),
24
- CV_MAIN: new Set([11, 12, 18]),
25
+ CV_MAIN: new Set([11, 12]),
25
26
  }
26
27
 
27
28
  const CATEGORY_LABELS = {
@@ -33,6 +34,7 @@ const CATEGORY_LABELS = {
33
34
  CL_ALL: 'CL系',
34
35
  CA: '重巡',
35
36
  CAV: '航巡',
37
+ CA_ALL: 'CA系',
36
38
  CVL: '轻空母',
37
39
  BB: '战舰(BB/FBB)',
38
40
  BBV: '航战',
@@ -45,7 +47,7 @@ const CATEGORY_LABELS = {
45
47
  LHA: '扬陆',
46
48
  AR: '工作舰',
47
49
  AS: '潜母',
48
- CV_MAIN: '正规/装甲空母',
50
+ CV_MAIN: 'CV/CVB',
49
51
  }
50
52
 
51
53
  const EQUIPMENT_CATEGORY_LABELS = {
@@ -313,17 +315,152 @@ function normalizeOutcomes(outcomes, rule = null, options = {}) {
313
315
  : Number.isFinite(Number(outcome.probability)) ? Number(outcome.probability) : null,
314
316
  }))
315
317
 
318
+ if (!normalized.length) return []
319
+ const hasKnownProbability = normalized.some((outcome) => outcome.probability != null)
316
320
  const hasMissingProbability = normalized.some((outcome) => outcome.probability == null)
317
- const equalize = options.equalizeUnknown !== false && (hasMissingProbability || rule?.confidence === 'unknown')
318
- const estimated = equalize || rule?.confidence === 'approximate' && normalized.length > 1
319
- return normalized.map((outcome) => {
321
+ const allUnknown = !hasKnownProbability
322
+ if (rule?.continueOnFailure === true) {
323
+ if (!hasKnownProbability) {
324
+ // A rule such as “within this LOS range, go to N; if the check fails,
325
+ // continue below” has two effective branches. The source does not give
326
+ // a ratio, so expose the same temporary 50/50 estimate used by the
327
+ // route evaluator instead of showing an unhelpful “unknown” row.
328
+ const probability = 1 / (normalized.length + 1)
329
+ return normalized.map((outcome) => ({ to: outcome.to, probability, estimated: true }))
330
+ }
331
+ const estimated = rule.confidence === 'unknown' || rule.confidence === 'approximate'
332
+ return normalized
333
+ .filter((outcome) => outcome.probability != null)
334
+ .map((outcome) => ({
335
+ to: outcome.to,
336
+ probability: outcome.probability,
337
+ ...(estimated ? { estimated: true } : {}),
338
+ }))
339
+ }
340
+ if (allUnknown && options.equalizeUnknown === false) {
341
+ return normalized.map((outcome) => ({ to: outcome.to, probability: null }))
342
+ }
343
+ const equalize = options.equalizeUnknown !== false && (
344
+ allUnknown || !hasMissingProbability && rule?.confidence === 'unknown'
345
+ )
346
+ const estimated = equalize || hasMissingProbability || rule?.confidence === 'unknown'
347
+ || rule?.confidence === 'approximate' && normalized.length > 1
348
+ return normalized
349
+ .filter((outcome) => equalize || outcome.probability != null)
350
+ .map((outcome) => {
320
351
  const normalizedOutcome = {
321
352
  to: outcome.to,
322
353
  probability: equalize && normalized.length ? 1 / normalized.length : outcome.probability,
323
354
  }
324
355
  if (estimated) normalizedOutcome.estimated = true
325
356
  return normalizedOutcome
326
- })
357
+ })
358
+ }
359
+
360
+ function ruleDistribution(rule) {
361
+ const raw = (Array.isArray(rule?.outcomes) ? rule.outcomes : [])
362
+ .filter((outcome) => outcome && typeof outcome.to === 'string')
363
+ .map((outcome) => ({
364
+ to: outcome.to,
365
+ probability: outcome.probability == null
366
+ ? null
367
+ : Number.isFinite(Number(outcome.probability)) ? Number(outcome.probability) : null,
368
+ }))
369
+ if (!raw.length) return { outcomes: [], residual: rule?.continueOnFailure === true ? 1 : 0 }
370
+
371
+ const known = raw.filter((outcome) => outcome.probability != null)
372
+ const knownTotal = known.reduce((total, outcome) => total + outcome.probability, 0)
373
+ const partial = rule?.continueOnFailure === true
374
+ || (known.length > 0 && knownTotal < 1 - 1e-9)
375
+ || (known.length > 0 && known.length < raw.length)
376
+
377
+ if (partial) {
378
+ if (known.length) {
379
+ return {
380
+ outcomes: normalizeOutcomes(raw, rule),
381
+ residual: Math.max(0, 1 - knownTotal),
382
+ }
383
+ }
384
+ const probability = 1 / (raw.length + 1)
385
+ return {
386
+ outcomes: raw.map((outcome) => ({ to: outcome.to, probability, estimated: true })),
387
+ residual: probability,
388
+ }
389
+ }
390
+
391
+ return { outcomes: normalizeOutcomes(raw, rule), residual: 0 }
392
+ }
393
+
394
+ function evaluateRouteRules(mapDefinition, node, context) {
395
+ const rules = rulesForNode(mapDefinition, node)
396
+ if (!rules.length) {
397
+ return { status: 'terminal', rule: null, predicate: null, matchedRuleIds: [], outcomes: [], unknownProbability: 0 }
398
+ }
399
+
400
+ const totals = new Map()
401
+ const matchedRuleIds = []
402
+ let residual = 1
403
+ let firstRule = null
404
+ let firstPredicate = null
405
+ let unresolvedPredicate = null
406
+ let terminal = false
407
+
408
+ function addOutcome(outcome, mass) {
409
+ if (!Number.isFinite(mass) || mass <= 0) return
410
+ const current = totals.get(outcome.to)
411
+ if (!current) {
412
+ totals.set(outcome.to, {
413
+ to: outcome.to,
414
+ probability: mass,
415
+ ...(outcome.estimated === true ? { estimated: true } : {}),
416
+ })
417
+ return
418
+ }
419
+ current.probability += mass
420
+ if (outcome.estimated === true) current.estimated = true
421
+ }
422
+
423
+ for (const rule of rules) {
424
+ if (residual <= 1e-9) break
425
+ const predicate = evaluatePredicate(rule.predicate, context)
426
+ if (predicate.status === 'false') continue
427
+ if (predicate.status === 'unknown') {
428
+ unresolvedPredicate = { rule, predicate }
429
+ break
430
+ }
431
+
432
+ if (!firstRule) {
433
+ firstRule = rule
434
+ firstPredicate = predicate
435
+ }
436
+ if (rule.id) matchedRuleIds.push(rule.id)
437
+ if (rule.terminal === true) {
438
+ residual = 0
439
+ terminal = true
440
+ break
441
+ }
442
+ const distribution = ruleDistribution(rule)
443
+ if (!distribution.outcomes.length) {
444
+ unresolvedPredicate = { rule, predicate }
445
+ break
446
+ }
447
+ distribution.outcomes.forEach((outcome) => addOutcome(outcome, residual * outcome.probability))
448
+ residual *= distribution.residual
449
+ }
450
+
451
+ const outcomes = [...totals.values()]
452
+ const unknownProbability = (unresolvedPredicate || residual > 1e-9) ? residual : 0
453
+ const status = unknownProbability > 1e-9
454
+ ? 'unknown'
455
+ : outcomes.length ? 'matched' : terminal ? 'terminal' : 'unknown'
456
+ return {
457
+ status,
458
+ rule: firstRule || unresolvedPredicate?.rule || null,
459
+ predicate: firstPredicate || unresolvedPredicate?.predicate || null,
460
+ matchedRuleIds,
461
+ outcomes,
462
+ unknownProbability,
463
+ }
327
464
  }
328
465
 
329
466
  function routeDecision(mapDefinition, node, context, overrides = {}) {
@@ -334,9 +471,17 @@ function routeDecision(mapDefinition, node, context, overrides = {}) {
334
471
  const manualOutcomes = Array.isArray(configuredManualOutcomes)
335
472
  ? normalizeOutcomes(configuredManualOutcomes, { confidence: 'unknown' }, { equalizeUnknown: false })
336
473
  : null
337
- const autoOutcomes = selected.status === 'matched'
338
- ? normalizeOutcomes(selected.rule?.outcomes, selected.rule)
339
- : []
474
+ const automatic = manual
475
+ ? {
476
+ status: selected.status,
477
+ rule: selected.rule,
478
+ predicate: selected.predicate,
479
+ matchedRuleIds: selected.rule?.id ? [selected.rule.id] : [],
480
+ outcomes: selected.status === 'matched' ? normalizeOutcomes(selected.rule?.outcomes, selected.rule) : [],
481
+ unknownProbability: 0,
482
+ }
483
+ : evaluateRouteRules(mapDefinition, node, context)
484
+ const autoOutcomes = automatic.outcomes
340
485
  const phaseRuleMatched = manual && hasPhaseRules(mapDefinition, node) && selected.status === 'matched'
341
486
  const baseOutcomes = phaseRuleMatched
342
487
  ? autoOutcomes
@@ -349,9 +494,11 @@ function routeDecision(mapDefinition, node, context, overrides = {}) {
349
494
  : baseOutcomes
350
495
  return {
351
496
  node,
352
- status: selected.status,
353
- rule: selected.rule,
354
- predicate: selected.predicate,
497
+ status: automatic.status,
498
+ rule: automatic.rule,
499
+ predicate: automatic.predicate,
500
+ matchedRuleIds: automatic.matchedRuleIds,
501
+ unknownProbability: automatic.unknownProbability,
355
502
  baseOutcomes,
356
503
  manualOutcomes,
357
504
  outcomes,
@@ -383,10 +530,12 @@ function propagateProbability(start, decisions) {
383
530
  return
384
531
  }
385
532
  const decision = decisions[node]
386
- if (!decision || decision.status === 'unknown' || decision.status === 'terminal' && !decision.outcomes.length) {
533
+ if (!decision || decision.status === 'terminal' && !decision.outcomes.length
534
+ || decision.status === 'unknown' && !decision.outcomes.length) {
387
535
  if (decision?.status === 'unknown') unknownNodes.add(node)
388
536
  return
389
537
  }
538
+ if (decision.status === 'unknown' || Number(decision.unknownProbability) > 1e-9) unknownNodes.add(node)
390
539
  const nextPath = new Set(path)
391
540
  nextPath.add(node)
392
541
  decision.outcomes.forEach((outcome) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poi-plugin-compass",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "通常海域舰队罗盘",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -52,7 +52,7 @@
52
52
  "icon": "fa/compass",
53
53
  "priority": 20,
54
54
  "apiVer": {
55
- "11.99.99": "0.1.2"
55
+ "11.99.99": "0.1.3"
56
56
  }
57
57
  },
58
58
  "peerDependencies": {