19 |
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 |
# |
# |
21 |
# |
# |
22 |
#Fichero: visita.py |
#Fichero: visita.py |
23 |
|
|
24 |
"""Clase Visita.""" |
"""Clase Visita.""" |
25 |
|
|
26 |
from xml.dom import minidom |
from xml.dom import minidom |
27 |
import random |
import random |
28 |
import sys |
import sys |
29 |
import string |
import string |
30 |
import os |
import os |
31 |
|
|
32 |
from bloquedialogo import * |
from bloquedialogo import * |
33 |
from tema import * |
from tema import * |
34 |
from contexto import * |
from contexto import * |
35 |
from conversacion import * |
from conversacion import * |
36 |
|
|
37 |
class Visita: |
class Visita: |
38 |
"""Control de dialogos de los personajes no jugadores. |
"""Control de dialogos de los personajes no jugadores. |
39 |
|
|
40 |
Representa el estado de la conversación entre el PNJ y un grupo de PJs. |
Representa el estado de la conversación entre el PNJ y un grupo de PJs. |
41 |
Contiene los siguientes atributos: |
Contiene los siguientes atributos: |
42 |
- _conversacion: objeto de conversacion utilizado durante la visita. |
- _conversacion: objeto de conversacion utilizado durante la visita. |
43 |
- _items: secuencia de textos visitados durante el dialogo |
- _items: secuencia de textos visitados durante el dialogo |
44 |
- _items_especiales: secuencia de textos especiales visitados durante el dialogo |
- _items_especiales: secuencia de textos especiales visitados durante el dialogo |
45 |
- _hitos: diccionario de variables definidas durante la conversacion |
- _hitos: diccionario de variables definidas durante la conversacion |
46 |
|
|
47 |
- _estado: tipo de mensaje devuelto en la última pregunta. |
- _estado: tipo de mensaje devuelto en la última pregunta. |
48 |
- pnj: personaje que posee la conversacion |
- pnj: personaje que posee la conversacion |
49 |
- pj_activo: personaje que realizó la última intervención en el diálogo |
- pj_activo: personaje que realizó la última intervención en el diálogo |
50 |
- participantes: diccionario con los personajes jugadores que han escuchado la conversación, y la lista de temas que han oido |
- participantes: diccionario con los personajes jugadores que han escuchado la conversación, y la lista de temas que han oido |
51 |
- _comando: último comando solicitado. |
- _comando: último comando solicitado. |
52 |
- _nombretema: último tema por el que se preguntó (independientemente de que se haya encontrado o no) |
- _nombretema: último tema por el que se preguntó (independientemente de que se haya encontrado o no) |
53 |
- _temas_especiales: diccionario con las lista de temas especiales por defecto en este diálogo. |
- _temas_especiales: diccionario con las lista de temas especiales por defecto en este diálogo. |
54 |
|
|
55 |
self.activo <-> hay un item de texto activo |
self.activo <-> hay un item de texto activo |
56 |
""" |
""" |
57 |
|
|
58 |
ENCONTRADO = "encontrado" |
ENCONTRADO = "encontrado" |
59 |
BUSCANDO = "busqueda" |
BUSCANDO = "busqueda" |
61 |
NO_ENCONTRADO = "no encontrado" |
NO_ENCONTRADO = "no encontrado" |
62 |
OFFTOPIC = "offtopic" |
OFFTOPIC = "offtopic" |
63 |
|
|
64 |
def __init__(self, conversacion, personajenojugador, temasEspeciales = None): |
def __init__(self, conversacion, personajenojugador, temasEspeciales = None): |
65 |
"""Crea un controlador de dialogo para la conversacion a partir de un fichero de descripción de conversacion. |
"""Crea un controlador de dialogo para la conversacion a partir de un fichero de descripción de conversacion. |
66 |
|
|
67 |
conversacion: jerarquía de conversación cargada en memoria |
conversacion: jerarquía de conversación cargada en memoria |
68 |
personajenojugador: objeto PNJ que responde durante la conversación |
personajenojugador: objeto PNJ que responde durante la conversación |
69 |
temasEspeciales: respuestas por defecto en caso de fallo |
temasEspeciales: respuestas por defecto en caso de fallo |
70 |
|
|
71 |
Requiere: |
Requiere: |
72 |
id != None |
id != None |
73 |
personajenojugador != None |
personajenojugador != None |
74 |
""" |
""" |
75 |
|
|
76 |
self._estado = Visita.BUSCANDO |
self._estado = Visita.BUSCANDO |
77 |
self.pnj = personajenojugador |
self.pnj = personajenojugador |
78 |
self.pj_activo = None |
self.pj_activo = None |
79 |
self.activo = None |
self.activo = None |
80 |
self._conversacion = conversacion |
self._conversacion = conversacion |
81 |
self._items = [] |
self._items = [] |
82 |
self._items_especiales = [] |
self._items_especiales = [] |
83 |
self._hitos = {} |
self._hitos = {} |
84 |
self._comando = None |
self._comando = None |
85 |
if temasEspeciales == None: |
if temasEspeciales == None: |
86 |
temasEspeciales = temasEspecialesDefault |
temasEspeciales = temasEspecialesDefault |
87 |
self._temas_especiales = temasEspeciales |
self._temas_especiales = temasEspeciales |
88 |
|
|
89 |
def asignar_hito(self, nombre, valor="definido"): |
def asignar_hito(self, nombre, valor="definido"): |
90 |
escribir ("Definido hito " + str(nombre) + "=" + str(valor)) |
escribir ("Definido hito " + str(nombre) + "=" + str(valor)) |
103 |
self._items.insert(0, items["tema"]) |
self._items.insert(0, items["tema"]) |
104 |
|
|
105 |
def buscar_items(self, tema): |
def buscar_items(self, tema): |
106 |
"""Determina si el tema encontrado tiene algún item presentable. Devuelve el item correspondiente. |
"""Determina si el tema encontrado tiene algún item presentable. Devuelve el item correspondiente. |
107 |
|
|
108 |
Un item es presentable si no ha sido ya utilizado y se cumplen sus requisitos. |
Un item es presentable si no ha sido ya utilizado y se cumplen sus requisitos. |
109 |
Si el tema tiene algún item presentable, lo devuelve. |
Si el tema tiene algún item presentable, lo devuelve. |
110 |
Si no hay ningún item presentable y el tema no se ha usado ninguna vez, es un tema nulo. |
Si no hay ningún item presentable y el tema no se ha usado ninguna vez, es un tema nulo. |
111 |
Si no hay items presentables y el tema ya se ha usado alguna vez, devuelve un item de tema agotado. |
Si no hay items presentables y el tema ya se ha usado alguna vez, devuelve un item de tema agotado. |
112 |
|
|
113 |
Requiere: |
Requiere: |
114 |
tema != None |
tema != None |
115 |
comando != None |
comando != None |
116 |
|
|
117 |
Asegura: |
Asegura: |
118 |
tema nulo <-> return == None |
tema nulo <-> return == None |
119 |
tema activado <-> self._items := self._items.insert(0, item) y return == item |
tema activado <-> self._items := self._items.insert(0, item) y return == item |
120 |
tema agotado <-> return == itemAgotado |
tema agotado <-> return == itemAgotado |
121 |
""" |
""" |
122 |
|
|
123 |
itemEncontrado = None |
itemEncontrado = None |
124 |
# Busca el primer item que cumpla los requisitos y no haya sido ya devuelto en esta conversación. |
# Busca el primer item que cumpla los requisitos y no haya sido ya devuelto en esta conversación. |
125 |
for item in tema.items(): |
for item in tema.items(): |
126 |
if item.satisfecho_en(self) and (item._comando == "ambos" or self.comando() == item._comando) and not self._item_gastado(item): |
if item.satisfecho_en(self) and (item._comando == "ambos" or self.comando() == item._comando) and not self._item_gastado(item): |
127 |
itemEncontrado = item |
itemEncontrado = item |
128 |
self.estado_encontrado() |
self.estado_encontrado() |
129 |
return itemEncontrado |
return itemEncontrado |
130 |
|
|
131 |
# -- Tema sin items activos -- |
# -- Tema sin items activos -- |
132 |
# Si el tema no está en ningún item de self._items, es un tema nulo |
# Si el tema no está en ningún item de self._items, es un tema nulo |
133 |
if not filter(lambda item: item.tema() == tema, self._items): # tema in map(lambda item: item.tema(), self._items): |
if not filter(lambda item: item.tema() == tema, self._items): # tema in map(lambda item: item.tema(), self._items): |
134 |
return None |
return None |
135 |
# Tema agotado |
# Tema agotado |
136 |
self.estado_agotado() |
self.estado_agotado() |
137 |
# Busca item de tema agotado en el contexto del tema |
# Busca item de tema agotado en el contexto del tema |
138 |
return self.item_tema_especial([tema.contexto(), tema.contexto().conversacion(), self], "temaAgotado") |
return self.item_tema_especial([tema.contexto(), tema.contexto().conversacion(), self], "temaAgotado") |
139 |
|
|
140 |
def _item_gastado(self, item): |
def _item_gastado(self, item): |
141 |
"""Indica si el item está repetido.""" |
"""Indica si el item está repetido.""" |
168 |
# Error: debería haber encontrado el tema especial |
# Error: debería haber encontrado el tema especial |
169 |
raise ValueError("Tema especial de tipo " + tipo + " no ha encontrado items." ) |
raise ValueError("Tema especial de tipo " + tipo + " no ha encontrado items." ) |
170 |
return None |
return None |
171 |
|
|
172 |
def _item_tema_especial(self, objeto, tipo): |
def _item_tema_especial(self, objeto, tipo): |
173 |
"""Encuentra un item satisfecho. Si no hay ninguno o el elegido está gastado, devuelve None""" |
"""Encuentra un item satisfecho. Si no hay ninguno o el elegido está gastado, devuelve None""" |
174 |
# Control de error en el objeto pasado como parámetro |
# Control de error en el objeto pasado como parámetro |
175 |
# if not objeto.__dict__.has_key("_temas_especiales"): |
# if not objeto.__dict__.has_key("_temas_especiales"): |
176 |
# return none |
# return none |
177 |
|
|
178 |
temas = objeto.temas_especiales()[tipo] |
temas = objeto.temas_especiales()[tipo] |
179 |
for tema in temas: |
for tema in temas: |
180 |
items = filter (lambda i: i.satisfecho_en(self), tema.items()) |
items = filter (lambda i: i.satisfecho_en(self), tema.items()) |
181 |
if items: |
if items: |
182 |
item = random.choice(items) |
item = random.choice(items) |
183 |
#Item especial gastado? |
#Item especial gastado? |
184 |
if not item in self._items_especiales: |
if not item in self._items_especiales: |
187 |
if self._item_repeticiones(item, self._items_especiales)!= 0: |
if self._item_repeticiones(item, self._items_especiales)!= 0: |
188 |
# Quedan repeticiones por gastar |
# Quedan repeticiones por gastar |
189 |
return item |
return item |
190 |
return None |
return None |
191 |
|
|
192 |
def temas_especiales(self): |
def temas_especiales(self): |
193 |
return self._temas_especiales |
return self._temas_especiales |
194 |
|
|
195 |
|
|
196 |
def responder(self, personaje, comando, nombretema): |
def responder(self, personaje, comando, nombretema): |
197 |
"""Encuentra los items que debe responder el PNJ para responder al tema preguntado""" |
"""Encuentra los items que debe responder el PNJ para responder al tema preguntado""" |
198 |
|
|
199 |
nuevoItem = None |
nuevoItem = None |
200 |
resultado = {"especiales":[],"tema":None} |
resultado = {"especiales":[],"tema":None} |
201 |
self.activo = "si" |
self.activo = "si" |
202 |
self.pj_activo = personaje |
self.pj_activo = personaje |
203 |
self._nombretema = nombretema |
self._nombretema = nombretema |
204 |
self.estado_buscando() |
self.estado_buscando() |
205 |
if not self._conversacion._sinonimos.has_key(nombretema): |
if not self._conversacion._sinonimos.has_key(nombretema): |
206 |
# Tema no encontrado |
# Tema no encontrado |
207 |
self.estado_no_encontrado() |
self.estado_no_encontrado() |
208 |
if len(self._items)>0: |
if len(self._items)>0: |
209 |
objetos = [self._items[0].tema().contexto()] |
objetos = [self._items[0].tema().contexto()] |
210 |
else: |
else: |
211 |
objetos = [] |
objetos = [] |
212 |
objetos.append(self._conversacion) |
objetos.append(self._conversacion) |
213 |
objetos.append(self) |
objetos.append(self) |
214 |
# Busca item de tema no encontrado en el último contexto visitado |
# Busca item de tema no encontrado en el último contexto visitado |
215 |
resultado["especiales"] = [self.item_tema_especial(objetos, "temaNoEncontrado")] |
resultado["especiales"] = [self.item_tema_especial(objetos, "temaNoEncontrado")] |
216 |
|
|
217 |
if self.estado() == Visita.BUSCANDO: |
if self.estado() == Visita.BUSCANDO: |
218 |
nombretema = self._conversacion._sinonimos[nombretema] |
nombretema = self._conversacion._sinonimos[nombretema] |
219 |
self.pj_activo = personaje |
self.pj_activo = personaje |
220 |
self._comando = comando |
self._comando = comando |
221 |
|
|
222 |
# Busca el tema en cada uno de los items pasados |
# Busca el tema en cada uno de los items pasados |
223 |
for i in range(len(self._items)): |
for i in range(len(self._items)): |
224 |
item = self._items[i] |
item = self._items[i] |
225 |
# Busca si el tema está repetido |
# Busca si el tema está repetido |
226 |
if item.tema().nombre() == nombretema: |
if item.tema().nombre() == nombretema: |
227 |
nuevoItem = self.buscar_items(item.tema()) |
nuevoItem = self.buscar_items(item.tema()) |
228 |
if self.estado() == Visita.AGOTADO: resultado["especiales"] = [nuevoItem] |
if self.estado() == Visita.AGOTADO: resultado["especiales"] = [nuevoItem] |
229 |
if self.estado() != Visita.BUSCANDO: break |
if self.estado() != Visita.BUSCANDO: break |
230 |
# Busca en los temas relacionados del item |
# Busca en los temas relacionados del item |
231 |
nombrecontexto_relacionado = item.relacionado(nombretema) |
nombrecontexto_relacionado = item.relacionado(nombretema) |
232 |
if nombrecontexto_relacionado: |
if nombrecontexto_relacionado: |
233 |
nuevoItem = self.buscar_items(self._conversacion.contexto(nombrecontexto_relacionado).tema(nombretema)) |
nuevoItem = self.buscar_items(self._conversacion.contexto(nombrecontexto_relacionado).tema(nombretema)) |
234 |
if self.estado() == Visita.AGOTADO: resultado["especiales"] = [nuevoItem] |
if self.estado() == Visita.AGOTADO: resultado["especiales"] = [nuevoItem] |
235 |
if self.estado() != Visita.BUSCANDO: break |
if self.estado() != Visita.BUSCANDO: break |
236 |
# Busca en el mismo contexto del item, si el contexto está dentro de la tolerancia a offtopics |
# Busca en el mismo contexto del item, si el contexto está dentro de la tolerancia a offtopics |
237 |
if i < self._conversacion.tolerancia(): |
if i < self._conversacion.tolerancia(): |
238 |
tema = item.tema().contexto().tema(nombretema) |
tema = item.tema().contexto().tema(nombretema) |
239 |
if tema: |
if tema: |
240 |
nuevoItem = self.buscar_items(tema) |
nuevoItem = self.buscar_items(tema) |
241 |
if self.estado() == Visita.AGOTADO: resultado["especiales"] = [nuevoItem] |
if self.estado() == Visita.AGOTADO: resultado["especiales"] = [nuevoItem] |
242 |
if self.estado() != Visita.BUSCANDO: break |
if self.estado() != Visita.BUSCANDO: break |
243 |
|
|
246 |
for contexto in self._conversacion.contextos(): |
for contexto in self._conversacion.contextos(): |
247 |
tema = contexto.tema(nombretema) |
tema = contexto.tema(nombretema) |
248 |
if tema: |
if tema: |
249 |
nuevoItem = self.buscar_items(tema) |
nuevoItem = self.buscar_items(tema) |
250 |
if self.estado() == Visita.AGOTADO: |
if self.estado() == Visita.AGOTADO: |
251 |
resultado["especiales"] = [nuevoItem] |
resultado["especiales"] = [nuevoItem] |
252 |
break |
break |
253 |
# Comprueba si el tema encontrado está offtopic |
# Comprueba si el tema encontrado está offtopic |
254 |
if self.estado() == Visita.ENCONTRADO: |
if self.estado() == Visita.ENCONTRADO: |
255 |
if tema.contexto() not in self._conversacion._contextos_iniciales: |
if tema.contexto() not in self._conversacion._contextos_iniciales: |
256 |
# Tema offtopic |
# Tema offtopic |
257 |
self.estado_offtopic() |
self.estado_offtopic() |
258 |
if len(self._items)>0: |
if len(self._items)>0: |
259 |
objetos = [self._items[0].tema().contexto()] |
objetos = [self._items[0].tema().contexto()] |
260 |
else: |
else: |
261 |
objetos = [] |
objetos = [] |
262 |
objetos.append(self._conversacion) |
objetos.append(self._conversacion) |
263 |
objetos.append(self) |
objetos.append(self) |
264 |
# Busca item de tema offtopic en el último contexto visitado |
# Busca item de tema offtopic en el último contexto visitado |
265 |
resultado["especiales"] = [self.item_tema_especial(objetos, "temaOfftopic")] |
resultado["especiales"] = [self.item_tema_especial(objetos, "temaOfftopic")] |
276 |
elif self.estado() == Visita.BUSCANDO: |
elif self.estado() == Visita.BUSCANDO: |
277 |
# Tema no encontrado |
# Tema no encontrado |
278 |
self.estado_no_encontrado() |
self.estado_no_encontrado() |
279 |
if len(self._items)>0: |
if len(self._items)>0: |
280 |
objetos = [self._items[0].tema().contexto()] |
objetos = [self._items[0].tema().contexto()] |
281 |
else: |
else: |
282 |
objetos = [] |
objetos = [] |
283 |
objetos.append(self._conversacion) |
objetos.append(self._conversacion) |
284 |
objetos.append(self) |
objetos.append(self) |
285 |
# Busca item de tema offtopic en el último contexto visitado |
# Busca item de tema offtopic en el último contexto visitado |
286 |
resultado["especiales"] = [self.item_tema_especial(objetos, "temaNoEncontrado")] |
resultado["especiales"] = [self.item_tema_especial(objetos, "temaNoEncontrado")] |
287 |
return resultado |
return resultado |
288 |
|
|
289 |
def comando(self): |
def comando(self): |
290 |
return self._comando |
return self._comando |
291 |
|
|
307 |
def estado_no_encontrado(self): |
def estado_no_encontrado(self): |
308 |
self._estado = Visita.NO_ENCONTRADO |
self._estado = Visita.NO_ENCONTRADO |
309 |
|
|
310 |
def items(self): |
def items(self): |
311 |
return self._items |
return self._items |
312 |
|
|
313 |
# Inicializa las respuestas por defecto |
# Inicializa las respuestas por defecto |
314 |
archivo = os.path.join(BloqueDialogo.DIR_DESC_DIALOGOS, "general", "general-texto_por_defecto" + BloqueDialogo.EXT_DESC_DIALOGOS) |
archivo = os.path.join(BloqueDialogo.DIR_DESC_DIALOGOS, "general", "general-texto_por_defecto" + BloqueDialogo.EXT_DESC_DIALOGOS) |
315 |
#print archivo |
#print archivo |
316 |
stream = open(archivo) |
stream = open(archivo) |
317 |
doc_xml = minidom.parse(stream).documentElement |
doc_xml = minidom.parse(stream).documentElement |
318 |
stream.close() |
stream.close() |
319 |
|
|
320 |
#temasEspecialesDefault: lista de respuestas por defecto |
#temasEspecialesDefault: lista de respuestas por defecto |
321 |
temasEspecialesDefault = parserXMLconversacion._parsear_temas_especiales(doc_xml) |
temasEspecialesDefault = parserXMLconversacion._parsear_temas_especiales(doc_xml) |
322 |
|
|
323 |
|
|
324 |
# Código para pruebas unitarias del módulo. |
# Código para pruebas unitarias del módulo. |
325 |
|
|
326 |
def test(): |
def test(): |
327 |
conversacion = Conversacion("tutora") |
conversacion = Conversacion("tutora") |
328 |
c = Visita(conversacion, None) |
c = Visita(conversacion, None) |
329 |
while (1): |
while (1): |
330 |
texto = raw_input("? +preguntar por ") |
texto = raw_input("? +preguntar por ") |
331 |
respuesta = c.responder(None, None, texto.encode('ISO-8859-1')) |
respuesta = c.responder(None, None, texto.encode('ISO-8859-1')) |
332 |
for item in respuesta: |
for item in respuesta: |
333 |
print str(item) |
print str(item) |
334 |
|
|
335 |
if (__name__ == '__main__'): |
if (__name__ == '__main__'): |
336 |
test() |
test() |