main.c
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <signal.h>
#include <sys/time.h>
#include <curses.h>
#include "worms.h"
#include "helper.h"
#define TIMESTEP 200000
void SetTimer(void);
void SetSignals(void);
WINDOW * mainwin;
int oldcur;
int main(void) {
srand( (unsigned) time(NULL) );
SetTimer();
SetSignals();
if ( (mainwin = initscr()) == NULL ) {
perror("error initialising ncurses");
exit(EXIT_FAILURE);
}
noecho();
keypad(mainwin, TRUE);
oldcur = curs_set(0);
InitWorm();
Draw();
while ( 1 ) {
int key = getch();
switch ( key ) {
case KEY_UP:
case 'Y':
case 'y':
ChangeDir(UP);
break;
case KEY_DOWN:
case 'N':
case 'n':
ChangeDir(DOWN);
break;
case KEY_LEFT:
case 'G':
case 'g':
ChangeDir(LEFT);
break;
case KEY_RIGHT:
case 'J':
case 'j':
ChangeDir(RIGHT);
break;
case 'Q':
case 'q':
Quit(USER_QUIT);
break;
}
}
return EXIT_SUCCESS;
}
void SetTimer(void) {
struct itimerval it;
timerclear(&it.it_interval);
timerclear(&it.it_value);
it.it_interval.tv_usec = TIMESTEP;
it.it_value.tv_usec = TIMESTEP;
setitimer(ITIMER_REAL, &it, NULL);
}
void SetSignals(void) {
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGALRM, &sa, NULL);
sa.sa_handler = SIG_IGN;
sigaction(SIGTSTP, &sa, NULL);
}
worms.h
#ifndef PG_WORMS_H
#define PG_WORMS_H
struct worm_part {
struct worm_part * next;
int x;
int y;
};
typedef struct worm_part WORM;
#define DOWN 1
#define UP 2
#define LEFT 3
#define RIGHT 4
#define WORMBIT 'O'
#define EMPTY ' '
#define WORMFOOD 'X'
void InitWorm(void);
void MoveWorm(void);
void Draw(void);
void FreeWorm(void);
#endif
worms.c
#include <stdlib.h>
#include <curses.h>
#include "worms.h"
#include "helper.h"
#define MOVESCORE 1
#define FOODSCORE 10
#define WORMSIZE 8
static WORM * worm;
static int dir = DOWN;
static int rows, cols;
int score = 0;
void PlaceFood(void);
void Draw(void);
void InitWorm(void) {
WORM * temp;
int x = 1, y = 1, i;
for ( i = 0; i < WORMSIZE; i++ ) {
if ( i == 0 ) {
if ( (worm = malloc(sizeof(WORM))) == NULL )
Error_Quit("couldn't allocate memory for worm");
temp = worm;
}
else {
if ( (temp->next = malloc(sizeof(WORM))) == NULL )
Error_Quit("couldn't allocate memory for worm");
temp = temp->next;
}
temp->x = x;
temp->y = y++;
}
temp->next = NULL;
GetTermSize(&rows, &cols);
}
void Draw(void) {
WORM * temp = worm;
box(stdscr, 0, 0);
while ( temp ) {
move(temp->y, temp->x);
addch(WORMBIT);
temp = temp->next;
}
PlaceFood();
}
void MoveWorm(void) {
WORM * temp = worm;
int x, y, ch;
while ( temp->next != NULL )
temp = temp->next;
if ( (temp->next = malloc(sizeof(WORM))) == NULL )
Error_Quit("couldn't allocate memory in MoveWorm()");
switch ( dir ) {
case DOWN:
x = temp->x;
y = temp->y + 1;
break;
case UP:
x = temp->x;
y = temp->y - 1;
break;
case LEFT:
x = temp->x - 1;
y = temp->y;
break;
case RIGHT:
x = temp->x + 1;
y = temp->y;
break;
}
temp = temp->next;
temp->next = NULL;
temp->x = x;
temp->y = y;
move(y, x);
switch ( (ch = inch()) ) {
case EMPTY:
score += MOVESCORE;
temp = worm->next;
move(worm->y, worm->x);
addch(EMPTY);
free(worm);
worm = temp;
case WORMFOOD:
move(y, x);
addch(WORMBIT);
if ( ch == WORMFOOD ) {
PlaceFood();
score += FOODSCORE;
}
refresh();
break;
case WORMBIT:
Quit(HITSELF);
default:
Quit(HITWALL);
}
}
void PlaceFood(void) {
int x, y;
do {
x = rand() % (cols - 3) + 1;
y = rand() % (rows - 3) + 1;
move(y, x);
} while ( inch() != EMPTY );
addch(WORMFOOD);
}
void ChangeDir(int d) {
WORM * temp = worm;
while ( temp->next != NULL )
temp = temp->next;
switch ( d ) {
case LEFT:
if ( dir == RIGHT )
return;
move(temp->y, temp->x - 1);
break;
case RIGHT:
if ( dir == LEFT )
return;
move(temp->y, temp->x + 1);
break;
case UP:
if ( dir == DOWN )
return;
move(temp->y - 1, temp->x);
break;
case DOWN:
if ( dir == UP )
return;
move(temp->y + 1, temp->x);
break;
}
dir = d;
}
void FreeWorm(void) {
WORM * temp = worm;
while ( worm ) {
temp = worm->next;
free(worm);
worm = temp;
}
}
helper.h
#ifndef PG_HELPER_H
#define PG_HELPER_H
#define USER_QUIT 1
#define HITSELF 2
#define HITWALL 3
void Error_Quit(char * msg);
void Quit(int reason);
void GetTermSize(int * rows, int * cols);
void handler(int signum);
void ChangeDir(int d);
#endif
helper.c
#include <stdlib.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <curses.h>
#include "helper.h"
#include "worms.h"
void Error_Quit(char * msg) {
extern WINDOW * mainwin;
extern int oldcur;
delwin(mainwin);
curs_set(oldcur);
endwin();
refresh();
FreeWorm();
perror(msg);
exit(EXIT_FAILURE);
}
void Quit(int reason) {
extern WINDOW * mainwin;
extern int oldcur;
extern int score;
delwin(mainwin);
curs_set(oldcur);
endwin();
refresh();
FreeWorm();
switch ( reason ) {
case HITWALL:
printf("\nYou hit a wall!\n");
printf("Your score is %d\n", score);
break;
case HITSELF:
printf("\nYou ran into yourself!\n");
printf("Your score is %d\n", score);
break;
default:
printf("\nGoodbye!\n");
break;
}
exit(EXIT_SUCCESS);
}
void GetTermSize(int * rows, int * cols) {
struct winsize ws;
if ( ioctl(0, TIOCGWINSZ, &ws) < 0 ) {
perror("couldn't get window size");
exit(EXIT_FAILURE);
}
*rows = ws.ws_row;
*cols = ws.ws_col;
}
void handler(int signum) {
extern WINDOW * mainwin;
extern int oldcur;
switch ( signum ) {
case SIGALRM:
MoveWorm();
return;
case SIGTERM:
case SIGINT:
delwin(mainwin);
curs_set(oldcur);
endwin();
refresh();
FreeWorm();
exit(EXIT_SUCCESS);
}
}
Please send all comments, suggestions, bug reports etc to mail@paulgriffiths.net