Snake in C

di il
21 risposte

Snake in C

Buongiorno a tutti/e,
da qualche mese mi sto approcciando al mondo della programmazione in C: inizialmente ho fatto diversi programmi semplici per imparare le basi del linguaggio ed ora sto cercando di cimentarmi con dei programmi più complessi, come ad esempio il sempreverde e gettonatissimo "Snake".

Questo è il codice che ho scritto:

/*	19 Febbraio 2021
	Simulazione del gioco SNAKE
	
	  0   1   2   3   4   5   6   7   8   9
	-----------------------------------------
  0	|   |   |   |   |   |   |   |   |   |   | 
	-----------------------------------------
  1	|   | * |   | O | O |   |   |   |   |   |    
	-----------------------------------------
  2	|   |   |   |   | O |   |   |   |   |   |
	------------------------------------------
  3	|   |   |   |   | O |   |   |   |   |   |
	------------------------------------------
  4	|   |   |   |   | O | O | O |   |   |   |
	------------------------------------------
  5	|   |   |   |   |   |   |   |   |   |   |
	------------------------------------------
  6	|   |   |   |   |   |   |   |   |   |   |
	------------------------------------------
	
	Memorizzo le coordinate del serpente in una lista concatenata:

	Testa del serpente						 Coda
	(1,3) -> (1,4) -> (2,4) -> (3,4) -> (4,4) -> (4,5) -> (4,6) -> NULL 
	
	Boccone
	(1,1)
	
	////// APPUNTI //////
	- Sito interessante: https://www.cprogramming.com/tutorial/lesson1.html
	- Compilatore online: https://www.cee.studio/online_cxx.html?s=cpcxx0 
	- Usare putchar() al posto di printf() ?????
*/

#include <stdio.h>
#include <stdlib.h>					// rand(), srand(), malloc(), free()
#include <time.h>	
#include <windows.h>				// SetColor(), gotoxy()
#include <conio.h>					// kbhit()

#define CAMPO_GIOCO_X 40			// larghezza del campo da gioco
#define CAMPO_GIOCO_Y 17			// altezza del campo da gioco
#define VERDE_LIMONE 10
#define BIANCO 15
#define ROSSO 12
#define RITARDO 100

typedef struct boccone
{
	int x;
	int y;
} Boc;

typedef struct serpente
{
	int x;
	int y;
	struct serpente *prossimo;
} Ser;

////// PROTOTIPI DELLE FUNZIONI //////
Boc creaBoccone ();
Ser *creaSerpente ();
void disegnaSerpente(Ser *t);
void disegnaCornice ();
Ser *muoviSerpente (Ser *testa, int direzione, int bocconeMangiato); 
int collisioneCornice (Ser *testa);
int collisioneSeStesso (Ser *testa);
int fineGioco (Ser *testa);
void ritardo (int milliSec);
void gotoxy (short x, short y);
void SetColor (unsigned short color);

// -------------------------------------------------------------------------- //
// MAIN										        //
// -------------------------------------------------------------------------- //

main ()
{
	int i, contaBocconi = 0;
	int direzione = 0;
	char c;
	Boc boccone;
	Ser *testa, *nuovo;

	srand(time(NULL));
	
	////// PAGINA INIZIALE //////
	printf ("-------------------------------\n");
	printf ("  Simulazione del gioco Snake\n");
	printf ("-------------------------------\n");
	printf ("Sposta il serpente con le freccette.\n");
	printf ("ESC per terminare il gioco.\n");
        printf ("Premi un tasto per iniziare!");
	c = getchar ();
	system ("cls");
	
	////// DISEGNO CORNICE //////
	disegnaCornice ();
	
	////// OUTPUT BOCCONI MANGIATI //////
	gotoxy (0, CAMPO_GIOCO_Y + 3);
	printf ("Hai mangiato %d bocconi", contaBocconi);	
	
	////// CREO E DISEGNO IL PRIMO BOCCONE //////
	boccone = creaBoccone ();				
	
	////// CREO IL SERPENTE //////
	testa = creaSerpente ();
	
	////// DISEGNO IL SERPENTE //////
	disegnaSerpente (testa);
		
	do
	{		
		gotoxy(0, CAMPO_GIOCO_Y + 2);				// debug
		printf("Testa: %2d, %2d", testa->x, testa->y);		// debug
		////// SE PREMO LE FRECCETTE O ESC //////
		if (kbhit())
		{
			c = getch ();		// PERCHE' SE METTO GETCHAR() NON FUNZIONA ???
			if (c == 224)		// per le freccette sono 2 i byte restituiti:
				continue;		// il primo è 0xE0 (224) per il secondo vedi giù
			switch (c)
			{
				// FRECCIA SU = 0x48 (72)
				case 72: direzione = 1;
						 break;
				// FRECCIA GIU = 0x50 (80)
				case 80: direzione = 3;
						 break;
				// FRECCIA SX = 0x4b (75)
				case 75: direzione = 4;
						 break;
				// FRECCIA DX = 0x4d (77)
				case 77: direzione = 2;
						 break;
			}
			////// ESC PER TERMINARE IL GIOCO //////
			if (c == 27)
            	break;
		}
		
		////// SPOSTO IL SERPENTE //////
		if (direzione != 0)
		{
			////// SE MANGIO IL BOCCONE ///////
			if (testa->x == boccone.x && testa->y == boccone.y)
				{
					////// BEEP //////
					printf ("\7"); 								
					contaBocconi++;
					////// CREO E DISEGNO UN NUOVO BOCCONE //////
					boccone = creaBoccone ();
					testa = muoviSerpente (testa, direzione, 1);
					////// OUTPUT BOCCONI MANGIATI //////
					gotoxy (0, CAMPO_GIOCO_Y + 3);
					printf ("Hai mangiato %d bocconi", contaBocconi);		
				}
			////// SE NON MANGIO IL BOCCONE ///////
			else
				testa = muoviSerpente (testa, direzione, 0);
		}	
	
		////// RALLENTO IL MOVIMENTO DEL SERPENTE //////
		ritardo (RITARDO);
	} while(fineGioco(testa) != 1);
	
		
	////// OUTPUT FINE GIOCO //////
	gotoxy (0, CAMPO_GIOCO_Y + 3);
	printf ("====> FINE GIOCO! Hai mangiato %d bocconi\n", contaBocconi);
	
	////// FINE MAIN //////
	return 0;
}


// -------------------------------------------------------------------------- //
// FUNZIONI									        //
// -------------------------------------------------------------------------- //

// creo un serpente di 3 elementi
Ser *creaSerpente()
{
	Ser *testa, *coda, *nuovo;	
	// 1° elemento (testa)
	nuovo = (Ser *)malloc(sizeof(Ser));		
	testa = nuovo;
	testa->x = 3;
	testa->y = 3;
	testa->prossimo = NULL;
	coda = testa;			
	// 2° elemento					
	nuovo = (Ser *)malloc(sizeof(Ser));		
	nuovo->x = 3;
	nuovo->y = 4;
	nuovo->prossimo = NULL;
	coda->prossimo = nuovo;					
	coda = nuovo;	
	// 3° elemento
	nuovo = (Ser *)malloc(sizeof(Ser));	
	nuovo->x = 3;
	nuovo->y = 5;
	nuovo->prossimo = NULL;
	coda->prossimo = nuovo;
	coda = nuovo;	
	return testa;
}

