This commit is contained in:
Jakub Frankovič 2026-05-08 14:16:31 +02:00
parent 3e55eb2087
commit 733d099ceb
43 changed files with 2560 additions and 131 deletions

151
a3/checkcheck.txt Normal file
View File

@ -0,0 +1,151 @@
# ODOVZDANIE PROJEKTU: SNAKE GAME
## Umiestnenie projektu
```
/home/kubo/pvjc26/a3/
```
## Spustenie hry
```bash
cd /home/kubo/pvjc26/a3/
./program
```
---
## Čo bolo implementované
### ✓ Herné požiadavky (podľa zadania)
1. **Svet (hracie pole)**
- Viditeľná oblasť obrazovky s hranicami
- Dynamické rozmery podľa veľkosti terminálu
2. **Odmeny**
- 20 odmien (`*`) náhodne rozhádzaných po svete
- Zjedené odmeny označené súradnicami [-1, -1]
- Detekcia kedy sú všetky zjedené → víťazstvo
3. **Had**
- Telo z `x` znakov
- Počiatok: 5 segmentov v strede obrazovky
- Reprezentácia: Spojový zoznam (linked list)
- Rast pri zjedení odmeny
4. **Pohyb hada**
- Vektor rýchlosti [sx, sy]
- Zmena podľa šípiek:
- ↑ = [0, -1]
- ↓ = [0, 1]
- ← = [-1, 0]
- → = [1, 0]
- Bezpečnosť: Zákaz otočenia na 180°
5. **Logika hry - konečné stavy**
- Zrážka so stenou → END_WALL
- Zrážka so sebou samým → END_SNAKE
- Všetky odmeny zjedené → END_FOOD (víťazstvo)
- Pokračovanie → END_CONTINUE
### ✓ Technické implementácie
- Ncurses knižnica na grafiku v terminálu
- Herný loop s reálnym časom
- Detekcia vstupov v reálnom čase
- Spracovanie signálov bezpečne
- Čistá pamäť bez curiek
---
## Súbory projektu
### Jadrové súbory hry
- `snake.h` - Datové štruktúry, API
- `snake.c` - Logika hada (103 riadkov)
- `add_snake()` - Pridanie segmentu
- `remove_snake()` - Odstránenie chvosta
- `is_snake()` - Detekcia kolízie
- `step_state()` - Hlavná logika hry ⭐
### GUI a program
- `game.h` - Rozhranie hry (36 riadkov)
- `game.c` - GUI s ncurses (219 riadkov)
- `init_game()` - Inicializácia
- `render_game()` - Vykreslenie
- `handle_input()` - Vstup
- `run_game()` - Herný loop ⭐
- `program.c` - Hlavný program (16 riadkov)
### Dokumentácia a testovanie
- `README.md` - Dokumentácia projektu
- `PREZENTACIA.md` - Detailná prezentácia
- `test_snake.c` - Unit testy
- `validate.sh` - Validačný skript
- `program` - Skompilovaný spustiteľný súbor (22 KB)
---
## Kompilácia
```bash
gcc -o program program.c game.c snake.c -lncurses -Wall
```
**Závislosť:** `libncurses-dev` (už nainštalovaná)
---
## Ovládanie hry
| Klávesa | Akcia |
|---------|-------|
| ↑↓←→ | Pohyb hada |
| q | Ukončenie hry |
---
## Testy
Všetky testy úspešne prešli:
```bash
./test_logic
```
Výsledky:
- ✓ Adding snake parts
- ✓ Checking if coordinates are part of snake
- ✓ Counting snake parts
- ✓ Removing snake tail
- ✓ Testing game state
- ✓ Testing wall collision
---
## Validácia projektu
```bash
./validate.sh
```
Všetky kontroly prešli:
- ✓ 6/6 súborov
- ✓ FOOD_COUNT = 20
- ✓ Bez chýb kompilácie
- ✓ Spustiteľný súbor
- ✓ Všetky testy prešli
- ✓ Všetky funkcie implementované
- ✓ Všetky datové štruktúry prítomné
---
## Poznámky pre cvičiaceho
1. **Spustenie:** `./program`
2. **Koniec:** Stlačiť 'q' alebo hra skončí pri zrážke/víťazstve
3. **Zdrojový kód:** Všetky súbory .c a .h v adresári
4. **Testovanie:** `./test_logic` pre overenie logiky
Projekt je **úplný** a **testovaný**. Pripravený na prezentáciu! 🎮
---
**Vytvorené:** 8. máj 2026
**Umiestnenie:** `/home/kubo/pvjc26/a3/snake/`

219
a3/game.c Normal file
View File

@ -0,0 +1,219 @@
#include <ncurses.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#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();
}

36
a3/game.h Normal file
View File

@ -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

BIN
a3/program Executable file

Binary file not shown.

16
a3/program.c Normal file
View File

@ -0,0 +1,16 @@
#include <stdio.h>
#include <unistd.h>
#include "game.h"
int main() {
printf("Starting Snake Game...\n");
printf("Use arrow keys to move the snake.\n");
printf("Press 'q' to quit.\n");
printf("Eat all food (*) to win!\n\n");
sleep(2);
run_game();
printf("Thanks for playing!\n");
return 0;
}

100
a3/snake.c Normal file
View File

@ -0,0 +1,100 @@
#include <stdlib.h>
#include "snake.h"
struct snake* add_snake(struct snake* snake, int x, int y) {
struct snake* new_part = (struct snake*)malloc(sizeof(struct snake));
if (new_part == NULL) {
return snake;
}
new_part->x = x;
new_part->y = y;
new_part->next = snake;
return new_part;
}
struct snake* remove_snake(struct snake* snake) {
if (snake == NULL) {
return NULL;
}
if (snake->next == NULL) {
free(snake);
return NULL;
}
struct snake* current = snake;
while (current->next->next != NULL) {
current = current->next;
}
free(current->next);
current->next = NULL;
return snake;
}
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;
}
void free_snake(struct snake* sn) {
while (sn != NULL) {
struct snake* temp = sn;
sn = sn->next;
free(temp);
}
}
int step_state(struct state* state) {
if (state == NULL || state->snake == NULL) {
return END_USER;
}
int new_x = state->snake->x + state->sx;
int new_y = state->snake->y + state->sy;
if (new_x < 0 || new_x >= state->width || new_y < 0 || new_y >= state->height) {
return END_WALL;
}
// Check if new position is on snake's body
if (is_snake(state->snake, new_x, new_y)) {
return END_SNAKE;
}
// Find food at new position
int food_index = -1;
int items_left = 0;
for (int i = 0; i < FOOD_COUNT; i++) {
if (state->foodx[i] == new_x && state->foody[i] == new_y) {
food_index = i;
} else if (state->foodx[i] >= 0 && state->foody[i] >= 0) {
items_left++;
}
}
if (food_index >= 0) {
// Food found - mark as eaten and grow snake
state->foodx[food_index] = -1;
state->foody[food_index] = -1;
state->snake = add_snake(state->snake, new_x, new_y);
if (items_left == 0) {
return END_FOOD;
}
return END_CONTINUE;
} else {
// No food - normal movement (remove tail and add head)
state->snake = remove_snake(state->snake);
state->snake = add_snake(state->snake, new_x, new_y);
return END_CONTINUE;
}
}

112
a3/snake.h Normal file
View File

