feat: ginka 数据集生成工具生成新版数据集

This commit is contained in:
unanmed 2025-04-29 20:47:05 +08:00
parent 44b90e7630
commit 8add448195
4 changed files with 209 additions and 88 deletions

View File

@ -1,24 +1,15 @@
import { GinkaConfig } from './types';
const numMap: Record<number, number> = {
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<number, number> = {};
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<number, number> = {};
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<number>();
const blueGemSet = new Set<number>();
const greenGemSet = new Set<number>();
const potionSet = new Set<number>();
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<Enemy>();
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(

View File

@ -11,11 +11,15 @@ export function parseGinka(data: Map<string, FloorData>) {
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);

View File

@ -12,23 +12,40 @@ export interface TowerInfo {
config: BaseConfig;
}
export interface GinkaData {
tag: number[];
val: number[];
}
export interface GinkaConfig extends BaseConfig {
data: Record<string, string[]>;
data: Record<string, GinkaData>;
mapping: {
redGem: number[];
blueGem: number[];
greenGem: number[];
yellowGem: number[];
item: number[];
potion: number[];
key: number[];
door: number[];
/** 键表示图块值表示等级或其增加的属性值0是最低级以此类推 */
redGem: Record<number, number>;
/** 键表示图块值表示等级或其增加的属性值0是最低级以此类推 */
blueGem: Record<number, number>;
/** 键表示图块值表示等级或其增加的属性值0是最低级以此类推 */
greenGem: Record<number, number>;
/** 键表示图块值表示等级或其增加的属性值0是最低级以此类推 */
yellowGem: Record<number, number>;
/** 键表示图块值表示等级或其增加的属性值0是最低级以此类推 */
item: Record<number, number>;
/** 键表示图块值表示等级或其增加的属性值0是最低级以此类推 */
potion: Record<number, number>;
/** 键表示图块值表示等级或其增加的属性值0是最低级以此类推 */
key: Record<number, number>;
/** 键表示图块值表示等级或其增加的属性值0是最低级以此类推 */
door: Record<number, number>;
floor: number[];
arrow: number[];
wall: number[];
decoration: number[];
};
}
export interface GinkaTrainData {
text: string[];
tag: number[];
val: number[];
map: number[][];
size: [number, number];
}

View File

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