// Snake implementation for OnlineGDB. Adopted by Uri Keshet, March 2021
// ----------------------------------------------------------------------
// System-dependent part - do not modify.
// ----------------------------------------------------------------------
char nonblocking_getch();
void close_screen(), positional_putch(int x, int y, char ch);
void millisecond_sleep(int n), init_screen(), update_screen();
#include <time.h> // nanosleep
#include <ncurses.h> // getch, mvaddch, etc.
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);}
void init_screen() { initscr(); noecho(); cbreak(); nodelay(stdscr, TRUE);}
void update_screen() { refresh(); }
void close_screen() { endwin(); }
//
// This should run on OnlineGDB and some similar online compilers.
// To run this on Windows 10 using MSYS2, first install ncurses (on command line):
// > pacman -S ncurses-devel
// and when compiling, include a link to the ncurses library:
// > gcc Snake.cc -lncurses
// ----------------------------------------------------------------------
#include <time.h> // time
#include <stdlib.h> // rand, srand
#define w 80
#define h 24
int head, quit, board[w * h];
enum Dir { N, E, S, W } dir;
enum State { SPACE=0, FOOD=1, BORDER=2 };
// negative values represent snake (negated time-to-live in given cell)
// reduce a time-to-live, effectively erasing the tail
void age() {
for(int i = 0; i < w * h; ++i)
if(board[i] < 0)
++board[i];
}
// put food at random empty position
void plant() {
int r;
do
r = rand() % (w * h);
while(board[r] != SPACE);
board[r] = FOOD;
}
// initialize board, plant first food item
void start(void) {
for(int i = 0; i < w; ++i)
board[i] = board[i + (h - 1) * w] = BORDER;
for(int i = 0; i < h; ++i)
board[i * w] = board[i * w + w - 1] = BORDER;
head = w * (h - 1 - h % 2) / 2; // screen center for any h
board[head] = -7;
dir = E;
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; // keep in mind len is negative
age();
break;
case FOOD:
board[head] = len - 1;
plant();
break;
default:
quit = 1;
}
}
void show() {
const char * symbol = " @.";
for(int 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 'w': if (dir!=S) dir = N; break;
case 'a': if (dir!=E) dir = W; break;
case 's': if (dir!=N) dir = S; break;
case 'd': if (dir!=W) dir = E; break;
case 'q': quit = 1; break;
}
step();
millisecond_sleep(100);
}
while(!quit);
millisecond_sleep(999);
close_screen();
return 0;
}