@ -0,0 +1,112 @@
#ifndef snake_h_INCLUDED
#define snake_h_INCLUDED
// Number of food items on the plane
#define FOOD_COUNT 20
/**
* 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 meads 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 shoud calculate new posision of the snake head
* from the current position and speed vector.
* Then it should modify snake parst 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

BIN
a3/test_logic Executable file

Binary file not shown.

BIN
a3/test_snake Executable file

Binary file not shown.

73
a3/test_snake.c Normal file
View File

@ -0,0 +1,73 @@
#include <stdio.h>
#include <stdlib.h>
#include "snake.h"
int main() {
printf("=== Testing Snake Game Library ===\n\n");
// Test 1: Add snake parts
printf("Test 1: Adding snake parts\n");
struct snake* snake = NULL;
snake = add_snake(snake, 5, 5);
snake = add_snake(snake, 5, 6);
snake = add_snake(snake, 5, 7);
printf("Added 3 snake parts\n");
// Test 2: Check if coordinates are part of snake
printf("\nTest 2: Checking if coordinates are part of snake\n");
printf("Is (5,5) part of snake? %d (expected: 1)\n", is_snake(snake, 5, 5));
printf("Is (5,6) part of snake? %d (expected: 1)\n", is_snake(snake, 5, 6));
printf("Is (10,10) part of snake? %d (expected: 0)\n", is_snake(snake, 10, 10));
// Test 3: Count snake parts
printf("\nTest 3: Counting snake parts\n");
int count = 0;
struct snake* curr = snake;
while (curr != NULL) {
count++;
curr = curr->next;
}
printf("Snake has %d parts (expected: 3)\n", count);
// Test 4: Remove snake tail
printf("\nTest 4: Removing snake tail\n");
snake = remove_snake(snake);
count = 0;
curr = snake;
while (curr != NULL) {
count++;
curr = curr->next;
}
printf("After removal, snake has %d parts (expected: 2)\n", count);
// Test 5: Test game state step
printf("\nTest 5: Testing game state\n");
struct state state;
state.snake = snake;
state.width = 20;
state.height = 20;
state.sx = 1; // Move right
state.sy = 0;
// Initialize food
for (int i = 0; i < FOOD_COUNT; i++) {
state.foodx[i] = 10 + i;
state.foody[i] = 10;
}
int result = step_state(&state);
printf("Step result: %d (0=CONTINUE, 1=WALL, 2=SNAKE, 3=FOOD, 4=USER)\n", result);
// Test 6: Test wall collision
printf("\nTest 6: Testing wall collision\n");
state.sx = 20; // Move far to the right (should hit wall)
state.sy = 0;
result = step_state(&state);
printf("After moving right 20, result: %d (expected: 1=WALL)\n", result);
// Cleanup
free_snake(state.snake);
printf("\n=== All tests completed ===\n");
return 0;
}

14
a5.2/Makefile Normal file
View File

@ -0,0 +1,14 @@
CFLAGS=-std=c99 -Wall -g
all: game
%.o: %.c
gcc $(CFLAGS) -c $< -o $@
clean:
rm -f *.o game
game: main.o game.o world.o
gcc main.o game.o world.o -lcurses -lm -o game
.PHONY: all clean

BIN
a5.2/game Executable file

Binary file not shown.

218
a5.2/game.c Normal file
View File

@ -0,0 +1,218 @@
#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "world.h"
#include "game.h"
// Helper function to get a random position within bounds
// Returns 1 if position is valid, 0 if we can't find a distinct position
static int get_random_position(int* x, int* y, int width, int height,
int cat_x, int cat_y,
int* existing_x, int* existing_y, int existing_count) {
// Leave 1 cell for border
int min_x = 1;
int max_x = width - 2;
int min_y = 1;
int max_y = height - 2;
int attempts = 0;
while (attempts < 100) {
*x = min_x + rand() % (max_x - min_x + 1);
*y = min_y + rand() % (max_y - min_y + 1);
// Check if position is distinct from cat
if (*x == cat_x && *y == cat_y) {
attempts++;
continue;
}
// Check if position is distinct from other entities
int distinct = 1;
for (int i = 0; i < existing_count; i++) {
if (*x == existing_x[i] && *y == existing_y[i]) {
distinct = 0;
break;
}
}
if (distinct) {
return 1;
}
attempts++;
}
return 0;
}
// Initialize the game state
void* init_game() {
// Allocate memory for the game state
struct game* state = calloc(1, sizeof(struct game));
if (state == NULL) {
return NULL;
}
// Use current time for random seed (more random than time(NULL) alone)
srand((unsigned int)time(NULL) + rand());
// Initial state values
state->score = 0;
strcpy(state->message, "");
// Place cat at a random position (leaving room for border)
state->cat_x = 5 + rand() % 20;
state->cat_y = 2 + rand() % 15;
// Place 5 mice at distinct random positions
for (int i = 0; i < MOUSE_COUNT; i++) {
int px, py;
get_random_position(&px, &py, 30, 20, state->cat_x, state->cat_y,
state->mouse_x, state->mouse_y, i);
state->mouse_x[i] = px;
state->mouse_y[i] = py;
state->mouse_alive[i] = 1; // All mice start alive
}
return state;
}
// Draw the game state on screen
static void draw_game(struct game* state, struct event* event) {
// Clear screen
clear_screen();
// Draw border around the playing field (using walls: '#' in cyan)
// Top and bottom borders
for (int x = 0; x < event->width; x++) {
set_color_cell('#', x, 0, COLOR_CYAN, COLOR_BLACK);
set_color_cell('#', x, event->height - 1, COLOR_CYAN, COLOR_BLACK);
}
// Left and right borders
for (int y = 1; y < event->height - 1; y++) {
set_color_cell('#', 0, y, COLOR_CYAN, COLOR_BLACK);
set_color_cell('#', event->width - 1, y, COLOR_CYAN, COLOR_BLACK);
}
// Draw cat in yellow
set_color_cell('c', state->cat_x, state->cat_y, COLOR_YELLOW, COLOR_BLACK);
// Draw alive mice in green
for (int i = 0; i < MOUSE_COUNT; i++) {
if (state->mouse_alive[i]) {
set_color_cell('m', state->mouse_x[i], state->mouse_y[i], COLOR_GREEN, COLOR_BLACK);
}
}
// Display score
char score_msg[50];
snprintf(score_msg, sizeof(score_msg), "Score: %d/5", state->score);
set_message(score_msg, 2, event->height - 2);
// Display end-game message if game is won
if (state->game_won) {
set_message(state->message, (event->width - 40) / 2, event->height / 2);
}
}
// Handle game events
int game_event(struct event* event, void* game) {
struct game* state = (struct game*)game;
// Handle ESC key to quit
if (event->type == EVENT_ESC) {
return 1; // Non-zero means end the game
}
// Handle keyboard input (arrow keys to move cat)
if (event->type == EVENT_KEY) {
if (event->key == KEY_UP) {
int new_y = state->cat_y - 1;
// Clamp to walls (1 inside border, event->height-2 is last valid position)
if (new_y > 0) {
state->cat_y = new_y;
}
} else if (event->key == KEY_DOWN) {
int new_y = state->cat_y + 1;
if (new_y < event->height - 1) {
state->cat_y = new_y;
}
} else if (event->key == KEY_LEFT) {
int new_x = state->cat_x - 1;
if (new_x > 0) {
state->cat_x = new_x;
}
} else if (event->key == KEY_RIGHT) {
int new_x = state->cat_x + 1;
if (new_x < event->width - 1) {
state->cat_x = new_x;
}
}
}
// Handle timer tick (move mice)
if (event->type == EVENT_TIMEOUT) {
for (int i = 0; i < MOUSE_COUNT; i++) {
if (state->mouse_alive[i]) {
// Random direction: 0=up, 1=down, 2=left, 3=right, 4=stay
int direction = rand() % 5;
int new_x = state->mouse_x[i];
int new_y = state->mouse_y[i];
if (direction == 0) { // Up
new_y = state->mouse_y[i] - 1;
} else if (direction == 1) { // Down
new_y = state->mouse_y[i] + 1;
} else if (direction == 2) { // Left
new_x = state->mouse_x[i] - 1;
} else if (direction == 3) { // Right
new_x = state->mouse_x[i] + 1;
}
// direction 4 means stay (no change to new_x, new_y)
// Clamp mouse to walls
if (new_x > 0 && new_x < event->width - 1) {
state->mouse_x[i] = new_x;
}
if (new_y > 0 && new_y < event->height - 1) {
state->mouse_y[i] = new_y;
}
}
}
}
// Check collision: cat catches mouse
for (int i = 0; i < MOUSE_COUNT; i++) {
if (state->mouse_alive[i] &&
state->cat_x == state->mouse_x[i] &&
state->cat_y == state->mouse_y[i]) {
state->mouse_alive[i] = 0; // Mark mouse as eaten
state->score++;
}
}
// Check if all mice are caught (game won)
if (state->score == MOUSE_COUNT && !state->game_won) {
strcpy(state->message, "Zlapil si vsetky mysky! Lusty macko! :-D");
state->game_won = 1;
state->win_tick = 0;
}
// If game is won, display message and eventually exit
if (state->game_won) {
state->win_tick++;
draw_game(state, event);
// After 30 frames (~3 seconds at 10fps), exit
if (state->win_tick > 30) {
return 1;
}
return 0;
}
// Draw the current game state
draw_game(state, event);
return 0; // Continue the game
}

26
a5.2/game.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef _GAME_H_INCLUDE_
#define _GAME_H_INCLUDE_
#include "world.h"
#define MOUSE_COUNT 5
// Game state structure
struct game {
int cat_x; // Cat X position
int cat_y; // Cat Y position
int mouse_x[MOUSE_COUNT]; // 5 mouse X positions
int mouse_y[MOUSE_COUNT]; // 5 mouse Y positions
int mouse_alive[MOUSE_COUNT]; // 1 if alive, 0 if eaten
int score; // Score (0-5)
char message[100]; // End-game message
int game_won; // Flag to track if game is won
int win_tick; // Counter to display message for a few frames
};
// Returns pointer to newly allocated game state
void* init_game();
// Handles game events and updates state
int game_event(struct event* event, void* game);
#endif

BIN
a5.2/game.o Normal file

Binary file not shown.

8
a5.2/main.c Normal file
View File

@ -0,0 +1,8 @@
#include "game.h"
#include "world.h"
#include <stdlib.h>
int main(int argc, char** argv) {
start_world(init_game, game_event, free);
return 0;
}

BIN
a5.2/main.o Normal file

Binary file not shown.

198
a5.2/world.c Normal file
View File

@ -0,0 +1,198 @@
#include "world.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int TIMEOUT;
void abort_game(const char* message){
endwin();
puts(message);
exit(1);
}
void check_bounds(const char* source,int x, int y){
char msg[200];
if (x < 0 || x >= COLS){
sprintf(msg,"%s:: width %d is out of bounds (0,%d)",source,x,COLS);
abort_game(msg);
}
if (y < 0 || y >= LINES){
sprintf(msg,"%s:: height %d is out of bounds (0,%d)",source,y,LINES);
abort_game(msg);
}
}
void clear_screen(){
// Clear screen
mvaddch(0,0,' ');
int screenchars = LINES*COLS;
for (int j = 1; j < screenchars;j++ ){
addch(' ');
}
}
void game_speed(int value){
if (value < 0){
abort_game("world_seed:: cannot be negative\n");
}
TIMEOUT =value;
}
void set_message(const char* message,int x,int y) {
int l = strlen(message);
for (int i = 0; i < l; i++){
check_bounds("set_message",x+i,y);
set_cell(message[i],x+i,y);
}
}
void assert_message(int event,const char* message){
if (event == 0){
abort_game(message);
}
}
void set_cell(int character,int x,int y) {
check_bounds("set_cell",x,y);
set_color_cell(character,x,y,COLOR_WHITE,COLOR_BLACK);
}
void set_color_cell(int character,int x,int y,short front_color,short back_color){
check_bounds("set_color_cell",x,y);
if (has_colors()){
int pair = COLOR_COUNT * front_color + back_color;
attron(COLOR_PAIR(pair));
mvaddch(y,x,character);
attroff(COLOR_PAIR(pair));
}
else{
mvaddch(y,x,character);
}
}
int start_world(void* (*init_game)(),int (*world_event)(struct event* event,void* game),void (*destroy_game)(void*)){
srand(time(NULL));
int r = 1;
// Speed global variable
TIMEOUT = 100;
if (initscr() == NULL){
// TODO Which Error?
puts("Curses Error.");
return -1;
}
noecho(); // Nevypisuj vstup na obrazovku
cbreak(); // Zabudni starý vstup
nodelay(stdscr,TRUE); // Nečakaj na stlačenie
keypad(stdscr,TRUE); // Aktivuje šípky
curs_set(FALSE); // Neviditeľný kurzor
/* Get all the mouse events */
mousemask(ALL_MOUSE_EVENTS, NULL);
MEVENT mouse_event;
if (has_colors()){ // Zistenie či terminál podporuje farby
start_color();
for (int i = 0; i < COLOR_COUNT;i++){
for (int j = 0; j < COLOR_COUNT;j++){
init_pair(i * COLOR_COUNT + j, i,j);
}
}
}
else {
puts("No colors!\n");
}
void* game = NULL;
if (init_game != NULL){
game = init_game();
assert_message(game != NULL,"init_game:: should return non null pointer");
}
timeout(TIMEOUT);
// Initial step
struct event event;
memset(&event,0,sizeof(struct event));
event.height = LINES;
event.width = COLS;
event.type = EVENT_START;
clock_t start_time = clock();
clock_t last_timeout = start_time;
clock_t next_timeout = last_timeout + TIMEOUT;
event.time_ms = start_time;
// Start event
r = world_event(&event,game);
refresh();
while (!r) {
memset(&event,0,sizeof(struct event));
event.height = LINES;
event.width = COLS;
event.key = getch();
// No key was pressed
if (event.key == ERR){
event.type = EVENT_TIMEOUT;
last_timeout = clock();
next_timeout = last_timeout + TIMEOUT;
}
// Mouse event
else if (event.key == KEY_MOUSE ){
event.type = EVENT_MOUSE;
if(getmouse(&mouse_event) == OK){
event.mouse_x = mouse_event.x;
event.mouse_y = mouse_event.y;
if(mouse_event.bstate & BUTTON1_PRESSED){
event.mouse_left = 1;
}
if(mouse_event.bstate & BUTTON2_PRESSED){
event.mouse_middle = 1;
}
if(mouse_event.bstate & BUTTON3_PRESSED){
event.mouse_right = 1;
}
}
}
else if (event.key == KEY_RESIZE) {
event.type = EVENT_RESIZE;
}
else{
event.type = EVENT_KEY;
if (event.key == 27){
int k = getch();
if (k == -1){
// Esc Was pressed
event.type = EVENT_ESC;
}
else {
// Alt was pressed
event.key = k;
event.alt_key = 1;
}
}
}
// Draw new world
event.time_ms = clock();
r = world_event(&event,game);
refresh();
event.time_ms = clock();
// set new timeout
int nt = next_timeout - event.time_ms;
//printf("%d\n",nt);
if (nt > 0){
timeout(nt);
}
else {
timeout(TIMEOUT);
next_timeout = event.time_ms + TIMEOUT;
}
}
memset(&event,0,sizeof(struct event));
event.height = LINES;
event.width = COLS;
event.type = EVENT_END;
event.time_ms = clock();
world_event(&event,game);
if (destroy_game != NULL){
destroy_game(game);
}
endwin();
return r;
};

