mirror of
https://github.com/unanmed/ginka-generator.git
synced 2026-05-14 04:41:12 +08:00
feat: 单独对 Apeiria 塔提供支持
This commit is contained in:
parent
c34756016d
commit
98f7a9cdcf
@ -14,12 +14,14 @@
|
||||
"license": "ISC",
|
||||
"packageManager": "pnpm@10.5.2",
|
||||
"devDependencies": {
|
||||
"@types/cli-progress": "^3.11.6",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/node": "^22.13.10",
|
||||
"tsx": "^4.19.3",
|
||||
"vitest": "^3.0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"cli-progress": "^3.12.0",
|
||||
"fs-extra": "^11.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,10 +8,16 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
cli-progress:
|
||||
specifier: ^3.12.0
|
||||
version: 3.12.0
|
||||
fs-extra:
|
||||
specifier: ^11.3.0
|
||||
version: 11.3.0
|
||||
devDependencies:
|
||||
'@types/cli-progress':
|
||||
specifier: ^3.11.6
|
||||
version: 3.11.6
|
||||
'@types/fs-extra':
|
||||
specifier: ^11.0.4
|
||||
version: 11.0.4
|
||||
@ -285,6 +291,9 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@types/cli-progress@3.11.6':
|
||||
resolution: {integrity: sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==}
|
||||
|
||||
'@types/estree@1.0.6':
|
||||
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
|
||||
|
||||
@ -326,6 +335,10 @@ packages:
|
||||
'@vitest/utils@3.0.8':
|
||||
resolution: {integrity: sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==}
|
||||
|
||||
ansi-regex@5.0.1:
|
||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
assertion-error@2.0.1:
|
||||
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||
engines: {node: '>=12'}
|
||||
@ -342,6 +355,10 @@ packages:
|
||||
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
cli-progress@3.12.0:
|
||||
resolution: {integrity: sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
debug@4.4.0:
|
||||
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
|
||||
engines: {node: '>=6.0'}
|
||||
@ -355,6 +372,9 @@ packages:
|
||||
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
|
||||
es-module-lexer@1.6.0:
|
||||
resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==}
|
||||
|
||||
@ -385,6 +405,10 @@ packages:
|
||||
graceful-fs@4.2.11:
|
||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||
|
||||
is-fullwidth-code-point@3.0.0:
|
||||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
jsonfile@6.1.0:
|
||||
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
||||
|
||||
@ -437,6 +461,14 @@ packages:
|
||||
std-env@3.8.1:
|
||||
resolution: {integrity: sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==}
|
||||
|
||||
string-width@4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
strip-ansi@6.0.1:
|
||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
tinybench@2.9.0:
|
||||
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||
|
||||
@ -681,6 +713,10 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc@4.35.0':
|
||||
optional: true
|
||||
|
||||
'@types/cli-progress@3.11.6':
|
||||
dependencies:
|
||||
'@types/node': 22.13.10
|
||||
|
||||
'@types/estree@1.0.6': {}
|
||||
|
||||
'@types/fs-extra@11.0.4':
|
||||
@ -736,6 +772,8 @@ snapshots:
|
||||
loupe: 3.1.3
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
ansi-regex@5.0.1: {}
|
||||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
cac@6.7.14: {}
|
||||
@ -750,12 +788,18 @@ snapshots:
|
||||
|
||||
check-error@2.1.1: {}
|
||||
|
||||
cli-progress@3.12.0:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
|
||||
debug@4.4.0:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
deep-eql@5.0.2: {}
|
||||
|
||||
emoji-regex@8.0.0: {}
|
||||
|
||||
es-module-lexer@1.6.0: {}
|
||||
|
||||
esbuild@0.25.1:
|
||||
@ -807,6 +851,8 @@ snapshots:
|
||||
|
||||
graceful-fs@4.2.11: {}
|
||||
|
||||
is-fullwidth-code-point@3.0.0: {}
|
||||
|
||||
jsonfile@6.1.0:
|
||||
dependencies:
|
||||
universalify: 2.0.1
|
||||
@ -870,6 +916,16 @@ snapshots:
|
||||
|
||||
std-env@3.8.1: {}
|
||||
|
||||
string-width@4.2.3:
|
||||
dependencies:
|
||||
emoji-regex: 8.0.0
|
||||
is-fullwidth-code-point: 3.0.0
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
strip-ansi@6.0.1:
|
||||
dependencies:
|
||||
ansi-regex: 5.0.1
|
||||
|
||||
tinybench@2.9.0: {}
|
||||
|
||||
tinyexec@0.3.2: {}
|
||||
|
||||
@ -18,11 +18,48 @@ const numMap: Record<number, number> = {
|
||||
53: 12 // 道具
|
||||
};
|
||||
|
||||
export function convertFloor(
|
||||
const apeiriaMap: Record<number, number> = {
|
||||
0: 0, // 空地
|
||||
1: 1, // 墙壁
|
||||
224: 1, // 吸血鬼,视为墙壁
|
||||
21: 2, // 黄钥匙
|
||||
22: 2, // 蓝钥匙
|
||||
23: 2, // 红钥匙
|
||||
27: 3, // 红宝石
|
||||
28: 4, // 蓝宝石
|
||||
29: 0, // 绿宝石
|
||||
31: 5, // 红血瓶
|
||||
32: 5, // 蓝血瓶
|
||||
33: 5, // 绿血瓶
|
||||
34: 5, // 黄血瓶
|
||||
81: 6, // 门
|
||||
201: 7, // 弱怪
|
||||
202: 8, // 中怪
|
||||
203: 9, // 强怪
|
||||
87: 10, // 楼梯
|
||||
88: 10, // 楼梯
|
||||
161: 11, // 箭头
|
||||
162: 11, // 箭头
|
||||
163: 11, // 箭头
|
||||
164: 11, // 箭头
|
||||
53: 12, // 幸运金币
|
||||
50: 12, // 飞
|
||||
49: 12, // 炸
|
||||
47: 12 // 破
|
||||
};
|
||||
|
||||
export interface ApeiriaEnemy {
|
||||
hp: number;
|
||||
atk: number;
|
||||
def: number;
|
||||
}
|
||||
|
||||
function convert(
|
||||
map: number[][],
|
||||
[x, y, w, h]: [number, number, number, number],
|
||||
name: string,
|
||||
floorId: string
|
||||
floorId: string,
|
||||
numMap: Record<number, number>
|
||||
) {
|
||||
const clipped: number[][] = [];
|
||||
|
||||
@ -42,3 +79,73 @@ export function convertFloor(
|
||||
|
||||
return clipped;
|
||||
}
|
||||
|
||||
function convertApeiriaEnemy(
|
||||
map: number[][],
|
||||
enemyMap: Record<number, ApeiriaEnemy>
|
||||
) {
|
||||
const width = map[0].length;
|
||||
const height = map.length;
|
||||
const enemy = new Set<ApeiriaEnemy>();
|
||||
for (let ny = 0; ny < height; ny++) {
|
||||
for (let nx = 0; nx < width; nx++) {
|
||||
const tile = map[ny][nx];
|
||||
if (tile > 200 && tile <= 280) {
|
||||
// 这些是怪物
|
||||
if (enemyMap[tile]) enemy.add(enemyMap[tile]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const attrs = [...enemy].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 < height; ny++) {
|
||||
for (let nx = 0; nx < width; nx++) {
|
||||
const tile = map[ny][nx];
|
||||
if (tile > 200 && tile <= 280) {
|
||||
// 这些是怪物
|
||||
if (enemyMap[tile]) {
|
||||
// 替换为弱怪/中怪/强怪
|
||||
const enemy = enemyMap[tile];
|
||||
const attr = (enemy.atk + enemy.def) * enemy.hp;
|
||||
const ad = attr - minAttr;
|
||||
if (ad < delta / 3) {
|
||||
map[ny][nx] = 201;
|
||||
} else if (ad < (delta * 2) / 3) {
|
||||
map[ny][nx] = 202;
|
||||
} else {
|
||||
map[ny][nx] = 203;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
export function convertFloor(
|
||||
map: number[][],
|
||||
clip: [number, number, number, number],
|
||||
name: string,
|
||||
floorId: string
|
||||
) {
|
||||
return convert(map, clip, name, floorId, numMap);
|
||||
}
|
||||
|
||||
export function convertApeiriaMap(
|
||||
map: number[][],
|
||||
clip: [number, number, number, number],
|
||||
name: string,
|
||||
floorId: string,
|
||||
enemyMap: Record<number, ApeiriaEnemy>
|
||||
) {
|
||||
return convert(
|
||||
convertApeiriaEnemy(map, enemyMap),
|
||||
clip,
|
||||
name,
|
||||
floorId,
|
||||
apeiriaMap
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import { mirrorMapX, mirrorMapY, rotateMap } from './topology/transform';
|
||||
import { directions, tileType } from './topology/graph';
|
||||
import { calculateVisualSimilarity } from './vision/similarity';
|
||||
import { BaseConfig } from './types';
|
||||
import { Presets, SingleBar } from 'cli-progress';
|
||||
|
||||
interface MinamoConfig extends BaseConfig {}
|
||||
|
||||
@ -32,9 +33,9 @@ function chooseFrom<T>(arr: T[], n: number): T[] {
|
||||
return copy.slice(0, n);
|
||||
}
|
||||
|
||||
function choosePair(n: number) {
|
||||
function choosePair(n: number, max: number = 1000) {
|
||||
const totalCount = Math.round((n * (n - 1)) / 2);
|
||||
const count = Math.min(totalCount, 1000);
|
||||
const count = Math.min(totalCount, max);
|
||||
const pairs: number[] = [];
|
||||
for (let i = 0; i < n; i++) {
|
||||
for (let j = i + 1; j < n; j++) {
|
||||
@ -79,7 +80,7 @@ function generateTransformData(
|
||||
}
|
||||
}
|
||||
// 随机抽取最多两个
|
||||
const trans = chooseFrom(types, Math.floor(Math.random() * 3));
|
||||
const trans = chooseFrom(types, Math.floor(Math.random() * 2));
|
||||
return trans
|
||||
.map(([rot, flip]) => {
|
||||
const com1 = `${id1}.${rot}.${flip}:${id1}`;
|
||||
@ -153,7 +154,7 @@ function generateSimilarData(id: string, map: number[][]) {
|
||||
// 生成最多五个微调地图
|
||||
const width = map[0].length;
|
||||
const height = map.length;
|
||||
const num = Math.floor(Math.random() * 6);
|
||||
const num = Math.floor(Math.random() * 3);
|
||||
const res: [id: string, data: MinamoTrainData][] = [];
|
||||
|
||||
for (let i = 0; i < num; i++) {
|
||||
@ -210,7 +211,11 @@ function generateDataset(
|
||||
): Record<string, MinamoTrainData> {
|
||||
const data: Record<string, MinamoTrainData> = {};
|
||||
|
||||
pairs.forEach(v => {
|
||||
const progress = new SingleBar({}, Presets.shades_classic);
|
||||
|
||||
progress.start(pairs.length, 0);
|
||||
|
||||
pairs.forEach((v, i) => {
|
||||
const num1 = Math.floor(v / floorIds.length);
|
||||
const num2 = v % floorIds.length;
|
||||
const id1 = floorIds[num1];
|
||||
@ -268,8 +273,11 @@ function generateDataset(
|
||||
// 地图微调训练集
|
||||
Object.assign(data, Object.fromEntries(generateSimilarData(id1, map1)));
|
||||
// Object.assign(data, Object.fromEntries(generateSimilarData(id2, map2)));
|
||||
progress.update(i + 1);
|
||||
});
|
||||
|
||||
progress.stop();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -277,7 +285,7 @@ function parseAllData(data: Map<string, FloorData>): MinamoDataset {
|
||||
const length = data.size;
|
||||
const totalCount = Math.round((length * (length - 1)) / 2);
|
||||
|
||||
const pairs = choosePair(length);
|
||||
const pairs = choosePair(length, 10000);
|
||||
|
||||
console.log(
|
||||
`✅ 共发现 ${length} 个楼层,共 ${totalCount} 种组合,选取 ${pairs.length} 个组合`
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { readFile } from 'fs-extra';
|
||||
import { join } from 'path';
|
||||
import { BaseConfig, TowerInfo } from './types';
|
||||
import { convertFloor } from './floor';
|
||||
import { convertApeiriaMap, convertFloor } from './floor';
|
||||
|
||||
interface DatasetMergable<T> {
|
||||
datasetId: number;
|
||||
@ -67,7 +67,30 @@ export async function parseTowerInfo(
|
||||
|
||||
export async function getAllFloors(...info: TowerInfo[]) {
|
||||
const floorData = await Promise.all(
|
||||
info.map(tower => {
|
||||
info.map(async tower => {
|
||||
// 获取必要信息
|
||||
const enemyFile = await readFile(
|
||||
join(tower.path, 'enemys.js'),
|
||||
'utf-8'
|
||||
);
|
||||
const mapFile = await readFile(
|
||||
join(tower.path, 'maps.js'),
|
||||
'utf-8'
|
||||
);
|
||||
const enemyMap = JSON.parse(
|
||||
enemyFile.split('\n').slice(1).join('\n')
|
||||
) as Record<string, any>;
|
||||
const mapData = JSON.parse(
|
||||
mapFile.split('\n').slice(1).join('\n')
|
||||
) as Record<number, any>;
|
||||
const enemyNumMap: Record<number, any> = {};
|
||||
// 将怪物转化为数字映射
|
||||
for (const [key, value] of Object.entries(mapData)) {
|
||||
if (value.cls === 'enemys') {
|
||||
enemyNumMap[parseInt(key)] = enemyMap[value.id];
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
tower.floorIds.map(async id => {
|
||||
const floorFile = await readFile(
|
||||
@ -75,13 +98,25 @@ export async function getAllFloors(...info: TowerInfo[]) {
|
||||
'utf-8'
|
||||
);
|
||||
const data = JSON.parse(
|
||||
floorFile.split('\n').slice(1).join('\n')
|
||||
floorFile
|
||||
.replaceAll("'", '"')
|
||||
.slice(floorFile.indexOf('=') + 1)
|
||||
);
|
||||
const map = data.map as number[][];
|
||||
// 裁剪地图
|
||||
const { clip } = tower.config;
|
||||
const area = clip.special[id] ?? clip.defaults;
|
||||
return convertFloor(map, area, tower.name, id);
|
||||
if (tower.name === 'Apeiria') {
|
||||
return convertApeiriaMap(
|
||||
map,
|
||||
area,
|
||||
tower.name,
|
||||
id,
|
||||
enemyNumMap
|
||||
);
|
||||
} else {
|
||||
return convertFloor(map, area, tower.name, id);
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user