Init commit

This commit is contained in:
Administrator 2024-02-22 10:34:16 +00:00
commit 83e6c9e1f0
41 changed files with 1455 additions and 0 deletions

0
code-samples/.gitkeep Normal file
View file

View file

@ -0,0 +1,11 @@
# Frogger
## Controls
### Movement
Arrow keys (up, right, down, left)
### Exit
Esc key
## Installation
```git clone XXX && cd frogger```
## Make
```make frogger``` makes switch-case realisation of fsm which you can observe in file src/fsm.c \
```make frogger_fsmtable``` makes fsm realisation based on matrix of pointers on state-control functions which you can observe in file src/fsm_matrix.c

View file

@ -0,0 +1,54 @@
#ifndef DEFINES_H
#define DEFINES_H
#define WIN_INIT(time) {\
initscr();\
noecho();\
curs_set(0);\
keypad(stdscr, TRUE);\
timeout(time);\
}
#define GET_USER_INPUT getch()
#define PRINT_FROG(x, y) mvprintw(BOARDS_BEGIN + (y), BOARDS_BEGIN + (x), "@")
#define MVPRINTW(y, x, ...) mvprintw(BOARDS_BEGIN + (y), BOARDS_BEGIN + (x), __VA_ARGS__)
#define MVADDCH(y, x, c) mvaddch(BOARDS_BEGIN + (y), BOARDS_BEGIN + (x), c)
#define CLEAR_BACKPOS(y, x) mvaddch(BOARDS_BEGIN + (y), BOARDS_BEGIN + (x), ' ')
#define YOU_WON "tests/game_progress/you_won.txt"
#define YOU_LOSE "tests/game_progress/you_lose.txt"
#define LEVEL_DIR "tests/levels/level_"
#define INTRO_MESSAGE "Press ENTER to start!"
#define INTRO_MESSAGE_LEN 21
#define LEVEL_CNT 5
#define LEVELNAME_MAX 25
#define MAX_WIN_COUNT 10
#define ROWS_MAP 21
#define COLS_MAP 90
#define BOARDS_BEGIN 2
#define FROGSTART_X (BOARD_M / 2)
#define FROGSTART_Y (BOARD_N)
#define INITIAL_TIMEOUT 150
#define BOARD_N (ROWS_MAP + MAP_PADDING * 2)
#define BOARD_M 30
#define HUD_WIDTH 12
#define MAP_PADDING 3
#define BANNER_N 10
#define BANNER_M 100
#define SUCCESS 0
#define ERROR 1
#define NO_INPUT -1
#define ESCAPE 27
#define ENTER_KEY 10
#endif

View file

@ -0,0 +1,20 @@
#ifndef FROGGER_BACKEND_H
#define FROGGER_BACKEND_H
#include <ncurses.h>
#include "defines.h"
#include "objects.h"
#include "string.h"
int lvlproc(board_t *map, game_stats_t *stats);
void add_proggress(board_t *map);
void stats_init(game_stats_t *stats);
void frogpos_init(player_pos *frog);
void fill_finish(char *finish_line);
void shift_map(board_t *map);
bool check_collide(player_pos *frog, board_t *map);
bool check_finish_state(player_pos *frog, board_t *map);
bool check_level_compl(board_t *map);
#endif

View file

@ -0,0 +1,17 @@
#ifndef FROGGER_FRONTEND_H
#define FROGGER_FRONTEND_H
#include <string.h>
#include "defines.h"
#include "objects.h"
void print_overlay(void);
void print_rectangle(int top_y, int bottom_y, int left_x, int right_x);
void print_stats(game_stats_t *stats);
void print_board(board_t *game, player_pos *frog);
void print_cars(board_t *game);
void print_finished(board_t *game);
void print_banner(game_stats_t *stats);
int read_banner(game_stats_t *stats, banner_t *banner);
#endif

View file

@ -0,0 +1,11 @@
#ifndef FROGGER_H
#define FROGGER_H
#include <locale.h>
#include "fsm.h"
#include "frog_backend.h"
#include "frog_frontend.h"
void game_loop();
#endif

View file

@ -0,0 +1,35 @@
#ifndef FSM_H
#define FSM_H
#include "defines.h"
#include "objects.h"
#include "frog_backend.h"
#include "frog_frontend.h"
typedef enum
{
START = 0,
SPAWN,
MOVING,
SHIFTING,
REACH,
COLLIDE,
GAMEOVER,
EXIT_STATE
} frog_state;
typedef enum
{
MOVE_UP = 0,
MOVE_DOWN,
MOVE_RIGHT,
MOVE_LEFT,
ESCAPE_BTN,
ENTER_BTN,
NOSIG
} signals;
signals get_signal(int user_input);
void sigact(signals sig, frog_state *state, game_stats_t *stats, board_t *map, player_pos *frog_pos);
#endif

