Simple Snake Game C++ in Unix

i found myself stuck with it, i had googled a lot of solution which can't solve mine. A lot of solution is written for window system only.Firstly, how to check the keyboard input without the ENTER key pressed by the user? Anyone please help! i had stuck with this problem for 2 days.

Here is my code.

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
//SimpleSnakeGame.cpp
//Date : 30/7/2015

#include <iostream>
#include <cstdlib>
#include <ncurses.h>



bool gameover;
const int width = 20;
const int height = 10;
enum eDirection {STOP = 0, LEFT, RIGHT, UP, DOWN};
eDirection dir;
int x, y, foodx, foody, score;


void setUp();
//1)set the snake at the centre of the map.
//2)set the food position

void draw();
/* 
draw the map:
#########
#  	    #
#########
*/

void input();
//get the input of W, A, S, D from the keyboard.
//make switch case.

void logic();
//1)direction of the snake using W,A,S,D.
//2)Random food position
//3)Eat the food and generate another position.
//4)Hit the wall = GameOver.
//5)Tail of the snake grow when eat the food.



using namespace std;

int main()
{
	srand(time(0));
	setUp();
	while(!gameover)
	{
		draw();	
		input();
		logic();

	}
	
	
	
	return 0;
}

void setUp()
{

	gameover = false;
	dir = STOP;
	x = width/2;
	y = height/2;
	foodx = rand()%width;
	foody = rand()%height;

}

void draw()
{
	system("clear");

	for(int i = 0; i < width; i++)
		cout << "#";
	cout << endl;

	for(int j = 0; j < height; j++)
	{
		for(int i = 0; i <width; i++)
		{
			if(i ==0)
				cout << "#";
			else if(i == width -1)
				cout << "#";
			else if(i == x && j == y)
				cout << "O";
			else if(i == foodx && j == foody)
				cout << "F";
			else
				cout << " ";
		}

		cout << endl;
	}

	for(int i = 0; i < width; i++)
		cout << "#";
	cout << endl;
}


void input()
{

	if(_kbhit())
	{
		switch(getch())
		{
			case 'a':
				dir = LEFT;
				break;
			case 'w':
				dir = UP;
				break;
			case 'd':
				dir = RIGHT;
				break;
			case 's':
				dir = DOWN;
				break;
			case 'x':
				gameover = true;	
		}

	}
}
void logic()
{	
	switch (dir)
	{
		case 'LEFT':
			x --;
			break;
		case 'UP':
			y --;
			break;
		case 'RIGHT':
			x ++;
			break;
		case 'DOWN':
			y ++;
			break;
		default:
			break;			  
	}

	if(x > width || x < 0)
		gameover = true;
	if(y > height || y < 0)
		gameover =true;

	if(x == foodx && y == foody)
	{
		score = score + 10;
		foodx = rand()%width;
		foody = rand()%height;
	}

}
To handle the keyboard that way, you need to look at the ioctl() function call. Another way is to use the curses library.
The C++ doesn't have a standard way of checking keyboard input so it is likely to be different for each operating system you run on.
Here is one way:

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

#include <unistd.h>
#include <termios.h>

char get1ch()
	{
	char buf = 0;
	
	struct termios old = {0};
	
	if( tcgetattr( 0, &old ) < 0 )
		perror( "tcsetattr()" );
		
	old.c_lflag &= ~ICANON;
	old.c_lflag &= ~ECHO;
	old.c_cc[ VMIN ] = 1;
	old.c_cc[ VTIME ] = 0;
	
	if( tcsetattr( 0, TCSANOW, &old ) < 0 )
		perror( "tcsetattr ICANON" );
		
	if( read( 0, &buf, 1 ) < 0 )
		perror( "read()" );
		
	old.c_lflag |= ICANON;
	old.c_lflag |= ECHO;
	
	if( tcsetattr( 0, TCSADRAIN, &old ) < 0 )
		perror( "tcsetattr ~ICANON" );

	return buf;
	
	}
		/*	get1ch()	*/


Last edited on
Here is the way I get input:

kbhit.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef kbhit_h
#define kbhit_h
#include <ncursesw/ncurses.h>

// checks if a key a has been pressed(non-blocking)
int kbhit()
{
    int ch = getch();
    if (ch != ERR) {
        ungetch(ch);
        return 1;
    } else {
        return 2;
    }
}

#endif 


file/s used in

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
#include "kbhit.h"

int main()
{
	if(kbhit())
	{
	    switch (getch())           // check what key it was and take action
	    {
	        case 'W':
	        case 'w':
	        case KEY_UP:
	            // ...
	            break;
	        case 'A':
	        case 'a':
	        case KEY_LEFT:
	            // ...
	            break;
	        case 'S':
	        case 's':
	        case KEY_DOWN:
	            // ...
	            break;
	        case 'D':
	        case 'd':
	        case KEY_RIGHT:
	            // ...
	            break;
	    }
	}

	return 0;
}


If you are really stuck I could post my snake game that I have created with ncurses though I would suggest you take one step at a time and do it yourself. It is a really fun experience once it is close to being done.
Last edited on
I tried to copy your code to test it on my macbook.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <ncurses.h>

// checks if a key a has been pressed(non-blocking)
int kbhit()
{
    int ch = getch();
    if (ch != ERR) {
        ungetch(ch);
        return 1;
    } else {
        return 2;
    }
}


And it return me this error message
1
2
3
4
5
6
7
8
9
10
11
Undefined symbols for architecture x86_64:
  "_main", referenced from:
     implicit entry/start for main executable
  "_stdscr", referenced from:
      kbhit() in test3-f530cb.o
  "_ungetch", referenced from:
      kbhit() in test3-f530cb.o
  "_wgetch", referenced from:
      kbhit() in test3-f530cb.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)


I don't get it.... Please help
i still can't solve it, it keep give me the same error message
Hi,

If you are going to use curses, you can't just put a snippet into your code like that, AFAIK there is a procedure - the system has to be initialised & setup. Read the documentation.

http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/
http://math.hws.edu/orr/s04/cpsc225/curses.html


All I did was Google ncurses c++

Good Luck :+)
i had solved this problem by change the system into "stty raw"

but here is another problem, i have to keep pressing the same key in order to make it move in same direction.

how do i make it run without keep pressing the same key?

here is the code:

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
void input()
{
	system("stty raw");

	char key;
	key = getchar();
	{
		switch(key)
		{
			case 'a':
				dir = LEFT;
				break;
			case 'w':
				dir = UP;
				break;
			case 'd':
				dir = RIGHT;
				break;
			case 's':
				dir = DOWN;
				break;
			case 'x':
				gameover = true;	
		}
	}

	

}
void logic()
{	
	switch (dir)
	{
		case LEFT:
			x --;
			break;
		case UP:
			y --;
			break;
		case RIGHT:
			x ++;
			break;
		case DOWN:
			y ++;
			break;
		default:
			break;			  
	}

	system("stty cooked");
Last edited on
Any one please help? Thank you
Topic archived. No new replies allowed.