diff --git a/data/src/floor.ts b/data/src/floor.ts index 62b1fb9..ce0b22b 100644 --- a/data/src/floor.ts +++ b/data/src/floor.ts @@ -1,24 +1,15 @@ import { GinkaConfig } from './types'; const numMap: Record = { - 0: 0, // 空地 - 1: 1, // 墙壁 - 21: 2, // 钥匙 - 27: 3, // 红宝石 - 28: 4, // 蓝宝石 - 31: 5, // 血瓶 - 81: 6, // 门 - 201: 7, // 弱怪 - 202: 8, // 中怪 - 203: 9, // 强怪 - 87: 10, // 楼梯 - 88: 10, // 楼梯 - 91: 11, // 箭头 - 92: 11, // 箭头 - 93: 11, // 箭头 - 94: 11, // 箭头 - 53: 12, // 道具 - 29: 13 // 绿宝石 + 0: 0, + 1: 1, + 2: 2, + 91: 29, + 92: 29, + 93: 29, + 94: 29, + 87: 28, + 88: 28 }; export interface Enemy { @@ -45,39 +36,158 @@ function convert( clipped.push(row); } - // 2. 转换一般图块 - const mapping: Record = {}; - config.mapping.wall.forEach(v => (mapping[v] = 1)); - config.mapping.key.forEach(v => (mapping[v] = 2)); - config.mapping.redGem.forEach(v => (mapping[v] = 3)); - config.mapping.blueGem.forEach(v => (mapping[v] = 4)); - config.mapping.potion.forEach(v => (mapping[v] = 5)); - config.mapping.door.forEach(v => (mapping[v] = 6)); - config.mapping.item.forEach(v => (mapping[v] = 12)); - config.mapping.greenGem.forEach(v => (mapping[v] = 13)); - const yellowGem = new Set(config.mapping.yellowGem); + const res: number[][] = Array.from({ length: clipped.length }, () => + Array.from({ length: clipped[0].length }) + ); + + // 2. 初步映射 for (let nx = 0; nx < w; nx++) { for (let ny = 0; ny < h; ny++) { const tile = clipped[ny][nx]; - const enemy = enemyMap[tile]; - if (yellowGem.has(tile)) { - const rand = Math.random(); - if (rand < 2 / 5) { - clipped[ny][nx] = 3; - } else if (rand < 4 / 5) { - clipped[ny][nx] = 4; - } else { - clipped[ny][nx] = 13; - } - continue; + if (numMap[tile] !== void 0) { + res[ny][nx] = numMap[tile]; } - if (mapping[tile]) clipped[ny][nx] = mapping[tile]; - else if (numMap[tile]) clipped[ny][nx] = numMap[tile]; - else if (!enemy) clipped[ny][nx] = 0; } } - // 3. 转换怪物 + // 3. 转换一般图块 + const mapping: Record = {}; + const dict = config.mapping; + dict.wall.forEach(v => (mapping[v] = 1)); + dict.decoration.forEach(v => (mapping[v] = 2)); + dict.floor.forEach(v => (mapping[v] = 28)); + dict.arrow.forEach(v => (mapping[v] = 29)); + for (let nx = 0; nx < w; nx++) { + for (let ny = 0; ny < h; ny++) { + const tile = clipped[ny][nx]; + if (mapping[tile] !== void 0) res[ny][nx] = mapping[tile]; + } + } + + // 4. 转换含等级图块 + const redGemSet = new Set(); + const blueGemSet = new Set(); + const greenGemSet = new Set(); + const potionSet = new Set(); + for (let nx = 0; nx < w; nx++) { + for (let ny = 0; ny < h; ny++) { + const tile = clipped[ny][nx]; + if (dict.redGem[tile] !== void 0) { + redGemSet.add(dict.redGem[tile]); + } else if (dict.blueGem[tile] !== void 0) { + blueGemSet.add(dict.blueGem[tile]); + } else if (dict.greenGem[tile] !== void 0) { + greenGemSet.add(dict.greenGem[tile]); + } else if (dict.yellowGem[tile] !== void 0) { + redGemSet.add(dict.yellowGem[tile]); + blueGemSet.add(dict.yellowGem[tile]); + greenGemSet.add(dict.yellowGem[tile]); + } else if (dict.potion[tile] !== void 0) { + potionSet.add(dict.potion[tile]); + } + } + } + const minRedGem = Math.min(...redGemSet); + const maxRedGem = Math.max(...redGemSet); + const minBlueGem = Math.min(...blueGemSet); + const maxBlueGem = Math.max(...blueGemSet); + const minGreenGem = Math.min(...greenGemSet); + const maxGreenGem = Math.max(...greenGemSet); + const minPotion = Math.min(...potionSet); + const maxPotion = Math.max(...potionSet); + + for (let nx = 0; nx < w; nx++) { + for (let ny = 0; ny < h; ny++) { + const tile = clipped[ny][nx]; + if (dict.redGem[tile] !== void 0) { + const value = dict.redGem[tile]; + if (maxRedGem === minRedGem) { + res[ny][nx] = 10; + } else { + const level = Math.floor( + ((value - minRedGem) / (maxRedGem - minRedGem)) * 3 + ); + res[ny][nx] = 10 + level; + } + } else if (dict.blueGem[tile] !== void 0) { + const value = dict.blueGem[tile]; + if (maxBlueGem === minBlueGem) { + res[ny][nx] = 13; + } else { + const level = Math.floor( + ((value - minBlueGem) / (maxBlueGem - minBlueGem)) * 3 + ); + res[ny][nx] = 13 + level; + } + } else if (dict.greenGem[tile] !== void 0) { + const value = dict.greenGem[tile]; + if (maxGreenGem === minGreenGem) { + res[ny][nx] = 16; + } else { + const level = Math.floor( + ((value - minGreenGem) / (maxGreenGem - minGreenGem)) * + 3 + ); + res[ny][nx] = 16 + level; + } + } else if (dict.yellowGem[tile] !== void 0) { + const rand = Math.random(); + const value = dict.yellowGem[tile]; + if (rand < 2 / 5) { + if (maxRedGem === minRedGem) { + res[ny][nx] = 10; + } else { + const level = Math.floor( + ((value - minRedGem) / (maxRedGem - minRedGem)) * 3 + ); + res[ny][nx] = 10 + level; + } + } else if (rand < 4 / 5) { + if (maxBlueGem === minBlueGem) { + res[ny][nx] = 13; + } else { + const level = Math.floor( + ((value - minBlueGem) / (maxBlueGem - minBlueGem)) * + 3 + ); + res[ny][nx] = 13 + level; + } + } else { + if (maxGreenGem === minGreenGem) { + res[ny][nx] = 16; + } else { + const level = Math.floor( + ((value - minGreenGem) / + (maxGreenGem - minGreenGem)) * + 3 + ); + res[ny][nx] = 16 + level; + } + } + } else if (dict.potion[tile] !== void 0) { + const value = dict.potion[tile]; + if (maxGreenGem === minGreenGem) { + res[ny][nx] = 19; + } else { + const level = Math.floor( + ((value - minPotion) / (maxPotion - minPotion)) * 3 + ); + res[ny][nx] = 19 + level; + } + } else if (dict.door[tile] !== void 0) { + const level = dict.door[tile]; + res[ny][nx] = 3 + level; + } else if (dict.key[tile] !== void 0) { + const level = dict.key[tile]; + res[ny][nx] = 7 + level; + } else if (dict.item[tile] !== void 0) { + const level = dict.item[tile]; + res[ny][nx] = 22 + level; + } + } + } + + // 5. 转换怪物 const enemySet = new Set(); for (let nx = 0; nx < w; nx++) { for (let ny = 0; ny < h; ny++) { @@ -89,39 +199,30 @@ function convert( } const enemyArr = [...enemySet]; enemyArr.sort((a, b) => a.num - b.num); - if ( - enemyArr.length === 3 && - enemyArr[0].num === 201 && - enemyArr[1].num === 202 && - enemyArr[2].num === 203 - ) { - // pass - } else { - const attrs = [...enemySet].map(v => (v.atk + v.def) * v.hp); - const maxAttr = Math.max(...attrs); - const minAttr = Math.min(...attrs); - const delta = maxAttr - minAttr; - for (let ny = 0; ny < w; ny++) { - for (let nx = 0; nx < h; nx++) { - const tile = clipped[ny][nx]; - if (tile < 32) continue; - const enemy = enemyMap[tile]; - if (!enemy) continue; - // 替换为弱怪/中怪/强怪 - const attr = (enemy.atk + enemy.def) * enemy.hp; - const ad = attr - minAttr; - if (ad < delta / 3) { - clipped[ny][nx] = 7; - } else if (ad < (delta * 2) / 3) { - clipped[ny][nx] = 8; - } else { - clipped[ny][nx] = 9; - } + + const attrs = [...enemySet].map(v => (v.atk + v.def) * v.hp); + const maxAttr = Math.max(...attrs); + const minAttr = Math.min(...attrs); + const delta = maxAttr - minAttr; + for (let ny = 0; ny < w; ny++) { + for (let nx = 0; nx < h; nx++) { + const tile = clipped[ny][nx]; + const enemy = enemyMap[tile]; + if (!enemy) continue; + // 替换为弱怪/中怪/强怪 + const attr = (enemy.atk + enemy.def) * enemy.hp; + const ad = attr - minAttr; + if (ad < delta / 3 || delta === 0) { + res[ny][nx] = 25; + } else if (ad < (delta * 2) / 3) { + res[ny][nx] = 26; + } else { + res[ny][nx] = 27; } } } - return clipped; + return res; } export function convertFloor( diff --git a/data/src/process/ginka.ts b/data/src/process/ginka.ts index 7964fbf..bb52865 100644 --- a/data/src/process/ginka.ts +++ b/data/src/process/ginka.ts @@ -11,11 +11,15 @@ export function parseGinka(data: Map) { data.forEach((floor, key) => { const config = floor.config as GinkaConfig; - const text = config.data[floor.id] ?? []; + const data = config.data[floor.id] ?? { + tag: Array(64).fill(0), + val: Array(16).fill(0) + }; resolved[key] = { map: floor.map, size: [floor.map[0].length, floor.map.length], - text: text + tag: data.tag, + val: data.val }; i++; progress.update(i); diff --git a/data/src/types.ts b/data/src/types.ts index b438d62..edca260 100644 --- a/data/src/types.ts +++ b/data/src/types.ts @@ -12,23 +12,40 @@ export interface TowerInfo { config: BaseConfig; } +export interface GinkaData { + tag: number[]; + val: number[]; +} + export interface GinkaConfig extends BaseConfig { - data: Record; + data: Record; mapping: { - redGem: number[]; - blueGem: number[]; - greenGem: number[]; - yellowGem: number[]; - item: number[]; - potion: number[]; - key: number[]; - door: number[]; + /** 键表示图块,值表示等级或其增加的属性值,0是最低级,以此类推 */ + redGem: Record; + /** 键表示图块,值表示等级或其增加的属性值,0是最低级,以此类推 */ + blueGem: Record; + /** 键表示图块,值表示等级或其增加的属性值,0是最低级,以此类推 */ + greenGem: Record; + /** 键表示图块,值表示等级或其增加的属性值,0是最低级,以此类推 */ + yellowGem: Record; + /** 键表示图块,值表示等级或其增加的属性值,0是最低级,以此类推 */ + item: Record; + /** 键表示图块,值表示等级或其增加的属性值,0是最低级,以此类推 */ + potion: Record; + /** 键表示图块,值表示等级或其增加的属性值,0是最低级,以此类推 */ + key: Record; + /** 键表示图块,值表示等级或其增加的属性值,0是最低级,以此类推 */ + door: Record; + floor: number[]; + arrow: number[]; wall: number[]; + decoration: number[]; }; } export interface GinkaTrainData { - text: string[]; + tag: number[]; + val: number[]; map: number[][]; size: [number, number]; } diff --git a/ginka/train_wgan.py b/ginka/train_wgan.py index 3c2e0c3..386342a 100644 --- a/ginka/train_wgan.py +++ b/ginka/train_wgan.py @@ -36,9 +36,8 @@ from shared.image import matrix_to_image_cv # 19-21. 三种等级的血瓶 # 22-24. 三种等级的道具 # 25-27. 三种等级的怪物 -# 28-29. 留空 -# 30. 楼梯入口 -# 31. 箭头入口 +# 28. 楼梯入口 +# 29. 箭头入口 BATCH_SIZE = 16