113
a5.2/world.h Normal file
View File

@ -0,0 +1,113 @@
#ifndef _WORLD_H_
#define _WORLD_H_
#include <curses.h>
/**
* World represented as a rectangular matrix of colorful characters.
*
* Point [0,0] is displayed the upper left corner of the screen.
*
*/
enum event_type {
EVENT_START,
EVENT_TIMEOUT,
EVENT_KEY,
EVENT_MOUSE,
EVENT_RESIZE,
EVENT_ESC,
EVENT_END,
};
struct event {
/**
* Last width of the screen.
*/
int width;
/**
* Last height of the screen.
*/
int height;
/**
* Last pressed key or Curses event.
*
* Special event values:
* ERR if timeout,
* KEY_RESIZE if screen resize
* KEY_EVENT, other event,
* KEY_MOUSE, mouse clicked
*
* Key values:
*
* ' ' Space
* KEY_DOWN Arrow down
* KEY_UP Arrow up
* KEY_LEFT Arrow left
* KEY_RIGHT Arrow right
* KEY_A1 Upper left of keypad
* KEY_A3 Upper right of keypad
* KEY_B2 Center of keypad
* KEY_C1 Lower left of keypad
* KEY_C3 Lower right of keypad
*
* KEY_ENTER
* KEY_BACKSPACE
*/
int key;
int alt_key;
enum event_type type;
int mouse_x;
int mouse_y;
int mouse_left;
int mouse_right;
int mouse_middle;
long int time_ms;
};
/**
* Sets cell to a state.
* @param event
* @param x coordinate of cell
* @param y coordinate of cell
* @param new state of the cell
*/
void set_cell(int character,int x,int y);
/**
* COLOR_BLACK 0
* COLOR_RED 1
* COLOR_GREEN 2
* COLOR_YELLOW 3
* COLOR_BLUE 4
* COLOR_MAGENTA 5
* COLOR_CYAN 6
* COLOR_WHITE 7
*/
#define COLOR_COUNT 8
void set_color_cell(int character,int x,int y,short front_color,short back_color);
/**
*
* @param event
* @param number of commandline arguments
* @param init_world
* @param destroy_world
*
* void init_world(struct event* w);
* Initializes user state.
* Free user state.
* @param event
*/
int start_world(void* (*init_game)(),int (*world_event)(struct event* event,void* game),void (*destroy_game)(void* game));
void game_speed(int value);
void set_message(const char* message,int x,int y);
void clear_screen();
#endif

