mirror of
https://github.com/unanmed/ginka-generator.git
synced 2026-11-15 11:24:30 +08:00
feat: 单独对 Apeiria 塔提供支持
This commit is contained in:
1 parent
c34756016d
commit
98f7a9cdcf
5 files changed
+220
-12
No files matched your search
+109
-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
|
||||
);
|
||||
}
|
||||
+14
-6
@@ -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} 个组合`
|
||||
|
||||
+39
-4
@@ -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);
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user