Linux Console Snake Game Using Cpp

Namaste Friends,




Today, In this post I am going to share with you few piece of code to make a terminal snake game for Linux Console.


Introduction


Basically Snake Game is One Of the Most Easiest, Interesting, Popular Game To Play. In This Game, A Snake Continuously Move From One Point To Another In a Specific Rectangle Like Area Where A Player Can Control The Movement Of Snake With Keyboard So, That Snake Can't hit The Boundary Wall. To Make Scores In This Game, Snake has To Eat Food That Automatically Appears at Random Place Of Boundary!. Very Easy And Interesting. Actually, In Linux, There is few little codes adjustment compared to windows because of CONIO.H header file. Basically, This header file Provides Some Unique Console Input Output Function.

Requirement To Run It



  • Linux
  • GCC Compiler




Before Explanation, Let's Quickly Take A Look At Terminal Snake Game and Yes, I tried my best to make these codes easy to understand and I also used comments to make the concept more clear.


Example Codes.



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
#include <iostream>   // Standard Header
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <stropts.h>
#include <stdio.h>

using namespace std;


/*
__author__ = suraj singh bisht
__email__  = surajsinghbisht054@gmail.com
__Github__ = https://github.com/surajsinghbisht054

*/


///++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
///                       Testing Configuration
///++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
///
/// Operating System            :   Linux 4.4.0-109-generic #132-Ubuntu, x86_64 GNU/Linux
/// Editor   : Code::Blocks 13.12
/// Compiler          : g++ (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609
/// Blog    : www.bitforestinfo.com
///
///++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


/// ++++++++++++++++++++ [ Configuration Panel] +++++++++++++++++++++++++++++++++{

/// Global Dimension
const int width = 50;                      // Boundary Width
const int height= 25;                      // Boundary Height
const char block='#';                      // Block Char
const char* clearcommand = "clear";        // Teminal Command (Linux: clear | Windows : clr )


/// global arrays for Data Records
int background[height][width];   /// Background Array
int snake[50][2];               /// Maximum Snake Length
int food[2]={0,0};              /// Snake Food Array
int score = 0;                  /// Score
int snakelen = 3;               /// Snake Starting Length
int snakemovementspeedx = 1;    /// Horizontal Speed
int snakemovementspeedy = 1;    /// Vertical Speed
int lap=200;                    /// Waiting Time Betweeen Frames

/// ++++++++++++++++++++ [ Configuration Panel] END +++++++++++++++++++++++++++++++++}




/// Declearing Global Temporary Variable To Save Memory
int bytesWaiting, i;
int px,py,nx, ny;
char k;
int h,w;
int x,y;
int movementx=snakemovementspeedx;                /// Snake Movement
int movementy=0;                /// Snake Movement


/*
Reference Links:
        https://www.quora.com/With-which-function-can-I-replace-kbhit-in-C++-because-the-header-conio-h-doesnt-exist-in-linux

Ubuntu Users:
    sudo apt-get install libncurses5-dev libncursesw5-dev
*/


///
/// http://www.flipcode.com/archives/_kbhit_for_Linux.shtml
/// Check KeyBoard Pressed Or Not
int _kbhit() {
    static const int STDIN = 0;
    static bool initialized = false;

    if (! initialized) {
        // Use termios to turn off line buffering
        termios term;
        tcgetattr(STDIN, &term);
        term.c_lflag &= ~ICANON;
        tcsetattr(STDIN, TCSANOW, &term);
        setbuf(stdin, NULL);
        initialized = true;
    }

    //int bytesWaiting;
    ioctl(STDIN, FIONREAD, &bytesWaiting);
    return bytesWaiting;
}




/// Initialise background borders Onto Array
void initialise_background(void){
    //int i;

    // insert top border
    for(i=0; i<width; i++){
        background[0][i]=1;

    }

    // insert left border
    for(i=0; i<height; i++){
        background[i][0]=1;

    }


    // insert right border
    for(i=0; i<height; i++){
        background[i][width-1]=1;

    }


    // insert bottom border
    for(i=0; i<width; i++){
        background[height-1][i]=1;

    }

}



/// Initialise Snake Coordinates
void initialise_snake(void){

    snake[0][0]=3; /// Coordinates x
    snake[0][1]=3; /// Coordinates y

    snake[1][0]=3+1; /// Coordinates x
    snake[1][1]=3; /// Coordinates y

    snake[2][0]=3+2; /// Coordinates x
    snake[2][1]=3; /// Coordinates y


    snake[3][0]=3+3; /// Coordinates x
    snake[3][1]=3; /// Coordinates y


    snake[4][0]=3+4; /// Coordinates x
    snake[4][1]=3; /// Coordinates y

}



