v2.17.0 - familias grids/refs cross-coleccion + roguelike a lo grande
[2.17.0] — 2026-07-08
Release aditivo sobre 2.16.0 (bump minor, SPEC §7.0): dos frentes
independientes. En el core, la familia declarativa grids y el agregado
cross-colección en refs cierran la tercera etapa de reglas puras-de-datos (SPEC §11),
migrando los 4 perfiles que duplicaban esta lógica en JS. En roguelike, mecánicas
100% data-driven (llaves/puertas, jefe, progresión, permadeath), un solo generador
compartido entre los dos motores, mazmorra en mundo continuo 3D con minimapa, y guardado
persistente. La versión del protocolo sigue en 0.1.
Added — familias declarativas grids + agregado cross-colección en refs (SPEC §11, segunda y tercera etapa)
- Familia
gridsdeclarativa en el core:{ rule, emptyRule?, collection|singleton, shape?: { singleton, rowsField?, colsField? }, legend?: { rule, tileTarget, palField?, palMax? } }— valida filas de mapa/escena de ancho uniforme (autoconsistente contra la
primera fila, o contra una forma fija externa comoplatform.rows/.colsvíashape) y
resuelve cada símbolo delegend/fillcontra tiles+paleta, sin ejecutar código de perfil. - 4 perfiles migrados (mismos rule-ids, cero cambio de comportamiento observable):
adventure/dungeon(scene-rows/scene-dims/scene-legend-ref, autoconsistente) y
monster-rpg/tower-defense(map-dims/map-legend-ref, forma fija víaplatform) —
la misma lógica estaba duplicada en JS en los 4. refs[].targetacepta agregado cross-colección:arrayField(+itemField) además
decollection— el set válido se junta escaneando un array anidado en TODAS las
entradas de la colección, no soloObject.keys. Cubre "algún X entre N filas otorga el
valor Y" como dato en vez de código a mano.dungeon.warp-lockmigrado:warp.lockedexige un item que ALGÚNpickup, en
CUALQUIER escena, otorgue — antes unSetarmado a mano enruleScenes, ahora una
entrada declarativa derefs.- SPEC §6.1/§11 actualizado (grids + agregado cross-colección documentados; queda como
"tercera etapa" de reglas puras-de-datos — lo que sigue enruleses lógica genuinamente
no reducible a forma declarativa sin volverse un DSL).test/profile-descriptor.jscubre
las formas nuevas del descriptor (31 chequeos, de 28). Suite sin regresiones: mismas 136
reglas, 190 casos de conformidad, mutation audit 20/20.
Added — roguelike a lo grande: mecánicas por datos, un solo generador, mundo continuo
- Mecánicas nuevas 100% data-driven en el perfil
roguelikey su ejemplo:
llaves y puertas cerradas (generator.lockChance/keyChance+ tileskey/
lockedDoor; cerraduras mutuas entre salas; la llave abre AMBOS lados de forma
permanente), jefe custodiando el cofre (boss: tile/hp/damage — bloquea la
victoria hasta caer), progresión por experiencia (progression.killsPerAtk/
maxBonus: el ataque sube con las bajas) y permadeath declarativo
(progression.permadeath). 3 reglas nuevas de perfil (generator-chance,
boss-fields,progression-fields) → 136 reglas. - Solvencia por construcción: toda sala conserva ≥1 salida SIN cerrar, y como el
cofre aparece donde exploras (generación perezosa), nunca queda tras una puerta
cerrada — las llaves abren atajos y zonas extra. Verificado como invariante por BFS
ennpm test. - Un solo generador, dos motores POR CONSTRUCCIÓN: el visor 2D
(examples/roguelike.html) deja de duplicar la generación y pasa a importar la
misma lógica pura (game3d-logic.mjs) que usa game3d — renderer fino sobre
funciones testeadas.build-standaloneaprende a inlinear imports relativos de
módulos ESM (+2 chequeos) y el standalone sigue autocontenido. - game3d, mazmorra en mundo continuo: las salas se añaden contiguas a la escena
al explorar (comparten muro fronterizo, sin celdas duplicadas) y los pisos se
apilan físicamente en vertical — la cámara viaja con el jugador. Minimapa 3D
del grafo explorado (cubos por sala, pisos apilados, conexiones cerradas en rojo,
escaleras en azul) renderizado en una segunda pasada. Guardado automático de la
run en localStorage (rgSave/rgLoadpuros y testeados; N = nueva partida; el
guardado se limpia al ganar o caer en permadeath) y estadísticas de run en los
overlays (salas, bajas, caídas, profundidad máxima). - +17 chequeos en
npm testy CI (171 de lógica): solvencia por BFS, cerraduras
y llaves generadas, unlock que abre ambos lados, jefe que bloquea/daña/cae,
levelup con tope, permadeath → gameover, save/load ida y vuelta, y el bot
explorador ONLINE que gana jugando (rutea por el grafo conocido, toma salidas
inexploradas y derrota al jefe antes del cofre). - Verificado en navegador en ambos motores: el 2D con la lógica compartida
(puerta cerrada/llave con los textos del GAME.md) y game3d con guardado +
restauración tras recargar, nueva partida con N y victoria del explorador por
eventos de teclado reales — misma firma de run que el test de Node (6 salas,
1 baja, prof. 5): mismo mundo otra vez. - Nota: añadir tiradas al generador cambia los mundos respecto a v2.16.0 — la
paridad garantizada es entre motores, no con releases anteriores.