En un pasado mensaje realizamos un pequeño programa – de código muy breve- que generaba texto automático. En realidad, verborrea carente de sentido pero aparentemente interesante e inteligible.
Ahora, vamos a ver cómo puede escribirse código para generar poemas libres. Hay ya bastantes experiencias al respecto ( por ejemplo esta , esta, esta, o esta ). En esta ocasión usaremos una técnica diferente a la utilizada en la creación de prosa. Un algoritmo que permite una mayor variedad de oraciones ya que, en vez de combinar frases preprogamadas para formar oraciones largas, se usan palabras que encajan en modelos de versos predeterminados.
Estos modelos (que en terminología inglesa suelen recibir el nombre de pattern o de template) son moldes en los que existen huecos que deben llenarse con ciertas palabras, elegidas de entre las de una lista. Algo así como si se tratara de encajar piezas de un puzzle al que se le han quitado algunas, quedando libres huecos en los que sólo caben ciertas fichas.
Así, por ejemplo, a partir de la frase:
Una oropéndola, eso eres. Como el manantial ansioso de la silenciosa poesía
Podríamos crear el siguiente molde:
PALABRA 1 , eso eres. Como el PALABRA 2 PALABRA 3 de la PALABRA 4 PALABRA 5
PALABRA 1 podría ser elegida de una lista, tan larga y variada como queramos, de sustantivos (una oropéndola, en el ejemplo). PALABRA 2 de una lista de sustantivos masculinos en singular (manantial, en el ejemplo). PALABRA 3 de una lista de adjetivos en masculino. PALABRA 4 de una lista de adjetivos femeninos y PALABRA5 de una lista de sustantivos femeninos.
Igualmente sería posible definir moldes que contuvieran verbos:
Una caricia muere calmadamente entre marinos que cantan
Se convertiría en:
Una- SUSTANTIVO FEMENINO-VERBO EN TERCERA PERSONA SINGULAR DEL PRESENTE DE INDICATIVO-ADVERBIO- entre- SUSTANTIVO MASCULINO PLURAL- que- VERBO EN TERCER APEROSNA DEL PLURAL DEL PRESENTE DE INDICATIVO.
Podríamos definir miles de moldes, de templates y listas de miles de palabras. Incluso, estos moldes se pueden copiar de autores humanos célebres para que su estructura sea aún mejor. Y el Corpus de palabras (los Corpa, para ser exactos, puesto que cada lista sería un mundo independiente) podría ser tan extenso como se deseara. Con esos mimbres, basta un código sencillo que vaya rellenando los moldes de manera aleatoria. Los versos unitarios creados serán más o menos buenos en función de la fortuna del azar. Cuanto más extensas sean las listas y más moldes haya, más variados y creativos serán los ripios.
Un poema completo se conformaría añadiendo versos uno tras otro. Aquí chocamos con un problema que hoy por hoy no está resuelto. ¿Cómo dar sentido a todo el poema? ¿Cómo lograr que un verso aleatorio tenga relación con el siguiente verso aleatorio? ¿Cómo conseguir que el conjunto de versos tenga un significado global, por encima del que cada frase individual contenga? Es decir, ¿cómo encontrar versos que tengan sinergias entre ellos, que 1+1>2? Esto es muy complicado porque los algoritmos deberían “comprender” las palabras, las metáforas. Un auténtico y complicado problema de inteligencia artificial para el que no hay una solución todavía.
En el ejercicio práctico que aquí se propone se intentan lograr poemas abiertos, libres, oníricos y, sobre todo, sin rima. El idioma castellano es particularmente complejo a la hora de programar algoritmos que lo manejen (un interesante artículo respecto a la generación automática de poesía en castellano es el de Pablo Gervás aquí ) ya que nuestra lengua presenta género, número, verbo declinado, etc. En inglés, por ejemplo, las cosas son un poco más sencillas porque el género apenas se tiene en cuenta, la declinación del verbo es muy pequeña, las conjugaciones son sencillas y el número se forma si casi variedad. Ello implica que las palabras se combinan más fácilmente. Por el contrario, el inglés es más complicado de tratar en términos de rima porque palabras que se escriben igual pueden no rimar (put no rima con cut, por ejemplo).
Conceptos de este ejercicio
Siguiendo la teoría explicada más arriba se definen 14 tablas.
- Acción verbal en presente de indicativo (soy, si no eres,..)
- Artículo + sustantivo femenino singular (una golondrina, la vida,…)
- Adjetivo femenino singular (desnuda, anhelante, sincera,…)
- Verbo en tercera persona del singular del presente de indicativo (muere, canta, no comprende,…)
- Sustantivo masculino singular (vino, elixir,…)
- Adverbio (calmadamente, remotamente,…)
- Artículo + adjetivo + nombre en masculino singular (El suave rostro, El perdedor,…)
- Sustantivo femenino singular ( apatía, senda, miel,…)
- Exclamación (¡oh!, ¡Señor!,…)
- Sustantivo masculino plural (marinos, deseos,…)
- Verbo en tercera persona del plural del presente de indicativo ( mueren, sienten,…)
- Adjetivo femenino (extraña, ansiosa,…)
- Sustantivo masculino plural (mentirosos, arrogantes,…)
- Adjetivo masculino singular ( extraño, veleidoso,…)
Y se definen 15 moldes o templates, tales como por ejemplo:
El-5-4-14-y-6-.-La-8-4-12-entre-13
Esto implica que se tomaría una palabra de la lista 5, se continuaría con una palabra de las lista 4 y otra de la 14. Entonces, añadiríamos una palabra fija “y” y otra palabra de las lista 6. Pondríamos un punto, una palabra de las lista 8, otra de la 4, otra de la 12, una palabra fija que es “entre” y, finalmente, una de la lista 13. Otros modelos puede ser (elegidos de entre poemas de Gamoneda, Benedetti y otros autores) los siguientes:
2-eso eres.Como el -5-14-de la-3-8
Los-10-son-13-cuando un-14-5-4-6
3-3-12-12-9-cuán-6-7-4
Y-4-3-porque-1-2-3-que-4
9-10-que-11-,-que-11-entre-13
1-2-3-que-4-como el-5-de los- 10
3-,-6-,-como un – 8
9-2-de los-10- que-6-4
El-5-es una-8-12-que-4-6-junto a los -13-10
7-de tu-14-5-4-entre-10
2-,-3-es una-12-y-3-8
Ahora, ya sólo queda escribir el código que vaya rellenando los huecos de los moldes. Para simplificar y abreviar no está contemplado el tratamiento de la puntuación pero esta se puede corregir fácilmente de manera manual una vez generado el poema.
<HEAD>
<TITLE>EJEMPLO DE POEMA AUTOMATICO. Félix Remírez. 2009</TITLE>
<META http-equiv=Content-Type content="text/html; charset=windows-1252">
<script language="JavaScript1.2">
function displaymessage() {
// vamos a generar un poema de una cantidad de versos aleatoria entre 6 y 12. Por tanto hemos de repetir la creación de un verso esas veces con un bulce del tipo 'for'.
// Podríamos cambiar el número de versos mínimos y máximos a voluntad.
var poema=" ";
var rnd_versos = 6 + Math.round((6)*Math.random());
for (x1 = 1; x1 < rnd_versos; x1++) {
// Ahora determinamos aleatoriamente el tipo de verso que vamos a construir de entre los 15 posibles señalados como templates. Sería posible ampliar el número de tipos a voluntad.
var rnd_tipo = 1 + Math.round((14)*Math.random());
// Ahora determinamos las 14 palabras clave aleatoriamente - ver tabla en la explicación teórica.
// Se puede ampliar el ejemplo con más palabras clave tanto en tipo como en casos
var palabras1 = new Array (
"soy",
"eres",
"te muestras como",
"somos",
"vives como",
"si no eres",
"no soy",
"quiero ser",
"deseo que seas",
"seremos siempre"
);
var l1 = palabras1.length;
var rnd_no1 = Math.round((l1-1)*Math.random());
var espacio=" "
var message1=palabras1[rnd_no1]+espacio;
var palabras2 = new Array (
"una oropéndola",
"una golondrina",
"una idea fugaz",
"una caricia en la noche",
"una serpiente",
"una estancia",
"la vida",
"la muerte",
"la casta piedad",
"la aurora"
);
var l2 = palabras2.length;
var rnd_no2 = Math.round((l2-1)*Math.random());
var message2=palabras2[rnd_no2]+espacio;
var palabras3 = new Array (
"desnuda",
"disconforme",
"anhelante",
"helada",
"sincera",
"vacía",
"plena",
"inquieta",
"velada",
"arrebolada"
);
var l3 = palabras3.length;
var rnd_no3 = Math.round((l3-1)*Math.random());
var message3=palabras3[rnd_no3]+espacio;
var palabras4 = new Array (
"muere",
"vive",
"canta",
"expira",
"gime",
"espera",
"no comprende",
"liba",
"revolotea",
"lame"
);
var l4 = palabras4.length;
var rnd_no4 = Math.round((l4-1)*Math.random());
var message4=palabras4[rnd_no4]+espacio;
var palabras5 = new Array (
"vino",
"hechizo",
"manantial",
"elixir",
"espíritu",
"aire",
"grito",
"sonido",
"emporio",
"nirvana"
);
var l5 = palabras5.length;
var rnd_no5 = Math.round((l5-1)*Math.random());
var message5=palabras5[rnd_no5]+espacio;
var palabras6 = new Array (
"calmadamente",
"estrechamente",
"suavemente",
"sosegadamente",
"remotamente",
"imperiosamente",
"injustamente",
"verdaderamente",
"solamente",
"perdidamente"
);
var l6 = palabras6.length;
var rnd_no6 = Math.round((l6-1)*Math.random());
var message6=palabras6[rnd_no6]+espacio;
var palabras7 = new Array (
"el suave rostro",
"el verde Elíseo",
"el noble Arturo",
"el perdedor",
"un ganador",
"el pretendiente",
"el solitario",
"el mercader",
"el respirar",
"el soñador"
);
var l7 = palabras7.length;
var rnd_no7 = Math.round((l7-1)*Math.random());
var message7=palabras7[rnd_no7]+espacio;
var palabras8 = new Array (
"apatía",
"cárcel",
"ubre",
"senda",
"miel",
"esperanza",
"marea",
"tranquilidad",
"poesía",
"sangre"
);
var l8 = palabras8.length;
var rnd_no8 = Math.round((l8-1)*Math.random());
var message8=palabras8[rnd_no8]+espacio;
var palabras9 = new Array (
"¡Oh!",
"¡Señor!",
"¿Por qué?",
"¡No!",
"Sí!",
"¡Oh, poesía!",
"¡Ah, imposibles lejanos!",
"¡Ven!",
"¡Ah, destino!",
"¡No más!"
);
var l9 = palabras9.length;
var rnd_no9 = Math.round((l9-1)*Math.random());
var message9=palabras9[rnd_no6]+espacio;
var palabras10 = new Array (
"marinos",
"deseos",
"sentimientos",
"veleros",
"vapores",
"humos",
"sueños",
"suspiros",
"perfumes",
"afectos"
);
var l10 = palabras10.length;
var rnd_no10 = Math.round((l10-1)*Math.random());
var message10=palabras10[rnd_no10]+espacio;
var palabras11 = new Array (
"mueren",
"viven",
"cantan",
"sienten",
"compran",
"sufren",
"arrullan",
"esperan",
"acallan",
"liban"
);
var l11 = palabras11.length;
var rnd_no11 = Math.round((l11-1)*Math.random());
var message11=palabras11[rnd_no11]+espacio;
var palabras12 = new Array (
"extraña",
"ansiosa",
"veleidosa",
"encantadora",
"sensata",
"silenciosa",
"femenina",
"gozosa",
"olvidada",
"endiosada"
);
var l12 = palabras12.length;
var rnd_no12 = Math.round((l12-1)*Math.random());
var message12=palabras12[rnd_no12]+espacio;
var palabras13 = new Array (
"mentirosos",
"extasiados",
"melindrosos",
"arrogantes",
"sumisos",
"vencidos",
"sensatos",
"inquietos",
"serenos",
"hundidos"
);
var l13 = palabras13.length;
var rnd_no13 = Math.round((l13-1)*Math.random());
var message13=palabras13[rnd_no13]+espacio;
var palabras14 = new Array (
"extraño",
"ansioso",
"veleidoso",
"encantador",
"precario",
"masculino",
"inguinal",
"ebrio",
"mechón de cabello",
"alocado"
);
var l14 = palabras14.length;
var rnd_no14 = Math.round((l14-1)*Math.random());
var message14=palabras14[rnd_no14]+espacio;
// ahora formamos el verso de acuerdo con el modelo - en este ejemplo hay 12 tipos- elegido aleatoriamente. Son los templates o patterns.Cada pattern se programa en un caso de la función case
// antes que nada creamos una variable que maneje la coma ya que es un caracter reservado de javascript
var lacoma=',';
// y vamos a elegir el tipo de verso
switch (rnd_tipo) {
case 0:
var verso="El "+message5+" "+message4+" "+message14+" y "+ message6+". La "+message8+" "+message4+" "+message12+" entre "+messsage13+".";
break;
case 1:
var verso="El "+message5+" "+message4+" "+message14+" y "+ message6+". La "+message8+" "+message4+" "+message12+" entre "+messsage13+".";
break;
case 2:
var verso=message2+" "+lacoma+" eso eres. Como el "+message5+lacoma+" "+message14+" de la "+message3+" "+message8;
break;
case 3:
var verso="Los "+message10+" son "+message13+ "cuando un "+message14+" "+message5+" "+message4+" "+message6;
break;
case 4:
var verso=message3+" "+lacoma+" "+message3+" "+lacoma+" "+message12+" "+lacoma+message12+". "+message9+lacoma+" cuán "+message6+" "+message7+" "+message4;
break;
case 5:
var verso="Y "+message4+" "+message3+" porque "+message1+" "+message2+" "+message3+" que"+" "+message4;
break;
case 6:
var verso=message9+" "+message10+" que "+" "+message11+lacoma+" "+" "+message11+" entre"+" "+message13;
break;
case 7:
var verso=message1+" "+message2+" "+message3+" que "+message4+" como el "+message5+" de los "+message10;
break;
case 8:
var verso=message3+lacoma+" "+message6+" "+lacoma+" como la "+message8;
break;
case 9:
var verso=message9+" "+message2+" de los"+" "+message10+" que "+message6+" "+message4;
break;
case 10:
var verso="El"+" "+message5+" es una"+" "+message8+" "+message12+"que"+" "+message4+" "+message6+" junto a los"+" "+message13+" "+message10;
break;
case 11:
var verso=message7+" de tu"+" "+message14+" "+message5+" que "+message4+" entre"+" "+message10;
break;
case 12:
var verso=message2+lacoma+" "+message3+lacoma+" es una "+message12+"y"+" "+message3+" "+message8;
break;
case 13:
var verso="Los "+message10+" "+message11+" cerca de "+message2+lacoma+" y parecen tan "+message13;
break;
case 14:
var verso=message1+" la "+message8+" que "+message6+" "+message4;
break;
case 15:
var verso= message11+" los "+message10 +" "+message6+". "+message11+" y somos la "+message3+" y "+message12+" "+message8+" del "+message14;
break;
}
//Al salir del bloque switch case tenemos el verso almacenado en la variable verso.
// var poema=poema + verso;
//espacio = document.createTextNode("br");
var myDiv1 = document.getElementById("div1");
myDiv1.appendChild(document.createTextNode(verso));
myDiv1.appendChild(document.createElement("br"));
// y volvemos al bucle for para completar el poema
}
}
// lo visualizamos en una zona DIV llamada div1.
// var poemafinal=poema
</script>
<!--ahora escribimos la estructura HTML de la página-->
<BODY bgcolor="#EEDDD9">
<CENTER>
Generador automático de poemas <bR>
Félix Remírez<BR>
Año 2009<br><br>
</center>
<LEFT>
<!--en la zona DIV1 escribimos el texto cuando hacemos click en un botón-->
<div id="div1">
<input type="button" value="Crear texto" onclick="displaymessage()" />
<br><br>
</div>
<!--en la zona DIV2 ponemos un botón que borre el texto y empiece de nuevo-->
<div id="div2"><br><br>
<input type="button" value="Limpiar pantalla" onclick="window.location.reload()" style="font-family: Verdana; font-size: 8 pt">
</div>
</BODY>
</HTML>
Para probarlo basta con copiar el programa y pegarlo en el bloc de notas. Guardar el fichero con el nombre poemas.html.
El lector tiene libertad total para ampliar las listas de palabras e introducir más templates a su voluntad.
He aquí algunos ejemplos generados con el programa. He modificado la puntuación ya que el algoritmo anterior no la tiene en cuenta. Adjunto sólo un par de ellos que me parecen razonables de entre todos los millones que pueden crearse.
Velada , velada , gozosa ,gozosa .
¡Señor! , cuán estrechamente el pretendiente expira.
Los vapores son sensatos cuando un masculino hechizo
canta sosegadamente .
El sonido es una marea gozosa que gime perdidamente
junto a los serenos veleros.
Una caricia en la noche , helada , es una encantadora y helada poesía. Velada , velada , gozosa ,gozosa .
¡Oh! , cuán calmadamente un ganador vive.
Somos la senda que remotamente revolotea,
el perdedor de tu extraño hechizo
que expira entre deseos.
Quiero ser una oropéndola vacía que muere
como el hechizo de los vapores, desnuda , solamente ,
como la poesía …
Una idea fugaz , eso eres.
Como el sonido veleidoso de la plena esperanza.
Plena , plena , femenina , femenina .
¡Ah, imposibles lejanos! ,
cuán injustamente el pretendiente espera.
Los sueños cantan cerca de la vida , y parecen
tan sensatos ….
helada , helada , endiosada ,endiosada .
¡Ah, destino! , cuán solamente el verde Elíseo vive …
desnuda , desnuda , gozosa ,gozosa .
¡No! , cuán sosegadamente un ganador gime.