Vous êtes sur la page 1sur 45

1 /*Programação - Projeto Final

2 Alunos:
3 Iara Figueiras - 89668
4 Leonardo Duarte - 89691
5 Jogo com o nível máximo implementado (nível 3) e que incorpora todos o requisitos especificados no enunciado
6 Para uma melhor experiência adicionar a flag -f ao correr o programa e entrar no modo fixe! (é uma surpresa:))
7 Este jogo foi desenvolvido segundo a norma C99*/
8
9 /*Ficheiro main_loop.c
10 Este ficheiro é aquele em que são evidentes todas as operações efetuadas no decorrer do jogo, incluindo, portanto, a função
11 main que, por sua vez, faz uso da função "loopDoJogo" auxilidada por "loop_verificacao_caminhos" que trocam, procuram caminhos,
12 eliminam bolhas e guardam o resultado da pontuação no ficheiro "resultados.txt";
13 Assim, são incluidos diversos ficheiros que permitem aceder a funções que tornam possivel a execução destas tarefas.*/
14
15 //------------------------------------------------------------------INCLUDES---------------------------------------------------
16 #include "leitura_dados.h" //ficheiro com funções que permitem aceder e interpretar os dados introduzidos no terminal
17 #include "op_listas.h" //ficheiro com funções necessárias para realizar operações com as bolhas nas listas (e alguns extras :))
18 #include "graphics.h" //ficheiro com todas as funções necessárias para a representação gráfica do jogo
19 #include "animacoes.h" //ficheiro que dá acesso a funções da representação das animações do jogo
20 //ficheiro que dá acesso a funções que guardarão e ordenarão a pontuação do jogador no ficheiro de resultados
21 #include "resultados.h"
22
23 //----------------------------------------------------------------------MAIN---------------------------------------------------
24 int main(int argc, char const *argv[])
25 {
26
27 DADOS dados; //declaração da variável que será utilizada para guardar os dados gerais do programa
28 //invoca a função lerConfigDados para extrair os dados do terminal, se esta função retornar 1, houve um erro
29 if(lerConfigDados(&dados, argc, argv) == 1)
30 mensagemErroDados(); //é mostrada a mensagem no terminals
31
32 //inicializar o apontador que servirá de acesso a todas as bolhas no programa
33 APBLOCO * OApontador = NULL;
34 iniciarSDL2(dados);//inicia a biblioteca gráfica e abre a janela do jogo
35 //executa o loop do jogo que só terminará quando o utilizador assim o definir ou não houver opções de jogada
36 loopDoJogo(&dados, OApontador);
37 sairSDL2(); //destruir o renderer, a janela e fecha a execução da biblioteca gráfca
38 fimDoJogo(dados.pontos); //guarda a pontuação do jogador ordenadamente no ficheiro de resultados
39
40 return 0;
41 }
42
43 //------------------------------------------------------------------------LOOP-------------------------------------------------
44 /*
45 Função principal do programa responsável pelo ciclo do jogo
46 recebe a estrutura de dados para que se possa aceder às configurações gerais do jogo (por endereço para que as alterações
47 efetuadas sejam também alteradas na função loopDoJogo e, consequentemente na main).
48 Recebe também o apontador (inicializado a NULL) que dará acesso às posições e respetivas cores das bolhas para que no deccorer
49 desta função se possa monitorizar as alterações e consequências nestes valores das ações do jogador.
50 É responsável pelo seguinte ciclo de ações:
51 -> detetar o clique na janela
52 -> ordenar a troca, se for caso disso e correr a função que procura e elimina caminhos de bolhas
53 -> verificar se o jogo continua, acaba, o se é recomeçado
54 */
55 void loopDoJogo(DADOS * dados, APBLOCO * OApontador)
56 { int jogoContinua = TRUE; //variável que indica se o jogo reúne todas as condições para continuar
57 int jogoRepete = FALSE; //variavel que indica se o jogo se repetirá (ao carregar no botão novo jogo)
58 int bolhas_selecionadas = 0; //variável que guardará o número de bolhas selecionadas para troca
59 TROCA_FIXE_INFO troca_info;
60 SDL_Event event; //evento para detetar cliques
61 do //início do ciclo de um novo jogo
62 { OApontador = criaListas(*dados); //criar as listas de bolhas e atribuir o endereço de acesso à variável correspondente
63 if (dados->fixe == TRUE) //no modo fixe as bolhas iniciais não podem formar já caminhos inicialmente
64 remove_bolhas_iniciais_em_caminhos(*dados, OApontador);
65 dados->pontos = 0; //inicializar variáveis de jogabilidade
66 jogoRepete = FALSE;
67 jogoContinua = TRUE;
68 bolhas_selecionadas = 0;
69 troca_info.x0 = -1; //inicializar variáveis de troca
70 troca_info.y0 = -1;
71 troca_info.i = -1;
72 troca_info.j = -1;
73 troca_info.direcao = 0;
74 reset(*dados, OApontador); //desenha a disposição inicial das bolhas na janela
75 while (jogoContinua == TRUE) //início do loop de execução dos comandos do jogo
76 { SDL_WaitEvent(&event); //aguarda interação do utilizador
77 switch (event.type)
78 { case SDL_QUIT: //caregar no X da Janela
79 { jogoContinua = FALSE;
80 jogoRepete = FALSE;
81 break;
82 }
83 //no modo fixe, ao pressionar o rato, é logo intrepretada a informação da troca para que se proceda à animação
84 case SDL_MOUSEBUTTONDOWN:
85 { if (dados->fixe == TRUE)
86 { switch(event.button.button)
87 { case SDL_BUTTON_LEFT: //no caso de ser o botão esquerdo verifica se carregou numa bolha ou num botão
88 { //intrepretar informação da troca
89 troca_info.aTrocar = deteta_clique_troca_fixe(*dados, OApontador, &troca_info);
90 break;
91 }
92 }
93 }
94 break;
95 }//no modo fixe, se o utilizador estiver a trocar uma bolha ao mexer no rato, a posição da bolha a trocar é atualizada
96 case SDL_MOUSEMOTION:
97 { if (troca_info.aTrocar == TRUE)
98 { if (troca_info.direcao == 0) //se a direção for indefinida, define-a
99 define_direcao(&troca_info);
100 mexe_bolhas_troca(*dados, OApontador, troca_info); //calcula a posição da bolha e mostra a animação da troca
101 }
102 break;
103 }
104 case SDL_MOUSEBUTTONUP: //quando um botão do rato deixar de ser premido
105 { switch (event.button.button)
106 { case SDL_BUTTON_LEFT: //no caso de ser o botão esquerdo verifica se carregou numa bolha ou num botão
107 { if (dados->fixe == TRUE)
108 mouse_up_fixe(dados, OApontador, &jogoRepete, &jogoContinua, &troca_info);
109 else
110 mouse_up_usual(dados, OApontador, &jogoRepete, &jogoContinua, &bolhas_selecionadas);
111 }
112 }
113 break;
114 }
115 }
116 }
117 OApontador = byebye_listas(*dados, OApontador); //no final de cada jogo, a memória alocada é libertada e OApontador = NULL
118 }while(jogoRepete == TRUE); //se carregar em novo jogo 'jogoRepete' vai tomar o valor TRUE e o jogo recomeca
119 }
120
121/*Função cujo propósito é auxiliar a função loopDOJogo
122 Esta função recebe: a estrutura de dados para que se possa aceder às configurações gerais do jogo (por endereço para que as alterações
123 efetuadas sejam também alteradas na função loopDoJogo e, consequentemente na main)
124 apontador de acesso às listas que serve de suporte à organização das bolhas
125 as variáveis de controlo do loop do jogo por endereço
126 e a informação relativa à troca que está a ser efetuada por endereço
127 Assim, é responsável por todas as ações que tenham de ser executadas quando o botão do rato é deixado de ser premidono modo fixe
128 marca o fim da animação de troca*/
129 void mouse_up_fixe(DADOS * dados, APBLOCO * OApontador, int * jogoRepete, int * jogoContinua, TROCA_FIXE_INFO * troca_info)
130 {
131 switch(deteta_clique(*dados, OApontador)) //verificar no modo fixe se clicou os botões
132 { case 3: //botão fim do jogo
133 {
134 if (troca_info->aTrocar == FALSE)
135 {
136 *jogoContinua = FALSE;
137 *jogoRepete =FALSE;
138 break;
139 }
140 }
141 case 4: //botão novo jogo
142 {
143 if (troca_info->aTrocar == FALSE)
144 {
145 *jogoContinua = FALSE;
146 *jogoRepete = TRUE;
147 break;
148 }
149 }
150 default: //se não clicou nos botões, avalia se a troca com animação foi bem sucedida
151 { if(troca_info->aTrocar == TRUE)
152 { troca_info->aTrocar = FALSE;
153 //troca as bolhas se os desvios fores grandes o suficiente ou retorna as bolhas às posições iniciais
154 normaliza_bolhas_trocadas_animacao(*dados, OApontador);
155 loop_verificacao_caminhos(dados, OApontador); //verifica se a troca efetuou caminhos
156 }
157 }
158 }
159 }
160
161 /*Função cujo propósito é auxiliar a função loopDOJogo
162 Esta função recebe :a estrutura de dados para que se possa aceder às configurações gerais do jogo (por endereço para que as alterações
163 efetuadas sejam também alteradas na função loopDoJogo e, consequentemente na main)
164 o apontador de acesso às listas que serve de suporte à organização das bolhas
165 as variáveis de controlo do loop do jogo por endereço
166 e o número de bolhas selecionadas por endereco
167 Assim, é responsável por todas as ações que tenham de ser executadas quando o botão do rato é deixado de ser premido, nomeadamete
168 a seleção das bolhas para a troca quando o jogo não está no modo fixe*/
169 void mouse_up_usual(DADOS * dados, APBLOCO * OApontador, int * jogoRepete, int * jogoContinua, int * bolhas_selecionadas)
170 {
171 switch(deteta_clique(*dados, OApontador))
172 { case 3: //botão fim do jogo
173 { *jogoContinua = FALSE;
174 *jogoRepete =FALSE;
175 break;
176 }
177 case 4: //botão novo jogo
178 { *jogoRepete = TRUE;
179 *jogoContinua = FALSE;
180 break;
181 }
182 case 0: break; //não clicou em nada
183 case -1: //se o utilizador carregou na bolha que estava já marcada, a seleção dessa bolha é anulada
184 { *bolhas_selecionadas = 0; //o número de bolhas selecionadas é anulado
185 reset(*dados, OApontador); //apaga o mais na bolha selecionada
186 break;
187 }
188 case 1:
189 { reset(*dados, OApontador); //pinta o + na bolha selecionada
190 *bolhas_selecionadas += 1;
191 /*se forem selecinadas duas bolhas corre o ciclo de verificação de caminhos até que a configuração das bolhas não
192 forme caminhos*/
193 if (*bolhas_selecionadas == 2)
194 { *bolhas_selecionadas = 0;
195 loop_verificacao_caminhos(dados, OApontador);
196 if(verifica_estado_do_jogo(OApontador, *dados) == FALSE) //verifica se existem mais hipóteses de jogada
197 { DEBUG("sair. Não há mais hipoteses de jogada.");
198 *jogoContinua = FALSE;
199 *jogoRepete = FALSE;
200 }
201 }
202 break;
203 }
204 }
205 }
206
207 /*
208 Função cujo propósito é auxiliar a função loopDoJogo
209 Esta função recebe a estrutura de dados para que se possa aceder às configurações gerais do jogo (por endereço para que as alterações
210 efetuadas sejam também alteradas na função loopDoJogo e, consequentemente na main) e o apontador de acesso às listas que servem de
211 suporte à organização das bolhas
212 Assim, é responsável pelo ciclo de ações:
213 -> marcar as bolhas a ser eliminadas
214 -> eliminar bolhas marcadas
215 até que não hajam mais bolhas a ser eliminadas
216 */
217 void loop_verificacao_caminhos(DADOS * dados, APBLOCO * OApontador)
218 {
219
220 if(dados->debug == TRUE && dados->fixe == FALSE) //se em modo de debug espera que o utilizador carregue em enter
221 DEBUG("trocar!");
222 else if(dados->fixe == FALSE) //caso contrário, há um pequeno delay para que seja possivel acompanhar o decorrer das ações
223 SDL_Delay(DELAY);
224
225 if (dados->fixe == FALSE)
226 {
227 troca_bolhas(*dados, OApontador); //troca as bolhas selecionadas
228 reset(*dados, OApontador); //desenha as bolhas no ecrã já trocadas
229 }
230 while(TRUE) //até ser dada a indicação de break (não se formam mais caminhos) corre as ações pelas quais esta função é responsável
231 {
232 if(dados->debug == TRUE)
233 DEBUG("procurar e marcar bolhas a ser eliminadas!");
234 else if(dados->fixe == FALSE)
235 SDL_Delay(DELAY);
236
237 if(marcar_bolhas_a_eliminar(*dados, OApontador) == FALSE) //marcar as bolhas a eliminar
238 break; //se não houver bolhas a eliminar a configuração das bolhas não forma mais caminhos e esta função termina
239
240 if(dados->debug == TRUE)
241 reset(*dados, OApontador); //no modo de debug, as bolhas a ser eliminadas serão marcadas com um X
242
243 if(dados->debug == TRUE)
244 DEBUG("eliminar bolhas marcadas!");
245 else if(dados->fixe == FALSE)
246 SDL_Delay(DELAY);
247
248 if (dados->fixe == TRUE) //no modo fixe, são usadas funções diferentes que complementam o jogo com animações
249 {
250 dados->pontos += elimina_bolhas_marcadas_animacao(*dados, OApontador);
251 //anima descida de bolhas acima de bolhas que foram eliminadas
252 atualizaJanela_animacoes_descer_velhas(*dados,OApontador);
253 }
254 else
255 { //as bolhas marcadas são eliminadas e os pontos aumentados no número de bolhas
256 dados->pontos += elimina_bolhas_marcadas(*dados, OApontador);
257 reset(*dados, OApontador);
258 }
259
260 if (dados->fixe == TRUE)//no modo fixe, são adicionadas bolhas que substituem as removidas
261 {
262 if (dados->debug == TRUE)
263 DEBUG("adicionar bolhas, porque este é o modo fixe!");
264 //anima adição de bolhas
265 adiciona_bolhas(OApontador, *dados, TRUE);
266 atualizaJanela_animacoes_descer_novas(*dados, OApontador);
267 }
268 }
269 }
1 /*
2 Ficheiro header_base.h
3 Header que inclui as bibliotecas básicas, estruturas básicas e MACROS que são usados por grande parte dos ficheiros que compõem
4 o jogo*/
5
6 //prevenir inclusões múltiplas
7 #ifndef INCLUIDO
8 #define INCLUIDO
9
10 //----------------------------------------------------------------------INCLUDES-----------------------------------------------
11 #include <stdlib.h> //necessário para alocação de memória
12 #include <math.h>
13 #include <stdio.h> //funções básicas de C
14 #include <string.h> //operações de strings
15
16 //------------------------------------------------------------------ VARIAVEIS ESTRUTURADAS ------------------------------------
17 typedef struct estruturaDeDados
18 {
19 int larguraJanela; //em bolhas
20 int alturaJanela; //em bolhas
21 int dimBolha;
22 int debug;
23 int fixe;
24
25 int windowSizeX;
26 int windowSizeY;
27 float dl;
28
29 int altura_barra_superior;
30 int pontos;
31
32 }DADOS;
33
34 //estrutura de dados que guarda informação relevante para a animação da bolha
35 typedef struct troca
36 {
37 int x0; //posição do rato quando fez o primeiro clique
38 int y0;
39
40 int i; //coordenadas da bolha clicada
41 int j;
42
43 int direcao; //direção da troca (1-Horizontal 2-Vertical)
44 int aTrocar; //o utilizador ainda está a premir o rato pelo que a animação continua
45
46 }TROCA_FIXE_INFO;
47
48 //----------------------------------------------------------------------MACROS--------------------------------------------------
49 //lógica booleana
50 #define TRUE 1
51 #define FALSE 0
52
53 //limites janela
54 #define LARGURA_MINIMA1 (30*12+30*(0.1)*(12+1))
55 #define LARGURA_MINIMA (30*5+30*(0.1)*(5+1))
56 #define LARGURA_MAXIMA (20*60+20*(0.1)*(60+1))
57 #define ALTURA_MAXIMA (1.5*20 + 20*30 + (0.1)*20*(30+1) + 1.75*20)
58
59 //constantes definidas pelos programadores
60 #define DL 0.05
61
62 //macros função 'CoordenadasMatriztransformaParaRefJanela'
63 #define EIXO_X 0
64 #define EIXO_Y 1
65
66 //debug
67 #define LINHA {printf("-----%d--------\n", __LINE__);}
68
69 #endif
1 /*
2 Ficheiro op_listas.h
3 Header que serve de apoio ao ficheio op_listas.c e que tornará possivel que outros ficheiros acedam às funções que permitem fazer operações
4 sobre as listas
5 */
6
7 //prevenir inclusões múltiplas
8 #ifndef INCLUDED
9 #define INCLUDED
10
11 //----------------------------------------------------------------------INCLUDES----------------------------------------------------------------------
12 #include "header_base.h"
13
14 //----------------------------------------------------------------------MACROS----------------------------------------------------------------------
15
16 //Tipos de marcas possíveis para bolhas
17 #define MARCADA_PARA_TROCA 1
18 #define SOFREU_ALTERACAO 2
19 #define MARCADA_PARA_ELIMINACAO_TEMP 3
20 #define MARCADA_PARA_ELIMINACAO 4
21 #define ANIMACAO_DESCER 100
22 #define ANIMACAO_TROCA 300
23
24
25 //------------------------------------------------------------------ VARIAVEIS ESTRUTURADAS -------------------------------------------------------------------
26 //definição da estrutura que guarda a toda a informação necessária referente a uma bolha
27 typedef struct estrutura_bolha
28 {
29 int cor;
30 int marca;
31 }BOLHA;
32
33 /*definição da estrutura dos blocos da lista dinâmica que servirá de suporte à disposição das bolhas
34 è composta pelo espaço útil reservado para guardar a informação da bolha e um apontador para o próximo bloco*/
35 typedef struct bloco_lista
36 {
37 BOLHA bolha;
38 struct bloco_lista * prox;
39
40 }BLOCO;
41
42 //redefinição de apontador para a variàvel estruturada BLOCO como APBLOCO para melhor compreensão do código
43 typedef BLOCO * APBLOCO;
44
45
46 //----------------------------------------------------------------------PROTÓTIPOS----------------------------------------------------------------------
47 //protótipos das duas funções definidas em main_loop.c
48 void loopDoJogo(DADOS * dados, APBLOCO * OApontador);
49 void loop_verificacao_caminhos(DADOS * dados, APBLOCO * OApontador);
50 void mouse_up_fixe(DADOS * dados, APBLOCO * OApontador, int * jogoRepete, int * jogoContinua, TROCA_FIXE_INFO * troca_info);
51 void mouse_up_usual(DADOS * dados, APBLOCO * OApontador, int * jogoRepete, int * jogoContinua, int * bolhas_selecionadas);
52
53 //------------------------------------------------protótipos das funções que operam sobre as listas -----------------------------------
54 APBLOCO* criaListas(DADOS dados);
55
56
57 //posição
58 APBLOCO apbloco_bolha_em_i_j(APBLOCO * OApontador, int i, int j, DADOS dados);
59
60 //troca de bolhas
61 void troca_bolhas(DADOS dados, APBLOCO * OApontador);
62 void troca_em_coluna(APBLOCO * OApontador, APBLOCO anterior, APBLOCO bolhaTroca_1, int coluna);
63 void troca_entre_colunas(APBLOCO * OApontador, DADOS dados, APBLOCO bolhaTroca_1, int i, int j);
64 void eliminar_marcas_troca(APBLOCO * OApontador, DADOS dados, int j);
65

