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 funzionivoid 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 serpenteint 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 ciboint cibo_cols = 0;int cibo_row = 0;// punteggioint score = 0;main(){organizzaHeader();disegnaQuadro();disegnaSnake();generaCibo();int tasto = getch();if (tasto == 13) // ha premuto invio{start();}// evito messaggi sul quadro di giocoSetColor(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 quadroint 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 corpofor (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 codaGotoXY(snake_cols[snake_lenght-1],snake_row[snake_lenght-1]);printf(" ");// aggiungo testaGotoXY(snake_cols[0]+dc,snake_row[0]+dr);SetColor(2);printf("*");// slitto arrayfor(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 quadroif (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 ciboif (snake_cols[0] == cibo_cols & snake_row[0] == cibo_row){// aggiungo coda al serpentesnake_lenght++;snake_cols[snake_lenght-1] = snake_cols[snake_lenght-2];snake_row[snake_lenght-1] = snake_row[snake_lenght-2];// score +1GotoXY(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 generatorerandom = rand() % diff; //genera un numero casuale fra 0 e 5random+= min; //otteniamo un valore fra 1 e 6return random;}void aggiorna_punteggio(int score) // da controllare{// punteggioGotoXY(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){// startGotoXY(10+3,2);printf("Start: ");SetColor(12);printf("0");SetColor(15);// titoloGotoXY(MAX_WIDTH/2+6, 2);SetColor(10);printf("SNAKE");// punteggioGotoXY(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);}
