-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsnake.c
145 lines (129 loc) · 3.17 KB
/
snake.c
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
// Snake
// The problem with implementing this task in C is, the language standard
// does not cover some details essential for interactive games:
// a nonblocking keyboard input, a positional console output,
// a and millisecond-precision timer: these things are all system-dependent.
// Therefore the program is split in two pieces, a system-independent
// game logic, and a system-dependent UI, separated by a tiny API:
char nonblocking_getch();
void positional_putch(int x, int y, char ch);
void millisecond_sleep(int n);
void init_screen();
void update_screen();
void close_screen();
// The implementation of a system-dependent part.
// Requires POSIX IEEE 1003.1-2008 compliant system and ncurses library.
#ifdef __linux__
#define _POSIX_C_SOURCE 200809L
#include <time.h> // nanosleep
#include <ncurses.h> // getch, mvaddch, and others
char nonblocking_getch() { return getch(); }
void positional_putch(int x, int y, char ch) { mvaddch(x, y, ch); }
void millisecond_sleep(int n) {
struct timespec t = { 0, n * 1000000 };
nanosleep(&t, 0);
// for older POSIX standards, consider usleep()
}
void update_screen() { refresh(); }
void init_screen() {
initscr();
noecho();
cbreak();
nodelay(stdscr, TRUE);
}
void close_screen() { endwin(); }
#endif
// An implementation for some other system...
#ifdef _WIN32
#error "not implemented"
#endif
// The game logic, system-independent
#include <time.h> // time
#include <stdlib.h> // rand, srand
#define w 80
#define h 40
int board[w * h];
int head;
enum Dir { N, E, S, W } dir;
int quit;
enum State { SPACE=0, FOOD=1, BORDER=2 };
// negative values denote the snake (a time-to-live in given cell)
// reduce a time-to-live, effectively erasing the tail
void age() {
int i;
for(i = 0; i < w * h; ++i)
if(board[i] < 0)
++board[i];
}
// put a piece of food at random empty position
void plant() {
int r;
do
r = rand() % (w * h);
while(board[r] != SPACE);
board[r] = FOOD;
}
// initialize the board, plant a very first food item
void start(void) {
int i;
for(i = 0; i < w; ++i)
board[i] = board[i + (h - 1) * w] = BORDER;
for(i = 0; i < h; ++i)
board[i * w] = board[i * w + w - 1] = BORDER;
head = w * (h - 1 - h % 2) / 2;
board[head] = -5;
dir = N;
quit = 0;
srand(time(0));
plant();
}
void step() {
int len = board[head];
switch(dir) {
case N: head -= w; break;
case S: head += w; break;
case W: --head; break;
case E: ++head; break;
}
switch(board[head]) {
case SPACE:
board[head] = len - 1;
age();
break;
case FOOD:
board[head] = len - 1;
plant();
break;
default:
quit = 1;
}
}
void show() {
const char * symbol = " @.";
int i;
for(i = 0; i < w * h; ++i)
positional_putch(i / w, i % w,
board[i] < 0 ? '#' : symbol[board[i]]);
update_screen();
}
int main (int argc, char * argv[]) {
init_screen();
start();
do {
show();
switch(nonblocking_getch()) {
case 'i': dir = N; break;
case 'j': dir = W; break;
case 'k': dir = S; break;
case 'l': dir = E; break;
case 'q': quit = 1; break;
}
step();
millisecond_sleep(100); // beware, this approach
// is not suitable for anything but toy projects like this
}
while(!quit);
millisecond_sleep(999);
close_screen();
return 0;
}