summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--game.c104
-rw-r--r--main.c6
-rw-r--r--map.h17
-rw-r--r--snake.c167
-rw-r--r--snake.h54
6 files changed, 344 insertions, 8 deletions
diff --git a/Makefile b/Makefile
index 027c0c6..149cb8f 100644
--- a/Makefile
+++ b/Makefile
@@ -5,8 +5,8 @@ OBJ = $(patsubst %.c, %.o, $(SRC))
CC := gcc
-CFLAGS := -Wall -Wextra -Werror -pedantic -std=c99 -g
-LDFLAGS :=
+CFLAGS := -Wall -Wextra -Werror -g
+LDFLAGS := -lncurses
$(TARGET): $(OBJ)
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
diff --git a/game.c b/game.c
new file mode 100644
index 0000000..7a213c3
--- /dev/null
+++ b/game.c
@@ -0,0 +1,104 @@
+#include <locale.h>
+#include <ncurses.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "snake.h"
+
+#define DEFAULT_WIDTH 30
+#define DEFAULT_HEIGHT 25
+
+/* draws the game map. Excepts a window large enough to print the map */
+static void draw(WINDOW *display, Map *map) {
+ werase(display);
+
+ box(display, 0, 0);
+
+ if (!map->fruit_eaten) {
+ mvwaddch(display, map->fruit.y, map->fruit.x, '*');
+ }
+
+ for (PointList *i = map->snake->tail; i != NULL; i = i->next) {
+ mvwaddch(display, i->point.y, i->point.x, '#');
+ }
+
+ wrefresh(display);
+}
+
+int main(int argc, char **argv) {
+ setlocale(LC_ALL, "");
+
+ initscr();
+ cbreak();
+ noecho();
+ nodelay(stdscr, true);
+ curs_set(0);
+
+ const unsigned short target_fps = 5;
+ bool quit = false;
+
+ Map *map;
+ if (argc == 3) {
+ unsigned int width = (unsigned int)strtol(argv[1], NULL, 10);
+ unsigned int height = (unsigned int)strtol(argv[2], NULL, 10);
+ map = create_map(width, height);
+ } else {
+ map = create_map(DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ }
+
+ if (map == NULL) {
+ fprintf(stderr, "ERROR: Failed to allocate map");
+ endwin();
+ exit(1);
+ }
+
+ WINDOW *map_window = newwin(map->height, map->width, (LINES/2) - (map->height/2), (COLS/2) - (map->width/2));
+
+ draw(map_window, map);
+
+ while (!quit) {
+ int key;
+
+ /* Get Input */
+ key = getch();
+
+ switch(key) {
+ case 'w':
+ map->snake->moving_direction = NORTH;
+ break;
+ case 'a':
+ map->snake->moving_direction = WEST;
+ break;
+ case 's':
+ map->snake->moving_direction = SOUTH;
+ break;
+ case 'd':
+ map->snake->moving_direction = EAST;
+ break;
+ case 'q':
+ quit = true;
+ break;
+ }
+
+ /* Process game logic */
+ if (!move_snake(map)) {
+ nodelay(stdscr, false);
+ /* TODO: Proper game over logic */
+ clear();
+ mvprintw(0, 0, "Game Over");
+ getch();
+ quit = true;
+ }
+
+ /* Draw frame */
+ if (!quit) draw(map_window, map);
+
+ /* TODO: calculate a proper wait time */
+ napms((1.0f/(float)target_fps) * 1000);
+ }
+
+ free_map(map);
+
+ delwin(map_window);
+ endwin();
+ return 0;
+}
diff --git a/main.c b/main.c
deleted file mode 100644
index 6cc468f..0000000
--- a/main.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#include <stdio.h>
-
-int main() {
- printf("Hello, world!\n");
- return 0;
-}
diff --git a/map.h b/map.h
new file mode 100644
index 0000000..018376f
--- /dev/null
+++ b/map.h
@@ -0,0 +1,17 @@
+#ifndef MAP_H
+#define MAP_H
+
+typedef struct Point {
+ int x;
+ int y;
+} Point;
+
+typedef struct Map {
+ Point fruit;
+ unsigned int height;
+ unsigned int width;
+} Map;
+
+Map createMap(unsigned int width, unsigned int height);
+
+#endif
diff --git a/snake.c b/snake.c
index 8b13789..0cd6915 100644
--- a/snake.c
+++ b/snake.c
@@ -1 +1,168 @@
+#include "snake.h"
+#include <stdlib.h>
+#include <stdbool.h>
+#include <time.h>
+static bool position_in_snake(Point point, const Snake *snake) {
+ for (PointList *i = snake->tail; i != NULL; i = i->next) {
+ if (point.x == i->point.x && point.y == i->point.y) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool point_out_of_bounds(Point point, const Map* map) {
+ if (point.x >= map->width || point.y >= map->height)
+ return true;
+ return false;
+}
+
+static void spawn_fruit(Map *map) {
+ if (map->snake->length >= map->width * map->height) return;
+
+ Point point;
+ do {
+ point.x = rand() % map->width;
+ point.y = rand() % map->height;
+ } while(position_in_snake(point, map->snake));
+
+ map->fruit.x = point.x;
+ map->fruit.y = point.y;
+ map->fruit_eaten = false;
+}
+
+void free_snake(Snake *snake) {
+ while(snake->tail != NULL) {
+ PointList *tmp = snake->tail;
+ snake->tail = snake->tail->next;
+ free(tmp);
+ }
+ free(snake);
+}
+
+/*⠀⠀⠀⢘⠀⡂⢠⠆⠀⡰⠀⡀⢀⣠⣶⣦⣶⣶⣶⣶⣾⣿⣿⡿⢀⠈⢐⠈⠀⠀
+ *⠀⠀⠀⡃⠀⡀⣞⡇⢰⠃⣼⣇⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⠛⣰⣻⡀⢸⠀⠀⠀
+ *⠀⠀⢐⢀⠀⣛⣽⣇⠘⢸⣿⣿⣷⣾⣿⣿⣿⣿⣿⣿⠟⢡⣾⣿⢿⡇⠀⡃⠀⠀
+ *⠀⠀⢐⠀⠀⢳⣿⡯⡞⣾⣿⣿⣿⣿⣿⣿⢿⣿⠟⢁⣴⣿⣿⣿⡜⢷⠀⢘⠄⠀
+ *⠀⠀⠂⡂⠸⡆⠙⠛⡵⣿⣿⣿⣿⣿⡿⠤⠛⣠⣴⣿⣿⠿⣟⣟⠟⢿⡆⢳⠀⠀
+ *⠀⠀⠸⠁⠀⡾⠁⠀⠀⠀⠀⠉⠉⠉⠈⣠⡌⢁⠄⡛⠡⠉⠍⠙⢳⢾⠁⢸⠀⠀
+ *⠀⠀⢈⠀⢀⠌⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣷⡎⠙⢬⣳⣪⡯⢜⣷⢸⠂⡈⠄⠀
+ *⠀⠀⠐⡀⠀⢣⠀⠀⠀⠀⠀⠀⠀⣴⣿⣾⣷⢿⢻⣅⣌⡯⢛⣿⣿⡞⠠⡁⠂⠀
+ *⠀⠀⠀⠄⠀⢉⡀⠀⠀⢀⡠⠤⠼⣇⣳⣿⣿⣟⡜⣿⣿⣿⣿⣿⣿⡇⠸⠡⠀⠀
+ *⠀⠀⡀⠅⠀⠃⢀⡀⣿⡹⠗⢀⠛⠥⣺⣿⣿⡝⢹⣸⣿⣿⣿⣿⡏⠠⠰⠈⠐⠀
+ *⠠⠈⠀⠄⣀⠀⠀⠸⠻⠦⠀⠀⠀⠀⠀⠉⠐⠀⠘⠻⢹⣿⡿⠃⠀⡀⠕⣈⠡⡄
+ *⠀⠀⣴⡀⣬⠁⠀⠀⡁⠂⠀⣀⣀⠔⠌⠤⣀⡀⠀⠀⡈⢸⠪⠀⠀⡌⠤⠈⡀⣠
+ *⠀⠀⣿⣿⣾⡇⠀⠀⠀⣴⢫⣾⠃⠠⢰⣶⣴⠶⣿⣦⠀⠀⠀⢄⣂⠀⠀⠰⠀⠙
+ *⠀⠀⠉⠛⠛⠀⢀⣴⣿⢗⡟⠡⣄⣀⡀⠀⢀⣤⠞⡅⠀⠁⠀⡾⠀⠀⠠⡗⠀⢀
+ *⠀⠀⠀⠀⠀⣴⡿⢋⠔⠃⠀⠀⠍⠙⠉⠈⠑⠁⠂⠀⠀⠀⡡⡁⣠⡼⣸⠅⠀⠘
+ *⠀⠀⠀⣼⠛⢡⠔⠁⠐⣆⠀⠀⠀⠀⠀⠀⠀⠀⠁⢀⡔⡞⢛⣿⡿⠃⠏⠀⠀⢠
+ *⠀⠀⠀⠈⠗⠀⠀⠀⠀⠘⣷⣀⢀⣀⣀⠀⡀⢀⣌⡧⠂⠀⡞⠛⡟⠀⠀⠀⡠⠜
+ *⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠓⠈⠙⠙⠋⠉⠁⠀⠀⠀⠀⠀⠀⠀⡂⠠⠤⢶
+ */
+Snake *create_snake(unsigned int x, unsigned int y, Direction direction) {
+ Snake *snake = malloc(sizeof(Snake));
+ if (snake == NULL) return NULL;
+ snake->head = malloc(sizeof(PointList));
+ if (snake->head == NULL) {
+ free(snake);
+ return NULL;
+ }
+ snake->length = 1;
+ snake->tail = snake->head;
+
+ snake->head->point.x = x;
+ snake->head->point.y = y;
+
+ snake->moving_direction = direction;
+
+ return snake;
+}
+
+Map *create_map(unsigned int width, unsigned int height) {
+ srand(time(NULL));
+
+ Map *map = malloc(sizeof(Map));
+ if (map == NULL) return NULL;
+
+ map->width = width;
+ map->height = height;
+
+ unsigned int snake_x = rand() % width;
+ unsigned int snake_y = rand() % height;
+
+ Direction snake_dir;
+ if (rand() % 2 == 0) {
+ if (snake_x > width / 2) {
+ snake_dir = WEST;
+ } else {
+ snake_dir = EAST;
+ }
+ } else {
+ if (snake_y > height / 2) {
+ snake_dir = NORTH;
+ } else {
+ snake_dir = SOUTH;
+ }
+ }
+
+ Snake *snake = create_snake(snake_x, snake_y, snake_dir);
+ if (snake == NULL) {
+ free_snake(snake);
+ return NULL;
+ }
+ map->snake = snake;
+
+ spawn_fruit(map);
+
+ return map;
+}
+
+bool move_snake(Map *map) {
+ PointList *neohead = malloc(sizeof(PointList));
+ if (neohead == NULL) {
+ exit(1);
+ }
+ neohead->point = map->snake->head->point;
+ neohead->next = NULL;
+
+ switch(map->snake->moving_direction) {
+ case NORTH:
+ neohead->point.y--;
+ break;
+ case WEST:
+ neohead->point.x--;
+ break;
+ case EAST:
+ neohead->point.x++;
+ break;
+ case SOUTH:
+ neohead->point.y++;
+ break;
+ break;
+ }
+
+ if (point_out_of_bounds(neohead->point, map)) return false;
+
+ if (position_in_snake(neohead->point, map->snake)) return false;
+
+ map->snake->head->next = neohead;
+ map->snake->head = neohead;
+
+ if (neohead->point.x == map->fruit.x && neohead->point.y == map->fruit.y && !map->fruit_eaten) {
+ map->fruit_eaten = true;
+ map->snake->length++;
+ spawn_fruit(map);
+ } else {
+ PointList *tmp = map->snake->tail;
+ map->snake->tail = map->snake->tail->next;
+ free(tmp);
+ }
+
+ return true;
+}
+
+void free_map(Map *map) {
+ free_snake(map->snake);
+ free(map);
+}
diff --git a/snake.h b/snake.h
index e69de29..a7439bb 100644
--- a/snake.h
+++ b/snake.h
@@ -0,0 +1,54 @@
+#ifndef SNAKE_H
+#define SNAKE_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+typedef enum Direction {
+ NORTH,
+ WEST,
+ EAST,
+ SOUTH
+} Direction;
+
+typedef struct Point {
+ unsigned int x;
+ unsigned int y;
+} Point;
+
+typedef struct PointList {
+ Point point;
+ struct PointList *next;
+} PointList;
+
+typedef struct Snake {
+ PointList *head;
+ PointList *tail;
+ size_t length;
+ Direction moving_direction;
+} Snake;
+
+typedef struct Map {
+ Point fruit;
+ Snake *snake;
+ bool fruit_eaten;
+ unsigned int height;
+ unsigned int width;
+} Map;
+
+Snake *create_snake(unsigned int x, unsigned int y, Direction direction);
+
+Map *create_map(unsigned int width, unsigned int height);
+
+/* Attempts to move the snake.
+ * Returns true if the snake sucessfully moved,
+ * false for game over.
+ */
+bool move_snake(Map *map);
+
+/* Frees the snake */
+void free_snake(Snake *snake);
+
+void free_map(Map *map);
+
+#endif /* SNAKE_H */