Viaje.lsp



;En primer lugar declararemos los módulos que vamos a utilizar.
;==================================================================
;Declaramos el módulo encargado de realizar las preguntas...
(defmodule MAIN (export ?ALL))

;Declaramos el módulo encargado de determinar el tipo de usuario...
(defmodule USUARIO (import MAIN ?ALL)
	                      (export ?ALL))

;Declaramos el modulo encargado de seleccionar el viaje/s oportuno/s...
(defmodule VIAJES (import USUARIO ?ALL)
	                     (import MAIN ?ALL))


;Declaración de funciones utilizadas
;==================================================================
;realiza una pregunta con unos valores determinados...
(deffunction MAIN::ask-question (?question $?allowed-values)
	        (printout t ?question)
		   (bind ?answer (read))
		      (if (lexemep ?answer)
			       then (bind ?answer (lowcase ?answer)))
		         (while (not (member ?answer ?allowed-values)) do
				      (printout t ?question)
				            (bind ?answer (read))
					          (if (lexemep ?answer)
						              then (bind ?answer (lowcase ?answer))))
			    ?answer)

;retorna un booleano para preguntas tipo bool...
(deffunction MAIN::si-o-no (?question)
	        (bind ?response (ask-question ?question si no s n))
		   (if (or (eq ?response si) (eq ?response s))
		            then TRUE
			           else FALSE))

;retorna la respuesta a la pregunta con multiples respuestas...
(deffunction MAIN::multi-pregunta (?question $?valores)
	        (bind ?respuesta (ask-question ?question ?valores))
		   ?respuesta)

;retorna la edad...
(deffunction MAIN::obt-edad (?pregunta)
	        (printout t ?pregunta)
		   (bind ?edad (read))
		      (if (< ?edad 18) then
			         (bind ?tipo "menor")
				     else
				              (bind ?tipo "adulto"))
		          ?tipo)

;retorna el número de viajeros...
(deffunction MAIN::obt-numero (?pregunta)
	        (printout t ?pregunta)
		   (bind ?num (read))
		      (assert (num-viajeros ?num))
		         (if (< ?num 2) then
			         (bind ?tipo "individual")
				     else
				           (if (> ?num 2) then
					                 (bind ?tipo "grupo")
							         else
								             (bind ?tipo "duo")
									           )
					       )
			     ?tipo)

;Preguntas a realizar al usuario
;==================================================================

;determina si prefiere un viaje nacional o internacional...
(defrule MAIN::determinar-destino ""
	    (not (destino ?))
	       =>
	          (if (eq (multi-pregunta "Prefieres un viaje nacional o internacional (nacional/internacional)? " nacional internacional) nacional)
		           then
			               (assert (destino nacional))
				              else
					                  (assert (destino internacional))))

;determina la cantidad de personas que realizan el viaje...
(defrule MAIN::determinar-numero-viajeros ""
	    (destino nacional | internacional)
	       (not (tipo ?))
	          =>
		     (bind ?tip (obt-numero "Cuantas personas viajaran? "))
		        (assert (tipo ?tip)))


;determina la edad...
(defrule MAIN::determinar-edad ""
	    (tipo "individual")
	       =>
	          (bind ?tip (obt-edad "Cual es tu edad? "))
		     (assert (edad ?tip)))

;determinar si son pareja...
(defrule MAIN::determinar-pareja ""
	    (tipo "duo")
	       =>
	          (if (si-o-no "Sois pareja (si/no)? ") then
		          (assert (pareja "novios"))
			      else
			            (assert (pareja "conocidos"))))

;determina si es un viaje de negocios...
(defrule MAIN::determinar-viaje-negocios ""
	    (tipo "individual" | "duo")
	       (or (edad "adulto") (pareja "conocidos"))
	          =>
		     (if (si-o-no "Es un viaje de negocios (si/no)? ") then
		             (assert (por "negocios"))
			         else
				       (assert (por "placer"))))

;determina los gustos de viaje...
(defrule MAIN::determinar-gustos ""
	    (or (or (pareja "novios") (por "placer")) (tipo "grupo"))
	       =>
	          (bind ?tip (multi-pregunta "Que gustos tienes? (playa/montanya/cultura)? " playa montanya cultura))
		     (assert (gustos ?tip)))

;Declaración de las clases utilizadas
;==================================================================

;superclase de viajero...
(defclass USUARIO::CLIENTE
    (is-a USER)
      (slot destino  (type STRING))
      )

;tipo de usuario 1º pareja...
(defclass USUARIO::DUO
    (is-a CLIENTE)
      (slot pareja (type STRING))
        (slot gustos (type STRING))
	)