// -------------------------------------------------------------------------- //

// direzione: 1 = su, 2 = dx, 3 = giu, 4 = sx
// bocconeMangiato: 1 = tieni l'ultimo elemento, 0 = elimina l'ultimo elemento	
Ser *muoviSerpente (Ser *testa, int direzione, int bocconeMangiato)
{
	Ser *nuovo, *prec;
	nuovo = (Ser *)malloc(sizeof(Ser));
	switch (direzione)					        // DA FARE: IL SERPENTE NON PUO' INVERTIRE
	{									//          LA MARCIA DI 180°
		case 1: nuovo->x = testa->x;
				nuovo->y = testa->y - 1;
				break;
		case 2: nuovo->x = testa->x + 1;
				nuovo->y = testa->y;
				break;
		case 3: nuovo->x = testa->x;
				nuovo->y = testa->y + 1;
				break;
		case 4: nuovo->x = testa->x - 1;
				nuovo->y = testa->y;
				break;			
	}
	nuovo->prossimo = testa;
	testa = nuovo;
	////// DISEGNO LA NUOVA TESTA //////
	gotoxy(testa->x, testa->y);		
	printf("%c", 'O');
	////// CANCELLO L'ULTIMO ELEMENTO //////
	if (bocconeMangiato == 0)
	{
		while (nuovo->prossimo != NULL)
		{
			prec = nuovo;
			nuovo = nuovo->prossimo;
		}
		gotoxy(nuovo->x, nuovo->y);		 
		printf("%c", ' ');				
		free (nuovo);
		prec->prossimo = NULL;	
	}
	return testa;
}

// -------------------------------------------------------------------------- //

Boc creaBoccone ()				        // DA FARE: CONTROLLA CHE IL BOCCONE NON VENGA 
{								//          CREATO DOVE C'E' IL SERPENTE
	Boc b;			
	do 
	{
		b.y = rand() % CAMPO_GIOCO_Y + 1;
		b.x = rand() % CAMPO_GIOCO_X + 1;
		gotoxy (b.x, b.y);
		SetColor (VERDE_LIMONE);
		printf ("*");
		SetColor (BIANCO);
	
	} while (0);
	return b;
}

// -------------------------------------------------------------------------- //

void disegnaCornice ()
{
	int i;
	SetColor (ROSSO);
	// cornice superiore
	gotoxy (0, 0);
	for (i = 0; i <= CAMPO_GIOCO_X + 1; i++)		
		printf ("#");
	// cornici laterali
	for (i = 1; i <= CAMPO_GIOCO_Y; i++)
	{
		gotoxy (0, i);
		printf ("#");
		gotoxy (CAMPO_GIOCO_X + 1, i);
		printf ("#");
	}
	// cornice inferiore
	gotoxy (0, CAMPO_GIOCO_Y + 1);
	for (i = 0; i <= CAMPO_GIOCO_X + 1; i++)		
		printf ("#");
	SetColor (BIANCO);
}

// -------------------------------------------------------------------------- //

void disegnaSerpente (Ser *p)		// passo il puntatore alla testa del serpente
{
	do
	{	
		gotoxy (p->x, p->y);
		printf ("O");
		p = p->prossimo;
	} while (p != NULL);	
}

// -------------------------------------------------------------------------- //

// return: 1 = collisione, 0 = non-collisione
int collisioneCornice (Ser *testa)
{
	if (testa->y == 0 || testa->y == CAMPO_GIOCO_Y + 1 ||
	    testa->x == 0 || testa->x == CAMPO_GIOCO_X + 1)
	    return 1;
	else
		return 0;
}

// -------------------------------------------------------------------------- //

// return: 1 = collisione, 0 = non-collisione
int collisioneSeStesso (Ser *testa)
{
	Ser *p;
	p = testa->prossimo;
	while (p != NULL)
	{
		if (testa->x == p->x && testa->y == p->y)
			return 1;
		p = p->prossimo;
	}
	return 0;
}


// -------------------------------------------------------------------------- //

// return: 1 = fine gioco, 0 = continua
int fineGioco (Ser *testa)
{
	return (collisioneCornice (testa) || collisioneSeStesso (testa));
}

// -------------------------------------------------------------------------- //

void ritardo (int milliSec)
{ 
    clock_t start_time = clock ();
    while (clock() < start_time + milliSec)
        ; 
} 

// -------------------------------------------------------------------------- //

