poi-plugin-compass 0.1.2 → 0.1.4

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/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.4",
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.4"
56
56
  }
57
57
  },
58
58
  "peerDependencies": {