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 魔塔,如果你想要贡献数据集,需要对你的魔塔进行手动数据处理,流程如下:
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` 文件夹打包发给我

View File

@ -4,12 +4,12 @@ const numMap: Record<number, number> = {
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])
];
}

View File

@ -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<string, FloorData>) {
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);

View File

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

View File

@ -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