poi-plugin-compass 0.1.5 → 0.1.6
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/data/normal-maps.json +4 -3
- package/index.js +53 -3
- package/logic.js +94 -1
- package/package.json +2 -2
package/data/normal-maps.json
CHANGED
|
@@ -1138,9 +1138,10 @@
|
|
|
1138
1138
|
"name": "拉包尔方面海域",
|
|
1139
1139
|
"start": "1",
|
|
1140
1140
|
"boss": "G",
|
|
1141
|
-
"manualNodes": ["I"],
|
|
1141
|
+
"manualNodes": ["I", "O"],
|
|
1142
1142
|
"manualOutcomes": {
|
|
1143
|
-
"I": [{ "to": "J", "probability": null }, { "to": "O", "probability": null }]
|
|
1143
|
+
"I": [{ "to": "J", "probability": null }, { "to": "O", "probability": null }],
|
|
1144
|
+
"O": [{ "to": "P", "probability": null }, { "to": "Q", "probability": null }]
|
|
1144
1145
|
},
|
|
1145
1146
|
"sourceRefs": [
|
|
1146
1147
|
{ "kind": "kcwiki", "url": "https://zh.kcwiki.cn/wiki/南方海域/5-6", "checkedAt": "2026-09-11" },
|
|
@@ -1186,7 +1187,7 @@
|
|
|
1186
1187
|
{ "id": "5-6-L-fallback", "node": "L", "priority": 999, "predicate": { "kind": "always", "label": "索敌不足" }, "outcomes": [{ "to": "M", "probability": 1 }], "confidence": "approximate" },
|
|
1187
1188
|
{ "id": "5-6-I-p2", "node": "I", "priority": 10, "predicate": { "kind": "phase", "phase": "P2", "label": "P2:向下前往 J" }, "outcomes": [{ "to": "J", "probability": 1 }], "confidence": "verified" },
|
|
1188
1189
|
{ "id": "5-6-I-p3", "node": "I", "priority": 20, "predicate": { "kind": "phase", "phase": "P3", "label": "P3:向上前往 O" }, "outcomes": [{ "to": "O", "probability": 1 }], "confidence": "verified" },
|
|
1189
|
-
{ "id": "5-6-O-
|
|
1190
|
+
{ "id": "5-6-O-manual", "node": "O", "priority": 1, "predicate": { "kind": "always", "label": "能动分歧:选择 P / Q" }, "outcomes": [{ "to": "P", "probability": null }, { "to": "Q", "probability": null }], "confidence": "verified" },
|
|
1190
1191
|
{ "id": "5-6-Q-locked", "node": "Q", "priority": 10, "predicate": { "kind": "flag", "flag": "qRoutes", "value": false, "label": "Q-U/Q-W 路线未解锁" }, "outcomes": [{ "to": "Q2", "probability": 1 }], "confidence": "verified" },
|
|
1191
1192
|
{ "id": "5-6-Q-fast-plus", "node": "Q", "priority": 20, "predicate": { "kind": "speed", "mode": "highPlus" }, "outcomes": [{ "to": "W", "probability": 1 }], "confidence": "verified" },
|
|
1192
1193
|
{ "id": "5-6-Q-high-light", "node": "Q", "priority": 30, "predicate": { "kind": "all", "predicates": [{ "kind": "count", "category": "CL", "op": ">=", "value": 1 }, { "kind": "count", "category": "DD", "op": ">=", "value": 3 }, { "kind": "speed", "mode": "allHigh" }] }, "outcomes": [{ "to": "W", "probability": 1 }], "confidence": "verified" },
|
package/index.js
CHANGED
|
@@ -50,6 +50,13 @@ const MAP_PHASES = {
|
|
|
50
50
|
],
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
const MAP_ROUTE_OPTIONS = {
|
|
54
|
+
'5-6': [
|
|
55
|
+
{ id: 'locked', label: '未解锁', flags: { qRoutes: false } },
|
|
56
|
+
{ id: 'unlocked', label: '已解锁', flags: { qRoutes: true } },
|
|
57
|
+
],
|
|
58
|
+
}
|
|
59
|
+
|
|
53
60
|
function phaseOptionsForMap(mapId) {
|
|
54
61
|
return MAP_PHASES[mapId] || []
|
|
55
62
|
}
|
|
@@ -58,10 +65,25 @@ function phaseOptionForMap(mapId, phaseId) {
|
|
|
58
65
|
return phaseOptionsForMap(mapId).find((phase) => phase.id === phaseId) || phaseOptionsForMap(mapId)[0] || null
|
|
59
66
|
}
|
|
60
67
|
|
|
61
|
-
function
|
|
68
|
+
function routeOptionsForMap(mapId) {
|
|
69
|
+
return MAP_ROUTE_OPTIONS[mapId] || []
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function defaultRouteOptionId(mapId) {
|
|
73
|
+
return routeOptionsForMap(mapId)[0]?.id || null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function routeOptionForMap(mapId, routeOptionId) {
|
|
77
|
+
return routeOptionsForMap(mapId).find((option) => option.id === routeOptionId)
|
|
78
|
+
|| routeOptionsForMap(mapId)[0]
|
|
79
|
+
|| null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function phaseFlags(mapId, phaseId, routeOptionId) {
|
|
62
83
|
const flags = {}
|
|
63
84
|
if (mapId === '7-3') flags.phase1 = phaseId === 'P1'
|
|
64
|
-
|
|
85
|
+
const routeOption = routeOptionForMap(mapId, routeOptionId)
|
|
86
|
+
if (routeOption?.flags) Object.assign(flags, routeOption.flags)
|
|
65
87
|
return flags
|
|
66
88
|
}
|
|
67
89
|
|
|
@@ -662,11 +684,14 @@ class Compass extends React.Component {
|
|
|
662
684
|
const initialDeckId = [1, 2, 3, 4].includes(Number(preferences.deckId)) ? Number(preferences.deckId) : 1
|
|
663
685
|
const preferredPhaseId = preferences.phaseByMap?.[initialMapId] ?? preferences.phaseId
|
|
664
686
|
const initialPhaseId = phaseOptionForMap(initialMapId, preferredPhaseId)?.id || null
|
|
687
|
+
const initialRouteOptionId = routeOptionForMap(initialMapId, preferences.routeOptionByMap?.[initialMapId])?.id
|
|
688
|
+
|| defaultRouteOptionId(initialMapId)
|
|
665
689
|
const initialExportPreferences = storedExportPreferencesForView(preferences, initialMapId, initialPhaseId, initialDeckId)
|
|
666
690
|
this.state = {
|
|
667
691
|
deckId: initialDeckId,
|
|
668
692
|
mapId: initialMapId,
|
|
669
693
|
phaseId: initialPhaseId,
|
|
694
|
+
routeOptionId: initialRouteOptionId,
|
|
670
695
|
selectedNode: pluginState.currentNode || null,
|
|
671
696
|
selectedEdge: null,
|
|
672
697
|
overrides: storedOverridesForView(preferences, initialMapId, initialPhaseId, initialDeckId),
|
|
@@ -717,11 +742,14 @@ class Compass extends React.Component {
|
|
|
717
742
|
}
|
|
718
743
|
const phaseByMap = isRecord(previous.phaseByMap) ? { ...previous.phaseByMap } : {}
|
|
719
744
|
phaseByMap[this.state.mapId] = this.state.phaseId
|
|
745
|
+
const routeOptionByMap = isRecord(previous.routeOptionByMap) ? { ...previous.routeOptionByMap } : {}
|
|
746
|
+
if (this.state.routeOptionId) routeOptionByMap[this.state.mapId] = this.state.routeOptionId
|
|
720
747
|
storage.setItem(PREFERENCES_STORAGE_KEY, JSON.stringify({
|
|
721
748
|
...previous,
|
|
722
749
|
mapId: this.state.mapId,
|
|
723
750
|
phaseId: this.state.phaseId,
|
|
724
751
|
phaseByMap,
|
|
752
|
+
routeOptionByMap,
|
|
725
753
|
deckId: this.state.deckId,
|
|
726
754
|
probabilityFormat: this.state.probabilityFormat,
|
|
727
755
|
showEstimatedAir: this.state.showEstimatedAir,
|
|
@@ -743,10 +771,13 @@ class Compass extends React.Component {
|
|
|
743
771
|
selectMap(mapId) {
|
|
744
772
|
const preferences = readCompassPreferences()
|
|
745
773
|
const phaseId = phaseOptionForMap(mapId, preferences.phaseByMap?.[mapId])?.id || null
|
|
774
|
+
const routeOptionId = routeOptionForMap(mapId, preferences.routeOptionByMap?.[mapId])?.id
|
|
775
|
+
|| defaultRouteOptionId(mapId)
|
|
746
776
|
const exportPreferences = storedExportPreferencesForView(preferences, mapId, phaseId, this.state.deckId)
|
|
747
777
|
this.setState({
|
|
748
778
|
mapId,
|
|
749
779
|
phaseId,
|
|
780
|
+
routeOptionId,
|
|
750
781
|
selectedNode: null,
|
|
751
782
|
selectedEdge: null,
|
|
752
783
|
overrides: storedOverridesForView(preferences, mapId, phaseId, this.state.deckId),
|
|
@@ -800,6 +831,11 @@ class Compass extends React.Component {
|
|
|
800
831
|
this.setState({ phaseId, selectedNode: null, selectedEdge: null, overrides, ...exportPreferences, exportError: null })
|
|
801
832
|
}
|
|
802
833
|
|
|
834
|
+
selectRouteOption(routeOptionId) {
|
|
835
|
+
if (!routeOptionForMap(this.state.mapId, routeOptionId)) return
|
|
836
|
+
this.setState({ routeOptionId, selectedNode: null, selectedEdge: null, exportRoute: null, exportError: null })
|
|
837
|
+
}
|
|
838
|
+
|
|
803
839
|
selectNode(node) {
|
|
804
840
|
this.setState({ selectedNode: node, selectedEdge: null })
|
|
805
841
|
}
|
|
@@ -1299,7 +1335,7 @@ class Compass extends React.Component {
|
|
|
1299
1335
|
admiralCoefficient: mapLosConfig.admiralCoefficient,
|
|
1300
1336
|
passedNodes: pluginState.currentMapId === this.state.mapId ? pluginState.passedNodes : [],
|
|
1301
1337
|
phase: phase?.id || null,
|
|
1302
|
-
flags: phaseFlags(this.state.mapId, phase?.id),
|
|
1338
|
+
flags: phaseFlags(this.state.mapId, phase?.id, this.state.routeOptionId),
|
|
1303
1339
|
})
|
|
1304
1340
|
const geometry = mapGeometry(this.state.mapId)
|
|
1305
1341
|
const displayMapDefinition = phase?.boss
|
|
@@ -1342,6 +1378,18 @@ class Compass extends React.Component {
|
|
|
1342
1378
|
}, option.label)),
|
|
1343
1379
|
)
|
|
1344
1380
|
: null,
|
|
1381
|
+
routeOptionsForMap(this.state.mapId).length > 1
|
|
1382
|
+
? h('div', { className: 'compass-phase-group', role: 'group', 'aria-label': '路线解锁状态' },
|
|
1383
|
+
h('span', { className: 'compass-option-label' }, 'Q路线'),
|
|
1384
|
+
routeOptionsForMap(this.state.mapId).map((option) => h('button', {
|
|
1385
|
+
key: option.id,
|
|
1386
|
+
type: 'button',
|
|
1387
|
+
className: routeOptionForMap(this.state.mapId, this.state.routeOptionId)?.id === option.id ? 'is-active' : '',
|
|
1388
|
+
'aria-pressed': routeOptionForMap(this.state.mapId, this.state.routeOptionId)?.id === option.id,
|
|
1389
|
+
onClick: () => this.selectRouteOption(option.id),
|
|
1390
|
+
}, option.label)),
|
|
1391
|
+
)
|
|
1392
|
+
: null,
|
|
1345
1393
|
h('div', { className: 'compass-option-group', role: 'group', 'aria-label': '概率标签格式' },
|
|
1346
1394
|
h('span', { className: 'compass-option-label' }, '概率'),
|
|
1347
1395
|
...[
|
|
@@ -1423,6 +1471,8 @@ exports.__test = {
|
|
|
1423
1471
|
nodeIconUrl,
|
|
1424
1472
|
losConfig: losCatalog.maps,
|
|
1425
1473
|
phaseOptionsForMap,
|
|
1474
|
+
routeOptionsForMap,
|
|
1475
|
+
routeOptionForMap,
|
|
1426
1476
|
pluginState,
|
|
1427
1477
|
routeEdges,
|
|
1428
1478
|
ruleRows,
|
package/logic.js
CHANGED
|
@@ -290,6 +290,17 @@ function hasPhaseRules(mapDefinition, node) {
|
|
|
290
290
|
return rulesForNode(mapDefinition, node).some((rule) => containsPhasePredicate(rule.predicate))
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
function containsVisitedPredicate(predicate) {
|
|
294
|
+
if (!predicate || typeof predicate !== 'object') return false
|
|
295
|
+
if (predicate.kind === 'visited') return true
|
|
296
|
+
if (Array.isArray(predicate.predicates) && predicate.predicates.some(containsVisitedPredicate)) return true
|
|
297
|
+
return containsVisitedPredicate(predicate.predicate)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function hasVisitedRules(mapDefinition) {
|
|
301
|
+
return (mapDefinition?.rules || []).some((rule) => containsVisitedPredicate(rule.predicate))
|
|
302
|
+
}
|
|
303
|
+
|
|
293
304
|
function selectRouteRule(mapDefinition, node, context) {
|
|
294
305
|
const rules = rulesForNode(mapDefinition, node)
|
|
295
306
|
for (const rule of rules) {
|
|
@@ -562,6 +573,74 @@ function propagateProbability(start, decisions) {
|
|
|
562
573
|
}
|
|
563
574
|
}
|
|
564
575
|
|
|
576
|
+
function propagatePathProbability(start, mapDefinition, context = {}, overrides = {}) {
|
|
577
|
+
const reach = {}
|
|
578
|
+
const edgeMass = {}
|
|
579
|
+
const unknownNodes = new Set()
|
|
580
|
+
const unknownEdges = new Set()
|
|
581
|
+
const estimatedNodes = new Set()
|
|
582
|
+
const estimatedEdges = new Set()
|
|
583
|
+
const decisionVariants = new Map()
|
|
584
|
+
let visits = 0
|
|
585
|
+
|
|
586
|
+
function rememberDecision(node, passedNodes, decision) {
|
|
587
|
+
const variants = decisionVariants.get(node) || []
|
|
588
|
+
const key = passedNodes.join('\u0000')
|
|
589
|
+
if (!variants.some((variant) => variant.key === key)) {
|
|
590
|
+
variants.push({ key, passedNodes, decision })
|
|
591
|
+
decisionVariants.set(node, variants)
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function walk(node, passedNodes, mass, path, estimatedPath) {
|
|
596
|
+
if (!Number.isFinite(mass) || mass <= 0) return
|
|
597
|
+
if (++visits > 10000) {
|
|
598
|
+
unknownNodes.add(node)
|
|
599
|
+
return
|
|
600
|
+
}
|
|
601
|
+
reach[node] = number(reach[node]) + mass
|
|
602
|
+
if (estimatedPath) estimatedNodes.add(node)
|
|
603
|
+
if (path.has(node)) {
|
|
604
|
+
unknownNodes.add(node)
|
|
605
|
+
return
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const decision = routeDecision(mapDefinition, node, { ...context, passedNodes }, overrides)
|
|
609
|
+
rememberDecision(node, passedNodes, decision)
|
|
610
|
+
if (!decision || decision.status === 'terminal' && !decision.outcomes.length
|
|
611
|
+
|| decision.status === 'unknown' && !decision.outcomes.length) {
|
|
612
|
+
if (decision?.status === 'unknown') unknownNodes.add(node)
|
|
613
|
+
return
|
|
614
|
+
}
|
|
615
|
+
if (decision.status === 'unknown' || Number(decision.unknownProbability) > 1e-9) unknownNodes.add(node)
|
|
616
|
+
|
|
617
|
+
const nextPath = new Set(path)
|
|
618
|
+
nextPath.add(node)
|
|
619
|
+
decision.outcomes.forEach((outcome) => {
|
|
620
|
+
const key = edgeKey(node, outcome.to)
|
|
621
|
+
if (outcome.probability == null) {
|
|
622
|
+
unknownEdges.add(key)
|
|
623
|
+
return
|
|
624
|
+
}
|
|
625
|
+
const edgeEstimated = estimatedPath || outcome.estimated === true
|
|
626
|
+
if (edgeEstimated) estimatedEdges.add(key)
|
|
627
|
+
edgeMass[key] = number(edgeMass[key]) + mass * outcome.probability
|
|
628
|
+
walk(outcome.to, [...passedNodes, node], mass * outcome.probability, nextPath, edgeEstimated)
|
|
629
|
+
})
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
walk(start, [], 1, new Set(), false)
|
|
633
|
+
return {
|
|
634
|
+
reach,
|
|
635
|
+
edgeMass,
|
|
636
|
+
unknownNodes: Array.from(unknownNodes),
|
|
637
|
+
unknownEdges: Array.from(unknownEdges),
|
|
638
|
+
estimatedNodes: Array.from(estimatedNodes),
|
|
639
|
+
estimatedEdges: Array.from(estimatedEdges),
|
|
640
|
+
decisionVariants,
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
565
644
|
function evaluateMap(mapDefinition, context = {}, overrides = {}) {
|
|
566
645
|
const nodes = new Set([mapDefinition?.start || '1'])
|
|
567
646
|
;(mapDefinition?.rules || []).forEach((rule) => nodes.add(rule.node))
|
|
@@ -574,7 +653,21 @@ function evaluateMap(mapDefinition, context = {}, overrides = {}) {
|
|
|
574
653
|
Array.from(nodes).forEach((node) => {
|
|
575
654
|
if (!decisions[node]) decisions[node] = routeDecision(mapDefinition, node, context, overrides)
|
|
576
655
|
})
|
|
577
|
-
const probability =
|
|
656
|
+
const probability = hasVisitedRules(mapDefinition)
|
|
657
|
+
? propagatePathProbability(mapDefinition?.start || '1', mapDefinition, context, overrides)
|
|
658
|
+
: propagateProbability(mapDefinition?.start || '1', decisions)
|
|
659
|
+
if (probability.decisionVariants) {
|
|
660
|
+
const preferredPath = Array.isArray(context.passedNodes) ? context.passedNodes : []
|
|
661
|
+
probability.decisionVariants.forEach((variants, node) => {
|
|
662
|
+
const preferred = preferredPath.length
|
|
663
|
+
? variants
|
|
664
|
+
.filter((variant) => variant.passedNodes.every((passedNode, index) => preferredPath[index] === passedNode))
|
|
665
|
+
.sort((left, right) => right.passedNodes.length - left.passedNodes.length)[0]
|
|
666
|
+
: null
|
|
667
|
+
decisions[node] = (preferred || variants[0]).decision
|
|
668
|
+
})
|
|
669
|
+
delete probability.decisionVariants
|
|
670
|
+
}
|
|
578
671
|
return { decisions, probability }
|
|
579
672
|
}
|
|
580
673
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poi-plugin-compass",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
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.
|
|
55
|
+
"11.99.99": "0.1.6"
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|