66 //eliminação de bolhas
67 int marcar_bolhas_a_eliminar(DADOS dados, APBLOCO * OApontador);
68 int marcar_bolhas_a_eliminar_vizinhanca(DADOS dados, APBLOCO * OApontador, int i, int j);
69 void marca_bolhas_candidatas_a_0(DADOS dados, APBLOCO * OApontador);
70 void marca_bolhas_candidatas_eliminacao_permanente(DADOS dados, APBLOCO * OApontador);
71 int elimina_bolhas_marcadas(DADOS dados, APBLOCO * OApontador);
72 void remove_bolhas_iniciais_em_caminhos(DADOS dados, APBLOCO * OApontador);
73
74 //adição de bolhas
75 void adiciona_bolhas(APBLOCO * OApontador, DADOS dados, int com_animacao);
76
77 //controlo
78 int verifica_estado_do_jogo(APBLOCO * OApontador, DADOS dados);
79 int avalia_matriz(int numero_de_cores[9][2]);
80 APBLOCO * byebye_listas(DADOS dados, APBLOCO * OApontador);
81
82
83 //------------------------------------------------protótipos das funções de debug sobre operações de listas ------------------
84 //debug
85 void estado(DADOS dados, APBLOCO * OApontador);
86 void DEBUG(char str[]);
87
88 #endif
1 /*Ficheiro op_listas.c
2 Este é o ficheiro onde estão definidas todas as funções que operam sobre as listas
3 Estão também incluídas neste ficheiro as funções de debug, algumas utilizadas na execução do programa no modo de debug, que
4 permitem dividir e analisar separadamente as operações que as listas estão a sofrer, e outras de depuração :) que são utilizadas
5 apenas no desenvolvimento de novas funcionalidades.*/
6
7 //----------------------------------------------------------------------INCLUDES-----------------------------------------------
8 /*incluir o header com protótipos das funções, macros, variáveis estruturadas e outros ficheiros com funções que necessitarão
9 de ser usadas/*
10 #include "op_listas.h"
11
12 //----------------------------------------------------------------------FUNÇÕES-------------------------------------------------
13 /*Função que cria um vetor dinâmico de apontadores do tipo apontador para BLOCO (APBLOCO), este vetor tem a dimensão do número
14 de colunas do jogo. Cada elemento do bloco é o primeiro apontador da lista dinâmica que representa as colunas da matriz, que esta
15 função também está responsável por criar.
16 As listas criadas representam as colunas com bolhas de baixo para cima, para que a eliminação de bolhas fosse facilitada.
17 Recebe como argumentos: uma variàvel do tipo DADOS e que fornecerá toda a informação acerca das dimensões dos vetores necessária.
18 Retorna o apontador para a lista dinâmica e que servirá de ponto de acesso a toda a informação referente às bolhas no decorrer do
19 programa.*/
20 APBLOCO * criaListas(DADOS dados)
21 {
22 APBLOCO * OApontador; //apontador para o vetor dinâmico
23 OApontador = (APBLOCO *) calloc(dados.larguraJanela, sizeof(APBLOCO)); //alocação do vetor dinâmico
24 if(OApontador == NULL) //verificar o sucesso da alocação
25 {
26 printf("Erro de alocação de memória.\n");
27 exit(0);
28 }
29 for (int j = 0; j < dados.larguraJanela; ++j)//corre as colunas
30 {
31 APBLOCO ultimo = NULL; //apontador para o último elemento adicionado à lista dinâmica
32 for (int i = 0; i < dados.alturaJanela; ++i) //criar listas dinâmicas com o mesmo número de elementos que linhas do jogo
33 {
34 BOLHA bolhacriada; //cria nova bolha
35 bolhacriada.cor = rand()%9 +1; //gera um número aleatório
36
37 //se o jogo for inicializado no modo fixe, será necessário procurar caminhos inicialmente, para isso marcam-se as bolhas
38 if (dados.fixe == TRUE)
39 bolhacriada.marca = SOFREU_ALTERACAO;
40 else//caso contrário, a marca é inicializada como sendo nula
41 bolhacriada.marca = 0;
42
43 APBLOCO atual; //apontador para a nova bolha criada
44 atual = (APBLOCO) calloc(1, sizeof(BLOCO)); //aloca memória para a nova bolha criada
45
46 if(atual == NULL) //testa alocação
47 {
48 printf("Erro de alocação de memória.\n");
49 exit(0);
50 }
51
52 //atribui a bolha criada ao novo bloco alocado
53 atual->bolha = bolhacriada;
54 atual->prox = NULL;
55
56 if (i==0) //o apontador para o primeiro apontador de cada lista ficará guardado no vetor dinâmico
57 *(OApontador+j)= atual;
58 else
59 ultimo->prox = atual; //criação das ligações da lista dinâmica
60
61 ultimo = atual;
62 }
63 }
64
65 return OApontador; //retorna o apontador para o vetor dinâmico, o único ponto de acesso a toda a informação
66 }
67
68 //---------------------------------------------------------------- posição -----------------------------------------------------
69
70 /*
71 Função que procura a bolha nas coordenadas i e j da pseudo-matriz formada pelas listas dinâmicas e retorna o endereço do bloco
72 correspondente
73 Esta é uma solução que permite aceder facilmente nas outras funções desevolvidas a bolhas sabendo a sua posição relativa
74 Recebe como argumentos: o ponto de acesso a toda a informação, o apontador "OApontador"
75 os inteiros i e j representando as coordenadass da bolha na pseudo-matriz
76 os dados do programa através da variável estruturada DADOS
77 Retorna: O apontador para o bloco da bolha procurada no caso de ela existir
78 NULL no caso de as coordenadas requesitadas não estarem associadas a nenhuma bolha
79 */
80 APBLOCO apbloco_bolha_em_i_j(APBLOCO * OApontador, int i, int j, DADOS dados)
81 {
82
83 if (i>= dados.alturaJanela || j >= dados.larguraJanela || i<0 || j<0) //se está fora dos limites da janela retorna NULL
84 return NULL;
85
86 APBLOCO step_coluna_j = *(OApontador + j); //variavel que será usada para correr a coluna j
87
88 int k = 0; //contador
89 while(step_coluna_j != NULL) //enquanto a lista dinâmica não chega ao fim
90 {
91 if (k == i) //quando for atingida a linha desejada corta o ciclo
92 break;
93 k++;
94 step_coluna_j = step_coluna_j->prox;
95 }
96 /*retorna o endereço do bloco de memória da bolha procurada se entretanto a lista não chegou ao fim sem a ter atingido as
97 coordenadas procuradas*/
98 return step_coluna_j;
99 }
100
101 //-------------------------------------------------------------troca de bolhas --------------------------------------------------
102 /*
103 Função que é responsável pela troca de duas bolhas
104 Recebe: os dados do programa
105 o apontador de acesso a toda a informação das bolhas
106 Assim, procura as bolhas marcadas e analisa a situação em 3 casos distintos:
107 -> troca entre colunas que delegrará a outras funções
108 -> troca em coluna que também delegará a outra função
109 -> troca inválida, neste caso eliminará as marcas para troca das bolhas
110 */
111 void troca_bolhas(DADOS dados, APBLOCO * OApontador)
112 {
113 int i; //variavel para correr as colunas
114 int j; //variavel para correr as colunas
115 int BREAK = FALSE; //flag para controlar o ciclo
116
117 APBLOCO bolhaTroca_1; //apontador para o bloco de uma das bolhas a ser trocadas
118
119 for (j = 0; j < dados.larguraJanela; ++j)//percorre as linhas
120 {
121 bolhaTroca_1 = *(OApontador + j); //no início de cada coluna o apontador está no vetor dinâmico
122 i = 0;
123
124 while(bolhaTroca_1 != NULL)//percorre as colunas
125 {
126 if (bolhaTroca_1->bolha.marca == MARCADA_PARA_TROCA) //quando for encontrada a primeira bolha para troca sai do ciclo
127 {
128 BREAK = TRUE; //para sair dos dois ciclos usa a falg BREAK
129 break;
130 }
131 bolhaTroca_1 = bolhaTroca_1->prox;
132 i++;
133 }
134
135 if (BREAK == TRUE) //se a flag for TRUE sai do ciclo
136 break;
137 }
138
139 /* Sabendo que apenas duas bolhas podem estar marcadas e que o algoritmo de procura utilizado corre a janela da esquerda para
140 a direita e debaixo para cima, há três possiblidades:
141 -> a segunda bolha a trocar está à direita e terá de ser feita uma troca entre colunas
142 -> a segunda bolha a trocar está a cima e terá de ser feita uma troca na mesma coluna
143 -> a segunda bolha a trocar não está em nenhuma das situações anteriores pelo que é uma troca inválida
144 Assim, trataremos cada uma destas três possibilidades individualmente
145 */
146
147 //variáveis que guardarão os endereços das bolhas acima e à direita da primeira identificada para a troca
148 APBLOCO bolha_a_cima = bolhaTroca_1->prox;
149 APBLOCO bolha_direita = apbloco_bolha_em_i_j(OApontador, i,j+1, dados);
150
151 if (bolha_a_cima!= NULL && bolha_a_cima->bolha.marca == MARCADA_PARA_TROCA) //a bolha acima foi marcada para troca
152 { //apontador para a bolha anterior necessária para a função que fará a troca
153 APBLOCO anterior = apbloco_bolha_em_i_j(OApontador, i-1, j, dados);
154 troca_em_coluna(OApontador, anterior, bolhaTroca_1, j);//troca na mesma coluna
155 }
156 else if(bolha_direita != NULL && bolha_direita->bolha.marca == MARCADA_PARA_TROCA) //a bolha à direita foi marcada para troca
157 troca_entre_colunas(OApontador, dados, bolhaTroca_1, i, j); //troca bolhas entre colunas
158 else //se a troca for inválida
159 eliminar_marcas_troca(OApontador, dados, j); //elimina as marcas de troca de todas as bolhas
160 }
161
162 /*
163 Função que é responsável pela troca de bolhas na mesma coluna
164 Recebe: o apontador de acesso a toda a informação referente às bolhas
165 o apontador para o bloco da bolha anterior à primeira que será trocada (pode ser NULL)
166 o apontador para o bloco da bolha mais a baixo
167 a coordenada da coluna em que se efetuará a troca
168 Assim, reordena as duas bolhas tratando de mudar as ligações entre os elementos das listas*/
170 void troca_em_coluna(APBLOCO * OApontador, APBLOCO anterior, APBLOCO bolhaTroca_1, int coluna)
171 {
172 /*marca ambas as bolhas que serão trocadas com a marca SOFREU_ALTERACAO para que posteriormente se possoa avaliar
173 se a troca formou caminhos de bolhas da mesma cor*/
174 bolhaTroca_1->bolha.marca = SOFREU_ALTERACAO;
175 (bolhaTroca_1->prox)->bolha.marca = SOFREU_ALTERACAO;
176
177 //se a primera bolha trocar for a primeira da lista
178 if (anterior == NULL)
179 *(OApontador+coluna) = bolhaTroca_1->prox; //temos de alterar os apontadores do vetor dinâmico
180 else
181 anterior->prox = bolhaTroca_1->prox;
182
183 //reordenação das bolhas
184 APBLOCO bolhaTroca_2 = bolhaTroca_1->prox;
185 bolhaTroca_1->prox = bolhaTroca_2->prox;
186 bolhaTroca_2->prox = bolhaTroca_1;
187
188 }
189
190 /*
191 Função que é responsável pela troca de bolhas em colunas contíguas
192 Recebe: o apontador de acesso a toda a informação referente às bolhas
193 uma variável contendo a informação dos dados do programa
194 o apontador para o bloco da bolha mais à esquerda a ser trocada
195 as coordenadas i, j da bolha mais à esquerda a ser trocada
196 Assim, reordena as duas bolhas tratando de mudar as ligações entre os elementos das listas
197 */
198 void troca_entre_colunas(APBLOCO * OApontador, DADOS dados, APBLOCO bolhaTroca_1, int i, int j)
199 {
200
201 APBLOCO anterior_1 = apbloco_bolha_em_i_j(OApontador, i-1,j, dados); //bolha anterior à mais à esquerda
202 APBLOCO bolhaTroca_2 = apbloco_bolha_em_i_j(OApontador, i,j+1, dados); //bolha mais à direita que será trocada
203 APBLOCO anterior_2 = apbloco_bolha_em_i_j(OApontador, i-1,j+1, dados); //bolha anterior à mais à direita que será trocada
204
205 //para ter a certeza que o nosso amigo não aparece
206 if (bolhaTroca_2 == NULL)
207 return;
208
209 /*marca ambas as bolhas que serão trocadas com a marca SOFREU_ALTERACAO para que posteriormente se possoa avaliar
210 se a troca formou caminhos de bolhas da mesma cor*/
211 bolhaTroca_1->bolha.marca = SOFREU_ALTERACAO;
212 bolhaTroca_2->bolha.marca = SOFREU_ALTERACAO;
213
214 //variável auxiliar para permitir a troca
215 APBLOCO aux = bolhaTroca_1->prox;
216
217 //se as bolhas forem as primeiras das listas ter-se-à de alterar os endereços guardados no vetor dinâmico
218 if (anterior_1 == NULL)
219 {
220 *(OApontador+j+1) = bolhaTroca_1;
221 *(OApontador+j) = bolhaTroca_2;
222 }
223 else
224 {
225 anterior_2->prox = bolhaTroca_1;
226 anterior_1->prox = bolhaTroca_2;
227 }
228
229 //troca de endereços
230 bolhaTroca_1->prox = bolhaTroca_2->prox;
231 bolhaTroca_2->prox = aux;
232 }
233
234
235 /*
236 Função que corre todas as bolhas do jogo e anula a marca de todas as que estejam marcadas para troca
237 Recebe: os dados do jogo
238 o apontador de acesso a toda a informação das bolhas
239 as coordenadas coluna a partir da qual vale a pena procurar, pois o método de procura empregado anteriormente
240 garante que a segunda bolha a trocar estará na coluna dessa bolha ou numa coluna à direita
241 */
242 void eliminar_marcas_troca(APBLOCO * OApontador, DADOS dados, int j)
243 {
244 for (; j < dados.larguraJanela; ++j)//percorre as linhas
245 {
246 APBLOCO step = *(OApontador +j);
247 while(step != NULL)
248 {
249
250 if (step->bolha.marca == MARCADA_PARA_TROCA)
251 step->bolha.marca = 0;
252
253 step = step->prox;
254 }
255 }
256 }
257
258
259 //-------------------------------------------------------------eliminação de bolhas ---------------------------------------------
260
261 /*Função que é responsável por marcar as bolhas que terão de ser eliminadas.
262 Recebe: os dados do jogo
263 o apontador de acesso a toda a informação das bolhas
264 Retorna: TRUE se houver bolhas a eliminar
265 FALSE se não houver bolhas a eliminar
266 Esta função corre todas as bolhas da janela e para as que estão marcadas com SOFREU_ALTERACAO verifica se existem caminhos de
267 mais de 3 bolhas da mesma cor. Faz isso chamando a função recursiva "marcar_bolhas_a_eliminar_vizinhanca" que retorna o numero
268 de bolhas formam um caminho da mesma cor. Por forma selecionar as bolhas para eliminação marca todas as que tenham sofrido alteração
269 com uma eliminação temporária e apenas se o número de bolhas no caminho formado que as contém for superior as dois, as marca para
270 uma eliminação permanente, caso contrário a marca é anulada.*/
271 int marcar_bolhas_a_eliminar(DADOS dados, APBLOCO * OApontador)
272 {
273 int continuar_a_procurar; //variável que regulará o ciclo, será TRUE se for necessário voltar a verificar se há bolhas a eliminar
274 APBLOCO bolhaAtual; //apontador para a bolha em torno da qual se averiguará se são formados caminhos da mesma cor
275 int bolhas_cadeia = 0; //variável que guardará o número de bolhas numa determinada cadeia
276 //variável que serivirá para verificar se no correr desta função haverá bolhas que precisarão se ser posteriormente eliminadas
277 int vao_ser_eliminadas_bolhas = FALSE;
278
279 do //ciclo que corre enquanto houver bolhas que tenham sofrido alteração para as quais ainda não foi verificado se forma caminhos
280 {
281 continuar_a_procurar = FALSE;
282
283 for (int j = 0; j < dados.larguraJanela; ++j)//percorre as linhas
284 {
285 int i = 0; //coordenada na coluna
286 bolhaAtual = *(OApontador + j);
287 while(bolhaAtual != NULL)
288 {
289 if (bolhaAtual == NULL) //se não existirem mais bolhas numa coluna, passa à seguinte
290 break;
291
292 if (bolhaAtual->bolha.marca == SOFREU_ALTERACAO) //se a bolha sofreu alteração verifica se forma caminhos
293 {
294 bolhaAtual->bolha.marca = MARCADA_PARA_ELIMINACAO_TEMP; //marca a bolha para eliminação temporária
295 continuar_a_procurar = TRUE; //procura, pelo menos, mais uma vez
296 //verifica as cadeias à volta dessa bolha e verifica se forma uma cadeia com mais de 2 elementos
297 bolhas_cadeia = marcar_bolhas_a_eliminar_vizinhanca(dados, OApontador, i, j) + 1;
298 if(bolhas_cadeia > 2)//se tiver mais de dois elementos
299 {
300 //as bolhas que estavam marcadas para eliminação temporária passam agora a eliminação permanente
301 marca_bolhas_candidatas_eliminacao_permanente(dados, OApontador);
302 vao_ser_eliminadas_bolhas = TRUE; //neste caso já sabemos que vão ser eliminadas bolhas
303 }
304 //caso contrário a marca das bolhas que estavam marcadas para eliminação temporária é anulada
305 else
306 marca_bolhas_candidatas_a_0(dados, OApontador);
307 }
308 bolhaAtual = bolhaAtual->prox;
309 i++;
310 }
311 }
312 }while(continuar_a_procurar == TRUE); //enquanto não forem averiguadas todas as bolhas que sofreram alteração
313
314 return vao_ser_eliminadas_bolhas;
315 }
316
317
318 /*Função recursiva que é responsável pela marcação temporária de todas as bolhas que formem caminhos de uma dada cor
319 Recebe: os dados do jogo
320 o apontador de acesso a toda a informação das bolhas
321 a coordenada da bolha que numa determinada instância servirá de centro em torno do qual as bolhas serão avaliadas
322 Retorna: O numero de bolhas que encontrou da mesma cor do que a bolha central dada e que foram encontradas em todas as vezes que
323 se invocou a si própria pelo que no final da execução da primeira vez que foi invocada retornará o núremo de bolhas que formam
324 uma cadeia de uma determinada cor.
325
326 Esta função recebe na sua primeira invocação as coordenadas de uma bolha a partir do qual ela terá de avaliar se existe um caminho
327 que a contenha. Para isso verifica se as bolhas contíguas a ela são da mesma cor e não foram já marcadas para eliminação (caso
328 contrário gerar-se-ia um ciclo infinito). Para cada bolha que encontre da mesma cor chamar-se-á a si própria pr forma a avaliar
329 a vizinhamnça dessa bolha e a assim sucessivamente. Até que encontra uma bolha que não forma já mais caminhos com a sua vizinhança
330 pelo que retorna 0. Isto fará com que a última instância a ser invocada desta função acabe, e que as intruções da função que a
331 invocou prossigam (procurando outra bolha ou retornando o valor de bolhas que encontrou, que, neste caso, será maior que 1) até chegar
332 à primeira função invocada e ser retornado para a função "marcar_bolhas_a_eliminar" o numero de bolhas a formar o caminho encontrado.*/
333 int marcar_bolhas_a_eliminar_vizinhanca(DADOS dados, APBLOCO * OApontador, int i, int j)
334 {
335 int bolhas_encontradas = 0; //variável que guardará as bolhas em caminho encontradas por cada instância desta função
336 APBLOCO bolha_a_testar; //apontador para a bolha a testar
337 APBLOCO bolha_central = apbloco_bolha_em_i_j(OApontador, i, j, dados); //bolha em torno da qual se procurarão bolhas da mesma cor
338 //---------------------------------------------------------------------testar bolha em cima ---------------------------------
339 bolha_a_testar = apbloco_bolha_em_i_j(OApontador, i+1, j, dados);
340 if (bolha_a_testar != NULL) //se a bolha for da mesma cor e não tiver sido já marcada para eliminação temporária ou permanete então marca-a
341 { if (bolha_a_testar->bolha.marca != MARCADA_PARA_ELIMINACAO_TEMP && bolha_a_testar->bolha.marca != MARCADA_PARA_ELIMINACAO &&
342 bolha_a_testar->bolha.cor == bolha_central->bolha.cor)
343 {
344 bolha_a_testar->bolha.marca = MARCADA_PARA_ELIMINACAO_TEMP; //marca para eliminação temporária
345 bolhas_encontradas++; //encontrou uma bolha da mesma cor pelo que bolhas_encontradas aumentará em 1
346 //irá correr a mesma função em torno da bolha que encontrou recentemente para que se possa continuar o teste e verificar a existência de caminhos
347 bolhas_encontradas += marcar_bolhas_a_eliminar_vizinhanca(dados, OApontador, i+1, j);
348 }
349 }
350 //---------------------------------------------------------------------testar bolha á direita -----------------------------
351 bolha_a_testar = apbloco_bolha_em_i_j(OApontador, i, j+1, dados);
352 if (bolha_a_testar != NULL)
353 { if (bolha_a_testar->bolha.marca != MARCADA_PARA_ELIMINACAO_TEMP && bolha_a_testar->bolha.marca != MARCADA_PARA_ELIMINACAO &&
354 bolha_a_testar->bolha.cor == bolha_central->bolha.cor)
355 { bolha_a_testar->bolha.marca = MARCADA_PARA_ELIMINACAO_TEMP;
356 bolhas_encontradas++;
357 bolhas_encontradas += marcar_bolhas_a_eliminar_vizinhanca(dados, OApontador, i, j+1);
358 }
359 }
360 //---------------------------------------------------------------------testar bolha em baixo -----------------------------------
361 if (bolha_a_testar != NULL)
362 { if (bolha_a_testar->bolha.marca != MARCADA_PARA_ELIMINACAO_TEMP && bolha_a_testar->bolha.marca != MARCADA_PARA_ELIMINACAO &&
363 bolha_a_testar->bolha.cor == bolha_central->bolha.cor)
364 { bolha_a_testar->bolha.marca = MARCADA_PARA_ELIMINACAO_TEMP;
365 bolhas_encontradas++;
366 bolhas_encontradas += marcar_bolhas_a_eliminar_vizinhanca(dados, OApontador, i-1, j);
367 }
368 }
369 //---------------------------------------------------------------------testar bolha á esquerda ----------------------------------
370 bolha_a_testar = apbloco_bolha_em_i_j(OApontador, i, j-1, dados);
371 if (bolha_a_testar != NULL)
372 { if (bolha_a_testar->bolha.marca != MARCADA_PARA_ELIMINACAO_TEMP && bolha_a_testar->bolha.marca != MARCADA_PARA_ELIMINACAO &&
373 bolha_a_testar->bolha.cor == bolha_central->bolha.cor)
374 { bolha_a_testar->bolha.marca = MARCADA_PARA_ELIMINACAO_TEMP;
375 bolhas_encontradas++;
376 bolhas_encontradas += marcar_bolhas_a_eliminar_vizinhanca(dados, OApontador, i, j-1);
377 }
378 }
379 return bolhas_encontradas;
380 }
381
382 /*
383 Função que corre todas as bolhas do jogo e marca para eliminação permanente todas as que estejam marcadas para eliminação temporária
384 Recebe: os dados do jogo
385 o apontador de acesso a toda a informação das bolhas
386 */
387 void marca_bolhas_candidatas_eliminacao_permanente(DADOS dados, APBLOCO * OApontador)
388 {
389 for (int j = 0; j < dados.larguraJanela; ++j)//verifica todas as colunas
390 {
391 APBLOCO bolhaAtual = *(OApontador +j);
392 while(bolhaAtual != NULL)
393 {
394 if (bolhaAtual == NULL)
395 break;
396 if(bolhaAtual->bolha.marca == MARCADA_PARA_ELIMINACAO_TEMP)//se encontrar bolha marcada para eliminação temporária
397 bolhaAtual->bolha.marca = MARCADA_PARA_ELIMINACAO;//marca para eliminação permanente
398
399 bolhaAtual = bolhaAtual->prox;
400 }
401 }
402 }
403
404 /*
405 Função que corre todas as bolhas do jogo e anula a marca de todas as bolhas que estejam marcadas para eliminação temporária
406 Recebe: os dados do jogo
407 o apontador de acesso a toda a informação das bolhas
408 */
409 void marca_bolhas_candidatas_a_0(DADOS dados, APBLOCO * OApontador)
410 {
411 for (int j = 0; j < dados.larguraJanela; ++j)//percorre as linhas
412 {
413 APBLOCO bolhaAtual = *(OApontador +j);
414 while(bolhaAtual != NULL)
415 {
416 if (bolhaAtual == NULL)
417 break;
418 if(bolhaAtual->bolha.marca == MARCADA_PARA_ELIMINACAO_TEMP)//se encontrar bolha marcada para eliminação temporária
419 bolhaAtual->bolha.marca = 0;//anula a marca
420
421 bolhaAtual = bolhaAtual->prox;
422 }
423 }
424 }
425
426 /*
427 Função que elimina todas as bolhas que estejam marcadas para eliminação permante e que marca todas as bolhas acima de uma bolha
428 eliminada como tendo sofrido alteração para que possa, posteriormente, ser verificado se essa eliminação formou novos caminhos.
429 Recebe: os dados do jogo
430 o apontador de acesso a toda a informação das bolhas
431 Retorna: o numero de bolhas eliminadas e que será usado na contagem dos pontos do jogador
432 */
433 int elimina_bolhas_marcadas(DADOS dados, APBLOCO * OApontador)
434 {
435 int bolhas_rebentadas_total = 0; //variável que guardará o número de bolhas rebentadas
436
437 for (int j = 0; j < dados.larguraJanela; ++j)//verifica todas as colunas
438 {
439 APBLOCO step = NULL, anterior = NULL; //variáveis para correr todas as bolhas
440 APBLOCO apaga; //variável para guardar a bolha a eliminar
441 int eliminou = FALSE; //variável que verifica se foram eliminadas bolhas numa coluna em particular
442
443 step = *(OApontador +j);
444 while(step != NULL)//percorre as colunas
445 {
446 if (step->bolha.marca == MARCADA_PARA_ELIMINACAO)
447 {
448 //se a bolha é marcada para eliminação é feito free do bloco e reorganização da lista
449 apaga = step;
450 step = step->prox;
451 free(apaga);
452 bolhas_rebentadas_total++;
453 eliminou = TRUE; //peo menos uma bolha foi eliminada na coluna j
454
455 if (anterior == NULL) //se a bolha eliminada é a primeira da lista, é necessário alterar os endereços do vetor dinâmico
456 *(OApontador +j) = step;
457 else
458 anterior->prox = step;
459 }
460 /*todas as bolhas acima de uma bolha que tenha sido eliminada numa coluna têm que ser marcadas com tendo sofrido alteração.
461 Assim é possível verificar novamente se a eliminação de algumas bolhas formou novos caminhos*/
462 else
463 {
464 if (eliminou == TRUE) //se já foi eliminada uma bolha nesta coluna
465 step->bolha.marca = SOFREU_ALTERACAO;//marcada como tendo sofrido ateração
466 anterior = step;
467 step = step->prox;
468 }
469 }
470 }
471
472 return bolhas_rebentadas_total;
473 }
474
475 /*
476 Função que no modo fixe garante que inicialmente não aparecem bolhas a formar já caminhos. Para isso, como no modo fixe todas as
477 bolhas são marcadas como inicialmente terem sofrido alteração, as funções de marcação e eliminação descritas anteriormente podem
478 ser utilizadas.
479 Recebe: os dados do jogo
480 o apontador de acesso a toda a informação das bolhas
481 */
482 void remove_bolhas_iniciais_em_caminhos(DADOS dados, APBLOCO * OApontador)
483 {
484 while(TRUE) //até haver bolhas a eliminar (até ainda haver caminhos)
485 {
486 if(marcar_bolhas_a_eliminar(dados, OApontador)==FALSE) //marca bolhas em caminhos. Se já não existirem caminhos, sai do ciclo
487 break;
488 elimina_bolhas_marcadas(dados, OApontador); //elimina as bolhas
489 adiciona_bolhas(OApontador, dados, FALSE); //como está no modo fixe adiciona bolhas, mas sem animação
490 }
491 }
492
493
494 //------------------------------------------------------------- adição de bolhas --------------------------------------------------
495
496 /*
497 Função que no modo fixe adiciona bolhas para substituir aquelas que foram eliminadas.
498 Recebe: o apontador de acesso a toda a informação das bolhas
499 os dados do jogo
500 o inteiro com_animação que pode tomar os valores TRUE ou FALSE e que indica se a adição de bolhas será feita para haver posterior
501 animação da queda ou não.
502 Assim, a função adiciona bolhas marcando-as de dois modos distintos: marcadas como tendo sofrido alteração para que outra função depois
503 avalie se há novos caminhos formados ou de acordo com a sua posições relativas na sua coluna imaginando-as numa nova matriz acima da janela,
504 mecanismo usado para facilitar a animação da queda.*/
505 void adiciona_bolhas(APBLOCO * OApontador, DADOS dados, int com_animacao)
506 {
507
508 for (int j = 0; j < dados.larguraJanela; ++j) //verifica todas as colunas
509 {
510 APBLOCO step = *(OApontador + j), ultimo = NULL; //variáveis que varrerão as colinas
511 int i = 0; //variável que controlará o número de bolhas da coluna e regulará a adição de novas bolhas
512 while(step != NULL)//qundo step for NULL o ciclo acabará e anterior guardará o endereço da última bolha da coluna
513 {
514 ultimo = step;
515 step = step->prox;
516 i++;
517 }
518
519 for (int k = 0; k < dados.alturaJanela -i; ++k) //cria o número de bolhas necessário para a coluna ficar completa
520 {
521 BOLHA bolhacriada; //cria nova bolha
522 bolhacriada.cor = rand()%9 +1; //aleatório
523
524 /*Para adicionar bolhas sem animação basta marcá-las com tendo sofrido alteração para posteriormente ser avaliado se a formação das
525 bolhas formou novos caminhos, este modo é utilizado apenas ao procurar no inicio do jogo bolhas que façam já caminhos.
526 Se estivermos no modo fixe a forma de fazer com que as bolhas adicionadas caiam é imaginá-las numa outra pseudo-matriz acima
527 da janela de jogo em que as bolhas têm que estar o mais abaixo possível na sua coluna, assim, uma formas de represenatar as suas
528 posições relativas nessa matriz é adicionar ao número ANIMACAO_DESCER a sua posição contada relativamente ao fundo dessa nova matriz
529 imaginária para que posteriormente se possa através dessas posições animar a descida.*/
530 if (com_animacao == FALSE)
531 bolhacriada.marca = SOFREU_ALTERACAO;
532 else
533 bolhacriada.marca = ANIMACAO_DESCER + k;
534
535 APBLOCO atual; //apontador para a nova bolha criada
536 atual = (APBLOCO) calloc(1, sizeof(BLOCO)); //aloca memória para a nova bolha criada
537
538 if(atual == NULL) //testa alocação
539 {
540 printf("Erro de alocação de memória.\n");
541 exit(0);
542 }
543
544 atual->bolha = bolhacriada; //põe bolha no espaço útil do bloco
545 atual->prox = NULL;
546
547 if (ultimo == NULL) //o apontador para o primeiro apontador de cada lista ficará guardado no vetor dinâmico
548 *(OApontador+j)= atual;
549 else
550 ultimo->prox = atual; //criação das ligações da lista dinâmica
551
552 ultimo = atual;
553 }
554 }
555 }
556
557

