mirror of
https://github.com/unanmed/ginka-generator.git
synced 2026-07-16 22:11:15 +08:00
feat: 机关门检测
This commit is contained in:
parent
32227d2c44
commit
d406b97c90
@ -243,7 +243,7 @@ const labelConfig: IAutoLabelConfig = {
|
|||||||
wall: 1,
|
wall: 1,
|
||||||
decoration: 16,
|
decoration: 16,
|
||||||
commonDoors: [2],
|
commonDoors: [2],
|
||||||
specialDoors: [2, 2],
|
specialDoors: [6, 6],
|
||||||
keys: [3],
|
keys: [3],
|
||||||
redGems: [3],
|
redGems: [3],
|
||||||
blueGems: [3],
|
blueGems: [3],
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export class MapTileConverter implements IMapTileConverter {
|
|||||||
|
|
||||||
private readonly emptyTiles = new Set<number>([0]);
|
private readonly emptyTiles = new Set<number>([0]);
|
||||||
private readonly doorTiles = new Set<number>();
|
private readonly doorTiles = new Set<number>();
|
||||||
|
private readonly specialDoorTiles = new Set<number>();
|
||||||
private readonly enemyTiles = new Set<number>();
|
private readonly enemyTiles = new Set<number>();
|
||||||
private readonly resourceTiles = new Set<number>();
|
private readonly resourceTiles = new Set<number>();
|
||||||
private readonly keyTiles = new Set<number>();
|
private readonly keyTiles = new Set<number>();
|
||||||
@ -224,8 +225,13 @@ export class MapTileConverter implements IMapTileConverter {
|
|||||||
if (isDoor) {
|
if (isDoor) {
|
||||||
this.doorTiles.add(tile);
|
this.doorTiles.add(tile);
|
||||||
this.noPassMap.set(tile, false);
|
this.noPassMap.set(tile, false);
|
||||||
const label = labels.commonDoors[0];
|
if (blockId === 'specialDoor') {
|
||||||
this.labelMap.set(tile, label);
|
this.specialDoorTiles.add(tile);
|
||||||
|
this.labelMap.set(tile, labels.specialDoors[0]);
|
||||||
|
} else {
|
||||||
|
const label = labels.commonDoors[0];
|
||||||
|
this.labelMap.set(tile, label);
|
||||||
|
}
|
||||||
} else if (isEnemy) {
|
} else if (isEnemy) {
|
||||||
this.enemyTiles.add(tile);
|
this.enemyTiles.add(tile);
|
||||||
this.noPassMap.set(tile, false);
|
this.noPassMap.set(tile, false);
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class MockTileConverter implements IMapTileConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isDoor(tile: number): boolean {
|
isDoor(tile: number): boolean {
|
||||||
return tile === 2;
|
return tile === 2 || tile === 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
isEnemy(tile: number): boolean {
|
isEnemy(tile: number): boolean {
|
||||||
@ -76,7 +76,7 @@ const config: IAutoLabelConfig = {
|
|||||||
wall: 1,
|
wall: 1,
|
||||||
decoration: 16,
|
decoration: 16,
|
||||||
commonDoors: [2],
|
commonDoors: [2],
|
||||||
specialDoors: [6, 7],
|
specialDoors: [6, 6],
|
||||||
keys: [3],
|
keys: [3],
|
||||||
redGems: [3],
|
redGems: [3],
|
||||||
blueGems: [3],
|
blueGems: [3],
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { readFile } from 'fs/promises';
|
import { readFile } from 'fs/promises';
|
||||||
import {
|
import {
|
||||||
BranchType,
|
BranchType,
|
||||||
|
DoorKind,
|
||||||
GraphNodeType,
|
GraphNodeType,
|
||||||
IAutoLabelConfig,
|
IAutoLabelConfig,
|
||||||
IFloorInfo,
|
IFloorInfo,
|
||||||
@ -466,12 +467,21 @@ function collectReachableNodes(
|
|||||||
*/
|
*/
|
||||||
function isUselessBranchNode(
|
function isUselessBranchNode(
|
||||||
topo: MapTopology,
|
topo: MapTopology,
|
||||||
branchNode: MapGraphNode
|
branchNode: MapGraphNode,
|
||||||
|
specialDoorLinkedEnemies?: Set<MapGraphNode>
|
||||||
): boolean {
|
): boolean {
|
||||||
if (branchNode.type !== GraphNodeType.Branch) {
|
if (branchNode.type !== GraphNodeType.Branch) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
branchNode.branch === BranchType.Enemy &&
|
||||||
|
specialDoorLinkedEnemies &&
|
||||||
|
specialDoorLinkedEnemies.has(branchNode)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const branchTile = getNodeTile(branchNode);
|
const branchTile = getNodeTile(branchNode);
|
||||||
if (countGridPassableDirections(topo, branchTile) <= 1) {
|
if (countGridPassableDirections(topo, branchTile) <= 1) {
|
||||||
// 格子层只有一个可通行方向时,直接按死胡同分支处理。
|
// 格子层只有一个可通行方向时,直接按死胡同分支处理。
|
||||||
@ -598,13 +608,18 @@ function computeBranchClusterStats(branchNodes: Iterable<MapGraphNode>): {
|
|||||||
* @param branchNodes 当前楼层里所有分支节点的去重集合
|
* @param branchNodes 当前楼层里所有分支节点的去重集合
|
||||||
* @returns 闲置门/怪数量,以及该楼层是否存在闲置分支
|
* @returns 闲置门/怪数量,以及该楼层是否存在闲置分支
|
||||||
*/
|
*/
|
||||||
function computeIdleBranchStats(branchNodes: Iterable<MapGraphNode>): {
|
function computeIdleBranchStats(
|
||||||
|
branchNodes: Iterable<MapGraphNode>,
|
||||||
|
specialDoorLinkedEnemies?: Set<MapGraphNode>
|
||||||
|
): {
|
||||||
idleDoorBranchCount: number;
|
idleDoorBranchCount: number;
|
||||||
idleEnemyBranchCount: number;
|
idleEnemyBranchCount: number;
|
||||||
|
ignoredIdleEnemyBySpecialDoorCount: number;
|
||||||
hasIdleBranch: boolean;
|
hasIdleBranch: boolean;
|
||||||
} {
|
} {
|
||||||
let idleDoorBranchCount = 0;
|
let idleDoorBranchCount = 0;
|
||||||
let idleEnemyBranchCount = 0;
|
let idleEnemyBranchCount = 0;
|
||||||
|
let ignoredIdleEnemyBySpecialDoorCount = 0;
|
||||||
|
|
||||||
for (const node of branchNodes) {
|
for (const node of branchNodes) {
|
||||||
if (node.type !== GraphNodeType.Branch || node.neighbors.size !== 1) {
|
if (node.type !== GraphNodeType.Branch || node.neighbors.size !== 1) {
|
||||||
@ -613,6 +628,11 @@ function computeIdleBranchStats(branchNodes: Iterable<MapGraphNode>): {
|
|||||||
|
|
||||||
if (node.branch === BranchType.Door) {
|
if (node.branch === BranchType.Door) {
|
||||||
idleDoorBranchCount++;
|
idleDoorBranchCount++;
|
||||||
|
} else if (
|
||||||
|
specialDoorLinkedEnemies &&
|
||||||
|
specialDoorLinkedEnemies.has(node)
|
||||||
|
) {
|
||||||
|
ignoredIdleEnemyBySpecialDoorCount++;
|
||||||
} else {
|
} else {
|
||||||
idleEnemyBranchCount++;
|
idleEnemyBranchCount++;
|
||||||
}
|
}
|
||||||
@ -621,6 +641,7 @@ function computeIdleBranchStats(branchNodes: Iterable<MapGraphNode>): {
|
|||||||
return {
|
return {
|
||||||
idleDoorBranchCount,
|
idleDoorBranchCount,
|
||||||
idleEnemyBranchCount,
|
idleEnemyBranchCount,
|
||||||
|
ignoredIdleEnemyBySpecialDoorCount,
|
||||||
hasIdleBranch: idleDoorBranchCount + idleEnemyBranchCount > 0
|
hasIdleBranch: idleDoorBranchCount + idleEnemyBranchCount > 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -704,6 +725,59 @@ function buildMergedNonBranchAreas(graph: IMapGraph): {
|
|||||||
return { areas, areaMap };
|
return { areas, areaMap };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findSpecialDoorLinkedEnemyNodes(
|
||||||
|
branchNodes: Iterable<MapGraphNode>,
|
||||||
|
areaMap: Map<MapGraphNode, IMergedNonBranchArea>
|
||||||
|
): Set<MapGraphNode> {
|
||||||
|
const specialDoorNodes = new Set<MapGraphNode>();
|
||||||
|
for (const node of branchNodes) {
|
||||||
|
if (
|
||||||
|
node.type === GraphNodeType.Branch &&
|
||||||
|
node.branch === BranchType.Door &&
|
||||||
|
node.doorKind === DoorKind.Special
|
||||||
|
) {
|
||||||
|
specialDoorNodes.add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (specialDoorNodes.size === 0) {
|
||||||
|
return new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
const specialDoorLinkedAreas = new Set<IMergedNonBranchArea>();
|
||||||
|
for (const specialDoor of specialDoorNodes) {
|
||||||
|
for (const neighbor of specialDoor.neighbors) {
|
||||||
|
const area = areaMap.get(neighbor);
|
||||||
|
if (area) {
|
||||||
|
specialDoorLinkedAreas.add(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (specialDoorLinkedAreas.size === 0) {
|
||||||
|
return new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
const linkedEnemyNodes = new Set<MapGraphNode>();
|
||||||
|
for (const node of branchNodes) {
|
||||||
|
if (
|
||||||
|
node.type !== GraphNodeType.Branch ||
|
||||||
|
node.branch !== BranchType.Enemy
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const neighbor of node.neighbors) {
|
||||||
|
const area = areaMap.get(neighbor);
|
||||||
|
if (area && specialDoorLinkedAreas.has(area)) {
|
||||||
|
linkedEnemyNodes.add(node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return linkedEnemyNodes;
|
||||||
|
}
|
||||||
|
|
||||||
function buildRepeatedGuardCandidates(
|
function buildRepeatedGuardCandidates(
|
||||||
branchNodes: Iterable<MapGraphNode>,
|
branchNodes: Iterable<MapGraphNode>,
|
||||||
areaMap: Map<MapGraphNode, IMergedNonBranchArea>
|
areaMap: Map<MapGraphNode, IMergedNonBranchArea>
|
||||||
@ -936,18 +1010,44 @@ export function parseFloorInfo(
|
|||||||
hasLargeEnemyCluster
|
hasLargeEnemyCluster
|
||||||
} = computeBranchClusterStats(branchNodes);
|
} = computeBranchClusterStats(branchNodes);
|
||||||
|
|
||||||
const { idleDoorBranchCount, idleEnemyBranchCount, hasIdleBranch } =
|
const mergedAreas = buildMergedNonBranchAreas(topo.graph);
|
||||||
computeIdleBranchStats(branchNodes);
|
const specialDoorLinkedEnemies = findSpecialDoorLinkedEnemyNodes(
|
||||||
|
branchNodes,
|
||||||
|
mergedAreas.areaMap
|
||||||
|
);
|
||||||
|
const specialDoorLinkedEnemyCount = specialDoorLinkedEnemies.size;
|
||||||
|
|
||||||
|
const {
|
||||||
|
idleDoorBranchCount,
|
||||||
|
idleEnemyBranchCount,
|
||||||
|
ignoredIdleEnemyBySpecialDoorCount,
|
||||||
|
hasIdleBranch
|
||||||
|
} = computeIdleBranchStats(branchNodes, specialDoorLinkedEnemies);
|
||||||
const {
|
const {
|
||||||
repeatedGuardDoorBranchCount,
|
repeatedGuardDoorBranchCount,
|
||||||
repeatedGuardEnemyBranchCount,
|
repeatedGuardEnemyBranchCount,
|
||||||
hasRepeatedGuardIdleBranch
|
hasRepeatedGuardIdleBranch
|
||||||
} = computeRepeatedGuardIdleStats(topo.graph, branchNodes, width);
|
} = computeRepeatedGuardIdleStats(topo.graph, branchNodes, width);
|
||||||
|
|
||||||
// 无用分支逐点判定:只要任意一个分支命中,整层就带有无用分支标签。
|
let hasUselessBranch = false;
|
||||||
const hasUselessBranch = [...branchNodes].some(node =>
|
let ignoredUselessBranchBySpecialDoorCount = 0;
|
||||||
isUselessBranchNode(topo, node)
|
for (const node of branchNodes) {
|
||||||
);
|
const useless = isUselessBranchNode(
|
||||||
|
topo,
|
||||||
|
node,
|
||||||
|
specialDoorLinkedEnemies
|
||||||
|
);
|
||||||
|
if (useless) {
|
||||||
|
if (
|
||||||
|
node.branch === BranchType.Enemy &&
|
||||||
|
specialDoorLinkedEnemies.has(node)
|
||||||
|
) {
|
||||||
|
ignoredUselessBranchBySpecialDoorCount++;
|
||||||
|
} else {
|
||||||
|
hasUselessBranch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 统计拓扑图信息
|
// 统计拓扑图信息
|
||||||
let maxEmptyArea = 0;
|
let maxEmptyArea = 0;
|
||||||
@ -984,6 +1084,9 @@ export function parseFloorInfo(
|
|||||||
itemDensity: count(flattened, itemTiles) / area,
|
itemDensity: count(flattened, itemTiles) / area,
|
||||||
entryCount: count(flattened, entryTiles),
|
entryCount: count(flattened, entryTiles),
|
||||||
specialDoorCount: count(flattened, specialDoorTiles),
|
specialDoorCount: count(flattened, specialDoorTiles),
|
||||||
|
specialDoorLinkedEnemyCount,
|
||||||
|
ignoredIdleEnemyBySpecialDoorCount,
|
||||||
|
ignoredUselessBranchBySpecialDoorCount,
|
||||||
maxDoorClusterSize,
|
maxDoorClusterSize,
|
||||||
maxEnemyClusterSize,
|
maxEnemyClusterSize,
|
||||||
hasLargeDoorCluster,
|
hasLargeDoorCluster,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import {
|
|||||||
CannotInOut,
|
CannotInOut,
|
||||||
GraphNodeType,
|
GraphNodeType,
|
||||||
BranchType,
|
BranchType,
|
||||||
|
DoorKind,
|
||||||
ResourceType,
|
ResourceType,
|
||||||
type IMapTopology,
|
type IMapTopology,
|
||||||
type IMapGraph,
|
type IMapGraph,
|
||||||
@ -160,14 +161,19 @@ export class MapTopology implements IMapTopology {
|
|||||||
|
|
||||||
if (type === GraphNodeType.Branch) {
|
if (type === GraphNodeType.Branch) {
|
||||||
const tile = this.originMap[y][x];
|
const tile = this.originMap[y][x];
|
||||||
|
const isDoor = converter.isDoor(tile);
|
||||||
|
const convertedTile = this.convertedMap[y][x];
|
||||||
nodeMap.set(idx, {
|
nodeMap.set(idx, {
|
||||||
type: GraphNodeType.Branch,
|
type: GraphNodeType.Branch,
|
||||||
index: nodeIndex++,
|
index: nodeIndex++,
|
||||||
tiles,
|
tiles,
|
||||||
neighbors,
|
neighbors,
|
||||||
branch: converter.isDoor(tile)
|
branch: isDoor ? BranchType.Door : BranchType.Enemy,
|
||||||
? BranchType.Door
|
doorKind: isDoor
|
||||||
: BranchType.Enemy
|
? this.config.specialDoors.includes(convertedTile)
|
||||||
|
? DoorKind.Special
|
||||||
|
: DoorKind.Common
|
||||||
|
: DoorKind.Common
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,6 +89,12 @@ export interface IFloorInfo {
|
|||||||
readonly entryCount: number;
|
readonly entryCount: number;
|
||||||
/** 机关门数量 */
|
/** 机关门数量 */
|
||||||
readonly specialDoorCount: number;
|
readonly specialDoorCount: number;
|
||||||
|
/** 机关门关联怪数量 */
|
||||||
|
readonly specialDoorLinkedEnemyCount: number;
|
||||||
|
/** 因机关门关联而被豁免的闲置怪数量 */
|
||||||
|
readonly ignoredIdleEnemyBySpecialDoorCount: number;
|
||||||
|
/** 因机关门关联而被豁免的无用分支数量 */
|
||||||
|
readonly ignoredUselessBranchBySpecialDoorCount: number;
|
||||||
/** 同类门分支连通块的最大大小 */
|
/** 同类门分支连通块的最大大小 */
|
||||||
readonly maxDoorClusterSize: number;
|
readonly maxDoorClusterSize: number;
|
||||||
/** 同类怪分支连通块的最大大小 */
|
/** 同类怪分支连通块的最大大小 */
|
||||||
@ -360,6 +366,11 @@ export const enum BranchType {
|
|||||||
Enemy
|
Enemy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum DoorKind {
|
||||||
|
Common,
|
||||||
|
Special
|
||||||
|
}
|
||||||
|
|
||||||
export interface IMapGraphNodeBase {
|
export interface IMapGraphNodeBase {
|
||||||
/** 节点类型 */
|
/** 节点类型 */
|
||||||
readonly type: GraphNodeType;
|
readonly type: GraphNodeType;
|
||||||
@ -385,6 +396,8 @@ export interface IBranchMapGraphNode extends IMapGraphNodeBase {
|
|||||||
readonly type: GraphNodeType.Branch;
|
readonly type: GraphNodeType.Branch;
|
||||||
/** 分支节点类型 */
|
/** 分支节点类型 */
|
||||||
readonly branch: BranchType;
|
readonly branch: BranchType;
|
||||||
|
/** 门子类型,仅 Door 分支有效 */
|
||||||
|
readonly doorKind: DoorKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEntryMapGraphNode extends IMapGraphNodeBase {
|
export interface IEntryMapGraphNode extends IMapGraphNodeBase {
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
// 基本图块定义
|
// 基本图块定义
|
||||||
// 新方案 ID:0=空地 1=墙壁 2=门 3=资源(all) 4=怪物 5=入口 6=掩码
|
// 新方案 ID:0=空地 1=墙壁 2=普通门 3=资源(all) 4=怪物 5=入口 6=机关门 7=掩码
|
||||||
export const emptyTiles = new Set([0]);
|
export const emptyTiles = new Set([0]);
|
||||||
export const wallTiles = new Set([1]);
|
export const wallTiles = new Set([1]);
|
||||||
export const decorationTiles = new Set([16]);
|
export const decorationTiles = new Set([16]);
|
||||||
export const commonDoorTiles = new Set([2]);
|
export const commonDoorTiles = new Set([2]);
|
||||||
export const specialDoorTiles = new Set([2]);
|
export const specialDoorTiles = new Set([6]);
|
||||||
export const keyTiles = new Set([3]);
|
export const keyTiles = new Set([3]);
|
||||||
export const redGemTiles = new Set([3]);
|
export const redGemTiles = new Set([3]);
|
||||||
export const blueGemTiles = new Set([3]);
|
export const blueGemTiles = new Set([3]);
|
||||||
|
|||||||
@ -28,7 +28,8 @@ class GinkaSeperatedDataset(Dataset):
|
|||||||
RESOURCE = 3
|
RESOURCE = 3
|
||||||
MONSTER = 4
|
MONSTER = 4
|
||||||
ENTRANCE = 5
|
ENTRANCE = 5
|
||||||
MASK_ID = 6
|
SPECIAL_DOOR = 6
|
||||||
|
MASK_ID = 7
|
||||||
MAP_SIZE = 13 * 13
|
MAP_SIZE = 13 * 13
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -51,7 +52,7 @@ class GinkaSeperatedDataset(Dataset):
|
|||||||
|
|
||||||
def compute_density_stats(self) -> dict:
|
def compute_density_stats(self) -> dict:
|
||||||
wall_densities = [self.count_tile(item['map'], self.WALL) / self.MAP_SIZE for item in self.data]
|
wall_densities = [self.count_tile(item['map'], self.WALL) / self.MAP_SIZE for item in self.data]
|
||||||
door_densities = [self.count_tile(item['map'], self.DOOR) / self.MAP_SIZE for item in self.data]
|
door_densities = [self.count_tile(item['map'], self.DOOR) / self.MAP_SIZE + self.count_tile(item['map'], self.SPECIAL_DOOR) / self.MAP_SIZE for item in self.data]
|
||||||
monster_densities = [self.count_tile(item['map'], self.MONSTER) / self.MAP_SIZE for item in self.data]
|
monster_densities = [self.count_tile(item['map'], self.MONSTER) / self.MAP_SIZE for item in self.data]
|
||||||
entrance_densities = [self.count_tile(item['map'], self.ENTRANCE) / self.MAP_SIZE for item in self.data]
|
entrance_densities = [self.count_tile(item['map'], self.ENTRANCE) / self.MAP_SIZE for item in self.data]
|
||||||
resource_densities = [self.count_tile(item['map'], self.RESOURCE) / self.MAP_SIZE for item in self.data]
|
resource_densities = [self.count_tile(item['map'], self.RESOURCE) / self.MAP_SIZE for item in self.data]
|
||||||
@ -79,7 +80,7 @@ class GinkaSeperatedDataset(Dataset):
|
|||||||
def build_target_density(self, map_data: list) -> torch.Tensor:
|
def build_target_density(self, map_data: list) -> torch.Tensor:
|
||||||
return torch.FloatTensor([
|
return torch.FloatTensor([
|
||||||
self.count_tile(map_data, self.WALL) / self.MAP_SIZE,
|
self.count_tile(map_data, self.WALL) / self.MAP_SIZE,
|
||||||
self.count_tile(map_data, self.DOOR) / self.MAP_SIZE,
|
(self.count_tile(map_data, self.DOOR) + self.count_tile(map_data, self.SPECIAL_DOOR)) / self.MAP_SIZE,
|
||||||
self.count_tile(map_data, self.MONSTER) / self.MAP_SIZE,
|
self.count_tile(map_data, self.MONSTER) / self.MAP_SIZE,
|
||||||
self.count_tile(map_data, self.ENTRANCE) / self.MAP_SIZE,
|
self.count_tile(map_data, self.ENTRANCE) / self.MAP_SIZE,
|
||||||
self.count_tile(map_data, self.RESOURCE) / self.MAP_SIZE
|
self.count_tile(map_data, self.RESOURCE) / self.MAP_SIZE
|
||||||
@ -153,7 +154,7 @@ class GinkaSeperatedDataset(Dataset):
|
|||||||
def create_degreaded(self, raw: np.ndarray):
|
def create_degreaded(self, raw: np.ndarray):
|
||||||
# 阶段一:仅生成墙壁骨架
|
# 阶段一:仅生成墙壁骨架
|
||||||
target1 = raw.copy()
|
target1 = raw.copy()
|
||||||
self.degrade_tile(target1, [self.DOOR, self.RESOURCE, self.MONSTER, self.ENTRANCE])
|
self.degrade_tile(target1, [self.DOOR, self.SPECIAL_DOOR, self.RESOURCE, self.MONSTER, self.ENTRANCE])
|
||||||
inp1 = target1.copy()
|
inp1 = target1.copy()
|
||||||
|
|
||||||
# 阶段二:生成怪物、门,同时也允许生成入口以适配结构
|
# 阶段二:生成怪物、门,同时也允许生成入口以适配结构
|
||||||
@ -164,7 +165,7 @@ class GinkaSeperatedDataset(Dataset):
|
|||||||
|
|
||||||
# 阶段三:生成资源
|
# 阶段三:生成资源
|
||||||
target3 = raw.copy()
|
target3 = raw.copy()
|
||||||
self.degrade_tile(target3, [self.WALL, self.DOOR, self.MONSTER, self.ENTRANCE])
|
self.degrade_tile(target3, [self.WALL, self.DOOR, self.SPECIAL_DOOR, self.MONSTER, self.ENTRANCE])
|
||||||
inp3 = raw.copy()
|
inp3 = raw.copy()
|
||||||
|
|
||||||
return target1, inp1, target2, inp2, target3, inp3
|
return target1, inp1, target2, inp2, target3, inp3
|
||||||
@ -182,7 +183,7 @@ class GinkaSeperatedDataset(Dataset):
|
|||||||
inp1[self.std_mask()] = self.MASK_ID
|
inp1[self.std_mask()] = self.MASK_ID
|
||||||
|
|
||||||
# stage2:对 floor+功能元素区域 std_mask
|
# stage2:对 floor+功能元素区域 std_mask
|
||||||
need_mask = np.isin(inp2, [self.FLOOR, self.DOOR, self.MONSTER, self.ENTRANCE])
|
need_mask = np.isin(inp2, [self.FLOOR, self.DOOR, self.SPECIAL_DOOR, self.MONSTER, self.ENTRANCE])
|
||||||
inp2[need_mask & self.std_mask()] = self.MASK_ID
|
inp2[need_mask & self.std_mask()] = self.MASK_ID
|
||||||
|
|
||||||
# stage3:对 floor+resource 区域 std_mask
|
# stage3:对 floor+resource 区域 std_mask
|
||||||
@ -201,7 +202,7 @@ class GinkaSeperatedDataset(Dataset):
|
|||||||
|
|
||||||
need_mask = np.isin(inp2, [self.FLOOR, self.WALL])
|
need_mask = np.isin(inp2, [self.FLOOR, self.WALL])
|
||||||
inp1[need_mask & self.std_mask()] = self.MASK_ID
|
inp1[need_mask & self.std_mask()] = self.MASK_ID
|
||||||
need_mask = np.isin(inp2, [self.FLOOR, self.DOOR, self.MONSTER, self.ENTRANCE])
|
need_mask = np.isin(inp2, [self.FLOOR, self.DOOR, self.SPECIAL_DOOR, self.MONSTER, self.ENTRANCE])
|
||||||
inp2[need_mask] = self.MASK_ID
|
inp2[need_mask] = self.MASK_ID
|
||||||
need_mask = np.isin(inp3, [self.FLOOR, self.RESOURCE])
|
need_mask = np.isin(inp3, [self.FLOOR, self.RESOURCE])
|
||||||
inp3[need_mask] = self.MASK_ID
|
inp3[need_mask] = self.MASK_ID
|
||||||
|
|||||||
@ -33,7 +33,7 @@ from shared.image import matrix_to_image_cv
|
|||||||
# stage3 → resource(资源点)
|
# stage3 → resource(资源点)
|
||||||
|
|
||||||
# 图块 ID 定义:
|
# 图块 ID 定义:
|
||||||
# 0. 空地 1. 墙壁 2. 门 3. 资源 4. 怪物 5. 入口 6. 掩码(MASK_TOKEN)
|
# 0. 空地 1. 墙壁 2. 普通门 3. 资源 4. 怪物 5. 入口 6. 机关门 7. 掩码(MASK_TOKEN)
|
||||||
|
|
||||||
# 共用 VQ-VAE 超参
|
# 共用 VQ-VAE 超参
|
||||||
# 三组编码器(vq1/vq2/vq3)共享相同超参,分别对三阶段地图上下文独立编码
|
# 三组编码器(vq1/vq2/vq3)共享相同超参,分别对三阶段地图上下文独立编码
|
||||||
@ -76,8 +76,8 @@ STAGE2_VQ_WEIGHT = 0.5
|
|||||||
STAGE3_VQ_WEIGHT = 0.5
|
STAGE3_VQ_WEIGHT = 0.5
|
||||||
|
|
||||||
# 全局参数
|
# 全局参数
|
||||||
NUM_CLASSES = 7 # 图块类型数
|
NUM_CLASSES = 8 # 图块类型数
|
||||||
MASK_TOKEN = 6 # 掩码图块
|
MASK_TOKEN = 7 # 掩码图块
|
||||||
MAP_W = 13 # 地图宽度
|
MAP_W = 13 # 地图宽度
|
||||||
MAP_H = 13 # 地图高度
|
MAP_H = 13 # 地图高度
|
||||||
MAP_SIZE = MAP_W * MAP_H # 地图大小
|
MAP_SIZE = MAP_W * MAP_H # 地图大小
|
||||||
@ -276,7 +276,7 @@ def compute_remaining(
|
|||||||
remain = torch.zeros(current.size(0), DENSITY_DIM, device=current.device)
|
remain = torch.zeros(current.size(0), DENSITY_DIM, device=current.device)
|
||||||
|
|
||||||
visible_wall = (current == 1).sum(dim=1).float() / MAP_SIZE
|
visible_wall = (current == 1).sum(dim=1).float() / MAP_SIZE
|
||||||
visible_door = (current == 2).sum(dim=1).float() / MAP_SIZE
|
visible_door = ((current == 2) | (current == 6)).sum(dim=1).float() / MAP_SIZE
|
||||||
visible_monster = (current == 4).sum(dim=1).float() / MAP_SIZE
|
visible_monster = (current == 4).sum(dim=1).float() / MAP_SIZE
|
||||||
visible_entrance = (current == 5).sum(dim=1).float() / MAP_SIZE
|
visible_entrance = (current == 5).sum(dim=1).float() / MAP_SIZE
|
||||||
visible_resource = (current == 3).sum(dim=1).float() / MAP_SIZE
|
visible_resource = (current == 3).sum(dim=1).float() / MAP_SIZE
|
||||||
@ -419,7 +419,7 @@ def full_generate_specific_z(
|
|||||||
|
|
||||||
pred2_np = maskgit_sample(
|
pred2_np = maskgit_sample(
|
||||||
mg2, inp2, z2, struct, target_density, 2,
|
mg2, inp2, z2, struct, target_density, 2,
|
||||||
GENERATE_STEP, target_tiles=[2, 4, 5], keep_fixed=keep_fixed[1]
|
GENERATE_STEP, target_tiles=[2, 6, 4, 5], keep_fixed=keep_fixed[1]
|
||||||
)
|
)
|
||||||
merged12 = pred1_np.copy()
|
merged12 = pred1_np.copy()
|
||||||
merged12[pred2_np != 0] = pred2_np[pred2_np != 0]
|
merged12[pred2_np != 0] = pred2_np[pred2_np != 0]
|
||||||
@ -742,6 +742,9 @@ def validate(
|
|||||||
true_map = true_map_batch[batch_idx]
|
true_map = true_map_batch[batch_idx]
|
||||||
pred_count = float((pred_map == tile_id).sum().item())
|
pred_count = float((pred_map == tile_id).sum().item())
|
||||||
true_count = float((true_map == tile_id).sum().item())
|
true_count = float((true_map == tile_id).sum().item())
|
||||||
|
if tile_id == 2:
|
||||||
|
pred_count += float((pred_map == 6).sum().item())
|
||||||
|
true_count += float((true_map == 6).sum().item())
|
||||||
density_metrics[tile_id]["mae"] += abs(pred_count - true_count) / MAP_SIZE
|
density_metrics[tile_id]["mae"] += abs(pred_count - true_count) / MAP_SIZE
|
||||||
density_metrics[tile_id]["over"] += max(pred_count - true_count, 0.0) / MAP_SIZE
|
density_metrics[tile_id]["over"] += max(pred_count - true_count, 0.0) / MAP_SIZE
|
||||||
density_metrics[tile_id]["count"] += 1
|
density_metrics[tile_id]["count"] += 1
|
||||||
|
|||||||
BIN
tiles/6.png
BIN
tiles/6.png
Binary file not shown.
|
Before Width: | Height: | Size: 414 B After Width: | Height: | Size: 995 B |
BIN
tiles/7.png
Normal file
BIN
tiles/7.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 184 B |
Loading…
Reference in New Issue
Block a user