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 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 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 generatorerandom = rand() % diff; //genera un numero casuale fra 0 e 5random+= 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);}