558 //------------------------------------------------------------- controlo -----------------------------------------------------


559 /*Função que, no modo normal, onde não caem novas bolhas, verifica se há mais hipóteses de jogada.
560 Recebe: o apontador de acesso a toda a informação das bolhas
561 os dados do jogo
562 Retorna: TRUE se for possível continuar ou FALSE se não houver mais possibilidades de jogada
563 Esta função faz conjuntos de colunas da matriz imaginária tal que essas colunas sejam contíguas umas às outras e que os
564 conjuntos sejam separados por colunas em branco (que não são incluidas neles). Assim, é verificada cada porção de bolhas do
565 conjunto entre colunas em branco, pois sabemos que é impossível trocar bolhas entre duas colunas se entre elas existir uma
566 completamente vazia.
567 Desta forma a função varre todas as bolhas em todos os conjuntos e guarda o número de bolhas de cada cor numa matriz que
568 posteriormente será avaliada por uma outra função. Se para alguma cor houver mais de tres bolhas no mesmo conjunto o jogo
569 pode continuar caso contrário esta função retorna FALSE.*/
570 int verifica_estado_do_jogo(APBLOCO * OApontador, DADOS dados)
571 { int extremo_inferior = 0; //variável que guardará o extremo inferior de cada conjunto de bolhas
573 int extremo_superior = 0; //variável que guardará o extremo superior de cada conjunto de bolhas
575 int numero_de_cores[9][2];//matriz para guardar o número de bolhas de cada cor
577 int inicio = TRUE; //flag que indica se é a primeira vez que o loop está a ser feito
579 int j = 0;
580 APBLOCO step_colunas; //variável para correr todas as bolhas da coluna
582 while(TRUE)
583 {
584 if (inicio == FALSE) //se já tiver avaliado um conjunto é necessário encontrar o próximo limite inferior
585 {
586 extremo_superior++;
587 //vai correndo o extremo superior a partir da posição seguinte ao extremo superior da iteração anterior
588 for ( ;extremo_superior < dados.larguraJanela; extremo_superior++)
589 {
590 if (*(OApontador + extremo_superior) != NULL) //se não for uma coluna vazia, é esse o nosso novo extremo inferior
591 {
592 extremo_inferior= extremo_superior;
593 break;
594 }
595 }
596 //se o extremo superior é igual ao limite da janela é o ciclo acaba
597 if (extremo_superior == dados.larguraJanela)
598 break;
599 }
601 inicio = FALSE;
603 memset(numero_de_cores, 0, sizeof(numero_de_cores[0][0]) * 9 * 2); //poe a matriz a 0
604 j = extremo_inferior;
605 //saltita de coluna em coluna até encontrer uma coluna vazia ou acabarem as colunas a verificar
606 for (step_colunas = *(OApontador + j); step_colunas != NULL && j< dados.larguraJanela; j++, step_colunas = *(OApontador +j));
607 extremo_superior = j-1; //atribui essa posição ao extremo superior
609 //não há mai colunas a procurar
610 if (extremo_superior >= dados.larguraJanela || extremo_inferior >= dados.larguraJanela)
611 break;
612 //se a primeira coluna estiver vazia isto acontece, pelo que queremos procurar na coluna seguinte
613 else if (extremo_superior < extremo_inferior)
614 continue;
615 //conseguiu arranjar um conjunto válido
616 else
617 {
618 for (; extremo_inferior <= extremo_superior; extremo_inferior++) //varre todas as bolhas do conjunto
619 {
620 APBLOCO step;
621 step = *(OApontador + extremo_inferior);
623 for (; step != NULL; step = step->prox)
624 {
625 numero_de_cores[step->bolha.cor-1][1]++; //vai atualizando a matriz
626 }
627 }
628 }
630 if(avalia_matriz(numero_de_cores) == 1) //dá a matriz a uma outra função para avaliar se naquele conjunto é possivel formar caminhos
631 return TRUE; //se sim retorna TRUE e não é necessário avaliar os restantes conjuntos pois é possível já em um
632 }
633
634 return FALSE;
635 }
637 /*
638 Função que avalia a matriz da quantidade de bolhas de cada cor e se alguma for maior que dois retorna TRUE
639 Recebe: a matriz com o número debolhas de cada cor
640 Retorna: TRUE se houver mais de duas bolhas de alguma cor
641 FALSE no caso contrário
642 */
643 int avalia_matriz(int numero_de_cores[9][2])
644 {
645 for (int i = 0; i < 9; ++i) //corre toda a coluna
646 {
647 if (numero_de_cores[i][1] > 2)//se algum é maior de dois
648 return TRUE;
649 }
650
651 return FALSE;
652 }
653
654 /*
655 Função que liberta a memória ocupada pelas bolhas e pelo vetor dinâmico
656 Recebe: os dados do jogo
657 o apontador de acesso a toda a informação das bolhas
658 Retorna: NULL que será atribuido a OApontador (sgnifica que já não há bolhas acessíveis)
659 */
660 APBLOCO * byebye_listas(DADOS dados, APBLOCO * OApontador)
661 {
662 APBLOCO step, anterior;
663 for (int j = 0; j < dados.larguraJanela ; ++j) //verifica todas as colunas
664 {
665 anterior = *(OApontador + j);
666 if (anterior == NULL) //a coluna não tem bolhas logo passa à coluna seguinte
667 continue;
668
669 for (step = anterior->prox; step != NULL; step = step->prox) //começa em baixo e vai libertando a memória das bolhas ao longo da coluna
670 {
671 free(anterior);
672 anterior = step;
673 }
674 free(anterior); //desaloca a última bolha
675
676 }
677
678 free(OApontador); //desaloca o vetor dinâmico
679 return NULL;
680 }
681
682 //------------------------------------------------protótipos das funções de debug sobre operações de listas -----------------------
683
684 /*
685 Função que entre operações de listas para a execução do programa até que o jogador carregue em ENTER
686 Recebe: string com o motivo da paragem
687 */
688 void DEBUG(char str[])
689 {
690 printf("Enter para %s", str);
691 getchar();
692 }
693

