From 305975051e804c6e9b3904f625528bb4e195807f Mon Sep 17 00:00:00 2001 From: mszczepa <marin.szczepanski@inria.fr> Date: Tue, 31 May 2022 14:11:17 +0200 Subject: [PATCH] Add simple snake level --- levels/snake/Makefile | 23 ++++++ levels/snake/events_map.c | 67 +++++++++++++++++ levels/snake/events_map.h | 24 ++++++ levels/snake/input_manager.c | 60 +++++++++++++++ levels/snake/input_manager.h | 9 +++ levels/snake/main.c | 141 +++++++++++++++++++++++++++++++++++ levels/sokoban/main.c | 2 +- 7 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 levels/snake/Makefile create mode 100644 levels/snake/events_map.c create mode 100644 levels/snake/events_map.h create mode 100644 levels/snake/input_manager.c create mode 100644 levels/snake/input_manager.h create mode 100644 levels/snake/main.c diff --git a/levels/snake/Makefile b/levels/snake/Makefile new file mode 100644 index 00000000..6a069157 --- /dev/null +++ b/levels/snake/Makefile @@ -0,0 +1,23 @@ +MAINPROG := main +OBJECTS := main.o \ + ../../engines/fullgame/map.o ../../engines/fullgame/action.o ../../engines/fullgame/common.o ../../engines/fullgame/events.o ../../engines/fullgame/read_input.o \ + events_map.o input_manager.o + +#DEFINES := -Dspontaneous_generation +DEFILES := +CC := gcc +CFLAGS := -g -Wall -Wextra $(DEFINES) + +all: main + +main: $(OBJECTS) + $(CC) $(CFLAGS) $(DEFINES) -o $@ $(OBJECTS) + +clean: + rm *.o + rm ../../engines/fullgame/*.o + +clear: clean + rm main + +.PHONY: all clean clear diff --git a/levels/snake/events_map.c b/levels/snake/events_map.c new file mode 100644 index 00000000..dd051c1c --- /dev/null +++ b/levels/snake/events_map.c @@ -0,0 +1,67 @@ +#include "events_map.h" + +bool no_apple = true; +int apple_id = 0; + +void update_snake(void) { + map * m = current_map(); + int i = coord_idx(m->player_y, m->player_x); + + // snake ate itself, bad + if (m->entities[i] + && m->entities[i]->category == NPC + && m->entities[i]->id != head->body->id) { + printf("You chomped yourself!\n"); + level_failed(); + return; + } + + // snake ate apple, good + if (m->entities[i] + && m->entities[i]->category == ITEM) { + m->entities[i]->category = NPC; + snake * new_head = malloc(sizeof(snake)); + new_head->body = m->entities[i]; + new_head->next = head; + head = new_head; + no_apple = true; + return; + } + + // snake moved + snake * pred = NULL; + snake * tail = head; + while (tail->next) { + pred = tail; + tail = tail->next; + } + tail->next = head; + pred->next = NULL; + head = tail; + + int j = 0; + while (!m->entities[j] + || m->entities[j]->id != head->body->id) + j++; + m->entities[j] = NULL; + m->entities[i] = head->body; +} + + +void spawn_apple(void) { + if (!no_apple) + return; + + map * m = current_map(); + int i = rand() % (m->width * m->height); + + while (m->entities[i]) + i = rand() % (m->width * m->height); + + m->entities[i] = malloc(sizeof(entity)); + m->entities[i]->category = ITEM; + m->entities[i]->id = apple_id++ + (m->width * m->height); + #ifndef spontaneous_generation + no_apple = false; + #endif +} diff --git a/levels/snake/events_map.h b/levels/snake/events_map.h new file mode 100644 index 00000000..1c425dbd --- /dev/null +++ b/levels/snake/events_map.h @@ -0,0 +1,24 @@ +#ifndef EVENTS_MAP_H +#define EVENTS_MAP_H + +#include "../../engines/fullgame/agdbentures.h" + +typedef struct Snake { + entity * body; + struct Snake * next; +} snake; + +snake * head; + +/** + * moves the snake and makes it longer if it ate an apple. + * Have it crash and game over if it ate itself. + */ +void update_snake(void); + +/** + * Creates an apple at a random location outside of the snake's body. + */ +void spawn_apple(void); + +#endif // EVENTS_MAP_H diff --git a/levels/snake/input_manager.c b/levels/snake/input_manager.c new file mode 100644 index 00000000..4c958b91 --- /dev/null +++ b/levels/snake/input_manager.c @@ -0,0 +1,60 @@ +#include "input_manager.h" + +void apply_input(command * c) { + map * m = current_map(); + direction dir = m->player_direction; + + if (!strncmp(c->command_buffer, "U", 1)) { + dir = UP; + } else if (!strncmp(c->command_buffer, "D", 1)) { + dir = DOWN; + } else if (!strncmp(c->command_buffer, "L", 1)) { + dir = LEFT; + } else if (!strncmp(c->command_buffer, "R", 1)) { + dir = RIGHT; + } else { + printf("Unknown input command. Continuing in the same direction\n"); + } + + int y, x; + coord_in_dir(m->player_y, m->player_x, dir, &y, &x); + + // there is no wall in this game so collision will be 0 or -1 + if (collision(x, y)) { + switch (dir) { + case UP: + m->player_y = m->height - 1; + break; + case DOWN: + m->player_y = 0; + break; + case LEFT: + m->player_x = m->width - 1; + break; + case RIGHT: + m->player_x = 0; + break; + default: + printf("apply_input: invalid direction\n"); + break; + } + } else { + switch (dir) { + case UP: + up(); + break; + case DOWN: + down(); + break; + case LEFT: + left(); + break; + case RIGHT: + right(); + break; + default: + printf("apply_input: invalid direction\n"); + break; + } + } +} diff --git a/levels/snake/input_manager.h b/levels/snake/input_manager.h new file mode 100644 index 00000000..0ce4098e --- /dev/null +++ b/levels/snake/input_manager.h @@ -0,0 +1,9 @@ +#ifndef INPUT_MANAGER_H +#define INPUT_MANAGER_H + +#include "../../engines/fullgame/agdbentures.h" + +// examine la commande c et appelle les fonctions d'action ou de déplacement correspondantes +void apply_input(command * c); + +#endif // INPUT_MANAGER_H \ No newline at end of file diff --git a/levels/snake/main.c b/levels/snake/main.c new file mode 100644 index 00000000..4dd30231 --- /dev/null +++ b/levels/snake/main.c @@ -0,0 +1,141 @@ +/** @AGDB + * level_name: Snake + * program_name: main + * level_number: 1109 + * available_commands: next step continue + * map_height: 10 + * map_width: 10 + * + * BUG: We forgot to reset no_apple to false in spawn_apple, so apples spawn at each step. + * tag: snake simple chained_list + * + * HINT1: Do 1 box at a time + */ + +/** + * Entry point of the game. Manages the main loop, and apart from + * that only calls functions. + * + * Game design related tip: + * Snake is a weird game you can't win, yet many people like it, maybe + * because it is simplistic and allows you to pass time + * on your brand new Nokia phone, or TI82. +*/ + +#include "../../engines/fullgame/agdbentures.h" +#include "events_map.h" +#include "input_manager.h" + +const char * str_map = +"\ + \n\ + ^ \n\ + A \n\ + A \n\ + A \n\ + \n\ + \n\ + \n\ + \n\ + \n\ +"; + + +/** + * Generates a snake in the form of a linked list + */ +void create_snake(void) { + snake * cur; + snake * pred = NULL; + map * m = current_map(); + + for (int i = 0; i < m->width * m->height; i++) { + if (m->entities[i] && m->entities[i]->category == NPC) { + cur = malloc(sizeof(snake)); + if (pred) + pred->next = cur; + else + head = cur; + cur->body = m->entities[i]; + cur->next = NULL; + pred = cur; + } + } + + // actual head is where the player is + int i = coord_idx(m->player_y, m->player_x); + m->entities[i] = malloc(sizeof(entity)); + m->entities[i]->category = NPC; + m->entities[i]->id = pred->body->id + 1; + cur = malloc(sizeof(snake)); + cur->next = head; + cur->body = m->entities[i]; + head = cur; +} + + +/** + * Frees the body of a snake starting from its parameter + */ +void free_snake(snake * s) { + if (s->next) + free_snake(s->next); + free(s); +} + + +int main(int argc, char ** argv) { + bool manual_mode = true; + if (argc > 1) + manual_mode = false; + + if (manual_mode) { + printf("Manual mode. Available commands: UP, DOWN, LEFT, RIGHT and *_N variants.\nUse Ctrl+C to quit.\n"); + } else { + if (!init_inputs_file(argv[1])) { + printf("Error while reading the inputs!\n"); + return EXIT_FAILURE; + } + } + + + load_map("torus", str_map); + create_snake(); + srand(0xC0FF33); // random seed + spawn_apple(); + add_event(update_snake); + add_event(spawn_apple); + + if (manual_mode) + show_map(); + + command * com = get_next_input(); + + // The Holy Main Loop + while (!exit_main_loop) { + apply_input(com); + + /* since the events may be applied if the input is FORWARD_N, + we need to check to avoid triggering the same event twice*/ + if (!exit_main_loop) + apply_events(); + + if (manual_mode) + show_map(); + + if (!exit_main_loop) + com = get_and_free_input(com); + + // plus aucun input mais on a pas encore gagné ou perdu -> défaite + if (!manual_mode && com == NULL && !exit_main_loop) + level_failed(); + } + + free_snake(head); + free_command(com); + remove_all_events(); + free_map_stack(); + close_inputs_file(); + + return EXIT_SUCCESS; +} diff --git a/levels/sokoban/main.c b/levels/sokoban/main.c index 21fee5b4..c51503ac 100644 --- a/levels/sokoban/main.c +++ b/levels/sokoban/main.c @@ -43,7 +43,7 @@ int main(int argc, char ** argv) { manual_mode = false; if (manual_mode) { - printf("Manual mode. Available commands: UP, DOWN, LEFT, RIGHT and *_N variants; TOUCH.\nUse Ctrl+C to quit.\n"); + printf("Manual mode. Available commands: UP, DOWN, LEFT, RIGHT and *_N variants.\nUse Ctrl+C to quit.\n"); } else { if (!init_inputs_file(argv[1])) { printf("Error while reading the inputs!\n"); -- GitLab