Lección 73 de 170

Volver declarativa una regla ajustable

Curso de desarrollo de videojuegos con IA

Crea y valida una estructura de configuración que separe los valores ajustables del comportamiento durante la ejecución.

1062. Identidad de la lección

Módulo
3.4 — Datos declarativos
Lección
2 — Volver declarativa una regla ajustable
Tipo académico
Construcción guiada
Tipo de esquema
práctica
Orden
2 del módulo
Tiempo estimado
45–60 minutos

En esta lección convertirás una regla ajustable del juego en un pequeño contrato de datos que se pueda revisar. Definirás su estructura, establecerás valores predeterminados seguros, rechazarás entradas inválidas y campos desconocidos, distinguirás entre un campo ausente y uno que contenga explícitamente undefined, e identificarás qué código conserva la responsabilidad del comportamiento.

1063. Objetivo de aprendizaje

Al terminar, podrás definir y validar un conjunto de datos declarativos para que la configuración válida llegue al sistema de comportamiento, la inválida se rechace con una razón útil y los campos omitidos se traten de forma distinta a los valores proporcionados con un tipo incorrecto.

1064. Por qué importa

Un valor ajustable solo resulta útil si el juego puede cargarlo de manera consistente y otra persona puede revisar qué significa. Separar la configuración del comportamiento permite ajustar una regla sin reescribir el flujo que la ejecuta. La validación protege el tiempo de ejecución frente a datos incompletos, mal tipados, fuera de rango o escritos con errores.

La política sobre campos ausentes también forma parte del contrato. Si un campo es opcional, su valor predeterminado debe aplicarse normalmente cuando se omite. Si el campo se proporciona, su valor todavía debe validarse. Sin comprobar que la propiedad exista realmente en el objeto, { enabled: undefined } puede confundirse con una omisión y sustituirse silenciosamente por el valor predeterminado.

1065. Conocimientos previos

Debes poder:

  • distinguir entre datos declarativos, comportamiento y estado de ejecución, como se trabajó en 3.4 L1 — Los datos no son comportamiento;
  • describir en lenguaje sencillo la regla que quieres ajustar;
  • leer la sintaxis básica de objetos y tipos;
  • localizar de dónde obtiene actualmente el valor el comportamiento que lo consume.

Si el código existente no está claro, sigue primero el valor desde su origen hasta el comportamiento que lo utiliza. No empieces trasladando código ejecutable a un archivo de datos.

1066. Concepto central

Un conjunto de datos declarativos es un contrato, no solo una colección de números. El contrato responde seis preguntas:

  1. Estructura: ¿Qué campos existen y qué tipo tiene cada uno?
  2. Valores predeterminados: ¿Qué valor seguro se aplica cuando se omite un campo opcional?
  3. Presencia: ¿Un undefined proporcionado explícitamente cuenta como omisión o como valor inválido?
  4. Validez: ¿Qué valores deben rechazarse y por qué?
  5. Campos desconocidos: ¿Qué ocurre si la entrada contiene una clave ajena al esquema aprobado?
  6. Responsabilidad: ¿Qué comportamiento lee los datos validados y aplica la regla del juego?

Para esta lección, elige una regla pequeña y ajustable. Un ejemplo genérico es una regla de recuperación:

const recoveryConfig = {
  delaySeconds: 3,
  amount: 10,
  enabled: true
};

El objeto describe una configuración. No realiza la recuperación, no espera tres segundos ni modifica una entidad. Esas acciones siguen siendo comportamiento.

Un contrato útil es:

type RecoveryConfig = {
  delaySeconds: number;
  amount: number;
  enabled: boolean;
};

El tipo describe el resultado ya validado, pero no demuestra que los datos externos o editados sean válidos. Una frontera de validación debe examinar los valores reales antes de devolver un RecoveryConfig.