694 /*
695 Função que imprime todas as bolhas na matriz, evidenciando toda a sua informação, endereço, cor e marca
696 Recebe: os dados do jogo
697 o apontador de acesso a toda a informação das bolhas
698 */
699 void estado(DADOS dados, APBLOCO * OApontador)
700 {
701 printf("----------------------------------------------\n");
702 for (int j = 0; j < dados.larguraJanela; ++j)//percorre todas as colunas
703 {
704 int i = 0;
705 APBLOCO bolhaAtual = *(OApontador +j);
706 while(bolhaAtual != NULL)
707 {
708 printf("%p", (void *) bolhaAtual); fflush(stdout);
709 printf("(%d,%d) c:%d m:%d | ", i, j, bolhaAtual->bolha.cor, bolhaAtual->bolha.marca); fflush(stdout);
710 bolhaAtual = bolhaAtual->prox;
711 i++;
712 }
713 printf("\n");
714 fflush(stdout);
715 }
716 printf("----------------------------------------------\n");
717 }
1 /*
2 Ficheiro graphics.h
3 Header que serve de apoio ao ficheio graphics.c e que tornará possivel que outros ficheiros acedam às funções que permitem
4 desenhar as bolhas e a interface gráfica bo ecrã
5 */
6
7 //----------------------------------------------------------------------INCLUDES-----------------------------------------------
8 //incluir funções da biblioteca gráfica
9 #include <SDL.h>
10 #include <SDL2_gfxPrimitives.h>
11 //e outras que permitem fazer operações entre listas e que já inclui o header base
12 #include "op_listas.h"
13
14 //----------------------------------------------------------------------MACROS--------------------------------------------------
15 #define PI 3.141592654
16 #define DELAY 400 //em modo que não seja o de debug há um pequeno intervalo de espera para que se perceba o que está a acontecer
17
18 //----------------------------------------------------------------------PROTÓTIPOS----------------------------------------------
19 //funções de controlo
20 void iniciarSDL2(DADOS dados);
21 void reset(DADOS dados, APBLOCO * OApontador);
22 void sairSDL2();
23
24 //funções de desenho na janela
25 void atualizaJanela(DADOS dados, APBLOCO * OApontador);
26 void desenha_butoes(DADOS dados);
27 void corBolha(int codigoCor, int cor[]);
28 void pinta_mais(int mais_x, int mais_y, DADOS dados, APBLOCO * OApontador);
29 void pinta_X(int mais_x, int mais_y, DADOS dados, APBLOCO * OApontador);
30
31 //funções de posição na janela e deteção de cliques
32 int deteta_clique(DADOS dados, APBLOCO * OApontador);
33 int CoordenadasMatriztransformaParaRefJanela(DADOS dados, int coordenada, int eixo);
34 float distancia(int x1, int y1 , int x2, int y2);
1 /*
2 Ficheiro graphics.c
3 Este é o ficheiro onde estão definidas todas as funções que contribuem para a atualização da componente gráfica do jogo
4 Estão também incluídas neste ficheiro as funções que permitem iniciar e encerrar a janela, desenhar as marcas de troca e
5 eliminação das bolhas e detetar a posição do clique do jogador.
6 */
7
8 //----------------------------------------------------------------------INCLUDES------------------------------------------------
9 //incluir o header com protótipos das funções, macros e outros ficheiros com funções que necessitaram de ser usadas
10 #include "graphics.h"
11
12 //--------------------------------------------------------------------VARIÁVEIS GLOBAIS-----------------------------------------
13 //variáveis de acesso à janela e ao renderer
14 SDL_Window* g_pWindow = NULL;
15 SDL_Renderer* g_pRenderer = NULL;
16
17 //----------------------------------------------------------------------FUNÇÕES--------------------------------------------------
18
19 //----------------------------------------controlo------------------------------------------
20 /*
21 Função que inicia a bibioteca gráfica e que abre a janela que servirá de suporte ao jogo
22 Recebe: os dados do programa para poder dimensionar corretamente a janela
23 */
24 void iniciarSDL2(DADOS dados)
25 {
26 if(SDL_Init(SDL_INIT_EVERYTHING) >= 0)
27 {
28 //criar a janela
29 g_pWindow = SDL_CreateWindow("Bubble Trouble", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, dados.windowSizeX,
30 dados.windowSizeY,SDL_WINDOW_SHOWN);
31 //criar o renderer se a janela foi aberta com sucesso
32 if(g_pWindow != 0)
33 g_pRenderer = SDL_CreateRenderer(g_pWindow, -1, 0);
34 else
35 {
36 printf("Erro na iniciação da biblioteca gráfica\n");
37 exit(0);
38 }
39 }
40 else
41 {
42 printf("Erro na iniciação da biblioteca gráfica\n");
43 exit(0);
44 }
45 SDL_SetRenderDrawColor(g_pRenderer, 255, 255, 255, 255); //pintar o background com branco
46 SDL_RenderClear(g_pRenderer); //limpar a janela com branco
47 }
48
49 /*
50 Função que limpa a janela e a desenha já com as alterações feitas posteriormente
51 Recebe: os dados do jogo
52 o apontador de acesso a toda a informação relativa às bolhas
53 */
54 void reset(DADOS dados, APBLOCO * OApontador)
55 {
56 SDL_SetRenderDrawColor(g_pRenderer, 255, 255, 255, 255);
57 SDL_RenderClear(g_pRenderer); //limpar a janela com branco
58 atualizaJanela(dados, OApontador); //desenha as bolhas na janela já atualizadas
59 SDL_RenderPresent(g_pRenderer); //atualiza o renderer
60 }
61
62 /*
63 Função que termina a utilização da biblioteca gáfica destruindo o renderer e a janela
64 */
65 void sairSDL2()
66 {
67 SDL_DestroyWindow(g_pWindow);
68 SDL_HideWindow(g_pWindow);
69 SDL_DestroyRenderer(g_pRenderer);
70 SDL_Quit();
71 }
72
73
74 //----------------------------------------desenho------------------------------------------
75
76
77 /*
78 Função que desenha os botões e as bolhas em jogo
79 Recebe: os dados do jogo
80 o apontador de acesso a toda a informação relativa às bolhas
81 */
82 void atualizaJanela(DADOS dados, APBLOCO * OApontador)
83 {
84
85 desenha_butoes(dados); //desenha butões e pontuação
86 for (int j = 0; j < dados.larguraJanela; ++j)//todas as colunas
87 {
88
89 APBLOCO bolhaAtual = *(OApontador + j);
90 int i = 0; //coordenada vertical
91
92 while(bolhaAtual != NULL)
93 {
94 if (bolhaAtual == NULL)
95 break;
96 int cor[3]; //variavel que guarda a cor da bolha
97 corBolha(bolhaAtual->bolha.cor, cor);//a partir do código da cor que está na matriz, dá valores de rgb vetor cor
98
99 //desenha a bolha depois de transformadas as coordenas da matriz para o referencial (em pixeis) da janela
100 int pos_janela_x = CoordenadasMatriztransformaParaRefJanela(dados, j, EIXO_X);
101 int pos_janela_y = CoordenadasMatriztransformaParaRefJanela(dados, i, EIXO_Y);
102 filledCircleRGBA(g_pRenderer, pos_janela_x, pos_janela_y, dados.dimBolha/2.0, cor[0], cor[1], cor[2],255);
103 circleRGBA(g_pRenderer, pos_janela_x, pos_janela_y, (dados.dimBolha/2.0), 0, 0, 0,255);
104
105 if (bolhaAtual->bolha.marca == MARCADA_PARA_TROCA) //as bolhas que serão trocadas são marcadas com um mais
106 pinta_mais(pos_janela_x, pos_janela_y, dados, OApontador);
107 //no modo de debug as bolhas que serão eliminadas serão pintadas com um X
108 else if(dados.debug == TRUE && bolhaAtual->bolha.marca == MARCADA_PARA_ELIMINACAO)
109 pinta_X(pos_janela_x, pos_janela_y, dados, OApontador);
110
111 bolhaAtual = bolhaAtual->prox;
112 i++;
113 }
114 }
115 }
116
117 /*
118 Função que desenha os botões e pontuação do jogador na janela
119 Recebe: os dados do programa
120 */
121 void desenha_butoes(DADOS dados)
122 {
123 SDL_SetRenderDrawColor(g_pRenderer, 0, 0, 205, 255); //selecionar a cor azul para desenhar linhas
124 char strPontos[20]=""; //string para imprimir no ecrã a pontuação
125 //desenhar a linha superiror
126 SDL_RenderDrawLine( g_pRenderer,0, dados.altura_barra_superior, dados.windowSizeX, dados.altura_barra_superior);
127 //Botao novo jogo
128 SDL_RenderDrawLine( g_pRenderer,0.02*dados.windowSizeX, 0.25*dados.dimBolha, 0.20*dados.windowSizeX, 0.25*dados.dimBolha);
129 SDL_RenderDrawLine( g_pRenderer,0.02*dados.windowSizeX, 0.25*dados.dimBolha, 0.02*dados.windowSizeX, 1.25*dados.dimBolha);
130 SDL_RenderDrawLine( g_pRenderer,0.02*dados.windowSizeX, 1.25*dados.dimBolha, 0.20*dados.windowSizeX, 1.25*dados.dimBolha);
131 SDL_RenderDrawLine( g_pRenderer,0.20*dados.windowSizeX, 0.25*dados.dimBolha, 0.20*dados.windowSizeX, 1.25*dados.dimBolha);
132 if(dados.windowSizeX > LARGURA_MINIMA1) //escreve novo jogo numa linha se a largura da janela for superior a um determinado valor critico
133 stringRGBA(g_pRenderer, 0.11*dados.windowSizeX-37, 0.75*dados.dimBolha-4, "Novo Jogo", 0,0,0, 255);
134 else //se a largura for inferior a um valor critico imprime as duas palavras na vertical
135 {
136 stringRGBA(g_pRenderer, 0.20*dados.windowSizeX-30, 0.50*dados.dimBolha-4, "Novo", 0,0,0, 255);
137 stringRGBA(g_pRenderer, 0.20*dados.windowSizeX-30, 0.90*dados.dimBolha-4, "Jogo", 0,0,0, 255);
138 }
139
140 //desenha o botão fim
141 SDL_RenderDrawLine( g_pRenderer,0.25*dados.windowSizeX, 0.25*dados.dimBolha, 0.37*dados.windowSizeX, 0.25*dados.dimBolha);
142 SDL_RenderDrawLine( g_pRenderer,0.25*dados.windowSizeX, 0.25*dados.dimBolha, 0.25*dados.windowSizeX, 1.25*dados.dimBolha);
143 SDL_RenderDrawLine( g_pRenderer,0.25*dados.windowSizeX, 1.25*dados.dimBolha, 0.37*dados.windowSizeX, 1.25*dados.dimBolha);
144 SDL_RenderDrawLine( g_pRenderer,0.37*dados.windowSizeX, 0.25*dados.dimBolha, 0.37*dados.windowSizeX, 1.25*dados.dimBolha);
145 stringRGBA(g_pRenderer, 0.31*dados.windowSizeX-12, 0.75*dados.dimBolha-4, "Fim", 0,0,0, 255);
146
147 // desenha o quadro da pontuação e imprime os pontos do jogador
148 SDL_RenderDrawLine( g_pRenderer,0.60*dados.windowSizeX, 0.25*dados.dimBolha, 0.98*dados.windowSizeX, 0.25*dados.dimBolha);
149 SDL_RenderDrawLine( g_pRenderer,0.60*dados.windowSizeX, 0.25*dados.dimBolha, 0.60*dados.windowSizeX, 1.25*dados.dimBolha);
150 SDL_RenderDrawLine( g_pRenderer,0.60*dados.windowSizeX, 1.25*dados.dimBolha, 0.98*dados.windowSizeX, 1.25*dados.dimBolha);
151 SDL_RenderDrawLine( g_pRenderer,0.98*dados.windowSizeX, 0.25*dados.dimBolha, 0.98*dados.windowSizeX, 1.25*dados.dimBolha);
152 stringRGBA(g_pRenderer, dados.windowSizeX*0.62, 0.75*dados.dimBolha-4, "Pontos:", 0,0,0, 255);
153 sprintf(strPontos, "%d", dados.pontos); //imprime numa string os pontos do utilizador
154 stringRGBA(g_pRenderer, dados.windowSizeX*0.62 + 56, 0.75*dados.dimBolha-4, strPontos, 0,0,0, 255); //imprime os pontos
155 }
156
157 /*
158 Função que desenha um + na bolha que cujas coordenadas recebe
159 Recebe: as coordenadas da bolha onde é para imprimir o +, mais_x e mais_y
160 os dados do programa
161 o apontador de acesso a toda a informação relativa às bolhas
162 */
163 void pinta_mais(int mais_x, int mais_y, DADOS dados, APBLOCO * OApontador)
164 {
165 SDL_SetRenderDrawColor(g_pRenderer, 255, 179, 0, 255);
166 SDL_RenderDrawLine(g_pRenderer, mais_x - (int) (0.6 * dados.dimBolha/2), mais_y, mais_x + (int) (0.6 * dados.dimBolha/2), mais_y);
167 SDL_RenderDrawLine(g_pRenderer, mais_x, mais_y-(int) (0.6 * dados.dimBolha/2), mais_x, mais_y+(int) (0.6 * dados.dimBolha/2));
168 }
169
170 /*
171 Função que desenha um X na bolha que cujas coordenadas recebe
172 Recebe: as coordenas da bolha onde é para imprimir o +, mais_x e mais_y
173 os dados do programa
174 o apontador de acesso a toda a informação relativa às bolhas
175 */
176 void pinta_X(int mais_x, int mais_y, DADOS dados, APBLOCO * OApontador )
177 {
178 SDL_SetRenderDrawColor(g_pRenderer, 255, 179, 0, 255);
179 SDL_RenderDrawLine(g_pRenderer, mais_x - (int) (0.6 * dados.dimBolha/2 * cos(PI/4)), mais_y - (int) (0.6 * dados.dimBolha/2 * sin(PI/4)),
180 mais_x + (int) (0.6 * dados.dimBolha/2 * cos(PI/4)), mais_y + (int) (0.6 * dados.dimBolha/2 * sin(PI/4)));
181 SDL_RenderDrawLine(g_pRenderer, mais_x - (int) (0.6 * dados.dimBolha/2 * cos(PI/4)), mais_y + (int) (0.6 * dados.dimBolha/2 * sin(PI/4)),
182 mais_x + (int) (0.6 * dados.dimBolha/2 * cos(PI/4)), mais_y - (int) (0.6 * dados.dimBolha/2 * sin(PI/4)));
183 }
184
185
186 //----------------------------------------posição na janela e deteção de clique------------------------------------------
187
188 /*
189 Função que verifica se o utilizador carregou nos botões ou nas bolhas
190 No caso de ter caregado numa bolha, marca-a para troca se não estiver marcada, caso contrário desmarca-a e retorna -1
191 Retorna: 0 se nao carregou em nada
192 3 se carrega no botao fim do jogo
193 4 se carrega no botao novo jogo
194 1 se carregou numa bolha e a marcou com sucesso
195 -1 se carregou na bolha que estava já marcada
196 */
197 int deteta_clique(DADOS dados, APBLOCO * OApontador)
198 {
199
200 int mouse_pos[2];
201 SDL_GetMouseState(&mouse_pos[0], &mouse_pos[1]); //deteta a posição do rato
202 if(mouse_pos[0]>= 0.02*dados.windowSizeX && mouse_pos[0]<=0.20*dados.windowSizeX &&
203 mouse_pos[1]>= 0.25*dados.dimBolha && mouse_pos[1]<=1.25*dados.dimBolha) //limites do botão novo jogo
204 return 4;
205 else if(mouse_pos[0]>= 0.25*dados.windowSizeX && mouse_pos[0]<=0.37*dados.windowSizeX &&
206 mouse_pos[1]>= 0.25*dados.dimBolha && mouse_pos[1]<=1.25*dados.dimBolha) //limites do botão fim do jogo
207 return 3;
208
209 for (int j = 0; j < dados.larguraJanela; ++j)
210 {
211 int i = 0;
212 APBLOCO bolha_carregada = *(OApontador + j);
213 //Corre todas as bolhas em jogo até encontrar uma cujo centro diste de menos de um raio em relação ao sítio onde o utilizador carregou
214 while(bolha_carregada != NULL)
215 {
216 int centro_x = CoordenadasMatriztransformaParaRefJanela(dados, j, EIXO_X);
217 int centro_y = CoordenadasMatriztransformaParaRefJanela(dados, i, EIXO_Y);
218 //se a distancia for inferior a um raio
219 if (dados.fixe == FALSE && bolha_carregada != NULL && distancia(centro_x, centro_y, mouse_pos[0], mouse_pos[1]) <= dados.dimBolha/2)
220 {
221 if (bolha_carregada->bolha.marca != MARCADA_PARA_TROCA)
222 {
223 bolha_carregada->bolha.marca = MARCADA_PARA_TROCA; //marca para troca
224 return 1;
225 }
226 else
227 {
228 bolha_carregada->bolha.marca = 0; // a bolha encontrada estava já marcada
229 return -1;
230 }
231 }
232 bolha_carregada = bolha_carregada->prox;
233 i++;
234 }
235 }
236 return 0;
237 }
238