View file

@ -0,0 +1,33 @@
#ifndef OBJECTS_H
#define OBJECTS_H
#include <ncurses.h>
#include "defines.h"
typedef struct
{
int x;
int y;
} player_pos;
typedef struct
{
char finish[BOARD_M + 2];
char ways[ROWS_MAP + 2][COLS_MAP + 2];
} board_t;
typedef struct
{
int score;
int level;
int speed;
int lives;
int won;
} game_stats_t;
typedef struct
{
char matrix[BANNER_N + 1][BANNER_M + 1];
} banner_t;
#endif

View file

@ -0,0 +1,16 @@
CC := gcc
CFLAGS := -I inc -std=c99 -Wall -Werror -Wpedantic
OBJS_FSMSWITCHCASE := out/frogger.o out/fsm.o out/backend.o out/frontend.o
OBJS_FSMTABLE := out/frogger.o out/fsm_matrix.o out/backend.o out/frontend.o
frogger: $(OBJS_FSMSWITCHCASE)
$(CC) $(CFLAGS) $^ -o $@ -lncurses
frogger_fsmtable: $(OBJS_FSMTABLE)
$(CC) $(CFLAGS) $^ -o $@ -lncurses
out/%.o: src/%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
$(RM) out/*.o frogger frogger_fsmtable

View file

View file

@ -0,0 +1,102 @@
#include "frog_backend.h"
int lvlproc(board_t *map, game_stats_t *stats)
{
timeout(INITIAL_TIMEOUT - stats->speed * 15);
char levelname[LEVELNAME_MAX + 1] = { 0 };
sprintf(levelname, LEVEL_DIR"%d.txt", stats->level);
FILE *level = fopen(levelname, "r");
int rc = SUCCESS;
if (level)
{
for (int i = 0; i < ROWS_MAP && !rc; i++)
{
if (fgets(map->ways[i], COLS_MAP + 2, level) == NULL)
rc = ERROR;
else
map->ways[i][strcspn(map->ways[i], "\n")] = '\0';
}
}
fclose(level);
return rc;
}
void add_proggress(board_t *map)
{
int position = 0;
for (; map->finish[position] == '0'; position++) ;
for (int progress_cnt = BOARD_M / 5; progress_cnt > 0; progress_cnt--)
map->finish[position++] = '0';
}
bool check_finish_state(player_pos *frog, board_t *map)
{
bool rc = FALSE;
if (frog->y == 1)
rc = TRUE;
return rc;
}
bool check_level_compl(board_t *map)
{
bool rc = TRUE;
for (int i = 0; i < BOARD_M && rc; i++)
if (map->finish[i] != '0')
rc = FALSE;
return rc;
}
bool check_collide(player_pos *frog, board_t *map)
{
bool rc = FALSE;
if (frog->y > MAP_PADDING && frog->y < ROWS_MAP + MAP_PADDING + 1 && \
map->ways[frog->y - MAP_PADDING - 1][frog->x - 1] == ']')
rc = TRUE;
return rc;
}
void frogpos_init(player_pos *frog)
{
frog->x = FROGSTART_X;
frog->y = FROGSTART_Y;
}
void fill_finish(char *finish_line)
{
for (int i = 0; i < BOARD_M; i++)
finish_line[i] = ' ';
finish_line[BOARD_M] = '\0';
}
void stats_init(game_stats_t *stats)
{
stats->level = 1;
stats->score = 0;
stats->speed = 1;
stats->lives = 9;
stats->won = FALSE;
}
void shift_map(board_t *map)
{
for (int i = 1; i < ROWS_MAP; i += 2)
{
memmove(&map->ways[i][1], &map->ways[i][0], COLS_MAP * sizeof(char));
map->ways[i][0] = map->ways[i][COLS_MAP];
}
}

View file

@ -0,0 +1,36 @@
#include "frogger.h"
int main(void)
{
WIN_INIT(50);
setlocale(LC_ALL, "");
print_overlay();
game_loop();
endwin();
return SUCCESS;
}
void game_loop()
{
board_t map;
game_stats_t stats;
player_pos frog;
bool break_flag = TRUE;
int signal = 0;
frog_state state = START;
stats_init(&stats);
while (break_flag)
{
if (state == GAMEOVER || state == EXIT_STATE)
break_flag = FALSE;
sigact(get_signal(signal), &state, &stats, &map, &frog);
if (state == MOVING || state == START)
signal = GET_USER_INPUT;
}
}

View file

@ -0,0 +1,137 @@
#include "frog_frontend.h"
void print_overlay(void)
{
print_rectangle(0, BOARD_N + 1, 0, BOARD_M + 1);
print_rectangle(0, BOARD_N + 1, BOARD_M + 2, BOARD_M + HUD_WIDTH + 3);
print_rectangle(1, 3, BOARD_M + 3, BOARD_M + HUD_WIDTH + 2);
print_rectangle(4, 6, BOARD_M + 3, BOARD_M + HUD_WIDTH + 2);
print_rectangle(7, 9, BOARD_M + 3, BOARD_M + HUD_WIDTH + 2);
print_rectangle(10, 12, BOARD_M + 3, BOARD_M + HUD_WIDTH + 2);
MVPRINTW(2, BOARD_M + 5, "LEVEL");
MVPRINTW(5, BOARD_M + 5, "SCORE");
MVPRINTW(8, BOARD_M + 5, "SPEED");
MVPRINTW(11, BOARD_M + 5, "LIVES");
MVPRINTW(BOARD_N / 2, (BOARD_M - INTRO_MESSAGE_LEN) / 2 + 1, INTRO_MESSAGE);
}
void print_rectangle(int top_y, int bottom_y, int left_x, int right_x)
{
MVADDCH(top_y, left_x, ACS_ULCORNER);
int i = left_x + 1;
for (;i < right_x; i++)
MVADDCH(top_y, i, ACS_HLINE);
MVADDCH(top_y, i, ACS_URCORNER);
for (int i = top_y + 1; i < bottom_y; i++)
{
MVADDCH(i, left_x, ACS_VLINE);
MVADDCH(i, right_x, ACS_VLINE);
}
MVADDCH(bottom_y, left_x, ACS_LLCORNER);
i = left_x + 1;
for (;i < right_x; i++)
MVADDCH(bottom_y, i, ACS_HLINE);
MVADDCH(bottom_y, i, ACS_LRCORNER);
}
void print_stats(game_stats_t *stats)
{
MVPRINTW(2, BOARD_M + 12, "%d", stats->level);
MVPRINTW(5, BOARD_M + 12, "%d", stats->score);
MVPRINTW(8, BOARD_M + 12, "%d", stats->speed);
MVPRINTW(11, BOARD_M + 12, "%d", stats->lives);
}
void print_board(board_t *game, player_pos *frog)
{
print_cars(game);
PRINT_FROG(frog->x, frog->y);
}
void print_cars(board_t *game)
{
for(int i = MAP_PADDING + 1; i < BOARD_N - MAP_PADDING + 1; i++)
{
if (i % 2 == (MAP_PADDING + 1) % 2)
{
for (int j = 1; j < BOARD_M + 1; j++)
MVADDCH(i, j, ACS_BLOCK);
}
else
{
for (int j = 1; j < BOARD_M + 1; j++)
{
if (game->ways[i - MAP_PADDING - 1][j - 1] == '0')
MVADDCH(i, j, ' ');
else
MVADDCH(i, j, ']');
}
}
}
}
void print_finished(board_t *game)
{
for (int i = 0; i < BOARD_M; i++)
{
if (game->finish[i] == '0')
MVADDCH(1, i + 1, ACS_BLOCK);
else
MVADDCH(1, i + 1, ' ');
}
}
void print_banner(game_stats_t *stats)
{
banner_t banner;
memset(banner.matrix, 0, (BANNER_N + 1) * (BANNER_M + 1));
clear();
if (!(read_banner(stats, &banner)))
{
for (int i = 0; i < BANNER_N; i++)
for (int j = 0; j < BANNER_M; j++)
if (banner.matrix[i][j] == '#')
MVADDCH(i, j, ACS_BLOCK);
else
MVADDCH(i, j, ' ');
refresh();
napms(2000);
}
}
int read_banner(game_stats_t *stats, banner_t *banner)
{
int rc = SUCCESS;
FILE *file = NULL;
if (stats->lives)
file = fopen(YOU_WON, "r");
else
file = fopen(YOU_LOSE, "r");
if (file)
{
for (int i = 0; i < BANNER_N - 1 && !rc; i++)
{
if (fgets(banner->matrix[i], BANNER_M + 2, file) == NULL)
rc = ERROR;
else
banner->matrix[i][strcspn(banner->matrix[i], "\n")] = '\0';
}
fclose(file);
}
else
rc = ERROR;
return rc;
}

View file

@ -0,0 +1,186 @@
#include "fsm.h"
// This is a finite state machine realisation based on switch-case statement.
/*
Function sigact() describes state switching logic.
States are checked in order specified in function sigact().
It enters a state-case which it corresponds to, where begins another switch-case statement.
Inner switch-case statement is looking for a signal given by get_signal().
After finding it makes some action and switches state to the next one.
Pros:
1) Less memory usage.
Cons:
1) A lot of codelines.
*/
signals get_signal(int user_input)
{
signals rc = NOSIG;
if (user_input == KEY_UP)
rc = MOVE_UP;
else if (user_input == KEY_DOWN)
rc = MOVE_DOWN;
else if (user_input == KEY_LEFT)
rc = MOVE_LEFT;
else if (user_input == KEY_RIGHT)
rc = MOVE_RIGHT;
else if (user_input == ESCAPE)
rc = ESCAPE_BTN;
else if (user_input == ENTER_KEY)
rc = ENTER_BTN;
return rc;
}
void moveup(player_pos *frog_pos)
{
if (frog_pos->y != 1)
{
CLEAR_BACKPOS(frog_pos->y, frog_pos->x);
frog_pos->y -= 2;
}
}
void movedown(player_pos *frog_pos)
{
if (frog_pos->y != BOARD_N)
{
CLEAR_BACKPOS(frog_pos->y, frog_pos->x);
frog_pos->y += 2;
}
}
void moveright(player_pos *frog_pos)
{
if (frog_pos->x != BOARD_M)
{
CLEAR_BACKPOS(frog_pos->y, frog_pos->x);
frog_pos->x++;
}
}
void moveleft(player_pos *frog_pos)
{
if (frog_pos->x != 1)
{
CLEAR_BACKPOS(frog_pos->y, frog_pos->x);
frog_pos->x--;
}
}
void sigact(signals sig, frog_state *state, game_stats_t *stats, board_t *map, player_pos *frog_pos)
{
switch (*state)
{
case START:
switch (sig)
{
case ENTER_BTN:
*state = SPAWN;
break;
case ESCAPE_BTN:
*state = EXIT_STATE;
break;
default:
*state = START;
break;
}
break;
case SPAWN:
if (stats->level > LEVEL_CNT)
*state = GAMEOVER;
else
if (!lvlproc(map, stats))
{
fill_finish(map->finish);
print_finished(map);
frogpos_init(frog_pos);
*state = MOVING;
}
else
*state = EXIT_STATE;
break;
case MOVING:
switch (sig)
{
case MOVE_UP:
moveup(frog_pos);
break;
case MOVE_DOWN:
movedown(frog_pos);
break;
case MOVE_RIGHT:
moveright(frog_pos);
break;
case MOVE_LEFT:
moveleft(frog_pos);
break;
case ESCAPE_BTN:
*state = EXIT_STATE;
break;
default:
break;
}
if (*state != EXIT_STATE)
{
if (check_collide(frog_pos, map))
*state = COLLIDE;
else if (check_finish_state(frog_pos, map))
*state = REACH;
else
*state = SHIFTING;
}
break;
case SHIFTING:
shift_map(map);
if (check_collide(frog_pos, map))
*state = COLLIDE;
else
{
*state = MOVING;
print_board(map, frog_pos);
print_stats(stats);
}
break;
case REACH:
stats->score += 1;
add_proggress(map);
if (check_level_compl(map))
{
stats->level++;
stats->speed++;
*state = SPAWN;
}
else
{
frogpos_init(frog_pos);
print_finished(map);
*state = MOVING;
}
break;
case COLLIDE:
if (stats->lives)
{
stats->lives--;
frogpos_init(frog_pos);
*state = MOVING;
}
else
*state = GAMEOVER;
break;
case GAMEOVER:
print_banner(stats);
break;
default:
break;
}
}