/// Update Snake
void update_snake_coordination(void){
    //int px,py,nx, ny;
    px = snake[0][0];
    py = snake[0][1];
    snake[0][0] = px + movementx;
    snake[0][1] = py + movementy;
    nx =snake[0][0];
    ny =snake[0][1];

    for(i=1; i<snakelen; i++){
        nx = snake[i][0];
        ny = snake[i][1];
        snake[i][0]=px;
        snake[i][1]=py;
        px = nx;
        py = ny;



    }
}



/// Install Snake Coordinates Into Background Array rev = ( 1 To Draw And 0 To Erase)
void draw_snake_in_background(const int rev){
    //int x,y;

    for(i=0; i<snakelen; i++){
        x = snake[i][0];
        y = snake[i][1];
        if((x!=0)&&(y!=0)){
        background[y][x]=rev;
        }

    }

}


/// Print Array Frame
void print_array_frame(void){

    for(h=0; h<height; h++){

        for(w=0; w<width; w++){
            i=background[h][w];

            if(i==1){
            cout << block;
            }else if (i==2){
            cout << "+";
            }else{
            cout << " ";
            }

        }
        cout << endl;


    }


}


/// Clear Background
void clear_background(void){
    system(clearcommand);
}


/// Update loop
void mainloop(void){

    clear_background();          // clear background
    draw_snake_in_background(1); // Install Snake
    print_array_frame();         // Print Frame
    draw_snake_in_background(0); // Uninstall Snake

}



/// Waiting Function
void sleepcp(int milliseconds) // Cross-platform sleep function
{
    clock_t time_end;
    time_end = clock() + milliseconds * CLOCKS_PER_SEC/1000;
    while (clock() < time_end)
    {
    }
}


/// Reaction On Press Button Of Keyboard
void reaction_on_keyboard(const char k){
    if(k=='d'||k=='6'){
        // Right Turn
        movementx = snakemovementspeedx;
        movementy = 0;


    }else if(k=='a'||k=='4'){
        // Left Turn
        movementx = -snakemovementspeedx;
        movementy = 0;

    }else if(k=='w'||k=='8'){

        // Turn UP
        movementx = 0;
        movementy = -snakemovementspeedy;

    }else if(k=='s'||k=='2'){
        // Turn Down
        movementx = 0;
        movementy = snakemovementspeedy;


    }else if(k=='q'||k=='z'||k=='c'){
        cout << "[+] Exit Safely [+]"<<endl;
        exit(0);
    }

}


/// Create Snake Food
void cook_food(void){

    if (food[0]==0){
        x = rand() % width + 1;
        y = rand() % height + 1;
        food[0] = x;
        food[1] = y;
        background[y][x]=2;
    }


}


/// Check Snake & Food Status
void capture_food(void){

        x=food[0];
        y=food[1];

        if ((x==snake[0][0])&&(y==snake[0][1])){
            background[y][x]=0;
            food[0]=0;
            score++;
            snakelen++;
            cook_food();

        }



}

/// Check Snake is Not Touching Boundary
void check_over_lapping(void){
    //int px,py;
    px = snake[0][0];
    py = snake[0][1];

    if((px==0)||(px==(width-1))||(py==0)||(py==(height-1))){

    cout << "[+]        Game Over           [+]" << endl;
    exit(0);

    }

}



/// Loop
void loop(void){
    int frame=0;
    x=0;
    y=0;

    while(x<500){
        sleepcp(lap);

        if(_kbhit())   /// If keyboard hit
        {
        cin >> k; /// Character
        reaction_on_keyboard(k);
        }

        mainloop();                 /// RUn Main Loop FUnction
        update_snake_coordination();/// Update Snake COordination
        check_over_lapping();       /// Check Snake status
        cook_food();                /// Make Sure Food is available
        capture_food();             /// Snake Eaten Food Or Not
        cout << "< Frame : " << frame << "  | Score  : " << score << " > "<< endl; /// Print Status
        frame++;
    }

}


/// Main Trigger Function
main(){

    initialise_background(); /// Install All Variables
    initialise_snake();      /// Install Snake data
    loop();                 /// run Update Loop
}



How it's Going To Work?


Some Important Points To Understand To Above Codes.


  • I, Used Integer Array To Manage And Print Background, Snake, And Food. In Array, Value 0 Represent Empty Space, Value 1 Represent Snake Or Boundary Block, Value 2 Represent Snake Food.
  • To Make Movement In Terminal, I Used Frame By Frame Concept. Basically, After Printing One Frame, Call System Function And Pass Clear Command To Make Terminal Clear. And Then Print Another Frame.
  • I Used Global Array To Make Snake Game More lightweight and Fast. Actually, Declaring New Variables Every time is simply waste Of memory.
  • Using Some Trick Function To make These Codes More Interesting.




I hope you enjoyed it.
please leave comments below about your views,
Or You can also share your
improved Codes Here.

Share this

Related Posts

Previous
Next Post »