changeset 65:8e38593b0a08

misc: cleanup a lot by assuming POSIX
author David Demelier <markand@malikania.fr>
date Wed, 29 Jan 2020 14:07:02 +0100
parents 8ab607924882
children 9ef6bf3d8ad8
files Makefile README.md TODO.md nsnake.c sysconfig.sh
diffstat 5 files changed, 196 insertions(+), 276 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Tue Jan 28 21:10:00 2020 +0100
+++ b/Makefile	Wed Jan 29 14:07:02 2020 +0100
@@ -31,7 +31,7 @@
 MANDIR=         ${PREFIX}/share/man
 VARDIR=         ${PREFIX}/var
 
-VERSION=        2.3.0
+VERSION=        3.0.0
 SRCS=           nsnake.c
 OBJS=           ${SRCS:.c=.o}
 
@@ -41,12 +41,7 @@
 all: nsnake
 
 .c.o:
-	${CC} -c -DVARDIR=\"${VARDIR}\" ${PORTCFLAGS} ${CFLAGS} $<
-
-${OBJS}: sysconfig.h
-
-sysconfig.h: sysconfig.sh
-	CC="${CC}" CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" ./sysconfig.sh > $@
+	${CC} -c -DVARDIR=\"${VARDIR}\" ${CFLAGS} $<
 
 nsnake: ${OBJS}
 	${CC} -o $@ ${OBJS} ${LDFLAGS} ${LIBS}
@@ -67,13 +62,13 @@
 	rm -f ${DESTDIR}${MANDIR}/man6/nsnake.6
 
 clean:
-	rm -f ${OBJS} nsnake sysconfig.h nsnake-${VERSION}.tar.xz
+	rm -f ${OBJS} ${PROG} nsnake-${VERSION}.tar.xz
 
 dist: clean
 	mkdir nsnake-${VERSION}
 	cp -R extern nsnake-${VERSION}
 	cp CHANGES.md INSTALL.md LICENSE.md README.md nsnake-${VERSION}
-	cp Makefile nsnake.6 nsnake.c sysconfig.sh nsnake-${VERSION}
+	cp Makefile nsnake.6 nsnake.c nsnake-${VERSION}
 	tar -cJf nsnake-${VERSION}.tar.xz nsnake-${VERSION}
 	rm -rf nsnake-${VERSION}
 
--- a/README.md	Tue Jan 28 21:10:00 2020 +0100
+++ b/README.md	Wed Jan 29 14:07:02 2020 +0100
@@ -1,7 +1,7 @@
 NSnake
 ======
 
-NSnake is small game written in C and using ncurses API for drawing stuff on the
-screen.
+NSnake is small game written in C11 and using ncurses API for drawing stuff on
+the screen.
 
 It currently supports colors and can save highscores in a file.
--- a/TODO.md	Tue Jan 28 21:10:00 2020 +0100
+++ b/TODO.md	Wed Jan 29 14:07:02 2020 +0100
@@ -1,4 +1,5 @@
 NSnake TODO
 ===========
 
-- Use less globals.
+- Avoid slow response on key presses.
+- Add new states: menu, running (+dead/+paused), resizing, scores.
--- a/nsnake.c	Tue Jan 28 21:10:00 2020 +0100
+++ b/nsnake.c	Wed Jan 29 14:07:02 2020 +0100
@@ -16,11 +16,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <errno.h>
 #include <pwd.h>
-#include <signal.h>
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -32,12 +29,14 @@
 
 #include <curses.h>
 
-#include "sysconfig.h"
-
 #define HEIGHT          23
 #define WIDTH           78
 #define SIZE            ((HEIGHT - 2) * (WIDTH - 2))
 
+#ifndef VARDIR
+#define VARDIR          "/var"
+#endif
+
 #define DATABASE        VARDIR "/db/nsnake/scores.txt"
 #define DATABASE_WC     VARDIR "/db/nsnake/scores-wc.txt"
 
