Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/pilas/ejemplos/piezas.py
blob: e7cae75ad6eac1716691d65b46a4352d31431e4f (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
# -*- encoding: utf-8 -*-
# pilas engine - a video game framework.
#
# copyright 2010 - hugo ruscitti
# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
#
# website - http://www.pilas-engine.com.ar

import pilas
import random


class Piezas(pilas.escenas.Normal):
    """Representa la escena de rompecabezas.

    La escena comienza con una imagen que se descompone en muchos
    actores Pieza.
    """

    def __init__(self, ruta_a_la_imagen="ejemplos/data/piezas.png", filas=4, columnas=4, al_terminar=None):
        pilas.actores.utils.eliminar_a_todos()
        pilas.escenas.Normal.__init__(self, pilas.colores.grisoscuro)
        grilla = pilas.imagenes.cargar_grilla(ruta_a_la_imagen, columnas, filas)
        self.crear_piezas(grilla, filas, columnas)
        self.pieza_en_movimiento = None

        pilas.eventos.click_de_mouse.conectar(self.al_hacer_click)
        pilas.eventos.termina_click.connect(self.al_soltar_el_click)
        pilas.eventos.mueve_mouse.connect(self.al_mover_el_mouse)

        self.sonido_tick = pilas.sonidos.cargar("tick.wav")
        self.al_terminar = al_terminar
        self.piezas_desconectadas = filas * columnas -1

    def crear_piezas(self, grilla, filas, columnas):
        "Genera todas las piezas en base al tamaño del constructor."
        self.piezas = []
        self.grupos = {}

        for x in range(filas * columnas):
            self.grupos[x] = set([x])
            pieza = Pieza(self, grilla, x, filas, columnas)
            self.piezas.append(pieza)
            pieza.x = random.randint(-200, 200)
            pieza.y = random.randint(-200, 200)


    def al_hacer_click(self, evento):
        "Atiente cualquier click que realice el usuario en la pantalla."
        x, y = evento.x, evento.y
        pieza_debajo_de_mouse = pilas.actores.utils.obtener_actor_en(x, y)

        if pieza_debajo_de_mouse and isinstance(pieza_debajo_de_mouse, Pieza):
            self.pieza_en_movimiento = pieza_debajo_de_mouse
            self.pieza_en_movimiento.mostrar_arriba_todas_las_piezas()

    def al_soltar_el_click(self, evento):
        if self.pieza_en_movimiento:
            self.pieza_en_movimiento.soltar_todas_las_piezas_del_grupo()
            self.pieza_en_movimiento.mostrar_abajo_todas_las_piezas()
            self.pieza_en_movimiento = None

    def al_mover_el_mouse(self, evento):
        if self.pieza_en_movimiento:
            self.pieza_en_movimiento.x += evento.dx
            self.pieza_en_movimiento.y += evento.dy
            
    def conectar(self, pieza_a, pieza_b):
        a = pieza_a.numero
        b = pieza_b.numero

  
        if a in self.grupos[b]:
            #Evita contectar mas de una vez a dos piezas.
            return

        """Inicialmente comienzo con::


            0: [0, 1, 2]
            1: [0, 1, 2]
            2: [0, 1, 2]
            3: [3]

        ¿y si conecto la pieza 3 con la 2?

        - tendría que obtener todas las piezas que conoce 2.
        
        - iterar en ese grupo y decirle a cada pieza que sume a 3 en su grupo::

            0: [0, 1, 2, 3]
            1: [0, 1, 2, 3]
            2: [0, 1, 2, 3]

        - luego solo me falta tomar a uno de esos grupos actualizados
          y decirle a 3 que ese será su grupo::

            3: [0, 1, 2, 3]
        """
        
        grupo_nuevo = set(self.grupos[a]).union(self.grupos[b])

        for pieza in grupo_nuevo:
            self.grupos[pieza] = grupo_nuevo 

        self.piezas_desconectadas -= 1

        if self.piezas_desconectadas < 1:
            if self.al_terminar:
                self.al_terminar()

        self.sonido_tick.reproducir()
        
class Pieza(pilas.actores.Animado):
    """Representa una pieza del rompecabezas.

    Esta pieza se puede arrastrar con el mouse y cuando se suelta
    intentará conectarse con las demás."""

    def __init__(self, escena_padre, grilla, cuadro, filas, columnas):
        "Genera la pieza que representa una parte de la imagen completa."
        self.escena_padre = escena_padre
        self.numero = cuadro
        pilas.actores.Animado.__init__(self, grilla)

        self.z_de_la_pieza_mas_alta = 0
        self.asignar_numero_de_piezas_laterales(cuadro, columnas)

        self.definir_cuadro(cuadro)

        self.radio_de_colision = self.obtener_ancho() / 2 + 12
        self.piezas_conectadas = []

    def asignar_numero_de_piezas_laterales(self, cuadro, columnas):
        "Guarda el numero de las piezas que se pueden conectar en sus bordes."
        self.numero_arriba = cuadro - columnas
        self.numero_abajo = cuadro + columnas

        if cuadro % columnas == 0:
            self.numero_izquierda = -1
        else:
            self.numero_izquierda = cuadro - 1

        if cuadro % columnas == columnas -1:
            self.numero_derecha = -1
        else:
            self.numero_derecha = cuadro + 1

    def soltar_todas_las_piezas_del_grupo(self):
        for numero in self.escena_padre.grupos[self.numero]:
            pieza = self.escena_padre.piezas[numero]
            pieza.soltar()

    def soltar(self):
        # Busca todas las colisiones entre esta pieza
        # que se suelta y todas las demás.
        colisiones = pilas.mundo.colisiones.obtener_colisiones(self, self.escena_padre.piezas)

        for x in colisiones:
            self.intentar_conectarse_a(x)

    def se_pueden_conectar_los_bordes(self, borde1, borde2):
        distancia = pilas.utils.distancia(borde1, borde2)
        return  distancia < 12

    def intentar_conectarse_a(self, otra):
        "Intenta vincular dos piezas, siempre y cuando coincidan en sus bordes."

        # Intenta conectar los bordes laterales
        if self.numero_derecha == otra.numero:
            if self.se_pueden_conectar_los_bordes(self.derecha, otra.izquierda):
                otra.izquierda = self.derecha
                otra.arriba = self.arriba
                self.conectar_con(otra)

        elif self.numero_izquierda == otra.numero:
            if self.se_pueden_conectar_los_bordes(self.izquierda, otra.derecha):
                otra.derecha = self.izquierda
                otra.arriba = self.arriba
                self.conectar_con(otra)

        # Intenta conectar los bordes superior e inferior
        if self.numero_abajo == otra.numero:
            if self.se_pueden_conectar_los_bordes(self.abajo, otra.arriba):
                otra.arriba = self.abajo
                otra.izquierda = self.izquierda
                self.conectar_con(otra)

        elif self.numero_arriba == otra.numero:
            if self.se_pueden_conectar_los_bordes(self.arriba, otra.abajo):
                otra.abajo = self.arriba
                otra.izquierda = self.izquierda
                self.conectar_con(otra)


    def conectar_con(self, otra_pieza):
        self.escena_padre.conectar(self, otra_pieza)


    def __repr__(self):
        return "<<Pieza %d>>" %(self.animacion.obtener_cuadro())


    def set_x(self, x):
        "A diferencia de los actores normales, las piezas tienen que mover a todo su grupo."
        dx = x - self.x

        for numero in self.escena_padre.grupos[self.numero]:
            try:
                pieza = self.escena_padre.piezas[numero]
                pieza.definir_posicion(pieza.x + dx, pieza.y)
            except IndexError:
                pass

    def set_y(self, y):
        "A diferencia de los actores normales, las piezas tienen que mover a todo su grupo."
        dy = y - self.y

        for numero in self.escena_padre.grupos[self.numero]:
            try:
                pieza = self.escena_padre.piezas[numero]
                pieza.definir_posicion(pieza.x, pieza.y + dy)
            except IndexError:
                pass

    def get_x(self):
        x, y = self.obtener_posicion()
        return x

    def get_y(self):
        x, y = self.obtener_posicion()
        return y

    x = property(get_x, set_x, doc="Define la posición horizontal.")
    y = property(get_y, set_y, doc="Define la posición vertical.")

    def mostrar_arriba_todas_las_piezas(self):
        for numero in self.escena_padre.grupos[self.numero]:
            pieza = self.escena_padre.piezas[numero]
            pieza.z = -1

    def mostrar_abajo_todas_las_piezas(self):
        for numero in self.escena_padre.grupos[self.numero]:
            pieza = self.escena_padre.piezas[numero]
            pieza.z = 0