feat: add gift zone city team and mashup mode

This commit is contained in:
manpengan
2026-03-29 00:49:03 +08:00
parent 5c2b4f40f9
commit 92bf1f5070
12 changed files with 862 additions and 60 deletions

View File

@@ -105,6 +105,38 @@ function placePieces(piecePool, levelPreset, rng) {
return placedPieces
}
export function generateBoardFromDefinition({
boardId,
cityId,
levelId,
seed,
elements,
levelPreset,
extraState = {},
}) {
const effectiveSeed = seed ?? levelPreset.seedBase
const rng = createRng(effectiveSeed)
const selectedElements = [...elements]
const counts = normalizePiecesPerElement(levelPreset.piecesPerElement, levelPreset.elementCount)
const piecePool = createPiecePool(selectedElements, counts)
const pieces = placePieces(shuffleWithRng(piecePool, rng), levelPreset, rng)
const overlapGraph = buildOverlapGraph(pieces)
const boardState = {
boardId: boardId ?? `${cityId}-${levelId}-${effectiveSeed}`,
cityId,
levelId,
seed: effectiveSeed,
pieces,
overlapGraph,
metrics: {},
...extraState,
}
boardState.metrics = evaluateBoard(boardState, levelPreset)
return boardState
}
export function generateBoard({ cityId, levelId, seed, contentSystem }) {
const city = contentSystem.getCity(cityId)
const levelPreset = contentSystem.getLevelPreset(cityId, levelId)
@@ -116,23 +148,14 @@ export function generateBoard({ cityId, levelId, seed, contentSystem }) {
const effectiveSeed = seed ?? levelPreset.seedBase
const rng = createRng(effectiveSeed)
const selectedElements = pickElements(city, levelPreset, rng)
const counts = normalizePiecesPerElement(levelPreset.piecesPerElement, levelPreset.elementCount)
const piecePool = createPiecePool(selectedElements, counts)
const pieces = placePieces(shuffleWithRng(piecePool, rng), levelPreset, rng)
const overlapGraph = buildOverlapGraph(pieces)
const boardState = {
boardId: `${cityId}-${levelId}-${effectiveSeed}`,
return generateBoardFromDefinition({
cityId,
levelId,
seed: effectiveSeed,
pieces,
overlapGraph,
metrics: {},
}
boardState.metrics = evaluateBoard(boardState, levelPreset)
return boardState
elements: selectedElements,
levelPreset,
})
}
export default generateBoard

View File

@@ -0,0 +1,99 @@
import { generateBoardFromDefinition } from './generate-board.js'
import { createRng, shuffleWithRng } from './random.js'
function createMashupLevelPreset(elementCount) {
return {
id: 1,
seedBase: 31001,
elementCount,
piecesPerElement: Array.from({ length: elementCount }, () => 3),
layers: 4,
density: 'medium_high',
}
}
function getSourceCities(cityIds, contentSystem) {
return cityIds
.map((cityId) => contentSystem.getCity(cityId))
.filter(Boolean)
}
function createCityElementPools(cities, rng) {
return cities.map((city) => ({
cityId: city.id,
cursor: 0,
elements: shuffleWithRng(
city.elements.map((element) => ({
...element,
sourceCityId: city.id,
sourceCityName: city.display.name,
})),
rng,
),
}))
}
function pickMashupElements(cities, targetCount, rng) {
const pools = createCityElementPools(cities, rng)
const selected = []
while (selected.length < targetCount) {
let didAdd = false
for (const pool of pools) {
const element = pool.elements[pool.cursor]
if (!element) {
continue
}
selected.push(element)
pool.cursor += 1
didAdd = true
if (selected.length === targetCount) {
break
}
}
if (!didAdd) {
break
}
}
return selected
}
export function generateMashupBoard({ cityIds, seed, contentSystem }) {
const cities = getSourceCities(cityIds, contentSystem)
if (cities.length === 0) {
throw new Error('Mashup mode requires at least one source city')
}
const effectiveSeed = seed ?? 31001
const rng = createRng(effectiveSeed)
const maxElementCount = cities.reduce((sum, city) => sum + city.elements.length, 0)
const elementCount = Math.min(8, maxElementCount)
const levelPreset = createMashupLevelPreset(elementCount)
const selectedElements = pickMashupElements(cities, elementCount, rng)
return generateBoardFromDefinition({
boardId: `mashup-${effectiveSeed}`,
cityId: 'mashup',
levelId: 1,
seed: effectiveSeed,
elements: selectedElements,
levelPreset,
extraState: {
sourceCityIds: cities.map((city) => city.id),
elementDefinitions: selectedElements.map((element) => ({
elementId: element.id,
name: element.name,
sourceCityId: element.sourceCityId,
sourceCityName: element.sourceCityName,
})),
},
})
}
export default generateMashupBoard

View File

@@ -1,6 +1,7 @@
export { classifyDeadlock } from './classify-deadlock.js'
export { evaluateBoard } from './evaluate-board.js'
export { generateBoard } from './generate-board.js'
export { generateMashupBoard } from './generate-mashup-board.js'
export {
buildOverlapGraph,
getClickablePieces,