ginka-generator/data/src/vision/similarity.ts

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;
}