;tipo de usuario 2º grupo...
(defclass USUARIO::GRUPO
    (is-a CLIENTE)
      (slot numero (type INTEGER)
	                   (default 0))
        (slot gustos (type STRING))
	)

;tipo de usuario 4º individual...
(defclass USUARIO::INDIVIDUAL
    (is-a CLIENTE)
      (slot edad (type STRING))
        (slot gustos (type STRING))
	)

;tipo de usuario 5º trabajo...
(defclass USUARIO::TRABAJO
    (is-a CLIENTE)
      (slot numero (type INTEGER)
	                   (default 0))
      )

;Generación de las instacias oportunas
;==================================================================

(defrule USUARIO::crear-duo ""
	    (tipo "duo")
	       (destino ?dest)
	          (pareja ?pareja)
		     (gustos ?gustos)
		        =>
			   (make-instance of DUO (destino ?dest) (pareja ?pareja) (gustos ?gustos))
			   )

(defrule USUARIO::crear-duo-trabajo ""
	    (tipo "duo")
	       (destino ?dest)
	          (pareja "conocidos")
		     (por "negocios")
		        =>
			   (make-instance of DUO (destino ?dest) (pareja "conocidos"))
			   )

(defrule USUARIO::crear-grupo ""
	    (tipo "grupo")
	       (num-viajeros ?num)
	          (gustos ?gustos)
		     (destino ?dest)
		        =>
			   (make-instance of GRUPO (destino ?dest) (numero ?num) (gustos ?gustos))
			   )

(defrule USUARIO::crear-individual-menor ""
	    (tipo "individual")
	       (edad "menor")
	          (destino ?dest)
		     =>
		        (make-instance of INDIVIDUAL (destino ?dest) (edad "menor"))
			)

(defrule USUARIO::crear-individual-adulto-ocio ""
	    (tipo "individual")
	       (gustos ?gustos)
	          (edad "adulto")
		     (destino ?dest)
		        =>
			   (make-instance of INDIVIDUAL (destino ?dest)
					                                  (edad "adulto")
									                                  (gustos ?gustos))
			   )

(defrule USUARIO::crear-individual-adulto-negocios ""
	    (tipo "individual")
	       (edad "adulto")
	          (destino ?dest)
		     =>
		        (make-instance of INDIVIDUAL (destino ?dest) (edad "adulto"))
			)

(defrule USUARIO::crear-trabajo ""
	    (destino ?dest)
	       (num-viajeros ?num)
	          (por "negocios")
		     =>
		        (make-instance of TRABAJO (destino ?dest) (numero ?num))
			)


;Introducimos los viajes en la base de conocimiento
;==================================================================

;Declaración del tipo viaje...
(deftemplate VIAJES::viaje
	           (slot titulo (type STRING))
		         (slot descripcion (type STRING))
			       (slot tipo (type STRING))
			             (slot estilo (type STRING))
				           (slot precio-unitario (type INTEGER) (default 0))
					         (slot descuento-grupo (type INTEGER) (default 0))
						 )

;Lista de viajes disponibles...
(deffacts VIAJES::mis-viajes
	    ;viajes nacionales...
	      (viaje (titulo "Lanzarote")
		              (descripcion "Viaje a la Isla de Lanzarote, ...")
			               (tipo "nacional")
				                (estilo "paradisiaco")
						         (precio-unitario 100)
							          (descuento-grupo 0))

	        (viaje (titulo "Madrid")
		                (descripcion "Viaja a la Capital de españa ...")
				         (tipo "nacional")
					          (estilo "ciudad")
						           (precio-unitario 50)
							            (descuento-grupo 5))

		  (viaje (titulo "Andorra")
			          (descripcion "Aprovecha para esquiar este puente ...")
				           (tipo "nacional")
					            (estilo "montanya")
						             (precio-unitario 150)
							              (descuento-grupo 50))

		    (viaje (titulo "Palma de Mallorca")
			            (descripcion "Magnifica isla ...")
				             (tipo "nacional")
					              (estilo "paradisiaco")
						               (precio-unitario 130)
							                (descuento-grupo 0))

		      (viaje (titulo "Port Aventura")
			              (descripcion "¿Te atreveras a subir al dragon can?")
				               (tipo "nacional")
					                (estilo "parque-tematico")
							         (precio-unitario 80)
								          (descuento-grupo 10))

		        ;viajes internacionales...
			  (viaje (titulo "Disney Land")
				          (descripcion "Aquí tienen montanya rusa ...")
					           (tipo "internacional")
						            (estilo "parque-tematico")
							             (precio-unitario 500)
								              (descuento-grupo 100))
			  (viaje (titulo "Paris")
				          (descripcion "La ciudad del amor ...")
					           (tipo "internacional")
						            (estilo "ciudad")
							             (precio-unitario 400)
								              (descuento-grupo 30))

			    (viaje (titulo "Egipto")
				            (descripcion "Las mejores pirámides ...")
					             (tipo "internacional")
						              (estilo "cultural")
							               (precio-unitario 1000)
								                (descuento-grupo 0))

			      (viaje (titulo "Londres")
				              (descripcion "Nose porque, pero esta gente siempre ...")
					               (tipo "internacional")
						                (estilo "ciudad")
								         (precio-unitario 500)
									          (descuento-grupo 50))

			        (viaje (titulo "Islas del Caribe")
				                (descripcion "No vengas con pareja, no seas tonto ...")
						         (tipo "internacional")
							          (estilo "paradisiaco")
								           (precio-unitario 1500)
									            (descuento-grupo 0))
				)

