chore: 调整数据集

This commit is contained in:
unanmed 2026-02-03 13:26:34 +08:00
parent 10166fa073
commit df2bd80bec
7 changed files with 184 additions and 112 deletions

View File

@ -103,7 +103,14 @@ const ignoredTower: string[] = [
'zjhy1',
'zrsz',
'zuozuozhieX_renewed',
'ZZZE50Ex'
'ZZZE50Ex',
'1DQSJ',
'againstcov',
'Alive',
'Aria',
'atom',
'bdf1',
'bingyuanhouzhuan'
];
const ignoredFloor: Record<string, string[]> = {
@ -119,7 +126,8 @@ const ignoredFloor: Record<string, string[]> = {
'MT71',
'ML26',
'ML27',
'MQ28'
'MQ28',
'MT104'
],
cxzl: [
'MT63',
@ -153,11 +161,11 @@ const ignoredFloor: Record<string, string[]> = {
zd1: ['MH9', 'MT10'],
zhenshishenghuo: ['MT52', 'MT131', 'MT132'],
CatLegend: ['MT20', 'MT63', 'MT67'],
cxzl_wycx: ['MT304', 'MT777'],
cxzl_wycx: ['MT304', 'MT777', 'MT494'],
evernight: ['MT30'],
bits1: ['MT28'],
bits2: ['dltsd5'],
bits114514: ['bysl7', 'byxd3', 'mkpy6', 'sdhxmiddle1'],
bits2: ['dltsd5', 'hdxd5'],
bits114514: ['bysl7', 'byxd3', 'mkpy6', 'sdhxmiddle1', 'zlzl1', 'zlzl2'],
Black: ['MT12'],
chaoshuang2: ['MT153'],
EasyPreduct: ['PT40'],
@ -214,10 +222,36 @@ const ignoredFloor: Record<string, string[]> = {
WhiteLily_1521: ['B9'],
xsdsj: ['MT40'],
xxchuanshuo0: ['D35', 'D38', 'D39'],
yesterdayReturn: ['MT6-1']
yesterdayReturn: ['MT6-1'],
'9922': [
'Ch4_15',
'East3',
'East7',
'MT12',
'East11',
'North2',
'North3',
'North4'
],
amiyamiya: ['MT27']
};
const labelConfig: IAutoLabelConfig = {
classes: {
empty: 0,
wall: 1,
decoration: 2,
commonDoors: [3],
specialDoors: [6, 6],
keys: [7],
redGems: [10],
blueGems: [13],
greenGems: [16],
potions: [19],
items: [23],
enemies: [26],
entry: 29
},
allowedSize: [[13, 13]],
allowUselessBranch: false,
maxWallDensityStd: 0.25,
@ -228,19 +262,25 @@ const labelConfig: IAutoLabelConfig = {
minResourceRatio: 0.02,
maxResourceRatio: 0.3,
minDoorRatio: 0,
maxDoorRatio: 0.2,
maxDoorRatio: 0.12,
minFishCount: 0,
maxFishCount: 2,
minEntryCount: 1,
maxEntryCount: 4,
ignoreIssues: true,
customTowerFilter: info => {
// if (info.name !== 'Apeiria') {
// return false;
// }
// if (info.color !== TowerColor.Blue && info.color !== TowerColor.Green) {
// return false;
// }
if (info.people < 1000) {
return false;
}
if (info.name.startsWith('50') && info.name.length > 2) {
return false;
}
if (info.name.startsWith('51') && info.name.length > 2) {
return false;
}
@ -250,7 +290,6 @@ const labelConfig: IAutoLabelConfig = {
if (ignoredTower.includes(info.name)) {
return false;
}
return true;
},
customFloorFilter: floor => {
@ -267,7 +306,7 @@ const labelConfig: IAutoLabelConfig = {
return false;
}
if (floor.tower.name === 'Apeiria') {
return Math.random() > 0.9;
return Math.random() < 0.2;
}
return true;
}

View File

@ -94,7 +94,7 @@ export async function autoLabelTowers(
continue;
}
// 转换楼层
const converted = convertTowerMap(result, floor);
const converted = convertTowerMap(result, floor, config);
const floorInfo = parseFloorInfo(info, converted.map);
const floorData: IConvertedMapInfo = {
data: converted,

View File

@ -1,6 +1,6 @@
import { readFile } from 'fs/promises';
import { IFloorInfo, ITowerInfo, TowerColor } from './types';
import { buildTopologicalGraph } from 'src/topology/graph';
import { buildTopologicalGraph } from '../topology/graph';
import {
commonDoorTiles,
doorTiles,
@ -14,8 +14,8 @@ import {
resourceTiles,
specialDoorTiles,
wallTiles
} from 'src/shared';
import { NodeType } from 'src/topology/interface';
} from '../shared';
import { NodeType } from '../topology/interface';
interface IRawTowerInfo {
/** 作者 id */

View File

@ -1,4 +1,9 @@
import { ICodeRunResult, IConvertedMap, INeededFloorData } from './types';
import {
IAutoLabelConfig,
ICodeRunResult,
IConvertedMap,
INeededFloorData
} from './types';
/**
*
@ -39,7 +44,8 @@ function edge(x: number, y: number, width: number, height: number) {
export function convertTowerMap(
result: ICodeRunResult,
floor: INeededFloorData
floor: INeededFloorData,
config: IAutoLabelConfig
): IConvertedMap {
const width = floor.map[0].length;
const height = floor.map.length;
@ -109,33 +115,31 @@ export function convertTowerMap(
core.status.hero.def = 0;
core.status.hero.mdef = 0;
const tiles = config.classes;
for (let nx = 0; nx < width; nx++) {
for (let ny = 0; ny < height; ny++) {
const num = floor.map[ny][nx];
if (num === 0) {
converted[ny][nx] = 0;
continue;
} else if (num === 17) {
hasCannotInOut = true;
converted[ny][nx] = 0;
if (num === 0 || num === 17) {
converted[ny][nx] = tiles.empty;
continue;
}
const loc = `${nx},${ny}`;
if (floor.changeFloor[loc]) {
converted[ny][nx] = 29;
converted[ny][nx] = tiles.entry;
continue;
}
const block = result.map[num];
if (!block) {
// 图块不存在说明是额外素材中的内容,默认不可通行,视为墙壁
converted[ny][nx] = 1;
converted[ny][nx] = tiles.wall;
continue;
}
// 怪物处理
if (block.cls === 'enemys' || block.cls === 'enemy48') {
const enemy = result.enemy[block.id];
if (!enemy) {
converted[ny][nx] = 0;
converted[ny][nx] = tiles.empty;
continue;
}
const value = enemy.hp * (enemy.atk + enemy.def);
@ -146,7 +150,7 @@ export function convertTowerMap(
if (block.cls === 'items') {
const item = result.item[block.id];
if (!item) {
converted[ny][nx] = 0;
converted[ny][nx] = tiles.empty;
continue;
}
// 先清空内容
@ -155,22 +159,32 @@ export function convertTowerMap(
heroStatus.def = 0;
heroStatus.mdef = 0;
if (block.id === 'pickaxe') {
converted[ny][nx] = 24;
const idx = Math.min(tiles.items.length - 1, 0);
converted[ny][nx] = tiles.items[idx];
continue;
} else if (block.id === 'bomb') {
converted[ny][nx] = 24;
const idx = Math.min(tiles.items.length - 1, 1);
converted[ny][nx] = tiles.items[idx];
continue;
} else if (block.id === 'centerFly') {
converted[ny][nx] = 23;
const idx = Math.min(tiles.items.length - 1, 2);
converted[ny][nx] = tiles.items[idx];
continue;
} else if (block.id === 'yellowKey') {
converted[ny][nx] = 7;
const idx = Math.min(tiles.items.length - 1, 0);
converted[ny][nx] = tiles.keys[idx];
continue;
} else if (block.id === 'blueKey') {
converted[ny][nx] = 8;
const idx = Math.min(tiles.items.length - 1, 1);
converted[ny][nx] = tiles.keys[idx];
continue;
} else if (block.id === 'redKey') {
converted[ny][nx] = 9;
const idx = Math.min(tiles.items.length - 1, 2);
converted[ny][nx] = tiles.keys[idx];
continue;
} else if (block.id === 'greenKey') {
const idx = Math.min(tiles.items.length - 1, 3);
converted[ny][nx] = tiles.keys[idx];
continue;
}
// 执行道具效果
@ -213,30 +227,34 @@ export function convertTowerMap(
itemMap.set(ny * width + nx, arr);
continue;
} else {
converted[ny][nx] = 0;
converted[ny][nx] = tiles.empty;
continue;
}
}
// 门信息,这种处理方式只能处理 2.7+ 的塔,老塔估计处理不了,不过老塔占比也不大,忽略就好了
if (block.doorInfo && Object.keys(block.doorInfo.keys).length > 0) {
if (block.id === 'specialDoor') {
converted[ny][nx] = 6;
converted[ny][nx] = tiles.specialDoors[0];
continue;
} else if (
'redKey' in block.doorInfo.keys ||
'greenKey' in block.doorInfo.keys
) {
converted[ny][nx] = 5;
} else if ('greenKey' in block.doorInfo.keys) {
const idx = Math.min(tiles.commonDoors.length - 1, 3);
converted[ny][nx] = tiles.commonDoors[idx];
} else if ('redKey' in block.doorInfo.keys) {
const idx = Math.min(tiles.commonDoors.length - 1, 2);
converted[ny][nx] = tiles.commonDoors[idx];
continue;
} else if ('blueKey' in block.doorInfo.keys) {
converted[ny][nx] = 4;
const idx = Math.min(tiles.commonDoors.length - 1, 1);
converted[ny][nx] = tiles.commonDoors[idx];
continue;
} else if ('yellowKey' in block.doorInfo.keys) {
converted[ny][nx] = 3;
const idx = Math.min(tiles.commonDoors.length - 1, 0);
converted[ny][nx] = tiles.commonDoors[idx];
continue;
} else {
// 其他门一律视为黄门
converted[ny][nx] = 3;
// 其余视为绿门
const idx = Math.min(tiles.commonDoors.length - 1, 3);
converted[ny][nx] = tiles.commonDoors[idx];
continue;
}
}
@ -261,16 +279,16 @@ export function convertTowerMap(
fg2Block?.cannotIn ||
fg2Block?.cannotOut
) {
converted[ny][nx] = 0;
converted[ny][nx] = tiles.empty;
hasCannotInOut = true;
continue;
}
// 墙壁处理
if (block.canPass) {
converted[ny][nx] = 0;
converted[ny][nx] = tiles.empty;
continue;
} else {
converted[ny][nx] = 1;
converted[ny][nx] = tiles.wall;
}
}
}
@ -285,15 +303,18 @@ export function convertTowerMap(
enemyMap.forEach((value, pos) => {
const nx = pos % width;
const ny = Math.floor(pos / width);
converted[ny][nx] = 26;
converted[ny][nx] = tiles.enemies[0];
});
} else {
enemyMap.forEach((value, pos) => {
const nx = pos % width;
const ny = Math.floor(pos / width);
const ratio = (value - minEnemyValue) / enemyValueDelta;
const n = Math.min(Math.floor(ratio * 3), 2);
converted[ny][nx] = 26 + n;
const idx = Math.min(
Math.floor(ratio * tiles.enemies.length),
tiles.enemies.length - 1
);
converted[ny][nx] = tiles.enemies[idx];
});
}
@ -317,19 +338,19 @@ export function convertTowerMap(
// 资源判定为占比最大的那个
// 如果只有一种资源且道具包含这种属性,全部使用最低的资源种类
if (minHpValue === maxHpValue && hp > 0) {
converted[ny][nx] = 19;
converted[ny][nx] = tiles.potions[0];
return;
}
if (minAtkValue === maxAtkValue && atk > 0) {
converted[ny][nx] = 10;
converted[ny][nx] = tiles.redGems[0];
return;
}
if (minDefValue === maxDefValue && def > 0) {
converted[ny][nx] = 13;
converted[ny][nx] = tiles.blueGems[0];
return;
}
if (minMdefValue === maxMdefValue && mdef > 0) {
converted[ny][nx] = 16;
converted[ny][nx] = tiles.greenGems[0];
return;
}
const hpRatio = (hp - minHpValue) / hpValueDelta;
@ -353,68 +374,38 @@ export function convertTowerMap(
switch (maxIndex) {
case 0: {
// 血瓶
const n = Math.min(Math.floor(hpRatio * 4), 3);
converted[ny][nx] = 19 + n;
const idx = Math.min(
Math.floor(hpRatio * tiles.potions.length),
tiles.potions.length - 1
);
converted[ny][nx] = tiles.potions[idx];
break;
}
case 1: {
// 红宝石,这里不可能只有一种数值了,不需要判断
if (itemAtkSet.size === 2) {
if (atkRatio <= 0.5) {
// 小宝石
converted[ny][nx] = 10;
} else {
// 大宝石
if (maxAtkValue / minAtkValue > 3) {
converted[nx][nx] = 12;
} else {
converted[ny][nx] = 11;
}
}
} else {
const n = Math.min(Math.floor(atkRatio * 3), 2);
converted[ny][nx] = 10 + n;
}
const idx = Math.min(
Math.floor(atkRatio * tiles.redGems.length),
tiles.redGems.length - 1
);
converted[ny][nx] = tiles.redGems[idx];
break;
}
case 2: {
// 蓝宝石,这里不可能只有一种数值了,不需要判断
if (itemDefSet.size === 2) {
if (defRatio <= 0.5) {
// 小宝石
converted[ny][nx] = 13;
} else {
// 大宝石
if (maxDefValue / minDefValue > 3) {
converted[nx][nx] = 15;
} else {
converted[ny][nx] = 14;
}
}
} else {
const n = Math.min(Math.floor(defRatio * 3), 2);
converted[ny][nx] = 13 + n;
}
const idx = Math.min(
Math.floor(defRatio * tiles.blueGems.length),
tiles.blueGems.length - 1
);
converted[ny][nx] = tiles.blueGems[idx];
break;
}
case 2: {
// 绿宝石,这里不可能只有一种数值了,不需要判断
if (itemMdefSet.size === 2) {
if (mdefRatio <= 0.5) {
// 小宝石
converted[ny][nx] = 16;
} else {
// 大宝石
if (maxMdefValue / minMdefValue > 3) {
converted[nx][nx] = 18;
} else {
converted[ny][nx] = 17;
}
}
} else {
const n = Math.min(Math.floor(mdefRatio * 3), 2);
converted[ny][nx] = 16 + n;
}
const idx = Math.min(
Math.floor(mdefRatio * tiles.greenGems.length),
tiles.greenGems.length - 1
);
converted[ny][nx] = tiles.greenGems[idx];
break;
}
}
@ -428,7 +419,7 @@ export function convertTowerMap(
!isFinite(converted[ny][nx]) ||
converted[ny][nx] < 0
) {
converted[ny][nx] = 0;
converted[ny][nx] = tiles.empty;
}
}
}

View File

@ -95,7 +95,44 @@ export interface IFloorInfo {
readonly wallDensityStd: number;
}
export interface IMapBlockConfig {
/** 空地的图块数字 */
readonly empty: number;
/** 墙的图块数字 */
readonly wall: number;
/** 装饰图块 */
readonly decoration: number;
/**
* 绿
* `specialDoors`
*/
readonly commonDoors: number[];
/** 机关门,普通机关门使用第一项作为结果,对于四种基础门及机关门之外的门,会选用第二项作为结果 */
readonly specialDoors: [number, number];
/**
* 绿
*
*/
readonly keys: number[];
/** 红宝石图块,平均分为若干档 */
readonly redGems: number[];
/** 蓝宝石图块,平均分为若干档 */
readonly blueGems: number[];
/** 绿宝石图块,平均分为若干档 */
readonly greenGems: number[];
/** 血瓶图块,平均分为若干档 */
readonly potions: number[];
/** 道具图块,依次为破炸飞,如果长度不够则取最后一个 */
readonly items: number[];
/** 怪物图块,按怪物强度分为若干当,强度为血量乘攻防和 */
readonly enemies: number[];
/** 入口图块 */
readonly entry: number;
}
export interface IAutoLabelConfig {
/** 地图图块分类 */
readonly classes: Readonly<IMapBlockConfig>;
/** 地图允许大小 */
readonly allowedSize: [number, number][];
/** 是否允许无用节点 */

View File

@ -2,15 +2,15 @@
export const emptyTiles = new Set([0]);
export const wallTiles = new Set([1]);
export const decorationTiles = new Set([2]);
export const commonDoorTiles = new Set([3, 4, 5]);
export const commonDoorTiles = new Set([3]);
export const specialDoorTiles = new Set([6]);
export const keyTiles = new Set([7, 8, 9]);
export const redGemTiles = new Set([10, 11, 12]);
export const blueGemTiles = new Set([13, 14, 15]);
export const greenGemTiles = new Set([16, 17, 18]);
export const potionTiles = new Set([19, 20, 21, 22]);
export const itemTiles = new Set([23, 24, 25]);
export const enemyTiles = new Set([26, 27, 28]);
export const keyTiles = new Set([7]);
export const redGemTiles = new Set([10]);
export const blueGemTiles = new Set([13]);
export const greenGemTiles = new Set([16]);
export const potionTiles = new Set([19]);
export const itemTiles = new Set([23]);
export const enemyTiles = new Set([26]);
export const entryTiles = new Set([29]);
// 组合图块定义

View File

@ -67,13 +67,18 @@ class EncoderFusion(nn.Module):
num_layers=2
)
self.norm = nn.LayerNorm(d_model)
self.fc = nn.Sequential(
nn.Linear(d_model * 2, d_model * 2),
nn.LayerNorm(d_model * 2),
nn.GELU()
)
def forward(self, logits):
x = self.norm(self.transformer(logits))
h_mean = torch.mean(x, dim=1)
h_max = torch.max(x, dim=1).values
h = torch.cat([h_mean, h_max], dim=1)
return h
return self.fc(h)
class VAEEncoder(nn.Module):
def __init__(self, device, tile_classes=32, latent_dim=32, width=13, height=13):