Created
August 9, 2018 15:17
-
-
Save wilfreddv/e5e1be501e25a50e0574b6a41814dc1e to your computer and use it in GitHub Desktop.
Snake in C using Ncurses
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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