Esta lección adopta dos políticas explícitas:

  • Los campos desconocidos se rechazan.
  • Un valor predeterminado solo se aplica cuando la propiedad correspondiente no existe en el objeto. Si el campo existe y contiene undefined, se considera un valor proporcionado y la validación de tipo debe rechazarlo.

1067. Modelo mental

Usa el modelo DATOS → VALIDAR → CONSUMIR:

Etapa Responsabilidad Pregunta guía
DATOS Declarar valores ajustables y su estructura prevista ¿Qué se puede ajustar?
VALIDAR Detectar la presencia de campos, aplicar valores a las omisiones, validar lo proporcionado y rechazar claves desconocidas ¿Está la entrada completa, bien tipada, permitida y sin ambigüedades?
CONSUMIR Ejecutar el comportamiento con valores validados ¿Qué hace el juego en ejecución con esta configuración?

La dirección es de una sola vía. El sistema de comportamiento puede leer la configuración validada, pero esta no debe contener funciones ejecutables, flujo de control ni estado de ejecución oculto.

1068. Ejemplo concreto

Supón que el comportamiento contiene valores escritos directamente en el código:

const delaySeconds = 3;
const amount = 10;

Traslada únicamente los valores ajustables a un contrato de configuración:

type RecoveryConfig = {
  delaySeconds: number;
  amount: number;
  enabled: boolean;
};

const defaultRecoveryConfig: RecoveryConfig = {
  delaySeconds: 3,
  amount: 10,
  enabled: true
};

El validador puede rechazar campos desconocidos, distinguir una omisión de un undefined explícito y comprobar todos los valores resultantes:

function validateRecoveryConfig(
  input: Record<string, unknown>
): RecoveryConfig {
  const allowedKeys = new Set([
    "delaySeconds",
    "amount",
    "enabled"
  ]);

  for (const key of Object.keys(input)) {
    if (!allowedKeys.has(key)) {
      throw new Error(
        `Unknown recovery configuration field: ${key}`
      );
    }
  }

  const delaySeconds = Object.hasOwn(input, "delaySeconds")
    ? input.delaySeconds
    : defaultRecoveryConfig.delaySeconds;

  const amount = Object.hasOwn(input, "amount")
    ? input.amount
    : defaultRecoveryConfig.amount;

  const enabled = Object.hasOwn(input, "enabled")
    ? input.enabled
    : defaultRecoveryConfig.enabled;

  if (
    typeof delaySeconds !== "number" ||
    !Number.isFinite(delaySeconds) ||
    delaySeconds < 0
  ) {
    throw new Error(
      "delaySeconds must be a finite number greater than or equal to zero"
    );
  }

  if (
    typeof amount !== "number" ||
    !Number.isFinite(amount) ||
    amount <= 0
  ) {
    throw new Error(
      "amount must be a finite number greater than zero"
    );
  }

  if (typeof enabled !== "boolean") {
    throw new Error("enabled must be a boolean");
  }

  return { delaySeconds, amount, enabled };
}

Esta implementación produce resultados distintos:

  • { amount: 12 } se acepta. Los campos ausentes reciben sus valores predeterminados.
  • { enabled: undefined } se rechaza. El campo está presente, por lo que el valor proporcionado debe ser booleano.
  • { amount: -5 } se rechaza porque el valor queda fuera del rango permitido.
  • { delaySecond: 4 } se rechaza porque la clave no pertenece al esquema.

Aquí es importante usar Object.hasOwn. Una comprobación como input.enabled === undefined trataría igual un campo omitido y { enabled: undefined }. Esa alternativa solo sería correcta si el contrato definiera deliberadamente undefined como omisión y existieran pruebas para esa política. Esta lección no adopta esa política.

El validador no decide cuándo ocurre la recuperación. Establece una configuración utilizable. El comportamiento existente sigue decidiendo cuándo comienza la recuperación y cómo aplica la cantidad configurada.

1069. Flujo de trabajo con IA