//Defines gotoxy() for ANSI C compilers.
void gotoxy (short x, short y) 
{
	COORD pos = {x, y};
	SetConsoleCursorPosition (GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

// -------------------------------------------------------------------------- //

void SetColor (unsigned short color)
{
    HANDLE hCon = GetStdHandle (STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute (hCon,color);
}

// FINE PROGRAMMA
Ho deciso di memorizzare le coordinate dei vari elementi del serpente in una lista concatenata perché mi sembrava il modo più semplice per tenere traccia dello spazio occupato dal serpente nella piano cartesiano. Inizialmente avevo previsto una funzione disegnaSerpente(), che richiamavo all'interno del ciclo principale, ma poi, a causa di un malfunzionamento che non riuscivo a correggere, ho deciso di disegnare il serpente direttamente nella funzione muoviSerpente().

Il programma sembra funzionare ma vorrei avere un'opinione per capire cosa c'è che non va o che potrebbe essere migliorato nella struttura del programma, nella sua logica, nel modo in cui è scritto, nell'aspetto grafico, ecc.

Grazie mille a chi mi risponderà.
Mauro

21 Risposte

  • Re: Snake in C

    Ho visto il codice complimenti è ben commentato e di facile lettura adesso vedo se posso migliorare qualcosa...
  • Re: Snake in C

    Ti ho riscritto la funzione disegnaCornice() adesso è un pò più bellina dimmi cosa ne pensi:
    void disegnaCornice ()
    {
    	int i;
    
    	enum cornice{lato=186,angolo_dx=187,basso_dx=188,basso_sx=200,angolo_sx=201,alto=205}; // pezzi della cornice
    
    	SetColor (ROSSO);
    	// agolo alto a sinistra
    	gotoxy(0, 0);
    	printf("%c",angolo_sx);
    
    	// cornice superiore
    	gotoxy (1, 0);
    	for (i = 0; i < CAMPO_GIOCO_X ; i++)
    		printf ("%c",alto);
    
    	// angolo alto a destra
    	gotoxy(CAMPO_GIOCO_X + 1, 0);
    	printf("%c",angolo_dx);
    
    	// cornici laterali
    	for (i = 1; i <= CAMPO_GIOCO_Y; i++)
    	{
    		gotoxy (0, i);
    		printf ("%c",lato);
    		gotoxy (CAMPO_GIOCO_X + 1, i);
    		printf ("%c",lato);
    	}
    
        // angolo basso a sinistra
    	gotoxy(0,CAMPO_GIOCO_Y + 1);
    	printf("%c",basso_sx);
    
    	// angolo basso a destra
    	gotoxy(CAMPO_GIOCO_X + 1,CAMPO_GIOCO_Y + 1);
    	printf("%c",basso_dx);
    
    	// cornice inferiore
    	gotoxy (1, CAMPO_GIOCO_Y + 1);
    	for (i = 0; i < CAMPO_GIOCO_X ; i++)
    		printf ("%c",alto);
    	SetColor (BIANCO);
    }
  • Re: Snake in C

    Qua invece c'è un boccone più carino:
    Boc creaBoccone ()				
    {								
    	Boc b;
    	int target=207;
    	do
    	{
    		b.y = rand() % CAMPO_GIOCO_Y + 1;
    		b.x = rand() % CAMPO_GIOCO_X + 1;
    		gotoxy (b.x, b.y);
    		SetColor (VERDE_LIMONE);
    		printf ("%c",target);
    		SetColor (BIANCO);
    
    	} while (0);
    	return b;
    }
  • Re: Snake in C

    Ciao, avete fatto venir voglia anche a me di cimentarmi con lo Snake!

    Ecco il codice che ho scritto:
    #include <stdio.h>
    #include <stdlib.h>
    #include <conio.h>
    #include <time.h>
    #include <windows.h>
    
    #define R 14
    #define C 28
    
    #define BITE 3
    #define SOUND 7
    #define BLOCK 79
    
    #define UP 72
    #define DOWN 80
    #define LEFT 75
    #define RIGHT 77
    
    void set_position(COORD xy)
    {
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), xy);
    }
    
    void delay(int ms)
    {
        clock_t goal = ms * CLOCKS_PER_SEC / 1000 + clock();
        while(goal > clock());
    }
    
    int get_input(const int direction)
    {
        return getch() == 224 ? getch() : direction;
    }
    
    void print_border()
    {
        printf("%c", 218);
        for(unsigned int i = 0; i < C - 2; ++i)
        {
            printf("%c", 196);
        }
        printf("%c\n", 191);
        for(unsigned int i = 0; i < R - 2; ++i)
        {
            printf("%c", 179);
            set_position((COORD){C - 1, i + 1});
            printf("%c\n", 179);
        }
        printf("%c", 192);
        for(unsigned int i = 0; i < C - 2; ++i)
        {
            printf("%c", 196);
        }
        printf("%c\n", 217);
    }
    
    void print_score(const unsigned int score)
    {
        set_position((COORD){C + 3, 1});
        printf("SCORE: %u", score);
    }
    
    void print_snake(const COORD *snake, const unsigned int dim)
    {
        for(unsigned int i = 0; i < dim; ++i)
        {
            set_position(snake[i]);
            printf("%c", BLOCK);
        }
    }
    
    void initialize_grid(int grid[R][C], const COORD *snake, const unsigned int dim)
    {
        for(unsigned int i = 1; i < R - 1; ++i)
        {
            for(unsigned int j = 1; j < C - 1; grid[i][j++] = 1);
        }
        for(unsigned int i = 0; i < dim; ++i)
        {
            grid[snake[i].Y][snake[i].X] = 0;
        }
    }
    
    void generate_bite(int grid[R][C])
    {
        unsigned int i;
        unsigned int j;
        do
        {
            i = rand() % (R - 2) + 1;
            j = rand() % (C - 2) + 1;
        }
        while(grid[i][j] != 1);
        grid[i][j] = 2;
        set_position((COORD){j, i});
        printf("%c", BITE);
    }
    
    void define_direction(int *direction, const int input)
    {
        if((*direction == UP || *direction == DOWN) && (input == LEFT || input == RIGHT) || (*direction == LEFT || *direction == RIGHT) && (input == UP || input == DOWN))
        {
            *direction = input;
        }
    }
    
    void define_new_head(COORD *snake, const unsigned int dim, const int direction)
    {
        snake[dim] = snake[dim - 1];
        if(direction == UP)
        {
            --snake[dim].Y;
        }
        else if(direction == DOWN)
        {
            ++snake[dim].Y;
        }
        else if(direction == LEFT)
        {
            --snake[dim].X;
        }
        else
        {
            ++snake[dim].X;
        }
    }
    
    int snake_update(int grid[R][C], COORD *snake, unsigned int *dim, unsigned int *score)
    {
        if(grid[snake[*dim].Y][snake[*dim].X])
        {
            if(grid[snake[*dim].Y][snake[*dim].X] == 1)
            {
                grid[snake[0].Y][snake[0].X] = 1;
                set_position(snake[0]);
                printf(" ");
                for(unsigned int i = 0; i < *dim; ++i)
                {
                    snake[i] = snake[i + 1];
                }
            }
            else
            {
                ++*dim;
                *score += 10;
                printf("%c", SOUND);
                print_score(*score);
                generate_bite(grid);
            }
            grid[snake[*dim - 1].Y][snake[*dim - 1].X] = 0;
            set_position(snake[*dim - 1]);
            printf("%c", BLOCK);
            return 1;
        }
        return 0;
    }
    
    int main()
    {
        srand(time(0));
        int grid[R][C] = {0};
        COORD snake[(R - 2) * (C - 2) + 1] = {{1, R / 2}, {2, R / 2}, {3, R / 2}};
        unsigned int dim = 3;
        unsigned int score = 0;
        int direction = RIGHT;
        print_border();
        print_score(score);
        print_snake(snake, dim);
        initialize_grid(grid, snake, dim);
        generate_bite(grid);
        set_position((COORD){0, R + 1});
        printf("Press any key to start.\n");
        define_direction(&direction, get_input(direction));
        set_position((COORD){0, R + 1});
        printf("                       ");
        do
        {
            delay(250);
            if(kbhit())
            {
                define_direction(&direction, get_input(direction));
            }
            define_new_head(snake, dim, direction);
        }
        while(snake_update(grid, snake, &dim, &score));
        set_position((COORD){0, R + 1});
        printf("GAME OVER!\n");
        return 0;
    }

    mauronew ha scritto:


    Il programma sembra funzionare ma vorrei avere un'opinione per capire cosa c'è che non va o che potrebbe essere migliorato nella struttura del programma, nella sua logica, nel modo in cui è scritto, nell'aspetto grafico, ecc.
    Avendo dato solo un'occhiata veloce al tuo programma, non saprei risponderti con precisione, ma magari il codice che ho postato potrebbe fornirti qualche spunto.


    EDIT:
    Apportata una piccola modifica al codice, se riscontrate qualche bug fatemi sapere.
  • Re: Snake in C

    giammo ha scritto:


    Ti ho riscritto la funzione disegnaCornice() adesso è un pò più bellina dimmi cosa ne pensi:
    Grazie, oggi pomeriggio la provo e poi ti dico!
  • Re: Snake in C

    Nippolo ha scritto:


    Ciao, avete fatto venir voglia anche a me di cimentarmi con lo Snake!
    Grazie Nippolo, oggi pomeriggio mi studio il tuo codice...ti chiedo solo se per favore puoi commentarlo in modo da rendermi un po' meno complicata la comprensione, grazie!
  • Re: Snake in C

    mauronew ha scritto:


    Grazie Nippolo, oggi pomeriggio mi studio il tuo codice...ti chiedo solo se per favore puoi commentarlo in modo da rendermi un po' meno complicata la comprensione, grazie!
    Provo a spiegare un po' l'idea di base, poi se qualche parte del codice non ti è chiara chiedi pure!
    In pratica per tenere traccia delle coordinate del serpente ho utilizzato un array al posto di una lista concatenata. Tale array ha una capienza massima pari al numero di celle della griglia, mentre i primi dim elementi rappresentano le coordinate dei vari "pezzi" del serpente partendo dalla coda fino alla testa (in pratica dim rappresenta la lunghezza attuale del serpente).
    Inoltre ho introdotto una matrice di interi grid contenente:
    - 0 in corrispondenza della cornice e del serpente;
    - 2 in corrispondenza del boccone;
    - 1 altrove.
    In questo modo un'eventuale collisione del serpente con il bordo esterno o con sé stesso può essere identificata con lo stesso metodo in maniera molto semplice.
  • Re: Snake in C

    Con il sistema a lista concatenata il movimento è più macchinoso e meno fluido, ma non sono sicuro se derivi da quello...
  • Re: Snake in C

    Nippolo ha scritto:


    In pratica per tenere traccia delle coordinate del serpente ho utilizzato un array al posto di una lista concatenata. Tale array ha una capienza massima pari al numero di celle della griglia, mentre i primi dim elementi rappresentano le coordinate dei vari "pezzi" del serpente partendo dalla coda fino alla testa (in pratica dim rappresenta la lunghezza attuale del serpente).
    La parte evidenziata presuppone che ad ogni movimento a vuoto (ossia senza mangiare il boccone) del serpente vadano effettuate dim assegnazioni. Per evitare ciò avrei pensato di implementare una sorta di "array circolare":
    #include <stdio.h>
    #include <stdlib.h>
    #include <conio.h>
    #include <time.h>
    #include <windows.h>
    
    #define R 14
    #define C 28
    #define DIM (R - 2) * (C - 2) + 1
    #define START_LENGTH 3
    
    #define BITE 3
    #define SOUND 7
    #define BLOCK 79
    
    #define UP 72
    #define DOWN 80
    #define LEFT 75
    #define RIGHT 77
    
    void set_position(const COORD xy)
    {
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), xy);
    }
    
    void delay(const int ms)
    {
        clock_t goal = ms * CLOCKS_PER_SEC / 1000 + clock();
        while(goal > clock());
    }
    
    int get_input(const int direction)
    {
        return getch() == 224 ? getch() : direction;
    }
    
    void print_border()
    {
        printf("%c", 218);
        for(unsigned int i = 0; i < C - 2; ++i)
        {
            printf("%c", 196);
        }
        printf("%c\n", 191);
        for(unsigned int i = 0; i < R - 2; ++i)
        {
            printf("%c", 179);
            set_position((COORD){C - 1, i + 1});
            printf("%c\n", 179);
        }
        printf("%c", 192);
        for(unsigned int i = 0; i < C - 2; ++i)
        {
            printf("%c", 196);
        }
        printf("%c\n", 217);
    }
    
    void print_score(const unsigned int score)
    {
        set_position((COORD){C + 3, 1});
        printf("SCORE: %u", score);
    }
    
    void print_snake(const COORD *snake, const unsigned int dim)
    {
        for(unsigned int i = 0; i < dim; ++i)
        {
            set_position(snake[i]);
            printf("%c", BLOCK);
        }
    }
    
    void initialize_grid(int grid[R][C], const COORD *snake, const unsigned int dim)
    {
        for(unsigned int i = 1; i < R - 1; ++i)
        {
            for(unsigned int j = 1; j < C - 1; grid[i][j++] = 1);
        }
        for(unsigned int i = 0; i < dim; ++i)
        {
            grid[snake[i].Y][snake[i].X] = 0;
        }
    }
    
    void generate_bite(int grid[R][C])
    {
        unsigned int i;
        unsigned int j;
        do
        {
            i = rand() % (R - 2) + 1;
            j = rand() % (C - 2) + 1;
        }
        while(grid[i][j] != 1);
        grid[i][j] = 2;
        set_position((COORD){j, i});
        printf("%c", BITE);
    }
    
    void define_direction(int *direction, const int input)
    {
        if((*direction == UP || *direction == DOWN) && (input == LEFT || input == RIGHT) || (*direction == LEFT || *direction == RIGHT) && (input == UP || input == DOWN))
        {
            *direction = input;
        }
    }
    
    void define_new_head(COORD *snake, COORD **head, const int direction)
    {
        COORD *temp = *head;
        *head = *head == snake + DIM - 1 ? snake : *head + 1;
        **head = *temp;
        if(direction == UP)
        {
            --(*head)->Y;
        }
        else if(direction == DOWN)
        {
            ++(*head)->Y;
        }
        else if(direction == LEFT)
        {
            --(*head)->X;
        }
        else
        {
            ++(*head)->X;
        }
    }
    
    int snake_update(int grid[R][C], COORD *snake, COORD **tail, COORD *new_head, unsigned int *score)
    {
        if(grid[new_head->Y][new_head->X])
        {
            if(grid[new_head->Y][new_head->X] == 1)
            {
                grid[(*tail)->Y][(*tail)->X] = 1;
                set_position(**tail);
                printf(" ");
                *tail = *tail == snake + DIM - 1 ? snake : *tail + 1;
            }
            else
            {
                *score += 10;
                printf("%c", SOUND);
                print_score(*score);
                generate_bite(grid);
            }
            grid[new_head->Y][new_head->X] = 0;
            set_position(*new_head);
            printf("%c", BLOCK);
            return 1;
        }
        return 0;
    }
    
    int main()
    {
        srand(time(0));
        int grid[R][C] = {0};
        int direction = RIGHT;
        unsigned int score = 0;
        COORD snake[DIM] = {{1, R / 2}, {2, R / 2}, {3, R / 2}};
        COORD *tail = snake;
        COORD *head = snake + START_LENGTH - 1;
        print_border();
        print_score(score);
        print_snake(snake, START_LENGTH);
        initialize_grid(grid, snake, START_LENGTH);
        generate_bite(grid);
        set_position((COORD){0, R + 1});
        printf("Press any key to start.\n");
        define_direction(&direction, get_input(direction));
        set_position((COORD){0, R + 1});
        printf("                       ");
        do
        {
            delay(230);
            if(kbhit())
            {
                define_direction(&direction, get_input(direction));
            }
            define_new_head(snake, &head, direction);
        }
        while(snake_update(grid, snake, &tail, head, &score));
        set_position((COORD){0, R + 1});
        printf("GAME OVER!\n");
        return 0;
    }
    Le funzioni modificate sono:
    - define_new_head();
    - snake_update();
    - main().

    Ora non so quanto questo cambiamento possa essere incisivo a livello di prestazioni, ma rispetto a prima il costo computazionale dovrebbe restare costante anche al crescere della serpentina. Che ne pensate?

    Al crescere della serpentina non rimane però costante la probabilità di generare il boccone in una casella casuale "valida". Invece di andare a tentativi pensavo a qualche metodo più "deterministico" per farlo, ma al momento non mi viene in mente nulla di particolarmente valido...
  • Re: Snake in C

    Nippolo ha scritto:


    Al crescere della serpentina non rimane però costante la probabilità di generare il boccone in una casella casuale "valida". Invece di andare a tentativi pensavo a qualche metodo più "deterministico" per farlo, ma al momento non mi viene in mente nulla di particolarmente valido...
    Ecco il codice aggiornato con una nuova versione di generate_bite():
    #include <stdio.h>
    #include <stdlib.h>
    #include <conio.h>
    #include <time.h>
    #include <windows.h>
    
    #define R 14
    #define C 28
    #define DIM (R - 2) * (C - 2)
    #define START_LENGTH 3
    
    #define BITE 3
    #define SOUND 7
    #define BLOCK 79
    
    #define UP 72
    #define DOWN 80
    #define LEFT 75
    #define RIGHT 77
    
    void set_position(const COORD xy)
    {
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), xy);
    }
    
    void delay(const int ms)
    {
        clock_t goal = ms * CLOCKS_PER_SEC / 1000 + clock();
        while(goal > clock());
    }
    
    int get_input(const int direction)
    {
        return getch() == 224 ? getch() : direction;
    }
    
    void print_border()
    {
        printf("%c", 218);
        for(unsigned int i = 0; i < C - 2; ++i)
        {
            printf("%c", 196);
        }
        printf("%c\n", 191);
        for(unsigned int i = 0; i < R - 2; ++i)
        {
            printf("%c", 179);
            set_position((COORD){C - 1, i + 1});
            printf("%c\n", 179);
        }
        printf("%c", 192);
        for(unsigned int i = 0; i < C - 2; ++i)
        {
            printf("%c", 196);
        }
        printf("%c\n", 217);
    }
    
    void print_score(const unsigned int score)
    {
        set_position((COORD){C + 3, 1});
        printf("SCORE: %u", score);
    }
    
    void print_snake(const COORD *snake, const unsigned int dim)
    {
        for(unsigned int i = 0; i < dim; ++i)
        {
            set_position(snake[i]);
            printf("%c", BLOCK);
        }
    }
    
    void initialize_grid(int grid[R][C], const COORD *snake, const unsigned int dim)
    {
        for(unsigned int i = 1; i < R - 1; ++i)
        {
            for(unsigned int j = 1; j < C - 1; grid[i][j++] = 1);
        }
        for(unsigned int i = 0; i < dim; ++i)
        {
            grid[snake[i].Y][snake[i].X] = 0;
        }
    }
    
    void generate_bite(int grid[R][C], const unsigned int score)
    {
        unsigned int r = rand() % (DIM - score / 10 - START_LENGTH) + 1;
        for(unsigned int i = 1; i < R - 1; ++i)
        {
            for(unsigned int j = 1; j < C - 1; ++j)
            {
                if(!(r -= grid[i][j] == 1))
                {
                    grid[i][j] = 2;
                    set_position((COORD){j, i});
                    printf("%c", BITE);
                    return;
                }
            }
        }
    }
    
    void define_direction(int *direction, const int input)
    {
        if((*direction == UP || *direction == DOWN) && (input == LEFT || input == RIGHT) || (*direction == LEFT || *direction == RIGHT) && (input == UP || input == DOWN))
        {
            *direction = input;
        }
    }
    
    void define_new_head(COORD *snake, COORD **head, const int direction)
    {
        COORD *temp = *head;
        *head = *head == snake + DIM - 1 ? snake : *head + 1;
        **head = *temp;
        if(direction == UP)
        {
            --(*head)->Y;
        }
        else if(direction == DOWN)
        {
            ++(*head)->Y;
        }
        else if(direction == LEFT)
        {
            --(*head)->X;
        }
        else
        {
            ++(*head)->X;
        }
    }
    
    int snake_update(int grid[R][C], COORD *snake, COORD **tail, COORD *new_head, unsigned int *score)
    {
        if(grid[new_head->Y][new_head->X])
        {
            if(grid[new_head->Y][new_head->X] == 1)
            {
                grid[(*tail)->Y][(*tail)->X] = 1;
                set_position(**tail);
                printf(" ");
                *tail = *tail == snake + DIM - 1 ? snake : *tail + 1;
            }
            else
            {
                *score += 10;
                printf("%c", SOUND);
                print_score(*score);
                generate_bite(grid, *score);
            }
            grid[new_head->Y][new_head->X] = 0;
            set_position(*new_head);
            printf("%c", BLOCK);
            return 1;
        }
        return 0;
    }
    
    int main()
    {
        srand(time(0));
        int grid[R][C] = {0};
        int direction = RIGHT;
        unsigned int score = 0;
        COORD snake[DIM] = {{1, R / 2}, {2, R / 2}, {3, R / 2}};
        COORD *tail = snake;
        COORD *head = snake + START_LENGTH - 1;
        print_border();
        print_score(score);
        print_snake(snake, START_LENGTH);
        initialize_grid(grid, snake, START_LENGTH);
        generate_bite(grid, score);
        set_position((COORD){0, R + 1});
        printf("Press any key to start.\n");
        define_direction(&direction, get_input(direction));
        set_position((COORD){0, R + 1});
        printf("                       ");
        do
        {
            delay(230);
            if(kbhit())
            {
                define_direction(&direction, get_input(direction));
            }
            define_new_head(snake, &head, direction);
        }
        while(snake_update(grid, snake, &tail, head, &score));
        set_position((COORD){0, R + 1});
        printf("GAME OVER!\n");
        return 0;
    }
    In pratica genero un numero casuale n compreso tra 1 e il numero di caselle "vuote" della mappa, per poi identificare l'n-esima casella vuota e piazzarci il boccone.

    Se riscontrate qualche bug fatemi sapere!
  • Re: Snake in C

    Ho creato 4 diversi "livelli" implementando la possibilità di attraversare la cornice e di piazzare un ostacolo all'interno della mappa:
    #include <stdio.h>
    #include <stdlib.h>
    #include <conio.h>
    #include <time.h>
    #include <windows.h>
    
    #define R 15
    #define C 29
    #define DIM (R - 2) * (C - 2)
    #define START_LENGTH 3
    #define DIM_OBSTACLE 31
    
    #define BITE 3
    #define SOUND 7
    #define BLOCK 79
    
    #define UL 201
    #define UR 187
    #define DL 200
    #define DR 188
    #define HOR 205
    #define VER 186
    #define CROSS 206
    #define UL_THROUGH 218
    #define UR_THROUGH 191
    #define DL_THROUGH 192
    #define DR_THROUGH 217
    #define HOR_THROUGH 196
    #define VER_THROUGH 179
    
    #define UP 72
    #define DOWN 80
    #define LEFT 75
    #define RIGHT 77
    
    COORD coord_obstacle[DIM_OBSTACLE] = {{ 3, R / 2}, { 4, R / 2}, { 5, R / 2}, { 6, R / 2}, { 7, R / 2}, { 8, R / 2}, { 9, R / 2},
                                          {10, R / 2}, {11, R / 2}, {12, R / 2}, {13, R / 2}, {15, R / 2}, {16, R / 2}, {17, R / 2},
                                          {18, R / 2}, {19, R / 2}, {20, R / 2}, {21, R / 2}, {22, R / 2}, {23, R / 2}, {24, R / 2},
                                          {25, R / 2}, {C / 2,  3}, {C / 2,  4}, {C / 2,  5}, {C / 2,  6}, {C / 2,  8}, {C / 2,  9},
                                          {C / 2, 10}, {C / 2, 11}, {C / 2, R / 2}};
    
    int char_obstacle[DIM_OBSTACLE] = {HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR,
                                       HOR, HOR, HOR, HOR, VER, VER, VER, VER, VER, VER, VER, VER, CROSS};
    
    void set_position(const COORD xy)
    {
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), xy);
    }
    
    void delay(const int ms)
    {
        clock_t goal = ms * CLOCKS_PER_SEC / 1000 + clock();
        while(goal > clock());
    }
    
    int get_input(const int direction)
    {
        return getch() == 224 ? getch() : direction;
    }
    
    void choose_settings(int *through, int *obstacle)
    {
        char c;
        do
        {
            printf("THROUGH MODE (y/n) --> ");
            scanf(" %c", &c);
        }
        while(c != 'y' && c != 'n');
        *through = c == 'y';
        do
        {
            printf("OBSTACLES (y/n) --> ");
            scanf(" %c", &c);
        }
        while(c != 'y' && c != 'n');
        *obstacle = c == 'y';
        system("CLS");
    }
    
    void initialize_grid(int grid[R][C])
    {
        for(unsigned int i = 1; i < R - 1; ++i)
        {
            for(unsigned int j = 1; j < C - 1; grid[i][j++] = 1);
        }
    }
    
    void print_and_apply_border(int grid [R][C], const int through)
    {
        printf("%c", through ? UL_THROUGH : UL);
        for(unsigned int i = 0; i < C - 2; ++i)
        {
            printf("%c", through ? HOR_THROUGH : HOR);
        }
        printf("%c\n", through ? UR_THROUGH : UR);
        for(unsigned int i = 0; i < R - 2; ++i)
        {
            printf("%c", through ? VER_THROUGH : VER);
            set_position((COORD){C - 1, i + 1});
            printf("%c\n", through ? VER_THROUGH : VER);
        }
        printf("%c", through ? DL_THROUGH : DL);
        for(unsigned int i = 0; i < C - 2; ++i)
        {
            printf("%c", through ? HOR_THROUGH : HOR);
        }
        printf("%c\n", through ? DR_THROUGH : DR);
        if(!through)
        {
            for(unsigned int j = 0; j < C; ++j)
            {
                grid[0][j] = grid[R - 1][j] = 0;
            }
            for(unsigned int i = 1; i < R - 1; ++i)
            {
                grid[i][0] = grid[i][C - 1] = 0;
            }
        }
    }
    
    void print_and_apply_snake(int grid[R][C], const COORD *snake)
    {
        for(unsigned int i = 0; i < START_LENGTH; ++i)
        {
            set_position(snake[i]);
            printf("%c", BLOCK);
            grid[snake[i].Y][snake[i].X] = 0;
        }
    }
    
    void print_and_apply_obstacle(int grid[R][C])
    {
        for(unsigned int i = 0; i < DIM_OBSTACLE; ++i)
        {
            set_position(coord_obstacle[i]);
            printf("%c", char_obstacle[i]);
            grid[coord_obstacle[i].Y][coord_obstacle[i].X] = 0;
        }
    }
    
    void print_score(const unsigned int score)
    {
        set_position((COORD){C + 3, 1});
        printf("SCORE: %u", score);
    }
    
    void generate_bite(int grid[R][C], const unsigned int score, const int obstacle)
    {
        unsigned int r = rand() % (DIM - score / 10 - START_LENGTH - obstacle * DIM_OBSTACLE) + 1;
        for(unsigned int i = 1; i < R - 1; ++i)
        {
            for(unsigned int j = 1; j < C - 1; ++j)
            {
                if(!(r -= grid[i][j] == 1))
                {
                    grid[i][j] = 2;
                    set_position((COORD){j, i});
                    printf("%c", BITE);
                    return;
                }
            }
        }
    }
    
    void define_direction(int *direction, const int input)
    {
        if((*direction == UP || *direction == DOWN) && (input == LEFT || input == RIGHT) || (*direction == LEFT || *direction == RIGHT) && (input == UP || input == DOWN))
        {
            *direction = input;
        }
    }
    
    void define_new_head(COORD *snake, COORD **head, const int direction, const int through)
    {
        COORD new_head = **head;
        if(direction == UP)
        {
            new_head.Y = through && new_head.Y == 1 ? R - 2 : new_head.Y - 1;
        }
        else if(direction == DOWN)
        {
            new_head.Y = through && new_head.Y == R - 2 ? 1 : new_head.Y + 1;
        }
        else if(direction == LEFT)
        {
            new_head.X = through && new_head.X == 1 ? C - 2 : new_head.X - 1;
        }
        else
        {
            new_head.X = through && new_head.X == C - 2 ? 1 : new_head.X + 1;
        }
        *head = *head == snake + DIM - 1 ? snake : *head + 1;
        **head = new_head;
    }
    
    int snake_update(int grid[R][C], COORD *snake, COORD **tail, COORD *new_head, unsigned int *score, const int obstacle)
    {
        if(grid[new_head->Y][new_head->X])
        {
            if(grid[new_head->Y][new_head->X] == 1)
            {
                grid[(*tail)->Y][(*tail)->X] = 1;
                set_position(**tail);
                printf(" ");
                *tail = *tail == snake + DIM - 1 ? snake : *tail + 1;
            }
            else
            {
                *score += 10;
                printf("%c", SOUND);
                print_score(*score);
                generate_bite(grid, *score, obstacle);
            }
            grid[new_head->Y][new_head->X] = 0;
            set_position(*new_head);
            printf("%c", BLOCK);
            return 1;
        }
        return 0;
    }
    
    int main()
    {
        srand(time(0));
        int grid[R][C];
        int through;
        int obstacle;
        int direction = RIGHT;
        unsigned int score = 0;
        COORD snake[DIM] = {{2, R / 4}, {3, R / 4}, {4, R / 4}};
        COORD *tail = snake;
        COORD *head = snake + START_LENGTH - 1;
        choose_settings(&through, &obstacle);
        initialize_grid(grid);
        print_and_apply_border(grid, through);
        print_and_apply_snake(grid, snake);
        print_score(score);
        if(obstacle)
        {
            print_and_apply_obstacle(grid);
        }
        generate_bite(grid, score, obstacle);
        set_position((COORD){0, R + 1});
        printf("Press any key to start.\n");
        define_direction(&direction, get_input(direction));
        set_position((COORD){0, R + 1});
        printf("                       ");
        do
        {
            delay(240);
            if(kbhit())
            {
                define_direction(&direction, get_input(direction));
            }
            define_new_head(snake, &head, direction, through);
        }
        while(snake_update(grid, snake, &tail, head, &score, obstacle));
        set_position((COORD){0, R + 1});
        printf("GAME OVER!\n");
        return 0;
    }
    L'ho testato poco, ma sembra funzionare!
  • Re: Snake in C

    Ciao Nippolo,
    scusa il ritardo ma ho avuto tempo solo ora per leggere il tuo codice (mi riferisco alla prima versione che hai mandato), ora ti devo chiedere delle spiegazioni:

    1) Come mai hai creato un vettore così grande per contenere i vari elementi dello snake?
    
    COORD snake[(R - 2) * (C - 2) + 1] = {{1, R / 2}, {2, R / 2}, {3, R / 2}};
    
    2) Nella seguente funzione perché passi il riferimento allo snake come "const"?
    
    void print_snake (const COORD *snake, const unsigned int dim)
    {
        for (unsigned int i = 0; i < dim; ++i)
        {
            set_position (snake[i]);
            printf ("%c", BLOCK);
        }
    }
    
    3) Non capisco cosa vuol dire il 152 nella seguente funzione
    
    void define_direction(int *direction, const int input)
    {
        if (*direction != input && *direction + input != 152)
        {
            *direction = input;
        }
    }
    
    4) Veramente molto elegante e sintetico il modo in cui leggi la pressione di una delle quattro freccette!
    
    int get_input (const int direction)
    {
        return getch () == 224 ? getch () : direction;
    }
    
    Devo ancora controllare le funzioni define_new_head() e snake_update(), probabilmente avrò dei dubbi anche li...comunque mi piace come hai scritto il codice, è decisamente di un altro livello rispetto al mio
  • Re: Snake in C

    Ciao!

    mauronew ha scritto:


    (mi riferisco alla prima versione che hai mandato)
    Il codice attualmente presente nel mio primo post in questo topic credo sia leggermente diverso da quello in tuo possesso. Il motivo è che il giorno successivo ho editato il messaggio per apportare delle modifiche al codice. In pratica ho riscritto la funzione define_direction() e forse (non ricordo bene) modificata una printf(). Quindi a scanso di equivoci sarebbe meglio riferirci al codice aggiornato.

    mauronew ha scritto:


    3) Non capisco cosa vuol dire il 152 nella seguente funzione
    Come già detto lascia perdere il vecchio codice e considera la nuova versione di define_direction()!
    In ogni caso il 152 deriva da un ragionamento un po' contorto sul fatto che

    UP + DOWN = LEFT + RIGHT = 152

    mauronew ha scritto:


    1) Come mai hai creato un vettore così grande per contenere i vari elementi dello snake?
    Come già detto in un precedente post ho creato una matrice di interi chiamata grid di dimensioni RxC che rappresenta il campo di gioco compresa la cornice (ossia il bordo esterno che se toccato ci fa perdere). Quindi le celle della mappa in cui il serpente può muoversi sono date da R*C meno le celle della cornice (tale differenza è appunto pari a (R-2)*(C-2)). Si deduce quindi che in teoria la massima lunghezza dello snake sia appunto (R-2)*(C-2), da cui deriva la dimensione dell'array. Il +1 serve per non andare a leggere zone di memoria che non competono all'array nel caso in cui il serpentone riempia l'intera mappa.

    mauronew ha scritto:


    2) Nella seguente funzione perché passi il riferimento allo snake come "const"?
    Di solito quando non devo apportare modifiche preferisco esplicitarlo utilizzando il const nei parametri della funzione. Ciò costituisce secondo me una norma di "buona programmazione", ma si può tranquillamente farne a meno!

    mauronew ha scritto:


    Devo ancora controllare le funzioni define_new_head() e snake_update(), probabilmente avrò dei dubbi anche li...comunque mi piace come hai scritto il codice, è decisamente di un altro livello rispetto al mio
    Se hai dubbi chiedi pure!
    Poi più in là, quando questo codice ti sarà del tutto chiaro, potrai se vuoi dare un'occhiata anche ai codici postati nel mio terzo, quarto e quinto post, in cui introduco rispettivamente una sorta di "array circolare", una versione "più deterministica" di generate_bite(), la possibilità di simulare "diversi livelli".
  • Re: Snake in C

    @Nippolo

    Il codice mi è piaciuto, è ben strutturato, ci si mette bene le mani.

    Ho messo la testa che si orienta, in velocità ho usato un'orribile variabile pubblica, ma si può integrare nelle chiamate con le altre, oppure una struct con tutte le caratteristiche del serpente...

    Scusa mauronew, il tuo codice non l'ho ancora guardato.
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <conio.h>
    #include <time.h>
    #include <windows.h>
    
    #define R 15
    #define C 29
    #define DIM (R - 2) * (C - 2)
    #define START_LENGTH 3 // min 2 max 7
    #define DIM_OBSTACLE 31
    
    #define BITE 3
    #define SOUND 7
    #define BLOCK_BODY 79
    #define BLOCK_BODY_U 30
    #define BLOCK_BODY_L 17
    #define BLOCK_BODY_R 16
    #define BLOCK_BODY_D 31
    
    
    
    #define UL 201
    #define UR 187
    #define DL 200
    #define DR 188
    #define HOR 205
    #define VER 186
    #define CROSS 206
    #define UL_THROUGH 218
    #define UR_THROUGH 191
    #define DL_THROUGH 192
    #define DR_THROUGH 217
    #define HOR_THROUGH 196
    #define VER_THROUGH 179
    
    #define UP 72
    #define DOWN 80
    #define LEFT 75
    #define RIGHT 77
    
    int head_direction = BLOCK_BODY_R;
    
    COORD coord_obstacle[DIM_OBSTACLE] = {{ 3, R / 2}, { 4, R / 2}, { 5, R / 2}, { 6, R / 2}, { 7, R / 2}, { 8, R / 2}, { 9, R / 2},
                                          {10, R / 2}, {11, R / 2}, {12, R / 2}, {13, R / 2}, {15, R / 2}, {16, R / 2}, {17, R / 2},
                                          {18, R / 2}, {19, R / 2}, {20, R / 2}, {21, R / 2}, {22, R / 2}, {23, R / 2}, {24, R / 2},
                                          {25, R / 2}, {C / 2,  3}, {C / 2,  4}, {C / 2,  5}, {C / 2,  6}, {C / 2,  8}, {C / 2,  9},
                                          {C / 2, 10}, {C / 2, 11}, {C / 2, R / 2}};
    
    int char_obstacle[DIM_OBSTACLE] = {HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR, HOR,
                                       HOR, HOR, HOR, HOR, VER, VER, VER, VER, VER, VER, VER, VER, CROSS};
    
    void set_position(const COORD xy)
    {
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), xy);
    }
    
    void delay(const int ms)
    {
        clock_t goal = ms * CLOCKS_PER_SEC / 1000 + clock();
        while(goal > clock());
    }
    
    int get_input(const int direction)
    {
        return getch() == 224 ? getch() : direction;
    }
    
    void choose_settings(int *through, int *obstacle)
    {
        char c;
        do
        {
            printf("THROUGH MODE (y/n) --> ");
            scanf(" %c", &c);
        }
        while(c != 'y' && c != 'n');
        *through = c == 'y';
        do
        {
            printf("OBSTACLES (y/n) --> ");
            scanf(" %c", &c);
        }
        while(c != 'y' && c != 'n');
        *obstacle = c == 'y';
        system("CLS");
    }
    
    void initialize_grid(int grid[R][C])
    {
        for(unsigned int i = 1; i < R - 1; ++i)
        {
            for(unsigned int j = 1; j < C - 1; grid[i][j++] = 1);
        }
    }
    
    void print_and_apply_border(int grid [R][C], const int through)
    {
        printf("%c", through ? UL_THROUGH : UL);
        for(unsigned int i = 0; i < C - 2; ++i)
        {
            printf("%c", through ? HOR_THROUGH : HOR);
        }
        printf("%c\n", through ? UR_THROUGH : UR);
        for(unsigned int i = 0; i < R - 2; ++i)
        {
            printf("%c", through ? VER_THROUGH : VER);
            set_position((COORD){C - 1, i + 1});
            printf("%c\n", through ? VER_THROUGH : VER);
        }
        printf("%c", through ? DL_THROUGH : DL);
        for(unsigned int i = 0; i < C - 2; ++i)
        {
            printf("%c", through ? HOR_THROUGH : HOR);
        }
        printf("%c\n", through ? DR_THROUGH : DR);
        if(!through)
        {
            for(unsigned int j = 0; j < C; ++j)
            {
                grid[0][j] = grid[R - 1][j] = 0;
            }
            for(unsigned int i = 1; i < R - 1; ++i)
            {
                grid[i][0] = grid[i][C - 1] = 0;
            }
        }
    }
    
    void print_and_apply_snake(int grid[R][C], const COORD *snake)
    {
    
        for(unsigned int i = 0; i < START_LENGTH-1; ++i)
        {
            set_position(snake[i]);
            printf("%c", BLOCK_BODY);
            grid[snake[i].Y][snake[i].X] = 0;
        }
        set_position(snake[START_LENGTH-1]);
        printf("%c", head_direction);
        grid[snake[START_LENGTH-1].Y][snake[START_LENGTH-1].X] = 0;
    }
    
    void print_and_apply_obstacle(int grid[R][C])
    {
        for(unsigned int i = 0; i < DIM_OBSTACLE; ++i)
        {
            set_position(coord_obstacle[i]);
            printf("%c", char_obstacle[i]);
            grid[coord_obstacle[i].Y][coord_obstacle[i].X] = 0;
        }
    }
    
    void print_score(const unsigned int score)
    {
        set_position((COORD){C + 3, 1});
        printf("SCORE: %u", score);
    }
    
    void generate_bite(int grid[R][C], const unsigned int score, const int obstacle)
    {
        unsigned int r = rand() % (DIM - score / 10 - START_LENGTH - obstacle * DIM_OBSTACLE) + 1;
        for(unsigned int i = 1; i < R - 1; ++i)
        {
            for(unsigned int j = 1; j < C - 1; ++j)
            {
                if(!(r -= grid[i][j] == 1))
                {
                    grid[i][j] = 2;
                    set_position((COORD){j, i});
                    printf("%c", BITE);
                    return;
                }
            }
        }
    }
    
    void define_direction(int *direction, const int input)
    {
        if((*direction == UP || *direction == DOWN) && (input == LEFT || input == RIGHT) || (*direction == LEFT || *direction == RIGHT) && (input == UP || input == DOWN))
        {
            *direction = input;
        }
    }
    
    void define_new_head(COORD *snake, COORD **head, const int direction, const int through)
    {
        COORD new_head = **head;
        if(direction == UP)
        {
            head_direction = BLOCK_BODY_U;
            new_head.Y = through && new_head.Y == 1 ? R - 2 : new_head.Y - 1;
        }
        else if(direction == DOWN)
        {
            head_direction = BLOCK_BODY_D;
            new_head.Y = through && new_head.Y == R - 2 ? 1 : new_head.Y + 1;
        }
        else if(direction == LEFT)
        {
            head_direction = BLOCK_BODY_L;
            new_head.X = through && new_head.X == 1 ? C - 2 : new_head.X - 1;
        }
        else
        {
            head_direction = BLOCK_BODY_R;
            new_head.X = through && new_head.X == C - 2 ? 1 : new_head.X + 1;
        }
        *head = *head == snake + DIM - 1 ? snake : *head + 1;
        **head = new_head;
    }
    
    int snake_update(int grid[R][C], COORD *snake, COORD **tail, COORD *new_head, unsigned int *score, const int obstacle, COORD *old_head)
    {
        if(grid[new_head->Y][new_head->X])
        {
            if(grid[new_head->Y][new_head->X] == 1)
            {
                grid[(*tail)->Y][(*tail)->X] = 1;
                set_position(**tail);
                printf(" ");
                *tail = *tail == snake + DIM - 1 ? snake : *tail + 1;
            }
            else
            {
                *score += 10;
                printf("%c", SOUND);
                print_score(*score);
                generate_bite(grid, *score, obstacle);
            }
    
            grid[new_head->Y][new_head->X] = 0;
            set_position(*old_head);
            printf("%c", BLOCK_BODY);
            set_position(*new_head);
            printf("%c", head_direction);
            return 1;
        }
        return 0;
    }
    
    int main()
    {
        srand(time(0));
        int grid[R][C];
        int through;
        int obstacle;
        int direction = RIGHT;
        unsigned int score = 0;
        COORD snake[DIM] = {{2, R / 4}, {3, R / 4}, {4, R / 4}, {5, R / 4}, {6, R / 4}, {7, R / 4}, {8, R / 4}};
        COORD *tail = snake;
        COORD *head = snake + START_LENGTH - 1;
        COORD *old = head;
        choose_settings(&through, &obstacle);
        initialize_grid(grid);
        print_and_apply_border(grid, through);
        print_and_apply_snake(grid, snake);
        print_score(score);
        if(obstacle)
        {
            print_and_apply_obstacle(grid);
        }
        generate_bite(grid, score, obstacle);
        set_position((COORD){0, R + 1});
        printf("Press any key to start.\n");
        define_direction(&direction, get_input(direction));
        set_position((COORD){0, R + 1});
        printf("                       ");
        do
        {
            delay(200);
            if(kbhit())
            {
                define_direction(&direction, get_input(direction));
            }
            old=head;
            define_new_head(snake, &head, direction, through);
        }
        while(snake_update(grid, snake, &tail, head, &score, obstacle, old));
        set_position((COORD){0, R + 1});
        printf("GAME OVER!\n");
        return 0;
    }
    
    Qui il mio Snake (Nibblerer_2), con schemi personalizzabili e scorrimento fluido del serpente, in VB.Net:
    http://www.pierotofy.it/pages/sorgenti/dettagli/19600-Nibblerer2/
Devi accedere o registrarti per scrivere nel forum
21 risposte