mirror of
https://github.com/unanmed/ginka-generator.git
synced 2026-05-26 06:21:10 +08:00
chore: 调整过滤算法
This commit is contained in:
parent
202decf476
commit
4b63de743a
@ -254,6 +254,10 @@ const labelConfig: IAutoLabelConfig = {
|
|||||||
entry: 5
|
entry: 5
|
||||||
},
|
},
|
||||||
allowedSize: [[13, 13]],
|
allowedSize: [[13, 13]],
|
||||||
|
allowLargeDoorCluster: false,
|
||||||
|
allowLargeEnemyCluster: false,
|
||||||
|
allowIdleBranch: false,
|
||||||
|
allowRepeatedGuardIdleBranch: false,
|
||||||
allowUselessBranch: false,
|
allowUselessBranch: false,
|
||||||
maxWallDensityStd: 1,
|
maxWallDensityStd: 1,
|
||||||
maxEmptyArea: 8,
|
maxEmptyArea: 8,
|
||||||
|
|||||||
@ -176,34 +176,44 @@ export async function autoLabelTowers(
|
|||||||
ignoredFloorsEntry++;
|
ignoredFloorsEntry++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (floorInfo.hasLargeDoorCluster) {
|
const filteredByLargeDoorCluster =
|
||||||
|
!config.allowLargeDoorCluster && floorInfo.hasLargeDoorCluster;
|
||||||
|
const filteredByLargeEnemyCluster =
|
||||||
|
!config.allowLargeEnemyCluster &&
|
||||||
|
floorInfo.hasLargeEnemyCluster;
|
||||||
|
if (filteredByLargeDoorCluster) {
|
||||||
ignoredFloorsContinuousDoor++;
|
ignoredFloorsContinuousDoor++;
|
||||||
}
|
}
|
||||||
if (floorInfo.hasLargeEnemyCluster) {
|
if (filteredByLargeEnemyCluster) {
|
||||||
ignoredFloorsContinuousEnemy++;
|
ignoredFloorsContinuousEnemy++;
|
||||||
}
|
}
|
||||||
if (
|
if (filteredByLargeDoorCluster || filteredByLargeEnemyCluster) {
|
||||||
floorInfo.hasLargeDoorCluster ||
|
|
||||||
floorInfo.hasLargeEnemyCluster
|
|
||||||
) {
|
|
||||||
ignoredFloorsContinuous++;
|
ignoredFloorsContinuous++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (
|
const filteredByIdleDoorBranch =
|
||||||
floorInfo.idleDoorBranchCount > 0 ||
|
!config.allowIdleBranch && floorInfo.idleDoorBranchCount > 0;
|
||||||
floorInfo.repeatedGuardDoorBranchCount > 0
|
const filteredByRepeatedGuardDoorBranch =
|
||||||
) {
|
!config.allowRepeatedGuardIdleBranch &&
|
||||||
|
floorInfo.repeatedGuardDoorBranchCount > 0;
|
||||||
|
const filteredByIdleEnemyBranch =
|
||||||
|
!config.allowIdleBranch && floorInfo.idleEnemyBranchCount > 0;
|
||||||
|
const filteredByRepeatedGuardEnemyBranch =
|
||||||
|
!config.allowRepeatedGuardIdleBranch &&
|
||||||
|
floorInfo.repeatedGuardEnemyBranchCount > 0;
|
||||||
|
if (filteredByIdleDoorBranch || filteredByRepeatedGuardDoorBranch) {
|
||||||
ignoredFloorsIdleDoor++;
|
ignoredFloorsIdleDoor++;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
floorInfo.idleEnemyBranchCount > 0 ||
|
filteredByIdleEnemyBranch ||
|
||||||
floorInfo.repeatedGuardEnemyBranchCount > 0
|
filteredByRepeatedGuardEnemyBranch
|
||||||
) {
|
) {
|
||||||
ignoredFloorsIdleEnemy++;
|
ignoredFloorsIdleEnemy++;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
floorInfo.hasIdleBranch ||
|
(!config.allowIdleBranch && floorInfo.hasIdleBranch) ||
|
||||||
floorInfo.hasRepeatedGuardIdleBranch
|
(!config.allowRepeatedGuardIdleBranch &&
|
||||||
|
floorInfo.hasRepeatedGuardIdleBranch)
|
||||||
) {
|
) {
|
||||||
ignoredFloorsIdle++;
|
ignoredFloorsIdle++;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -87,6 +87,10 @@ const config: IAutoLabelConfig = {
|
|||||||
entry: 5
|
entry: 5
|
||||||
},
|
},
|
||||||
allowedSize: [[5, 5]],
|
allowedSize: [[5, 5]],
|
||||||
|
allowLargeDoorCluster: false,
|
||||||
|
allowLargeEnemyCluster: false,
|
||||||
|
allowIdleBranch: false,
|
||||||
|
allowRepeatedGuardIdleBranch: false,
|
||||||
allowUselessBranch: false,
|
allowUselessBranch: false,
|
||||||
minEnemyRatio: 0,
|
minEnemyRatio: 0,
|
||||||
maxEnemyRatio: 1,
|
maxEnemyRatio: 1,
|
||||||
@ -120,6 +124,23 @@ function parseTestFloor(map: number[][]) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseTestFloorWithConverter(
|
||||||
|
map: number[][],
|
||||||
|
converter: IMapTileConverter
|
||||||
|
) {
|
||||||
|
return parseFloorInfo(tower, map, map, [], config, converter, 'F1');
|
||||||
|
}
|
||||||
|
|
||||||
|
class BlockedDirectionConverter extends MockTileConverter {
|
||||||
|
getCannotIn(): number {
|
||||||
|
return 0b1111;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCannotOut(): number {
|
||||||
|
return 0b1111;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe('parseFloorInfo useless branch detection', () => {
|
describe('parseFloorInfo useless branch detection', () => {
|
||||||
it('marks a branch with only one grid-level passable direction as useless', () => {
|
it('marks a branch with only one grid-level passable direction as useless', () => {
|
||||||
const floor = parseTestFloor([
|
const floor = parseTestFloor([
|
||||||
@ -259,3 +280,18 @@ describe('parseFloorInfo repeated guard idle detection', () => {
|
|||||||
expect(floor.hasRepeatedGuardIdleBranch).toBe(false);
|
expect(floor.hasRepeatedGuardIdleBranch).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('parseFloorInfo wall-only passability detection', () => {
|
||||||
|
it('ignores cannotIn and cannotOut flags in the useless branch quick check', () => {
|
||||||
|
const floor = parseTestFloorWithConverter(
|
||||||
|
[
|
||||||
|
[1, 1, 1, 1, 1, 1],
|
||||||
|
[1, 5, 2, 4, 3, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1]
|
||||||
|
],
|
||||||
|
new BlockedDirectionConverter()
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(floor.hasUselessBranch).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { readFile } from 'fs/promises';
|
import { readFile } from 'fs/promises';
|
||||||
import {
|
import {
|
||||||
BranchType,
|
BranchType,
|
||||||
CannotInOut,
|
|
||||||
GraphNodeType,
|
GraphNodeType,
|
||||||
IAutoLabelConfig,
|
IAutoLabelConfig,
|
||||||
IFloorInfo,
|
IFloorInfo,
|
||||||
@ -28,11 +27,11 @@ import { gaussainHeatmap, generateHeatmap } from './heatmap';
|
|||||||
import { MapTopology } from './topo';
|
import { MapTopology } from './topo';
|
||||||
|
|
||||||
// 格子层四方向通行检查,供无用分支主算法复用。
|
// 格子层四方向通行检查,供无用分支主算法复用。
|
||||||
const branchCheckDirs: [number, number, CannotInOut, CannotInOut][] = [
|
const branchCheckDirs: [number, number][] = [
|
||||||
[-1, 0, CannotInOut.Left, CannotInOut.Right],
|
[-1, 0],
|
||||||
[1, 0, CannotInOut.Right, CannotInOut.Left],
|
[1, 0],
|
||||||
[0, -1, CannotInOut.Top, CannotInOut.Bottom],
|
[0, -1],
|
||||||
[0, 1, CannotInOut.Bottom, CannotInOut.Top]
|
[0, 1]
|
||||||
];
|
];
|
||||||
|
|
||||||
interface IRawTowerInfo {
|
interface IRawTowerInfo {
|
||||||
@ -361,43 +360,23 @@ function getNodeTile(node: MapGraphNode): number {
|
|||||||
* 同一个 Empty / Resource 拓扑节点可能从多个方向贴住分支节点,
|
* 同一个 Empty / Resource 拓扑节点可能从多个方向贴住分支节点,
|
||||||
* 但在“死胡同分支”这条快捷规则里,我们关心的是分支格子本身到底有几个可走方向。
|
* 但在“死胡同分支”这条快捷规则里,我们关心的是分支格子本身到底有几个可走方向。
|
||||||
*
|
*
|
||||||
|
* 当前项目会在更早的楼层过滤阶段直接剔除带 `cannotIn/cannotOut` 的地图,
|
||||||
|
* 所以这里不再额外兼容方向限制,只要目标格子不是墙,就视为该方向可走。
|
||||||
|
*
|
||||||
* @param topo 当前楼层的拓扑信息
|
* @param topo 当前楼层的拓扑信息
|
||||||
* @param from 起点格子,使用 y * width + x 的平坦坐标
|
|
||||||
* @param to 终点格子,必须是与起点四邻接的格子
|
* @param to 终点格子,必须是与起点四邻接的格子
|
||||||
* @param outFlag 从起点离开时对应的方向标记
|
* @returns 只要目标格子不是墙,就视为这两个格子之间存在通路
|
||||||
* @param inFlag 进入终点时对应的方向标记
|
|
||||||
* @returns 只要 from -> to 或 to -> from 任一方向可走,就视为这两个格子之间存在通路
|
|
||||||
*/
|
*/
|
||||||
function hasGridPassage(
|
function hasGridPassage(topo: MapTopology, to: number): boolean {
|
||||||
topo: MapTopology,
|
|
||||||
from: number,
|
|
||||||
to: number,
|
|
||||||
outFlag: CannotInOut,
|
|
||||||
inFlag: CannotInOut
|
|
||||||
): boolean {
|
|
||||||
const width = topo.convertedMap[0]?.length ?? 0;
|
const width = topo.convertedMap[0]?.length ?? 0;
|
||||||
const fromX = from % width;
|
|
||||||
const fromY = (from - fromX) / width;
|
|
||||||
const toX = to % width;
|
const toX = to % width;
|
||||||
const toY = (to - toX) / width;
|
const toY = (to - toX) / width;
|
||||||
|
|
||||||
if (
|
if (topo.convertedMap[toY]?.[toX] == null) {
|
||||||
topo.noPass[fromY]?.[fromX] ||
|
|
||||||
topo.noPass[toY]?.[toX] ||
|
|
||||||
topo.convertedMap[toY]?.[toX] == null
|
|
||||||
) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const canGo =
|
return topo.convertedMap[toY][toX] !== topo.config.wall;
|
||||||
!(topo.cannotOut[fromY][fromX] & outFlag) &&
|
|
||||||
!(topo.cannotIn[toY][toX] & inFlag);
|
|
||||||
const canCome =
|
|
||||||
!(topo.cannotOut[toY][toX] & inFlag) &&
|
|
||||||
!(topo.cannotIn[fromY][fromX] & outFlag);
|
|
||||||
|
|
||||||
// 与构图逻辑保持一致:双向只要任一方向能通过,就视为这两个格子存在通路。
|
|
||||||
return canGo || canCome;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -418,7 +397,7 @@ function countGridPassableDirections(topo: MapTopology, tile: number): number {
|
|||||||
const y = (tile - x) / width;
|
const y = (tile - x) / width;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
for (const [dx, dy, outFlag, inFlag] of branchCheckDirs) {
|
for (const [dx, dy] of branchCheckDirs) {
|
||||||
const nx = x + dx;
|
const nx = x + dx;
|
||||||
const ny = y + dy;
|
const ny = y + dy;
|
||||||
if (nx < 0 || nx >= width || ny < 0 || ny >= height) {
|
if (nx < 0 || nx >= width || ny < 0 || ny >= height) {
|
||||||
@ -426,7 +405,7 @@ function countGridPassableDirections(topo: MapTopology, tile: number): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const nextTile = ny * width + nx;
|
const nextTile = ny * width + nx;
|
||||||
if (hasGridPassage(topo, tile, nextTile, outFlag, inFlag)) {
|
if (hasGridPassage(topo, nextTile)) {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -188,6 +188,14 @@ export interface IAutoLabelConfig {
|
|||||||
readonly classes: Readonly<IMapBlockConfig>;
|
readonly classes: Readonly<IMapBlockConfig>;
|
||||||
/** 地图允许大小 */
|
/** 地图允许大小 */
|
||||||
readonly allowedSize: [number, number][];
|
readonly allowedSize: [number, number][];
|
||||||
|
/** 是否允许同类门分支连通块大小超过 3 */
|
||||||
|
readonly allowLargeDoorCluster: boolean;
|
||||||
|
/** 是否允许同类怪分支连通块大小超过 3 */
|
||||||
|
readonly allowLargeEnemyCluster: boolean;
|
||||||
|
/** 是否允许拓扑图上只连接一个邻居的闲置分支 */
|
||||||
|
readonly allowIdleBranch: boolean;
|
||||||
|
/** 是否允许重复守同一连通区域的闲置分支 */
|
||||||
|
readonly allowRepeatedGuardIdleBranch: boolean;
|
||||||
/** 是否允许无用节点 */
|
/** 是否允许无用节点 */
|
||||||
readonly allowUselessBranch: boolean;
|
readonly allowUselessBranch: boolean;
|
||||||
/** 最小怪物占比 */
|
/** 最小怪物占比 */
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user