#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include "grid.h" #include "snake.h" #define BASE_SPEED 6 void print_help() { printf("Usage: snake2025 [OPTIONS]\n"); printf("Options:\n"); printf(" -h, --help Display this help message\n"); printf(" -i, --input FILE Load initial grid configuration from FILE\n"); printf("\n"); printf("This is a simple snake game. Use the arrow keys to control the snake.\n"); printf("Press ESC to exit the game.\n"); } int main(int argc, char *argv[]) { FILE *stream; char *buf = NULL; size_t size_buf = 0; int nbl, nbc, i; int opt, option_index = 0; int loop_count = 0, nb_fruit = 0, total_fruits = 0; char *input_file = NULL; int width = 640, height = 480; MLV_Keyboard_button touche = MLV_KEYBOARD_NONE; int initial_size = 0; int current_level = 1; int global_score = 0; int previous_score = 0; int speed = BASE_SPEED; int boosts = 0; int boost_spawn_timer = 0; int boost_spawn_interval = 0; int boost_duration = 0; const int boost_duration_frames = 5 * 24; Grid *g; static struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"input", required_argument, 0, 'i'}, {0, 0, 0, 0}}; while ((opt = getopt_long(argc, argv, "hi:", long_options, &option_index)) != -1) { switch (opt) { case 'h': print_help(); return 0; case 'i': input_file = optarg; break; default: fprintf(stderr, "Unknown option. Use -h or --help for usage information.\n"); return 1; } } while (1) { Snake *snake = new_snake(); previous_score = global_score; if (input_file == NULL) { char level_file[256]; snprintf(level_file, sizeof(level_file), "levels/level%d", current_level); input_file = level_file; } stream = fopen(input_file, "r"); if (!stream) { fprintf(stderr, "Error: unable to open file for level %d\n", current_level); exit(EXIT_FAILURE); } nbl = count_nb_lines(stream); rewind(stream); nbc = getline(&buf, &size_buf, stream); if (nbc == -1) { fprintf(stderr, "Error: malformed file\n"); free(buf); fclose(stream); exit(EXIT_FAILURE); } nbc--; g = allocate_grid(nbl, nbc); copy(buf, g->grid[0]); for (i = 1; i < nbl; i++) { int size_tmp = getline(&buf, &size_buf, stream); if (size_tmp != nbc + 1) { fprintf(stderr, "Error: inconsistent line length\n"); free(buf); free_grid(g); fclose(stream); exit(EXIT_FAILURE); } copy(buf, g->grid[i]); } rewind(stream); nb_fruit = count_fruits(stream, g); total_fruits = nb_fruit; if (nb_fruit == 0) { fprintf(stderr, "Error: no fruits in the grid\n"); free(buf); free_grid(g); fclose(stream); exit(EXIT_FAILURE); } if (nb_fruit == -1) { fprintf(stderr, "Error: unable to count fruits\n"); free(buf); free_grid(g); fclose(stream); exit(EXIT_FAILURE); } free(buf); buf = NULL; fclose(stream); read_snake_from_grid(g, snake); initial_size = snake->size; MLV_create_window("SNAKE", "3R-IN1B", width, height); MLV_change_frame_rate(24); while ( MLV_get_event( &touche, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) == MLV_NONE || touche != MLV_KEYBOARD_ESCAPE) { Element result; char stats[256]; MLV_clear_window(MLV_COLOR_BLACK); loop_count = (loop_count + 1) % speed; if (loop_count == 0) { if (boost_spawn_timer >= boost_spawn_interval) { int x, y; do { x = rand() % g->nbc; y = rand() % g->nbl; } while (g->grid[y][x] != EMPTY); g->grid[y][x] = BOOST; boost_spawn_timer = 0; boost_spawn_interval = 50 + rand() % BASE_SPEED; } else { boost_spawn_timer++; } result = move_snake(snake, g); if (result == WALL || result == SNAKE) { if (result == WALL) { MLV_draw_text(width / 2 - 80, height / 2, "Game Over! You hit a wall.", MLV_COLOR_RED); } else if (result == SNAKE) { MLV_draw_text(width / 2 - 80, height / 2, "Game Over! You hit yourself.", MLV_COLOR_RED); } MLV_actualise_window(); MLV_wait_seconds(3); global_score = previous_score; boosts = 0; boost_duration = 0; speed = BASE_SPEED; break; } else if (result == BOOST) { boosts++; boost_duration = boost_duration_frames; speed = BASE_SPEED - boosts; speed = (BASE_SPEED - boosts) > 1 ? (BASE_SPEED - boosts) : 1; printf("Boost collected! Speed increased. Boost duration: %d frames\n", boost_duration); } else if (result == FRUIT) { Position *tail = snake->segments_list; Position *prev = NULL; int dx, dy; while (tail->next != NULL) { prev = tail; tail = tail->next; } dx = tail->x - (prev ? prev->x : tail->x); dy = tail->y - (prev ? prev->y : tail->y); add_segment(snake, tail->x + dx, tail->y + dy); place_snake(g, snake); nb_fruit--; global_score++; if (snake->size == initial_size + total_fruits) { MLV_draw_text( width / 2 - 200, height / 2, "You Win! All fruits collected. Proceeding to the next level.", MLV_COLOR_GREEN); MLV_actualise_window(); MLV_wait_seconds(3); current_level++; break; } } } if (boost_duration > 0) { boost_duration--; if (boost_duration == 0) { boosts--; speed = (BASE_SPEED - boosts) > 1 ? (BASE_SPEED - boosts) : 1; printf("Boost expired. Speed reset. Active boosts: %d\n", boosts); } } draw_grid(g); snprintf(stats, sizeof(stats), "Level: %d | Fruits Left: %d/%d | Score: %d | Snake Size: %d | Boosts: %d", current_level, nb_fruit, total_fruits, global_score, snake->size, boosts); MLV_draw_text(10, height - 20, stats, MLV_COLOR_WHITE); MLV_actualise_window(); switch (touche) { case MLV_KEYBOARD_DOWN: if (snake->dir != TOP) snake->dir = BOTTOM; break; case MLV_KEYBOARD_UP: if (snake->dir != BOTTOM) snake->dir = TOP; break; case MLV_KEYBOARD_LEFT: if (snake->dir != RIGHT) snake->dir = LEFT; break; case MLV_KEYBOARD_RIGHT: if (snake->dir != LEFT) snake->dir = RIGHT; break; default: break; } touche = MLV_KEYBOARD_NONE; MLV_delay_according_to_frame_rate(); } MLV_free_window(); free_grid(g); free_snake(snake); input_file = NULL; if (touche == MLV_KEYBOARD_ESCAPE) { break; } } return 0; }