pvjc26/a5.2/game.c
2026-05-08 14:16:31 +02:00

219 lines
6.8 KiB
C

#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
}