1. Este site usa cookies. Ao continuar a usar este site está a concordar com o nosso uso de cookies. Saber Mais.

[Encerrar] Minesweeper

Discussão em 'Programação' iniciada por Keith, 19 de Junho de 2007. (Respostas: 15; Visualizações: 883)

  1. Keith

    Keith Power Member

    Boa noite.

    Estou a tentar fazer o jogo Minesweeper em C. De momento estou na fase crítica do projecto, destapar as casas vazias.

    Já tenho o tabuleiro (L x C), gero N minas, conto quantas minas há em torno de cada posição do tabuleiro. Realmente o que me falta, e o que me está a dar mais dores de cabeça, é mostrar as casas conforme elas são escolhidas. Ou seja, há uma série de casas juntas umas das outras, que não têm minas em torno delas. Elas deverão ser mostradas todas ao mesmo tempo.

    Quem já jogou o clássico sabe o que estou a falar. O que peço são dicas de como revelar essas casas. Já experimentei vários métodos, com e sem recursividade, mas não consigo que ele funcione.

    Já agora, estou a usar um tabuleiro de char's [linhas][colunas][2], em que 1ª dimensão ([l][c][0]) têm o tabuleiro tal como o jogador o deverá ver, e a 2ª têm tudo o que já fiz em relação a gerar minas e conta-las.

    Aqui vai um screen:

    [​IMG]

    Alguém pode sugerir alguma coisa?

    Muito obrigado desde já,
    Keith
     
    Última edição: 25 de Junho de 2007
  2. ngr

    ngr

    Pelo que entendi, ao escolheres uma posição que tenha um 0 (ex: [10][10]) vais ter de verificar a casa de cima[9][10], baixo[11][10], esquerda[10][9] e direita[10][11]. Se, por exemplo, a casa de cima for 0, chamas a própria função (ou seja, recursiva) que vai verificar se essa casa ([9][10]) tem um 0 na posição acima ([8][10]), etc. Como toda a função recursiva tem que ter uma condição de paragem que é achar um valor diferente de 0. Parâmetros são a posição da casa e o vector.
    No clássico dá a ideia que os grupos de 0's não costumam ser tão grandes. Neste tabuleiro se escolhesses a [10][10] ficavas com quase 1/4 do tabuleiro descoberto. Mas isso tem a ver com o sorteio. Desde que o algoritmo esteja bem é o que interessa.
    Espero ter ajudado!
     
  3. Keith

    Keith Power Member

    O algoritmo está correcto. O número de minas é que é demasiado baixo para o tamanho do tabuleiro. Experimenta no clássico e já vês.

    Não me basta verificar apenas nas casas em torno da qual escolhi. Se todas elas estiverem ligadas por zeros, todas têm de ser abertas. O problema é esse mesmo. Como conseguir abrir todas as casas ligadas por zeros.

    Se puderes ajudar aí, agradeço.
     
  4. Keith

    Keith Power Member

    Se jogar na 10x10 como disseste, isto é o que abre:

    [​IMG]

    e não tanto como disseste.

    Tal como disse, depende do número de minas no tabuleiro.

    Keith
     
  5. FASC

    FASC Power Member

    Relê bem o que o "ngr" disse. Usando recursividade como ele disse, consegues fazer o que queres.
     
  6. ngr

    ngr

    Antes de escrever o texto eu fui experimentar o clássico para ter a certeza que não te enganava. Mas no tabuleiro de 9x9 realmente a abertura das casas vazias é mínima.
    Cada casa que a função processa, chama a própria função 4 vezes (uma para cada direcção). Só pára quando encontra uma casa diferente de 0, a qual também tem de mostrar.
     
  7. Keith

    Keith Power Member

    Algo deste género?

    Código:
    int Revelar (int L, int C)
    {
    int i, j;
    
       if (CM[L][C][1] != '0') { CM[L][C][0] = CM[L][C][1]; return 0; }
    
       else { CM[L][C][0] = CM[L][C][1];
                Revelar (L-1,C);
                Revelar (L,C-1);
                Revelar (L+1,C);
                Revelar (L,C+1); }
    
       return 0;
    }
    
    Obtenho um segmentation fault.
    Podes sugerir algo?

    Obrigado,
    Keith
     
  8. MeY-ZiNG

    MeY-ZiNG Power Member

    Algumas coisas:

    1) o "int i,j" já podes tirar...

    2) a funcao pode ser void porque nunca precisas de retornar nada para a funcao que chama esta... desde que aqui chames uma funcao para mostrar a casa vazia quando é caso disso (dentro do if)... assim escusas de encher a heap de int's inuteis e melhoras um pouco a eficiencia...

    3) para que serve a terceira dimensao do array? => CM[L][C][esta-posicao]

    4) faltam-te ai os casos especiais em que o L-1, L+1, C-1 e C+1 ultrapassam as dimensoes do tabuleiro... neste caso L-1 < 0, C-1< 0, L+1 > LMAX, C+1 > CMAX (LMAX e CMAX tens que definir como variavel global ou passa-los como parametro para a funcao recursiva...). Deve ser por isso que tens seg fault.

    Sugestão:

    Código:
    void Revelar (int L, int C) {
       if (L < 0 || C < 0 || L > LMAX || C > CMAX) 
                return;
    
       else if (CM[L][C][1] != '0') 
                CM[L][C][0] = CM[L][C][1];
    
       else { 
                CM[L][C][0] = CM[L][C][1];
                Revelar (L-1,C);
                Revelar (L,C-1);
                Revelar (L+1,C);
                Revelar (L,C+1);
       }
       return;
    }
    
     
    Última edição: 20 de Junho de 2007
  9. Keith

    Keith Power Member

    1) eu sei. esqueci-me de o fazer antes de colocar aqui o código.
    2) usei o tipo int, pois em c uso pouca recursividade (uso apenas em Haskell e o estilo de programação é totalmente diferente) e pensei que poderia forçar a saída da função dessa forma.
    3) a terceira dimensão contém tudo o que fiz, desde minar, contar posicões, etc. Se ela for 1 mostra o tabuleiro de baixo, se for 0 mostra o tabuleiro de cima. Tal como está na imagem.
    4) Esqueci-me disso. Estou a tentar agora.

    Obrigado
     
  10. Keith

    Keith Power Member


    Continuo com segmentation fault.
    Obrigado à mesma.

    Keith
     
  11. MeY-ZiNG

    MeY-ZiNG Power Member

    2) podes sair da funcao quando quiseres usando o "return;", sendo ela void.
    3) Continuo sem perceber o que é o terceiro indice... nao estarás a fazer aqui confusão? Não deveria ser CM[L][C] != '0' naquele if? Qual é a representação interna que estás a usar para os elementos do tabuleiro, não são chars tipo '0', '1' e '*' ?

    EU faria desta forma:

    Usando essa representação para os elementos, e partindo do principio que tens uma funcao do genero "print-tabuleiro" que se limita a printar os 0s, 1s, e *s que encontra no array, usando dois ciclos for (para linhas e colunas).

    Neste caso o mais simples seria ter dois arrays de duas dimensoes, um completamente preenchido (que é gerado de forma aleatoria para cada novo jogo) e outro com as mesmas dimensões, que é inicializado a NULL... neste caso seriam respectivamente "tabuleiro" e "tabuleiro_visivel", ambos com dimensao LMAX x CMAX.

    Dessa forma, a função para revelar os zeros primos de um zero seria chamada sempre que o jogador escolhesse uma posicao do "tabuleiro" que contém um zero... se contém outra coisa basta revelar o que essa coisa é (numero ou *) e caso seja um * => game over.

    A função para mostrar os zeros "ligados" ficaria assim:

    Código:
    void mostrar_zeros (int L, int C) {
       /* se tiver fora dos limites ou nao for um zero... */
       if (L < 0 || C < 0 || L > LMAX || C > CMAX || tabuleiro[L][C] != '0')
                return;
    
       else { 
                tabuleiro_visivel[L][C] = '0';
                mostrar_zeros(L-1,C);
                mostrar_zeros(L,C-1);
                mostrar_zeros(L+1,C);
                mostrar_zeros(L,C+1);
       }
       return;
    }
    
    Enfim, apeteceu-me pensar um bocado no problema mas cada um o resolve à sua maneira... boa sorte nisso que eu vou dormir!
     
  12. Keith

    Keith Power Member

    Tal como tu tens um "tabuleiro" e um "tabuleiro_visivel", eu juntei tudo num.
    Sei lá, imagina um paralepipedo. Tens a largura, o comprimento e tens a altura. Neste caso a altura é 2, e encontra-se divido: ou seja na metade de cima tens o "tabuleiro_visivel", na metade de baixo tens o "tabuleiro".

    Continuo com segmentation fault. *Dass...

    Keith
     
  13. ngr

    ngr

    Penso que o erro está na comparação do maior. Se o vector tiver dimensão 10 a posição 10 não existe. Logo em vez de:
    Código:
       if (L < 0 || C < 0 || L > LMAX || C > CMAX || tabuleiro[L][C] != '0')
    deveria estar:
    Código:
       if (L < 0 || C < 0 || L > LMAX-1 || C > CMAX-1 || tabuleiro[L][C] != '0')
    Já agora se quiseres poupar chamadas à função de posições inválidas, fazes os if antes de as chamar, ou seja:
    Código:
    void revelar (int L, int C) {
    /* esta posição revela sempre, seja mina, 0, 1, 2, etc  porque é sempre válida*/
       CM[L][C][0] = CM[L][C][1];
    
       if(CM[L][C][1] == '0') { 
            if(L > 0)
                revelar (L-1,C);
            if(C > 0)
                revelar (L,C-1);
            if(L < LMAX-1)
                revelar (L+1,C);
            if(C < CMAX-1)
                revelar (L,C+1);
       }
    }
    edit: corrigir um erro de português e um sinal no código
     
    Última edição: 21 de Junho de 2007
  14. Keith

    Keith Power Member

    Estive a experimentar algumas hipóteses fornecidas por vocês, e aquela que revela mais casas de uma vez, resultado de várias combinações de código é a seguinte:

    Código:
    void Revelar (int L, int C)
    {
       CM[L][C][0] = CM[L][C][1];
    
       if(CM[L][C][1] == '0')
       { CM[L-1][C-1][0] = CM[L-1][C-1][1];    CM[L-1][C][0] = CM[L-1][C][1];
         CM[L-1][C+1][0] = CM[L-1][C+1][1];   CM[L][C-1][0] = CM[L][C-1][1];
         CM[L][C+1][0] = CM[L][C+1][1];      CM[L+1][C-1][0] = CM[L+1][C-1][1];
         CM[L+1][C][0] = CM[L+1][C][1];      CM[L+1][C+1][0] = CM[L+1][C+1][1];
       }
    
       if ((CM[L][C][1] == '0') && (L - 1 > -1))       Revelar (L-1,C);
       if ((CM[L][C][1] == '0') && (L + 1 > LMAX-1))  Revelar (L+1,C);
       if ((CM[L][C][1] == '0') && (C - 1 > -1))       Revelar (L,C-1);
       if ((CM[L][C][1] == '0') && (C + 1 > CMAX-1))  Revelar (L,C+1);
    
       if ((CM[L][C][1] == '0') && (L - 1 > -1) && (C - 1 > 0))               Revelar (L-1,C-1);
       if ((CM[L][C][1] == '0') && (L - 1 > -1) && (C + 1 > CMAX-1))      Revelar (L-1,C+1);
       if ((CM[L][C][1] == '0') && (L + 1 > LMAX-1) && (C - 1 > 0))        Revelar (L+1,C-1);
       if ((CM[L][C][1] == '0') && (L + 1 > LMAX-1) && (C + 1 > CMAX-1))  Revelar (L+1,C+1);
    }
    
    Mas mesmo assim, não consigo todas as posições:

    [​IMG]

    Além disso, acontece uma cena estúpida, que é neste caso, joguei na posicão [10][8] e ele mostra-me casas do outro extremo do campo.

    Keith
     
  15. ngr

    ngr

    Corrigi um erro que tinha no código que te dei, num sinal. Não sei se reparaste e corrigiste ou copiaste logo!
    Mas acho que tens o mesmo erro aqui:
    Código:
    && (L + 1 > LMAX-1) &&
    devia estar
    Código:
    && (L + 1 < LMAX) &&
    O objectivo é ser verdadeiro se for menor que o máximo e não maior que o máximo, senão vais parar fora do vector.
    Nunca reparei que a versão clássica abre as casas na diagonal até ver o teu código. Claro que fui experimentar para confirmar.
    Não era mais fácil meteres os IF's todos dentro do
    Código:
    if(CM[L][C][1] == '0')
    do que andares a meter
    Código:
    if ((CM[L][C][1] == '0') &&
    em todos?
     
  16. Keith

    Keith Power Member

    Se mudar o sinal para <, fico com segmentation fault. Já se ficar com o sinal > ele abre casas.
    No mínimo estranho, mas pronto...
    Eu tinha reparado que tinhas um sinal trocado.

    Eu acho que tens razão nos if's, estou só numa fase de experimentação, pois cada vez mais consigo abrir mais casas, mas não a totalidade, e já estou a começar a ficar irritado... lol

    Obrigado pela atenção que tens dado ao meu problema, ngr.

    Keith
     

Partilhar esta Página