Skip to content

Instantly share code, notes, and snippets.

@wilfreddv
Created August 9, 2018 15:17
Show Gist options
  • Save wilfreddv/e5e1be501e25a50e0574b6a41814dc1e to your computer and use it in GitHub Desktop.
Save wilfreddv/e5e1be501e25a50e0574b6a41814dc1e to your computer and use it in GitHub Desktop.
Snake in C using Ncurses
#include <ncurses.h>
#include <time.h>
#include <stdlib.h>
#define MINSCRX 150
#define MINSCRY 50
#define UP 0
#define RIGHT 1
#define DOWN 2
#define LEFT 3
#define BEGINSPEED 50000000L
typedef struct {
int x;
int y;
char ch;
} Particle;
void checkscrsize()
{
/* This checks if the terminal is big enough (MINSCRX x MINSCRY) */
int scry, scrx;
getmaxyx(stdscr, scry, scrx);
if ( scry < MINSCRY || scrx < MINSCRX )
{
printw("Terminal too small, please resize to at least %dx%d.\n", MINSCRX, MINSCRY);
printw("Current size: %dx%d\n", scrx, scry);
refresh();
getch();
endwin();
exit(1);
}
}
void printoutlines()
{
int i, j;
for ( i=0; i<MINSCRX-1; i++ )
addch(ACS_CKBOARD);
printw("\n");
for ( i=0; i<(MINSCRY-2); i++ )
{
addch(ACS_CKBOARD);
for ( j=0; j<(MINSCRX-3); j++ )
printw(" ");
addch(ACS_CKBOARD);
printw("\n");
}
for ( i=0; i<MINSCRX-1; i++ )
addch(ACS_CKBOARD);
}
void setup()
{
/* Setup function for all things ncurses related */
initscr(); //Start curses mode
cbreak(); //Line buffering disabled
keypad(stdscr, TRUE); //Enable arrow keys
noecho(); //Don't echo user input
checkscrsize(); //Check if screensize is appropriate
nodelay(stdscr, 1); //No delay for keyboard input (0 for testing)
printoutlines();
curs_set(0); //Remove cursor
}
void changetail(Particle *tail, Particle head, int dir, int length) {
for(; length; length--) {
tail[length].x = tail[length-1].x;
tail[length].y = tail[length-1].y;
}
tail[0].x = head.x;
tail[0].y = head.y;
}
int main(void)
{
/* Setting game timer (0 to x nanoseconds per frame) */
struct timespec _time, _time1;
_time.tv_sec = 0;
_time.tv_nsec = BEGINSPEED;
srand( time(NULL) ); //Seed rand for apple coords
int score, dir, key, i;
score = 0;
dir = RIGHT; //Starting direction
Particle head;
head.ch = '$';
head.x = 8;
head.y = 10;
Particle apple;
apple.ch = '@';
apple.x = ( rand() % 148 ) + 1;
apple.y = ( rand() % 48 ) + 1;
Particle tail[100];
tail[0].ch = '#';
tail[0].x = 9;
tail[0].y = 10;
setup(); //Set up ncurses
bool hit = false;
/* Game loop */
while (1) {
/* Direction handling */
switch (dir) {
case UP:
changetail(tail, head, UP, score);
head.y--;
break;
case RIGHT:
changetail(tail, head, RIGHT, score);
head.x++;
break;
case DOWN:
changetail(tail, head, DOWN, score);
head.y++;
break;
case LEFT:
changetail(tail, head, LEFT, score);
head.x--;
break;
}
/* Apple collision detection */
if ( head.x == apple.x && head.y == apple.y ) {
score++;
tail[score].ch = '#';
apple.x = ( rand() % 148 ) + 1;
apple.y = ( rand() % 48 ) + 1;
}
/* Tail or wall collision detection */
for ( i = 0; i <= score; i++ ) {
if ( head.x == tail[i].x && head.y == tail[i].y ||
MINSCRX-2 <= head.x || head.x <= 0 || MINSCRY-1 <= head.y || head.y <= 0 )
hit = true;
}
if ( hit ) break;
/* Read user input and change Direction
Also changes speed because down- and upwards would
be too fast otherwise
*/
key = getch();
switch (key) {
case KEY_UP:
if ( DOWN != dir ) {
dir = UP;
_time.tv_nsec *= 1.7;
}
break;
case KEY_RIGHT:
if ( LEFT != dir ) {
dir = RIGHT;
_time.tv_nsec = BEGINSPEED;
}
break;
case KEY_DOWN:
if ( UP != dir ) {
dir = DOWN;
_time.tv_nsec *= 1.7;
}
break;
case KEY_LEFT:
if ( RIGHT != dir ) {
dir = LEFT;
_time.tv_nsec = BEGINSPEED;
}
break;
}
/* Print all the stuff */
erase();
printoutlines();
mvprintw(head.y, head.x, "%c", head.ch);
for ( i=0; i<=score; i++ )
mvprintw(tail[i].y, tail[i].x, "%c", tail[i].ch);
mvprintw(apple.y, apple.x, "%c", apple.ch);
mvprintw(0, 2, "Score: %3d", score);
nanosleep(&_time, &_time1);
refresh();
}
nodelay(stdscr, 0);
mvprintw(25, 65, "Game over! Score: %d", score);
refresh();
getch();
endwin(); //End curses mode
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment