diff --git a/data/src/auto.ts b/data/src/auto.ts index 1d60eb5..359acfa 100644 --- a/data/src/auto.ts +++ b/data/src/auto.ts @@ -103,7 +103,14 @@ const ignoredTower: string[] = [ 'zjhy1', 'zrsz', 'zuozuozhieX_renewed', - 'ZZZE50Ex' + 'ZZZE50Ex', + '1DQSJ', + 'againstcov', + 'Alive', + 'Aria', + 'atom', + 'bdf1', + 'bingyuanhouzhuan' ]; const ignoredFloor: Record = { @@ -119,7 +126,8 @@ const ignoredFloor: Record = { 'MT71', 'ML26', 'ML27', - 'MQ28' + 'MQ28', + 'MT104' ], cxzl: [ 'MT63', @@ -153,11 +161,11 @@ const ignoredFloor: Record = { 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 = { 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; } diff --git a/data/src/auto/auto.ts b/data/src/auto/auto.ts index a89fa28..a47e561 100644 --- a/data/src/auto/auto.ts +++ b/data/src/auto/auto.ts @@ -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, diff --git a/data/src/auto/info.ts b/data/src/auto/info.ts index 9906b51..c5c22bb 100644 --- a/data/src/auto/info.ts +++ b/data/src/auto/info.ts @@ -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 */ diff --git a/data/src/auto/tower.ts b/data/src/auto/tower.ts index 40ef47f..5972348 100644 --- a/data/src/auto/tower.ts +++ b/data/src/auto/tower.ts @@ -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; } } } diff --git a/data/src/auto/types.ts b/data/src/auto/types.ts index 236cd96..1ab92ca 100644 --- a/data/src/auto/types.ts +++ b/data/src/auto/types.ts @@ -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; /** 地图允许大小 */ readonly allowedSize: [number, number][]; /** 是否允许无用节点 */ diff --git a/data/src/shared.ts b/data/src/shared.ts index d346f32..4d38968 100644 --- a/data/src/shared.ts +++ b/data/src/shared.ts @@ -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]); // 组合图块定义 diff --git a/ginka/vae_rnn/encoder.py b/ginka/vae_rnn/encoder.py index 624976b..c42384e 100644 --- a/ginka/vae_rnn/encoder.py +++ b/ginka/vae_rnn/encoder.py @@ -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):