boardgame-core/tests/core/region.test.ts

227 lines
8.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, it, expect, beforeEach } from 'vitest';
import { applyAlign, shuffle, type Region, type RegionAxis } from '../../src/core/region';
import { createRNG } from '../../src/utils/rng';
import { createEntityCollection } from '../../src/utils/entity';
import { type Part } from '../../src/core/part';
describe('Region', () => {
function createPart(id: string, position: number[]): Part {
const collection = createEntityCollection<Part>();
const part: Part = {
id,
sides: 1,
side: 0,
region: { id: 'region1', value: {} as Region },
position: [...position]
};
collection.add(part);
return part;
}
function createRegion(axes: RegionAxis[], parts: Part[]): Region {
const region: Region = {
id: 'region1',
axes: [...axes],
children: parts.map(p => ({ id: p.id, value: p }))
};
return region;
}
describe('applyAlign', () => {
it('should do nothing with empty region', () => {
const region = createRegion([{ name: 'x', min: 0, align: 'start' }], []);
applyAlign(region);
expect(region.children).toHaveLength(0);
});
it('should align parts to start', () => {
const part1 = createPart('p1', [5, 0]);
const part2 = createPart('p2', [7, 0]);
const part3 = createPart('p3', [2, 0]);
const region = createRegion(
[{ name: 'x', min: 0, align: 'start' }],
[part1, part2, part3]
);
applyAlign(region);
// 排序后应该是 part3(2), part1(5), part2(7) -> 对齐到 0, 1, 2
expect(region.children[0].value.position[0]).toBe(0);
expect(region.children[1].value.position[0]).toBe(1);
expect(region.children[2].value.position[0]).toBe(2);
});
it('should align parts to start with custom min', () => {
const part1 = createPart('p1', [5, 0]);
const part2 = createPart('p2', [7, 0]);
const region = createRegion(
[{ name: 'x', min: 10, align: 'start' }],
[part1, part2]
);
applyAlign(region);
expect(region.children[0].value.position[0]).toBe(10);
expect(region.children[1].value.position[0]).toBe(11);
});
it('should align parts to end', () => {
const part1 = createPart('p1', [2, 0]);
const part2 = createPart('p2', [4, 0]);
const part3 = createPart('p3', [1, 0]);
const region = createRegion(
[{ name: 'x', max: 10, align: 'end' }],
[part1, part2, part3]
);
applyAlign(region);
// 3 个部分,对齐到 end(max=10),应该是 8, 9, 10
expect(region.children[0].value.position[0]).toBe(8);
expect(region.children[1].value.position[0]).toBe(9);
expect(region.children[2].value.position[0]).toBe(10);
});
it('should align parts to center', () => {
const part1 = createPart('p1', [0, 0]);
const part2 = createPart('p2', [1, 0]);
const part3 = createPart('p3', [2, 0]);
const region = createRegion(
[{ name: 'x', min: 0, max: 10, align: 'center' }],
[part1, part2, part3]
);
applyAlign(region);
// 中心是 53 个部分应该是 4, 5, 6
expect(region.children[0].value.position[0]).toBe(4);
expect(region.children[1].value.position[0]).toBe(5);
expect(region.children[2].value.position[0]).toBe(6);
});
it('should handle even count center alignment', () => {
const part1 = createPart('p1', [0, 0]);
const part2 = createPart('p2', [1, 0]);
const region = createRegion(
[{ name: 'x', min: 0, max: 10, align: 'center' }],
[part1, part2]
);
applyAlign(region);
// 中心是 52 个部分应该是 4.5, 5.5
expect(region.children[0].value.position[0]).toBe(4.5);
expect(region.children[1].value.position[0]).toBe(5.5);
});
it('should sort children by position', () => {
const part1 = createPart('p1', [5, 0]);
const part2 = createPart('p2', [1, 0]);
const part3 = createPart('p3', [3, 0]);
const region = createRegion(
[{ name: 'x', min: 0, align: 'start' }],
[part1, part2, part3]
);
applyAlign(region);
// children 应该按位置排序
expect(region.children[0].value.id).toBe('p2');
expect(region.children[1].value.id).toBe('p3');
expect(region.children[2].value.id).toBe('p1');
});
});
describe('shuffle', () => {
it('should do nothing with empty region', () => {
const region = createRegion([], []);
const rng = createRNG(42);
shuffle(region, rng);
expect(region.children).toHaveLength(0);
});
it('should do nothing with single part', () => {
const part = createPart('p1', [0, 0]);
const region = createRegion([], [part]);
const rng = createRNG(42);
shuffle(region, rng);
expect(region.children[0].value.position).toEqual([0, 0]);
});
it('should shuffle positions of multiple parts', () => {
const part1 = createPart('p1', [0, 0]);
const part2 = createPart('p2', [1, 0]);
const part3 = createPart('p3', [2, 0]);
const region = createRegion([], [part1, part2, part3]);
const rng = createRNG(42);
const originalPositions = region.children.map(c => [...c.value.position]);
shuffle(region, rng);
// 位置应该被交换
const newPositions = region.children.map(c => c.value.position);
// 验证所有原始位置仍然存在(只是被交换了)
originalPositions.forEach(origPos => {
const found = newPositions.some(newPos =>
newPos[0] === origPos[0] && newPos[1] === origPos[1]
);
expect(found).toBe(true);
});
});
it('should be deterministic with same seed', () => {
const createRegionForTest = () => {
const part1 = createPart('p1', [0, 0]);
const part2 = createPart('p2', [1, 0]);
const part3 = createPart('p3', [2, 0]);
return createRegion([], [part1, part2, part3]);
};
const region1 = createRegionForTest();
const region2 = createRegionForTest();
const rng1 = createRNG(42);
const rng2 = createRNG(42);
shuffle(region1, rng1);
shuffle(region2, rng2);
const positions1 = region1.children.map(c => c.value.position);
const positions2 = region2.children.map(c => c.value.position);
expect(positions1).toEqual(positions2);
});
it('should produce different results with different seeds', () => {
const part1 = createPart('p1', [0, 0]);
const part2 = createPart('p2', [1, 0]);
const part3 = createPart('p3', [2, 0]);
const part4 = createPart('p4', [3, 0]);
const part5 = createPart('p5', [4, 0]);
const results = new Set<string>();
// 尝试多个种子,确保大多数产生不同结果
for (let seed = 1; seed <= 10; seed++) {
const region = createRegion([], [part1, part2, part3, part4, part5]);
const rng = createRNG(seed);
shuffle(region, rng);
const positions = JSON.stringify(region.children.map(c => c.value.position));
results.add(positions);
}
// 10 个种子中至少应该有 5 个不同的结果
expect(results.size).toBeGreaterThan(5);
});
});
});