diff options
Diffstat (limited to 'BatallaNaval.activity/BatallaNaval.py')
-rw-r--r-- | BatallaNaval.activity/BatallaNaval.py | 249 |
1 files changed, 133 insertions, 116 deletions
diff --git a/BatallaNaval.activity/BatallaNaval.py b/BatallaNaval.activity/BatallaNaval.py index ceee1ee..ea99a1d 100644 --- a/BatallaNaval.activity/BatallaNaval.py +++ b/BatallaNaval.activity/BatallaNaval.py @@ -7,14 +7,11 @@ import gtk import logging import random -try: - from sugar.activity.activity import Activity, ActivityToolbox -except: - pass - +# Permite definir un Log que filtra mensajes a la salida estándar dependiendo de nivel elegido. log = logging.getLogger('BatallaNaval') log.setLevel(logging.DEBUG) +# Diccionario que contiene el nombre y largo de cada barco lista_barcos = { "Portaaviones": 5, "Acorazado": 4, @@ -23,6 +20,8 @@ lista_barcos = { "Destructor": 2} class PanelPrincipal(gtk.HBox): + ''' Panel Principal es un Widget que contiene ambos tableros y se encarga + de crear los barcos en posiciones al azar para cada tablero ''' def __init__(self): gtk.HBox.__init__(self, True) @@ -34,23 +33,25 @@ class PanelPrincipal(gtk.HBox): barcos_propios = crear_barcos() for barco in barcos_propios: self.tablero1.agregar_barco(barco, True) - log.debug("barco:%s, %s (%s, %s)" % (barco.name, barco.orientacion, barco.pos[0], barco.pos[1])) + log.debug("barco:%s, %s (%s, %s)" % (barco.nombre, barco.orientacion, barco.pos[0], barco.pos[1])) log.debug("Barcos Enemigos") self.barcos_enemigos = crear_barcos() for barco in self.barcos_enemigos: self.tablero2.agregar_barco(barco, False) - log.debug("barco:%s, %s (%s, %s)" % (barco.name, barco.orientacion, barco.pos[0], barco.pos[1])) + log.debug("barco:%s, %s (%s, %s)" % (barco.nombre, barco.orientacion, barco.pos[0], barco.pos[1])) self.add(self.tablero1) self.add(self.tablero2) - self.show_all() - self.jugadas_enemigas = [] # Lleva un registro de las jugadas hechas por la computadora + + self.show_all() # Carga barcos remotos def cargar_barcos(self, barcos): + ''' Esta función es llamada cuando me conecto en red con otro usuario, recibo + una tupla con los datos de los barcos enemigos. ''' log.debug("Cargando barcos enemigos") self.tablero2.barcos = [] for dato in barcos: @@ -64,19 +65,25 @@ class PanelPrincipal(gtk.HBox): self.tablero2.agregar_barco(barco, False) log.debug("barco:%s, %s (%s, %s)" % (barco.name, barco.orientacion, barco.pos[0], barco.pos[1])) - # Cuando el enemigo juega sobre mi tablero, hago la judada y le respondo si fue tocado def jugada_red(self, x, y): + ''' Callback de colaboración para la señal Play. + Cuando el enemigo juega sobre mi tablero, reflejo la judada y le respondo si fue tocado ''' return self.tablero1.jugada(self.tablero1.filas[x-1][y-1]) - # Cuando yo mismo hice una jugada sobre el tablero enemigo, si hay colaboración actualizo según la respuesta def jugada_hecha(self, x, y): - # Si hay colaboración, indico al oponente la jugada que hice - if self.colaboracion: + ''' Cuando yo mismo hice una jugada sobre el tablero enemigo + si la actividad está compartida indico al oponente la jugada que hice, + en caso contrario me simulo una jugada random del enemigo. ''' + + # Si estoy compartiendo con alguien, indico al oponente la jugada que hice + if self.colaboracion and self.colaboracion.entered: + log.debug("Señalo jugada a los participantes") self.colaboracion.Play(x, y) return # Sinó, la computadora hace una jugada al azar sobre el tablero propio if len(self.jugadas_enemigas) == 100: + log.error("Alcanzó las 100 jugadas.") return ok = False @@ -86,14 +93,64 @@ class PanelPrincipal(gtk.HBox): ok = True self.jugadas_enemigas.append((x, y)) - self.tablero1.filas[x-1][y-1].clicked() + self.tablero1.jugada(self.tablero1.filas[x-1][y-1]) + +class Barco(gtk.Frame): + ''' Esta clase representa un barco, tiene nombre, largo, orientación y posición. + Como es un widget puede ser mostrado en la pantalla y tiene un texto con el nombre del barco ''' + horizontal = 'H' + vertical = 'V' + + def __init__(self, nombre, largo, pos, orientacion = horizontal): + gtk.Frame.__init__(self) + self.nombre = nombre + self.largo = largo + self.pos = pos + + # Agrega una etiqueta con el nombre del barco + self.label = gtk.Label(nombre) + self.add(self.label) + + self.set_orientacion(orientacion) # Graba la orientación y ajusta la etiqueta a dicha orientación. + + def set_orientacion(self, orientacion): + ''' Graba la orientación y ajusta la etiqueta a dicha orientación. ''' + self.orientacion = orientacion + if self.orientacion == Barco.horizontal: + self.label.set_angle(0) + else: + self.label.set_angle(90) + + def get_inicio(self): + return self.pos + + def get_fin(self): + if self.orientacion == Barco.horizontal: + return self.pos[0], self.pos[1] + self.largo - 1 + else: + return self.pos[0] + self.largo - 1, self.pos[1] + + def get_filas(self): + return range(self.get_inicio()[0], self.get_fin()[0]+1) + + def get_cols(self): + return range(self.get_inicio()[1], self.get_fin()[1]+1) + + def get_celdas(self): + return [(f, c) for f in self.get_filas() for c in self.get_cols()] + def crear_barcos(): - barcos = [Barco(b[0], b[1], None) for b in lista_barcos.items()] + ''' Parte de la lista_barcos para crear un conjunto de barcos en posiciones al azar. ''' - celdas_ocupadas = [] + # Convierte la lista de definición en una lista de objetos Barco sin posición definida + barcos = [Barco(nombre, largo, None) for nombre, largo in lista_barcos.items()] + + celdas_ocupadas = [] # Llevo una lista de las celdas ya ocupadas por los barcos for barco in barcos: + # Para cada barco me mantengo en un loop hasta que encuentre coordenadas al azar que no + # intersecten con ningún barco ya ubicado. ok = False while not ok: # Determino al azar si es horizontal o vertical @@ -107,41 +164,46 @@ def crear_barcos(): barco.set_orientacion(Barco.vertical) posx = random.randint(1, 10-barco.largo+1) posy = random.randint(1, 10) - barco.pos = (posx, posy) - ok = True - for celda in barco.get_celdas(): - if celda in celdas_ocupadas: - ok = False - if ok: + + # Verifico si la posición elegida no intersecciona con las celdas ya ocupadas por otros barcos + # Convierto las listas en sets y aplico intersección + interseccion = set(barco.get_celdas()) & set(celdas_ocupadas) + if not interseccion: + ok = True celdas_ocupadas.extend(barco.get_celdas()) return barcos class Celda(gtk.Button): - - def __init__(self, pos, clicked_cb): + ''' Esta clase representa una celda del tablero, como es subclase de button se le puede + conectar la señal "clicked". + También presenta funciones para ocultar y colorearse dependiendo de si tocó un barco o dió agua. ''' + + def __init__(self, pos): gtk.Button.__init__(self) self.pos = pos - - self.connect("clicked", clicked_cb) def ocultar(self): + ''' Oculta permanentemente la celda, de modo que show_all no la muestre ''' self.set_no_show_all(True) self.hide() - def tocado(self): - color = gtk.gdk.Color(65535,65535/2,0) + def colorear(self, color): self.modify_bg(gtk.STATE_NORMAL, color) self.modify_bg(gtk.STATE_PRELIGHT, color) self.show() # Por si está oculta atrás de un barco + + def tocado(self): + self.colorear( gtk.gdk.Color(65535,65535/2,0) ) def agua(self): - color = gtk.gdk.Color(0,0,65535/2) - self.modify_bg(gtk.STATE_NORMAL, color) - self.modify_bg(gtk.STATE_PRELIGHT, color) - self.show() # Por si está oculta atrás de un barco - + self.colorear( gtk.gdk.Color(0,0,65535/2) ) + + class Tablero(gtk.Frame): + ''' Define un tablero, el tablero está definido con una tabla exterior que permite poner + los títulos de las filas y las columnas (que a su vez son tablas) y una tabla interior + que tiene todas las celdas del tablero (tabla_celdas). ''' def __init__(self, llamada_jugada_hecha): gtk.Frame.__init__(self) @@ -159,62 +221,65 @@ class Tablero(gtk.Frame): label = gtk.Label(char) tabla_letras.attach(label, 0, 1, i-1, i) - # Se hace una tabla para ubicar las letras, los números y el tablero + # Se hace una tabla para ubicar las letras, los números y la tabla de celdas self.tabla = gtk.Table(2, 2, False) self.add(self.tabla) + # Opciones para las tablas de letras y números. opciones = gtk.SHRINK|gtk.FILL - label = gtk.Label(" ") + label = gtk.Label(" ") # Para dar más espacio a la columna de letras self.tabla.attach(label, 0, 1, 0, 1, xoptions=opciones, yoptions=opciones) - self.tabla.attach(tabla_numeros, 1, 2, 0, 1, xoptions=opciones, yoptions=opciones) self.tabla.attach(tabla_letras, 0, 1, 1, 2, xoptions=opciones, yoptions=opciones) - self.filas = [] - # El tablero es otra tabla self.tabla_celdas = gtk.Table(10, 10, True) self.tabla.attach(self.tabla_celdas, 1, 2, 1, 2, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL|gtk.EXPAND) - + + self.filas = [] + # Creo todas las celdas, las guardo en la colección de filas y las adjunto al tablero for i in range(1, 11): - row = [] + fila = [] for j in range(1, 11): - left = j - 1 - top = i - 1 - celda = Celda((i, j), self.celda_clickeada) - row.append(celda) - self.tabla_celdas.attach(celda, left, j, top, i) - #print label.get_text() - self.filas.append(row) + celda = Celda((i, j)) + celda.connect("clicked", self.celda_clickeada) + fila.append(celda) + self.tabla_celdas.attach(celda, j-1, j, i-1, i) + self.filas.append(fila) - self.barcos = [] # Los barcos que hay en el tablero + # Los barcos que hay en el tablero + self.barcos = [] + # Callback para cuando el jugador hace una jugada en el tablero self.llamada_jugada_hecha = llamada_jugada_hecha - def agregar_barco(self, barco, show): + def agregar_barco(self, barco, mostrar): + ''' Agrega un barco al tablero, si mostrar=True sustituye las celdas que ocupa por el barco. ''' self.barcos.append(barco) - if show: - for i in barco.get_filas(): - for j in barco.get_cols(): - self.ocultar_celda(i, j) - izq = barco.get_inicio()[1]-1 - der = barco.get_fin()[1] - arr = barco.get_inicio()[0]-1 - aba = barco.get_fin()[0] - self.tabla_celdas.attach(barco, izq, der, arr, aba) - + if mostrar: + # Oculta las celdas que ocupa el barco + for i, j in barco.get_celdas(): + self.ocultar_celda(i, j) + + # Obtiene los extremos del barco y lo adjunta a la tabla + arr, izq = barco.get_inicio() + aba, der = barco.get_fin() + self.tabla_celdas.attach(barco, izq-1, der, arr-1, aba) + def ocultar_celda(self, i, j): self.filas[i-1][j-1].ocultar() def celda_clickeada(self, celda): - # Cuando hay definido un callback de jugadas hechas, significa que es el tablero en el que puedo jugar + ''' Cuando hay definido un callback de jugadas hechas, significa que en este tablero puedo jugar. + Realizo la jugada y notifico al callback ''' if self.llamada_jugada_hecha: # Este es el callback para cuando clickean una celda self.jugada(celda) self.llamada_jugada_hecha(celda.pos[0], celda.pos[1]) def jugada(self, celda): + ''' verifica si la jugada da en algún barco o en el agua y manda la señal correcta a la celda ''' tocado = False for barco in self.barcos: if celda.pos in barco.get_celdas(): @@ -222,59 +287,16 @@ class Tablero(gtk.Frame): celda.tocado() if not tocado: celda.agua() - + + log.debug("Pos:%s Tocado:%s", str(celda.pos), tocado) return tocado -class Barco(gtk.Frame): - - horizontal = 'H' - vertical = 'V' - - def __init__(self, nombre, largo, pos, orientacion = horizontal): - #gtk.Label.__init__(self, nombre) - gtk.Frame.__init__(self) - self.nombre = nombre - self.largo = largo - self.pos = pos - self.label = gtk.Label(nombre) - self.add(self.label) - self.set_orientacion(orientacion) - - def set_orientacion(self, orientacion): - self.orientacion = orientacion - if self.orientacion == Barco.horizontal: - self.label.set_angle(0) - else: - self.label.set_angle(90) - - def get_inicio(self): - return self.pos - - def get_fin(self): - if self.orientacion == Barco.horizontal: - return self.pos[0], self.pos[1] + self.largo - 1 - else: - return self.pos[0] + self.largo - 1, self.pos[1] - - def get_filas(self): - return range(self.get_inicio()[0], self.get_fin()[0]+1) - - def get_cols(self): - return range(self.get_inicio()[1], self.get_fin()[1]+1) - - def get_celdas(self): - return [(f, c) for f in self.get_filas() for c in self.get_cols()] - -# Esta función es el punto de entrada común para sugar y modo standalone -# standalone es un boolean que indica si es Standalone o se ejecuta desde Sugar def init(standalone, ventana_principal): - + ''' Esta función es el punto de entrada común para sugar y modo standalone + standalone es un boolean que indica si es Standalone o se ejecuta desde Sugar ''' panel_principal = PanelPrincipal() if not standalone: - toolbox = ActivityToolbox(ventana_principal) - ventana_principal.set_toolbox(toolbox) - toolbox.show() ventana_principal.set_canvas(panel_principal) # Colaboración @@ -285,24 +307,19 @@ def init(standalone, ventana_principal): panel_principal.cargar_barcos, # World panel_principal.jugada_red, # Play panel_principal.tablero1.barcos)# Mis barcos - else: ventana_principal.add(panel_principal) panel_principal.colaboracion = None - + ventana_principal.set_title("Batalla Naval - ceibalJAM") ventana_principal.connect("destroy", lambda wid: gtk.main_quit()) ventana_principal.connect("delete_event", lambda a1, a2: gtk.main_quit()) ventana_principal.show() -# Este es el procedimiento principal en modo standalone -def main(): +# Este código se ejecuta sólo cuando se ejecuta directo ese módulo (no cuando se importa desde sugar) +if __name__ == "__main__": + log.addHandler(logging.StreamHandler()) ventana_principal = gtk.Window(gtk.WINDOW_TOPLEVEL) init(True, ventana_principal) gtk.main() - return 0 - -# Este código se ejecuta sólo cuando se ejecuta directo ese módulo (no cuando se importa desde sugar) -if __name__ == "__main__": - main() |