Ejercicio: El estado de la conversación
Objetivo
Construir con tus propias manos una mini máquina de estados: una tabla de transiciones, una sesión y una función pura procesar que nunca modifica lo que recibe. Es una versión reducida del motor.js real del curso (sin catálogo ni textos), pero con las mismas reglas de fondo.
Cómo se trabaja
La parte A se hace en tu terminal con el código de referencia del curso: te sirve para ver al motor real cambiar de estado. La parte B son tres funciones de JavaScript puro que se comprueban en esta página. Los eventos son palabras simples ("hola", "pedir", "cerrar"...) en lugar de mensajes de WhatsApp: así te concentras en el estado.
La tabla de transiciones que debes implementar (cada fila: estado actual, evento y estado siguiente):
| Estado | Evento | Siguiente estado |
|---|---|---|
INICIO |
hola |
MENU |
INICIO, MENU |
pedir |
ELIGIENDO |
ELIGIENDO |
cerrar |
TIPO_ENTREGA |
TIPO_ENTREGA |
delivery |
DIRECCION |
TIPO_ENTREGA |
recojo |
PAGO |
DIRECCION |
direccion |
PAGO |
PAGO |
pago |
CONFIRMAR |
CONFIRMAR |
confirmar |
FIN |
| cualquiera | cancelar |
FIN |
| cualquiera | humano |
ESPERANDO_HUMANO |
| cualquiera | menu |
MENU |
Reglas extra: en ESPERANDO_HUMANO solo funcionan menu y cancelar; cualquier otro evento se ignora sin contarlo como fallo (el bot calla). Si el estado es FIN, el siguiente evento empieza una sesión nueva.
💡 Reto extra
Añade un evento volver que regrese al estado anterior. Para lograrlo la sesión necesita recordar el historial (por ejemplo un arreglo historial de estados). Piensa: ¿qué debería pasar con volver en INICIO? Es el tipo de decisión de diseño que aparece al crecer una máquina de estados.
Pasos
Marca cada paso a medida que lo completes. Tu avance se guarda en este navegador.
💡 Pista 1: Una tabla en vez de muchos if
Un objeto de objetos como { INICIO: { hola: "MENU" } } te deja escribir TRANSICIONES[estado]?.[evento]. Guarda aparte los eventos globales (cancelar, humano, menu) y consúltalos si la tabla del estado no tiene el evento.
💡 Pista 2: ESPERANDO_HUMANO es un caso especial
Trátalo antes que nada: si el estado es ESPERANDO_HUMANO, solo menu y cancelar devuelven un estado nuevo; para todo lo demás devuelve null. En procesar, ese null no debe sumar fallos en este estado.
💡 Pista 3: No mutar la sesión
Crea la sesión nueva con { ...base } y modifica solo la copia. Para el caso FIN, usa como base crearSesion(sesion.telefono, ahora).
🔎 Comprueba tu trabajo
Pega tu código y pulsa Comprobar. La revisión ocurre en tu navegador; nada se envía a ningún sitio. Cada punto en rojo incluye una pista. Pega solo el JavaScript, sin las etiquetas <script>.
✅ Ver una solución posible
Intenta resolverlo antes de mirar. Tu versión puede ser distinta y también correcta.
const TRANSICIONES = {
INICIO: { hola: "MENU", pedir: "ELIGIENDO" },
MENU: { pedir: "ELIGIENDO" },
ELIGIENDO: { cerrar: "TIPO_ENTREGA" },
TIPO_ENTREGA: { delivery: "DIRECCION", recojo: "PAGO" },
DIRECCION: { direccion: "PAGO" },
PAGO: { pago: "CONFIRMAR" },
CONFIRMAR: { confirmar: "FIN" },
};
const GLOBALES = { cancelar: "FIN", humano: "ESPERANDO_HUMANO", menu: "MENU" };
function crearSesion(telefono, ahora = 0) {
return { telefono, estado: "INICIO", fallos: 0, ultimoMensajeCliente: ahora };
}
function siguienteEstado(estado, evento) {
if (estado === "ESPERANDO_HUMANO") {
return evento === "menu" || evento === "cancelar" ? GLOBALES[evento] : null;
}
return TRANSICIONES[estado]?.[evento] ?? GLOBALES[evento] ?? null;
}
function procesar(sesionPrevia, evento, ahora = 0) {
const base = sesionPrevia.estado === "FIN"
? crearSesion(sesionPrevia.telefono, ahora)
: sesionPrevia;
const sesion = { ...base, ultimoMensajeCliente: ahora };
const siguiente = siguienteEstado(sesion.estado, evento);
if (siguiente) {
sesion.estado = siguiente;
sesion.fallos = 0;
} else if (sesion.estado !== "ESPERANDO_HUMANO") {
sesion.fallos += 1;
}
return sesion;
}