239 /*Função que recebe uma cor sob a forma de um codiogo (1-9) e converte esse codigo para rgb que atribui ao vetor cor que recebe*/
240 void corBolha(int codigoCor, int cor[])
241 {
242 switch(codigoCor) //valor do código de cor
243 {
244 case 1: //vermelho
245 cor[0]=255;
246 cor[1]=0;
247 cor[2]=0;
248 break;
249 case 3://azul
250 cor[0]=0;
251 cor[1]=0;
252 cor[2]=255;
253 break;
254 case 2://roxo
255 cor[0]=128;
256 cor[1]=0;
257 cor[2]=128;
258 break;
259 case 4://cyan
260 cor[0]=0;
261 cor[1]=255;
262 cor[2]=255;
263 break;
264 case 5://verde
265 cor[0]=50;
266 cor[1]=205;
267 cor[2]=50;
268 break;
269 case 6://amarelo
270 cor[0]=255;
271 cor[1]=255;
272 cor[2]=0;
273 break;
274 case 7: //castanho
275 cor[0]=139;
276 cor[1]=69;
277 cor[2]=19;
278 break;
279 case 8: //preto
280 cor[0]=0;
281 cor[1]=0;
282 cor[2]=0;
283 break;
284 case 9: //branco
285 cor[0]=255;
286 cor[1]=255;
287 cor[2]=255;
288 break;
289 }
290 }
291

292 /*
293 Função que transforma coordenas da matriz para coordenadas da janela
294 Recebe o valor da coordenada e indicação de que eixo é que essa coordenada se refere (X, Y) e retorna o valor dessa coordenada no referencial da janela
295 */
296 int CoordenadasMatriztransformaParaRefJanela( DADOS dados, int coordenada, int eixo)
297 {
298 if (eixo == EIXO_X)
299 return (int) (coordenada+1)*(dados.dl)*dados.dimBolha + (dados.dimBolha/2.0)*(2*coordenada+1); //equação para os xx
300 else if (eixo == EIXO_Y)
301 return (int) (dados.windowSizeY - ((coordenada +1)*(dados.dl)*dados.dimBolha + (dados.dimBolha/2.0)*(2*coordenada+1)
302 + 1.5*dados.dimBolha ) + dados.altura_barra_superior ); // equação para os yy
303 return 0;
304 }
305
306 /*
307 Função que recebe as coordenas de dois pontos num referencial cartesiano e retorna a distância entre eles
308 */
309 float distancia(int x1, int y1 , int x2, int y2)
310 {
311 return sqrt( (float)(x1-x2)*(x1-x2) + (float)(y1-y2)*(y1-y2));
312 }
1 /*
2 Ficheiro leitura_dados.h
3 Header que serve de apoio ao ficheiro leitura_dados.c e que tornará possivel que outros ficheiros acedam às funções que permitem
4 interpretar as indicações dadas pelo jogador no terminal.
5 */
6 //----------------------------------------------------------------------INCLUDES------------------------------------------------
7 #include "header_base.h"
8 //gerar números "aleatórios"
9 #include <time.h>
10
11 //----------------------------------------------------------------------PROTÓTIPOS---------------------------------------------
12 int lerConfigDados(DADOS * dados, int argc, char const * argv[]);
13 void mensagemErroDados();
1 /*
2 Ficheiro leitura_dados.c
3 Este é o ficheiro onde estão definidas as funções relacionas com a leitura dos dados introduzidos pelo jogador no terminal
4 */
5
6 //----------------------------------------------------------------------INCLUDES-----------------------------------------------
7 //incluir o header com protótipos das funções, macros e outros ficheiros com funções que necessitaram de ser usadas
8 #include "leitura_dados.h"
9
10 //----------------------------------------------------------------------FUNÇÕES------------------------------------------------
11
12 /*Função que imprime uma mensagem de erro no caso de os dados term sido mal introduzidos pelo utilizador no terminal*/
13 void mensagemErroDados()
14 {
15 printf("\nDados inválidos.\n\n");
16 printf("Ao correr o programa introduza os dados da seguinte forma:\n");
17 printf("troca _bolhas -l 13 -a 13 -b 45 -f\n");
18 printf("Não necessariamente nesta ordem :)\n");
19 printf("Para uma melhor experiencia recomenda-se o modo fixe! (flag -f)\n");
20 printf("Neste modo a troca das bolhas é feita arrastando-as!\n");
21 exit(0);
22 }
23

