Gioco del 15 in c

di il
3 risposte

Gioco del 15 in c

Salve a tutti, stavo cercando di programmare il gioco del 15 utilizzando delle strutture e dei puntatori in c ma ho riscontrato un problema che non riesco a risolvere.

Il programma funziona fino al momento in cui viene richiesto lo spostamento di un numero, ho verificato la funzione con il debugger  la funzione fa quello che dovrebbe fare ma poi giustamente non riporta nel main il campo aggiornato.

come potrei fare? grazie mille 

di seguito allego il codice in c

#include<stdio.h>
#include <stdlib.h>
#include <time.h>

typedef struct{
    int campo[4][4];
    int x;
    int y;
}Gioco;

void inizializza(Gioco *pc){
    int numeri[15]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
    int pieno=0;
    int random;
    int trovato=0;
    int j=-1;
    int i=0;
    srand(time(NULL));

    while (pieno<15){
        do{
            random=rand()%15+1;
            for(int k=0;k<15;k++){
                if(random==numeri[k]){
                    trovato=1;
                    numeri[k]=0;
                    break;
                }
            }
        }while (trovato!=1);
        pieno++;
        if(j<3){
            j++;
        }else{
            j=0;
            i++;
        } 
        pc->campo[i][j]=random;
        trovato=0;
    }
    pc->x=3;
    pc->y=3;
    pc->campo[3][3]=0;
}

void stampa(Gioco *pc){
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            printf("%d\t ", pc->campo[i][j]);
        }
        printf("\n");
    }
}

int controlla(Gioco *pc, int n){
    int check=0;
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            if(pc->campo[i][j]==n){
                if(pc->campo[i-1][j]==0 || pc->campo[i][j+1]==0 || pc->campo[i+1][j]==0 || pc->campo[i][j-1]==0){
                    check=1;
                }
            }
        }
    }
    return check;
}

Gioco sposta(Gioco c, int n){
    int tmp=0;
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            if(c.campo[i][j]==n){
                c.campo[c.x][c.y]=n;
                c.x=i;
                c.y=j;
                c.campo[i][j]=0;
            }
        }
    }
    return c;
}



int vincente(Gioco *pc){
    int numeri[16]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0};
    int check=1,tmp=0;
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            if(pc->campo[i][j]!=numeri[tmp]){
                check=0;
            }
        }
    }
    return check;
}

int main(){
    Gioco campo;
    int num=10;
    
    inizializza(&campo);
    while(!vincente(&campo)){
        do{
        stampa(&campo);
        printf("quali numeri vuoi spostare?");
        scanf("%d", &num);
        }while(!controlla(&campo,num));
        campo=sposta(campo,num);
    }
    printf("HIA VINTOO");
    return 0;
}

