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()
|