24 /*Função que lê os dados introduzidos pelo utilizador na linha de comandos e guarda os dados na estrutura apropriada
25 É também responsável pela verificação de que os dados introduzidos são válidos.
26 Esta função recebe, por endereco, a variável que contém os dados do programa, o número de argumentos recebidos, e o vetor de
27 apontadores para as strings introduzidas
28 Se os dados forem inválidos a função termina o programa*/
29 int lerConfigDados(DADOS * dados, int argc, char const * argv[])
30 { dados->larguraJanela = 0; //inicializar valores
31 dados->alturaJanela = 0;
32 dados->dimBolha = 0;
33 dados->debug = FALSE;
34 dados->fixe = FALSE;
35 for (int i = 1; i < argc; ++i)
36 { if(strcmp(argv[i], "-l") == 0) //ler largura da janela em bolhas
37 { if(i+1 < argc && sscanf(argv[i+1], "%d", &(dados->larguraJanela)) != 1)
38 return 1;
39 i++;
40 }
41 else if(strcmp(argv[i], "-d") == 0) //modo de debugging
42 dados->debug = TRUE;
43 else if(strcmp(argv[i], "-b") == 0) //diametro da bolha em pixeis
44 { if(i+1 < argc && sscanf(argv[i+1], "%d", &(dados->dimBolha)) != 1)
45 return 1;
46 i++;
47 }
48 else if(strcmp(argv[i], "-f") == 0) //modo fixe
49 dados->fixe = TRUE;
50 else if(strcmp(argv[i], "-a") == 0) //ler altura da janela em bolhas
51 { if(i+1 < argc && sscanf(argv[i+1], "%d", &(dados->alturaJanela)) != 1)
52 return 1;
53 i++;
54 }
55 else
56 { printf("Erro: flag '%s' não definida.\n", argv[i]); //se o utilizador escreveu uma flag que não as acima
57 return 1;
58 }
59 }
60 if(dados->larguraJanela == 0 || dados->alturaJanela == 0 || dados->dimBolha == 0) //verificar que todos os valores foram lidos
61 return 1;
62 dados->dl = DL; //inicializar variáveis restantes
63 dados->pontos = 0;
64 dados->altura_barra_superior = 1.5*(dados->dimBolha);
65 dados->windowSizeX = round(dados->dimBolha * dados->dl * ((dados->larguraJanela) +1) + dados->larguraJanela * dados->dimBolha);
//calcula as dimensoes x e y da janela
66 dados->windowSizeY= round(dados->dimBolha * dados->dl * ((dados->alturaJanela) +1) + dados->alturaJanela * dados->dimBolha
67 + dados->altura_barra_superior);
68 //----------------------------------------------------limitar valores----------------------------------------------
69 if(dados->larguraJanela>= 100 || dados->alturaJanela>=100){
70 printf("Erro: Número excessivo de bolhas\n");
71 exit(0);
72 }
73 else if(dados->larguraJanela<= 0 || dados->alturaJanela<=0 || dados->dimBolha <= 0){
74 printf("Erro: um ou mais dados adotam valores inválidos (nulos ou negativos).\n");
75 exit(0);
76 }
77 else if(dados->dimBolha>50){
78 printf("Erro: o raio da bolha não pode ser superior a 50px.\n");
79 exit(0);
80 }
81 else if(dados->windowSizeX < LARGURA_MINIMA) {
82 printf("Erro: Janela demasiado pequena, aumente o valor de alguns dos parâmetros\n");
83 exit(0);
84 }
85 else if(dados->windowSizeY > ALTURA_MAXIMA) {
86 printf("Erro: Janela demasiado grande, diminua o valor de alguns dos parâmetros\n");
87 exit(0);
88 }
89 else if(dados->windowSizeX > LARGURA_MAXIMA) {
90 printf("Erro: Janela demasiado grande, diminua o valor de alguns dos parâmetros\n");
91 exit(0);
92 }
93 if (dados->debug == TRUE) //seed para aleatórios
94 srand(73);
95 else
96 srand(time(NULL));
97 return 0;
98 }
1 /*
2 Ficheiro resultados.h
3 Header que serve de apoio ao ficheiro resultados.c e que tornará possível que outros ficheiros acedam às funções que guardam a
4 pontuação do jogador no ficheiro "resultados .txt"
5 */
6
7 //----------------------------------------------------------------------INCLUDES-----------------------------------------------
8 #include "header_base.h" //funções básicas
9
10 //----------------------------------------------------------------------MACROS--------------------------------------------------
11 #define MAX_DIM_MATRIZ_RESULTADOS 100
12
13 //----------------------------------------------------------------------PROTÓTIPOS----------------------------------------------
14
15 void fimDoJogo(int pontos);
16 void bubleSortExtracaoFichResultados(int linha, int pontos[MAX_DIM_MATRIZ_RESULTADOS], char nome[MAX_DIM_MATRIZ_RESULTADOS][23]);
17 int guardaNovamenteNoficheiroResultados(int linha, int pontosFichResultados[MAX_DIM_MATRIZ_RESULTADOS],
18 char nomeDoJogadorFichResultados[MAX_DIM_MATRIZ_RESULTADOS][23]);
2 /*Ficheiro resultados.c
3 Este é o ficheiro onde estão definidas todas as funções que são utilizadas para guardar a pontuação do jogador no ficheiro
4 "resultados.txt"*/
6 //----------------------------------------------------------------------INCLUDES------------------------------------------------
7 #include "resultados.h"
9 //----------------------------------------------------------------------FUNÇÕES--------------------------------------------------
10 /*Função que, no final do jogo, pede o nome ao utilizador e guarda o seu nome, associado à pontuação obtida no jogo, no ficheiro
12 resultados.txt
13 Esta função está, também, responsável pela ordenação do ficheiro de resultados, fazendo uso de outras funções
14 Recebe como argumento os pontos que o utilizador obteve no jogo em questão
15 A função sai do programa em caso de invalidade do ficheiro de resultados.
16 */
17 void fimDoJogo(int pontos)
18 {
19 char nomeDoJogador[22]= ""; //string que reseberá o nome do utilizador
20 printf("Introduza o nome do jogador (max 20 ch): " );
21 fflush(stdout);
22 if(scanf("%20s", nomeDoJogador)!=1) //extrai a string com o nome do jogador
23 {
24 printf("Erro: leitura do nome do jogador não foi efetuada com sucesso.\n");
25 exit(0);
26 }
28 int pontosFichResultados[MAX_DIM_MATRIZ_RESULTADOS]; //vetor que guarda a extração dos dados do ficheiro de resultados
29 //vetor de strings (matriz de caracteres) que guarda o nome do jogador
30 char nomeDoJogadorFichResultados[MAX_DIM_MATRIZ_RESULTADOS][23];
31 char str[51] = ""; //string que será utilizada para ler as linhas e extrair informação do ficheiro de configurações
32 int linha = 0;
33
34 FILE *ficheiroResultados;
35 ficheiroResultados = fopen("resultados.txt", "r"); //abre o ficheiro dos resultados no final e em modo leitura
36
37 if (ficheiroResultados == NULL) //se há um erro na abertura do ficheiro (inexistência na diretoria do programa)
38 {
39 printf("Erro: ficheiro resultados não encontrado\n");
40 FILE *novoFicheiro; //cria um novo ficheiro
41 novoFicheiro = fopen("resultados.txt", "w");
42 fprintf(novoFicheiro, "Jogadores Pontuação\n"); //imprimir o cabeçalho no ficheiro
43 fclose(novoFicheiro);
44 printf("Novo ficheiro criado...\n");
45 }
46 else
47 {
48 fgets(str, 50, ficheiroResultados);//ignora primeira e segunda linhas
49 fgets(str, 50, ficheiroResultados);
50 do
51 {
52 str[0] = '\0';
53 fgets(str, 50, ficheiroResultados); //extrai uma linha
54 //interpreta a linha de onde extrai o nome e a pontuação
55 if(sscanf(str, "%20s %d", nomeDoJogadorFichResultados[linha], &pontosFichResultados[linha]) != 2)
56 {
57 //se o número de conversões não for dois, algo correu mal e o programa termina com erro (retorna -1)
58 printf("Erro: as pontuações alteram-se jogando!\n");
59 fflush(stdout);
60 exit(0);
61 }
62 linha++;
63 }while(feof(ficheiroResultados)==0); //enquanto nao atinge o fim do ficheiro
64 fclose(ficheiroResultados);//fecha o documento
65 }
66 pontosFichResultados[linha] = pontos; //adiciona os pontos e nome do jogador atual aos extraídos do ficheiro
67 strcpy(nomeDoJogadorFichResultados[linha], nomeDoJogador);
68 //organiza as pontuações dos jogadores, partindo do princípio que já estavam organizadas de forma decrescente
69 bubleSortExtracaoFichResultados(linha, pontosFichResultados, nomeDoJogadorFichResultados);
70 //guarda as pontuações organizadas no fichheiro
71 if(guardaNovamenteNoficheiroResultados(linha, pontosFichResultados, nomeDoJogadorFichResultados)==0)
72 exit(0);
73 }
75 /*
76 Função que recebe os vetores da pontuação dos jogadores e os seus nomes em que na ultima posição estão os dados dojogador anterior
77 A partir dessas matrizes organiza os dados de forma decrescente pela pontuação, supondo que os dados estavam já previamnete
78 organizados, utilizando o algoritmo Buble Sort.
79 */
80 void bubleSortExtracaoFichResultados(int linha, int pontos[MAX_DIM_MATRIZ_RESULTADOS], char nome[MAX_DIM_MATRIZ_RESULTADOS][23])
81 {
82 for (int i = linha; i > 0; --i) //percorre os índices do vetor, começando pela nova pontuação
83 {
84 //verifica se a pontuação acima é mais alta, senão é feita uma troca
85 /*seguidamente, se, na posição acima da qual o valor mais recente foi tranferido estiver uma pontuação inferior, é feita uma
86 outra troca e assim sucessivamente*/
87 if (pontos[i]>pontos[i-1]) //se a nova pontuação for superior à de cada casa troca as pontuações e os nomes de ordem
88 {
89 //troca dos pontos entre os indices i e i-1
90 int aux = pontos[i-1];
91 pontos[i-1] = pontos[i];
92 pontos[i] = aux;
93
94 //troca dos nomes entre os indices i e i-1
95 char strAux[23]= "";
96 strcpy(strAux, nome[i-1]);
97 strcpy(nome[i-1], nome[i]);
98 strcpy(nome[i], strAux);
99 }
100 else
101 break; //caso contrário, o vetor está já organizado e é inutil continuar a percorrer as linhas
102 }
103 }
104
105
106 /*
107 Função que recebe as matrizes dos nomes e vetor dos pontos dos jogadores já organizadas de forma decrescente de pontos e que imprime
108 os resultados no ficheiro correspondente
109 Retorna 1: no caso de sucesso
110 0: no caso de insucesso na abertura do ficheiro 'resulttados.txt'
111 */
112 int guardaNovamenteNoficheiroResultados(int linha, int pontosFichResultados[MAX_DIM_MATRIZ_RESULTADOS],
113 char nomeDoJogadorFichResultados[MAX_DIM_MATRIZ_RESULTADOS][23])
114 {
115 FILE * ficheiroResultados;
116 ficheiroResultados = fopen("resultados.txt", "w"); //abre o ficheiro em modo de escrita e apaga o seu conteúdo
117
118 if(ficheiroResultados == NULL) //se for impossível abrir o ficheiro
119 {
120 printf("Erro: a abertura do ficheiro resultados.\n");
121 return 0;
122 }
123
124 fprintf(ficheiroResultados, "Jogadores Pontuação\n"); //imprimir o cabeçalho no ficheiro
125
126 for (int i = 0; i <= linha; ++i)
127 { //imprimir as pontuações já organizadas
128 fprintf(ficheiroResultados, "\n%-20s %07d", nomeDoJogadorFichResultados[i], pontosFichResultados[i]);
129 }
130 printf("Pontuação guardada com sucesso!\n"); //mensagem de sucesso no terminal
131 return 1;
132 }
1 /*
2 Ficheiro animacoes.h
3 Header que serve de apoio ao ficheiro animacoes.c e que tornará possivel que outros ficheiros acedam às funções que permitem
4 criar as animações de descida e eliminação de bolhas
5 */
6 //------------------------------------------------------------------INCLUDES---------------------------------------------------
7
8 //----------------------------------------------------------------------INCLUDES-----------------------------------------------
9 #include "header_base.h" //inclui as funções, macros básicos e variáveis estruturadas base
10 #include "graphics.h" //inclui as funções e macros que permitem desenhar na janela gráfica
11 #include "op_listas.h" //inclui as funções, macros e variáveis estruturadas que operam sobre as listas de bolhas
12
13 //--------------------------------------------------------------------VARIÁVEIS GLOBAIS----------------------------------------
14 extern SDL_Renderer* g_pRenderer; //renderer
15
16 //----------------------------------------------------------------------MACROS------------------------------------------------
17 #define ITERACOES_IMPLOSAO 10
18 #define DELAY_IMPLOSAO 9 //intervalo de tempo entre iterações da implossão (em ms)
19
20 #define DS 0.1 //distância em termos de coordenadas da matriz que uma bolha percorre por iteração de descida
21 #define DELAY_DESCER 20 //intervalo de tempo entre iterações de descida das bolhas
22
23
24 //----------------------------------------------------------------------PROTÓTIPOS----------------------------------------------
25
26 //eliminação de bolhas
27 void atualizaJanela_animacoes_implosao(DADOS dados, APBLOCO * OApontador);
28 int elimina_bolhas_marcadas_animacao(DADOS dados, APBLOCO * OApontador);
29
30 //queda animada de bolhas
31 void adiciona_bolhas_animacao(APBLOCO * OApontador, DADOS dados);
32 void atualizaJanela_animacoes_descer_velhas(DADOS dados, APBLOCO * OApontador);
33 void atualizaJanela_animacoes_descer_novas(DADOS dados, APBLOCO * OApontador);
34 int CoordenadasMatriztransformaParaRefJanela_coordenadas_continuas(DADOS dados, float coordenada, int eixo);
35
36 //troca de bolhas
37 int deteta_clique_troca_fixe(DADOS dados, APBLOCO * OApontador, TROCA_FIXE_INFO * troca_info);
38 void define_direcao(TROCA_FIXE_INFO * troca_info);
39 void mexe_bolhas_troca(DADOS dados, APBLOCO * OApontador, TROCA_FIXE_INFO troca_info);
40 void restringe_ds(DADOS dados, int * ds);
41 void atualizaJanela_animacao_troca(DADOS dados, APBLOCO * OApontador, TROCA_FIXE_INFO troca_info);
42 void normaliza_bolhas_trocadas_animacao(DADOS dados, APBLOCO * OApontador);
43 void neutraliza_marcas_animacao_troca(DADOS dados, APBLOCO * OApontador);
1 /*
2 Ficheiro animacoes.c
3 Este é o ficheiro onde estão definidas todas as funções que contribuem para a animação de algumas componetes do jogo, nomeadamente
4 a descida, eliminação de bolhas e troca de bolhas
5 */
6
7 //----------------------------------------------------------------------INCLUDES------------------------------------------------
8 #include "animacoes.h" //protótipos, funções extra, macros
9
10 //----------------------------------------------------------------------FUNÇÕES-------------------------------------------------
11
12 //------------------------------------------------ eliminação de bolhas --------------------------------------------
13 /*
14 Função que elimina todas as bolhas que estejam marcadas para eliminação permanente e anima essa eliminação com uma implosão
15 Para além disso, marca todas as bolhas acima das bolhas eliminadas com informação referente às posições relativas dessas bolhas
16 em cada coluna.
17 Recebe: os dados do jogo
18 o apontador de acesso a toda a informação das bolhas
19 Retorna: o número de bolhas eliminadas e que será usado na contagem dos pontos do jogador
20 */
21 int elimina_bolhas_marcadas_animacao(DADOS dados, APBLOCO * OApontador)
22 {
23 int bolhas_rebentadas_total = 0; //variável que guarda o número de bolhas que foram rebentadas
24 atualizaJanela_animacoes_implosao(dados, OApontador); //anima a implosão
25
26 for (int j = 0; j < dados.larguraJanela; ++j)//percorre todas as colunas
27 {
28 APBLOCO step = NULL, anterior = NULL; //variáveis para varrer todas as bolhas
29 APBLOCO apaga; //variável que permite desalocar memória sem perder o ponto de acesso a algumas bolhas
30
31 step = *(OApontador +j);
32 int eliminou = FALSE; //variável que indica se já foram eliminadas bolhas na coluna j
33 int i = 0; //variável que guarda a posição relativa das bolhas na coluna
34
35 while(step != NULL)//percorre as linhas
36 {
37 if (step->bolha.marca == MARCADA_PARA_ELIMINACAO)
38 {
39 //liberta o bloco
40 apaga = step;
41 step = step->prox;
42 free(apaga);
43 bolhas_rebentadas_total++;
44 eliminou = TRUE;
45
46 if (anterior == NULL)
47 *(OApontador +j) = step;
48 else
49 anterior->prox = step;
50 }
51 else
52 {
53 /*todas as bolhas acima de uma bolha eliminada serão animadas ao cair, para isso é necessário saber a posição relativa
54 entre elas e que se destaquem das outras marcas com o auxilio da macro ANIMACAO_DESCER*/
55 if (eliminou == TRUE)
56 step->bolha.marca = ANIMACAO_DESCER + i;
57
58 anterior = step;
59 step = step->prox;
60 }
61 i++;
62 }
63 }
64
65 return bolhas_rebentadas_total;
66 }
67
68
69 /*
70 Função que trata da animação da implosão das bolhas
71 Para que as bolhas impludam todas simultaneamente em todas as iterações, todas as bolhas são imprimidas novamente e as marcadas
72 para eliminação com um raio cada vez menor
73 Recebe: os dados do jogo
74 o apontador de acesso a toda a informação das bolhas
75 */
76 void atualizaJanela_animacoes_implosao(DADOS dados, APBLOCO * OApontador)
77 {
78 int iteracoes = -5; //variável responsável pela terminação do ciclo
79 int raio = dados.dimBolha/2; //raio inicial
80
81 do{//até todas as iterações terem sido feitas
82 //limpa a janela
83 SDL_SetRenderDrawColor(g_pRenderer, 255, 255, 255, 255);
84 SDL_RenderClear(g_pRenderer); //limpar a janela com branco
85 desenha_butoes(dados);//desenha botões e pontuação
86
87 raio -= (dados.dimBolha/2)/ITERACOES_IMPLOSAO; //raio vai diminuindo
88
89 for (int j = 0; j < dados.larguraJanela; ++j)//percorre todas as colunas
90 {
91 int i = 0; //coordenada ao logo da coluna
92 APBLOCO bolhaAtual = *(OApontador +j);
93
94 while(bolhaAtual != NULL)
95 {
96 if (bolhaAtual == NULL)
97 break;
98 int cor[3];//variável que guarda a cor da bolha
99 corBolha(bolhaAtual->bolha.cor, cor);//a partir do código da cor que está na matriz, dá valores de rgb ao vetor cor
100 //desenha a bolha depois de transformadas as coordenas da matriz para o referencial(em píxeis) da janela
101 int pos_janela_x = CoordenadasMatriztransformaParaRefJanela(dados, j, EIXO_X);
102 int pos_janela_y = CoordenadasMatriztransformaParaRefJanela(dados, i, EIXO_Y);
103
104 if (bolhaAtual->bolha.marca == MARCADA_PARA_ELIMINACAO) //se a bolha está marcada para implosão
105 {
106 if (iteracoes != ITERACOES_IMPLOSAO) //na última iteração não imprime nada
107 {
108 filledCircleRGBA(g_pRenderer, pos_janela_x, pos_janela_y, raio, cor[0], cor[1], cor[2],255);
109 circleRGBA(g_pRenderer, pos_janela_x, pos_janela_y, raio, 0, 0, 0,255);
110 SDL_Delay(DELAY_IMPLOSAO);
111 }
112
113 bolhaAtual = bolhaAtual->prox;
114 i++;
115 continue;
116 }
117
118 //caso a bolha não esteja marcada para eliminação
119 filledCircleRGBA(g_pRenderer, pos_janela_x, pos_janela_y, dados.dimBolha/2.0, cor[0], cor[1], cor[2],255);
120 circleRGBA(g_pRenderer, pos_janela_x, pos_janela_y, (dados.dimBolha/2.0), 0, 0, 0,255);
121 bolhaAtual = bolhaAtual->prox;
122 i++;
123 }
124 }
125
126 SDL_RenderPresent(g_pRenderer); //atualiza a janela
127 iteracoes++;
128 }while(iteracoes <= ITERACOES_IMPLOSAO); //enquanto todas as iterações não tiverem sido feitas
129 }
130
131
132 //------------------------------------------------ queda de bolhas --------------------------------------------
134 /*
135 Função que trata da animação da queda de novas bolhas que substituem as eliminadas.
136 Assim, a função seleciona as bolhas marcadas com ANIMACAOO_DESCER + posição relativa na sua coluna imaginando-as numa nova matriz
acima
137 da janela, mecanismo usado para facilitar a animação da queda.
138 As bolhas começam todas a uma altura de três casas acima do topo da janela e a altura relativa vertical vai diminuindo ao longo do
139 ciclo até as bolhas atingirem a posição correspondente às suas coordenas reais nas colunas.
140 Recebe: os dados do jogo
141 o apontador de acesso a toda a informação das bolhas
142 */
143 void atualizaJanela_animacoes_descer_novas(DADOS dados, APBLOCO * OApontador)
144 {
145 int over = TRUE; //regula a execução do ciclo
146 float altura = dados.alturaJanela + 2; //altura inicial em coordenadas verticais
147 do
148 {
149 //limpa o ecrã
150 SDL_SetRenderDrawColor(g_pRenderer, 255, 255, 255, 255);
151 SDL_RenderClear(g_pRenderer); //limpar a janela com branco
152 over = TRUE;
153 desenha_butoes(dados); //desenha a pontuação e os botões
154 altura -= DS; //ao longo das iterações a altura vai diminundo
155
156 for (int j = 0; j < dados.larguraJanela; ++j)//percorre todas as colunas
157 {
158 int i = 0;
159 APBLOCO bolhaAtual = *(OApontador + j);
160
161 while(bolhaAtual != NULL)
162 {
163 if (bolhaAtual == NULL)
164 break;
165 int cor[3];//variável que guarda a cor da bolha
166 corBolha(bolhaAtual->bolha.cor, cor);//a partir do codigo da cor que está na matriz, dá valores de rgb ao vetor cor
167 //desenha a bolha depois de transformadas as coordenas da matriz para o referencial(em pixeis) da janela
168
169 //a posição em relação ao eixo x numca varia
170 int pos_janela_x = CoordenadasMatriztransformaParaRefJanela(dados, j, EIXO_X);
171 int pos_janela_y;
172
173 if (bolhaAtual->bolha.marca < ANIMACAO_DESCER) //se a bolha não estiver marcada para descer com animação
174 pos_janela_y = CoordenadasMatriztransformaParaRefJanela(dados, i, EIXO_Y); //coordenadas normais
175 else
176 {
177 //cordenadas relativas
178 pos_janela_y = CoordenadasMatriztransformaParaRefJanela_coordenadas_continuas(dados,
179 altura + bolhaAtual->bolha.marca- ANIMACAO_DESCER, EIXO_Y);
180 over = FALSE;
181 //se a bolha chegar ao seu sítio a sua marca deixa de ser ANIMACAO_DESCER
182 if (altura + bolhaAtual->bolha.marca- ANIMACAO_DESCER <= i)
183 bolhaAtual->bolha.marca = SOFREU_ALTERACAO; //passa a ser sofrer alteração pois a sua descida pode ter formado novos caminhos
184 }
185
186 //desenha as bolhas
187 filledCircleRGBA(g_pRenderer, pos_janela_x, pos_janela_y, dados.dimBolha/2.0, cor[0], cor[1], cor[2],255);
188 circleRGBA(g_pRenderer, pos_janela_x, pos_janela_y, (dados.dimBolha/2.0), 0, 0, 0,255);
189 bolhaAtual = bolhaAtual->prox;
190 i++;
191 }
192 }
193 SDL_RenderPresent(g_pRenderer);
194 SDL_Delay(DELAY_DESCER);
195
196 }while(over == FALSE);
197 }
198
199 /*
200 Função que trata da animação da queda das bolhas acima de outras que tenham sido eliminadas
201 As bolhas tem na sua marca a sua posição anterior + ANIMACAO_DESCER ao longo deste ciclo as bolhas são desenhadas no ecrã
202 até que a diferenca entre a sua posiçao anterior - "altura" e a posiçao que adotarão na dispoção de estabilidade for nula.
203 Recebe: os dados do jogo
204 o apontador de acesso a toda a informação das bolhas
205 */
206 void atualizaJanela_animacoes_descer_velhas(DADOS dados, APBLOCO * OApontador)
207 {
208 int over = TRUE; //regula a execução do ciclo
209 float altura = 0; //a altura relativa começa a 0
210 do
211 {
212 //limpa a janela
213 SDL_SetRenderDrawColor(g_pRenderer, 255, 255, 255, 255);
214 SDL_RenderClear(g_pRenderer); //limpar a janela com branco
215 over = TRUE;
216 desenha_butoes(dados); //desenha botões e
217 altura -= DS;//altura diminui ao longo das iterações
218
219 for (int j = 0; j < dados.larguraJanela; ++j)//percorre todas as colunas
220 {
221 int i = 0;
222 APBLOCO bolhaAtual = *(OApontador +j);
223 while(bolhaAtual != NULL)
224 {
225 int cor[3];//variável que guarda a cor da bolha
226 corBolha(bolhaAtual->bolha.cor, cor);//a partir do codigo da cor que esta na matriz, dá valores de rgb ao vetor cor
227 //coordenada no eixo x permanece inalterada
228 int pos_janela_x = CoordenadasMatriztransformaParaRefJanela(dados, j, EIXO_X);
229 int pos_janela_y;
230
231 //a bolha não está marcada para descer
232 if (bolhaAtual->bolha.marca < ANIMACAO_DESCER)
233 pos_janela_y = CoordenadasMatriztransformaParaRefJanela(dados, i, EIXO_Y);
234 else
235 {
236 pos_janela_y = CoordenadasMatriztransformaParaRefJanela_coordenadas_continuas(dados,
237 altura + bolhaAtual->bolha.marca- ANIMACAO_DESCER, EIXO_Y);
238 over = FALSE;
239 //até as coordenadas relativas serem as mesmas que as coordenadas normais da bolha
240 if (altura + bolhaAtual->bolha.marca - ANIMACAO_DESCER <= i)
241 bolhaAtual->bolha.marca = SOFREU_ALTERACAO;
242 }
243
244 //desenha a bolha
245 filledCircleRGBA(g_pRenderer, pos_janela_x, pos_janela_y, dados.dimBolha/2.0, cor[0], cor[1], cor[2],255);
246 circleRGBA(g_pRenderer, pos_janela_x, pos_janela_y, (dados.dimBolha/2.0), 0, 0, 0,255);
247 bolhaAtual = bolhaAtual->prox;
248 i++;
249 }
250 }
251 //atualiza a janela
252 SDL_RenderPresent(g_pRenderer);
253 SDL_Delay(DELAY_DESCER);
254
255 }while(over == FALSE);
256 }
257
258
259
260 /*
261 Função que transforma coordenaadas contínuas da matriz para coordenadas da janela
262 Esta função é necessária para calcular a posição das bolhas ao cair durante a animação de queda
263 Recebe o valor da coordenada e indicação de que eixo é que essa coordenada se refere (0-X, 1-Y) e retorna o valor dessa coordenada
264 no referencial da janela*/
265 int CoordenadasMatriztransformaParaRefJanela_coordenadas_continuas(DADOS dados, float coordenada, int eixo)
266 {
267 if (eixo == EIXO_X)
268 return (int) (coordenada+1)*(dados.dl)*dados.dimBolha + (dados.dimBolha/2.0)*(2*coordenada+1); //equacao para os xx
269
270 else if (eixo == EIXO_Y)
271 return (int) (dados.windowSizeY - ((coordenada +1)*(dados.dl)*dados.dimBolha + (dados.dimBolha/2.0)*(2*coordenada+1) +
272 1.5*dados.dimBolha ) + dados.altura_barra_superior ); // eqaucao para os yy
273
274 return 0;
275 }
276