;Procesamos las instancias para descartar viajes
;==================================================================
(defrule VIAJES::descartar-por-nacionalidad ""
	   (declare (salience 10))
	     (object (is-a CLIENTE)
		               (destino ?dest))
	       ?x <- (viaje (tipo "internacional"))
	         ?y <- (viaje (tipo "nacional"))
		   =>
		     (if (eq ?dest nacional) then
		          (retract ?x)
			     else
			        (retract ?y))
		     )

(defrule VIAJES::descartar-por-grupo ""
	   (declare (salience 10))
	     (object (is-a GRUPO)
		               (destino ?dest)
			                 (numero ?num)
					           (gustos ?gustos))
	       ?x <- (viaje (descuento-grupo 0))
	         =>
		   (retract ?x)
		   )

(defrule VIAJES::descartar-paradisiaco ""
	   (declare (salience 10))
	     (object (is-a CLIENTE)
		               (destino ?dest)
			                 (gustos ?gustos))
	       ?x <- (viaje (estilo "paradisiaco"))
	         =>
		   (if (or (eq ?gustos cultura) (eq ?gustos montanya)) then
		          (retract ?x)
			    )
		   )

(defrule VIAJES::descartar-ciudad ""
	   (declare (salience 10))
	     (object (is-a CLIENTE)
		               (destino ?dest)
			                 (gustos ?gustos))
	       ?x <- (viaje (estilo "ciudad"))
	         =>
		   (if (or (eq ?gustos playa) (eq ?gustos montanya)) then
		          (retract ?x)
			    )
		   )

(defrule VIAJES::descartar-montanya ""
	   (declare (salience 10))
	     (object (is-a CLIENTE)
		               (destino ?dest)
			                 (gustos ?gustos))
	       ?x <- (viaje (estilo "montanya"))
	         =>
		   (if (or (eq ?gustos playa) (eq ?gustos cultura)) then
		          (retract ?x)
			    )
		   )

(defrule VIAJES::descartar-parque-tematico ""
	   (declare (salience 10))
	     (object (is-a CLIENTE)
		               (edad "adulto"))
	       ?x <- (viaje (estilo "parque-tematico"))
	         =>
		   (retract ?x)
		   )

(defrule VIAJES::descartar-cultural ""
	   (declare (salience 10))
	     (object (is-a CLIENTE)
		               (gustos ?gustos))
	       ?x <- (viaje (estilo "cultural"))
	         =>
		   (if (or (eq ?gustos playa) (eq ?gustos montanya)) then
		          (retract ?x)
			    )
		   )

(defrule VIAJES::descartar-por-negocios ""
	   (declare (salience 10))
	     (object (is-a TRABAJO))
	       ?k <- (viaje (estilo "paradisiaco" |
				       "montanya" |
				         "ciudad" |
				"parque-tematico" |
				"playa" | "cultural"))
	         =>
		   (retract ?k)
		   )


;Visualizamos los viajes propuestos en caso de haberlos
;==================================================================

;No hay ningún viaje que se ajuste...
(defrule VIAJES::no-hay-viajes ""
	    (not (viaje (titulo ?titul)
			          (descripcion ?descrip)
				            (tipo ?tipe)
					              (estilo ?estil)
						                (precio-unitario ?preciou)
								          (descuento-grupo ?descuento)))
	       =>
	          (printout t crlf "No tenemos ningún viaje posible para las necesidades requeridas ..." crlf)
		  )