3 Risposte

  • Re: Gioco del 15 in c

    Ciao, lo scambio del numero con lo zero avviene correttamente, il problema è che andando avanti con i for quando arrivi all'ultimo elemento il programma li torna a scambiare riportando la griglia allo stato iniziale! ^^'
    Per risolvere basta mettere il return all'interno dell'if.

    Il programma può essere migliorato sotto vari punti di vista, giusto per citare un paio di cose:

    • relativamente alla funzione sposta() per esempio conviene passare come argomento un puntatore in modo da modificare direttamente la struct evitando un'inutile copia;
    • quando inizializzi la griglia devi tenere conto che non tutte le sequenze sono valide, in quanto alcune costituiscono uno schema impossibile da risolvere. Il seguente link chiarisce qual è il problema e come risolverlo: http://www.lidimatematici.it/blog/2014/08/25/sam-loyd-e-il-gioco-del-15/

    Ci sarebbero molte altre questioni su cui soffermarsi, se sei interessato fammi sapere.

  • Re: Gioco del 15 in c

    Ciao, grazie mille per la soluzione.
    Ho letto il primo punto relativo all'utilizzo di un puntatore e sarei molto curioso di sapere la tua soluzione. riguardo al secondo punto hai ragione, essendo un programma fatto per il principale divertimento di programmare non ci avevo nemmeno pensato.
    Sarei anche interessato alle altre questioni di cui parlavi.
    Grazie Mille

  • Re: Gioco del 15 in c

    09/12/2023 - MattINfo ha scritto:


    Ho letto il primo punto relativo all'utilizzo di un puntatore e sarei molto curioso di sapere la tua soluzione

    Nel seguente caso

    Gioco sposta(Gioco c, int n)
    {
    	...
        return c;
    }
    
    int main()
    {
    	...
    	campo = sposta(campo, num);
    	...
    }

    quando viene richiamata la funzione sposta(), una copia di campo viene creata e passata come argomento alla funzione; quest'ultima modificherà la suddetta copia per poi ritornarla al main(), dove la variabile campo verrà aggiornata con un'assegnazione.

    Con un'impostazione del genere invece

    void sposta(Gioco *c, int n)
    {
    	...
        return;
    }
    
    int main()
    {
    	...
    	sposta(&campo, num);
    	...
    }

    si risparmia la memoria per allocare la copia, e si evitano due copie della struct (quella per inizializzare la copia e quella per aggiornare l'oggetto campo). Ovviamente in casi del genere la differenza in termini di prestazioni è nulla, ma sono comunque questioni di cui bisogna tener conto.

    09/12/2023 - MattINfo ha scritto:


    Sarei anche interessato alle altre questioni di cui parlavi.

    Alcune osservazioni:

    • una struct deve indicare qualcosa di ben preciso e il suo utilizzo deve comportare una qualche utilità, come una semplificazione del codice o una sua maggiore chiarezza e leggibilità. E nel caso specifico io sinceramente non avrei utilizzato le struct;
    • se rifletti sul significato canonico di x e y, dovresti scrivere
    c.x = j;
    c.y = i;

    e non

    c.x = i;
    c.y = j;
    • nella funzione vincente() ti dimentichi di incrementare la variabile tmp
      Poi la variabile check è inutile, in quanto se la condizione dell'if è rispettata la funzione può direttamente ritornare 0
      Apro una breve parentesi: in C gli array statici (multidimensionali e non) occupano uno spazio di memoria contiguo, il che significa che un array multidimensionale può essere tranquillamente trattato come un array monodimensionale. 
      Al netto di quanto appena detto per la funzione vincente() farei qualcosa del genere:
    int vincente(int v[16])
    {
        for(unsigned int i = 0; i < 15; ++i)
        {
            if(v[i] != i + 1)
            {
                return  0;
            }
        }
        return 1;
    }
    
    int main()
    {
    	...
    	while(!vincente(*campo.campo))
    	{
    		...
    }
    • passiamo alla funzione controlla()
      Nella condizione dell'if più interno rischi di andare a leggere zone di memoria che non competono alla matrice; per esempio se n si trova in posizione i=0 e j=2, il primo controllo
    pc->campo[i - 1][j] == 0

    si trasforma in

    pc->campo[-1][2] == 0

    così come se n si trova in posizione i=1 e j=3, il secondo controllo

    pc->campo[i][j + 1] == 0

    si trasforma in

    pc->campo[1][4] == 0

    Quindi prima di accedere a pc->campo[i-1][j] devi assicurarti che i sia diverso da 0, così come prima di accedere a pc->campo[i][j+1] devi assicurarti che j sia diverso da 3, e così via…
    Anche in questo caso la variabile check è inutile, infatti se l'if più interno è rispettato la funzione può tornare direttamente 1.
    E ancora, invece di ricercare n nella matrice, dal momento che conosci la posizione dello 0 tramite x e y, non converrebbe maggiormente verificare se n è adiacente allo 0?!
    Infine nel momento in cui identifichi la posizione sia di n che dello 0, puoi anche scambiarli di posto, in modo che la funzione sposta() la elimini proprio.
    Al netto di quanto appena detto per la funzione controlla() farei qualcosa del genere:

    int controlla(Gioco *pc, int n)
    {
        if(pc->i != 0 && pc->campo[pc->i - 1][pc->j] == n)
        {
            pc->campo[pc->i--][pc->j] = n;
            pc->campo[pc->i][pc->j] = 0;
            return 1;
        }
        if(pc->j != 3 && pc->campo[pc->i][pc->j + 1] == n)
        {
            pc->campo[pc->i][pc->j++] = n;
            pc->campo[pc->i][pc->j] = 0;
            return 1;
        }
        if(pc->i != 3 && pc->campo[pc->i + 1][pc->j] == n)
        {
            pc->campo[pc->i++][pc->j] = n;
            pc->campo[pc->i][pc->j] = 0;
            return 1;
        }
        if(pc->j != 0 && pc->campo[pc->i][pc->j - 1] == n)
        {
            pc->campo[pc->i][pc->j--] = n;
            pc->campo[pc->i][pc->j] = 0;
            return 1;
        }
        return 0;
    }

    (ho utilizzato i e j al posto di y e x per evitare fraintendimenti)

    09/12/2023 - MattINfo ha scritto:


    essendo un programma fatto per il principale divertimento di programmare

    Nel precedente post avevo chiesto se fossi interessato, perché spesso capita di avere a che fare con persone a cui basta un programma pseudo-funzionante da portare al prof, e quindi volevo evitare di perdere tempo.
    In ogni caso per il momento credo di aver già messo abbastanza carne al fuoco!

Devi accedere o registrarti per scrivere nel forum
3 risposte