Sviluppare Snake in C

Creato il 10 giugno 2012 da Ketek @CarloVentrella

Vediamo come realizzare il celebre gioco di snake in C: è un algoritmo non particolarmente complesso e, se compreso bene, molto semplice da realizzare.

Prima di visualizzare il codice vi spiego il lato teorico del codice analizzando le varie sezioni in cui è suddiviso:

  • OrganizzaHeader: funzione che scrive il titolo del gioco, il punteggio e il timer di partenza;
  • disegnaQuadro: disegna il quadro di gioco;
  • disegnaSnake: disegna il serpente di lunghezza quattro al centro del quadro di gioco;
  • aggiornaPunteggio: aggiorna il punteggio di gioco ogni qualvolta il serpente mangia del cibo
  • generaCibo: funzione che inserisce  il cibo sulla base della funzione random() che genera numeri casuali;
  • start: è la funzione che gestisce un timer di 3 secondi, alla scadenza di questi viene richiamata la funzione muovi();
  • muovi: è la porzione di codice principale che gestisce il movimento del serpente, e quindi controlla se mangia del cibo o se mangia la sua stessa coda, o supera il limite del quadro di gioco.

A questo punto non ci rimane che spiegare come ho gestito il serpente e, quindi, il suo movimento: ho utilizzato due array che contengono le coordinate x ed y delle singole parti del serpente a partire da snake_row[0] e snake_cols[0] che corrispondono alla testa.

Il serpente è disegnato attraverso un ciclo (disegnaSnake()) che inserisce un' asterisco nelle posizioni contenute nei due array. Il movimento avviene quindi cancellando l'asterisco 'coda' ,aggiungendo l'asterisco 'testa'  e facendo slittare il contenuto dei due array in modo tale che ogni parte del serpente assuma le coordinate della parte precedente. L'unica parte che non 'slitta' in questo modo è la testa che assumere i valori in base al tasto precedentemente premuto.

Vediamo ora il codice:

 
#include <stdio.h>
#include<windows.h>
#include <windows.h>
#include <TIME.H>
#include <stdlib.h>
#include <conio.h>
#include<dos.h>
# include<unistd.h>
 
// dimensioni quadro di gioco
#define MAX_WIDTH 60
#define MAX_HEIGHT 20
// limiti: 10 - 69, 5 - 24
 
// tasti di movimento(ascii)
#define UP 119
#define DW 115
#define LF 97
#define RH 100
 
// prototipi di funzioni
void organizzaHeader(void);
void disegnaQuadro(void);
void disegnaSnake(void);
void aggiorna_punteggio(int);
void generaCibo(void);
int random(int,int);
void start(void);
void game_started(int);
void muovi();
 
// variabili globali del serpente
int snake_cols[100] = {MAX_WIDTH/2 +3, MAX_WIDTH/2 +2,MAX_WIDTH/2 +1,MAX_WIDTH/2};
int snake_row[100] = {MAX_HEIGHT/2 + 5,MAX_HEIGHT/2 + 5,MAX_HEIGHT/2 + 5,MAX_HEIGHT/2 + 5};
int snake_lenght = 4;
 
// coordinate del cibo
int cibo_cols = 0;
int cibo_row = 0;
 
// punteggio
int score = 0;
 
main()
{
 
    organizzaHeader();
    disegnaQuadro();
 
    disegnaSnake();
 
    generaCibo();
    int tasto = getch();
    if (tasto == 13) // ha premuto invio
    {
       start();
    }
    // evito messaggi sul quadro di gioco
    SetColor(15);
    GotoXY(0,27);
 
}
void start(void)
{
    int x;
    for (x = 3; x>=0; x--)
    {
        SetColor(12);
        GotoXY(20,2);
        printf("%d",x);
        Sleep(1000);
    }
    muovi();
}
 
