mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-08 12:16:12 -05:00
@@ -367,6 +367,42 @@ describe("calculateDamageCombos - excessive combo filtering", () => {
|
||||
});
|
||||
});
|
||||
|
||||
const TRI_SLOSHER_ID = 3010;
|
||||
const INKBRUSH_ID = 1100;
|
||||
const GOLD_DYNAMO_ROLLER_ID = 1021;
|
||||
const RAPID_BLASTER_PRO_WNT_R_ID = 252;
|
||||
|
||||
describe("calculateDamageCombos - deduplication", () => {
|
||||
test("no duplicate combos with bug report weapons", () => {
|
||||
const combos = calculateDamageCombos(
|
||||
[
|
||||
TRI_SLOSHER_ID,
|
||||
INKBRUSH_ID,
|
||||
GOLD_DYNAMO_ROLLER_ID,
|
||||
RAPID_BLASTER_PRO_WNT_R_ID,
|
||||
],
|
||||
[],
|
||||
0,
|
||||
1000,
|
||||
);
|
||||
|
||||
const canonicalKeys = combos.map((combo) => {
|
||||
const grouped = new Map<string, number>();
|
||||
for (const segment of combo.segments) {
|
||||
const key = `${segment.damageType}:${segment.damageValue}`;
|
||||
grouped.set(key, (grouped.get(key) ?? 0) + segment.count);
|
||||
}
|
||||
return [...grouped.entries()]
|
||||
.sort(([a], [b]) => a.localeCompare(b))
|
||||
.map(([key, count]) => `${key}:${count}`)
|
||||
.join("|");
|
||||
});
|
||||
|
||||
const uniqueKeys = new Set(canonicalKeys);
|
||||
expect(uniqueKeys.size).toBe(canonicalKeys.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe("virtual damage combos", () => {
|
||||
test("Explosher has COMBO damage type combining DIRECT and DISTANCE", () => {
|
||||
const sources = extractDamageSources([EXPLOSHER_ID]);
|
||||
|
||||
@@ -303,6 +303,62 @@ function backtrack(
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeCombo(combo: DamageCombo): DamageCombo {
|
||||
const grouped = new Map<
|
||||
string,
|
||||
{ segment: DamageSegment; totalCount: number }
|
||||
>();
|
||||
|
||||
for (const segment of combo.segments) {
|
||||
const key = `${segment.damageType}:${segment.damageValue}`;
|
||||
const existing = grouped.get(key);
|
||||
if (existing) {
|
||||
existing.totalCount += segment.count;
|
||||
} else {
|
||||
grouped.set(key, { segment, totalCount: segment.count });
|
||||
}
|
||||
}
|
||||
|
||||
const segments: DamageSegment[] = [];
|
||||
for (const { segment, totalCount } of grouped.values()) {
|
||||
segments.push({ ...segment, count: totalCount });
|
||||
}
|
||||
|
||||
segments.sort((a, b) => {
|
||||
const typeCompare = a.damageType.localeCompare(b.damageType);
|
||||
if (typeCompare !== 0) return typeCompare;
|
||||
return a.damageValue - b.damageValue;
|
||||
});
|
||||
|
||||
return {
|
||||
segments,
|
||||
totalDamage: combo.totalDamage,
|
||||
hitCount: combo.hitCount,
|
||||
};
|
||||
}
|
||||
|
||||
function comboKey(normalized: DamageCombo): string {
|
||||
return normalized.segments
|
||||
.map((s) => `${s.damageType}:${s.damageValue}:${s.count}`)
|
||||
.join("|");
|
||||
}
|
||||
|
||||
function deduplicateCombos(combos: DamageCombo[]): DamageCombo[] {
|
||||
const seen = new Map<string, DamageCombo>();
|
||||
|
||||
for (const combo of combos) {
|
||||
const normalized = normalizeCombo(combo);
|
||||
const key = comboKey(normalized);
|
||||
const existing = seen.get(key);
|
||||
|
||||
if (!existing || combo.segments.length < existing.segments.length) {
|
||||
seen.set(key, combo);
|
||||
}
|
||||
}
|
||||
|
||||
return [...seen.values()];
|
||||
}
|
||||
|
||||
function filterAndSortCombos(
|
||||
combos: DamageCombo[],
|
||||
maxCombosDisplayed: number,
|
||||
@@ -323,7 +379,9 @@ function filterAndSortCombos(
|
||||
return true;
|
||||
});
|
||||
|
||||
filtered.sort((a, b) => {
|
||||
const deduplicated = deduplicateCombos(filtered);
|
||||
|
||||
deduplicated.sort((a, b) => {
|
||||
const aDistTo100 = Math.abs(a.totalDamage - 100);
|
||||
const bDistTo100 = Math.abs(b.totalDamage - 100);
|
||||
if (aDistTo100 !== bDistTo100) {
|
||||
@@ -332,7 +390,7 @@ function filterAndSortCombos(
|
||||
return a.hitCount - b.hitCount;
|
||||
});
|
||||
|
||||
return filtered.slice(0, maxCombosDisplayed);
|
||||
return deduplicated.slice(0, maxCombosDisplayed);
|
||||
}
|
||||
|
||||
function hasOneShot(combo: DamageCombo): boolean {
|
||||
|
||||
Reference in New Issue
Block a user