diff --git a/README.md b/README.md index ded62e7..e4a4f0c 100644 --- a/README.md +++ b/README.md @@ -8,28 +8,34 @@ GINKA Model 内部集成了 Minamo Model 用做判别器,与 Ginka Model 对 对于 HTML5 魔塔,如果你想要贡献数据集,需要对你的魔塔进行手动数据处理,流程如下: -1. 选择楼层,可以是剧情层、战斗层等,但是需要满足下述条件 -2. 楼层中不应该有闲置怪,不应该在直线上有无间隔连续 3 个以上的怪物,不应该有无法到达的区域,不宜有过多的入口 -3. 最外面一层围上一圈墙壁(箭头楼层切换除外) -4. 所有箭头换成样板原版箭头(数字 91 至 94),所有上下楼梯换成样板原版楼梯(数字 87 和 88) -5. (可选,不改的话会自动按攻防和计算)怪物分为三个强度,弱怪,中怪,强怪,弱怪换为绿头怪(数字 201),中怪换成红头怪(数字 202),强怪换成青头怪(数字 203) -6. 在 `project` 文件夹下创建 `ginka-config.json` 文件,双击进入编辑,粘贴如下模板: +1. 在 `project` 文件夹下创建 `ginka-config.json` 文件,双击进入编辑,粘贴如下模板: ```json { "clip": { "defaults": [0, 0, 13, 13], - "special": { - "MT11": [3, 3, 7, 7] - } + "special": {} }, "mapping": { - "redGem": [27], - "blueGem": [28], - "greenGem": [29], - "yellowGem": [30], + "redGem": { + "27": 1 + }, + "blueGem": { + "28": 1 + }, + "greenGem": { + "29": 1 + }, + "yellowGem": { + "30": 1 + }, "item": [47, 49, 50, 51, 52, 53], - "potion": [31, 32, 33, 34], + "potion": { + "31": 100, + "32": 200, + "33": 400, + "34": 800 + }, "key": [21, 22, 23], "door": [81, 82, 83, 85], "wall": [1, 17] @@ -38,9 +44,7 @@ GINKA Model 内部集成了 Minamo Model 用做判别器,与 Ginka Model 对 } ``` -其中,`clip` 属性表示你的每张地图的那一部分会被当成数据集,例如填写 `[0, 0, 13, 13]` 就会让坐标为 `(0, 0)`,长宽为 `(13, 13)` 的矩形内容作为数据集。`special` 属性允许你针对单独的某几层设置不同的裁剪方式,例如设置 `MT11` 为 `[3, 3, 7, 7]` 等,如果没有设置默认使用 `defaults` 的裁剪方式。最好保证每个楼层大小一致,不然我还要手动分类。 +其中,`clip` 属性表示你的每张地图的那一部分会被当成数据集,例如填写 `[0, 0, 13, 13]` 就会让坐标为 `(0, 0)`,长宽为 `(13, 13)` 的矩形内容作为数据集。`special` 不用管。 -`mapping` 中表示每种图块的图块数字,如果自己添加了一些新的宝石、门、道具等,需要在里面填写 - -7. 在全塔属性中的楼层列表中去除不在数据集内的楼层 -8. 将 `project` 文件夹打包发给我即可 +2. 使用 [在线工具](https://unanmed.github.io/ginka-process) 处理数据,需要给每个地图添加标签,为每个图块分配种类,有一些图块包含多种等级,需要填写正确。 +3. 将 `project` 文件夹打包发给我 diff --git a/data/src/floor.ts b/data/src/floor.ts index ce0b22b..e308bfa 100644 --- a/data/src/floor.ts +++ b/data/src/floor.ts @@ -4,12 +4,12 @@ const numMap: Record = { 0: 0, 1: 1, 2: 2, - 91: 29, - 92: 29, - 93: 29, - 94: 29, - 87: 28, - 88: 28 + 91: 30, + 92: 30, + 93: 30, + 94: 30, + 87: 29, + 88: 29 }; export interface Enemy { @@ -55,8 +55,8 @@ function convert( 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)); + dict.floor.forEach(v => (mapping[v] = 29)); + dict.arrow.forEach(v => (mapping[v] = 30)); for (let nx = 0; nx < w; nx++) { for (let ny = 0; ny < h; ny++) { const tile = clipped[ny][nx]; @@ -101,32 +101,43 @@ function convert( const tile = clipped[ny][nx]; if (dict.redGem[tile] !== void 0) { const value = dict.redGem[tile]; - if (maxRedGem === minRedGem) { + if (maxRedGem - minRedGem < 1e-8) { res[ny][nx] = 10; } else { - const level = Math.floor( - ((value - minRedGem) / (maxRedGem - minRedGem)) * 3 + const level = Math.min( + Math.floor( + ((value - minRedGem) / (maxRedGem - minRedGem)) * 3 + ), + 2 ); res[ny][nx] = 10 + level; } } else if (dict.blueGem[tile] !== void 0) { const value = dict.blueGem[tile]; - if (maxBlueGem === minBlueGem) { + if (maxBlueGem - minBlueGem < 1e-8) { res[ny][nx] = 13; } else { - const level = Math.floor( - ((value - minBlueGem) / (maxBlueGem - minBlueGem)) * 3 + const level = Math.min( + Math.floor( + ((value - minBlueGem) / (maxBlueGem - minBlueGem)) * + 3 + ), + 2 ); res[ny][nx] = 13 + level; } } else if (dict.greenGem[tile] !== void 0) { const value = dict.greenGem[tile]; - if (maxGreenGem === minGreenGem) { + if (maxGreenGem - minGreenGem < 1e-8) { res[ny][nx] = 16; } else { - const level = Math.floor( - ((value - minGreenGem) / (maxGreenGem - minGreenGem)) * - 3 + const level = Math.min( + Math.floor( + ((value - minGreenGem) / + (maxGreenGem - minGreenGem)) * + 3 + ), + 2 ); res[ny][nx] = 16 + level; } @@ -134,43 +145,58 @@ function convert( const rand = Math.random(); const value = dict.yellowGem[tile]; if (rand < 2 / 5) { - if (maxRedGem === minRedGem) { + if (maxRedGem - minRedGem < 1e-8) { res[ny][nx] = 10; } else { - const level = Math.floor( - ((value - minRedGem) / (maxRedGem - minRedGem)) * 3 + const level = Math.min( + Math.floor( + ((value - minRedGem) / + (maxRedGem - minRedGem)) * + 3 + ), + 2 ); res[ny][nx] = 10 + level; } } else if (rand < 4 / 5) { - if (maxBlueGem === minBlueGem) { + if (maxBlueGem - minBlueGem < 1e-8) { res[ny][nx] = 13; } else { - const level = Math.floor( - ((value - minBlueGem) / (maxBlueGem - minBlueGem)) * - 3 + const level = Math.min( + Math.floor( + ((value - minBlueGem) / + (maxBlueGem - minBlueGem)) * + 3 + ), + 2 ); res[ny][nx] = 13 + level; } } else { - if (maxGreenGem === minGreenGem) { + if (maxGreenGem - minGreenGem < 1e-8) { res[ny][nx] = 16; } else { - const level = Math.floor( - ((value - minGreenGem) / - (maxGreenGem - minGreenGem)) * - 3 + const level = Math.min( + Math.floor( + ((value - minGreenGem) / + (maxGreenGem - minGreenGem)) * + 3 + ), + 2 ); res[ny][nx] = 16 + level; } } } else if (dict.potion[tile] !== void 0) { const value = dict.potion[tile]; - if (maxGreenGem === minGreenGem) { + if (maxGreenGem - minGreenGem < 1e-8) { res[ny][nx] = 19; } else { - const level = Math.floor( - ((value - minPotion) / (maxPotion - minPotion)) * 3 + const level = Math.min( + Math.floor( + ((value - minPotion) / (maxPotion - minPotion)) * 4 + ), + 3 ); res[ny][nx] = 19 + level; } @@ -233,3 +259,31 @@ export function convertFloor( ) { return convert(map, clip, config, enemyMap); } + +export function getCount(map: number[][], tiles: number[]) { + let n = 0; + map.flat().forEach(v => { + if (tiles.includes(v)) n++; + }); + return n; +} + +export function getRatio(map: number[][], tiles: number[]) { + const area = map.length * map[0].length; + return getCount(map, tiles) / area; +} + +function range(from: number, to: number) { + const length = to - from; + return Array.from({ length }, (_, i) => i + from); +} + +export function getGinkaRatio(map: number[][]) { + return [ + getRatio(map, [1, ...range(3, 32)]), + getRatio(map, [26, 27, 28]), + getRatio(map, range(7, 26)), + getRatio(map, [3, 4, 5, 6]), + getCount(map, [29, 30]) + ]; +} diff --git a/data/src/process/ginka.ts b/data/src/process/ginka.ts index bb52865..e518681 100644 --- a/data/src/process/ginka.ts +++ b/data/src/process/ginka.ts @@ -1,4 +1,5 @@ import { SingleBar, Presets } from 'cli-progress'; +import { getGinkaRatio } from 'src/floor'; import { GinkaTrainData, GinkaConfig, GinkaDataset } from 'src/types'; import { FloorData } from 'src/utils'; @@ -12,14 +13,13 @@ export function parseGinka(data: Map) { data.forEach((floor, key) => { const config = floor.config as GinkaConfig; const data = config.data[floor.id] ?? { - tag: Array(64).fill(0), - val: Array(16).fill(0) + tag: Array(64).fill(0) }; resolved[key] = { map: floor.map, size: [floor.map[0].length, floor.map.length], tag: data.tag, - val: data.val + val: getGinkaRatio(floor.map) }; i++; progress.update(i); diff --git a/data/src/types.ts b/data/src/types.ts index edca260..5fa3846 100644 --- a/data/src/types.ts +++ b/data/src/types.ts @@ -14,7 +14,6 @@ export interface TowerInfo { export interface GinkaData { tag: number[]; - val: number[]; } export interface GinkaConfig extends BaseConfig { diff --git a/ginka/train_wgan.py b/ginka/train_wgan.py index 386342a..02ce124 100644 --- a/ginka/train_wgan.py +++ b/ginka/train_wgan.py @@ -33,11 +33,11 @@ from shared.image import matrix_to_image_cv # 10-12. 三种等级的红宝石 # 13-15. 三种等级的蓝宝石 # 16-18. 三种等级的绿宝石 -# 19-21. 三种等级的血瓶 -# 22-24. 三种等级的道具 -# 25-27. 三种等级的怪物 -# 28. 楼梯入口 -# 29. 箭头入口 +# 19-22. 四种等级的血瓶 +# 23-25. 三种等级的道具 +# 26-28. 三种等级的怪物 +# 29. 楼梯入口 +# 30. 箭头入口 BATCH_SIZE = 16