View file

@ -0,0 +1,209 @@
#include "fsm.h"
// This is a finite state machine realisation based on matrix of "actions".
/*
Function sigact() takes an action function from fsm_table.
Game state defines an index of line in matrix (where to get).
User signal defines an index of column in matrix (what to get).
Pros:
1) By instantly taking needed action, speed of processing is higher than switch-case realisation.
2) Code is easy to read.
3) Flexible (easy to add new state)
Cons:
1) More memory usage.
*/
typedef struct game_params
{
game_stats_t *stats;
frog_state *state;
board_t *map;
player_pos *frog_pos;
bool *break_flag;
} params_t;
typedef void (*action)(params_t *prms);
void spawn(params_t *prms);
void moveup(params_t *prms);
void movedown(params_t *prms);
void moveright(params_t *prms);
void moveleft(params_t *prms);
void shifting(params_t *prms);
void reach(params_t *prms);
void collide(params_t *prms);
void gameover(params_t *prms);
void exitstate(params_t *prms);
void check(params_t *prms);
action fsm_table[8][7] = {
{NULL, NULL, NULL, NULL, exitstate, spawn, NULL},
{spawn, spawn, spawn, spawn, spawn, spawn, spawn},
{moveup, movedown, moveright, moveleft, exitstate, check, check},
{shifting, shifting, shifting, shifting, shifting, shifting, shifting},
{reach, reach, reach, reach, reach, reach, reach},
{collide, collide, collide, collide, collide, collide, collide},
{gameover, gameover, gameover, gameover, gameover, gameover, gameover},
{exitstate, exitstate, exitstate, exitstate, exitstate, exitstate, exitstate}
};
void sigact(signals sig, frog_state *state, game_stats_t *stats, board_t *map, player_pos *frog_pos)
{
params_t prms;
prms.stats = stats;
prms.state = state;
prms.map = map;
prms.frog_pos = frog_pos;
action act = fsm_table[*state][sig];
if (act)
act(&prms);
}
signals get_signal(int user_input)
{
signals rc = NOSIG;
if (user_input == KEY_UP)
rc = MOVE_UP;
else if (user_input == KEY_DOWN)
rc = MOVE_DOWN;
else if (user_input == KEY_LEFT)
rc = MOVE_LEFT;
else if (user_input == KEY_RIGHT)
rc = MOVE_RIGHT;
else if (user_input == ESCAPE)
rc = ESCAPE_BTN;
else if (user_input == ENTER_KEY)
rc = ENTER_BTN;
return rc;
}
void shifting(params_t *prms)
{
shift_map(prms->map);
if (check_collide(prms->frog_pos, prms->map))
*prms->state = COLLIDE;
else
{
*prms->state = MOVING;
print_board(prms->map, prms->frog_pos);
print_stats(prms->stats);
}
}
void check(params_t *prms)
{
if (check_collide(prms->frog_pos, prms->map))
*prms->state = COLLIDE;
else if (check_finish_state(prms->frog_pos, prms->map))
*prms->state = REACH;
else
*prms->state = SHIFTING;
}
void spawn(params_t *prms)
{
if (prms->stats->level > LEVEL_CNT)
*prms->state = GAMEOVER;
else
{
if (!lvlproc(prms->map, prms->stats))
{
fill_finish(prms->map->finish);
print_finished(prms->map);
frogpos_init(prms->frog_pos);
*prms->state = MOVING;
}
else
*prms->state = EXIT_STATE;
}
}
void moveup(params_t *prms)
{
if (prms->frog_pos->y != 1)
{
CLEAR_BACKPOS(prms->frog_pos->y, prms->frog_pos->x);
prms->frog_pos->y -= 2;
}
check(prms);
}
void movedown(params_t *prms)
{
if (prms->frog_pos->y != BOARD_N)
{
CLEAR_BACKPOS(prms->frog_pos->y, prms->frog_pos->x);
prms->frog_pos->y += 2;
}
check(prms);
}
void moveright(params_t *prms)
{
if (prms->frog_pos->x != BOARD_M)
{
CLEAR_BACKPOS(prms->frog_pos->y, prms->frog_pos->x);
prms->frog_pos->x++;
}
check(prms);
}
void moveleft(params_t *prms)
{
if (prms->frog_pos->x != 1)
{
CLEAR_BACKPOS(prms->frog_pos->y, prms->frog_pos->x);
prms->frog_pos->x--;
}
check(prms);
}
void reach(params_t *prms)
{
prms->stats->score += 1;
add_proggress(prms->map);
if (check_level_compl(prms->map))
{
prms->stats->level++;
prms->stats->speed++;
*prms->state = SPAWN;
}
else
{
frogpos_init(prms->frog_pos);
print_finished(prms->map);
*prms->state = MOVING;
}
}
void collide(params_t *prms)
{
if (prms->stats->lives)
{
prms->stats->lives--;
frogpos_init(prms->frog_pos);
*prms->state = MOVING;
}
else
*prms->state = GAMEOVER;
}
void gameover(params_t *prms)
{
print_banner(prms->stats);
}
void exitstate(params_t *prms)
{
*prms->state = EXIT_STATE;
}