mirror of
https://github.com/unanmed/ginka-generator.git
synced 2026-05-17 23:21:20 +08:00
146 lines
4.4 KiB
TypeScript
146 lines
4.4 KiB
TypeScript
interface VisualSimilarityConfig {
|
|
// 类型重要性权重(需根据游戏设定调整)
|
|
typeWeights: { [key: number]: number };
|
|
// 是否启用视觉焦点增强
|
|
enableVisualFocus: boolean;
|
|
// 是否启用密度感知
|
|
enableDensityAwareness: boolean;
|
|
}
|
|
|
|
const DEFAULT_CONFIG: VisualSimilarityConfig = {
|
|
typeWeights: {
|
|
0: 0.2, // 空地
|
|
1: 0.3, // 墙壁
|
|
2: 0.6, // 钥匙
|
|
3: 0.7, // 红宝石
|
|
4: 0.7, // 蓝宝石
|
|
5: 0.5, // 血瓶
|
|
6: 0.4, // 门
|
|
7: 0.5, // 弱怪
|
|
8: 0.6, // 中怪
|
|
9: 0.6, // 强怪
|
|
10: 0.4, // 楼梯
|
|
11: 0.4, // 箭头
|
|
12: 0.7 // 道具
|
|
},
|
|
enableVisualFocus: true,
|
|
enableDensityAwareness: true
|
|
};
|
|
|
|
export function calculateVisualSimilarity(
|
|
map1: number[][],
|
|
map2: number[][],
|
|
config = DEFAULT_CONFIG
|
|
): number {
|
|
// 尺寸校验
|
|
if (map1.length !== map2.length || map1[0]?.length !== map2[0]?.length) {
|
|
return 0; // 或抛出异常
|
|
}
|
|
|
|
const rows = map1.length;
|
|
const cols = map1[0].length;
|
|
let totalScore = 0;
|
|
let maxPossibleScore = 0;
|
|
|
|
// 视觉焦点权重图
|
|
const focusWeights = config.enableVisualFocus
|
|
? generateFocusWeights(rows, cols)
|
|
: Array(rows)
|
|
.fill(1)
|
|
.map(() => Array(cols).fill(1));
|
|
|
|
// 类型密度分布计算
|
|
const densityMap = config.enableDensityAwareness
|
|
? calculateDensityImpact(map1, map2, config.typeWeights)
|
|
: Array(rows)
|
|
.fill(1)
|
|
.map(() => Array(cols).fill(1));
|
|
|
|
for (let i = 0; i < rows; i++) {
|
|
for (let j = 0; j < cols; j++) {
|
|
const type1 = map1[i][j];
|
|
const type2 = map2[i][j];
|
|
|
|
// 基础类型权重
|
|
const baseWeight = Math.max(
|
|
config.typeWeights[type1] || 0.5,
|
|
config.typeWeights[type2] || 0.5
|
|
);
|
|
|
|
// 空间权重组合
|
|
const spatialWeight = focusWeights[i][j] * densityMap[i][j];
|
|
|
|
// 类型匹配得分
|
|
const typeScore = type1 === type2 ? 1 : 0;
|
|
|
|
totalScore += typeScore * baseWeight * spatialWeight;
|
|
maxPossibleScore += baseWeight * spatialWeight;
|
|
}
|
|
}
|
|
|
|
return maxPossibleScore > 0 ? totalScore / maxPossibleScore : 0;
|
|
}
|
|
|
|
/**
|
|
* 生成视觉焦点权重图(基于人类视觉注意力分布)
|
|
*/
|
|
function generateFocusWeights(rows: number, cols: number): number[][] {
|
|
const weights = [];
|
|
const centerX = cols / 2;
|
|
const centerY = rows / 2;
|
|
const maxDist = Math.sqrt(centerX ** 2 + centerY ** 2) * 0.7;
|
|
|
|
for (let i = 0; i < rows; i++) {
|
|
const rowWeights = [];
|
|
for (let j = 0; j < cols; j++) {
|
|
// 使用高斯分布模拟视觉焦点
|
|
const dx = (j - centerX) / cols;
|
|
const dy = (i - centerY) / rows;
|
|
const distance = Math.sqrt(dx ** 2 + dy ** 2);
|
|
const gaussian = Math.exp(-(distance ** 2) / (2 * 0.3 ** 2));
|
|
rowWeights.push(1.0 + 0.6 * gaussian); // 中心区域最高1.6倍权重
|
|
}
|
|
weights.push(rowWeights);
|
|
}
|
|
return weights;
|
|
}
|
|
|
|
/**
|
|
* 计算类型密度影响权重
|
|
*/
|
|
function calculateDensityImpact(
|
|
map1: number[][],
|
|
map2: number[][],
|
|
typeWeights: { [key: number]: number }
|
|
): number[][] {
|
|
const rows = map1.length;
|
|
const cols = map1[0].length;
|
|
const densityMap = Array(rows)
|
|
.fill(0)
|
|
.map(() => Array(cols).fill(0));
|
|
|
|
// 滑动窗口分析局部密度
|
|
const windowSize = 3;
|
|
const halfWindow = Math.floor(windowSize / 2);
|
|
|
|
for (let i = 0; i < rows; i++) {
|
|
for (let j = 0; j < cols; j++) {
|
|
let density = 0;
|
|
for (let di = -halfWindow; di <= halfWindow; di++) {
|
|
for (let dj = -halfWindow; dj <= halfWindow; dj++) {
|
|
const ni = i + di;
|
|
const nj = j + dj;
|
|
if (ni >= 0 && ni < rows && nj >= 0 && nj < cols) {
|
|
const weight1 = typeWeights[map1[ni][nj]] || 0.5;
|
|
const weight2 = typeWeights[map2[ni][nj]] || 0.5;
|
|
density += (weight1 + weight2) / 2;
|
|
}
|
|
}
|
|
}
|
|
// 密度权重:高密度区域增强对比度
|
|
densityMap[i][j] = 1.0 + 0.4 * (density / windowSize ** 2);
|
|
}
|
|
}
|
|
return densityMap;
|
|
}
|