Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/BatallaNaval.activity/BatallaNaval.py
blob: ea99a1de304916a68747d2af71e6e6c66efe6d7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
#!/usr/bin/env python
# -*- coding: cp1252 -*-

import pygtk
pygtk.require('2.0')
import gtk
import logging
import random

# 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,
    "Crucero": 3,
    "Submarino": 3,
    "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)
        
        self.tablero1 = Tablero(None)               # tablero propio  
        self.tablero2 = Tablero(self.jugada_hecha)  # tablero enemigo

        log.debug("Barcos Propios")
        barcos_propios = crear_barcos()
        for barco in barcos_propios:
            self.tablero1.agregar_barco(barco, True)
            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.nombre, barco.orientacion, barco.pos[0], barco.pos[1]))

        self.add(self.tablero1)
        self.add(self.tablero2)
        
        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:
            log.debug("Dato: %s" % str(dato))
            nombre = dato[0]
            orientacion = dato[1]
            largo = dato[2]
            pos = dato[3:5]
            
            barco = Barco(nombre, largo, pos, orientacion)
            self.tablero2.agregar_barco(barco, False)
            log.debug("barco:%s, %s (%s, %s)" % (barco.name, barco.orientacion, barco.pos[0], barco.pos[1]))
    
    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])
    
    def jugada_hecha(self, x, y):
        ''' 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
        while not ok:
            x, y = random.randint(1, 10), random.randint(1, 10)
            if not (x, y) in self.jugadas_enemigas:
                ok = True
        
        self.jugadas_enemigas.append((x, y))
        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():
    ''' Parte de la lista_barcos para crear un conjunto de barcos en posiciones al azar. '''
    
    # 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
            if random.randint(0, 1):
                # Calculo coordenadas random - horizontal
                barco.set_orientacion(Barco.horizontal)
                posx = random.randint(1, 10)
                posy = random.randint(1, 10-barco.largo+1)
            else:
                # Calculo coordenadas random - vertical
                barco.set_orientacion(Barco.vertical)
                posx = random.randint(1, 10-barco.largo+1)
                posy = random.randint(1, 10)
            barco.pos = (posx, posy)
            
            # 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):
    ''' 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
    
    def ocultar(self):
        ''' Oculta permanentemente la celda, de modo que show_all no la muestre '''
        self.set_no_show_all(True)
        self.hide()
    
    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):
        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)

        # Números
        tabla_numeros = gtk.Table(1, 10, True)
        for i in range(1, 11):
            label = gtk.Label(str(i))
            tabla_numeros.attach(label, i-1, i, 0, 1)
        
        # Letras
        tabla_letras = gtk.Table(10, 1, True)
        for i in range(1, 11):
            char = chr( ord('A') + i - 1 )
            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 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("      ") # 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)
        
        # 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):
            fila = []
            for j in range(1, 11):
                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)
        
        # 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, mostrar):
        ''' Agrega un barco al tablero, si mostrar=True sustituye las celdas que ocupa por el barco. '''
        self.barcos.append(barco)
        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 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():
                tocado = True
                celda.tocado()
        if not tocado:
            celda.agua()
            
        log.debug("Pos:%s Tocado:%s", str(celda.pos), tocado)
        return tocado
    
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:
        ventana_principal.set_canvas(panel_principal)
        
        # Colaboración
        panel_principal.colaboracion = ventana_principal.colaboracion
        panel_principal.colaboracion.set_up(
                None,   # Nuevo compañero
                None,   # Salió Compañero
                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 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()