Usa la IA como asistente de implementación y como objeto de revisión, no como autoridad sobre el contrato.

  1. Escribe con tus propias palabras la regla, los campos, los valores aplicables a las omisiones, los casos inválidos y las políticas de presencia y de campos desconocidos.
  2. Pide a la IA que proponga un tipo y un validador a partir de esa especificación.
  3. Exige que distinga entre una propiedad ausente y una propiedad establecida explícitamente como undefined.
  4. Compara la propuesta con el modelo DATOS → VALIDAR → CONSUMIR.
  5. Rechaza funciones ejecutables, estado de ejecución, recortes no documentados, comportamiento dentro de la estructura de datos o gestión silenciosa de campos desconocidos.
  6. Inspecciona el cambio resultante y ejecuta las comprobaciones disponibles.
  7. Pide a la IA que explique cada rama de validación y contrasta la explicación con el código.

Una petición útil sería: «Propón un tipo de configuración y un validador para esta regla. Rechaza campos desconocidos. Aplica valores predeterminados solo a propiedades propias ausentes y rechaza los valores undefined proporcionados explícitamente. No implementes el comportamiento del juego. Enumera las suposiciones por separado».

1070. Errores comunes

Confundir los valores predeterminados con la validación

Combinar un objeto con valores predeterminados puede completar campos omitidos, pero no demuestra que los valores proporcionados tengan el tipo o el rango correctos. { amount: -5 } sigue pareciendo completo después de la combinación.

Comprobar únicamente si el valor es undefined

Una condición como input.enabled === undefined no distingue un campo omitido de { enabled: undefined }. Si el contrato establece que los valores predeterminados solo se aplican a omisiones, comprueba la presencia con Object.hasOwn y valida después el valor proporcionado.

Dejar pasar campos desconocidos

Una errata como delaySecond puede parecer aceptada aunque no tenga ningún efecto. Esta lección rechaza los campos desconocidos antes de construir el resultado validado.

Recortar silenciosamente valores inválidos

El recorte puede ocultar un error de autoría. Rechaza el valor salvo que el diseño defina expresamente el recorte como parte de la regla.

1071. Práctica guiada

Crea un conjunto de configuración validado para una regla ajustable del proyecto o para un ejemplo pequeño y aislado.

Paso 1: Expresa la regla

Elige una regla con al menos dos valores ajustables. Escribe una frase sobre lo que hace el juego en ejecución y otra sobre lo que los datos pueden controlar.

Paso 2: Define el contrato

Documenta:

  • al menos dos campos tipados;
  • el significado y la unidad de cada campo numérico;
  • los valores aplicables cuando se omiten campos opcionales;
  • las condiciones de validez;
  • el rechazo de campos desconocidos;
  • si un undefined explícito cuenta como omisión o como entrada inválida.

En esta práctica, aplica valores únicamente a propiedades propias ausentes y rechaza los undefined proporcionados explícitamente.

Paso 3: Implementa la validación

Crea una frontera de validación y prueba al menos:

  • una configuración completa y válida;
  • una configuración parcial que reciba un valor para un campo omitido;
  • un tipo incorrecto;
  • un valor fuera de rango;
  • un campo desconocido o escrito con una errata;
  • un campo conocido proporcionado explícitamente como undefined.

El último caso debe fallar según la política de esta lección. No lo sustituyas silenciosamente por un valor predeterminado.

Paso 4: Conecta el consumidor

Haz que el comportamiento elegido consuma la configuración validada en lugar de un valor ajustable escrito directamente en el código. No traslades el comportamiento a los datos.

Paso 5: Revisa la frontera

Responde:

  • ¿Qué campos se pueden ajustar?
  • ¿Qué omisiones reciben valores?
  • ¿Qué ocurre si un campo conocido se establece explícitamente como undefined?
  • ¿Qué entradas inválidas o desconocidas se rechazan?
  • ¿Qué función conserva la responsabilidad del comportamiento del juego?

1072. Validación y evidencias

