From cd81b53a599ce22a43a395b922537ae2a00c4656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Frankovi=C4=8D?= Date: Fri, 8 May 2026 13:24:14 +0200 Subject: [PATCH] game --- du4/game.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++++ du4/game.h | 36 +++++++++ 2 files changed, 255 insertions(+) create mode 100644 du4/game.c create mode 100644 du4/game.h diff --git a/du4/game.c b/du4/game.c new file mode 100644 index 0000000..f16712b --- /dev/null +++ b/du4/game.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include "game.h" +#include "snake.h" + +#define GAME_SPEED 100000 // microseconds between steps + +struct state* init_game() { + struct state* state = (struct state*)malloc(sizeof(struct state)); + if (state == NULL) { + return NULL; + } + + // Get screen dimensions + int max_x, max_y; + getmaxyx(stdscr, max_y, max_x); + + state->width = max_x - 2; + state->height = max_y - 3; + + // Initialize snake in the center + state->snake = NULL; + int center_x = state->width / 2; + int center_y = state->height / 2; + + // Add 5 initial snake parts (head is added last) + state->snake = add_snake(state->snake, center_x, center_y); + state->snake = add_snake(state->snake, center_x, center_y + 1); + state->snake = add_snake(state->snake, center_x, center_y + 2); + state->snake = add_snake(state->snake, center_x, center_y + 3); + state->snake = add_snake(state->snake, center_x, center_y + 4); + + // Initialize velocity + state->sx = 1; // Moving right + state->sy = 0; + + // Initialize food randomly + srand((unsigned int)time(NULL)); + for (int i = 0; i < FOOD_COUNT; i++) { + state->foodx[i] = rand() % state->width; + state->foody[i] = rand() % state->height; + } + + return state; +} + +void render_game(struct state* state) { + if (state == NULL) { + return; + } + + // Clear the screen + clear(); + + // Draw border + for (int x = 0; x < state->width + 2; x++) { + mvaddch(0, x, '+'); + mvaddch(state->height + 1, x, '+'); + } + for (int y = 0; y < state->height + 2; y++) { + mvaddch(y, 0, '+'); + mvaddch(y, state->width + 1, '+'); + } + + // Draw food + for (int i = 0; i < FOOD_COUNT; i++) { + if (state->foodx[i] >= 0 && state->foody[i] >= 0) { + mvaddch(state->foody[i] + 1, state->foodx[i] + 1, '*'); + } + } + + // Draw snake + struct snake* current = state->snake; + while (current != NULL) { + if (current->x >= 0 && current->x < state->width && + current->y >= 0 && current->y < state->height) { + mvaddch(current->y + 1, current->x + 1, 'x'); + } + current = current->next; + } + + // Draw info line + int food_count = 0; + for (int i = 0; i < FOOD_COUNT; i++) { + if (state->foodx[i] >= 0 && state->foody[i] >= 0) { + food_count++; + } + } + + int snake_length = 0; + current = state->snake; + while (current != NULL) { + snake_length++; + current = current->next; + } + + mvprintw(state->height + 2, 0, "Food: %d Snake Length: %d Speed: (%d, %d)", + food_count, snake_length, state->sx, state->sy); + + refresh(); +} + +int handle_input(struct state* state) { + int ch = getch(); + + if (ch == ERR) { + return 0; // No input + } + + switch (ch) { + case 'q': + case 'Q': + return 1; // Quit + case KEY_UP: + if (state->sy == 0) { // Don't allow reversing + state->sx = 0; + state->sy = -1; + } + break; + case KEY_DOWN: + if (state->sy == 0) { // Don't allow reversing + state->sx = 0; + state->sy = 1; + } + break; + case KEY_LEFT: + if (state->sx == 0) { // Don't allow reversing + state->sx = -1; + state->sy = 0; + } + break; + case KEY_RIGHT: + if (state->sx == 0) { // Don't allow reversing + state->sx = 1; + state->sy = 0; + } + break; + } + + return 0; +} + +void cleanup_game(struct state* state) { + if (state != NULL) { + free_snake(state->snake); + free(state); + } +} + +void run_game() { + // Initialize ncurses + initscr(); + cbreak(); + noecho(); + keypad(stdscr, TRUE); + nodelay(stdscr, TRUE); + curs_set(0); // Hide cursor + + struct state* game = init_game(); + if (game == NULL) { + endwin(); + return; + } + + int game_over = 0; + int end_reason = 0; + + while (!game_over) { + // Handle input + if (handle_input(game)) { + break; // User quit + } + + // Update game state + end_reason = step_state(game); + if (end_reason != END_CONTINUE) { + game_over = 1; + } + + // Render + render_game(game); + + // Delay + usleep(GAME_SPEED); + } + + // Show end game message + if (game_over) { + const char* message = ""; + switch (end_reason) { + case END_WALL: + message = "GAME OVER: Hit wall! Press any key to exit..."; + break; + case END_SNAKE: + message = "GAME OVER: Snake bit itself! Press any key to exit..."; + break; + case END_FOOD: + message = "VICTORY: All food eaten! Press any key to exit..."; + break; + case END_USER: + message = "Game ended. Press any key to exit..."; + break; + default: + message = "Game ended. Press any key to exit..."; + } + + mvprintw(game->height / 2, 5, "%s", message); + refresh(); + nodelay(stdscr, FALSE); + getch(); + } + + // Cleanup + cleanup_game(game); + endwin(); +} diff --git a/du4/game.h b/du4/game.h new file mode 100644 index 0000000..90172ae --- /dev/null +++ b/du4/game.h @@ -0,0 +1,36 @@ +#ifndef GAME_H_INCLUDED +#define GAME_H_INCLUDED + +#include "snake.h" + +/** + * Initialize the game state + * @return initialized game state + */ +struct state* init_game(); + +/** + * Render the current game state to the screen + * @param state the game state to render + */ +void render_game(struct state* state); + +/** + * Handle input and update game state + * @param state the game state to update + * @return 0 if game should continue, non-zero if should exit + */ +int handle_input(struct state* state); + +/** + * Clean up and free game resources + * @param state the game state to free + */ +void cleanup_game(struct state* state); + +/** + * Run the main game loop + */ +void run_game(); + +#endif // GAME_H_INCLUDED