Files
algo-labyrinthes-giiiiive/Aloyse et Anna/game.c
Anna Mielcarek a4703085b6 Aloyse et Anna
j'ai push pas mal de fichier envoyer un message si vous en voulez un aute
2024-12-12 16:22:28 +01:00

542 lines
19 KiB
C

#include "game.h"
#include "game_history.h"
#include "maze.h"
/****************************/
/*+ Création et libération +*/
/****************************/
// Génération d'un nouveau jeu
game* create_newgame(int hsize, int vsize, mask* themask, generator gen_maze, objgenerator gen_obj, int nb_min, int braid) {
// Allocation mémoire pour le jeu
game* g = malloc(sizeof(game));
if (g == NULL) {
ERROR("Erreur d'allocation mémoire pour le jeu.");
}
// Création du proto-labyrinthe
maze* lab = NULL;
// Avec masque
if (themask != NULL) {
if (themask->hsize != hsize || themask->vsize != vsize) { // On verifie la taille du masque
resize_mask(themask, hsize, vsize);
}
lab = create_proto_maze(themask);
}
// Sans masque
else {
lab = create_proto_maze_nomask(hsize, vsize);
}
// On verifie la création du labyrinthe
if (lab == NULL) {
ERROR("Erreur de création du proto-labyrinthe");
}
// Modification du labyrinthe en fonction du générateur
if (gen_maze < 0 || gen_maze >= GEN_SIZE) {
free_game(g);
ERROR("Générateur de labyrinthe invalide");
} else {
gen_funs[gen_maze](lab);
}
// Modification du labyrinthe en fonction du générateur d'objets
if (gen_obj < 0 || gen_obj >= OBJ_SIZE) {
free_game(g);
ERROR("Générateur d'objets invalide");
} else {
obj_funs[gen_obj](lab);
}
// Modification du labyrinthe en fonction du nombre de minotaures
if (nb_min < 0) {
free_game(g);
ERROR("Nombre de minotaures invalide");
} else {
gen_minotaurs_maze(lab, nb_min);
}
// Modification du labyrinthe en fonction du tressage
if (braid < 0 || braid > 100) {
free_game(g);
ERROR("Taux de tressage invalide");
} else {
braid_maze(lab, braid);
}
// Initialisation du jeu
g->m = lab; // On affecte le labyrinthe
g->score = 0;
g->nbombs = 0;
g->npolys = 0;
g->player_alive = true;
g->player_dir = rand() % 4; // Le joueur est tourné dans une direction aléatoire
g->minotaurs_alive = malloc(sizeof(bool) * g->m->nb_minotaurs);
g->minotaurs_dirs = malloc(sizeof(cardinal) * g->m->nb_minotaurs);
for (int i = 0; i < g->m->nb_minotaurs; i++) {
g->minotaurs_alive[i] = true; // Les minotaures sont tous vivants
g->minotaurs_dirs[i] = rand() % 4; // Les minotaures sont tournés dans une direction aléatoire
}
g->nb_deadends = count_dead_ends(g->m);
g->exits = get_exits_maze(g->m);
g->turns = 0;
g->log = create_history();
if (g->log == NULL) {
free_game(g);
ERROR("Erreur de création de l'historique");
}
if (g == NULL) {
ERROR("Le labyrinthe n'a pas été créé");
}
return g;
}
void free_game(game* g) {
if (g == NULL) {
ERROR("Pointeur null");
} else {
if (g->m != NULL) {
free_maze(g->m);
}
if (g->minotaurs_alive != NULL) {
free(g->minotaurs_alive);
}
if (g->minotaurs_dirs != NULL) {
free(g->minotaurs_dirs);
}
if (g->log != NULL) {
free_history(g->log);
}
free(g);
}
return;
}
/***********************************************/
/*+ Implémentation des attaques de minotaures +*/
/***********************************************/
int game_try_kill_player(game* g, cardinal* dir) {
if (g == NULL || g->m == NULL) {
ERROR("Pointeur de jeu null");
}
// On teste les cases adjacentes au joueur pour les 4 directions
for (int i = 0; i < 4; i++) {
int adj = get_adj_maze(g->m, g->m->player, i);
if (adj != -1 && !has_wall_maze(g->m, g->m->player, i) && has_minotaur_maze(g->m, adj) != -1 && g->minotaurs_alive[has_minotaur_maze(g->m, adj)]) {
*dir = (i + 2) % 4;
g->player_alive = false;
return has_minotaur_maze(g->m, adj);
}
}
// Si on n'a pas trouvé de minotaure adjacent
return -1;
}
/***************************/
/*+ Traitement des objets +*/
/***************************/
void game_consume_object(game* g, int nb_copie, object obj) {
if (g == NULL || g->m == NULL) {
ERROR("Pointeur de jeu null");
}
// On teste le type de l'objet
switch (obj) {
case SMALLT:
g->score += nb_copie * VALSMALL;
break;
case MEDT:
g->score += nb_copie * VALMED;
break;
case LARGET:
g->score += nb_copie * VALLARGE;
break;
case BOMB:
g->nbombs += nb_copie;
break;
case POLY:
g->npolys += nb_copie;
break;
default:
break;
}
if (obj != EXIT && obj != NONE) {
for (int i = 0; i < nb_copie; i++) {
g->m->objects[g->m->player] = NONE;
}
}
return;
}
object game_treat_object(game* g) {
if (g == NULL || g->m == NULL) {
ERROR("Pointeur de jeu null");
}
// On récupère l'objet de la cellule du joueur
object obj = get_object_maze(g->m, g->m->player);
// On teste le type de l'objet
switch (obj) {
case SMALLT:
game_consume_object(g, 1, SMALLT);
return SMALLT;
case MEDT:
game_consume_object(g, 1, MEDT);
return MEDT;
case LARGET:
game_consume_object(g, 1, LARGET);
return LARGET;
case BOMB:
game_consume_object(g, 1, BOMB);
return BOMB;
case POLY:
game_consume_object(g, 1, POLY);
return POLY;
case EXIT:
return EXIT;
default:
break;
}
return NONE;
}
/**********************************************************/
/*+ Implémentation d'une demande de mouvement du joueur +*/
/**********************************************************/
bool implement_game_move(game* g, move mov, strategy strag) {
if (g == NULL || g->m == NULL) {
ERROR("Pointeur de jeu null");
}
// On teste si le joueur est vivant
if (!g->player_alive) {
return false;
}
// On teste si le mouvement est valide
if (!valid_move_maze(g->m, g->m->player, mov)) {
g->player_dir = (cardinal)mov;
return false;
}
// SINON on fait le mouvement
// On déplace le joueur et on le tourne dans la direction dans laquelle il avance
free_occupied_maze(g->m, g->m->player);
g->m->player = get_adj_maze(g->m, g->m->player, (cardinal)mov);
g->player_dir = (cardinal)mov;
make_occupied_maze(g->m, g->m->player);
// On gère les minotaures
// On creer le tableau des mouvements des minotaures
// pas la peine de l'initaliser à M_WAIT car on va le remplir
move* min_move = malloc(sizeof(move) * g->m->nb_minotaurs);
// On applique la stratégie des minotaures
str_funs[strag](g->m, mov, min_move);
// On teste si les minautores peuvent se déplacer
for (int i = 0; i < g->m->nb_minotaurs; i++) {
if (g->minotaurs_alive[i]) {
// Si le minotaure est vivant et qu'on peut le déplacer dans la direction demandée
if (valid_move_maze(g->m, g->m->minotaurs[i], min_move[i])) {
free_occupied_maze(g->m, g->m->minotaurs[i]);
g->m->minotaurs[i] = get_adj_maze(g->m, g->m->minotaurs[i], (cardinal)min_move[i]);
make_occupied_maze(g->m, g->m->minotaurs[i]);
g->minotaurs_dirs[i] = (cardinal)min_move[i];
}
// Sinon on le tourne dans la direction demandée et on mets son mouvements à M_WAIT
else {
g->minotaurs_dirs[i] = (cardinal)min_move[i];
min_move[i] = M_WAIT;
}
}
}
// On teste les objets
object obj = game_treat_object(g);
// On teste si le joueur est attaqué
cardinal* dir_player = malloc(sizeof(cardinal));
*dir_player = g->player_dir;
int min_killer = game_try_kill_player(g, dir_player);
if (min_killer != -1) {
g->player_alive = false;
}
// On gère l'historique
t_move* history_move = malloc(sizeof(t_move));
history_move->obj = obj;
history_move->playermove = mov;
history_move->minomoves = min_move;
history_move->killer = min_killer;
// On trouve la direction du minotaure qui a tué le joueur
if (min_killer != -1) {
for (cardinal i = 0; i < 4; i++) {
if (get_adj_maze(g->m, g->m->minotaurs[min_killer], i) == g->m->player) {
history_move->dirkill = i;
}
}
} else {
history_move->dirkill = NORTH;
}
turn new_turn = {T_MOVE, .tmove = history_move};
add_entry_history(new_turn, g->log);
g->turns++;
return true;
}
/*******************************/
/*+ Implémentation des bombes +*/
/*******************************/
bool game_bomb_wall(game* g) {
if (g == NULL || g->m == NULL) {
ERROR("Pointeur de jeu null");
}
// On teste si le joueur a une bombe
if (g->nbombs > 0 && g->player_alive) {
// On teste si on peut détruire le mur
if (get_adj_maze(g->m, g->m->player, g->player_dir) != -1 && valid_maze(g->m, get_adj_maze(g->m, g->m->player, g->player_dir)) &&
has_wall_maze(g->m, g->m->player, g->player_dir)) {
del_wall_maze(g->m, g->m->player, g->player_dir);
// On teste si le joueur est attaqué
cardinal* dir_player = malloc(sizeof(cardinal));
*dir_player = g->player_dir;
int min_killer = game_try_kill_player(g, dir_player);
if (min_killer != -1) {
g->player_alive = false;
}
g->nbombs--;
// On gère l'historique
t_bomb* history_move = malloc(sizeof(t_bomb));
history_move->bombdir = g->player_dir;
history_move->destroyed = true;
history_move->killer = game_try_kill_player(g, dir_player);
// On trouve la direction du minotaure qui a tué le joueur
if (min_killer != -1) {
for (cardinal i = 0; i < 4; i++) {
if (get_adj_maze(g->m, g->m->minotaurs[min_killer], i) == g->m->player) {
history_move->dirkill = i;
}
}
} else {
// Sinon on met une direction par défaut
history_move->dirkill = NORTH;
}
turn new_turn = {T_BOMB, .tbomb = history_move};
// On ajoute le tour à l'historique
add_entry_history(new_turn, g->log);
g->turns++;
return true;
}
// Sinon on ne peut pas détruire de mur mais on perd quand même une bombe
else {
g->nbombs--;
// On gère l'historique
t_bomb* history_move = malloc(sizeof(t_bomb));
history_move->bombdir = g->player_dir;
history_move->destroyed = false;
history_move->killer = -1;
history_move->dirkill = NORTH;
// On trouve la direction du minotaure qui a tué le joueur
turn new_turn = {T_BOMB, .tbomb = history_move};
add_entry_history(new_turn, g->log);
g->turns++;
return true;
}
}
return false;
}
/*************************************/
/*+ Implémentation des polys d'algo +*/
/*************************************/
bool game_kill_minotaurs(game* g, int portee) {
if (g == NULL || g->m == NULL) {
ERROR("Pointeur de jeu null");
}
if (portee < 0) {
ERROR("Portée invalide");
}
// On teste si le joueur a un poly d'algo
if (g->npolys > 0 && g->player_alive) {
int nb_min_kill = 0;
// On initialise le tableau des minotaures tués pour l'historique
bool* min_killed = malloc(sizeof(bool) * g->m->nb_minotaurs);
for (int i = 0; i < g->m->nb_minotaurs; i++) {
min_killed[i] = false;
}
// On teste dans les quatres directions
for (int i = 0; i < 4; i++) {
int cell_dir = g->m->player;
int portee_dir = portee;
while (portee_dir > 0 && get_adj_maze(g->m, cell_dir, i) != -1) {
int num_mino = has_minotaur_maze(g->m, get_adj_maze(g->m, cell_dir, i));
if (num_mino != -1 && g->minotaurs_alive[num_mino]) {
nb_min_kill++;
g->minotaurs_alive[num_mino] = false;
min_killed[num_mino] = true;
free_occupied_maze(g->m, get_adj_maze(g->m, cell_dir, i));
}
portee_dir--;
cell_dir = get_adj_maze(g->m, cell_dir, i);
}
}
if (nb_min_kill > 0) {
g->npolys--;
// On gère l'historique
turn new_turn = {T_POLY, .minokilled = min_killed};
add_entry_history(new_turn, g->log);
g->turns++;
return true;
}
}
return false;
}
/*****************************/
/*+ Gestion de l'historique +*/
/*****************************/
bool game_undo(game* g) {
if (g == NULL || g->log == NULL) {
ERROR("Pointeur de jeu null");
}
// On teste si l'historique est vide
if (sizeprev_history(g->log) == 0) {
return false;
}
// Sinon on annule le dernier tour
// On récupère le dernier tour
turn* last_turn = last_move_history(g->log);
rewind_history(g->log);
g->turns--;
switch (last_turn->type) {
case T_MOVE:
// On récupère les caractéristiques du tour
t_move* history_move = last_turn->tmove;
// On teste si le joueur a été attaqué
if (history_move->killer != -1) {
g->player_alive = true;
}
// On replace l'objet consommé
if (history_move->obj != NONE) {
game_consume_object(g, -1, history_move->obj);
g->m->objects[g->m->player] = history_move->obj;
}
// On deplace le joueur
free_occupied_maze(g->m, g->m->player);
g->m->player = get_adj_maze(g->m, g->m->player, (cardinal)(history_move->playermove + 2) % 4);
g->player_dir = (cardinal)((history_move->playermove + 2) % 4);
make_occupied_maze(g->m, g->m->player);
// On gère les déplacements des minotaures
for (int i = 0; i < g->m->nb_minotaurs; i++) {
if (g->minotaurs_alive[i]) {
// Si le minotaure est vivant on annule son déplacement
if (history_move->minomoves[i] != M_WAIT) {
free_occupied_maze(g->m, g->m->minotaurs[i]);
g->m->minotaurs[i] = get_adj_maze(g->m, g->m->minotaurs[i], (cardinal)(history_move->minomoves[i] + 2) % 4);
make_occupied_maze(g->m, g->m->minotaurs[i]);
g->minotaurs_dirs[i] = (cardinal)(history_move->minomoves[i] + 2) % 4;
}
}
}
return true;
case T_BOMB:
t_bomb* history_bomb = last_turn->tbomb;
// Si un mur a été détruit
if (history_bomb->destroyed) {
// On teste si le joueur a été attaqué
if (history_bomb->killer != -1) {
g->player_alive = true;
}
// On replace le mur détruit
build_wall_maze(g->m, g->m->player, history_bomb->bombdir);
}
// On recupère la bombe consommée
g->nbombs++;
return true;
case T_POLY:
bool* history_poly = last_turn->minokilled;
// On ressucite les minotaures tués
for (int i = 0; i < g->m->nb_minotaurs; i++) {
if (history_poly[i]) {
g->minotaurs_alive[i] = true;
make_occupied_maze(g->m, g->m->minotaurs[i]);
}
}
return true;
}
return false;
}
bool game_redo(game* g) {
if (g == NULL || g->log == NULL) {
ERROR("Pointeur de jeu null");
}
// On teste si l'historique est vide
if (sizenext_history(g->log) == 0) {
return false;
}
// Sinon on remet le dernier tour annulé
// On récupère le dernier tour annulé
turn* next_turn = next_move_history(g->log);
continue_history(g->log);
g->turns++;
switch (next_turn->type) {
case T_MOVE:
// On récupère les caractéristiques du tour
t_move* history_move = next_turn->tmove;
// On teste si le joueur avait été attaqué
if (history_move->killer != -1) {
g->player_alive = false;
}
// On deplace le joueur
free_occupied_maze(g->m, g->m->player);
g->m->player = get_adj_maze(g->m, g->m->player, (cardinal)(history_move->playermove));
g->player_dir = (cardinal)((history_move->playermove));
make_occupied_maze(g->m, g->m->player);
// On re-recupère l'objet
if (history_move->obj != NONE) {
game_consume_object(g, 1, history_move->obj);
g->m->objects[g->m->player] = NONE;
}
// On gère les déplacements des minotaures
for (int i = 0; i < g->m->nb_minotaurs; i++) {
if (g->minotaurs_alive[i]) {
// Si le minotaure est vivant on refait son déplacement
if (history_move->minomoves[i] != M_WAIT) {
free_occupied_maze(g->m, g->m->minotaurs[i]);
g->m->minotaurs[i] = get_adj_maze(g->m, g->m->minotaurs[i], (cardinal)(history_move->minomoves[i]));
make_occupied_maze(g->m, g->m->minotaurs[i]);
g->minotaurs_dirs[i] = (cardinal)(history_move->minomoves[i]);
}
}
}
return true;
case T_BOMB:
t_bomb* history_bomb = next_turn->tbomb;
// Si un mur avait été détruit puis reconstruit
if (history_bomb->destroyed) {
// On teste si le joueur a été attaqué
if (history_bomb->killer != -1) {
g->player_alive = false;
}
// On reconstruit le mur
del_wall_maze(g->m, g->m->player, history_bomb->bombdir);
}
return true;
case T_POLY:
bool* history_poly = next_turn->minokilled;
// On tue les minotaures qui avaient été ressucités
for (int i = 0; i < g->m->nb_minotaurs; i++) {
if (history_poly[i]) {
g->minotaurs_alive[i] = false;
free_occupied_maze(g->m, g->m->minotaurs[i]);
}
}
return true;
}
return false;
return false;
}