La evaluación práctica asociada exige:

  • el contrato de configuración declarado;
  • los significados, unidades y valores aplicables a omisiones;
  • la frontera de validación;
  • evidencias de casos válidos, omitidos, mal tipados, fuera de rango, desconocidos y con undefined explícito;
  • un consumidor que use únicamente datos validados;
  • una revisión del cambio que confirme que el estado de ejecución y el comportamiento ejecutable permanecen fuera del conjunto de datos.

1073. Ideas clave

  • Los datos declarativos describen valores ajustables; no ejecutan la regla.
  • Un tipo describe el resultado previsto, mientras que la validación comprueba la entrada real.
  • Los valores predeterminados resuelven omisiones, no errores.
  • La presencia de una propiedad y la igualdad de su valor son comprobaciones distintas.
  • Los campos desconocidos necesitan una política explícita; en esta lección se rechazan.
  • El sistema de comportamiento conserva la responsabilidad de aplicar la configuración validada.

1074. Siguiente lección

Continúa con 3.5 — Acoplamiento (lesson-s3-3-5-01-coupling-has-a-change-cost).

1075. Comprobación

Responde estas preguntas por tu cuenta antes de leer las respuestas.

¿Cuál es la responsabilidad principal de la etapa de validación?

  • A. Ejecutar la regla del juego
  • B. Almacenar el estado actual de ejecución
  • C. Aplicar valores a las omisiones y rechazar configuraciones inválidas
  • D. Reemplazar el sistema de comportamiento
Mostrar respuesta y explicación

Respuesta: Aplicar valores a las omisiones y rechazar configuraciones inválidas

Por qué: La validación establece una configuración completa y permitida. No ejecuta la regla ni conserva el estado de ejecución.

¿Por qué no basta con combinar una entrada con valores predeterminados para validarla?

  • A. Una combinación no puede completar campos opcionales omitidos
  • B. Una combinación puede conservar tipos incorrectos o valores fuera de rango
  • C. Los valores predeterminados siempre ejecutan comportamiento
  • D. Un tipo no puede contener campos numéricos
Mostrar respuesta y explicación

Respuesta: Una combinación puede conservar tipos incorrectos o valores fuera de rango

Por qué: Los valores predeterminados resuelven omisiones, no garantizan la corrección. Los valores proporcionados todavía requieren comprobaciones de tipo y rango.

¿Qué elemento pertenece al consumidor del comportamiento y no al conjunto de datos declarativos?

  • A. Un retraso de recuperación en segundos
  • B. Un booleano que activa la regla
  • C. La cantidad de recuperación configurada
  • D. La operación que espera y aplica la recuperación
Mostrar respuesta y explicación

Respuesta: La operación que espera y aplica la recuperación

Por qué: Esperar y modificar una entidad son comportamientos ejecutables. El retraso, el indicador de activación y la cantidad son valores de configuración.

¿Qué debes hacer si una propuesta de IA recorta silenciosamente un valor inválido, pero el diseño no define ese recorte?

  • A. Aceptarla porque cualquier resultado válido es seguro
  • B. Trasladar el recorte al archivo de datos
  • C. Rechazar o modificar la propuesta y definir la política prevista
  • D. Eliminar toda la validación
Mostrar respuesta y explicación

Respuesta: Rechazar o modificar la propuesta y definir la política prevista

Por qué: El recorte silencioso puede ocultar un error de autoría o de diseño. La política debe decidirse de manera intencional.

Según la política de esta lección, ¿cómo debe tratarse { enabled: undefined }?

  • A. Tratar el campo como omitido y aplicar true
  • B. Rechazarlo porque la propiedad está presente con un valor no booleano
  • C. Eliminar el campo y devolver el resto del objeto
  • D. Convertir undefined en false
Mostrar respuesta y explicación

Respuesta: Rechazarlo porque la propiedad está presente con un valor no booleano

Por qué: Object.hasOwn(input, "enabled") identifica la propiedad como proporcionada. Por tanto, su valor debe superar la validación booleana en lugar de recibir el valor reservado para una omisión.

Apoyar