Magazine Informatica

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.

Snake

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.

Movimento Snake

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);
}
 


Ritornare alla prima pagina di Logo Paperblog