@@ -50,7 +49,7 @@
 	GRID_FOOD
 };
 
-struct snake {
+static struct {
 	int score;              /* user score */
 	int length;             /* snake's size */
 	int dirx;               /* direction in x could be 0, 1 or -1 */
@@ -60,9 +59,20 @@
 		int x;          /* each snake part has (x, y) position */
 		int y;          /* each part will be displayed */
 	} pos[SIZE];
+} snake = {
+	.score  = 0,
+	.length = 4,
+	.dirx   = 1,
+	.diry   = 0,
+	.pos    = {
+		{ 5, 10 },
+		{ 5, 9  },
+		{ 5, 8  },
+		{ 5, 7  }
+	}
 };
 
-struct food {
+static struct {
 	enum {
 		NORM = 0,       /* both increase the score but only NORM */
 		FREE            /* increase the snake's length too */
@@ -70,7 +80,7 @@
 
 	int x;                  /* Position of the current food, will be used */
 	int y;                  /* in grid[][]. */
-};
+} food;
 
 struct score {
 	char name[32];          /* highscore's name */
@@ -78,15 +88,25 @@
 	long long time;         /* when? */
 };
 
-static bool setcolors = true;   /* enable colors */
-static bool warp = true;        /* enable wall crossing */
-static int color = 2;           /* green color by default */
-static bool noscore = false;    /* do not score */
-static bool verbose = true;     /* be verbose */
+static struct {
+	int color;              /* Color: 0-8 and -1 to disable) */
+	bool warp;              /* Enable warp (wall-crossing) */
+	bool quick;             /* Don't save scores */
+	bool verbose;           /* Be more verbose */
+} options = {
+	.color  = 2,
+	.warp   = true,
+	.quick  = false,
+	.verbose = false
+};
 
-static int grid[HEIGHT][WIDTH] = {{ GRID_EMPTY }};
-static WINDOW *top = NULL;
-static WINDOW *frame = NULL;
+static struct {
+	int grid[HEIGHT][WIDTH];
+	WINDOW *top;
+	WINDOW *frame;
+} view = {
+	.grid = {{ GRID_EMPTY }}
+};
 
 static const char *
 name(void)
@@ -116,14 +136,14 @@
 static void
 wset(WINDOW *frame, int pair)
 {
-	if (setcolors)
+	if (options.color >= 0)
 		wattron(frame, pair);
 }
 
 static void
 wunset(WINDOW *frame, int pair)
 {
-	if (setcolors)
+	if (options.color >= 0)
 		wattroff(frame, pair);
 }
 
@@ -131,13 +151,15 @@
 repaint(void)
 {
 	refresh();
-	wrefresh(top);
-	wrefresh(frame);
+	wrefresh(view.top);
+	wrefresh(view.frame);
 }
 
-static int
+static void
 init(void)
 {
+	srandom((unsigned int)time(NULL));
+
 	initscr();
 	noecho();
 	curs_set(0);
@@ -145,142 +167,142 @@
 	nodelay(stdscr, TRUE);
 
 	if (COLS < (WIDTH + 1) || LINES < (HEIGHT + 1))
-		return -1;
+		die(false, "terminal too small");
 
-	if (color < 0 || color > 8)
-		color = 4;
+	if (options.color < 0 || options.color > 8)
+		options.color = 4;
 	else
-		color += 2;
+		options.color += 2;
 
-	if (setcolors && has_colors()) {
-		int i;
-
+	if (options.color >= 0 && has_colors()) {
 		use_default_colors();
 		start_color();
 
 		init_pair(0, COLOR_WHITE, COLOR_BLACK); /* topbar */
 		init_pair(1, COLOR_YELLOW, -1);         /* food */
 
-		for (i = 0; i < COLORS; ++i)
+		for (int i = 0; i < COLORS; ++i)
 			init_pair(i + 2, i, -1);
 	}
 
-	top = newwin(1, 0, 0, 0);
-	frame = newwin(HEIGHT, WIDTH, (LINES/2)-(HEIGHT/2), (COLS/2)-(WIDTH/2));
-	box(frame, ACS_VLINE, ACS_HLINE);
+	view.top = newwin(1, 0, 0, 0);
+	view.frame = newwin(HEIGHT, WIDTH, (LINES / 2) - (HEIGHT / 2),
+	    (COLS / 2) - (WIDTH / 2));
+	box(view.frame, ACS_VLINE, ACS_HLINE);
 
-	if (setcolors) {
-		wbkgd(top, COLOR_PAIR(0));
-		wattrset(top, COLOR_PAIR(0) | A_BOLD);
+	if (options.color >= 0) {
+		wbkgd(view.top, COLOR_PAIR(0));
+		wattrset(view.top, COLOR_PAIR(0) | A_BOLD);
 	}
 
 	repaint();
-
-	return 1;
 }
 
 static void
-set_grid(struct snake *sn)
+update_grid(void)
 {
-	for (int i = 0; i < sn->length; ++i)
-		grid[sn->pos[i].y][sn->pos[i].x] = GRID_SNAKE;
+	for (int i = 0; i < snake.length; ++i)
+		view.grid[snake.pos[i].y][snake.pos[i].x] = GRID_SNAKE;
 
 	/*
 	 * each snake part must follow the last part, pos[0] is head, then
 	 * pos[2] takes pos[1] place, pos[3] takes pos[2] and so on.
 	 */
-	grid[sn->pos[sn->length - 1].y][sn->pos[sn->length - 1].x] = GRID_EMPTY;
-	memmove(&sn->pos[1], &sn->pos[0], sizeof (sn->pos) - sizeof (sn->pos[0]));
+	view.grid[snake.pos[snake.length - 1].y][snake.pos[snake.length - 1].x] = GRID_EMPTY;
+	memmove(&snake.pos[1], &snake.pos[0], sizeof (snake.pos) - sizeof (snake.pos[0]));
 }
 
 static void
-draw(const struct snake *sn, const struct food *fd)
+draw(void)
 {
-	for (int i = 0; i < sn->length; ++i) {
-		wset(frame, COLOR_PAIR(color));
-		mvwaddch(frame, sn->pos[i].y, sn->pos[i].x, '#');
-		wunset(frame, COLOR_PAIR(color));
+	for (int i = 0; i < snake.length; ++i) {
+		wset(view.frame, COLOR_PAIR(options.color));
+		mvwaddch(view.frame, snake.pos[i].y, snake.pos[i].x, '#');
+		wunset(view.frame, COLOR_PAIR(options.color));
 	}
 
 	/* Print head */
-	wset(frame, COLOR_PAIR(color) | A_BOLD);
-	mvwaddch(frame, sn->pos[0].y, sn->pos[0].x, '@');
-	wunset(frame, COLOR_PAIR(color) | A_BOLD);
+	wset(view.frame, COLOR_PAIR(options.color) | A_BOLD);
+	mvwaddch(view.frame, snake.pos[0].y, snake.pos[0].x, '@');
+	wunset(view.frame, COLOR_PAIR(options.color) | A_BOLD);
 
 	/* Erase the snake's tail */
-	mvwaddch(frame, sn->pos[sn->length].y, sn->pos[sn->length].x, ' ');
+	mvwaddch(view.frame, snake.pos[snake.length].y, snake.pos[snake.length].x, ' ');
 
 	/* Print food */
-	wset(frame, COLOR_PAIR(1) | A_BOLD);
-	mvwaddch(frame, fd->y, fd->x, (fd->type == FREE) ? '*' : '+');
-	wunset(frame, COLOR_PAIR(1) | A_BOLD);
+	wset(view.frame, COLOR_PAIR(1) | A_BOLD);
+	mvwaddch(view.frame, food.y, food.x, (food.type == FREE) ? '*' : '+');
+	wunset(view.frame, COLOR_PAIR(1) | A_BOLD);
 
 	/* Print score */
-	wmove(top, 0, 0);
-	wprintw(top, "Score: %d", sn->score);
+	wmove(view.top, 0, 0);
+	wprintw(view.top, "Score: %d", snake.score);
 	repaint();
 }
 
 static bool
-is_dead(const struct snake *sn)
+is_dead(void)
 {
-	if (grid[sn->pos[0].y][sn->pos[0].x] == GRID_SNAKE)
+	if (view.grid[snake.pos[0].y][snake.pos[0].x] == GRID_SNAKE)
 		return true;
 
 	/* No warp enabled means dead in wall */
-	return !warp && grid[sn->pos[0].y][sn->pos[0].x] == GRID_WALL;
+	return !options.warp && view.grid[snake.pos[0].y][snake.pos[0].x] == GRID_WALL;
 }
 
 static bool
-is_eaten(const struct snake *sn)
+is_eaten(void)
 {
-	return grid[sn->pos[0].y][sn->pos[0].x] == GRID_FOOD;
+	return view.grid[snake.pos[0].y][snake.pos[0].x] == GRID_FOOD;
 }
 
 static void
-spawn(struct food *fd)
+spawn(void)
 {
 	int num;
 
 	do {
-		fd->x = (random() % (WIDTH - 2)) + 1;
-		fd->y = (random() % (HEIGHT - 2)) + 1;
-	} while (grid[fd->y][fd->x] != GRID_EMPTY);
+		food.x = (random() % (WIDTH - 2)) + 1;
+		food.y = (random() % (HEIGHT - 2)) + 1;
+	} while (view.grid[food.y][food.x] != GRID_EMPTY);
 
 	/* Free food does not grow the snake */
 	num = ((random() % 7) - 1) + 1;
-	fd->type = (num == 6) ? FREE : NORM;
+	food.type = (num == 6) ? FREE : NORM;
+
+	/* Put on grid. */
+	view.grid[food.y][food.x] = GRID_FOOD;
 }
 
 static void
-direction(struct snake *sn, int ch)
+rotate(int ch)
 {
 	switch (ch) {
 	case KEY_LEFT: case 'h': case 'H':
-		if (sn->dirx != 1) {
-			sn->dirx = -1;
-			sn->diry = 0;
+		if (snake.dirx != 1) {
+			snake.dirx = -1;
+			snake.diry = 0;
 		}
 
 		break;
 	case KEY_UP: case 'k': case 'K':
-		if (sn->diry != 1) {
-			sn->dirx = 0;
-			sn->diry = -1;
+		if (snake.diry != 1) {
+			snake.dirx = 0;
+			snake.diry = -1;
 		}
 
 		break;
 	case KEY_DOWN: case 'j': case 'J':
-		if (sn->diry != -1) {
-			sn->dirx = 0;
-			sn->diry = 1;
+		if (snake.diry != -1) {
+			snake.dirx = 0;
+			snake.diry = 1;
 		}
 
 		break;
 	case KEY_RIGHT: case 'l': case 'L':
-		if (sn->dirx != -1) {
-			sn->dirx = 1;
-			sn->diry = 0;
+		if (snake.dirx != -1) {
+			snake.dirx = 1;
+			snake.diry = 0;
 		}
 
 		break;
@@ -292,25 +314,25 @@
 static const char *
 scores_path(void)
 {
-	return warp ? DATABASE_WC : DATABASE;
+	return options.warp ? DATABASE_WC : DATABASE;
 }
 
 static bool
-scores_read(struct score *scores)
+scores_read(struct score scores[SCORES_MAX])
 {
 	FILE *fp;
 
 	if (!(fp = fopen(scores_path(), "r")))
 		return false;
 
-	for (int i = 0; i < SCORES_MAX; ++i) {
-		int ret = fscanf(fp, "%31[^\\|]|%d|%lld\n", scores->name,
-		    &scores->score, &scores->time);
+	for (struct score *s = scores; s != &scores[SCORES_MAX]; ) {
+		int ret = fscanf(fp, "%31[^\\|]|%d|%lld\n", s->name,
+		    &s->score, &s->time);
 
 		if (ret == EOF)
 			break;
 		if (ret == 3)
-			scores++;
+			s++;
 	}
 
 	fclose(fp);
@@ -319,16 +341,15 @@
 }
 
 static bool
-scores_write(const struct score *scores)
+scores_write(const struct score scores[SCORES_MAX])
 {
 	FILE *fp;
 
 	if (!(fp = fopen(scores_path(), "w")))
 		return false;
 
-	for (int i = 0; i < SCORES_MAX; ++i)
-		fprintf(fp, "%s|%d|%lld\n", scores[i].name, scores[i].score,
-		    scores[i].time);
+	for (const struct score *s = scores; s != &scores[SCORES_MAX] && s->name[0]; ++s)
+		fprintf(fp, "%s|%d|%lld\n", s->name, s->score, s->time);
 
 	fclose(fp);
 
@@ -336,7 +357,7 @@
 }
 
 static bool
-scores_register(const struct snake *sn)
+scores_register(void)
 {
 	struct score scores[SCORES_MAX] = {0};
 	struct score *s;
@@ -345,7 +366,7 @@
 		return false;
 
 	for (s = scores; s != &scores[SCORES_MAX]; ++s)
-		if (sn->score >= s->score)
+		if (snake.score >= s->score)
 			break;
 
 	/* Not in top list. */
@@ -353,7 +374,7 @@
 		return true;
 
 	strncpy(s->name, name(), sizeof (s->name));
-	s->score = sn->score;
+	s->score = snake.score;
 	s->time = time(NULL);
 
 	return scores_write(scores);
@@ -382,72 +403,33 @@
 }
 
 static void
-wait(unsigned ms)
+wait(unsigned int ms)
 {
-#if defined(_WIN32)
-	Sleep(ms);
-#else
 	struct timespec ts = {
 		.tv_sec = 0,
 		.tv_nsec = ms * 1000000
 	};
 
-	nanosleep(&ts, NULL);
-#endif
+	while (nanosleep(&ts, &ts));
 }
 
 static void
-quit(const struct snake *sn)
+quit(void)
 {
-	if (sn != NULL) {
-		for (int i = 0; i < sn->length; ++i) {
-			mvwaddch(frame, sn->pos[i].y, sn->pos[i].x, ' ');
-			wait(50);
-			repaint();
-		}
+	/* Snake animation being dead. */
+	for (int i = 0; i < snake.length; ++i) {
+		mvwaddch(view.frame, snake.pos[i].y, snake.pos[i].x, ' ');
+		wait(50);
+		repaint();
 	}
 
-	delwin(top);
-	delwin(frame);
+	delwin(view.top);
+	delwin(view.frame);
 	delwin(stdscr);
 	endwin();
 }
 
-#if defined(HAVE_SIGWINCH)
-
-static void
-resize_handler(int signal)
-{
-	int x, y;
-
-	if (signal != SIGWINCH)
-		return;
-
-	/* XXX: I would like to pause the game until the terminal is resized */
-	endwin();
-
-#if defined(HAVE_RESIZETERM)
-	resizeterm(LINES, COLS);
-#endif
-	repaint(); clear();
-
-	/* Color the top bar */
-	wbkgd(top, COLOR_PAIR(0) | A_BOLD);
-
-	getmaxyx(stdscr, y, x);
-
-	if (x < WIDTH || y < HEIGHT) {
-		quit(NULL);
-		die(false, "Terminal has been resized too small, aborting");
-	}
-
-	mvwin(frame, (y / 2) - (HEIGHT / 2), (x / 2) - (WIDTH / 2));
-	repaint();
-}
-
-#endif
-
-static void
+static noreturn void
 usage(void)
 {
 	fprintf(stderr, "usage: nsnake [-cnsvw] [-C color]\n");
@@ -455,36 +437,30 @@
 }
 
 int
-main(int argc, char **argv)
+main(int argc, char *argv[])
 {
 	int x, y, ch;
-	bool running, show_scores = false;
-
-	struct snake sn = { 0, 4, 1, 0, {
-		{5, 10}, {5, 9}, {5, 8}, {5, 7} }
-	};
-
-	struct food fd = { NORM, 0, 0 };
+	bool running = true, show_scores = false;
 
 	while ((ch = getopt(argc, argv, "cC:nsvw")) != -1) {
 		switch (ch) {
 		case 'c':
-			setcolors = false;
+			options.color = -1;
 			break;
 		case 'C':
-			color = atoi(optarg);
+			options.color = atoi(optarg);
 			break;
 		case 'n':
-			noscore = true;
+			options.quick= true;
 			break;
 		case 's':
 			show_scores = true;
 			break;
 		case 'v':
-			verbose = true;
+			options.verbose = true;
 			break;
 		case 'w':
-			warp = false;
+			options.warp = false;
 			break;
 		default:
 			usage();
@@ -498,81 +474,60 @@
 	argc -= optind;
 	argv += optind;
 
-	srandom((unsigned)time(NULL));
-
-#if defined(HAVE_SIGWINCH)
-	signal(SIGWINCH, resize_handler);
-#endif
-
-	if (!init()) {
-		quit(NULL);
-		die(false, "Terminal too small, aborting");
-	}
-
-	if (top == NULL || frame == NULL) {
-		endwin();
-		die(false, "ncurses failed to init");
-	}
+	init();
 
 	/* Apply GRID_WALL to the edges */
 	for (y = 0; y < HEIGHT; ++y)
-		grid[y][0] = grid[y][WIDTH - 1] = GRID_WALL;
+		view.grid[y][0] = view.grid[y][WIDTH - 1] = GRID_WALL;
 	for (x = 0; x < WIDTH; ++x)
-		grid[0][x] = grid[HEIGHT - 1][x] = GRID_WALL;
+		view.grid[0][x] = view.grid[HEIGHT - 1][x] = GRID_WALL;
 
-	/* Do not spawn food on snake */
-	set_grid(&sn);
-	spawn(&fd);
-
-	/* Apply food on the grid */
-	grid[fd.y][fd.x] = GRID_FOOD;
-	draw(&sn, &fd);
+	/* Apply initial grid values */
+	update_grid();
+	spawn();
+	draw();
 
-	/* First direction is to right */
-	sn.pos[0].x += sn.dirx;
+	/* Move snake head position to avoid losing immediately. */
+	snake.pos[0].x += snake.dirx;
+
+	while (!is_dead() && running) {
+		int ch;
 
-	running = true;
-	while (!is_dead(&sn) && running) {
-		if (is_eaten(&sn)) {
-			int i;
+		if (is_eaten()) {
+			if (food.type == NORM)
+				snake.length += 2;
 
-			if (fd.type == NORM)
-				sn.length += 2;
-
-			for (i = 0; i < sn.length; ++i)
-				grid[sn.pos[i].y][sn.pos[i].x] = GRID_SNAKE;
+			for (int i = 0; i < snake.length; ++i)
+				view.grid[snake.pos[i].y][snake.pos[i].x] = GRID_SNAKE;
 
 			/* If the screen is totally filled */
-			if (sn.length >= SIZE) {
+			if (snake.length >= SIZE) {
 				/* Emulate new game */
-				for (i = 4; i < SIZE; ++i) {
-					mvwaddch(frame, sn.pos[i].y, sn.pos[i].x, ' ');
-					grid[sn.pos[i].y][sn.pos[i].x] = GRID_EMPTY;
+				for (int i = 4; i < SIZE; ++i) {
+					mvwaddch(view.frame, snake.pos[i].y, snake.pos[i].x, ' ');
+					view.grid[snake.pos[i].y][snake.pos[i].x] = GRID_EMPTY;
 				}
 
-				sn.length = 4;
+				snake.length = 4;
 			}
 
-			if (fd.type == NORM)
-				set_grid(&sn);
+			if (food.type == NORM)
+				update_grid();
 
-			/* Prevent food spawning on snake's tail */
-			spawn(&fd);
-
-			sn.score += 1;
-			grid[fd.y][fd.x] = GRID_FOOD;
+			spawn();
+			snake.score += 1;
 		}
 
-		/* Draw and define grid with snake parts */
-		draw(&sn, &fd);
-		set_grid(&sn);
+		/* Draw and define grid with snake.ke parts */
+		draw();
+		update_grid();
 
 		/* Go to the next position */
-		sn.pos[0].x += sn.dirx;
-		sn.pos[0].y += sn.diry;
+		snake.pos[0].x += snake.dirx;
+		snake.pos[0].y += snake.diry;
 
-		ch = getch();
-		if (ch == 'p') {
+		switch ((ch = getch())) {
+		case 'p':
 			nodelay(stdscr, FALSE);
 			while ((ch = getch()) != 'p' && ch != 'q')
 				;
@@ -581,37 +536,42 @@
 				running = 0;
 
 			nodelay(stdscr, TRUE);
-		} else if (ch == 'q')
+			break;
+		case 'q':
 			running = 0;
-		else if (ch == 'c')
-			color = (color + 1) % 8;
-		else if (ch)
-			direction(&sn, ch);
+			break;
+		case 'c':
+			options.color = (options.color + 1) % 8;
+			break;
+		default:
+			rotate(ch);
+			break;
+		}
 
 		/* If warp enabled, touching wall cross to the opposite */
-		if (warp) {
-			if (sn.pos[0].x == WIDTH - 1)
-				sn.pos[0].x = 1;
-			else if (sn.pos[0].x == 0)
-				sn.pos[0].x = WIDTH - 2;
-			else if (sn.pos[0].y == HEIGHT - 1)
-				sn.pos[0].y = 1;
-			else if (sn.pos[0].y == 0)
-				sn.pos[0].y = HEIGHT - 2;
+		if (options.warp) {
+			if (snake.pos[0].x == WIDTH - 1)
+				snake.pos[0].x = 1;
+			else if (snake.pos[0].x == 0)
+				snake.pos[0].x = WIDTH - 2;
+			else if (snake.pos[0].y == HEIGHT - 1)
+				snake.pos[0].y = 1;
+			else if (snake.pos[0].y == 0)
+				snake.pos[0].y = HEIGHT - 2;
 		}
 
-		if (sn.diry != 0)
+		if (snake.diry != 0)
 			wait(118);
 		else
 			wait(100);
 	}
 
-	/* The snake is dead. */
-	quit(&sn);
+	/* The snake.ke is dead. */
+	quit();
 
 	/* User has left or is he dead? */
-	printf("%sScore: %d\n", is_dead(&sn) ? "You died...\n" : "", sn.score);
+	printf("%sScore: %d\n", is_dead() ? "You died...\n" : "", snake.score);
 
-	if (!noscore && !scores_register(&sn))
+	if (!options.quick && !scores_register())
 		die(true, "Could not write score file %s", DATABASE);
 }
--- a/sysconfig.sh	Tue Jan 28 21:10:00 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#!/bin/sh
-#
-# sysconfig.sh -- automatically configure portability checks
-#
-# Copyright (c) 2011-2020 David Demelier <markand@malikania.fr>
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-#
-
-# Define CC, CFLAGS, LDFLAGS before calling this script.
-
-compile()
-{
-	$CC $CFLAGS -x "c" - -o /dev/null $LDFLAGS > /dev/null 2>&1 && echo $1
-}
-
-# resizeterm(3) (ncurses function)
-cat << EOF | compile "#define HAVE_RESIZETERM"
-#include <curses.h>
-
-int
-main(void)
-{
-	resizeterm(0, 0);
-}
-EOF