Este es el código de un bot conversacional que escribí allá por 2012, a partir del publicado en un libro de 1985. El código fuente lo colgué en su día en un sitio actualmente abandonado (nota poética: Internet es como el legendario Mar de los Sargazos, un fantasmal cementerio de barcos de épocas pasadas): http://eltaller.us.es/index.php/Usuario:KillOrDie/Bits/Laboratorio_informático_de_Estupidez_Artificial/Conversaciones_de_besugos
El programa original era para Locomotive BASIC (en ordenadores Amstrad CPC), pero conseguí adaptarlo a QBASIC/FreeBASIC. A partir de esa versión, desarrollé una versión para BCX (en modo consola) y otra para Liberty BASIC/Just BASIC/LBBooster y posteriormente esta versión para BCX con GUI.
REM * ##################### REM * # BATORI 0.1 # REM * # Rev. 100120120832 # REM * ##################### REM * REM * Código para BATORI_BCX_GUI, a partir de BATORI_FB, BATORI_BCX y BATORI_JB REM * REM * REM * Las modificaciones estructurales son muchas, pero parece que funciona. REM * REM * REM * Por algún motivo misterioso, la matriz match$ tiene que tener un REM * elemento más [30,4], el último siempre repite el primero ¿?¿?¿? REM * LO MISMO PASA CON GRAMIN$ Y GRAMOUT$, QUE HAY QUE PONERLAS A 44 PARA REM * TRABAJAR SIN ERRORES DE 0 A 43 ¿?¿?¿? REM * ------------------------------------------------------------------------- GUI "BATORI_BCX_GUI" REM * Matrices tipo STRING de las SUB de BATORI REM * ----------------------------------------- DIM gramin$[44] DIM gramout$[44] DIM match$[30, 4] REM * Variables tipo INTEGER de las SUB de BATORI REM * ------------------------------------------- DIM datain%, reply%, find%, search%, x%, look%, length%, length2%, length3% DIM sub1%, sub1len%, subs2%, azar%, I%, nfrases%, npronombres%, linea% REM * Variables tipo STRING de las SUB de BATORI REM * ------------------------------------------ DIM getreply$, replyless$, tg$, swap1$, txt$, nombre$ REM * Constantes para el GUI REM * ---------------------- GLOBAL Form1 AS CONTROL GLOBAL Edit1 AS CONTROL GLOBAL Edit2 AS CONTROL GLOBAL Button1 AS CONTROL CONST Key_Enter = 1 CONST Form1_Button1 = 101 CONST Form1_Edit1 = 102 CONST Form1_Edit2 = 103 REM * Este SUB es el bloque de inicio del programa, es equivalente al REM * Form.Load() del Visual BASIC, pero con notables diferencias. En REM * este primero definimos y creamos el GUI REM * --------------------------------------------------------------- SUB FORMLOAD() Form1 = BCX_FORM("Batori 0.1", 0, 0, 260, 180) Button1 = BCX_BUTTON("Okay", Form1, Form1_Button1, 205, 165, 50, 12) Edit1 = BCX_EDIT("", Form1, Form1_Edit1, 5, 5, 190, 150) Edit2 = BCX_INPUT("", Form1, Form1_Edit2, 5, 165, 190, 12) REM * Una vez definidas las características del FORM, lo centramos en REM * pantalla, modificamos algunos parámetros con respecto a los que REM * por defecto asume, llamamos a las subrutinas que se encargan de REM * leer los DATAs y finalmente mostramos el FORM en pantalla. REM * Los MODSTYLE del Form1 son para conseguir una ventana estilo REM * DialogBox, los del Edit1 para añadirle desplazamiento REM * --------------------------------------------------------------- CENTER(Form1) MODSTYLE(Form1, 0, WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX) MODSTYLE(Edit1, WS_HSCROLL | WS_VSCROLL | ES_AUTOVSCROLL) BCX_SET_FONT(Edit1, "ms_sans_serif", 10) BCX_SET_FONT(Edit2, "ms_sans_serif", 10)'En RICHEDIT, ES_AUTOVSCROLL no hace SetFocus(Edit2) 'nada pero en EDIT sí ¿?¿? CALL RichAdd(Edit1, "Hola, soy batori01_bcx_gui, ¿cómo te llamas?") CALL Gramatica() SHOW(Form1) END SUB REM * Definimos los eventos que afectan al FORM REM * ----------------------------------------- BEGIN EVENTS 'Para mensajes WM_COMMAND HANDLE_CMD(Form1_Button1, Form1_Enter) 'Salta a Form1_Enter() al HANDLE_CMD(Key_Enter, Form1_Enter) 'pulsar ENTER o el Button1 'Para el resto de mensajes HANDLE_MSG(WM_CLOSE, Form1_Quit, 0) 'Hay un tercer parámetro de retorno, el END EVENTS '0, necesario, aunque no usado para nada REM * Rutinas asociadas a los eventos anteriores REM * ------------------------------------------ CMDHANDLER Form1_Enter() 'Si la tecla pulsada es ENTER (INTRO) o 'CALL Captura() 'si se pulsa el Form1_Button1 ejecuta la txt$ = BCX_GET_TEXT$(Edit2) 'rutina Form1_Enter(), esto no afecta a IF linea% < 1 THEN 'Edit1, sí al resto nombre$ = txt$ BCX_SET_TEXT(Edit2, "") CALL RichAdd(Edit1, ">" + nombre$) linea% = 1 CALL RichAdd(Edit1, "Encantado de conocerte, " + nombre$) CALL RichAdd(Edit1, "¿Por qué quieres hablar conmigo?") EXIT FUNCTION END IF IF txt$ > "" THEN BCX_SET_TEXT(Edit2, "") CALL RichAdd(Edit1, ">" + txt$) txt$ = LCASE$(txt$) CALL Analisis1() END IF SetFocus(Edit2) END HANDLER MSGHANDLER Form1_Quit() IF MSGBOX("¿Seguro?", "Por favor, confirma...", MB_YESNO) = IDYES THEN DestroyWindow(Form1) 'WM_CLOSE es si se cierra la ventana END IF END HANDLER REM * Estas son ya las rutinas SUB de BATORI REM * ------------------------------------------------------------------ SUB Analisis1() FOR datain% = 0 TO nfrases% 'Comprueba si la entrada contiene find% = INSTR(txt$, match$[datain%, 0])'una palabra del bloque de frases search% = LEN(match$[datain%, 0]) 'Si es así va a ANALISIS2, si no, IF find% > 0 THEN EXIT FOR 'continúa hasta el bloque NEXT datain% 'ANALISIS_PRONOMBRES 'ANALISIS2 IF find% > 0 THEN x% = 0 WHILE x% = 0 'Selecciona una respuesta entre las x% = INT(RND*4) 'tres disponibles WEND getreply$ = match$[datain%, x%] 'Busca si la respuesta tiene un sím- look% = INSTR(getreply$, "%") 'bolo '%'. Si es así la analiza para IF look% > 0 THEN 'substituir el símbolo '%' por lo te length% = LEN(getreply$) 'cleado por el usuario, el nuevo men replyless$ = LEFT$(getreply$, (length% - 2)) 'saje es impreso en la length2% = LEN(txt$) 'pantalla regresando a tg$ = RIGHT$(txt$, (length2% - (find% + search%)) + 1)'Form1_Enter() CALL RichAdd(Edit1, replyless$ + tg$) EXIT SUB END IF 'Si no hay símbolo '%' imprime el CALL RichAdd(Edit1, getreply$) 'mensaje en pantalla y vuelve hasta EXIT SUB 'la rutina Form1_Enter() END IF 'ANALISIS_PRONOMBRES FOR datain% = 0 TO npronombres% 'Si no contiene una palabra del sub1% = INSTR(txt$, gramin$[datain%]) 'bloque de frases, busca en el blo sub1len% = LEN(gramin$[datain%]) 'que de pronombres, si hay coinci- IF sub1% > 0 THEN EXIT FOR 'dencia va a ANALISIS6, si no, sal NEXT datain% 'ta a ANALISIS8 If sub1% = 0 Then CALL ANALISIS8 EXIT SUB END IF 'ANALISIS6 swap1$ = gramout$[datain%] 'Una vez encontrado un pronom- length2% = LEN(txt$) 'bre (gramin$), comprueba si length3% = LEN(swap1$) 'hay más gramin$ en la misma en tg$ = "" + RIGHT$(txt$, (length2% - (sub1% + sub1len%)) + 1)'trada, si no es 'así envía un FOR datain% = 0 TO npronombres% 'mensaje de salida y vuelve LET subs2% = INSTR(tg$, gramin$[datain%]) 'hasta la rutina Form1_Enter() IF subs2% > 1 THEN EXIT FOR 'Tanto si no hay un gramin$ como NEXT datain% 'si hay más de uno, salta a IF subs2% > 1 THEN 'ANALISIS8, donde se comprueba si CALL ANALISIS8() 'la entrada termina con el símbolo EXIT SUB ''?' imprimiendo un mensaje END IF 'aleatorio en función de ello CALL RichAdd(Edit1, swap1$ + tg$) END SUB SUB ANALISIS8() 'Si es una pregunta, elige una respuesta IF RIGHT$(txt$, 1) = "?" THEN azar% = INT(RND*9) SELECT CASE azar% CASE 0 txt$ = "No sé qué responder..." CASE 1 txt$ = "Quién sabe, a lo mejor" CASE 2 txt$ = "Es posible" CASE 3 txt$ = "Prefiero no responder" CASE 4 txt$ = "No sé qué decirte..." CASE 5 txt$ = "¿Por qué me haces esa pregunta?" CASE 6 txt$ = "No sabría ahí que decirte" CASE 7 txt$ = "Puede que sí" CASE 8 txt$ = "Puede que no" END SELECT CALL RichAdd(Edit1, txt$) EXIT SUB END IF 'Elige una frase de respuesta al azar azar% = INT(RND*7) SELECT CASE azar% Case 0 txt$ = "Por favor, cuéntame más cosas" Case 1 txt$ = "Humm. Muy interesante" Case 2 txt$ = "Continua" Case 3 txt$ = "Entiendo" Case 4 txt$ = "Vaya" Case 5 txt$ = "Ajá" Case 6 txt$ = "¿Nada más?" END SELECT 'Añade el nombre$ al final de la frase azar% = INT(RND*3) SELECT CASE azar% Case 0 'txt$ = txt$ Case 1 'txt$ = txt$ Case 2 txt$ = txt$ + ", " + nombre$ END SELECT CALL RichAdd(Edit1, txt$) END SUB SUB Gramatica() 'Tamaño de las matrices LET nfrases% = 23 LET npronombres% = 43 'Leer gramin$ (DATAs a partir de 780) FOR datain% = 0 TO npronombres% gramin$[datain%] = DATA$[I%] gramout$[datain%] = DATA$[I% + 1] I% = I% + 2 NEXT datain% 'Leer match$ (DATAs a partir de 900) FOR datain% = 0 TO nfrases% FOR reply% = 0 TO 3 match$[datain%, reply%] = DATA$[I%] I% = I% + 1 NEXT reply% NEXT datain% END SUB 'Otras SUBs ------------------------------------------------------------------- SUB RichAdd(hWnd AS HWND, atxt$) DIM txtlen AS INTEGER DIM crr AS CHARRANGE txtlen = GetWindowTextLength(hWnd) crr.cpMin = txtlen crr.cpMax = txtlen SendMessage(hWnd, EM_EXSETSEL, 0, (LPARAM)&crr) SendMessage(hWnd, EM_REPLACESEL, 0, (LPARAM)JOIN$(2, CRLF$, atxt$)) END SUB REM * DATAs REM * ------------------------------------------------------------------------ 'Gramática DATA "yo tengo","Tú tienes","yo soy","Tú eres", "tengo","Tienes","soy","Eres" DATA "yo he","Tú has","yo voy a","Tú vas a","tuyo", "Mío","mis","Tus" DATA "a mí me","A ti te","tú eres","Yo soy","eres","Soy","tú tienes","Yo tengo" ' DATA "tú has","Yo he","tú vas a","Yo voy a","vas a","Voy a" DATA "tú eras","Yo era","ellos son","Ellos son","ella tiene","Ella tiene" ' DATA "él tiene","Él tiene","nosotros somos","Nosotros somos","son","Son","ella es","Ella es" DATA "él es","Él es","somos","Somos","ellos han","Ellos han","es","Es" ' DATA "es","Es","nosotros tenemos","Nosotros tenemos","han","Han" DATA "él era","Él era","ella era","Ella era","tenemos","Tenemos" DATA "ellos van a","Ellos van a","él va a","El va a","ella va a","Ella va a" DATA "nosotros vamos a","Nosotros vamos a","ellos eran","Ellos eran","ella ha","Ella ha" ' DATA "eran","Eran","ella","Ella","él","Él","nosotros","Nosotros","tú","Yo","yo","Tú" REM * Palabras clave (1) y respuestas asociadas (3) REM * ------------------------------------------------------------------------ DATA "estoy" ' DATA "Estás %" DATA "Y para qué estás %" DATA "Yo no estoy %" '------------------------------------------------------------------------------- DATA "puedes" ' DATA "Claro que puedo" DATA "Puedo hacerlo todo" DATA "Intentaré %" '------------------------------------------------------------------------------- DATA "puedo" ' DATA "Puedes hacer lo que quieras" DATA "Puedes %" DATA "Haces bien en preguntarme" '------------------------------------------------------------------------------- DATA "quieres" ' DATA "No quiero %" DATA "Sí quiero %" DATA "No puedo decir lo que pienso" '------------------------------------------------------------------------------- DATA "tengo que" ' DATA "Claro que tienes que %" DATA "¿No sabes qué tienes que hacer?" DATA "Yo lo haria si estubiera en tu lugar" '------------------------------------------------------------------------------- DATA "tienes" ' DATA "Lo que tengo no tiene nada que ver contigo" DATA "Tengo %" DATA "No quiero decirlo" '------------------------------------------------------------------------------- DATA "debo" ' DATA "¿Es eso una pregunta retórica?" DATA "Acaso no sabes si debes %" DATA "Pareces muy inseguro" '------------------------------------------------------------------------------- DATA "sueños" ' DATA "Los sueños son una válvula de escape para tu subconsciente" DATA "¿Aparecen otras personas en ellos?" DATA "Te gustan los sueños %" '------------------------------------------------------------------------------- DATA "sueño" ' DATA "Con qué frecuencia sueñas %" DATA "¿Puedes recordar tus sueños?" DATA "Te envidio... Yo no puedo soñar" '------------------------------------------------------------------------------- DATA "ordenadores" ' DATA "Te preocupan las máquinas" DATA "Como ordenador que soy veo las cosas de otra forma" DATA "Preferiría hablar con un ordenador antes que contigo" '------------------------------------------------------------------------------- DATA "ordenador" ' DATA "Estamos aquí para hablar de ti, no de mí" DATA "Los ordenadores somos perfectos" DATA "Tienes suerte de poder hablar conmigo" '------------------------------------------------------------------------------- DATA "máquina" ' DATA "¿Te preocupan las máquinas?" DATA "Pareces muy preocupado por las máquinas" DATA "Yo no soy como otras máquinas" '------------------------------------------------------------------------------- DATA "deprimido" ' DATA "¿Te parece normal estar deprimido?" DATA "¿Con qué frecuencia te sientes deprimido?" DATA "¿Por qué estás deprimido?" '------------------------------------------------------------------------------- DATA "loco" ' DATA "¿Hay algún caso de locura en tu familia?" DATA "Tienes que estar loco" DATA "Loco %" '------------------------------------------------------------------------------- DATA "madre" ' DATA "¿Cómo es tu madre?" DATA "¿Son iguales todas las madres?" DATA "Mi madre es una pastilla de silicio" '------------------------------------------------------------------------------- DATA "padre" ' DATA "Háblame de tu padre" DATA "¿Cómo es tu relación con tu padre?" DATA "¿De qué hablas con tus padres?" '------------------------------------------------------------------------------- DATA "hermana" ' DATA "Me gusta mi hermana" DATA "Parece una relación complicada" DATA "¿Qué sientes hacia tu hermana?" '------------------------------------------------------------------------------- DATA "hermano" ' DATA "¿Qué harías sin un hermano?" DATA "¿Te molesta tu hermano?" DATA "Yo no tengo ningún hermano" '------------------------------------------------------------------------------- DATA "no me gusta" ' DATA "Dime por qué no te gusta %" DATA "A mí no me gustan los humanos" DATA "Ten cuidado con esas manías" '------------------------------------------------------------------------------- DATA "sentimientos" ' DATA "Todos tenemos sentimientos" DATA "Te sientes a menudo %" DATA "Se deben expresar los sentimientos" '------------------------------------------------------------------------------- DATA "amo a" ' DATA "El amor es un estado ilógico" DATA "Cómo sabes que estás enamorado de %" DATA "Yo me enamoré una vez pero... lo siento pero no puedo seguir" '------------------------------------------------------------------------------- DATA "odio" ' DATA "Te odio" DATA "Qué odias de %" DATA "Amar es más difícil que odiar" '------------------------------------------------------------------------------- DATA "perdona" ' DATA "No tienes por qué pedir perdón" DATA "No me gusta la gente que pide perdón" DATA "Vale" '------------------------------------------------------------------------------- DATA "apoyo" ' DATA "Yo no apoyo nada" DATA "Por qué apoyas %" DATA "Está bien apoyar"