BIN
a5.2/world.o Normal file

Binary file not shown.

View File

@ -6,9 +6,9 @@ all: game
gcc $(CFLAGS) -c $< -o $@
clean:
rm *.o
rm game
rm -f *.o game
game: main.o game.o world.o
gcc main.o game.o world.o -lcurses -lm -o game
.PHONY: all clean

322
a5/game.c
View File

@ -1,138 +1,218 @@
#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "world.h"
#include "game.h"
// Start is called one in the beginning
void* init_game(){
// Allocate memory for the state
struct game* st = calloc(1,(sizeof(struct game)));
// Initialize cat
st->catx = 0;
st->caty = 0;
st->catx_position = 20;
st->caty_position = 10;
st->caught_count = 0;
// Helper function to get a random position within bounds
// Returns 1 if position is valid, 0 if we can't find a distinct position
static int get_random_position(int* x, int* y, int width, int height,
int cat_x, int cat_y,
int* existing_x, int* existing_y, int existing_count) {
// Leave 1 cell for border
int min_x = 1;
int max_x = width - 2;
int min_y = 1;
int max_y = height - 2;
// Initialize 5 mice at random positions (within boundaries)
for(int i = 0; i < MOUSE_COUNT; i++){
st->mousex[i] = 5 + rand() % 50;
st->mousey[i] = 5 + rand() % 15;
st->mouse_state[i] = 0; // alive
int attempts = 0;
while (attempts < 100) {
*x = min_x + rand() % (max_x - min_x + 1);
*y = min_y + rand() % (max_y - min_y + 1);
// Check if position is distinct from cat
if (*x == cat_x && *y == cat_y) {
attempts++;
continue;
}
// Store pointer to the state to the world variable
return st;
}
// Step is called in a loop once in interval.
// It should modify the state and draw it.
int game_event(struct event* event,void* game){
// Get state pointer
struct game* state = game;
// Check if position is distinct from other entities
int distinct = 1;
for (int i = 0; i < existing_count; i++) {
if (*x == existing_x[i] && *y == existing_y[i]) {
distinct = 0;
break;
}
}
if ( event->type == EVENT_ESC){
// Non zero means finish the loop and stop the game.
if (distinct) {
return 1;
}
// Check if all mice are caught
if(state->caught_count == MOUSE_COUNT){
clear_screen();
set_message("VSETKY MYSKY ZJEDENE! VITAZ!",10,12);
return 0;
attempts++;
}
// Check each mouse if it's caught by cat
for(int i = 0; i < MOUSE_COUNT; i++){
if(state->mouse_state[i] == 0){ // mouse is alive
if(state->mousex[i] == state->catx_position && state->mousey[i] == state->caty_position){
state->mouse_state[i] = 1; // caught
state->caught_count++;
}
}
}
if(event->type == EVENT_TIMEOUT) {
// Move cat
int cx = state->catx_position + state->catx;
int cy = state->caty_position + state->caty;
// Check boundaries (leave room for borders)
if (cx > 1 && cx < event->width - 2 && cy > 2 && cy < event->height - 2){
state->catx_position = cx;
state->caty_position = cy;
}
// Move each alive mouse randomly
for(int i = 0; i < MOUSE_COUNT; i++){
if(state->mouse_state[i] == 0){ // mouse is alive
int m = rand() % 4;
int mx = state->mousex[i];
int my = state->mousey[i];
if (m == 0) my -= 1;
else if (m == 1) my += 1;
else if (m == 2) mx -= 1;
else if (m == 3) mx += 1;
// Check boundaries (leave room for borders)
if (mx > 1 && mx < event->width - 2 && my > 2 && my < event->height - 2){
state->mousex[i] = mx;
state->mousey[i] = my;
}
}
}
}
else if (event->type == EVENT_KEY){
// Move cat according to keyboard
if ( event->key == KEY_UP){
state->catx = 0;
state->caty = -1;
}
else if ( event->key == KEY_DOWN){
state->catx = 0;
state->caty = 1;
}
else if ( event->key == KEY_LEFT){
state->catx = -1;
state->caty = 0;
}
else if ( event->key == KEY_RIGHT){
state->catx = +1;
state->caty = 0;
}
}
// Draw world state
clear_screen();
// Draw borders
for(int x = 0; x < event->width; x++){
set_cell('#', x, 1);
set_cell('#', x, event->height - 2);
}
for(int y = 1; y < event->height - 1; y++){
set_cell('#', 0, y);
set_cell('#', event->width - 1, y);
}
// Draw cat
set_cell('c', state->catx_position, state->caty_position);
// Draw mice
for(int i = 0; i < MOUSE_COUNT; i++){
if(state->mouse_state[i] == 0){ // only draw alive mice
set_cell('m', state->mousex[i], state->mousey[i]);
}
}
// Draw caught count
char msg[100];
sprintf(msg, "Chytene: %d/%d", state->caught_count, MOUSE_COUNT);
set_message(msg, 2, 0);
return 0;
}
// Initialize the game state
void* init_game() {
// Allocate memory for the game state
struct game* state = calloc(1, sizeof(struct game));
if (state == NULL) {
return NULL;
}
// Use current time for random seed (more random than time(NULL) alone)
srand((unsigned int)time(NULL) + rand());
// Initial state values
state->score = 0;
strcpy(state->message, "");
// Place cat at a random position (leaving room for border)
state->cat_x = 5 + rand() % 20;
state->cat_y = 2 + rand() % 15;
// Place 5 mice at distinct random positions
for (int i = 0; i < MOUSE_COUNT; i++) {
int px, py;
get_random_position(&px, &py, 30, 20, state->cat_x, state->cat_y,
state->mouse_x, state->mouse_y, i);
state->mouse_x[i] = px;
state->mouse_y[i] = py;
state->mouse_alive[i] = 1; // All mice start alive
}
return state;
}
// Draw the game state on screen
static void draw_game(struct game* state, struct event* event) {
// Clear screen
clear_screen();
// Draw border around the playing field (using walls: '#' in cyan)
// Top and bottom borders
for (int x = 0; x < event->width; x++) {
set_color_cell('#', x, 0, COLOR_CYAN, COLOR_BLACK);
set_color_cell('#', x, event->height - 1, COLOR_CYAN, COLOR_BLACK);
}
// Left and right borders
for (int y = 1; y < event->height - 1; y++) {
set_color_cell('#', 0, y, COLOR_CYAN, COLOR_BLACK);
set_color_cell('#', event->width - 1, y, COLOR_CYAN, COLOR_BLACK);
}
// Draw cat in yellow
set_color_cell('c', state->cat_x, state->cat_y, COLOR_YELLOW, COLOR_BLACK);
// Draw alive mice in green
for (int i = 0; i < MOUSE_COUNT; i++) {
if (state->mouse_alive[i]) {
set_color_cell('m', state->mouse_x[i], state->mouse_y[i], COLOR_GREEN, COLOR_BLACK);
}
}
// Display score
char score_msg[50];
snprintf(score_msg, sizeof(score_msg), "Score: %d/5", state->score);
set_message(score_msg, 2, event->height - 2);
// Display end-game message if game is won
if (state->game_won) {
set_message(state->message, (event->width - 40) / 2, event->height / 2);
}
}
// Handle game events
int game_event(struct event* event, void* game) {
struct game* state = (struct game*)game;
// Handle ESC key to quit
if (event->type == EVENT_ESC) {
return 1; // Non-zero means end the game
}
// Handle keyboard input (arrow keys to move cat)
if (event->type == EVENT_KEY) {
if (event->key == KEY_UP) {
int new_y = state->cat_y - 1;
// Clamp to walls (1 inside border, event->height-2 is last valid position)
if (new_y > 0) {
state->cat_y = new_y;
}
} else if (event->key == KEY_DOWN) {
int new_y = state->cat_y + 1;
if (new_y < event->height - 1) {
state->cat_y = new_y;
}
} else if (event->key == KEY_LEFT) {
int new_x = state->cat_x - 1;
if (new_x > 0) {
state->cat_x = new_x;
}
} else if (event->key == KEY_RIGHT) {
int new_x = state->cat_x + 1;
if (new_x < event->width - 1) {
state->cat_x = new_x;
}
}
}
// Handle timer tick (move mice)
if (event->type == EVENT_TIMEOUT) {
for (int i = 0; i < MOUSE_COUNT; i++) {
if (state->mouse_alive[i]) {
// Random direction: 0=up, 1=down, 2=left, 3=right, 4=stay
int direction = rand() % 5;
int new_x = state->mouse_x[i];
int new_y = state->mouse_y[i];
if (direction == 0) { // Up
new_y = state->mouse_y[i] - 1;
} else if (direction == 1) { // Down
new_y = state->mouse_y[i] + 1;
} else if (direction == 2) { // Left
new_x = state->mouse_x[i] - 1;
} else if (direction == 3) { // Right
new_x = state->mouse_x[i] + 1;
}
// direction 4 means stay (no change to new_x, new_y)
// Clamp mouse to walls
if (new_x > 0 && new_x < event->width - 1) {
state->mouse_x[i] = new_x;
}
if (new_y > 0 && new_y < event->height - 1) {
state->mouse_y[i] = new_y;
}
}
}
}
// Check collision: cat catches mouse
for (int i = 0; i < MOUSE_COUNT; i++) {
if (state->mouse_alive[i] &&
state->cat_x == state->mouse_x[i] &&
state->cat_y == state->mouse_y[i]) {
state->mouse_alive[i] = 0; // Mark mouse as eaten
state->score++;
}
}
// Check if all mice are caught (game won)
if (state->score == MOUSE_COUNT && !state->game_won) {
strcpy(state->message, "Zlapil si vsetky mysky! Lusty macko! :-D");
state->game_won = 1;
state->win_tick = 0;
}
// If game is won, display message and eventually exit
if (state->game_won) {
state->win_tick++;
draw_game(state, event);
// After 30 frames (~3 seconds at 10fps), exit
if (state->win_tick > 30) {
return 1;
}
return 0;
}
// Draw the current game state
draw_game(state, event);
return 0; // Continue the game
}

View File

@ -4,33 +4,23 @@
#define MOUSE_COUNT 5
// Set of variables that expresses state of the game.
//
// Game state structure
struct game {
// X speed of the cat
int catx;
// Y speed of the cat
int caty;
// X position of the cat
int catx_position;
// Y opsition of the cat
int caty_position;;
// X positions of the mice
int mousex[MOUSE_COUNT];
// Y positions of the mice
int mousey[MOUSE_COUNT];
// State of each mouse (0 = alive, 1 = caught)
int mouse_state[MOUSE_COUNT];
// Count of caught mice
int caught_count;
// Funky message
char message[100];
int cat_x; // Cat X position
int cat_y; // Cat Y position
int mouse_x[MOUSE_COUNT]; // 5 mouse X positions
int mouse_y[MOUSE_COUNT]; // 5 mouse Y positions
int mouse_alive[MOUSE_COUNT]; // 1 if alive, 0 if eaten
int score; // Score (0-5)
char message[100]; // End-game message
int game_won; // Flag to track if game is won
int win_tick; // Counter to display message for a few frames
};
// Returns pointer to newly allocated state
// Returns pointer to newly allocated game state
void* init_game();
// Changes world according to the game state (pressed key, screen size or other event)
int game_event(struct event* event,void* game);
// Handles game events and updates state
int game_event(struct event* event, void* game);
#endif

View File

@ -2,8 +2,7 @@
#include "world.h"
#include <stdlib.h>
int main(int argc, char** argv){
start_world(init_game,game_event,free);
int main(int argc, char** argv) {
start_world(init_game, game_event, free);
return 0;
}

104
du4/snake0.c Normal file
View File

@ -0,0 +1,104 @@
#include <stdlib.h>
#include "snake.h"
struct snake* add_snake(struct snake* snake, int x, int y) {
struct snake* new_part = (struct snake*)malloc(sizeof(struct snake));
if (new_part == NULL) {
return snake;
}
new_part->x = x;
new_part->y = y;
new_part->next = snake;
return new_part;
}
struct snake* remove_snake(struct snake* snake) {
if (snake == NULL) {
return NULL;
}
if (snake->next == NULL) {
free(snake);
return NULL;
}
struct snake* current = snake;
while (current->next->next != NULL) {
current = current->next;
}
free(current->next);
current->next = NULL;
return snake;
}
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;
}
void free_snake(struct snake* sn) {
while (sn != NULL) {
struct snake* temp = sn;
sn = sn->next;
free(temp);
}
}
int step_state(struct state* state) {
if (state == NULL || state->snake == NULL) {
return END_USER;
}
int new_x = state->snake->x + state->sx;
int new_y = state->snake->y + state->sy;
if (new_x < 0 || new_x >= state->width || new_y < 0 || new_y >= state->height) {
return END_WALL;
}
int food_index = -1;
int items_left = 0;
for (int i = 0; i < FOOD_COUNT; i++) {
if (state->foodx[i] == new_x && state->foody[i] == new_y) {
food_index = i;
} else if (state->foodx[i] >= 0 && state->foody[i] >= 0) {
items_left++;
}
}
if (food_index >= 0) {
state->foodx[food_index] = -1;
state->foody[food_index] = -1;
if (is_snake(state->snake, new_x, new_y)) {
state->snake = add_snake(state->snake, new_x, new_y);
return END_SNAKE;
}
state->snake = add_snake(state->snake, new_x, new_y);
if (items_left == 0) {
return END_FOOD;
}
return END_CONTINUE;
} else {
state->snake = remove_snake(state->snake);
if (is_snake(state->snake, new_x, new_y)) {
state->snake = add_snake(state->snake, new_x, new_y);
return END_SNAKE;
}
state->snake = add_snake(state->snake, new_x, new_y);
return END_CONTINUE;
}
}

112
du4/snake0.h Normal file
View File

@ -0,0 +1,112 @@
#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 meads 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 shoud calculate new posision of the snake head
* from the current position and speed vector.
* Then it should modify snake parst 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

BIN
du4/test_snake Executable file

Binary file not shown.

73
du4/test_snake.c Normal file
View File

@ -0,0 +1,73 @@
#include <stdio.h>
#include <stdlib.h>
#include "snake.h"
int main() {
printf("=== Testing Snake Game Library ===\n\n");
// Test 1: Add snake parts
printf("Test 1: Adding snake parts\n");
struct snake* snake = NULL;
snake = add_snake(snake, 5, 5);
snake = add_snake(snake, 5, 6);
snake = add_snake(snake, 5, 7);
printf("Added 3 snake parts\n");
// Test 2: Check if coordinates are part of snake
printf("\nTest 2: Checking if coordinates are part of snake\n");
printf("Is (5,5) part of snake? %d (expected: 1)\n", is_snake(snake, 5, 5));
printf("Is (5,6) part of snake? %d (expected: 1)\n", is_snake(snake, 5, 6));
printf("Is (10,10) part of snake? %d (expected: 0)\n", is_snake(snake, 10, 10));
// Test 3: Count snake parts
printf("\nTest 3: Counting snake parts\n");
int count = 0;
struct snake* curr = snake;
while (curr != NULL) {
count++;
curr = curr->next;
}
printf("Snake has %d parts (expected: 3)\n", count);
// Test 4: Remove snake tail
printf("\nTest 4: Removing snake tail\n");
snake = remove_snake(snake);
count = 0;
curr = snake;
while (curr != NULL) {
count++;
curr = curr->next;
}
printf("After removal, snake has %d parts (expected: 2)\n", count);
// Test 5: Test game state step
printf("\nTest 5: Testing game state\n");
struct state state;
state.snake = snake;
state.width = 20;
state.height = 20;
state.sx = 1; // Move right
state.sy = 0;
// Initialize food
for (int i = 0; i < FOOD_COUNT; i++) {
state.foodx[i] = 10 + i;
state.foody[i] = 10;
}
int result = step_state(&state);
printf("Step result: %d (0=CONTINUE, 1=WALL, 2=SNAKE, 3=FOOD, 4=USER)\n", result);
// Test 6: Test wall collision
printf("\nTest 6: Testing wall collision\n");
state.sx = 20; // Move far to the right (should hit wall)
state.sy = 0;
result = step_state(&state);
printf("After moving right 20, result: %d (expected: 1=WALL)\n", result);
// Cleanup
free_snake(state.snake);
printf("\n=== All tests completed ===\n");
return 0;
}

BIN
du5/program Executable file

Binary file not shown.

92
du6/a_station.h Normal file
View File

@ -0,0 +1,92 @@
#ifndef STATION_H
#define STATION_H
// Pocet trati k dispozicii
#define STATION_SIZE 10
// Maximalny pocet znakov pre ulozenie nazvu cielovej stanice
#define TARGET_SIZE 36
/**
* Jeden zaznam o kapacite do cielovej stanice
*/
struct car {
// Cielova stanica / nazov
char value[TARGET_SIZE];
// Pocet cestujuchich
int capacity;
// Smernik na dalsi zaznam
struct car* next;
};
/**
* Cela databaza zaznamov
*/
struct station {
// Dynamicke pole smernikov na zaznamy
// jeden zaznam ma typ struct car*
struct car** tracks;
// Velkost pola tracks
int track_count;
};
/**
* Vytvori prazdnu stanicu.
* Alokuje pole smernikov tracks na pociatocnu kapacitu STATION_SIZE
* nastavi velkost capacity na STATION_SIZE
* @return smernik na prazdnu stanicu
*/
struct station* create_station();
/**
* Uvolni pamat
* @param smernik na databazu
*/
void destroy_station(struct station* station);
/**
* Vyberie poziciu v poli station->tracks pre ulozenie zaznamu target
*
* Proces vyberu by mal splnat kriteria pre hash funkciu:
* - rovnaky retazec by mal vzdy mat rovnaky vysledok
* - pre rozne retazce by mali byt vysledky co najviac rozne
*
* @param smernik na databazu
* @param nazov cielovej stanice
* @return cislo v intervale 0 az N-1, kde N je station->track_count
*/
int select_track(struct station* station, const char* target);
/**
* Zvysi zaznam o pocte cestujucich do danej cielovej stanice.
*
* Najprv sa vyberie cielova trat pomocou select_track(). Ak zaznam neexistuje,
* vytvori sa novy. Ak zaznam na danej trati (spojkovom zozname) existuje, cislo sa pripocita.
* V databaze nesmu byt dva zaznamy s rovnakou cielovou stanicou.
*
* @param smernik na databazu
* @param nazov cielovej stanice
*/
void add_target_capacity(struct station* station, const char* target, int capacity);
/**
* Ziska zaznam o cielovej stanici.
* @param smernik na databazu
* @param nazov cielovej stanice
*
* @return kapacitu do cielovej stanice. Ak sa zaznam nenachedza, vrati nula.
*/
int get_target_capacity(struct station* station, const char* target);
/**
* Spocita pocet cielovych stanic
* @param smernik na databazu
*/
int count_targets(struct station* station);
/**
* Spocita kapacitu vo vsetkych zaznamoch
* @param smernik na databazu
*/
int count_capacity(struct station* station);
#endif

BIN
du6/station.o Normal file

Binary file not shown.

BIN
du6/test_station Executable file

Binary file not shown.

64
du6/test_station.c Normal file
View File

@ -0,0 +1,64 @@
#include <stdio.h>
#include <stdlib.h>
#include "a_station.h"
int main() {
printf("=== Testing Train Station Library ===\n\n");
// Test 1: Create station
printf("Test 1: Creating station\n");
struct station* st = create_station();
if (st != NULL) {
printf("Station created successfully\n");
printf("Station has %d tracks\n", st->track_count);
} else {
printf("ERROR: Could not create station\n");
return 1;
}
// Test 2: Add targets
printf("\nTest 2: Adding destinations\n");
add_target_capacity(st, "Bratislava", 100);
add_target_capacity(st, "Kosice", 150);
add_target_capacity(st, "Zilina", 80);
add_target_capacity(st, "Presov", 120);
printf("Added 4 destinations\n");
// Test 3: Count targets
printf("\nTest 3: Counting targets\n");
int target_count = count_targets(st);
printf("Number of targets: %d (expected: 4)\n", target_count);
// Test 4: Get target capacity
printf("\nTest 4: Getting target capacities\n");
printf("Bratislava capacity: %d (expected: 100)\n", get_target_capacity(st, "Bratislava"));
printf("Kosice capacity: %d (expected: 150)\n", get_target_capacity(st, "Kosice"));
printf("Zilina capacity: %d (expected: 80)\n", get_target_capacity(st, "Zilina"));
printf("Presov capacity: %d (expected: 120)\n", get_target_capacity(st, "Presov"));
printf("NonExistent capacity: %d (expected: 0)\n", get_target_capacity(st, "NonExistent"));
// Test 5: Add to existing target
printf("\nTest 5: Adding more capacity to existing target\n");
add_target_capacity(st, "Bratislava", 50);
printf("Bratislava new capacity: %d (expected: 150)\n", get_target_capacity(st, "Bratislava"));
// Test 6: Total capacity count
printf("\nTest 6: Total capacity\n");
int total_capacity = count_capacity(st);
printf("Total capacity: %d (expected: 500)\n", total_capacity);
// Test 7: Hash function consistency
printf("\nTest 7: Hash function consistency\n");
int track1 = select_track(st, "Bratislava");
int track2 = select_track(st, "Bratislava");
printf("Track for 'Bratislava': %d and %d (should be same)\n", track1, track2);
printf("Tracks match: %s\n", track1 == track2 ? "YES" : "NO");
// Test 8: Clean up
printf("\nTest 8: Destroying station\n");
destroy_station(st);
printf("Station destroyed successfully\n");
printf("\n=== All tests completed ===\n");
return 0;
}

BIN
master.zip Normal file

Binary file not shown.

54
world-master/.gitignore vendored Normal file
View File

@ -0,0 +1,54 @@
game
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf

29
world-master/LICENSE Normal file
View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2019, Daniel Hládek
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

14
world-master/Makefile Normal file
View File

@ -0,0 +1,14 @@
CFLAGS=-std=c99 -Wall -g
all: game
%.o: %.c
gcc $(CFLAGS) -c $< -o $@
clean:
rm *.o
rm game
game: main.o game.o world.o
gcc main.o game.o world.o -lcurses -lm -o game

78
world-master/README.md Normal file
View File

@ -0,0 +1,78 @@
# World Game Library
Learn asycnronous C programming by a game.
The library implements a game loop for a character-based ncurses game;
The library finds out the event types:
- start and end
- mouse events
- keyboard events
- screen resize events
It can print colors and arbitrary characters on the screen.
Running is interrupted when character is drawn out of the screen.
## Installation and Running
Make sure, that `ncurses` is installed.
Clone this repository.
Compile:
```c
make
```
Run:
```c
./game
```
## Make your own game
The game happens in a rectangular world of colorful characters.
Your goal as a programmer is to modify the world according to the pressed keys and the internal game state.
The library implements the game loop and draws the world to screen.
Your game in file `game.c` consists of two functions:
- `void* init_game()` is called in the beginning. Here you can initialize the internal state of the game.
The function should return a pointer to an instance of the initial game state `game`.
- `int game_event(struct event* event,void* game)`
is called by the game loop in periodic interval or when a key was pressed. Non-zero return value or `Ctrl+C` key interrupts the game loop.
The world variable represents state of two-dimensional grid of characters on the screen. The screen matrix looks like this:
```
origin
[0,0] width
+--------------------+
h | |
e | |
i | |
g | |
h | |
t | |
+--------------------+
```
The world has the following parameters:
- `int height`: y-dimension of the grid.
- `int width`: x-dimension of the grid.
- `int interval`: maximum time between steps in milliseconds.
### The Event Function
The `int game_event(struct event* event,void* game)`
function should:
1. Read the game state (from the `void* game`) pointer.
1. Examine the pressed key from event pointer. If the `key` variable is non-zero, a key was pressed. According to the pressed key, modify the game state `game`.
1. Draw the game state. In the beginning of the step function the screen is empty.
1. Returning non-zero value ends the game loop.

106
world-master/game.c Normal file
View File

@ -0,0 +1,106 @@
#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include "world.h"
#include "game.h"
// Start is called one in the beginning
void* init_game(){
// Allocate memory for the state
struct game* st = calloc(1,(sizeof(struct game)));
// Initialize state
st->mousex = 5;
st->mousey = 5;
st->catx = 0;
st->caty = 0;
st->catx_position = 15;
st->caty_position = 15;
// Store pointer to the state to the world variable
return st;
}
// Step is called in a loop once in interval.
// It should modify the state and draw it.
int game_event(struct event* event,void* game){
// Get state pointer
struct game* state = game;
char msg[200];
sprintf(msg,"%d",event->type);
set_message(msg,10,0);
if ( event->type == EVENT_ESC){
// Non zero means finish the loop and stop the game.
return 1;
}
// Read game variable and update the eventstate
// Is mouse caught ?
if ((state->caty_position == state->mousey) && (state->catx_position == state->mousex)){
clear_screen();
set_message("HAM",12,13);
return 0;
}
else if(event->type == EVENT_TIMEOUT) {
// Move cat
//state->catx_position += state->catx;
//state->caty_position += state->caty;
int cx = state->catx_position + state->catx;
int cy = state->caty_position + state->caty;
if (cx < 0 || cy < 0 || cx > event->width || cy > event->height){
}
else {
state->catx_position = cx;
state->caty_position = cy;
}
//state->catx_position += state->catx;
//state->caty_position += state->caty;
//state->caty_position += state->caty;
// random mouse movement
int m = rand() % 6;
if (m == 0){
state->mousey -= 1;
}
else if (m == 1){
state->mousey += 1;
}
else if (m == 2){
state->mousex -= 1;
}
else if (m == 3){
state->mousex += 1;
}
// Je myska mimo plochy
}
else if (event->type == EVENT_KEY){
// Move cat according to keyboard
if ( event->key == KEY_UP){
state->catx = 0;
state->caty = -1;
}
else if ( event->key == KEY_DOWN){
state->catx = 0;
state->caty = 1;
}
else if ( event->key == KEY_LEFT){
state->catx = -1;
state->caty = 0;
}
else if ( event->key == KEY_RIGHT){
state->catx = +1;
state->caty = 0;
}
}
// Draw world state
//
// Draw cat
clear_screen();
set_color_cell('c',state->catx_position,state->caty_position,COLOR_YELLOW,COLOR_RED);
set_color_cell('-',state->catx_position-1,state->caty_position,COLOR_YELLOW,COLOR_GREEN);
//set_cell('c',state->catx_position,state->caty_position);
// Draw mouse
set_cell('m',state->mousex,state->mousey);
set_message( state->message,1,0);
return 0;
}

30
world-master/game.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef _GAME_H_INCLUDE_
#define _GAME_H_INCLUDE_
#include "world.h"
// Set of variables that expresses state of the game.
//
struct game {
// X speed of the cat
int catx;
// Y speed of the cat
int caty;
// X position of the cat
int catx_position;
// Y opsition of the cat
int caty_position;;
// X position of the mouse
int mousex;
// Y position of the mouse
int mousey;
// Funky message
char message[100];
};
// Returns pointer to newly allocated state
void* init_game();
// Changes world according to the game state (pressed key, screen size or other event)
int game_event(struct event* event,void* game);
#endif

9
world-master/main.c Normal file
View File

@ -0,0 +1,9 @@
#include "game.h"
#include "world.h"
#include <stdlib.h>
int main(int argc, char** argv){
start_world(init_game,game_event,free);
return 0;
}

198
world-master/world.c Normal file
View File

@ -0,0 +1,198 @@
#include "world.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int TIMEOUT;
void abort_game(const char* message){
endwin();
puts(message);
exit(1);
}
void check_bounds(const char* source,int x, int y){
char msg[200];
if (x < 0 || x >= COLS){
sprintf(msg,"%s:: width %d is out of bounds (0,%d)",source,x,COLS);
abort_game(msg);
}
if (y < 0 || y >= LINES){
sprintf(msg,"%s:: height %d is out of bounds (0,%d)",source,y,LINES);
abort_game(msg);
}
}
void clear_screen(){
// Clear screen
mvaddch(0,0,' ');
int screenchars = LINES*COLS;
for (int j = 1; j < screenchars;j++ ){
addch(' ');
}
}
void game_speed(int value){
if (value < 0){
abort_game("world_seed:: cannot be negative\n");
}
TIMEOUT =value;
}
void set_message(const char* message,int x,int y) {
int l = strlen(message);
for (int i = 0; i < l; i++){
check_bounds("set_message",x+i,y);
set_cell(message[i],x+i,y);
}
}
void assert_message(int event,const char* message){
if (event == 0){
abort_game(message);
}
}
void set_cell(int character,int x,int y) {
check_bounds("set_cell",x,y);
set_color_cell(character,x,y,COLOR_WHITE,COLOR_BLACK);
}
void set_color_cell(int character,int x,int y,short front_color,short back_color){
check_bounds("set_color_cell",x,y);
if (has_colors()){
int pair = COLOR_COUNT * front_color + back_color;
attron(COLOR_PAIR(pair));
mvaddch(y,x,character);
attroff(COLOR_PAIR(pair));
}
else{
mvaddch(y,x,character);
}
}
int start_world(void* (*init_game)(),int (*world_event)(struct event* event,void* game),void (*destroy_game)(void*)){
srand(time(NULL));
int r = 1;
// Speed global variable
TIMEOUT = 100;
if (initscr() == NULL){
// TODO Which Error?
puts("Curses Error.");
return -1;
}
noecho(); // Nevypisuj vstup na obrazovku
cbreak(); // Zabudni starý vstup
nodelay(stdscr,TRUE); // Nečakaj na stlačenie
keypad(stdscr,TRUE); // Aktivuje šípky
curs_set(FALSE); // Neviditeľný kurzor
/* Get all the mouse events */
mousemask(ALL_MOUSE_EVENTS, NULL);
MEVENT mouse_event;
if (has_colors()){ // Zistenie či terminál podporuje farby
start_color();
for (int i = 0; i < COLOR_COUNT;i++){
for (int j = 0; j < COLOR_COUNT;j++){
init_pair(i * COLOR_COUNT + j, i,j);
}
}
}
else {
puts("No colors!\n");
}
void* game = NULL;
if (init_game != NULL){
game = init_game();
assert_message(game != NULL,"init_game:: should return non null pointer");
}
timeout(TIMEOUT);
// Initial step
struct event event;
memset(&event,0,sizeof(struct event));
event.height = LINES;
event.width = COLS;
event.type = EVENT_START;
clock_t start_time = clock();
clock_t last_timeout = start_time;
clock_t next_timeout = last_timeout + TIMEOUT;
event.time_ms = start_time;
// Start event
r = world_event(&event,game);
refresh();
while (!r) {
memset(&event,0,sizeof(struct event));
event.height = LINES;
event.width = COLS;
event.key = getch();
// No key was pressed
if (event.key == ERR){
event.type = EVENT_TIMEOUT;
last_timeout = clock();
next_timeout = last_timeout + TIMEOUT;
}
// Mouse event
else if (event.key == KEY_MOUSE ){
event.type = EVENT_MOUSE;
if(getmouse(&mouse_event) == OK){
event.mouse_x = mouse_event.x;
event.mouse_y = mouse_event.y;
if(mouse_event.bstate & BUTTON1_PRESSED){
event.mouse_left = 1;
}
if(mouse_event.bstate & BUTTON2_PRESSED){
event.mouse_middle = 1;
}
if(mouse_event.bstate & BUTTON3_PRESSED){
event.mouse_right = 1;
}
}
}
else if (event.key == KEY_RESIZE) {
event.type = EVENT_RESIZE;
}
else{
event.type = EVENT_KEY;
if (event.key == 27){
int k = getch();
if (k == -1){
// Esc Was pressed
event.type = EVENT_ESC;
}
else {
// Alt was pressed
event.key = k;
event.alt_key = 1;
}
}
}
// Draw new world
event.time_ms = clock();
r = world_event(&event,game);
refresh();
event.time_ms = clock();
// set new timeout
int nt = next_timeout - event.time_ms;
//printf("%d\n",nt);
if (nt > 0){
timeout(nt);
}
else {
timeout(TIMEOUT);
next_timeout = event.time_ms + TIMEOUT;
}
}
memset(&event,0,sizeof(struct event));
event.height = LINES;
event.width = COLS;
event.type = EVENT_END;
event.time_ms = clock();
world_event(&event,game);
if (destroy_game != NULL){
destroy_game(game);
}
endwin();
return r;
};

113
world-master/world.h Normal file
View File

@ -0,0 +1,113 @@
#ifndef _WORLD_H_
#define _WORLD_H_
#include <curses.h>
/**
* World represented as a rectangular matrix of colorful characters.
*
* Point [0,0] is displayed the upper left corner of the screen.
*
*/
enum event_type {
EVENT_START,
EVENT_TIMEOUT,
EVENT_KEY,
EVENT_MOUSE,
EVENT_RESIZE,
EVENT_ESC,
EVENT_END,
};
struct event {
/**
* Last width of the screen.
*/
int width;
/**
* Last height of the screen.
*/
int height;
/**
* Last pressed key or Curses event.
*
* Special event values:
* ERR if timeout,
* KEY_RESIZE if screen resize
* KEY_EVENT, other event,
* KEY_MOUSE, mouse clicked
*
* Key values:
*
* ' ' Space
* KEY_DOWN Arrow down
* KEY_UP Arrow up
* KEY_LEFT Arrow left
* KEY_RIGHT Arrow right
* KEY_A1 Upper left of keypad
* KEY_A3 Upper right of keypad
* KEY_B2 Center of keypad
* KEY_C1 Lower left of keypad
* KEY_C3 Lower right of keypad
*
* KEY_ENTER
* KEY_BACKSPACE
*/
int key;
int alt_key;
enum event_type type;
int mouse_x;
int mouse_y;
int mouse_left;
int mouse_right;
int mouse_middle;
long int time_ms;
};
/**
* Sets cell to a state.
* @param event
* @param x coordinate of cell
* @param y coordinate of cell
* @param new state of the cell
*/
void set_cell(int character,int x,int y);
/**
* COLOR_BLACK 0
* COLOR_RED 1
* COLOR_GREEN 2
* COLOR_YELLOW 3
* COLOR_BLUE 4
* COLOR_MAGENTA 5
* COLOR_CYAN 6
* COLOR_WHITE 7
*/
#define COLOR_COUNT 8
void set_color_cell(int character,int x,int y,short front_color,short back_color);
/**
*
* @param event
* @param number of commandline arguments
* @param init_world
* @param destroy_world
*
* void init_world(struct event* w);
* Initializes user state.
* Free user state.
* @param event
*/
int start_world(void* (*init_game)(),int (*world_event)(struct event* event,void* game),void (*destroy_game)(void* game));
void game_speed(int value);
void set_message(const char* message,int x,int y);
void clear_screen();
#endif