feat: 数据集密度占比

This commit is contained in:
unanmed 2025-04-29 21:57:55 +08:00
parent 8add448195
commit 44d6c7410e
5 changed files with 118 additions and 61 deletions

View File

@ -8,28 +8,34 @@ GINKA Model 内部集成了 Minamo Model 用做判别器,与 Ginka Model 对
对于 HTML5 魔塔,如果你想要贡献数据集,需要对你的魔塔进行手动数据处理,流程如下: 对于 HTML5 魔塔,如果你想要贡献数据集,需要对你的魔塔进行手动数据处理,流程如下:
1. 选择楼层,可以是剧情层、战斗层等,但是需要满足下述条件 1. 在 `project` 文件夹下创建 `ginka-config.json` 文件,双击进入编辑,粘贴如下模板:
2. 楼层中不应该有闲置怪,不应该在直线上有无间隔连续 3 个以上的怪物,不应该有无法到达的区域,不宜有过多的入口
3. 最外面一层围上一圈墙壁(箭头楼层切换除外)
4. 所有箭头换成样板原版箭头(数字 91 至 94所有上下楼梯换成样板原版楼梯数字 87 和 88
5. (可选,不改的话会自动按攻防和计算)怪物分为三个强度,弱怪,中怪,强怪,弱怪换为绿头怪(数字 201中怪换成红头怪数字 202强怪换成青头怪数字 203
6. 在 `project` 文件夹下创建 `ginka-config.json` 文件,双击进入编辑,粘贴如下模板:
```json ```json
{ {
"clip": { "clip": {
"defaults": [0, 0, 13, 13], "defaults": [0, 0, 13, 13],
"special": { "special": {}
"MT11": [3, 3, 7, 7]
}
}, },
"mapping": { "mapping": {
"redGem": [27], "redGem": {
"blueGem": [28], "27": 1
"greenGem": [29], },
"yellowGem": [30], "blueGem": {
"28": 1
},
"greenGem": {
"29": 1
},
"yellowGem": {
"30": 1
},
"item": [47, 49, 50, 51, 52, 53], "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], "key": [21, 22, 23],
"door": [81, 82, 83, 85], "door": [81, 82, 83, 85],
"wall": [1, 17] "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` 中表示每种图块的图块数字,如果自己添加了一些新的宝石、门、道具等,需要在里面填写 2. 使用 [在线工具](https://unanmed.github.io/ginka-process) 处理数据,需要给每个地图添加标签,为每个图块分配种类,有一些图块包含多种等级,需要填写正确。
3. 将 `project` 文件夹打包发给我
7. 在全塔属性中的楼层列表中去除不在数据集内的楼层
8. 将 `project` 文件夹打包发给我即可

View File

@ -4,12 +4,12 @@ const numMap: Record<number, number> = {
0: 0, 0: 0,
1: 1, 1: 1,
2: 2, 2: 2,
91: 29, 91: 30,
92: 29, 92: 30,
93: 29, 93: 30,
94: 29, 94: 30,
87: 28, 87: 29,
88: 28 88: 29
}; };
export interface Enemy { export interface Enemy {
@ -55,8 +55,8 @@ function convert(
const dict = config.mapping; const dict = config.mapping;
dict.wall.forEach(v => (mapping[v] = 1)); dict.wall.forEach(v => (mapping[v] = 1));
dict.decoration.forEach(v => (mapping[v] = 2)); dict.decoration.forEach(v => (mapping[v] = 2));
dict.floor.forEach(v => (mapping[v] = 28)); dict.floor.forEach(v => (mapping[v] = 29));
dict.arrow.forEach(v => (mapping[v] = 29)); dict.arrow.forEach(v => (mapping[v] = 30));
for (let nx = 0; nx < w; nx++) { for (let nx = 0; nx < w; nx++) {
for (let ny = 0; ny < h; ny++) { for (let ny = 0; ny < h; ny++) {
const tile = clipped[ny][nx]; const tile = clipped[ny][nx];
@ -101,32 +101,43 @@ function convert(
const tile = clipped[ny][nx]; const tile = clipped[ny][nx];
if (dict.redGem[tile] !== void 0) { if (dict.redGem[tile] !== void 0) {
const value = dict.redGem[tile]; const value = dict.redGem[tile];
if (maxRedGem === minRedGem) { if (maxRedGem - minRedGem < 1e-8) {
res[ny][nx] = 10; res[ny][nx] = 10;
} else { } else {
const level = Math.floor( const level = Math.min(
((value - minRedGem) / (maxRedGem - minRedGem)) * 3 Math.floor(
((value - minRedGem) / (maxRedGem - minRedGem)) * 3
),
2
); );
res[ny][nx] = 10 + level; res[ny][nx] = 10 + level;
} }
} else if (dict.blueGem[tile] !== void 0) { } else if (dict.blueGem[tile] !== void 0) {
const value = dict.blueGem[tile]; const value = dict.blueGem[tile];
if (maxBlueGem === minBlueGem) { if (maxBlueGem - minBlueGem < 1e-8) {
res[ny][nx] = 13; res[ny][nx] = 13;
} else { } else {
const level = Math.floor( const level = Math.min(
((value - minBlueGem) / (maxBlueGem - minBlueGem)) * 3 Math.floor(
((value - minBlueGem) / (maxBlueGem - minBlueGem)) *
3
),
2
); );
res[ny][nx] = 13 + level; res[ny][nx] = 13 + level;
} }
} else if (dict.greenGem[tile] !== void 0) { } else if (dict.greenGem[tile] !== void 0) {
const value = dict.greenGem[tile]; const value = dict.greenGem[tile];
if (maxGreenGem === minGreenGem) { if (maxGreenGem - minGreenGem < 1e-8) {
res[ny][nx] = 16; res[ny][nx] = 16;
} else { } else {
const level = Math.floor( const level = Math.min(
((value - minGreenGem) / (maxGreenGem - minGreenGem)) * Math.floor(
3 ((value - minGreenGem) /
(maxGreenGem - minGreenGem)) *
3
),
2
); );
res[ny][nx] = 16 + level; res[ny][nx] = 16 + level;
} }
@ -134,43 +145,58 @@ function convert(
const rand = Math.random(); const rand = Math.random();
const value = dict.yellowGem[tile]; const value = dict.yellowGem[tile];
if (rand < 2 / 5) { if (rand < 2 / 5) {
if (maxRedGem === minRedGem) { if (maxRedGem - minRedGem < 1e-8) {
res[ny][nx] = 10; res[ny][nx] = 10;
} else { } else {
const level = Math.floor( const level = Math.min(
((value - minRedGem) / (maxRedGem - minRedGem)) * 3 Math.floor(
((value - minRedGem) /
(maxRedGem - minRedGem)) *
3
),
2
); );
res[ny][nx] = 10 + level; res[ny][nx] = 10 + level;
} }
} else if (rand < 4 / 5) { } else if (rand < 4 / 5) {
if (maxBlueGem === minBlueGem) { if (maxBlueGem - minBlueGem < 1e-8) {
res[ny][nx] = 13; res[ny][nx] = 13;
} else { } else {
const level = Math.floor( const level = Math.min(
((value - minBlueGem) / (maxBlueGem - minBlueGem)) * Math.floor(
3 ((value - minBlueGem) /
(maxBlueGem - minBlueGem)) *
3
),
2
); );
res[ny][nx] = 13 + level; res[ny][nx] = 13 + level;
} }
} else { } else {
if (maxGreenGem === minGreenGem) { if (maxGreenGem - minGreenGem < 1e-8) {
res[ny][nx] = 16; res[ny][nx] = 16;
} else { } else {
const level = Math.floor( const level = Math.min(
((value - minGreenGem) / Math.floor(
(maxGreenGem - minGreenGem)) * ((value - minGreenGem) /
3 (maxGreenGem - minGreenGem)) *
3
),
2
); );
res[ny][nx] = 16 + level; res[ny][nx] = 16 + level;
} }
} }
} else if (dict.potion[tile] !== void 0) { } else if (dict.potion[tile] !== void 0) {
const value = dict.potion[tile]; const value = dict.potion[tile];
if (maxGreenGem === minGreenGem) { if (maxGreenGem - minGreenGem < 1e-8) {
res[ny][nx] = 19; res[ny][nx] = 19;
} else { } else {
const level = Math.floor( const level = Math.min(
((value - minPotion) / (maxPotion - minPotion)) * 3 Math.floor(
((value - minPotion) / (maxPotion - minPotion)) * 4
),
3
); );
res[ny][nx] = 19 + level; res[ny][nx] = 19 + level;
} }
@ -233,3 +259,31 @@ export function convertFloor(
) { ) {
return convert(map, clip, config, enemyMap); 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])
];
}

View File

@ -1,4 +1,5 @@
import { SingleBar, Presets } from 'cli-progress'; import { SingleBar, Presets } from 'cli-progress';
import { getGinkaRatio } from 'src/floor';
import { GinkaTrainData, GinkaConfig, GinkaDataset } from 'src/types'; import { GinkaTrainData, GinkaConfig, GinkaDataset } from 'src/types';
import { FloorData } from 'src/utils'; import { FloorData } from 'src/utils';
@ -12,14 +13,13 @@ export function parseGinka(data: Map<string, FloorData>) {
data.forEach((floor, key) => { data.forEach((floor, key) => {
const config = floor.config as GinkaConfig; const config = floor.config as GinkaConfig;
const data = config.data[floor.id] ?? { const data = config.data[floor.id] ?? {
tag: Array(64).fill(0), tag: Array(64).fill(0)
val: Array(16).fill(0)
}; };
resolved[key] = { resolved[key] = {
map: floor.map, map: floor.map,
size: [floor.map[0].length, floor.map.length], size: [floor.map[0].length, floor.map.length],
tag: data.tag, tag: data.tag,
val: data.val val: getGinkaRatio(floor.map)
}; };
i++; i++;
progress.update(i); progress.update(i);

View File

@ -14,7 +14,6 @@ export interface TowerInfo {
export interface GinkaData { export interface GinkaData {
tag: number[]; tag: number[];
val: number[];
} }
export interface GinkaConfig extends BaseConfig { export interface GinkaConfig extends BaseConfig {

View File

@ -33,11 +33,11 @@ from shared.image import matrix_to_image_cv
# 10-12. 三种等级的红宝石 # 10-12. 三种等级的红宝石
# 13-15. 三种等级的蓝宝石 # 13-15. 三种等级的蓝宝石
# 16-18. 三种等级的绿宝石 # 16-18. 三种等级的绿宝石
# 19-21. 三种等级的血瓶 # 19-22. 四种等级的血瓶
# 22-24. 三种等级的道具 # 23-25. 三种等级的道具
# 25-27. 三种等级的怪物 # 26-28. 三种等级的怪物
# 28. 楼梯入口 # 29. 楼梯入口
# 29. 箭头入口 # 30. 箭头入口
BATCH_SIZE = 16 BATCH_SIZE = 16