boardgame-core/src/core/region.ts

95 lines
2.7 KiB
TypeScript

import {Entity} from "@/utils/entity";
import {Part} from "./part";
import {RNG} from "@/utils/rng";
export type Region = {
id: string;
axes: RegionAxis[];
children: Entity<Part>[];
}
export type RegionAxis = {
name: string;
min?: number;
max?: number;
align?: 'start' | 'end' | 'center';
}
export function applyAlign(region: Entity<Region>) {
region.produce(applyAlignCore);
}
function applyAlignCore(region: Region) {
if (region.children.length === 0) return;
for (let axisIndex = 0; axisIndex < region.axes.length; axisIndex++) {
const axis = region.axes[axisIndex];
if (!axis.align) continue;
const positionValues = new Set<number>();
for (const child of region.children) {
positionValues.add(child.value.position[axisIndex] ?? 0);
}
const sortedPositions = Array.from(positionValues).sort((a, b) => a - b);
const positionMap = new Map<number, number>();
if (axis.align === 'start' && axis.min !== undefined) {
sortedPositions.forEach((pos, index) => {
positionMap.set(pos, axis.min! + index);
});
} else if (axis.align === 'end' && axis.max !== undefined) {
const count = sortedPositions.length;
sortedPositions.forEach((pos, index) => {
positionMap.set(pos, axis.max! - (count - 1 - index));
});
} else if (axis.align === 'center') {
const count = sortedPositions.length;
const min = axis.min ?? 0;
const max = axis.max ?? count - 1;
const range = max - min;
const center = min + range / 2;
sortedPositions.forEach((pos, index) => {
const offset = index - (count - 1) / 2;
positionMap.set(pos, center + offset);
});
}
for (const child of region.children) {
child.produce(draft => {
const currentPos = draft.position[axisIndex] ?? 0;
draft.position[axisIndex] = positionMap.get(currentPos) ?? currentPos;
});
}
}
region.children.sort((a, b) => {
for (let i = 0; i < region.axes.length; i++) {
const diff = (a.value.position[i] ?? 0) - (b.value.position[i] ?? 0);
if (diff !== 0) return diff;
}
return 0;
});
}
export function shuffle(region: Entity<Region>, rng: RNG) {
region.produce(region => shuffleCore(region, rng));
}
function shuffleCore(region: Region, rng: RNG){
if (region.children.length <= 1) return;
const children = [...region.children];
for (let i = children.length - 1; i > 0; i--) {
const j = rng.nextInt(i + 1);
const posI = [...children[i].value.position];
const posJ = [...children[j].value.position];
children[i].produce(draft => {
draft.position = posJ;
});
children[j].produce(draft => {
draft.position = posI;
});
}
}