summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorFederico Di Pierro2015-08-28 15:41:46 +0200
committerFederico Di Pierro2015-08-28 15:41:46 +0200
commitad708b87cec4b544b3bb2c82d0fd346e8f448551 (patch)
tree02597e70d5d30d2b04e2edb9e49b7e750e5db11b
downloadaur-ad708b87cec4b544b3bb2c82d0fd346e8f448551.tar.gz
Initial import
-rw-r--r--.SRCINFO17
-rw-r--r--PKGBUILD23
-rw-r--r--makefile3
-rw-r--r--snake.c414
4 files changed, 457 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..341e6843d17a
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,17 @@
+pkgbase = snake-ncurses
+ pkgdesc = snake game clone written in C and ncurses
+ pkgver = 8.7
+ pkgrel = 1
+ url = https://github.com/FedeDP/Snake
+ arch = i686
+ arch = x86_64
+ groups = games
+ license = GPL3
+ depends = ncurses
+ source = snake.c
+ source = makefile
+ md5sums = SKIP
+ md5sums = SKIP
+
+pkgname = snake-ncurses
+
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..e2982219a8b2
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,23 @@
+# Maintainer: Federico Di Pierro <nierro92@gmail.com>
+
+pkgname=snake-ncurses
+pkgver=8.7
+pkgrel=1
+pkgdesc="snake game clone written in C and ncurses"
+arch=(i686 x86_64)
+license=('GPL3')
+url="https://github.com/FedeDP/Snake"
+groups=('games')
+depends=('ncurses')
+source=("snake.c" "makefile")
+md5sums=('SKIP' 'SKIP')
+
+build() {
+ cd "$srcdir"
+ make
+}
+
+package() {
+ cd "$srcdir"
+ install -D -m755 $pkgname $pkgdir/usr/bin/$pkgname
+}
diff --git a/makefile b/makefile
new file mode 100644
index 000000000000..26e19e19bf7c
--- /dev/null
+++ b/makefile
@@ -0,0 +1,3 @@
+all:
+ gcc snake.c -o snake-ncurses -lncurses
+
diff --git a/snake.c b/snake.c
new file mode 100644
index 000000000000..5522d877ed3c
--- /dev/null
+++ b/snake.c
@@ -0,0 +1,414 @@
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <ncurses.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#define ROWS 30
+#define COLS 120
+#define STARTING_SIZE 3
+#define MAX_SCORE_LENGTH 9
+#define FRUIT_POINTS 7
+#define DEFAULT_SPEED 28000
+#define HARD_SPEED 22000
+#define EASY_SPEED 30000
+#define STORE -1
+#define LEAVE_OR_LOSE 1
+#define SNAKE_COLOR 2
+#define FRUIT_COLOR 1
+#define SNAKE_CHAR "O"
+#define FRUIT_CHAR "*"
+/* eigenvectors associated to snake movements */
+#define RIGHT 10
+#define LEFT -10
+#define UP -1
+#define DOWN 1
+
+/* Coordinates of snake's head and tail */
+struct point {
+ int x;
+ int y;
+};
+
+/* Program state struct */
+#pragma pack(push, 1)
+struct state {
+ int size;
+ int delay;
+ struct point snake_head;
+ struct point snake_tail;
+ struct point fruit_coord;
+};
+#pragma pack(pop)
+
+static void starting_questions(int argc, char *argv[]);
+static void check_term_size(int rowtot, int coltot);
+static void screen_init(int rowtot, int coltot);
+static void screen_end(int rowtot, int coltot, int quit_value);
+static void print_initial_snake(int x, int y);
+static void fruit_gen(void);
+static void grid_init(void);
+static void change_directions(void);
+static void eat_fruit(void);
+static void snake_move(int *quit_value);
+static void snake_grow(void);
+static void main_cycle(int *quit_value);
+static void colored_print(WINDOW *win, int x, int y, char *c, int color);
+static void init_func(char *argv);
+static void store_and_exit(void);
+static void store_score(void);
+static void print_score_list(void);
+static void manage_memory_error(void);
+static int *safe_realloc(int *ptr, size_t size);
+
+/* Give default "new match" values to program state struct */
+static struct state ps = {
+ .delay = DEFAULT_SPEED,
+ .size = STARTING_SIZE,
+ .snake_head = {ROWS/2, COLS/2},
+ .snake_tail = {ROWS/2, COLS/2 - (STARTING_SIZE - 1)},
+ .fruit_coord = {-1, -1}
+};
+static WINDOW *field, *score;
+static int *snake;
+
+int main(int argc, char *argv[])
+{
+ int rowtot, coltot, quit_value = 0;
+
+ starting_questions(argc, argv);
+ srand(time(NULL));
+ initscr();
+ getmaxyx(stdscr, rowtot, coltot);
+ check_term_size(rowtot, coltot);
+ screen_init(rowtot, coltot);
+ init_func(argv[1]);
+ grid_init();
+ wrefresh(field);
+ while (!quit_value) {
+ main_cycle(&quit_value);
+ }
+ screen_end(rowtot, coltot, quit_value);
+ free(snake);
+ return 0;
+}
+
+static void starting_questions(int argc, char *argv[])
+{
+ if ((argc == 1) || (((strcmp(argv[1],"-n")) != 0) && ((strcmp(argv[1],"-r")) != 0) && ((strcmp(argv[1],"-s")) != 0))) {
+ printf("Helper message.\nStart this program with:\n\t'-n $level'\tif you want to play a new game, where '$level' is one between easy and hard.\n\t\t\tLeaving only '-n' will play default level;\n\t'-r'\t\tto resume your last saved game;\n\t'-s'\t\tto view your top scores.\n");
+ exit(0);
+ }
+ if (((strcmp(argv[1],"-s")) == 0)) {
+ print_score_list();
+ exit(0);
+ }
+ if (argc == 3) {
+ if ((((strcmp(argv[argc - 1],"easy")) != 0)) && (((strcmp(argv[argc - 1],"hard")) != 0))) {
+ printf ("Level not recognized. Playing at default level.\n");
+ sleep(1);
+ } else {
+ if (((strcmp(argv[2],"easy")) == 0)) {
+ ps.delay = EASY_SPEED;
+ } else {
+ ps.delay = HARD_SPEED;
+ }
+ }
+ }
+}
+
+static void check_term_size(int rowtot, int coltot)
+{
+ if ((rowtot < ROWS + 6) || (coltot < COLS + 2)) {
+ clear();
+ endwin();
+ delwin(stdscr);
+ printf("This screen has %d rows and %d columns. Enlarge it.\n", rowtot, coltot);
+ printf("You need at least %d rows and %d columns.\n", ROWS + 6, COLS + 2);
+ exit(1);
+ }
+}
+
+static void screen_init(int rowtot, int coltot)
+{
+ start_color();
+ init_pair(1, COLOR_RED, COLOR_BLACK);
+ init_pair(2, COLOR_GREEN, COLOR_BLACK);
+ init_pair(3, COLOR_YELLOW, COLOR_BLACK);
+ init_pair(4, COLOR_CYAN, COLOR_BLACK);
+ raw();
+ noecho();
+ /* print sub windows centered */
+ field = subwin(stdscr, ROWS + 2, COLS + 2, (rowtot - 6 - ROWS) / 2, (coltot - COLS - 2) / 2);
+ score = subwin(stdscr, 2 + 2, coltot, rowtot - 4, 0);
+ keypad(field, TRUE);
+ nodelay(field, TRUE);
+ wattron(field, COLOR_PAIR(4));
+ wattron(score, COLOR_PAIR(3));
+ wborder(field, '|', '|', '-', '-', '+', '+', '+', '+');
+ wborder(score, '|', '|', '-', '-', '+', '+', '+', '+');
+ wattroff(field, COLOR_PAIR);
+ mvwprintw(score, 2, 1, "q anytime to *rage* quit. Arrow keys to move. s to save current game and leave.");
+ mvwprintw(score, 1, 1, "Points: ");
+ if (ps.size != STARTING_SIZE) {
+ wattron(score, A_BOLD);
+ }
+ mvwprintw(score, 1, strlen("Points: ") + 1, "%d", (ps.size - STARTING_SIZE) * FRUIT_POINTS);
+ wattron(field, A_BOLD);
+ wattron(score, A_BOLD);
+ colored_print(field, -1, -1, "Snake", 4);
+ mvwprintw(score, 0, 0, "Score");
+ wrefresh(score);
+}
+
+static void screen_end(int rowtot, int coltot, int quit_value)
+{
+ char exitmsg[] = "Leaving...bye! See you later :)";
+
+ wclear(field);
+ wclear(score);
+ delwin(field);
+ delwin(score);
+ attron(COLOR_PAIR(rand() % 4 + 1));
+ attron(A_BOLD);
+ if (quit_value == LEAVE_OR_LOSE) {
+ if (ps.size != STARTING_SIZE) {
+ store_score();
+ }
+ mvprintw(rowtot / 2, (coltot - strlen("You scored %d points!")) / 2, "You scored %d points!", (ps.size - STARTING_SIZE) * FRUIT_POINTS);
+ } else {
+ store_and_exit();
+ mvprintw(rowtot / 2, (coltot - strlen(exitmsg)) / 2, "%s", exitmsg);
+ }
+ refresh();
+ sleep(1);
+ attroff(COLOR_PAIR);
+ attroff(A_BOLD);
+ endwin();
+ delwin(stdscr);
+}
+
+static void print_initial_snake(int x, int y)
+{
+ int j;
+
+ colored_print(field, x, y, SNAKE_CHAR, SNAKE_COLOR);
+ for (j = 1; j < ps.size; j++) {
+ x = ((x - (snake[j] % 10)) + ROWS) % ROWS;
+ y = ((y - (snake[j] / 10)) + COLS) % COLS;
+ colored_print(field, x, y, SNAKE_CHAR, SNAKE_COLOR);
+ }
+}
+
+static void fruit_gen(void)
+{
+ int j, tot = ROWS * COLS;
+
+ if ((tot) == ps.size) {
+ return;
+ }
+ j = rand() % (tot);
+ do {
+ ps.fruit_coord.x = j / COLS;
+ ps.fruit_coord.y = j - (ps.fruit_coord.x * COLS);
+ j = (j + 1 + (tot)) % (tot);
+ } while ((mvwinch(field, ps.fruit_coord.x + 1, ps.fruit_coord.y + 1) & A_CHARTEXT) == *SNAKE_CHAR);
+ colored_print(field, ps.fruit_coord.x, ps.fruit_coord.y, FRUIT_CHAR, FRUIT_COLOR);
+}
+
+static void grid_init(void)
+{
+ print_initial_snake(ps.snake_head.x, ps.snake_head.y);
+ if (ps.fruit_coord.x == -1) {
+ fruit_gen();
+ } else {
+ colored_print(field, ps.fruit_coord.x, ps.fruit_coord.y, FRUIT_CHAR, FRUIT_COLOR);
+ }
+}
+
+static void snake_move(int *quit_value)
+{
+ char c;
+
+ ps.snake_head.x = ((ps.snake_head.x + snake[0] % 10) + ROWS) % ROWS;
+ ps.snake_head.y = ((ps.snake_head.y + snake[0] / 10) + COLS) % COLS;
+ c = mvwinch(field, ps.snake_head.x + 1, ps.snake_head.y + 1) & A_CHARTEXT;
+ colored_print(field, ps.snake_head.x, ps.snake_head.y, SNAKE_CHAR, SNAKE_COLOR);
+ if (c == *FRUIT_CHAR) {
+ eat_fruit();
+ mvwprintw(score, 1, strlen("Points: ") + 1, "%d", (ps.size - STARTING_SIZE) * FRUIT_POINTS);
+ wrefresh(score);
+ } else {
+ mvwprintw(field, ps.snake_tail.x + 1, ps.snake_tail.y + 1, " ");
+ ps.snake_tail.x = ((ps.snake_tail.x + snake[ps.size - 1] % 10) + ROWS) % ROWS;
+ ps.snake_tail.y = ((ps.snake_tail.y + snake[ps.size - 1] / 10) + COLS) % COLS;
+ if (c == *SNAKE_CHAR) {
+ *quit_value = 1;
+ return;
+ }
+ }
+}
+
+static void change_directions(void)
+{
+ int i;
+
+ for (i = ps.size - 1; i > 0; i--) {
+ snake[i] = snake[i - 1];
+ }
+}
+
+static void main_cycle(int *quit_value)
+{
+ wmove(field, ps.snake_head.x + 1, ps.snake_head.y + 1);
+ switch (wgetch(field)) {
+ case KEY_LEFT:
+ if (snake[0] != RIGHT) {
+ snake[0] = LEFT;
+ }
+ break;
+ case KEY_RIGHT:
+ if (snake[0] != LEFT) {
+ snake[0] = RIGHT;
+ }
+ break;
+ case KEY_UP:
+ if (snake[0] != DOWN) {
+ snake[0] = UP;
+ }
+ break;
+ case KEY_DOWN:
+ if (snake[0] != UP) {
+ snake[0] = DOWN;
+ }
+ break;
+ case 's':
+ *quit_value = STORE;
+ break;
+ case 'q': /* q to exit */
+ *quit_value = LEAVE_OR_LOSE;
+ break;
+ }
+ snake_move(quit_value);
+ change_directions();
+ usleep(ps.delay);
+}
+
+static void eat_fruit(void)
+{
+ ps.size++;
+ snake_grow();
+ fruit_gen();
+}
+
+static void snake_grow(void)
+{
+ snake = safe_realloc(snake, ps.size * sizeof(int));
+ snake[ps.size - 1] = snake[ps.size - 2];
+}
+
+static void colored_print(WINDOW *win, int x, int y, char *c, int color)
+{
+ wattron(win, COLOR_PAIR(color));
+ mvwprintw(win, x + 1, y + 1, c);
+ wattroff(win, COLOR_PAIR);
+}
+
+static void init_func(char *argv)
+{
+ char *path_resume_file = strcat(getpwuid(getuid())->pw_dir, "/.local/share/snake.txt");
+ FILE *f = NULL;
+ int i, resume = strcmp(argv, "-r");
+
+ if ((resume == 0) && (f = fopen(path_resume_file, "r"))) {
+ fread(&ps, sizeof(int), sizeof(struct state) / sizeof(int), f);
+ }
+ snake = safe_realloc(snake, sizeof(int) * ps.size);
+ if (f) {
+ fread(snake, sizeof(int), ps.size, f);
+ fclose(f);
+ remove(path_resume_file);
+ } else {
+ for (i = 0; i < ps.size; i++) {
+ snake[i] = RIGHT;
+ }
+ }
+}
+
+static void store_and_exit(void)
+{
+ char *path_resume_file = strcat(getpwuid(getuid())->pw_dir, "/.local/share/snake.txt");
+ FILE *f = fopen(path_resume_file, "w");
+
+ fwrite(&ps, sizeof(int), sizeof(struct state) / sizeof(int), f);
+ fwrite(snake, sizeof(int), ps.size, f);
+ fclose(f);
+}
+
+static void store_score(void)
+{
+ char *path_score_file = strcat(getpwuid(getuid())->pw_dir, "/.local/share/snake_score.txt");
+ FILE *f = NULL;
+ int i = 0, points = (ps.size - STARTING_SIZE) * FRUIT_POINTS, old_points;
+ long int len;
+
+ if ((f = fopen(path_score_file, "r+"))) {
+ while ((i < MAX_SCORE_LENGTH) && (!feof(f))) {
+ len = ftell(f);
+ fscanf(f, "%d\n", &old_points);
+ i++;
+ if (old_points < points) {
+ fseek(f, len, SEEK_SET);
+ fprintf(f, "%d\n", points);
+ points = old_points;
+ }
+ }
+ if (i < MAX_SCORE_LENGTH) {
+ fprintf(f, "%d\n", points);
+ }
+ } else {
+ f = fopen(path_score_file, "w");
+ fprintf(f, "%d\n", points);
+ }
+ fclose(f);
+}
+
+static void print_score_list(void)
+{
+ char *path_score_file = strcat(getpwuid(getuid())->pw_dir, "/.local/share/snake_score.txt");
+ FILE *f = NULL;
+ int i, score;
+
+ if ((f = fopen(path_score_file, "r"))) {
+ printf("\t\tTop scores:\n");
+ for (i = 0; (!feof(f) && (i < MAX_SCORE_LENGTH)); i++) {
+ fscanf(f, "%d\n", &score);
+ printf("\t\t%d) %d\n", i + 1, score);
+ }
+ fclose(f);
+ } else {
+ printf("No score list found.\n");
+ }
+}
+
+static void manage_memory_error(void)
+{
+ free(snake);
+ wclear(field);
+ wclear(score);
+ delwin(field);
+ delwin(score);
+ endwin();
+ delwin(stdscr);
+ printf("Memory allocation failed. Leaving.\n");
+ exit(EXIT_FAILURE);
+}
+
+static int *safe_realloc(int *ptr, size_t size)
+{
+ if (!(ptr = realloc(ptr, size))) {
+ manage_memory_error();
+ }
+ return ptr;
+} \ No newline at end of file