void muovi()
{
    int cibo = 1; // c'è cibo nel quadro
    int i;
    int dr = 0, dc = +1;
    int tasto=0;
    do
    {
        SetColor(2);
        // genero cibo se non ce n'è
        if (cibo == 0)
        {
            generaCibo();
            cibo = 1;
        }
 
        // controllo se la testa si è mangiata il corpo
        for (i=snake_lenght-1; i>0; i--)
        {
            if (snake_row[0] == snake_row[i] & snake_cols[0] == snake_cols[i])
            {
                SetColor(12);
                GotoXY(0,27);
                printf("GAME OVER");
                return 0;
            }
        }
 
 
        if (kbhit())
        {
             tasto=getch();
             if (tasto == UP){ dr = -1; dc = 0; }
             if (tasto == DW){ dr = +1; dc = 0; }
             if (tasto == LF){ dr = 0; dc = -1; }
             if (tasto == RH){ dr = 0; dc = +1; }
        }
        // movimento
 
           // cancello coda
           GotoXY(snake_cols[snake_lenght-1],snake_row[snake_lenght-1]);
           printf(" ");
 
           // aggiungo testa
           GotoXY(snake_cols[0]+dc,snake_row[0]+dr);
           SetColor(2);
           printf("*");
 
           // slitto array
           for(i=snake_lenght-1; i>0; i--)
           {
               snake_row[i] = snake_row[i-1];
               snake_cols[i] = snake_cols[i-1];
           }
 
            snake_row[0]  += dr;
            snake_cols[0] += dc;
 
        //controllo dei limiti del quadro
 
        if (snake_cols[0] == 69 || snake_cols[0] == 10 || snake_row[0] == 24 || snake_row[0] == 5)
        {
            GotoXY(snake_cols[0],snake_row[0]);
            SetColor(12);
            printf("*");
            GotoXY(MAX_WIDTH/2+5,MAX_HEIGHT+6);
            printf("GAME OVER");
            return 0;
        }
 
        // controllo se il serpente si mangia il cibo
        if (snake_cols[0] == cibo_cols & snake_row[0] == cibo_row)
        {
            // aggiungo coda al serpente
             snake_lenght++;
             snake_cols[snake_lenght-1] = snake_cols[snake_lenght-2];
             snake_row[snake_lenght-1] = snake_row[snake_lenght-2];
            // score +1
             GotoXY(MAX_WIDTH/2 + 34, 2);
             score += 10;
             SetColor(14);
             printf("%d", score);
             cibo = 0;
        }
    Sleep(70);
    }
    while(tasto != 27);
    if (tasto == 27)
    {
        SetColor(14);
        printf("STOP");
    }
}
void generaCibo(void)
{
    cibo_cols = random(11,68);
    cibo_row = random(6,23);
 
    GotoXY(cibo_cols,cibo_row);
    SetColor(4);
    printf("o");
 
}
int random(int min, int max)
{
   int random, diff = max - min;
   srand(time(NULL)); //inizializza il generatore
   random = rand() % diff; //genera un numero casuale fra 0 e 5
   random+= min; //otteniamo un valore fra 1 e 6
 
   return random;
}
void aggiorna_punteggio(int score) // da controllare
{
    // punteggio
    GotoXY(MAX_WIDTH/2 + 20, 2);
    SetColor(15);
    printf("Score: %d", score);
}
void disegnaSnake(void)
{
    int x;
    SetColor(2);
    for (x = snake_lenght-1; x>=0; x--)
    {
        GotoXY(snake_cols[x],snake_row[x]);
        printf("*");
    }
}
void disegnaQuadro(void)
{
    GotoXY(0,5);
    SetColor(6);
    int x = 0, y = 0;
    for (x=0; x<MAX_HEIGHT; x++)
    {
        printf("          @");
 
                               for(y = 1; y < MAX_WIDTH -1; y++)
                               {
                                   if (x == 0 || x == MAX_HEIGHT-1)
                                   {
                printf("@");
                                   }
                                   else
                                   {
                                       printf(" ");
                                   }
                               }
                               printf("@");
    }
}
 
void organizzaHeader(void)
{
    // start
    GotoXY(10+3,2);
    printf("Start: ");
    SetColor(12);
    printf("0");
    SetColor(15);
 
    // titolo
    GotoXY(MAX_WIDTH/2+6, 2);
    SetColor(10);
    printf("SNAKE");
 
    // punteggio
    GotoXY(MAX_WIDTH/2 + 27, 2);
    SetColor(15);
    printf("Score: ");
    SetColor(12);
    printf("0");
    SetColor(15);
}
void  GotoXY(int x, int  y) {
COORD CursorPos = {x, y};
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hConsole, CursorPos);
}
void SetColor(short Color)
{
HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE); // oppure system("COLOR E9");
SetConsoleTextAttribute(hCon,Color);
}