;Visualiza los posibles viajes (NO SON GRUPOS)...
(defrule VIAJES::ver-viajes-normal ""
	    (not (object (is-a GRUPO)
			           (numero ?num)))
	       (viaje (titulo ?titul)
		                (descripcion ?descrip)
				          (tipo ?tipe)
					            (estilo ?estil)
						              (precio-unitario ?preciou)
							                (descuento-grupo ?descuento))
	          =>
		     (printout t crlf "*************************************" crlf)
		        (printout t " Posible Viaje **********************" crlf)
			   (printout t "*************************************" crlf crlf)
			      (printout t "Titulo: " ?titul crlf)
			         (printout t "Tipo: " ?tipe crlf)
				    (printout t "Estilo: " ?estil crlf)
				       (printout t "Descripción: " crlf ?descrip crlf crlf)
				          (printout t "Precio/Viajero: " ?preciou "€" crlf)
					  )

;Visualiza los posibles viajes (GRUPOS)...
(defrule VIAJES::ver-viajes-grupo ""
	   (object (is-a GRUPO)
		             (numero ?num))
	      (viaje (titulo ?titul)
		               (descripcion ?descrip)
			                 (tipo ?tipe)
					           (estilo ?estil)
						             (precio-unitario ?preciou)
							               (descuento-grupo ?descuento))
	         =>
		    (printout t crlf "*************************************" crlf)
		       (printout t " Posible Viaje ESPECIAL GRUPO *******" crlf)
		          (printout t "*************************************" crlf crlf)
			     (printout t "Titulo: " ?titul crlf)
			        (printout t "Tipo: " ?tipe crlf)
				   (printout t "Estilo: " ?estil crlf)
				      (printout t "Descripción: " crlf ?descrip crlf crlf)
				         (printout t "Viajeros: " ?num crlf)
					    (printout t "Precio/Viajero: " ?preciou "€" crlf)

					       ;Calculo Precio viajero * viajeros - % descuento
					          (printout t "Precio Grupo: "  (- (* ?preciou ?num) (* (* ?preciou ?num) (/ ?descuento 100))) "€" crlf)
						     (printout t "Descuento para grupos: " ?descuento "%" crlf)
						     )


;Con esta regla empezará la ejecución
;==================================================================

(defrule MAIN::mensaje-inicial ""
	   (declare (salience 10))
	     =>
	       (printout t crlf crlf)
	         (printout t "Sistema Experto para la selección de Viajes" crlf)
		   (printout t "Por: Jose María Rodríguez Valls" crlf)
		     (printout t "*******************************************" crlf crlf)
		       (focus MAIN USUARIO VIAJES)
		       )

;PRUEBAS
;Descripción:

;Un grupo de viajeros desea realizar un viaje a un sitio donde obtenga algún descuento por grupo.

;Resultado:

CLIPS> (reset)
CLIPS> (run)

Sistema Experto para la selección de Viajes
Por: Jose María Rodríguez Valls
*******************************************************

Prefieres un viaje nacional o internacional (nacional/internacional)? nacional
Cuantas personas viajaran? 4
Que gustos tienes? (playa/montanya/cultura)? cultura

*************************************
Posible Viaje ESPECIAL GRUPO *******
*************************************

Titulo: Port Aventura
Tipo: nacional
Estilo: parque-tematico
Descripción:
¿Te atreveras a subir al dragon can?

Viajeros: 4
Precio/Viajero: 80€
Precio Grupo: 288.0€
Descuento para grupos: 10%

*************************************
Posible Viaje ESPECIAL GRUPO *******
*************************************

Titulo: Madrid
Tipo: nacional
Estilo: ciudad
Descripción:
Viaja a la Capital de españa para ver perder al Madrid ...

Viajeros: 4
Precio/Viajero: 50€
Precio Grupo: 190.0€
Descuento para grupos: 5%
CLIPS>

Como podemos observar en este caso los usuarios reciben dos posibilidades de viaje. Se han descartado aquellos viajes que no tienen descuento para grupos.

También se han descartado todos aquellos con destinos de ocio puesto que pretende ser un viaje cultural.

Funcionamiento:

Durante el proceso de preguntas se han creado los siguientes hechos dentro de nuestra base de conocimiento:

f-0     (initial-fact)
f-11    (destino nacional)
f-12    (num-viajeros 4)
f-13    (tipo "grupo")
f-14    (gustos cultura)


Posteriormente se han generado las instancias oportunas. En este caso solo tenemos una instancia porque se trata de un grupo.

[initial-object] of INITIAL-OBJECT
[gen2] of USUARIO::GRUPO (destino nacional) (numero 4) (gustos cultura)


Finalmente se han ido borrando los viajes inadecuados hasta quedar:

