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