refactor: Part[] -> Record<string, Part>

This commit is contained in:
2026-04-03 17:36:25 +08:00
parent 118007168a
commit 65a3d682b6
9 changed files with 57 additions and 41 deletions
+13 -5
View File
@@ -3,7 +3,7 @@ import {Part} from "./part";
export type PartTemplate<TMeta = {}> = Omit<Partial<Part<TMeta>>, 'id'> & TMeta;
export type PartPool<TMeta = {}> = {
parts: Part<TMeta>[];
parts: Record<string, Part<TMeta>>;
template: PartTemplate<TMeta>;
draw(): Part<TMeta> | undefined;
return(part: Part<TMeta>): void;
@@ -39,8 +39,12 @@ export function createPartPool<TMeta = {}>(
count: number,
idPrefix: string
): PartPool<TMeta> {
const parts = createParts(template, count, idPrefix);
const available = [...parts];
const partsArray = createParts(template, count, idPrefix);
const parts: Record<string, Part<TMeta>> = {};
for (const part of partsArray) {
parts[part.id] = part;
}
const available = [...partsArray];
return {
parts,
@@ -66,9 +70,13 @@ export function mergePartPools<TMeta = {}>(
return createPartPool({} as PartTemplate<TMeta>, 0, 'merged');
}
const allParts = pools.flatMap(p => p.parts);
const allPartsArray = pools.flatMap(p => Object.values(p.parts));
const allParts: Record<string, Part<TMeta>> = {};
for (const part of allPartsArray) {
allParts[part.id] = part;
}
const template = pools[0].template;
const available = allParts.filter(p => p.regionId === '');
const available = allPartsArray.filter(p => p.regionId === '');
return {
parts: allParts,
+6 -6
View File
@@ -27,16 +27,16 @@ export function roll<TMeta>(part: Part<TMeta>, rng: RNG) {
part.side = rng.nextInt(part.sides);
}
export function findPartById<TMeta>(parts: Part<TMeta>[], id: string): Part<TMeta> | undefined {
return parts.find(p => p.id === id);
export function findPartById<TMeta>(parts: Record<string, Part<TMeta>>, id: string): Part<TMeta> | undefined {
return parts[id];
}
export function isCellOccupied<TMeta>(parts: Part<TMeta>[], regionId: string, position: number[]): boolean {
export function isCellOccupied<TMeta>(parts: Record<string, Part<TMeta>>, regionId: string, position: number[]): boolean {
const posKey = position.join(',');
return parts.some(p => p.regionId === regionId && p.position.join(',') === posKey);
return Object.values(parts).some(p => p.regionId === regionId && p.position.join(',') === posKey);
}
export function getPartAtPosition<TMeta>(parts: Part<TMeta>[], regionId: string, position: number[]): Part<TMeta> | undefined {
export function getPartAtPosition<TMeta>(parts: Record<string, Part<TMeta>>, regionId: string, position: number[]): Part<TMeta> | undefined {
const posKey = position.join(',');
return parts.find(p => p.regionId === regionId && p.position.join(',') === posKey);
return Object.values(parts).find(p => p.regionId === regionId && p.position.join(',') === posKey);
}
+4 -3
View File
@@ -132,9 +132,10 @@ export function moveToRegion<TMeta>(part: Part<TMeta>, sourceRegion: Region | nu
part.regionId = targetRegion.id;
}
export function moveToRegionAll<TMeta>(parts: Part<TMeta>[], sourceRegion: Region | null, targetRegion: Region, positions?: number[][]) {
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
export function moveToRegionAll<TMeta>(parts: Record<string, Part<TMeta>>, sourceRegion: Region | null, targetRegion: Region, positions?: number[][]) {
const partIds = Object.keys(parts);
for (let i = 0; i < partIds.length; i++) {
const part = parts[partIds[i]];
if (sourceRegion && part.regionId === sourceRegion.id) {
sourceRegion.childIds = sourceRegion.childIds.filter(id => id !== part.id);
delete sourceRegion.partMap[part.position.join(',')];
+15 -10
View File
@@ -26,7 +26,7 @@ export function createInitialState() {
{ name: 'x', min: 0, max: BOARD_SIZE - 1 },
{ name: 'y', min: 0, max: BOARD_SIZE - 1 },
]),
pieces: [] as BoopPart[],
pieces: {} as Record<string, BoopPart>,
currentPlayer: 'white' as PlayerType,
winner: null as WinnerType,
players: {
@@ -119,7 +119,8 @@ registration.add('turn <player>', async function(cmd) {
}
if (countPiecesOnBoard(this.context, turnPlayer) >= MAX_PIECES_PER_PLAYER) {
const availableKittens = this.context.value.pieces.filter(
const pieces = this.context.value.pieces;
const availableKittens = Object.values(pieces).filter(
p => p.player === turnPlayer && p.pieceType === 'kitten'
);
@@ -174,7 +175,7 @@ export function placePiece(host: MutableSignal<BoopState>, row: number, col: num
`${player}-${pieceType}-${count}`
);
host.produce(s => {
s.pieces.push(piece);
s.pieces[piece.id] = piece;
board.childIds.push(piece.id);
board.partMap[`${row},${col}`] = piece.id;
});
@@ -184,10 +185,11 @@ export function placePiece(host: MutableSignal<BoopState>, row: number, col: num
export function applyBoops(host: MutableSignal<BoopState>, placedRow: number, placedCol: number, placedType: PieceType) {
const board = getBoardRegion(host);
const pieces = host.value.pieces;
const piecesArray = Object.values(pieces);
const piecesToBoop: { part: BoopPart; dr: number; dc: number }[] = [];
for (const part of pieces) {
for (const part of piecesArray) {
const [r, c] = part.position;
if (r === placedRow && c === placedCol) continue;
@@ -223,7 +225,7 @@ export function applyBoops(host: MutableSignal<BoopState>, placedRow: number, pl
part.position = [newRow, newCol];
board.partMap = Object.fromEntries(
board.childIds.map(id => {
const p = pieces.find(x => x.id === id)!;
const p = pieces[id];
return [p.position.join(','), id];
})
);
@@ -235,7 +237,7 @@ export function removePieceFromBoard(host: MutableSignal<BoopState>, part: BoopP
const playerData = getPlayer(host, part.player);
board.childIds = board.childIds.filter(id => id !== part.id);
delete board.partMap[part.position.join(',')];
host.value.pieces = host.value.pieces.filter(p => p.id !== part.id);
delete host.value.pieces[part.id];
playerData[part.pieceType].placed--;
}
@@ -292,9 +294,10 @@ export function hasWinningLine(positions: number[][]): boolean {
export function checkGraduation(host: MutableSignal<BoopState>, player: PlayerType): number[][][] {
const pieces = host.value.pieces;
const piecesArray = Object.values(pieces);
const posSet = new Set<string>();
for (const part of pieces) {
for (const part of piecesArray) {
if (part.player === player && part.pieceType === 'kitten') {
posSet.add(`${part.position[0]},${part.position[1]}`);
}
@@ -318,7 +321,8 @@ export function processGraduation(host: MutableSignal<BoopState>, player: Player
}
const board = getBoardRegion(host);
const partsToRemove = host.value.pieces.filter(
const pieces = host.value.pieces;
const partsToRemove = Object.values(pieces).filter(
p => p.player === player && p.pieceType === 'kitten' && allPositions.has(`${p.position[0]},${p.position[1]}`)
);
@@ -333,14 +337,15 @@ export function processGraduation(host: MutableSignal<BoopState>, player: Player
export function countPiecesOnBoard(host: MutableSignal<BoopState>, player: PlayerType): number {
const pieces = host.value.pieces;
return pieces.filter(p => p.player === player).length;
return Object.values(pieces).filter(p => p.player === player).length;
}
export function checkWinner(host: MutableSignal<BoopState>): WinnerType {
const pieces = host.value.pieces;
const piecesArray = Object.values(pieces);
for (const player of ['white', 'black'] as PlayerType[]) {
const positions = pieces
const positions = piecesArray
.filter(p => p.player === player && p.pieceType === 'cat')
.map(p => p.position);
if (hasWinningLine(positions)) return player;
+7 -6
View File
@@ -24,7 +24,7 @@ export function createInitialState() {
{ name: 'x', min: 0, max: BOARD_SIZE - 1 },
{ name: 'y', min: 0, max: BOARD_SIZE - 1 },
]),
parts: [] as TicTacToePart[],
parts: {} as Record<string, TicTacToePart>,
currentPlayer: 'X' as PlayerType,
winner: null as WinnerType,
turn: 0,
@@ -104,26 +104,27 @@ export function hasWinningLine(positions: number[][]): boolean {
export function checkWinner(host: MutableSignal<TicTacToeState>): WinnerType {
const parts = host.value.parts;
const partsArray = Object.values(parts);
const xPositions = parts.filter((p: TicTacToePart) => p.player === 'X').map((p: TicTacToePart) => p.position);
const oPositions = parts.filter((p: TicTacToePart) => p.player === 'O').map((p: TicTacToePart) => p.position);
const xPositions = partsArray.filter((p: TicTacToePart) => p.player === 'X').map((p: TicTacToePart) => p.position);
const oPositions = partsArray.filter((p: TicTacToePart) => p.player === 'O').map((p: TicTacToePart) => p.position);
if (hasWinningLine(xPositions)) return 'X';
if (hasWinningLine(oPositions)) return 'O';
if (parts.length >= MAX_TURNS) return 'draw';
if (partsArray.length >= MAX_TURNS) return 'draw';
return null;
}
export function placePiece(host: MutableSignal<TicTacToeState>, row: number, col: number, player: PlayerType) {
const board = host.value.board;
const moveNumber = host.value.parts.length + 1;
const moveNumber = Object.keys(host.value.parts).length + 1;
const piece = createPart<{ player: PlayerType }>(
{ regionId: 'board', position: [row, col], player },
`piece-${player}-${moveNumber}`
);
host.produce(state => {
state.parts.push(piece);
state.parts[piece.id] = piece;
board.childIds.push(piece.id);
board.partMap[`${row},${col}`] = piece.id;
});