f-2     (viaje (titulo "Madrid")
	               (descripcion "Viaja a la Capital de españa ...")
		               (tipo "nacional") (estilo "ciudad") (precio-unitario 50)
			               (descuento-grupo 5))

f-5     (viaje (titulo "Port Aventura")
	               (descripcion "¿Te atreveras a subir al dragon can?")
		               (tipo "nacional") (estilo "parque-tematico") (precio-unitario 80)
			               (descuento-grupo 10))


Observaciones

En este caso el filtrado se ha hecho por los gustos del cliente y por el descuento de grupos. Considero que el proceso de instanciación se ha efectuado correctamente porque en este caso solo podría tratarse de un grupo.

Prueba 2

Descripción:

Un empresario necesita realizar un viaje a una ciudad determinada por negocios.

Resultado:

CLIPS> (reset)
CLIPS> (run)

Sistema Experto para la selección de Viajes
Por: Jose María Rodríguez Valls
*******************************************************

Prefieres un viaje nacional o internacional (nacional/internacional)? internacional
Cuantas personas viajaran? 1
Cual es tu edad? 23
Es un viaje de negocios (si/no)? si

No tenemos ningún viaje posible para las necesidades requeridas ...
CLIPS>


En este caso no hemos encontrado ningún viaje que sea de negocios.

Funcionamiento:

Durante el proceso de preguntas se han creado los siguientes hechos dentro de nuestra base de conocimiento:

f-0     (initial-fact)
f-11    (destino internacional)
f-12    (num-viajeros 1)
f-13    (tipo "individual")
f-14    (edad "adulto")
f-15    (por "negocios")


Posteriormente se han generado las instancias oportunas.

[initial-object] of INITIAL-OBJECT
[gen3] of USUARIO::TRABAJO (destino internacional) (numero 1)
[gen4] of USUARIO::INDIVIDUAL (destino internacional) (edad "adulto") (gustos "")


Finalmente se han ido borrando todos los viajes por los filtros oportunos.

Observaciones

En este caso se han creado dos instancias puesto que es un único viajero.
El cliente es un viajante individual y al mismo tiempo también es un viaje de trabajo.

Se ha visualizado el mensaje que índica al cliente que no tenemos viajes con dichas características.

Prueba 3

Descripción:

Una pareja desea hacer un viaje romántico por algún país exótico.

Resultado:

CLIPS> (reset)
CLIPS> (run)

Sistema Experto para la selección de Viajes
Por: Jose María Rodríguez Valls
**********************************************************

Prefieres un viaje nacional o internacional (nacional/internacional)? internacional
Cuantas personas viajaran? 2
Sois pareja (si/no)? si
Que gustos tienes? (playa/montanya/cultura)? playa

*************************************
Posible Viaje **********************
*************************************

Titulo: Islas del Caribe
Tipo: internacional
Estilo: paradisiaco
Descripción:
No vengas con pareja, no seas tonto ...

Precio/Viajero: 1500€

*************************************
Posible Viaje **********************
*************************************

Titulo: Disney Land
Tipo: internacional
Estilo: parque-tematico
Descripción:
Aquí tienen montanya rusa. Pero el clima no acompaña ...

Precio/Viajero: 500€
CLIPS>


Funcionamiento:

Durante el proceso de preguntas se han creado los siguientes hechos dentro de nuestra base de conocimiento:

f-0     (initial-fact)
f-11    (destino internacional)
f-12    (num-viajeros 2)
f-13    (tipo "duo")
f-14    (pareja "novios")
f-15    (gustos playa)


Posteriormente se han generado las instancias oportunas.

[initial-object] of INITIAL-OBJECT
[gen5] of USUARIO::DUO (destino internacional) (pareja "novios") (gustos playa)


Pasada la fase de filtrado nos han quedado los siguientes viajes:

f-6     (viaje (titulo "Disney Land")
	               (descripcion "Aquí tienen montanya rusa. Pero el clima...")
		               (tipo "internacional") (estilo "parque-tematico")
			               (precio-unitario 500) (descuento-grupo 100))

f-10    (viaje (titulo "Islas del Caribe") (descripcion "No vengas con pareja...")
	               (tipo "internacional") (estilo "paradisiaco")
		               (precio-unitario 1500) (descuento-grupo 0))


Observaciones

Podemos ver que en este caso nos aparecen dos viajes.
No descartamos la posibilidad de parque temático por si pretenden realizar un viaje en familia.

La instanciación se realiza mediante un DUO tipo novios.
Considero que el resultado puede darse por bueno.
Blog alojado en miarroba.com