277 //----------------------------------------------- troca de bolhas -----------------------------------------------


278
279 /*
280 Função que verifica em que bolha o utilizador carregou se é que o fez
281 No caso de ter caregado numa bolha, marca-a para para ser animada para uma troca
282 Recebe como argumentos: os dados do jogo
283 o apontador de acesso à informação referente às bolhas
284 Retorna: FALSE se nao carregou em nenhuma bolha
285 TRUE se carregou numa bolha e a marcou com sucesso
286 */
287 int deteta_clique_troca_fixe(DADOS dados, APBLOCO * OApontador, TROCA_FIXE_INFO * troca_info)
288 {
289 int mouse_pos[2];
290 SDL_GetMouseState(&mouse_pos[0], &mouse_pos[1]); //deteta a posição do rato
291
292 for (int j = 0; j < dados.larguraJanela; ++j) //varre todas as colunas
293 {
294 int i = 0;
295 APBLOCO bolha_carregada = *(OApontador + j);
296 //Corre todas as bolhas em jogo até encontrar uma cujo centro diste de menos de um raio em relação ao sitio onde o utilizador carregou
297 while(bolha_carregada != NULL)
298 {
299 int centro_x = CoordenadasMatriztransformaParaRefJanela(dados, j, EIXO_X);
300 int centro_y = CoordenadasMatriztransformaParaRefJanela(dados, i, EIXO_Y);
301 //se a distancia for inferior a um raio
302 if (bolha_carregada != NULL && distancia(centro_x, centro_y, mouse_pos[0], mouse_pos[1]) <= dados.dimBolha/2)
303 {
304 //preenche a informação acerca da troca
305 troca_info->x0 = mouse_pos[0];
306 troca_info->y0 = mouse_pos[1];
307 troca_info->i = i;
308 troca_info->j = j;
309 troca_info->direcao = 0;
310 bolha_carregada->bolha.marca = ANIMACAO_TROCA;
311 return TRUE;
312 }
313
314 bolha_carregada = bolha_carregada->prox;
315 i++;
316 }
317 }
318 return FALSE;
319 }
320
321
322 /*
323 Função que à medida que o rato vai mexendo movimenta as bolhas para que a troca seja visualmente mais apelativa
324 Assim, por forma a passar a informação da magnitude e direção do desvio "codifica-o" na marca da bolha através da diferença em
325 relação à macro.
326 ANIMACAO_TROCA
327 Recebe: os dados do jogo
328 O apontador de acesso a toda a informação do programa
329 a informação da troca da bolha
330 */
331 void mexe_bolhas_troca(DADOS dados, APBLOCO * OApontador, TROCA_FIXE_INFO troca_info)
332 { int mouse_pos[2];
333 SDL_GetMouseState(&mouse_pos[0], &mouse_pos[1]); //deteta a posição do rato
334 int ds; //variável que condicionará a disância das bolhas à sua posição usual
335 if (troca_info.direcao == 1) //se a troca é horizontal
336 ds = mouse_pos[0]-troca_info.x0;
337 else
338 ds = -(mouse_pos[1]-troca_info.y0); //referencial invertido nos yy implica a utilização de -
339 restringe_ds(dados, &ds); //restringe os valores de ds para que a bolha não se movimente mais do que para as posições adjacentes
340 APBLOCO bolhaTroca1, bolhaTroca2, bolhaNaoTroca;
341 //analisa os quatro casos separadamente
342 if (ds >= 0 && troca_info.direcao == 1)
343 { bolhaTroca1 = apbloco_bolha_em_i_j(OApontador, troca_info.i, troca_info.j, dados);
344 bolhaTroca2 = apbloco_bolha_em_i_j(OApontador, troca_info.i, troca_info.j + 1, dados);
345 bolhaNaoTroca = apbloco_bolha_em_i_j(OApontador, troca_info.i, troca_info.j - 1, dados);
346 if (bolhaNaoTroca != NULL) //evitar que o nosso amigo apareça
347 bolhaNaoTroca->bolha.marca = 0; //reset ao desvio da bolha oposta à troca
348 bolhaTroca1->bolha.marca = ANIMACAO_TROCA + ds; //a informação do desvio em relação à posição usual é marcada da seguinte forma
349 if (bolhaTroca2 != NULL) //evitar que o nosso amigo apareça
350 bolhaTroca2->bolha.marca = ANIMACAO_TROCA - ds;
351 }
352 else if (ds < 0 && troca_info.direcao == 1)
353 { bolhaTroca1 = apbloco_bolha_em_i_j(OApontador, troca_info.i, troca_info.j, dados);
354 bolhaTroca2 = apbloco_bolha_em_i_j(OApontador, troca_info.i, troca_info.j - 1, dados);
355 bolhaNaoTroca = apbloco_bolha_em_i_j(OApontador, troca_info.i, troca_info.j + 1, dados);
356 if (bolhaNaoTroca != NULL)
357 bolhaNaoTroca->bolha.marca = 0;
358 bolhaTroca1->bolha.marca = ANIMACAO_TROCA + ds;
359 if (bolhaTroca2 != NULL)
360 bolhaTroca2->bolha.marca = ANIMACAO_TROCA - ds;
361 }
362 else if (ds >= 0 && troca_info.direcao == 2)
363 { bolhaTroca1 = apbloco_bolha_em_i_j(OApontador, troca_info.i, troca_info.j, dados);
364 bolhaTroca2 = apbloco_bolha_em_i_j(OApontador, troca_info.i + 1, troca_info.j, dados);
365 bolhaNaoTroca = apbloco_bolha_em_i_j(OApontador, troca_info.i - 1, troca_info.j, dados);
366 if (bolhaNaoTroca != NULL)
367 bolhaNaoTroca->bolha.marca = 0;
368 bolhaTroca1->bolha.marca = ANIMACAO_TROCA - ds;
369 if (bolhaTroca2 != NULL)
370 bolhaTroca2->bolha.marca = ANIMACAO_TROCA + ds;
371 }
372 else if (ds < 0 && troca_info.direcao == 2)
373 { bolhaTroca1 = apbloco_bolha_em_i_j(OApontador, troca_info.i, troca_info.j, dados);
374 bolhaTroca2 = apbloco_bolha_em_i_j(OApontador, troca_info.i - 1, troca_info.j, dados);
375 bolhaNaoTroca = apbloco_bolha_em_i_j(OApontador, troca_info.i + 1, troca_info.j, dados);
376 if (bolhaNaoTroca != NULL)
377 bolhaNaoTroca->bolha.marca = 0;
378 bolhaTroca1->bolha.marca = ANIMACAO_TROCA - ds;
379 if (bolhaTroca2 != NULL)
380 bolhaTroca2->bolha.marca = ANIMACAO_TROCA + ds;
381 }
382 atualizaJanela_animacao_troca(dados, OApontador, troca_info); //desenha as bolhas com os desvios
383 }
384
385 /*
386 Função que restringe o valor de ds, de modo a que os devios não sejam superiores a uma bolha e um itervalo
387 Assim, recebe, para além dos dados, ds por endereço e restringe-o se necessário
388 */
389 void restringe_ds(DADOS dados, int * ds)
390 {
391 if (abs(*ds) > dados.dimBolha + dados.dl*dados.dimBolha) //se o desvio for superior ao desejado
392 {
393 if(*ds > 0)
394 *ds = dados.dimBolha + dados.dl*dados.dimBolha; //é atribuido o valor máximo
395 else
396 *ds = -(dados.dimBolha + dados.dl*dados.dimBolha);
397 }
398 }
399
400 /*
401 Função que trata do desenho da animação da troca das bolhas
402 Assim, desenha as bolhas com os devios indicados nas suas marcas
403 Recebe: os dados do jogo
404 o apontador de acesso a toda a informação das bolhas
405 a informação da troca
406 */
407 void atualizaJanela_animacao_troca(DADOS dados, APBLOCO * OApontador, TROCA_FIXE_INFO troca_info)
408 {
409 //limpa o ecrâ
410 SDL_SetRenderDrawColor(g_pRenderer, 255, 255, 255, 255);
411 SDL_RenderClear(g_pRenderer); //limpar a janela com branco
412 desenha_butoes(dados); //desenha a pontuação e os botões
413 for (int j = 0; j < dados.larguraJanela; ++j)//percorre todas as colunas
414 {
415 int i = 0;
416 APBLOCO bolhaAtual = *(OApontador + j);
417
418 while(bolhaAtual != NULL)
419 {
420 int cor[3];//variavel que guarda a cor da bolha
421 corBolha(bolhaAtual->bolha.cor, cor);//a partir do código da cor que está na matriz, dá valores de rgb ao vetor cor
422 //desenha a bolha depois de transformadas as coordenadas da matriz para o referencial(em pixeis) da janela
423 int pos_janela_x = CoordenadasMatriztransformaParaRefJanela(dados, j, EIXO_X);
424 int pos_janela_y = CoordenadasMatriztransformaParaRefJanela(dados, i, EIXO_Y);
425
426 if(bolhaAtual->bolha.marca >= ANIMACAO_TROCA - 100) //se estiver marcada para a animação
427 {
428 if (troca_info.direcao == 1) //troca na horizontal
429 pos_janela_x += bolhaAtual->bolha.marca - ANIMACAO_TROCA;
430 if (troca_info.direcao == 2) //troca na vertical
431 pos_janela_y += bolhaAtual->bolha.marca - ANIMACAO_TROCA;
432 }
433
434 //desenha as bolhas
435 filledCircleRGBA(g_pRenderer, pos_janela_x, pos_janela_y, dados.dimBolha/2.0, cor[0], cor[1], cor[2],255);
436 circleRGBA(g_pRenderer, pos_janela_x, pos_janela_y, (dados.dimBolha/2.0), 0, 0, 0,255);
437 bolhaAtual = bolhaAtual->prox;
438 i++;
439 }
440 }
441 SDL_RenderPresent(g_pRenderer); //atualiza o renderer
442 }
443
444
445 /*
446 Função que após a animação das bolhas ter terminado avalia as seus devios e caso sejam grandes o suficentes troca as bolhas
447 Recebe: os dados do jogo
448 O apontador de acesso a toda a informação relativa às bolhas
449 */
450 void normaliza_bolhas_trocadas_animacao(DADOS dados, APBLOCO * OApontador)
451 {
452 int i; //variável para correr as colunas
453 int j; //variável para correr as colunas
454 int BREAK = FALSE; //flag para controlar o ciclo
455
456 APBLOCO bolhaTroca_1; //apontador para o bloco de uma das bolhas a ser trocadas
457
458 for (j = 0; j < dados.larguraJanela; ++j)//percorre as linhas
459 {
460 bolhaTroca_1 = *(OApontador + j); //no início de cada coluna o apontador está no vetor dinâmico
461 i = 0;
462
463 while(bolhaTroca_1 != NULL)//percorre as colunas
464 {
465 if (bolhaTroca_1->bolha.marca != 0) //quando for encontrada a primeira bolha para troca sai do ciclo
466 {
467 if (abs(bolhaTroca_1->bolha.marca - ANIMACAO_TROCA) >= (int) (0.51*dados.dimBolha) ) //se o desvio for grande o suficiente
468 {
469 BREAK = TRUE; //para sair dos dois ciclos usa a flag BREAK
470 break;
471 }
472 }
473 bolhaTroca_1 = bolhaTroca_1->prox;
474 i++;
475 }
476
477 if (BREAK == TRUE) //se a flag for TRUE sai do ciclo
478 break;
479 }
480
481 /* Sabendo que apenas duas bolhas podem estar marcadas e que o algoritmo de procura utilizado corre a janela da esquerda
482 para a direita e debaixo para cima, há três possiblidades:
483 -> a segunda bolha a trocar está à direita e terá de ser feita uma troca entre colunas
484 -> a segunda bolha a trocar está a cima e terá de ser feita uma troca na mesma coluna
485 -> a segunda bolha a trocar não está em nenhuma das situações anteriores pelo que bolha está situada numa borda da janela
486 Assim, trataremos as duas primeiras possibilidades e, no caso da terceira, eliminaremos apenas a marca da bolha
487 */
488
489 //variáveis que guardarão os endereços das bolhas a cima e à direita da primeira identificada para a troca
490 if (bolhaTroca_1 == NULL) //se nenhuma bolha for trocada
491 {
492 neutraliza_marcas_animacao_troca(dados, OApontador); //elimina as marcas de animação
493 reset(dados, OApontador); //imprime alterações
494 return;
495 }
496
497 //teste às bolhas acima e à direita
498 APBLOCO bolha_a_cima = bolhaTroca_1->prox;
499 APBLOCO bolha_direita = apbloco_bolha_em_i_j(OApontador, i,j+1, dados);
500
501
502 if (bolha_a_cima!= NULL && bolha_a_cima->bolha.marca >= ANIMACAO_TROCA - 100) //a bolha acima foi marcada para troca
503 { //apontador para a bolha anterior necessária para a função que fará a troca
504 APBLOCO anterior = apbloco_bolha_em_i_j(OApontador, i-1, j, dados);
505 troca_em_coluna(OApontador, anterior, bolhaTroca_1, j);//troca na mesma coluna
506 }
507 else if(bolha_direita != NULL && bolha_direita->bolha.marca >= ANIMACAO_TROCA - 100)
508 troca_entre_colunas(OApontador, dados, bolhaTroca_1, i, j); //troca bolhas entre colunas
509
510 neutraliza_marcas_animacao_troca(dados, OApontador);//elimina as marcas de animação
511 reset(dados, OApontador);
512 }
513
514 /*
515 Função que define a direção da troca (1 - horizontal e 2 - vertical)
516 Recebe por endereco a estrutura com a informação da troca
517 Define a direção da troca como sendo a direção em que o rato mais se movimentou na primeira vez em que se afastou do ponto em que
518 inicialmete carregou*/
519 void define_direcao(TROCA_FIXE_INFO * troca_info)
520 {
521 int mouse_pos[2];
522 SDL_GetMouseState(&mouse_pos[0], &mouse_pos[1]); //deteta a posição do rato
523
524 int dx = abs(mouse_pos[0]-troca_info->x0); //distância horizontal
525 int dy = abs(mouse_pos[1]-troca_info->y0); //distância vertical
526
527 if (dx > dy) //o deslocamento horizontal é maior
528 troca_info->direcao = 1;
529 else
530 troca_info->direcao = 2;
531
532 }
533
534 /*
535 Função que corre todas as bolhas do jogo e anula a marca de todas as bolhas que estejam marcadas para eliminação temporária
536 Recebe: os dados do jogo
537 o apontador de acesso a toda a informação das bolhas
538 */
539 void neutraliza_marcas_animacao_troca(DADOS dados, APBLOCO * OApontador)
540 {
541 for (int j = 0; j < dados.larguraJanela; ++j)//percorre as linhas
542 {
543 APBLOCO bolhaAtual = *(OApontador +j);
544 while(bolhaAtual != NULL)
545 {
546 if(bolhaAtual->bolha.marca != SOFREU_ALTERACAO) //se encontrar uma bolha que não esteja marcada como tendo sofrido alteração
547 bolhaAtual->bolha.marca = 0;//anula a marca
548
549 bolhaAtual = bolhaAtual->prox;
550 }
551 }
552 }
1 #makefile
2 all: bubble_troubles
3
4 animacoes.o: animacoes.c animacoes.h
5 gcc -c -g -D_THREAD_SAFE -I/usr/local/include/SDL2 -D_REENTRANT -Wall -pedantic -std=c99 animacoes.c
6 graphics.o: graphics.c graphics.h
7 gcc -c -g -D_THREAD_SAFE -I/usr/local/include/SDL2 -D_REENTRANT -Wall -pedantic -std=c99 graphics.c
8 op_listas.o: op_listas.c op_listas.h
9 gcc -c -g -Wall -pedantic -std=c99 op_listas.c
10 leitura_dados.o: leitura_dados.c leitura_dados.h header_base.h
11 gcc -c -g -Wall -pedantic -std=c99 leitura_dados.c
12 resultados.o: resultados.c resultados.h
13 gcc -c -g -Wall -pedantic -std=c99 resultados.c
14 main_loop.o: main_loop.c op_listas.h leitura_dados.h graphics.h animacoes.h resultados.h
15 gcc -c -g -D_THREAD_SAFE -I/usr/local/include/SDL2 -D_REENTRANT -Wall -pedantic -std=c99 main_loop.c
16 bubble_trouble: main_loop.o graphics.o leitura_dados.o resultados.o animacoes.o op_listas.o
17 gcc -g -D_THREAD_SAFE -I/usr/local/include/SDL2 -L/usr/local/lib -D_REENTRANT -lm -std=c99 -lSDL2 -lSDL2_gfx
main_loop.o graphics.o animacoes.o leitura_dados.o resultados.o op_listas.o -o bubble_trouble
18
19 clean:
20 rm main_loop.o op_listas.o graphics.o leitura_dados.o resultados.o animacoes.o bubble_trouble
21 run:
22 ./bubble_trouble -f -l 13 -a 13 -b 45

Vous aimerez peut-être aussi