Los juegos de texto basados en decisiones se modelan naturalmente como árboles generales: algunas escenas ofrecen dos caminos, otras tres o más. Implementaremos un motor para la historia de un caminante donde cada elección abre una nueva rama.
Con la representación LCRS evitamos arreglos de tamaño fijo y mantenemos el motor ligero.
Separar las opciones en una lista enlazada nos permite manejar un número variable de decisiones.
typedef struct Nodo Nodo;
typedef struct Opcion {
char texto[64];
Nodo *destino;
struct Opcion *siguiente;
} Opcion;
struct Nodo {
char descripcion[256];
Opcion *opciones;
};
La función agregarOpcion agrega nuevas opciones al final de la lista, sin límite de cantidad.
Nodo *crearNodo(const char *descripcion) {
Nodo *n = malloc(sizeof(Nodo));
if (!n) return NULL;
strncpy(n->descripcion, descripcion, sizeof(n->descripcion) - 1);
n->descripcion[sizeof(n->descripcion) - 1] = '\0';
n->opciones = NULL;
return n;
}
void agregarOpcion(Nodo *nodo, const char *texto, Nodo *destino) {
if (!nodo || !destino) return;
Opcion *nueva = malloc(sizeof(Opcion));
if (!nueva) return;
strncpy(nueva->texto, texto, sizeof(nueva->texto) - 1);
nueva->texto[sizeof(nueva->texto) - 1] = '\0';
nueva->destino = destino;
nueva->siguiente = NULL;
if (!nodo->opciones) {
nodo->opciones = nueva;
return;
}
Opcion *actual = nodo->opciones;
while (actual->siguiente) actual = actual->siguiente;
actual->siguiente = nueva;
}
La historia incluye escenas con dos y tres opciones para comprobar el comportamiento del motor.
Nodo *crearHistoria(void) {
Nodo *inicio = crearNodo("Al amanecer despiertas junto al camino principal. "
"El bosque murmura y una columna de humo se alza a lo lejos.");
Nodo *rio = crearNodo("Sigues el rumor del rio y encuentras un puente de madera.");
Nodo *colina = crearNodo("Subes la colina y descubres una fogata reciente.");
Nodo *bosque = crearNodo("Te internas en el bosque y tres sendas se cruzan en un claro.");
Nodo *aldea = crearNodo("Llegas a una aldea silenciosa; las puertas estan abiertas.");
Nodo *cueva = crearNodo("Encuentras una cueva iluminada por cristales azules.");
Nodo *laguna = crearNodo("Ante ti hay una laguna cristalina con un muelle oculto.");
Nodo *torre = crearNodo("La torre de vigilancia cruje mientras el viento la azota.");
Nodo *campamento = crearNodo("Un campamento vacio guarda restos de provisiones.");
Nodo *mercado = crearNodo("Sigues a un zorro y descubres un mercado ambulante.");
Nodo *finalRio = crearNodo("El pescador comparte comida y canciones. Descansas feliz.");
Nodo *finalColina = crearNodo("Avivas la fogata y pasas la noche bajo las estrellas. Fin.");
Nodo *finalAldea = crearNodo("Ayudas a reparar las defensas de la aldea. Te conviertes en su guardian.");
Nodo *finalDesierto = crearNodo("Marchas al desierto y nuevas aventuras te esperan lejos.");
Nodo *finalBosque = crearNodo("El arbol hueco canta historias y te duermes en el claro.");
Nodo *finalLaguna = crearNodo("Nadas hasta el muelle y hallas un cuaderno olvidado. Fin.");
Nodo *finalAgua = crearNodo("Guardas agua brillante y sigues tu viaje reconfortado.");
Nodo *finalMercado = crearNodo("Degustas especias extrañas y compartes risas con mercaderes.");
Nodo *finalBrujula = crearNodo("Obtienes una brujula que apunta a nuevas rutas legendarias.");
Nodo *finalCampamento = crearNodo("Descansas en una hamaca improvisada hasta el atardecer.");
Nodo *finalProvisiones = crearNodo("Empacas provisiones frescas y retomas la marcha con energía.");
Nodo *finalCueva = crearNodo("Conversas con una criatura subterranea y recibes consejos.");
Nodo *finalCristal = crearNodo("Los cristales te guian a un corredor iluminado que te devuelve al camino.");
Nodo *finalTorre = crearNodo("Enciendes la alarma y adviertes a viajeros lejanos. Eres un heroe.");
Nodo *finalHorizonte = crearNodo("Desde la torre observas el amanecer y trazas nuevos planes.");
agregarOpcion(inicio, "Tomar el sendero del rio", rio);
agregarOpcion(inicio, "Subir la colina humeante", colina);
agregarOpcion(inicio, "Explorar el bosque espeso", bosque);
agregarOpcion(rio, "Cruzar el puente hacia la aldea", aldea);
agregarOpcion(rio, "Descender al cauce y explorar cuevas", cueva);
agregarOpcion(rio, "Descansar a la orilla para pescar", finalRio);
agregarOpcion(colina, "Avivar la fogata y esperar", finalColina);
agregarOpcion(colina, "Subir a la torre de vigilancia", torre);
agregarOpcion(colina, "Bajar por un sendero hacia el valle", campamento);
agregarOpcion(bosque, "Seguir las luciernagas hasta una laguna", laguna);
agregarOpcion(bosque, "Escuchar al arbol hueco", finalBosque);
agregarOpcion(bosque, "Perseguir al zorro hasta un mercado", mercado);
agregarOpcion(aldea, "Ofrecer ayuda a los habitantes", finalAldea);
agregarOpcion(aldea, "Seguir camino hacia el desierto", finalDesierto);
agregarOpcion(cueva, "Hablar con el eco subterraneo", finalCueva);
agregarOpcion(cueva, "Seguir un tunel iluminado", finalCristal);
agregarOpcion(laguna, "Nadar hasta el muelle oculto", finalLaguna);
agregarOpcion(laguna, "Recolectar agua brillante y partir", finalAgua);
agregarOpcion(torre, "Encender la alarma de emergencia", finalTorre);
agregarOpcion(torre, "Observar el horizonte en silencio", finalHorizonte);
agregarOpcion(campamento, "Descansar entre las tiendas", finalCampamento);
agregarOpcion(campamento, "Tomar provisiones y volver al camino", finalProvisiones);
agregarOpcion(mercado, "Comer en un puesto de especias", finalMercado);
agregarOpcion(mercado, "Comprar una brujula misteriosa", finalBrujula);
return inicio;
}
El bucle muestra la escena actual, numera cualquier cantidad de opciones y avanza según la elección del usuario. Si un nodo no tiene opciones, la historia finaliza.
void jugar(Nodo *inicio) {
if (!inicio) return;
Nodo *actual = inicio;
char buffer[16];
while (actual) {
puts("\n---------------------------");
puts(actual->descripcion);
if (!actual->opciones) {
puts("\nFin de la historia. Gracias por jugar.");
break;
}
int indice = 1;
for (Opcion *op = actual->opciones; op; op = op->siguiente, ++indice) {
printf("%d) %s\n", indice, op->texto);
}
printf("Selecciona una opcion: ");
if (!fgets(buffer, sizeof(buffer), stdin)) break;
long eleccion = strtol(buffer, NULL, 10);
Opcion *opcionElegida = actual->opciones;
for (int i = 1; opcionElegida && i < eleccion; ++i) {
opcionElegida = opcionElegida->siguiente;
}
if (!opcionElegida) {
puts("Opcion invalida, intenta nuevamente.");
continue;
}
actual = opcionElegida->destino;
}
}
Antes de liberar memoria conviene imprimir el mapa completo para depurar la historia y comprobar que las ramas tienen la descripción y cantidad de opciones correctas. El siguiente helper recorre el árbol y dibuja la indentación.
void mostrarMapa(Nodo *nodo, int nivel) {
if (!nodo) return;
for (int i = 0; i < nivel; ++i) printf(" ");
printf("* %s\n", nodo->descripcion);
int indice = 1;
for (Opcion *op = nodo->opciones; op; op = op->siguiente, ++indice) {
for (int i = 0; i < nivel + 1; ++i) printf(" ");
printf("(%d) %s\n", indice, op->texto);
mostrarMapa(op->destino, nivel + 2);
}
}
Programa autocontenido: crea la aventura del caminante, ejecuta el motor y libera memoria.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Nodo Nodo;
typedef struct Opcion {
char texto[64];
Nodo *destino;
struct Opcion *siguiente;
} Opcion;
struct Nodo {
char descripcion[256];
Opcion *opciones;
};
Nodo *crearNodo(const char *descripcion) {
Nodo *n = malloc(sizeof(Nodo));
if (!n) return NULL;
strncpy(n->descripcion, descripcion, sizeof(n->descripcion) - 1);
n->descripcion[sizeof(n->descripcion) - 1] = '\0';
n->opciones = NULL;
return n;
}
void agregarOpcion(Nodo *nodo, const char *texto, Nodo *destino) {
if (!nodo || !destino) return;
Opcion *nueva = malloc(sizeof(Opcion));
if (!nueva) return;
strncpy(nueva->texto, texto, sizeof(nueva->texto) - 1);
nueva->texto[sizeof(nueva->texto) - 1] = '\0';
nueva->destino = destino;
nueva->siguiente = NULL;
if (!nodo->opciones) {
nodo->opciones = nueva;
return;
}
Opcion *actual = nodo->opciones;
while (actual->siguiente) actual = actual->siguiente;
actual->siguiente = nueva;
}
Nodo *crearHistoria(void) {
Nodo *inicio = crearNodo("Al amanecer despiertas junto al camino principal. "
"El bosque murmura y una columna de humo se alza a lo lejos.");
Nodo *rio = crearNodo("Sigues el rumor del rio y encuentras un puente de madera.");
Nodo *colina = crearNodo("Subes la colina y descubres una fogata reciente.");
Nodo *bosque = crearNodo("Te internas en el bosque y tres sendas se cruzan en un claro.");
Nodo *aldea = crearNodo("Llegas a una aldea silenciosa; las puertas estan abiertas.");
Nodo *cueva = crearNodo("Encuentras una cueva iluminada por cristales azules.");
Nodo *laguna = crearNodo("Ante ti hay una laguna cristalina con un muelle oculto.");
Nodo *torre = crearNodo("La torre de vigilancia cruje mientras el viento la azota.");
Nodo *campamento = crearNodo("Un campamento vacio guarda restos de provisiones.");
Nodo *mercado = crearNodo("Sigues a un zorro y descubres un mercado ambulante.");
Nodo *finalRio = crearNodo("El pescador comparte comida y canciones. Descansas feliz.");
Nodo *finalColina = crearNodo("Avivas la fogata y pasas la noche bajo las estrellas. Fin.");
Nodo *finalAldea = crearNodo("Ayudas a reparar las defensas de la aldea. Te conviertes en su guardian.");
Nodo *finalDesierto = crearNodo("Marchas al desierto y nuevas aventuras te esperan lejos.");
Nodo *finalBosque = crearNodo("El arbol hueco canta historias y te duermes en el claro.");
Nodo *finalLaguna = crearNodo("Nadas hasta el muelle y hallas un cuaderno olvidado. Fin.");
Nodo *finalAgua = crearNodo("Guardas agua brillante y sigues tu viaje reconfortado.");
Nodo *finalMercado = crearNodo("Degustas especias extrañas y compartes risas con mercaderes.");
Nodo *finalBrujula = crearNodo("Obtienes una brujula que apunta a nuevas rutas legendarias.");
Nodo *finalCampamento = crearNodo("Descansas en una hamaca improvisada hasta el atardecer.");
Nodo *finalProvisiones = crearNodo("Empacas provisiones frescas y retomas la marcha con energía.");
Nodo *finalCueva = crearNodo("Conversas con una criatura subterranea y recibes consejos.");
Nodo *finalCristal = crearNodo("Los cristales te guian a un corredor iluminado que te devuelve al camino.");
Nodo *finalTorre = crearNodo("Enciendes la alarma y adviertes a viajeros lejanos. Eres un heroe.");
Nodo *finalHorizonte = crearNodo("Desde la torre observas el amanecer y trazas nuevos planes.");
agregarOpcion(inicio, "Tomar el sendero del rio", rio);
agregarOpcion(inicio, "Subir la colina humeante", colina);
agregarOpcion(inicio, "Explorar el bosque espeso", bosque);
agregarOpcion(rio, "Cruzar el puente hacia la aldea", aldea);
agregarOpcion(rio, "Descender al cauce y explorar cuevas", cueva);
agregarOpcion(rio, "Descansar a la orilla para pescar", finalRio);
agregarOpcion(colina, "Avivar la fogata y esperar", finalColina);
agregarOpcion(colina, "Subir a la torre de vigilancia", torre);
agregarOpcion(colina, "Bajar por un sendero hacia el valle", campamento);
agregarOpcion(bosque, "Seguir las luciernagas hasta una laguna", laguna);
agregarOpcion(bosque, "Escuchar al arbol hueco", finalBosque);
agregarOpcion(bosque, "Perseguir al zorro hasta un mercado", mercado);
agregarOpcion(aldea, "Ofrecer ayuda a los habitantes", finalAldea);
agregarOpcion(aldea, "Seguir camino hacia el desierto", finalDesierto);
agregarOpcion(cueva, "Hablar con el eco subterraneo", finalCueva);
agregarOpcion(cueva, "Seguir un tunel iluminado", finalCristal);
agregarOpcion(laguna, "Nadar hasta el muelle oculto", finalLaguna);
agregarOpcion(laguna, "Recolectar agua brillante y partir", finalAgua);
agregarOpcion(torre, "Encender la alarma de emergencia", finalTorre);
agregarOpcion(torre, "Observar el horizonte en silencio", finalHorizonte);
agregarOpcion(campamento, "Descansar entre las tiendas", finalCampamento);
agregarOpcion(campamento, "Tomar provisiones y volver al camino", finalProvisiones);
agregarOpcion(mercado, "Comer en un puesto de especias", finalMercado);
agregarOpcion(mercado, "Comprar una brujula misteriosa", finalBrujula);
return inicio;
}
void jugar(Nodo *inicio) {
if (!inicio) return;
Nodo *actual = inicio;
char buffer[16];
while (actual) {
puts("\n---------------------------");
puts(actual->descripcion);
if (!actual->opciones) {
puts("\nFin de la historia. Gracias por jugar.");
break;
}
int indice = 1;
for (Opcion *op = actual->opciones; op; op = op->siguiente, ++indice) {
printf("%d) %s\n", indice, op->texto);
}
printf("Selecciona una opcion: ");
if (!fgets(buffer, sizeof(buffer), stdin)) break;
long eleccion = strtol(buffer, NULL, 10);
Opcion *opcionElegida = actual->opciones;
for (int i = 1; opcionElegida && i < eleccion; ++i) {
opcionElegida = opcionElegida->siguiente;
}
if (!opcionElegida) {
puts("Opcion invalida, intenta nuevamente.");
continue;
}
actual = opcionElegida->destino;
}
}
void mostrarMapa(Nodo *nodo, int nivel) {
if (!nodo) return;
for (int i = 0; i < nivel; ++i) printf(" ");
printf("* %s\n", nodo->descripcion);
int indice = 1;
for (Opcion *op = nodo->opciones; op; op = op->siguiente, ++indice) {
for (int i = 0; i < nivel + 1; ++i) printf(" ");
printf("(%d) %s\n", indice, op->texto);
mostrarMapa(op->destino, nivel + 2);
}
}
void liberarOpciones(Opcion *opcion) {
while (opcion) {
Opcion *sig = opcion->siguiente;
free(opcion);
opcion = sig;
}
}
void liberarHistoria(Nodo *nodo) {
if (!nodo) return;
for (Opcion *op = nodo->opciones; op; op = op->siguiente) {
liberarHistoria(op->destino);
}
liberarOpciones(nodo->opciones);
free(nodo);
}
int main(void) {
Nodo *aventura = crearHistoria();
jugar(aventura);
puts("\n=== Mapa completo de escenas ===");
mostrarMapa(aventura, 0);
liberarHistoria(aventura);
return 0;
}