Juan Carlos Álvarez Mateos David Júlvez Estrada Raúl Retuerto Ramos 5º Curso Ing. Telecomunicación Introducción En esta práctica vamos a implementar un Sistema Experto que ayude a nuestros usuarios a encontrar una pareja, una amistad o simplemente una relación esporádica. Nuestro Celestino, se basará en CLIPS sobre JESS, para la implementación del Sistema Experto. Los Sistemas Expertos, son sistemas informáticos que simulan el proceso de aprendizaje, de memorización, de razonamiento, de comunicación y de acción de un experto humano en cualquier rama de la ciencia. Estas características le permiten almacenar datos y conocimiento, sacar conclusiones lógicas, tomar decisiones, aprender de la experiencia y los datos existentes, comunicarse con expertos humanos, explicar el por qué de las decisiones tomadas y realizar acciones como consecuencia de todo lo anterior. Las principales ventajas de utilizar un Sistema Experto son: • • • • Con la ayuda de un Sistema Experto, personas con poca experiencia pueden resolver problemas que requieren un "conocimiento formal especializado". Los Sistemas Expertos pueden obtener conclusiones y resolver problemas de forma más rápida que los expertos humanos. Los Sistemas Expertos razonan pero en base a un conocimiento adquirido y no tienen sitio para la subjetividad. Se ha comprobado que los Sistemas Expertos tienen al menos, la misma competencia que un especialista humano. El uso de Sistemas Expertos es especialmente recomendado en las siguientes situaciones: o Cuando los expertos humanos en una determinada materia son escasos. o En situaciones complejas, donde la subjetividad humana puede llevar a conclusiones erróneas. o Cuando es muy elevado el volumen de datos que ha de considerarse para obtener una conclusión. Para la implementación de nuestro Sistema Experto, utilizaremos CLIPS, una herramienta para el desarrollo de sistemas expertos desarrollado por el Software Technology Branch (STB) de la NASA en 1986. CLIPS está diseñado para facilitar el desarrollo de software para modelar el conocimiento o experiencia humana. Existen tres formas de representar el conocimiento en CLIPS: • Rules: reglas para la heurística basada en el conocimiento y la experiencia. • Deffunctions: para el conocimiento procedimental. • Programación Orientada a Objeto: también para el conocimiento procedimental. Se puede desarrollar software utilizando sólo reglas, sólo objetos o una mezcla de ambos. Además CLIPS fue diseñado para su integración con otros lenguajes de programación como C, ADA, JAVA,… Nosotros utilizaremos CLIPS a través de JESS. JESS fue concebido originalmente como una copia en JAVA de CLIPS, pero actualmente posee numerosas características que lo diferencian de su antecesor. JESS también es una herramienta de desarrollo de sistemas expertos. JESS utiliza un algoritmo especial llamado “Rete” para relacionar los hechos con las reglas. Este algoritmo hace que JESS sea mucho más rápido que una simple concatenación de sentencias if..then en un bucle. JESS se ha ido influenciando de su origen JAVA, y es totalmente integrable con dicho lenguaje, pudiéndose escribir programas tanto como aplicaciones estándar como applets, lo que nos permite dotar de “inteligencia” a las aplicaciones que desarrollemos en este lenguaje. Además existen muchas más posibilidades, como que JESS corra en un servidor y que un interfaz gráfico en el lado cliente llame a éste. Además JESS nos ofrece la posibilidad tanto de integrar código JAVA embebido, como realizar aplicaciones JAVA que utilicen JESS. Por tanto, diseñaremos nuestro Celestino utilizando JESS, lo que permitirá, utilizando su base de conocimientos y los datos que le proporcione el usuario, seleccionar los perfiles más compatibles con nuestro usuario. Diseño del Sistema Experto Como ya hemos comentado, utilizaremos JESS para la implementación de nuestro Celestino, aunque el primer paso consistirá en encontrar al experto para traspasar su conocimiento y experiencia a nuestro sistema. Ya que el tema de encontrar amistades y/o pareja es bastante subjetivo, hemos estado estudiando páginas de contactos en Internet que nos indicarán las preguntas, así como los datos más frecuentes que se solicitan al usuario, así como las preferencias y gustos que se suelen indicar en este tipo de formularios. Además podremos basarnos en sus datos para crear una pequeña base de datos de perfiles de posibles contactos, que servirán como base de conocimiento de nuestro Sistema Experto. Para desarrollar nuestro Celestino realizamos una primera versión por la línea de comandos que implementa unas reglas básicas y permite una interacción básica con el cliente mediante preguntas y respuestas que le dará como resultado los perfiles compatibles existentes en la base de datos. Esta primera versión va eliminando a los perfiles, que no sean compatibles, de la base de conocimientos, según se vayan contestando preguntas, hasta que no queden posibles resultados, caso en el que se le comunicará al usuario de que no se dispone de ningún perfil compatible, o bien, se contesten todas las preguntas básicas. También se han incluido unas preguntas optativas para refinar aún más la búsqueda, y que permiten filtrar a los posibles candidatos/as por su físico (color pelo, ojos, altura) o por sus aficiones. Cuando el Celestino va buscando por aficiones, no elimina a los que no poseen una afición, ya que es posible que se hayan indicado varias aficiones por las que buscar, por lo que se van almacenando todos los perfiles que cumplan con los requisitos y posean al menos una de las aficiones solicitadas. A continuación pasamos a desarrollar un interfaz gráfico que hiciese más amigable y dotase de mayor funcionalidad a nuestro Celestino ayudándose de la potencia del lenguaje JAVA. Interfaz Gráfico Para dotar a nuestro Celestino de un interfaz más amigable con el usuario, decidimos realizar un interfaz gráfico en JAVA. De este modo, el usuario podrá navegar entre los distintos candidatos/as de una forma más intuitiva. Además de este modo se pueden añadir elementos que ayuden a nuestro usuario a decidirse, ya sean fotografías o una disposición de la información más ordenada y más fácilmente accesible. Además podremos valernos de la mayor potencia de JAVA para dotar de mayor funcionalidad y eficiencia a nuestro Celestino al generar dinámicamente los scripts clp que interpretará el motor de inferencias (Rete). A la hora de desarrollar el interfaz gráfico, decidimos utilizar código JAVA embebido en JESS en vez de utilizar JAVA directamente e incluir el código en CLIPS. De este modo, al tratarse de un interfaz gráfico sencillo, reducimos la complejidad de tener que escribir varias clases JAVA y su correspondiente código. Nuestra aplicación en JAVA genera el interfaz gráfico que se le presentará al usuario. El usuario deberá rellenar el formulario que se le presenta y en función de los datos introducidos y de los campos que ha rellenado o ha dejado vacíos, se genera un segundo script a partir de la versión básica, que hemos comentado anteriormente. Este segundo script añade reglas adicionales para procesar los datos adicionales que se pueden especificar en el interfaz gráfico, mucho más completo que el interfaz por la línea de comandos. El funcionamiento de la aplicación JAVA es el siguiente: carga el interfaz gráfico en pantalla y espera a que el usuario introduzca sus datos. Dependiendo de los datos introducidos y a partir del script básico, generará un segundo script con las reglas y definiciones necesarias para la búsqueda y será éste segundo script el que se ejecute con JESS con el motor de inferencias “Rete”. De este modo, al generar dinámicamente el segundo script, no estamos introduciendo reglas innecesarias y el script resulta mucho más eficiente. En la realización de la práctica hemos trabajado juntos, aunque si debemos especificar, podemos descomponer la práctica en: Versión básica: Interfaz Gráfico: Memoria: Juan Carlos Raúl David Código Fuente Celestino.clp – Versión básica por línea de comandos (defglobal ?*vector* = (new java.util.Vector)) (deftemplate persona (slot nombre) (slot telefono) (slot foto) (slot descripcion (type STRING)) (slot sexo) (slot edad) (slot ciudad) (multislot aficion) (slot altura) (slot complexion) (slot pelo) (slot ojos) (slot estudios) (slot relacion)) (defrule inicializar (declare (salience 100)) (not (persona (nombre nunca))) => (load-facts "final/celestino.dat") ) (defrule elimina-sexo ?fact <- (persona (sexo ?*sexo*)) => (retract ?fact)) (defrule elimina-ciudad ?fact <- (persona (ciudad ~?*ciudad*)) => (retract ?fact)) (defrule elimina-relacion ?fact <- (persona (relacion ~?*relacion*)) => (retract ?fact)) (defrule elimina-edad1 ?fact <- (persona (edad ?age&:(< ?*edad* (- ?age 5)))) => (retract ?fact)) (defrule elimina-edad2 ?fact <- (persona (edad ?age&:(> ?*edad* (+ ?age 5)))) => (retract ?fact)) (defrule misma-aficion1 (declare (salience -97)) ?fact <- (persona (nombre ?name)(telefono ?tlf)(foto ?foto)(descripcion ?desc)(edad ?edad)(aficion $?first ?*aficion1* $?last)) => ) (call ?*vector* addElement ?fact) (defrule misma-aficion2 (declare (salience -98)) ?fact <- (persona (nombre ?name)(telefono ?tlf)(foto ?foto)(descripcion ?desc)(edad ?edad)(aficion $?first ?*aficion2* $?last)) => (if (call ?*vector* contains ?fact)then else (call ?*vector* addElement ?fact) ) ) (defrule misma-aficion3 (declare (salience -99)) ?fact <- (persona (nombre ?name)(telefono ?tlf)(foto ?foto)(descripcion ?desc)(edad ?edad)(aficion $?first ?*aficion3* $?last)) => (if (call ?*vector* contains ?fact)then else (call ?*vector* addElement ?fact) ) ) (defglobal ?*file* = ( new java.io.OutputStreamWriter ( new java.io.FileOutputStream "./final/celestino_out.txt" ) ) ) (defglobal ?*writer* = ( new java.io.BufferedWriter ?*file* ) ) (deffunction hola () (bind ?enumeration (call ?*vector* elements ) ) (while (call ?enumeration hasMoreElements ) do ( bind ?fact (call ?enumeration nextElement) ) (call ?*writer* write (call ?fact toString) ) (call ?*writer* newLine) ) (call ?*writer* close) ) (reset) (run) (hola) Celestino.java – Aplicación JAVA con interfaz gráfico import import import import import import import import java.io.*; jess.*; java.util.Vector; javax.swing.*; java.awt.*; javax.swing.border.*; java.awt.event.*; java.util.*; public class Celestino implements ActionListener{ private private private private private private private private private private private private private private private private private private private private JTextField ageField; JCheckBox maleButton; JTextField cityField; JCheckBox friendButton; JCheckBox stableButton; JTextField heightField; JTextField hobby1Field; JTextField hobby2Field; JTextField hobby3Field; JComboBox hairList; JComboBox eyesList; JTextArea descArea; JLabel edadLabel; JLabel tlfLabel; JLabel photoLabel; JLabel countLabel; JButton rightButton; JButton leftButton; Vector vector; int index=0; Celestino ( JTextField ageField, JCheckBox maleButton, JTextField cityField, JCheckBox friendButton, JCheckBox stableButton, JTextField heightField, JTextField hobby1Field, JTextField hobby2Field, JTextField hobby3Field, JComboBox hairList, JComboBox eyesList, JLabel edadLabel, JLabel tlfLabel, JLabel photoLabel,JLabel countLabel, JTextArea descArea,JButton rightButton,JButton leftButton) { this.ageField=ageField; this.maleButton=maleButton; this.cityField=cityField; this.friendButton=friendButton; this.stableButton=stableButton; this.heightField=heightField; this.hobby1Field=hobby1Field; this.hobby2Field=hobby2Field; this.hobby3Field=hobby3Field; this.hairList=hairList; this.eyesList=eyesList; this.edadLabel=edadLabel; this.tlfLabel=tlfLabel; this.photoLabel=photoLabel; this.countLabel=countLabel; this.descArea=descArea; this.leftButton=leftButton; this.rightButton=rightButton; } public void actionPerformed(ActionEvent e) { if ((e.getActionCommand()).equals("Buscar") ) { Rete engine; Batch batch; boolean pelo= false; boolean ojos= false; boolean altura= false; try { this.index=0; leftButton.setEnabled(false); rightButton.setEnabled(false); photoLabel.setIcon(null); edadLabel.setText(" Edad: "); tlfLabel.setText(" Teléfono: "); descArea.setText(""); countLabel.setText(""); engine = new Rete (); batch = new Batch(); BufferedReader reader= new BufferedReader(new InputStreamReader(new FileInputStream("./final/celestino.clp"))); BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("./final/celestino2.clp"))); if (maleButton.isSelected()) writer.write("(defglobal ?*sexo* = hombre)"); else writer.write("(defglobal ?*sexo* = mujer)"); writer.newLine(); if ( (ageField.getText()).length() ==0) writer.write("(defglobal ?*edad* = 25)"); else { writer.write("(defglobal ?*edad* ="); writer.write(ageField.getText()+")"); } writer.newLine(); if ( (cityField.getText()).length() ==0) writer.write("(defglobal ?*ciudad* = Madrid)"); else { writer.write("(defglobal ?*ciudad* ="); writer.write(cityField.getText()+")"); } writer.newLine(); if ( (hobby1Field.getText()).length() ==0) writer.write("(defglobal ?*aficion1* = nada)"); else { writer.write("(defglobal ?*aficion1* = "); writer.write(hobby1Field.getText()+")"); } writer.newLine(); if ( (hobby2Field.getText()).length() ==0) writer.write("(defglobal ?*aficion2* = nada)"); else { writer.write("(defglobal ?*aficion2* = "); writer.write(hobby2Field.getText()+")"); } writer.newLine(); if ( (hobby3Field.getText()).length() ==0) writer.write("(defglobal ?*aficion3* = nada)"); else { writer.write("(defglobal ?*aficion3* = "); writer.write(hobby3Field.getText()+")"); } writer.newLine(); if (friendButton.isSelected()) writer.write("(defglobal ?*relacion* = amistad)"); else if (stableButton.isSelected()) writer.write("(defglobal ?*relacion* = estable)"); else writer.write("(defglobal ?*relacion* = esporadica)"); writer.newLine(); if ( (heightField.getText()).length() !=0) { writer.write("(defglobal ?*centimetros* = "); writer.write(heightField.getText()+")"); writer.newLine(); altura=true; } if ( hairList.getSelectedIndex()!=5 ) { } writer.write("(defglobal ?*color_pelo* = "); writer.write((hairList.getSelectedItem()).toString()+")"); writer.newLine(); pelo=true; if ( eyesList.getSelectedIndex()!=4 ) { } writer.write("(defglobal ?*color_ojos* = "); writer.write((eyesList.getSelectedItem()).toString()+")"); writer.newLine(); ojos=true; String linea; int count=1; while(count++<28) { writer.write(reader.readLine()); writer.newLine(); } if (altura) { writer.write("(defrule elimina-bajos"); writer.newLine(); writer.write(" ?fact <- (persona (altura ?largo&:(> ?*centimetros* (+ ?largo 6))))"); writer.newLine(); writer.write(" => (retract ?fact) )"); writer.newLine(); writer.newLine(); writer.write("(defrule elimina-altos"); writer.newLine(); writer.write(" ?fact <- (persona (altura ?largo&:(< ?*centimetros* (?largo 6))))"); writer.newLine(); writer.write(" =>(retract ?fact))"); writer.newLine(); writer.newLine(); } if (pelo) { writer.write("(defrule elimina-pelos"); writer.newLine(); writer.write("?fact <- (persona (pelo ~?*color_pelo*))"); writer.newLine(); writer.write("=>(retract ?fact))"); writer.newLine(); writer.newLine(); } if (ojos) { writer.write("(defrule elimina-ojos"); writer.newLine(); writer.write("?fact <- (persona (ojos ~?*color_ojos*))"); writer.newLine(); writer.write("=>(retract ?fact))"); writer.newLine(); writer.newLine(); } while((linea=reader.readLine())!=null) { writer.write(linea); writer.newLine(); } reader.close(); writer.close(); batch.batch("./final/celestino2.clp",engine); BufferedReader reader2= new BufferedReader(new InputStreamReader(new FileInputStream("./final/celestino_out.txt"))); vector=new Vector(); while((linea=reader2.readLine())!=null) vector.addElement(linea); reader2.close(); if (vector.size()!=0) { if (vector.size()>1) rightButton.setEnabled(true); updateData((vector.elementAt(0)).toString()); countLabel.setText("1 de "+vector.size()+" personas"); } else countLabel.setText("No Hay coincidencias"); } catch(Exception esc) { esc.printStackTrace(); } } else if ((e.getActionCommand()).equals("Anterior") ) { rightButton.setEnabled(true); index--; if (index==0) leftButton.setEnabled(false); updateData( (vector.elementAt(index)).toString()); countLabel.setText((index+1)+" de "+vector.size()+" personas"); } else { leftButton.setEnabled(true); index++; } if (index==vector.size()-1) rightButton.setEnabled(false); updateData( (vector.elementAt(index)).toString()); countLabel.setText((index+1)+" de "+vector.size()+" personas"); } public void updateData(String data) { StringTokenizer tokens = new StringTokenizer(data,"\"()"); while(true) { String token=tokens.nextToken(); if ( token.startsWith("telefono")) { } tlfLabel.setText(" Teléfono: "+token.substring(8,token.length())); if ( token.startsWith("foto")) { photoLabel.setIcon(new ImageIcon("./final/fotos/"+token.substring(5,token.length()))); } if ( token.startsWith("descripcion") ) { token=tokens.nextToken(); descArea.setText(token); } if ( token.startsWith("edad") ) { edadLabel.setText(" Edad: "+token.substring(4,token.length())); break; } } } public static void main(String args[]) throws Exception { JFrame frame = new JFrame ("CELESTINO"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel mainPane = new JPanel(); frame.setContentPane(mainPane); JPanel leftPane = new JPanel(); JPanel rightPane = new JPanel(); JPanel mydataPane = new JPanel(); JPanel searchPane = new JPanel(); mainPane.setLayout(new GridLayout(1,2)); GridBagConstraints c2 = new GridBagConstraints(); GridBagLayout gridbag2 = new GridBagLayout(); JButton button = new JButton ("Buscar"); ButtonGroup group = new ButtonGroup (); JCheckBox maleButton = new JCheckBox("Varón"); JCheckBox femaleButton = new JCheckBox("Mujer"); ButtonGroup group2 = new ButtonGroup(); JCheckBox friendButton = new JCheckBox("Amistad"); JCheckBox stableButton = new JCheckBox("Estable"); JCheckBox sexButton = new JCheckBox("Esporádica"); JPanel radioPanel = new JPanel( new GridLayout(0,1)); JLabel JLabel JLabel JLabel JLabel JLabel JLabel JLabel JLabel JLabel JLabel JLabel JLabel JLabel JLabel sexLabel = new JLabel(" Sexo:"); cityLabel = new JLabel(" Ciudad: "); relationLabel = new JLabel(" Tipo de relación que buscas:"); ageLabel = new JLabel(" Edad: "); nameLabel = new JLabel(" Nombre: "); searchLabel1 = new JLabel(" Los siguientes datos son " ); searchLabel2 = new JLabel( " referentes a la persona que buscas: hobbyLabel1 = new JLabel(" Aficción 1: "); hobbyLabel2 = new JLabel(" Aficción 2: "); hobbyLabel3 = new JLabel(" Aficción 3: "); heightLabel = new JLabel(" Altura(cm): "); hairLabel = new JLabel(" Color de pelo: "); eyesLabel = new JLabel(" Color de ojos: "); tlfLabel = new JLabel(" Teléfono: "); countLabel = new JLabel(""); JTextField JTextField JTextField JTextField JTextField JTextField JTextField cityField = new JTextField (10); ageField = new JTextField (10); nameField = new JTextField (10); heightField = new JTextField (10); hobbyField1 = new JTextField (10); hobbyField2 = new JTextField (10); hobbyField3 = new JTextField (10); JComboBox hairList = new JComboBox(); " ); JComboBox eyesList = new JComboBox(); JLabel photoLabel = new JLabel(); JLabel edadLabel = new JLabel(" Edad : "); JTextArea descArea = new JTextArea(); ImageIcon leftIcon = new ImageIcon("./final/left.gif"); ImageIcon rightIcon = new ImageIcon("./final/right.gif"); JButton leftButton = new JButton("Anterior",leftIcon); JButton rightButton = new JButton("Siguiente",rightIcon); Celestino celestino = new Celestino (ageField,maleButton, cityField, friendButton, stableButton, heightField, hobbyField1,hobbyField2, hobbyField3, hairList, eyesList,edadLabel,tlfLabel,photoLabel, countLabel, descArea, rightButton,leftButton); button.addActionListener(celestino); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); Insets insets = new Insets(5,0,5,0); leftPane.setLayout(new GridLayout(2,1)); mydataPane.setLayout(gridbag); CompoundBorder compoundBorder1 = BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(" Datos Personales: "),BorderFactory.createEmptyBorder(5,5,5,5)); mydataPane.setBorder(compoundBorder1); group.add(maleButton); group.add(femaleButton); group2.add(friendButton); group2.add(stableButton); group2.add(sexButton); c.insets=insets; c.gridwidth = GridBagConstraints.RELATIVE; c.fill = GridBagConstraints.BOTH; gridbag.setConstraints(nameLabel,c); nameLabel.setHorizontalAlignment(SwingConstants.LEFT); mydataPane.add(nameLabel); c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(nameField,c); mydataPane.add(nameField); c.gridwidth = GridBagConstraints.RELATIVE; gridbag.setConstraints(ageLabel,c); ageLabel.setHorizontalAlignment(SwingConstants.LEFT); mydataPane.add(ageLabel); c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(ageField,c); mydataPane.add(ageField); gridbag.setConstraints(sexLabel,c); sexLabel.setHorizontalAlignment(SwingConstants.LEFT); mydataPane.add(sexLabel); insets.bottom=0; insets.top=0; c.fill = GridBagConstraints.NONE; gridbag.setConstraints(maleButton,c); maleButton.setSelected(true); mydataPane.add(maleButton); gridbag.setConstraints(femaleButton,c); mydataPane.add(femaleButton); insets.bottom = 5; insets.top = 5; c.fill = GridBagConstraints.HORIZONTAL; c.gridwidth = GridBagConstraints.RELATIVE; gridbag.setConstraints(cityLabel,c); cityLabel.setHorizontalAlignment(SwingConstants.LEFT); mydataPane.add(cityLabel); c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(cityField,c); mydataPane.add(cityField); gridbag.setConstraints(relationLabel,c); relationLabel.setHorizontalAlignment(SwingConstants.LEFT); mydataPane.add(relationLabel); friendButton.setSelected(true); radioPanel.add(friendButton); radioPanel.add(stableButton); radioPanel.add(sexButton); c.fill = GridBagConstraints.NONE; gridbag.setConstraints(radioPanel,c); mydataPane.add(radioPanel); leftPane.add(mydataPane); searchPane.setLayout(gridbag); CompoundBorder compoundBorder2 = BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(" Datos de la persona que buscas: "),BorderFactory.createEmptyBorder(5,5,5,5)); searchPane.setBorder(compoundBorder2); c.fill = GridBagConstraints.BOTH; c.gridwidth = GridBagConstraints.RELATIVE; gridbag.setConstraints(heightLabel,c); heightLabel.setHorizontalAlignment(SwingConstants.LEFT); searchPane.add(heightLabel); c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(heightField,c); searchPane.add(heightField); c.gridwidth = GridBagConstraints.RELATIVE; c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(hairLabel,c); hairLabel.setHorizontalAlignment(SwingConstants.LEFT); searchPane.add(hairLabel); hairList.addItem("moreno"); hairList.addItem("castaño"); hairList.addItem("rubio"); hairList.addItem("pelirrojo"); hairList.addItem("blanco"); hairList.addItem("No importa"); hairList.setSelectedIndex(5); c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(hairList,c); searchPane.add(hairList); c.gridwidth = GridBagConstraints.RELATIVE; c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(eyesLabel,c); eyesLabel.setHorizontalAlignment(SwingConstants.LEFT); searchPane.add(eyesLabel); eyesList.addItem("negros"); eyesList.addItem("marrones"); eyesList.addItem("azules"); eyesList.addItem("verdes"); eyesList.addItem("No importa"); eyesList.setSelectedIndex(4); c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(eyesList,c); searchPane.add(eyesList); c.gridwidth = GridBagConstraints.RELATIVE; c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(hobbyLabel1,c); hobbyLabel1.setHorizontalAlignment(SwingConstants.LEFT); searchPane.add(hobbyLabel1); c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(hobbyField1,c); searchPane.add(hobbyField1); c.gridwidth = GridBagConstraints.RELATIVE; c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(hobbyLabel2,c); hobbyLabel2.setHorizontalAlignment(SwingConstants.LEFT); searchPane.add(hobbyLabel2); c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(hobbyField2,c); searchPane.add(hobbyField2); c.gridwidth = GridBagConstraints.RELATIVE; c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(hobbyLabel3,c); hobbyLabel3.setHorizontalAlignment(SwingConstants.LEFT); searchPane.add(hobbyLabel3); c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(hobbyField3,c); searchPane.add(hobbyField3); c.fill = GridBagConstraints.NONE; gridbag.setConstraints(button,c); searchPane.add(button); leftPane.add(searchPane); rightPane.setLayout(gridbag2); c2.fill = GridBagConstraints.BOTH; c2.gridwidth = GridBagConstraints.REMAINDER; photoLabel.setHorizontalAlignment(SwingConstants.CENTER); gridbag2.setConstraints(photoLabel,c2); rightPane.add(photoLabel); gridbag2.setConstraints(edadLabel,c2); edadLabel.setHorizontalAlignment(SwingConstants.LEFT); rightPane.add(edadLabel); gridbag2.setConstraints(tlfLabel,c2); tlfLabel.setHorizontalAlignment(SwingConstants.LEFT); rightPane.add(tlfLabel); descArea.setEditable(false); descArea.setLineWrap(true); descArea.setWrapStyleWord(true); descArea.setMargin( new Insets(3,3,3,3)); JScrollPane scrollPane = new JScrollPane(descArea); scrollPane.setPreferredSize(new Dimension(275,120)); JPanel textAreaPane = new JPanel(); textAreaPane.add(scrollPane); TitledBorder titledBorder = BorderFactory.createTitledBorder(" Descripción: "); Border emptyBorder = BorderFactory.createEmptyBorder(0,5,5,5); CompoundBorder compoundBorder = BorderFactory.createCompoundBorder(titledBorder,emptyBorder); textAreaPane.setBorder(compoundBorder); c2.fill = GridBagConstraints.NONE; gridbag2.setConstraints(textAreaPane,c2); rightPane.add(textAreaPane); c2.gridwidth=1; c2.weightx=1.0; c2.fill= GridBagConstraints.BOTH; gridbag2.setConstraints(leftButton,c2); leftButton.setEnabled(false); rightPane.add(leftButton); c2.gridwidth = GridBagConstraints.REMAINDER; gridbag2.setConstraints(rightButton,c2); rightButton.setEnabled(false); rightPane.add(rightButton); gridbag2.setConstraints(countLabel,c2); countLabel.setHorizontalAlignment(SwingConstants.CENTER); rightPane.add(countLabel); mainPane.add(leftPane); mainPane.add(rightPane); rightButton.addActionListener(celestino); leftButton.addActionListener(celestino); frame.pack(); frame.setResizable(false); frame.setVisible(true); } } Referencias • Manual de JESS http://herzberg.ca.sandia.gov/jess/docs • Página Oficial de JESS http://herzberg.ca.sandia.gov/jess/ • Comparativa CLIPS vs. JESS http://www.comp.lancs.ac.uk/~kristof/research/notes/clipsvsjess/ • Página Oficial de CLIPS http://www.ghg.net/clips/CLIPS.html • Páginas de Contactos http://www.amorspain.com http://www.contactos.net http://www.contactos.telecitasnacionales.com • API de JAVA http://java.sun.com/j2se/1.4.2/docs/api/index.html