diff --git a/Makefile b/Makefile index 8e61469..060358d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC = gcc CFLAGS = -Wall -Wextra -std=c99 -dIRS = a1 du1 du2 du3 du4 du5 +dIRS = a1 du1 du2 du3 du4 du5 a3 all: @for %%d in ($(dIRS)) do \ $(CC) $(CFLAGS) %%d\program.c -o %%d\program.exe diff --git a/a3/Makefile b/a3/Makefile new file mode 100644 index 0000000..f15718e --- /dev/null +++ b/a3/Makefile @@ -0,0 +1,19 @@ +CC = gcc +CFLAGS = -Wall -Wextra -std=c99 + +OBJS = main.o snake.o world.o + +snake: $(OBJS) + $(CC) $(CFLAGS) -o snake $(OBJS) + +main.o: main.c snake.h world.h + $(CC) $(CFLAGS) -c main.c + +snake.o: snake.c snake.h + $(CC) $(CFLAGS) -c snake.c + +world.o: world.c world.h snake.h + $(CC) $(CFLAGS) -c world.c + +clean: + del *.o *.exe \ No newline at end of file diff --git a/a3/main.c b/a3/main.c new file mode 100644 index 0000000..7ee1a4d --- /dev/null +++ b/a3/main.c @@ -0,0 +1,42 @@ +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "snake.h" +#include "world.h" + +int main(void) { + struct state game_state; + + setup_world(&game_state, 20, 10); + draw_world(&game_state); + + while (1) { + char command = read_input(); + + change_direction(&game_state, command); + + int outcome = step_state(&game_state); + draw_world(&game_state); + + if (outcome != END_CONTINUE) { + break; + } + +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } + + free_snake(game_state.snake); + printf("\n--- KONIEC HRY ---\n"); + + return 0; +} diff --git a/a3/main.o b/a3/main.o new file mode 100644 index 0000000..79356d2 Binary files /dev/null and b/a3/main.o differ diff --git a/a3/snake.c b/a3/snake.c new file mode 100644 index 0000000..8ab3ecb --- /dev/null +++ b/a3/snake.c @@ -0,0 +1,110 @@ +#include +#include "snake.h" +#include + +struct snake* add_snake (struct snake* snake, int x, int y) { + struct snake* head = NULL; + + head = malloc(sizeof(struct snake)); + + if (!head) { + return snake; + } + head->x = x; + head->y = y; + + head->next = NULL; + + if (snake !=NULL) { + head->next = snake; + } + return head; +} + +struct snake* remove_snake(struct snake* snake) { + if (snake == NULL) { + return NULL; + } + + if (snake->next == NULL) { + free(snake); + return NULL; + } + + struct snake* temp = snake; + while (temp->next->next != NULL) { + temp = temp->next; + } + + free(temp->next); + temp->next = NULL; + + return snake; +} + +void free_snake(struct snake* sn) { + struct snake* walker = sn; + struct snake* next_part = NULL; + while (walker !=NULL) { + next_part = walker->next; + free(walker); + walker = next_part; + } +} + +int is_snake(struct snake* snake, int x, int y) { + struct snake* current = snake; + + while (current !=NULL) { + if ((current->x==x) && (current->y==y)) { + return 1; + } + current = current->next; + } + return 0; +} + +int step_state(struct state* state) { + int x = state->snake->x + state->sx; + int y = state->snake->y + state->sy; + + if (x < 0) return END_WALL; + if (x >= state->width) return END_WALL; + if (y < 0) return END_WALL; + if (y >= state->height) return END_WALL; + + if (is_snake(state->snake, x, y)) { + return END_SNAKE; + } + + int food_found = -1; + for (int idx = 0; idx < FOOD_COUNT; idx++) { + if (state->foodx[idx] == x && state->foody[idx] == y) { + food_found = idx; + break; + } + } + + if (food_found != -1) { + state->foodx[food_found] = -1; + state->foody[food_found] = -1; + state->snake = add_snake(state->snake, x, y); + + int remaining = 0; + for (int k = 0; k < FOOD_COUNT; ++k) { + remaining += (state->foodx[k] >= 0 && state->foody[k] >= 0); + } + + if (remaining == 0) { + return END_FOOD; + } + return END_CONTINUE; + } + + state->snake = add_snake(state->snake, x, y); + state->snake = remove_snake(state->snake); + + return END_CONTINUE; +} + + diff --git a/a3/snake.exe b/a3/snake.exe new file mode 100644 index 0000000..594e170 Binary files /dev/null and b/a3/snake.exe differ diff --git a/a3/snake.h b/a3/snake.h new file mode 100644 index 0000000..df3a11b --- /dev/null +++ b/a3/snake.h @@ -0,0 +1,111 @@ +#ifndef snake_h_INCLUDED +#define snake_h_INCLUDED + +// Number of food items on the plane +#define FOOD_COUNT 5 + +/** + * One part of the snake; + * + * The snake is a linked list; + */ +struct snake { + // x position of the snake part + int x; + // y position of the snake part + int y; + // Pointer to the next snake part. + // The last part of the snake has NULL pointer to the next part. + struct snake* next; +}; + +// End game reason constants, return value of step_state +enum endgame { + // Continue the game + END_CONTINUE = 0, + // Snake hit a wall + END_WALL, + // Snake hit itself + END_SNAKE, + // No food left + END_FOOD, + // Other reason to end + END_USER +}; + +/** + * State of the game. + * + * The state consists of the snake, its speed and food on the plane. + * + * The snake is a linked list of snake parts. + * + * Speed vector is a vector added to the last head position to create a new head. + * + * Food are points on the plane. Food with negative coordinates means food is already eaten. + */ +struct state { + // Snake as a linked list + struct snake* snake; + // X of the food positions + int foodx[FOOD_COUNT]; + // Y of the food positions + int foody[FOOD_COUNT]; + int sx; + int sy; + int width; + int height; +}; + +/** + * Add a new snake part with given position. The new snake part becomes the new head. + * + * @param head of the snake. + * @param x coordinate of the new head; + * @param y coordinate of the new head. + * @return new head of the snake. + */ +struct snake* add_snake(struct snake* snake, int x, int y); + +/** + * Remove the last snake part. + * The last snake part should always have NULL next pointer. + * + * @param head of the snake. + * @return new head of the snake. + */ +struct snake* remove_snake(struct snake* snake); + +/** + * Finds out if given coordinates are part of the snake. + * @param snake + * @param x coordinate to search in snake + * @param y coordinate to search in snake + * @return True, if there is a snake part with coordinates x,y. False otherwise + */ +int is_snake(struct snake* snake, int x, int y); + +/** + * Remove and free each snake part; + * @param head of the snake. + */ +void free_snake(struct snake* sn); + +/** + * Change game state. + * + * The function should calculate new position of the snake head + * from the current position and speed vector. + * Then it should modify snake parts or food coordinates according to the rules: + * + * - If the new position is on the snake, end the game, return END_SNAKE. + * - If the new position is on the food, mark food as eaten + * (set its coordinates to -1) and add new snake part on the position of the food. If there is no food left, return END_FOOD. Else return END_CONTINUE. + * - If the new position is on the plane, add new snake part on the new position and remove the last part of the snake, return END_CONTINUE. + * + * @param current state of the game + * @return reason to end the game according to enum endgame. + */ +int step_state(struct state* state); + +#endif // snake_h_INCLUDED \ No newline at end of file diff --git a/a3/snake.o b/a3/snake.o new file mode 100644 index 0000000..b4ddfcc Binary files /dev/null and b/a3/snake.o differ diff --git a/a3/world.c b/a3/world.c new file mode 100644 index 0000000..38fe0d8 --- /dev/null +++ b/a3/world.c @@ -0,0 +1,109 @@ +#include +#include +#include + +#include "world.h" +#include "snake.h" + +static void place_food(struct state* s) { + int placed = 0; + + while (placed < FOOD_COUNT) { + int fx = rand() % s->width; + int fy = rand() % s->height; + + if (!is_snake(s->snake, fx, fy)) { + s->foodx[placed] = fx; + s->foody[placed] = fy; + placed++; + } + } +} + +void setup_world(struct state* s, int w, int h) { + if (!s) return; + + srand((unsigned int)time(NULL)); + s->width = w; + s->height = h; + + s->sx = 1; + s->sy = 0; + + s->snake = NULL; + int start_x = w / 2; + int start_y = h / 2; + + s->snake = add_snake(s->snake, start_x, start_y); + + place_food(s); +} + +void draw_world(const struct state* s) { + if (!s) return; + + system("clear"); // или "cls" на Windows + + for (int row = 0; row < s->height; row++) { + for (int col = 0; col < s->width; col++) { + int drawn = 0; + + if (is_snake(s->snake, col, row)) { + putchar('O'); + drawn = 1; + } else { + for (int i = 0; i < FOOD_COUNT; ++i) { + if (s->foodx[i] == col && s->foody[i] == row) { + putchar('*'); + drawn = 1; + break; + } + } + } + + if (!drawn) { + putchar('.'); + } + } + putchar('\n'); + } +} + +char read_input(void) { + char key = 0; + + printf("Zadaj smer (WASD): "); + scanf(" %c", &key); + + return key; +} + +void change_direction(struct state* s, char key) { + if (!s) return; + + switch (key) { + case 'w': + case 'W': + s->sx = 0; + s->sy = -1; + break; + case 's': + case 'S': + s->sx = 0; + s->sy = 1; + break; + case 'a': + case 'A': + s->sx = -1; + s->sy = 0; + break; + case 'd': + case 'D': + s->sx = 1; + s->sy = 0; + break; + default: + // ничего не делаем при некорректном вводе + break; + } +} diff --git a/a3/world.h b/a3/world.h new file mode 100644 index 0000000..3dbfc40 --- /dev/null +++ b/a3/world.h @@ -0,0 +1,11 @@ +#ifndef WORLD_MODULE_H +#define WORLD_MODULE_H + +#include "snake.h" + +void setup_world(struct state* s, int w, int h); +void draw_world(const struct state* s); +char read_input(void); +void change_direction(struct state* s, char key); + +#endif diff --git a/a3/world.o b/a3/world.o new file mode 100644 index 0000000..e539001 Binary files /dev/null and b/a3/world.o differ